5.使用langgraph构建带记忆的聊天机器人
本文将介绍基于langchain和langgraph来搭建带记忆的聊天机器人。LangGraph是Langchain团队开发的一个Python库,专门用于创建可以记住状态的、复杂的AI工作流和多智能体系统。
它的核心目标是解决传统AI编排中的关键痛点:
无法处理复杂的决策逻辑
难以实现智能体之间的交互
缺乏上下文记忆和状态管理
LangGraph通过有向图(Directed Graph)的方式,解决了这些问题。
1.安装库
2.状态定义 1 2 3 class State (TypedDict ): messages: Annotated[Sequence [BaseMessage], add_messages] language: str
State类用于langgraph图中的状态维护,其使用TypedDict定义了对话状态:
messages:存储对话历史,使用Annotated标注支持消息添加功能
language:指定回答语言,支持多语言切换。
3.核心组件初始化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class AIChat (): def __init__ (self ): self .model = ChatOpenAI(model="gpt-4o-mini" ) self .prompt = ChatPromptTemplate.from_messages([ ( "system" , "You are a helpful assistant. Answer all questions to the best of your ability in {language}." , ), MessagesPlaceholder(variable_name="messages" ), ]) self .memory = MemorySaver() self .trimmer = trim_messages( max_tokens=65 , strategy="last" , token_counter=self .model, include_system=True , allow_partial=False , start_on="human" , )
初始化方法设置了四个关键组件:
语言模型:选择GPT-4系列模型作为对话核心
提示词模板:定义了AI助手的角色和行为
记忆系统:管理对话历史记录
消息修剪器:防止上下文过长,保持对话效率
4.模型调用流程 1 2 3 4 5 6 7 8 9 10 11 12 13 14 def call_model (self, state: State ): chain = self .prompt | self .model trimmed_messages = self .trimmer.invoke(state["messages" ]) response = chain.invoke( {"messages" : trimmed_messages, "language" : state["language" ]} ) return {"messages" : [response]}
这个方法实现了完整的模型调用流程:
创建处理链:将提示词模板与模型连接
消息修剪:确保不超过token限制
模型调用:传入处理后的消息和语言设置
响应处理:将模型响应封装返回
5.对话图构建 1 2 3 4 5 6 7 8 9 10 11 12 13 def create_graph (self ): workflow = StateGraph(state_schema=State) workflow.add_edge(START, "model" ) workflow.add_node("model" , self .call_model) app = workflow.compile (checkpointer=self .memory) return app
这个方法构建了对话系统的工作流:
创建图:使用State类作为状态模式
定义流程:从开始到模型的处理流
添加功能:将call_model方法作为处理节点
完成构建:编译图并集成记忆系统
6.交互界面 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 def chat (self ): config = {"configurable" : {"thread_id" : "abc123" }} language = "Chinese" app = self .create_graph() while True : query = input ("请输入问题:" ) input_messages = [HumanMessage(query)] for chunk, metadata in app.stream( {"messages" : input_messages, "language" : language}, config, stream_mode="messages" , ): if isinstance (chunk, AIMessage): print (chunk.content, end="" ) print ('\n' )
交互界面实现了以下功能:
配置初始化:设置对话ID和语言
图实例化:创建对话处理图
交互循环:持续接收用户输入
流式输出:实时显示AI响应
7.聊天示例 1 2 3 4 5 6 7 8 9 10 11 请输入问题:我是谁。 你是一个提问者,想要了解更多信息。如果你愿意,可以告诉我更多关于你的事情,我会尽力帮助你! 请输入问题:我是qqq 你好,QQQ!很高兴认识你。有什么我可以帮助你的吗? 请输入问题:我是谁 你是QQQ!如果你有其他问题或想聊些什么,随时告诉我! 请输入问题:我是谁 你是提问的那个人,具体是谁我并不知道。如果你愿意,可以告诉我更多关于你的信息!
刚开始大模型不知道我是谁,告诉它后由于添加了记忆它能回答出来,然后由于我们使用修剪器去除了前面的历史消息,它又不能回答出来。结果符合预期。
8.完整代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 from langchain_openai import ChatOpenAIfrom langchain_core.messages import BaseMessagefrom langchain_core.messages import HumanMessage, SystemMessage, trim_messages, AIMessagefrom langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholderfrom langgraph.checkpoint.memory import MemorySaverfrom langgraph.graph import START, MessagesState, StateGraphfrom langgraph.graph.message import add_messagesfrom typing import Sequence from typing_extensions import Annotated, TypedDictclass State (TypedDict ): messages: Annotated[Sequence [BaseMessage], add_messages] language: str class AIChat (): def __init__ (self ): self .model = ChatOpenAI(model="gpt-4o-mini" ) self .prompt = ChatPromptTemplate.from_messages( [ ( "system" , "You are a helpful assistant. Answer all questions to the best of your ability in {language}." , ), MessagesPlaceholder(variable_name="messages" ), ] ) self .memory = MemorySaver() self .trimmer = trim_messages( max_tokens=65 , strategy="last" , token_counter=self .model, include_system=True , allow_partial=False , start_on="human" , ) def call_model (self, state: State ): chain = self .prompt | self .model trimmed_messages = self .trimmer.invoke(state["messages" ]) response = chain.invoke( {"messages" : trimmed_messages, "language" : state["language" ]} ) return {"messages" : [response]} def create_graph (self ): workflow = StateGraph(state_schema=State) workflow.add_edge(START, "model" ) workflow.add_node("model" , self .call_model) app = workflow.compile (checkpointer=self .memory) return app def chat (self ): config = {"configurable" : {"thread_id" : "abc123" }} language = "Chinese" app = self .create_graph() while True : query = input ("请输入问题:" ) input_messages = [HumanMessage(query)] for chunk, metadata in app.stream( {"messages" : input_messages, "language" : language}, config, stream_mode="messages" , ): if isinstance (chunk, AIMessage): print (chunk.content, end="" ) print ('\n' ) if __name__ == "__main__" : AIChat().chat()