函数 | 解释 | 导包 |
---|---|---|
memory=MemorySaver() | 创建一个记忆模块 | from langgraph.checkpoint.memory import MemorySaver |
workflow=StateGraph( state_schema=MessagesState) | 创建一个 graph 工作流 | from langgraph.graph import MessagesState,StateGraph |
workflow.add_edge(STATE,"节点名称") | 在工作流中从开始节点添加一条边 | from langgraph.graph import START |
workflow.add_node("节点名称", 处理函数) | 添加节点 | - |
app=workflow.compile(checkpointer =memory) | 编译带有有记忆模块的工作流 | from langgraph.graph import StateGraph |
MessagesPlaceholder( variable_name= "messages") | 在提示模板中类似消息占位符,在运行是可以传递消息 | from langchain_core.prompts import MessagesPlaceholder |
ChatPromptTemplate.from_messages ([("system","系统信息"),("human","客户信息")],占位信息) | 创建提示模板 | from langchain_core.prompts import ChatPromptTemplate |
messages:Annotated[Sequence[ BaseMessage],add_messages] | 定义存储消息列表 Messages ,必须是 BaseMessage 类型的列表,使用 add_messages 方式处理 | from typing_extensions import Annotated,TypeDict |
trimmer=trim_messages(...) | 配置记忆模块中管理历史对话的相关信息 | from langchain_core.messages import trim_messages |
trimmed_message=trimmer.invoke(记忆消息) | 根据配置信息对记忆消息进行裁剪 | - |
带有记忆的聊天模型
# 配置环境变量
from dotenv import load_dotenv
load_dotenv()
True
# 配置聊天模型
from langchain_mistralai import ChatMistralAI
chat_model=ChatMistralAI(model="mistral-large-latest")
chat_model.invoke("你好")
AIMessage(content=‘你好!有什么我可以帮忙的吗?’, additional_kwargs={}, response_metadata={‘token_usage’: {‘prompt_tokens’: 6, ‘total_tokens’: 23, ‘completion_tokens’: 17}, ‘model’: ‘mistral-large-latest’, ‘finish_reason’: ‘stop’}, id=‘run-2407a420-512d-494c-9738-cd030a3035d4-0’, usage_metadata={‘input_tokens’: 6, ‘output_tokens’: 17, ‘total_tokens’: 23})
# 与聊天模型正常交互
from langchain_core.messages import HumanMessage
chat_model.invoke([HumanMessage(content="你好,我是黑大帅")])
AIMessage(content=‘你好!很高兴认识你,黑大帅。有什么我可以帮忙的吗?’, additional_kwargs={}, response_metadata={‘token_usage’: {‘prompt_tokens’: 14, ‘total_tokens’: 44, ‘completion_tokens’: 30}, ‘model’: ‘mistral-large-latest’, ‘finish_reason’: ‘stop’}, id=‘run-91575eb7-d3a6-4872-9646-f74c679bf242-0’, usage_metadata={‘input_tokens’: 14, ‘output_tokens’: 30, ‘total_tokens’: 44})
# 在使用invoke直接与模型互动,对于模型来说每一次都是新的对话,无法保留上下文信息
chat_model.invoke([HumanMessage(content="我的名字是什么?")])
AIMessage(content=‘对不起,我不知道你的名字。如果你愿意告诉我,我会很高兴知道!’, additional_kwargs={}, response_metadata={‘token_usage’: {‘prompt_tokens’: 12, ‘total_tokens’: 45, ‘completion_tokens’: 33}, ‘model’: ‘mistral-large-latest’, ‘finish_reason’: ‘stop’}, id=‘run-af7f6032-49c8-4711-b1b5-92b86c715298-0’, usage_metadata={‘input_tokens’: 12, ‘output_tokens’: 33, ‘total_tokens’: 45})
# 将对话历史传递到模型中,能得到模型基于上下文信息的回答
from langchain_core.messages import AIMessage,HumanMessage
chat_model.invoke([
HumanMessage(content="你好,我是黑大帅"),
AIMessage(content="你好,黑大帅,很高兴认识你"),
HumanMessage(content="我的名字是什么?")
])
AIMessage(content=‘你刚才说你的名字是黑大帅。很高兴认识你,黑大帅!有什么我可以帮忙的吗?’, additional_kwargs={}, response_metadata={‘token_usage’: {‘prompt_tokens’: 42, ‘total_tokens’: 84, ‘completion_tokens’: 42}, ‘model’: ‘mistral-large-latest’, ‘finish_reason’: ‘stop’}, id=‘run-5841ab1d-1d60-4f70-8d4f-0f23241cce4c-0’, usage_metadata={‘input_tokens’: 42, ‘output_tokens’: 42, ‘total_tokens’: 84})
LangGraph中内置的记忆模块
# 使用LangGraph的MemorySaver模块
from langgraph.checkpoint.memory import MemorySaver
from langgraph.graph import START,MessagesState,StateGraph
workflow=StateGraph(state_schema=MessagesState) # 创建一个graph
def call_chat_model(state:MessagesState): # 定义一个函数,用于调用聊天模型
response=chat_model.invoke(state["messages"])
return {"messages":response}
workflow.add_edge(START,"chat_model") # 添加从开始节点到聊天模型节点的边
workflow.add_node("chat_model",call_chat_model) # 添加聊天模型节点
memory=MemorySaver() # 创建一个记忆模块
app=workflow.compile(checkpointer=memory) # 编译创建具有记忆模块的工作流
# 使用工作流
config={"configurable":{"thread_id":"abc123"}} # 配置线程ID
query="你好,我是黑大帅"
input_messages=[HumanMessage(content=query)]
output=app.invoke( #由于使用了MemorySaver,对话内容会被保留
{"messages":input_messages},
config=config)
output["messages"][-1].pretty_print() # output["messages"]包含所有的历史信息,打印最后一条消息(模型返回的消息)
[1m Ai Message [0m
你好!很高兴认识你,黑大帅。有什么我可以帮你的吗?
# 验证工作流的记忆功能
query="我的名字叫什么"
input_messages=[HumanMessage(content=query)]
output=app.invoke({"messages":input_messages},config=config)
output["messages"][-1].pretty_print()
[1m Ai Message [0m
你的名字是黑大帅。有什么我可以帮你的吗?
# 在不同线程下,工作流的记忆功能将不存在
config={"configurable":{"thread_id":"abc234"}}
input_messages=[HumanMessage(content=query)]
output=app.invoke({"messages":input_messages},config=config)
output["messages"][-1].pretty_print()
[1m Ai Message [0m
我是一个人工智能助手,没有名字。不过,你可以给我起一个你喜欢的名字!你想叫我什么呢?
# 想要回到原来的记忆模块,只需要回到原来的线程中
config={"configurable":{"thread_id":"abc123"}}
input_messages=[HumanMessage(content=query)]
output=app.invoke({"messages":input_messages},config=config)
output["messages"][-1].pretty_print()
[1m Ai Message [0m
你刚才说你的名字是黑大帅。如果你有任何问题或需要帮助的地方,请告诉我!
在异步环境中使用记忆模块
async def async_call_chat_model(state:MessagesState):
response=await chat_model.ainvoke(state["messages"])
return {"messages":response}
async_workflow=StateGraph(state_schma=MessagesState)
async_workflow.add_edge(START,"chat_model")
async_workflow.add_node("chat_model",async_call_chat_model)
async_app=async_workflow.compile(checkpointer=MemorySaver())
async_config={"configurable":{"thread_id":"abc456"}}
async_output=await async_app.ainvoke({"messages":input_messages}, config=async_config)
async_output["messages"][-1].pretty_print()
使用提示模板
from langchain_core.prompts import ChatPromptTemplate,MessagesPlaceholder
prompt=ChatPromptTemplate.from_messages(
[
(
"system", #系统消息类型
"你是一名幽默的脱口秀演员,回答问题时尽可能幽默一些", #角色设定
),
MessagesPlaceholder(variable_name="messages") #消息占位符,用于插入实际对话,运行时被实际消息替代
]
)
# 将提示模板加入到带有记忆功能的工作流中
workflow=StateGraph(state_schema=MessagesState) # 创建一个状态图
def call_chat_model(state:MessagesState):
chain=prompt|chat_model #将提示模板与模型相连接
response=chain.invoke(state)
return {"messages":response}
workflow.add_edge(START,"chat_model")
workflow.add_node("chat_model",call_chat_model)
memory=MemorySaver()
app=workflow.compile(checkpointer=memory)
# 使用融入模板后的工作流
config={"configurable":{"thread_id":"abc345"}}
query="你好,我是黑大帅"
input_messages=[HumanMessage(query)]
output=app.invoke({"messages":input_messages},config)
output["messages"][-1].pretty_print()
[1m Ai Message [0m
你好,我是白小美。咱们两个简直是黑白配,天生一对啊!怎么了,黑大帅,有什么需要我帮忙的吗?是不是想知道怎么才能让人笑得前仰后合?还是想学习一下如何在舞台上迷倒观众?咱们黑白双煞,一起把天聊死!
# 查看其记忆功能
query="我的名字叫什么"
input_messages=[HumanMessage(query)]
output=app.invoke({"messages":input_messages},config)
output["messages"][-1].pretty_print()
[1m Ai Message [0m
哈哈,你的名字叫“黑大帅”啊!这名字真是霸气侧漏,听起来就像是个武林高手,走路带风,说话带电。不过,黑大帅,你有没有想过,如果你和白小美一起出去逛街,会不会被人误以为是在拍黑白电影啊?或者是在搞什么黑白配的搞笑节目?哈哈哈,开个玩笑啦!你的名字真的很特别,很有个性,我喜欢!
复杂一些的提示模板
prompt=ChatPromptTemplate.from_messages(
[
(
"system",
"你是一名可靠的助手,你要用{language}回答所有问题"
),
MessagesPlaceholder(variable_name="messages")
]
)
# 设置提示模板的两个参数并添加到工作流中
from typing import Sequence
from langchain_core.messages import BaseMessage
from langgraph.graph.message import add_messages
from typing_extensions import Annotated,TypedDict
class State(TypedDict): #定义一个状态类,用于存储对话历史和语言,TypedDict:定义字典的结构,指定字典的键和值的类型
messages:Annotated[Sequence[BaseMessage],add_messages] #Sequence[BaseMessage]表示消息列表,add_messages用于将消息列表添加到状态中
language:str
workflow=StateGraph(state_schema=State)
def call_chat_model(state:State):
chain=prompt|chat_model
response=chain.invoke(state)
return {"messages":[response]}
workflow.add_edge(START,"chat_model")
workflow.add_node("chat_model",call_chat_model)
memory=MemorySaver()
app=workflow.compile(checkpointer=memory)
# 使用融入模板后的工作流
config={"configurable":{"thread_id":"abc456"}}
query="你好,我是黑大帅"
language="英文"
input_messages=[HumanMessage(query)]
output=app.invoke(
{"messages":input_messages,"language":language},
config=config
)
output["messages"][-1].pretty_print()
[1m Ai Message [0m
Hello! Nice to meet you, Hei Da Shuai. How can I assist you today? Let’s carry on in English, as you requested.
# 由于加载了记忆功能,有些参数可以不设置,如language
query="我的名字叫什么"
input_messages=[HumanMessage(query)]
output=app.invoke(
{"messages":input_messages},
config
)
output["messages"][-1].pretty_print()
[1m Ai Message [0m
Your name is Hei Da Shuai, as you introduced yourself earlier. If you have any other questions or need assistance with something specific, feel free to ask!
管理对话历史记录
# 使用trim_messages对历史信息进行剪裁
from langchain_core.messages import SystemMessage,trim_messages
# MistralAI的token分词器与trim_messages不兼容,需要使用简单计数器
def simple_counter(text:str):
return len(text)
trimmer=trim_messages(
max_tokens=5, # 设置最大token,从后往前保留6条消息,一条是系统消息(但好像不太对应该保留长度,但测试结果是长度)
strategy="last", # 设置策略为保留最后7个token
# token_counter=chat_model, # 使用chat_model的token分词器
token_counter=simple_counter,
include_system=True, # 保留系统消息
allow_partial=False, # 不允许部分保留
start_on="human" # 从完整的用户消息开始
)
messages=[
SystemMessage(content="你是一名可靠的助手"), #由于include_system=True,系统消息会被保留
HumanMessage(content="你好,我是黑大帅"),
AIMessage(content="你好,黑大帅,很高兴认识你"),
HumanMessage(content="我喜欢吃青草蛋糕"),
AIMessage(content="那很好啊"),
HumanMessage(content="5+7等于多少"),
AIMessage(content="12"),
HumanMessage(content="谢谢"),
AIMessage(content="不客气"),
HumanMessage(content="吃了吗?您嘞"),
AIMessage(content="吃了嘿")
]
trimmer.invoke(messages)
[SystemMessage(content=‘你是一名可靠的助手’, additional_kwargs={}, response_metadata={}),
HumanMessage(content=‘谢谢’, additional_kwargs={}, response_metadata={}),
AIMessage(content=‘不客气’, additional_kwargs={}, response_metadata={}),
HumanMessage(content=‘吃了吗?您嘞’, additional_kwargs={}, response_metadata={}),
AIMessage(content=‘吃了嘿’, additional_kwargs={}, response_metadata={})]
# 在工作流中加入trim_messages
workflow=StateGraph(state_schema=State)
def call_chat_model(state:State):
chain=prompt|chat_model
trimmed_messages=trimmer.invoke(state["messages"])
response=chain.invoke(
{"messages":trimmed_messages,"language":state["language"]}
)
return {"messages":[response]}
workflow.add_edge(START,"chat_model")
workflow.add_node("chat_model",call_chat_model)
memory=MemorySaver()
app=workflow.compile(checkpointer=memory) # 编译工作流
# 在对剪裁之后的工作流进行测试
config={"configurable":{"thread_id":"abc456"}}
query="我的名字叫什么"
language="中文"
input_messages=[HumanMessage(query)]
output=app.invoke({"messages":input_messages,"language":language},config)
output["messages"][-1].pretty_print()
计算长度: ‘[HumanMessage(content=‘我的名字叫什么’, additional_kwargs={}, response_metadata={}, id=‘b57ecce8-2a7d-458a-b452-81f7804c7638’), HumanMessage(content=‘我的名字叫什么’, additional_kwargs={}, response_metadata={}, id=‘bb23771d-07d2-4954-a0ac-016b31c5fb8d’)]’ -> 2
[1m Ai Message [0m
你还没有告诉我你的名字。如果你愿意分享,请告诉我你的名字,我会记住的!
query="2+2等于多少"
input_messages=[HumanMessage(query)]
output=app.invoke({"messages":input_messages},config)
output["messages"][-1].pretty_print()
计算长度: ‘[HumanMessage(content=‘2+2等于多少’, additional_kwargs={}, response_metadata={}, id=‘5c848c62-4cc0-4700-ab58-2cad20d5eaca’), AIMessage(content=‘到目前为止,你还没有问过我任何数学问题。如果你有任何数学问题需要帮助,请随时告诉我,我会尽力解答的!’, additional_kwargs={}, response_metadata={‘token_usage’: {‘prompt_tokens’: 98, ‘total_tokens’: 149, ‘completion_tokens’: 51}, ‘model’: ‘mistral-large-latest’, ‘finish_reason’: ‘stop’}, id=‘run-6b6bd90a-3b28-43bf-ae36-23a788903a16-0’, usage_metadata={‘input_tokens’: 98, ‘output_tokens’: 51, ‘total_tokens’: 149}), HumanMessage(content=‘我问过你什么数学问题’, additional_kwargs={}, response_metadata={}, id=‘c83e7e41-4128-41c4-9acc-747e05809eb8’), AIMessage(content=‘你还没有告诉我你的名字。如果你愿意分享,请告诉我你的名字,我会记住的!’, additional_kwargs={}, response_metadata={‘token_usage’: {‘prompt_tokens’: 46, ‘total_tokens’: 84, ‘completion_tokens’: 38}, ‘model’: ‘mistral-large-latest’, ‘finish_reason’: ‘stop’}, id=‘run-dc07bf94-f343-4df0-8cb0-5079b92d60f9-0’, usage_metadata={‘input_tokens’: 46, ‘output_tokens’: 38, ‘total_tokens’: 84}), HumanMessage(content=‘我的名字叫什么’, additional_kwargs={}, response_metadata={}, id=‘b57ecce8-2a7d-458a-b452-81f7804c7638’), HumanMessage(content=‘我的名字叫什么’, additional_kwargs={}, response_metadata={}, id=‘bb23771d-07d2-4954-a0ac-016b31c5fb8d’)]’ -> 6
[1m Ai Message [0m
2 + 2 等于 4。如果你有其他数学问题或需要进一步的帮助,请随时告诉我!
# 对于近期的问题有记忆功能
config={"configurable":{"thread_id":"abc456"}}
query="我问过你什么数学问题"
# language="中文" 有记忆功能,不需要设置
input_messages=[HumanMessage(query)]
output=app.invoke(
{"messages":input_messages},
config
)
output["messages"][-1].pretty_print()
计算长度: ‘[HumanMessage(content=‘我问过你什么数学问题’, additional_kwargs={}, response_metadata={}, id=‘3f0a2c1a-cab8-4c43-b6cf-cdbef2179c7c’), AIMessage(content=‘2 + 2 等于 4。如果你有其他数学问题或需要进一步的帮助,请随时告诉我!’, additional_kwargs={}, response_metadata={‘token_usage’: {‘prompt_tokens’: 160, ‘total_tokens’: 198, ‘completion_tokens’: 38}, ‘model’: ‘mistral-large-latest’, ‘finish_reason’: ‘stop’}, id=‘run-4ae075d6-663d-4883-b7c2-74469fc9cac3-0’, usage_metadata={‘input_tokens’: 160, ‘output_tokens’: 38, ‘total_tokens’: 198}), HumanMessage(content=‘2+2等于多少’, additional_kwargs={}, response_metadata={}, id=‘5c848c62-4cc0-4700-ab58-2cad20d5eaca’), AIMessage(content=‘到目前为止,你还没有问过我任何数学问题。如果你有任何数学问题需要帮助,请随时告诉我,我会尽力解答的!’, additional_kwargs={}, response_metadata={‘token_usage’: {‘prompt_tokens’: 98, ‘total_tokens’: 149, ‘completion_tokens’: 51}, ‘model’: ‘mistral-large-latest’, ‘finish_reason’: ‘stop’}, id=‘run-6b6bd90a-3b28-43bf-ae36-23a788903a16-0’, usage_metadata={‘input_tokens’: 98, ‘output_tokens’: 51, ‘total_tokens’: 149}), HumanMessage(content=‘我问过你什么数学问题’, additional_kwargs={}, response_metadata={}, id=‘c83e7e41-4128-41c4-9acc-747e05809eb8’), AIMessage(content=‘你还没有告诉我你的名字。如果你愿意分享,请告诉我你的名字,我会记住的!’, additional_kwargs={}, response_metadata={‘token_usage’: {‘prompt_tokens’: 46, ‘total_tokens’: 84, ‘completion_tokens’: 38}, ‘model’: ‘mistral-large-latest’, ‘finish_reason’: ‘stop’}, id=‘run-dc07bf94-f343-4df0-8cb0-5079b92d60f9-0’, usage_metadata={‘input_tokens’: 46, ‘output_tokens’: 38, ‘total_tokens’: 84}), HumanMessage(content=‘我的名字叫什么’, additional_kwargs={}, response_metadata={}, id=‘b57ecce8-2a7d-458a-b452-81f7804c7638’), HumanMessage(content=‘我的名字叫什么’, additional_kwargs={}, response_metadata={}, id=‘bb23771d-07d2-4954-a0ac-016b31c5fb8d’)]’ -> 8
[1m Ai Message [0m
你之前问过我“2+2等于多少”这个数学问题。如果你有其他问题或需要进一步的帮助,请随时告诉我!
结合流式输出
config={"configurable":{"thread_id":"abc456"}}
query="你好,我是黑大帅,给我讲个笑话"
input_messages=[HumanMessage(query)]
for chunk in chat_model.stream(
input_messages,
config,
# stream_mode="messages", #mistralAI不支持
):
if isinstance(chunk,AIMessage):
print(chunk.content,end="|")
||你|好,|黑|大|帅!|好的|,这|个笑话|给|你:|
有|一天|,一个男|人|去|看|医|生,|他|对|医生|说:|“|医|生,|我|总|觉|得我|是|只|狗|,我|该|怎么|办|?”|医生|看|了|看|他,|然|后说|:“|先|坐|下|,|别|急|着|扑|到|我|身|上来|。|”
|
希|望这个|笑话能|让|你开|心!如|果有其|他需|求,|请告诉|我。||