当前位置: 首页 > article >正文

吃透LangChain(四):消息管理与聊天历史存储

消息存储在内存

      下面我们展示一个简单的示例,其中聊天历史保存在内存中,此处通过全局 Python 字典实现。我们构建一个名为 get_session_history 的可调用对象,引用此字典以返回chatMessageHistory实例。通过在运行时向 RunnablewithMessageHistory 传递配置,可以指定可调用对象的参数。默认情况下,期望配置参数是一个字符串 session id。可以通过 history_factory_config 关键字参数进行调整。

安装所需库

pip install langchain-community

测试代码

from langchain_community.chat_message_histories import ChatMessageHistory
from langchain_core.chat_history import BaseChatMessageHistory
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_openai import ChatOpenAI# 用于存储不同会话的聊天消息历史
store = {}# 初始化OpenAI的本地聊天模型
chat_model = ChatOpenAI(streaming=True,openai_api_key="sk-XXX",openai_api_base="http://192.168.10.106:11434/v1",model_name="llama3.2"
)# 创建聊天提示模板,包含系统指令、消息历史占位和用户输入
prompt = ChatPromptTemplate.from_messages([("system", "你是一名得力助手并且非常擅长{ability},请用比较简短的话回答,字数在50以内"),MessagesPlaceholder(variable_name="history"),("human", "{input}"),
])# 将提示模板和聊天模型组合成一个可执行链
chain = prompt | chat_model# 定义函数,根据会话ID获取对应的聊天消息历史,若不存在则创建
def get_session_history(session_id: str) -> BaseChatMessageHistory:if session_id not in store:store[session_id] = ChatMessageHistory()return store[session_id]# 创建带消息历史管理功能的可运行对象
with_message_history = RunnableWithMessageHistory(chain,get_session_history,input_messages_key="input",history_messages_key="history",
)# 调用带消息历史管理的对象,传入用户问题和会话配置并获取结果
result = with_message_history.invoke({"ability": "数学", "input": "正弦是什么意思?"},config={"configurable": {"session_id": "qaz123"}},
)
# 打印聊天模型针对用户问题给出的回复结果
print("第一次调用 result = ", result)
print("第一次调用 store = ", store)# 调用带消息历史管理的对象,再次询问,相同session_id会记录上次的问题
result = with_message_history.invoke({"ability": "数学", "input": "什么?"},config={"configurable": {"session_id": "qaz123"}},
)
# 打印聊天模型针对用户问题给出的回复结果
print("第二次调用 result = ", result)
print("第二次调用 store = ", store)# 调用带消息历史管理的对象,再次询问,不同session_id的问题,将不会是预期答案。
result = with_message_history.invoke({"ability": "数学", "input": "什么?"},config={"configurable": {"session_id": "qaz1234"}},
)
# 打印聊天模型针对用户问题给出的回复结果
print("第三次调用 result = ", result)
print("第三次调用 store = ", store)

输出

第一次调用 result =  content='"正弦"是指圆周的上方一个点到原点的距离。' additional_kwargs={} response_metadata={'finish_reason': 'stop', 'model_name': 'llama3.2', 'system_fingerprint': 'fp_ollama'} id='run-42abc1bc-ac4c-4523-ae9f-2552001eaa10-0'
第一次调用 store =  {'qaz123': InMemoryChatMessageHistory(messages=[HumanMessage(content='正弦是什么意思?', additional_kwargs={}, response_metadata={}), AIMessage(content='"正弦"是指圆周的上方一个点到原点的距离。', additional_kwargs={}, response_metadata={'finish_reason': 'stop', 'model_name': 'llama3.2', 'system_fingerprint': 'fp_ollama'}, id='run-42abc1bc-ac4c-4523-ae9f-2552001eaa10-0')])}
第二次调用 result =  content='我orry!听起来我的答案可能有些偏离主流中文解释。更常说的,三角形中的一个角度是“正弦”,它是从直角向对边侧的角度大小,表示为sin().' additional_kwargs={} response_metadata={'finish_reason': 'stop', 'model_name': 'llama3.2', 'system_fingerprint': 'fp_ollama'} id='run-92cf1bc4-3d15-4e5a-8cc0-7b51b8988131-0'
第二次调用 store =  {'qaz123': InMemoryChatMessageHistory(messages=[HumanMessage(content='正弦是什么意思?', additional_kwargs={}, response_metadata={}), AIMessage(content='"正弦"是指圆周的上方一个点到原点的距离。', additional_kwargs={}, response_metadata={'finish_reason': 'stop', 'model_name': 'llama3.2', 'system_fingerprint': 'fp_ollama'}, id='run-42abc1bc-ac4c-4523-ae9f-2552001eaa10-0'), HumanMessage(content='什么?', additional_kwargs={}, response_metadata={}), AIMessage(content='我orry!听起来我的答案可能有些偏离主流中文解释。更常说的,三角形中的一个角度是“正弦”,它是从直角向对边侧的角度大小,表示为sin().', additional_kwargs={}, response_metadata={'finish_reason': 'stop', 'model_name': 'llama3.2', 'system_fingerprint': 'fp_ollama'}, id='run-92cf1bc4-3d15-4e5a-8cc0-7b51b8988131-0')])}
第三次调用 result =  content='请讲,需要我的帮助吗?' additional_kwargs={} response_metadata={'finish_reason': 'stop', 'model_name': 'llama3.2', 'system_fingerprint': 'fp_ollama'} id='run-f61cf9e8-b9e1-4532-84ee-0e72b0b67832-0'
第三次调用 store =  {'qaz123': InMemoryChatMessageHistory(messages=[HumanMessage(content='正弦是什么意思?', additional_kwargs={}, response_metadata={}), AIMessage(content='"正弦"是指圆周的上方一个点到原点的距离。', additional_kwargs={}, response_metadata={'finish_reason': 'stop', 'model_name': 'llama3.2', 'system_fingerprint': 'fp_ollama'}, id='run-42abc1bc-ac4c-4523-ae9f-2552001eaa10-0'), HumanMessage(content='什么?', additional_kwargs={}, response_metadata={}), AIMessage(content='我orry!听起来我的答案可能有些偏离主流中文解释。更常说的,三角形中的一个角度是“正弦”,它是从直角向对边侧的角度大小,表示为sin().', additional_kwargs={}, response_metadata={'finish_reason': 'stop', 'model_name': 'llama3.2', 'system_fingerprint': 'fp_ollama'}, id='run-92cf1bc4-3d15-4e5a-8cc0-7b51b8988131-0')]), 'qaz1234': InMemoryChatMessageHistory(messages=[HumanMessage(content='什么?', additional_kwargs={}, response_metadata={}), AIMessage(content='请讲,需要我的帮助吗?', additional_kwargs={}, response_metadata={'finish_reason': 'stop', 'model_name': 'llama3.2', 'system_fingerprint': 'fp_ollama'}, id='run-f61cf9e8-b9e1-4532-84ee-0e72b0b67832-0')])}

很明显,第二次大模型是知道我们在问的问题;第三次换了session_id,表示一个新的会话,就不知道我们具体要问什么了。

配置会话唯一键

我们可以通过向 history_factory_config参数传递一个 ConfigurableFieldspec 对象列表来自定义跟踪消息历史的配置参数。下面我们使用了两个参数:user id和conversation id。

配置user id和conversation id作为会话唯一键

from langchain_community.chat_message_histories import ChatMessageHistory
from langchain_core.chat_history import BaseChatMessageHistory
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.runnables import ConfigurableFieldSpec
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_openai import ChatOpenAI# 用于存储不同会话的聊天消息历史
store = {}# 初始化OpenAI的本地聊天模型
chat_model = ChatOpenAI(streaming=True,openai_api_key="sk-XXX",openai_api_base="http://192.168.10.106:11434/v1",model_name="llama3.2"
)# 创建聊天提示模板,包含系统指令、消息历史占位和用户输入
prompt = ChatPromptTemplate.from_messages([("system", "你是一名得力助手并且非常擅长{ability},请用比较简短的话回答,字数在50以内"),MessagesPlaceholder(variable_name="history"),("human", "{input}"),
])# 将提示模板和聊天模型组合成一个可执行链
chain = prompt | chat_model# 定义函数,根据用户ID、会话ID获取对应的聊天消息历史,若不存在则创建
def get_session_history(user_id: str, conversation_id: str) -> BaseChatMessageHistory:if (user_id, conversation_id) not in store:store[(user_id, conversation_id)] = ChatMessageHistory()return store[(user_id, conversation_id)]# 创建带消息历史管理功能的可运行对象
with_message_history = RunnableWithMessageHistory(chain,get_session_history,input_messages_key="input",history_messages_key="history",history_factory_config=[ConfigurableFieldSpec(id="user_id",annotation=str,name="USER ID",description="用户唯一标识",default="",is_shared=True,dependencies=[],),ConfigurableFieldSpec(id="conversation_id",annotation=str,name="CONVERSATION ID",description="会话唯一标识",default="",is_shared=True,dependencies=[],),]
)# 调用带消息历史管理的对象,传入用户问题和会话配置并获取结果
result = with_message_history.invoke({"ability": "数学", "input": "正弦是什么意思?"},config={"configurable": {"user_id": "1", "conversation_id": "123"}},
)
# 打印聊天模型针对用户问题给出的回复结果
print("第一次调用 result = ", result)
print("第一次调用 store = ", store)# 调用带消息历史管理的对象,再次询问,相同session_id会记录上次的问题
result = with_message_history.invoke({"ability": "数学", "input": "什么?"},config={"configurable": {"user_id": "1", "conversation_id": "123"}},
)
# 打印聊天模型针对用户问题给出的回复结果
print("第二次调用 result = ", result)
print("第二次调用 store = ", store)# 调用带消息历史管理的对象,再次询问,不同session_id的问题,将不会是预期答案。
result = with_message_history.invoke({"ability": "数学", "input": "什么?"},config={"configurable": {"user_id": "2", "conversation_id": "1234"}},
)
# 打印聊天模型针对用户问题给出的回复结果
print("第三次调用 result = ", result)
print("第三次调用 store = ", store)

输出

第一次调用 result =  content='正弦是三角学中一个重要的函数,代表的是一条直线在以正切为单位的圆边上的幅度。' additional_kwargs={} response_metadata={'finish_reason': 'stop', 'model_name': 'llama3.2', 'system_fingerprint': 'fp_ollama'} id='run-e62756a1-3bb6-4701-8946-762dc69e4ae5-0'
第一次调用 store =  {('1', '123'): InMemoryChatMessageHistory(messages=[HumanMessage(content='正弦是什么意思?', additional_kwargs={}, response_metadata={}), AIMessage(content='正弦是三角学中一个重要的函数,代表的是一条直线在以正切为单位的圆边上的幅度。', additional_kwargs={}, response_metadata={'finish_reason': 'stop', 'model_name': 'llama3.2', 'system_fingerprint': 'fp_ollama'}, id='run-e62756a1-3bb6-4701-8946-762dc69e4ae5-0')])}
第二次调用 result =  content='换句话说,它描述了一个点在单位圆(以原点为中心和半径1的圆)的位置和大小。 usually 表示为 sin(x) หร sin(θ),其中 x 或 θ 是角度,其正弦值表示该角度对应的水平位移。' additional_kwargs={} response_metadata={'finish_reason': 'stop', 'model_name': 'llama3.2', 'system_fingerprint': 'fp_ollama'} id='run-757467cf-c8b0-43bd-b664-a1756064f180-0'
第二次调用 store =  {('1', '123'): InMemoryChatMessageHistory(messages=[HumanMessage(content='正弦是什么意思?', additional_kwargs={}, response_metadata={}), AIMessage(content='正弦是三角学中一个重要的函数,代表的是一条直线在以正切为单位的圆边上的幅度。', additional_kwargs={}, response_metadata={'finish_reason': 'stop', 'model_name': 'llama3.2', 'system_fingerprint': 'fp_ollama'}, id='run-e62756a1-3bb6-4701-8946-762dc69e4ae5-0'), HumanMessage(content='什么?', additional_kwargs={}, response_metadata={}), AIMessage(content='换句话说,它描述了一个点在单位圆(以原点为中心和半径1的圆)的位置和大小。 usually 表示为 sin(x) หร sin(θ),其中 x 或 θ 是角度,其正弦值表示该角度对应的水平位移。', additional_kwargs={}, response_metadata={'finish_reason': 'stop', 'model_name': 'llama3.2', 'system_fingerprint': 'fp_ollama'}, id='run-757467cf-c8b0-43bd-b664-a1756064f180-0')])}
第三次调用 result =  content='您需要帮助或者有哪个问题?让我们相互交流一下。' additional_kwargs={} response_metadata={'finish_reason': 'stop', 'model_name': 'llama3.2', 'system_fingerprint': 'fp_ollama'} id='run-059599e4-b18f-4277-8665-dffec99d5678-0'
第三次调用 store =  {('1', '123'): InMemoryChatMessageHistory(messages=[HumanMessage(content='正弦是什么意思?', additional_kwargs={}, response_metadata={}), AIMessage(content='正弦是三角学中一个重要的函数,代表的是一条直线在以正切为单位的圆边上的幅度。', additional_kwargs={}, response_metadata={'finish_reason': 'stop', 'model_name': 'llama3.2', 'system_fingerprint': 'fp_ollama'}, id='run-e62756a1-3bb6-4701-8946-762dc69e4ae5-0'), HumanMessage(content='什么?', additional_kwargs={}, response_metadata={}), AIMessage(content='换句话说,它描述了一个点在单位圆(以原点为中心和半径1的圆)的位置和大小。 usually 表示为 sin(x) หร sin(θ),其中 x 或 θ 是角度,其正弦值表示该角度对应的水平位移。', additional_kwargs={}, response_metadata={'finish_reason': 'stop', 'model_name': 'llama3.2', 'system_fingerprint': 'fp_ollama'}, id='run-757467cf-c8b0-43bd-b664-a1756064f180-0')]), ('2', '1234'): InMemoryChatMessageHistory(messages=[HumanMessage(content='什么?', additional_kwargs={}, response_metadata={}), AIMessage(content='您需要帮助或者有哪个问题?让我们相互交流一下。', additional_kwargs={}, response_metadata={'finish_reason': 'stop', 'model_name': 'llama3.2', 'system_fingerprint': 'fp_ollama'}, id='run-059599e4-b18f-4277-8665-dffec99d5678-0')])}

输出还是差不多的,只是可以根据业务添加不同的标识值。

消息持久化

配置redis环境

pip install--upgrade --quiet redis

如果我们没有现有的 Redis 部署可以连接,可以启动本地 Redis Stack 服务器

docker run -d -p6379:6379 -p 8001:8001 redis/redis-stack:latest

我windows系统里边有,就直接用了

REDIS URL="redis://localhost:6379/0

调用聊天接口,看Redis是否存储历史记录

更新消息历史实现只需要我们定义一个新的可调用对象,这次返回一个RedisChatMessageHistory实例:

from langchain_community.chat_message_histories import RedisChatMessageHistory
from langchain_core.chat_history import BaseChatMessageHistory
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_openai import ChatOpenAI# 初始化OpenAI的本地聊天模型
chat_model = ChatOpenAI(streaming=True,openai_api_key="sk-XXX",openai_api_base="http://192.168.10.106:11434/v1",model_name="llama3.2"
)# 创建聊天提示模板,包含系统指令、消息历史占位和用户输入
prompt = ChatPromptTemplate.from_messages([("system", "你是一名得力助手并且非常擅长{ability},请用比较简短的话回答,字数在50以内,英文回答!"),MessagesPlaceholder(variable_name="history"),("human", "{input}"),
])# redis url
REDIS_URL="redis://localhost:6379/0"# 将提示模板和聊天模型组合成一个可执行链
chain = prompt | chat_model# 定义函数,根据会话ID获取对应的聊天消息历史,若不存在则创建
def get_session_history(session_id: str) -> RedisChatMessageHistory:return RedisChatMessageHistory(session_id, REDIS_URL)# 创建带消息历史管理功能的可运行对象
with_message_history = RunnableWithMessageHistory(chain,get_session_history,input_messages_key="input",history_messages_key="history",
)# 调用带消息历史管理的对象,传入用户问题和会话配置并获取结果
result = with_message_history.invoke({"ability": "数学", "input": "余弦是什么意思?"},config={"configurable": {"session_id": "qaz123"}},
)
# 打印聊天模型针对用户问题给出的回复结果
print("第一次调用 result = ", result)# 调用带消息历史管理的对象,再次询问,相同session_id会记录上次的问题
result = with_message_history.invoke({"ability": "数学", "input": "什么?"},config={"configurable": {"session_id": "qaz123"}},
)
# 打印聊天模型针对用户问题给出的回复结果
print("第二次调用 result = ", result)# 调用带消息历史管理的对象,再次询问,不同session_id的问题,将不会是预期答案。
result = with_message_history.invoke({"ability": "数学", "input": "什么?"},config={"configurable": {"session_id": "qaz1234"}},
)
# 打印聊天模型针对用户问题给出的回复结果
print("第三次调用 result = ", result)

输出

第一次调用 result =  content='“余弦”这个词通常可以指:\n\n1. 角余弦:一个三角形的中点(即斜边与对面角的半径)\n2. 自然数的余弦值:大约等于0.739085\n\n同时,也指在图像和信号处理等领域中的“余弦”函数,即函数cos(x),表示一个物体或信号沿x轴完成90度的振荡。' additional_kwargs={} response_metadata={'finish_reason': 'stop', 'model_name': 'llama3.2', 'system_fingerprint': 'fp_ollama'} id='run-8deefdc9-3619-4fc3-bc71-9103aaf0143e-0'
第二次调用 result =  content='你在问这两个解释之间有何区别?我解释了余弦的字面含义和数学中的余弦函数,希望让你了解两者之间的差异。' additional_kwargs={} response_metadata={'finish_reason': 'stop', 'model_name': 'llama3.2', 'system_fingerprint': 'fp_ollama'} id='run-9fddc554-5647-4797-aef6-c80239963863-0'
第三次调用 result =  content="I'm here to help with math questions and problems. My expertise covers various math topics, including algebra, geometry, calculus, statistics, and more. I can assist you with equations, formulas, theorems, and concepts. What's on your mind?" additional_kwargs={} response_metadata={'finish_reason': 'stop', 'model_name': 'llama3.2', 'system_fingerprint': 'fp_ollama'} id='run-af5c0ef2-6f8e-4b0e-914e-087f7357f5b9-0'

     就算我们关闭代码,下次用相同的session_id提问,它仍然可以知道上次的问题。

修改聊天历史

裁剪消息

      LLM 和聊天模型有限的上下文窗口,即使您没有直接达到限制,您可能也希望限制模型处理的干扰量。一种解决方案是只加载和存储最近的n 条消息。让我们使用一个带有一些预加载消息的示例历史记录:

from langchain_community.chat_message_histories import ChatMessageHistory
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.runnables import RunnablePassthrough
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_openai import ChatOpenAItemp_chat_history = ChatMessageHistory()
temp_chat_history.add_user_message("我叫豆子,你好!")
temp_chat_history.add_ai_message("你好!")
temp_chat_history.add_user_message("今天天气挺好的!")
temp_chat_history.add_ai_message("今天天气怎么样?")
temp_chat_history.add_user_message("我下午在打台球。")
temp_chat_history.add_ai_message("我下午在干什么?")
temp_chat_history.messages# 初始化OpenAI的本地聊天模型
chat_model = ChatOpenAI(streaming=True,openai_api_key="sk-XXX",openai_api_base="http://192.168.10.106:11434/v1",model_name="llama3.2"
)# 创建聊天提示模板,包含系统指令、消息历史占位和用户输入
prompt = ChatPromptTemplate.from_messages([("system", "你是一个乐于助人的助手。尽力回答所有问题。提供的聊天历史包括与您交谈的用户的事实。"),MessagesPlaceholder(variable_name="history"),("human", "{input}"),
])chain = prompt | chat_modeldef trim_history_message(chain_input):stored_message = temp_chat_history.messages;if len(stored_message) <= 2:return Falsetemp_chat_history.clear()for message in stored_message[-2:]:temp_chat_history.add_message(message)return True# 创建带消息历史管理功能的可运行对象
with_message_history = RunnableWithMessageHistory(chain,lambda session_id: temp_chat_history,input_messages_key="input",history_messages_key="history",
)# 先截取聊天历史为两条及以下,在调用大模型
chat_trim_message = (RunnablePassthrough.assign(messages_trimmed=trim_history_message)| with_message_history
)result = chat_trim_message.invoke({"input": "我今天下午在干什么?"},config={"configurable": {"session_id": "unused"}},
)print("第一次调用有历史记录:", result.content)
print("==========================")
print("第一次调用有历史记录:", temp_chat_history.messages)result = chat_trim_message.invoke({"input": "我是谁?"},config={"configurable": {"session_id": "unused"}},
)# 正确回答应该是 豆子,但已经不记得了
print("第二次调用没有历史记录:", result.content)
print("==========================")
print("第二次调用没有历史记录:", temp_chat_history.messages)

输出

第一次调用有历史记录: 今天下午你正在打台球!sounds fun!玩得怎么样?
==========================
第一次调用有历史记录: [HumanMessage(content='我下午在打台球。', additional_kwargs={}, response_metadata={}), AIMessage(content='我下午在干什么?', additional_kwargs={}, response_metadata={}), HumanMessage(content='我今天下午在干什么?', additional_kwargs={}, response_metadata={}), AIMessage(content='今天下午你正在打台球!sounds fun!玩得怎么样?', additional_kwargs={}, response_metadata={'finish_reason': 'stop', 'model_name': 'llama3.2', 'system_fingerprint': 'fp_ollama'}, id='run-4c2c7e2a-e882-44c3-9542-d2d8a859241b-0')]
第二次调用没有历史记录: 我不知道你是谁,因为我们刚开始聊天。我们的会话历史还非常 ngắn。你可以对 me 来说 anything 我能帮助你什么?
==========================
第二次调用没有历史记录: [HumanMessage(content='我今天下午在干什么?', additional_kwargs={}, response_metadata={}), AIMessage(content='今天下午你正在打台球!sounds fun!玩得怎么样?', additional_kwargs={}, response_metadata={'finish_reason': 'stop', 'model_name': 'llama3.2', 'system_fingerprint': 'fp_ollama'}, id='run-4c2c7e2a-e882-44c3-9542-d2d8a859241b-0'), HumanMessage(content='我是谁?', additional_kwargs={}, response_metadata={}), AIMessage(content='我不知道你是谁,因为我们刚开始聊天。我们的会话历史还非常 ngắn。你可以对 me 来说 anything 我能帮助你什么?', additional_kwargs={}, response_metadata={'finish_reason': 'stop', 'model_name': 'llama3.2', 'system_fingerprint': 'fp_ollama'}, id='run-b0e5b1ff-0f94-4766-a8c7-b80908fb630c-0')]

总结聊天历史

        我们也可以以其他方式使用相同的模式。例如,我们可以使用额外的LLM调用来在调用链之前生成对话摘要。让我们重新创建我们的聊天历史和聊天机器人链:

from langchain_community.chat_message_histories import ChatMessageHistory
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.runnables import RunnablePassthrough
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_openai import ChatOpenAItemp_chat_history = ChatMessageHistory()
temp_chat_history.add_user_message("我的名字叫Mark。")
temp_chat_history.add_ai_message("你叫什么名字?")
temp_chat_history.add_user_message("今天天气晴朗!")
temp_chat_history.add_ai_message("今天天气怎么样?")
temp_chat_history.add_user_message("我下午在打台球。")
temp_chat_history.add_ai_message("我下午在干什么?")
temp_chat_history.messages# 初始化OpenAI的本地聊天模型
chat_model = ChatOpenAI(streaming=True,openai_api_key="sk-XXX",openai_api_base="http://192.168.10.106:11434/v1",model_name="llama3.2"
)# 创建聊天提示模板,包含系统指令、消息历史占位和用户输入
prompt = ChatPromptTemplate.from_messages([("system", "你是一个乐于助人的助手。尽力回答所有问题。提供的聊天历史包括与您交谈的用户的事实。"),MessagesPlaceholder(variable_name="history"),("human", "{input}"),
])chain = prompt | chat_modeldef summarize_message(chain_input):stored_message = temp_chat_history.messages;if len(stored_message) == 0:return Falsesummarize_prompt = ChatPromptTemplate.from_messages([MessagesPlaceholder(variable_name="history"),("user", "将上述聊天消息浓缩成一条摘要消息。尽可能包含多个具体细节(名字,天气,干什么等)。"),])summarize_chain = summarize_prompt | chat_modelsummarize_result = summarize_chain.invoke({"history": stored_message})print("总结后的历史提示词:", summarize_result)temp_chat_history.clear()temp_chat_history.add_message(summarize_result)return True# 创建带消息历史管理功能的可运行对象
with_message_history = RunnableWithMessageHistory(chain,lambda session_id: temp_chat_history,input_messages_key="input",history_messages_key="history",
)# 先总结聊天历史为两条及以下,在调用大模型
chat_summarize_message = (RunnablePassthrough.assign(messages_summarized=summarize_message)| with_message_history
)result = chat_summarize_message.invoke({"input": "名字 天气 下午在干嘛"},config={"configurable": {"session_id": "unused"}},
)print("总结后一次回答三个问题:", result.content)
print("==========================")
print("总结后一次回答三个问题:", temp_chat_history.messages)

     上述代码中提示词有一定的指向性,由于大模型用的是低版本,不能很好的给出预期答案。运行很多次,挑了一个看起来还不错的结果:

总结后的历史提示词: content='今天是晴朗的天色,我叫Mark,将白日抽空打台球。' additional_kwargs={} response_metadata={'finish_reason': 'stop', 'model_name': 'llama3.2', 'system_fingerprint': 'fp_ollama'} id='run-470f3415-0c58-4588-9b42-5c159bb63c27-0'
总结后一次回答三个问题: 我是Mark,下午正在打台球,有点累了,然后去吃晚饭。我喜欢从事有趣且充满挑战性的活动。
==========================
总结后一次回答三个问题: [AIMessage(content='今天是晴朗的天色,我叫Mark,将白日抽空打台球。', additional_kwargs={}, response_metadata={'finish_reason': 'stop', 'model_name': 'llama3.2', 'system_fingerprint': 'fp_ollama'}, id='run-470f3415-0c58-4588-9b42-5c159bb63c27-0'), HumanMessage(content='名字 天气 下午在干嘛', additional_kwargs={}, response_metadata={}), AIMessage(content='我是Mark,下午正在打台球,有点累了,然后去吃晚饭。我喜欢从事有趣且充满挑战性的活动。', additional_kwargs={}, response_metadata={'finish_reason': 'stop', 'model_name': 'llama3.2', 'system_fingerprint': 'fp_ollama'}, id='run-f3b95a2b-497a-4763-9f3c-c2352424fccf-0')]

       请注意,再次调用链式模型会生成一个新的摘要,该摘要包括初始摘要以及新的消息等。您还可以设计一种混合方法,其中一定数量的消息保留在聊天历史记录中,而其他消息则被摘要。

       留给各位看官老爷自己发挥吧。

相关文章:

吃透LangChain(四):消息管理与聊天历史存储

消息存储在内存 下面我们展示一个简单的示例&#xff0c;其中聊天历史保存在内存中&#xff0c;此处通过全局 Python 字典实现。我们构建一个名为 get_session_history 的可调用对象&#xff0c;引用此字典以返回chatMessageHistory实例。通过在运行时向 RunnablewithMessageHi…...

【差分隐私相关概念】瑞丽差分隐私(RDP)命题4

命题4的证明详解&#xff08;分情况讨论&#xff09; 背景与设定 机制&#xff1a; f : D → R f: \mathcal{D} \to \mathcal{R} f:D→R 是由 n n n 个 ϵ \epsilon ϵ-差分隐私机制自适应组合而成。相邻输入&#xff1a; D D D 和 D ′ D D′ 是相邻数据集。目标&#xf…...

RoBoflow数据集的介绍

https://public.roboflow.com/object-detection&#xff08;该数据集的网址&#xff09; 可以看到一些基本情况 如果我们想要下载&#xff0c;直接点击 点击图像可以看到一些基本情况 可以点击红色箭头所指&#xff0c;右边是可供选择的一些yolo模型的格式 如果你想下载…...

免费将AI生成图像放大4倍的方法

有些人不需要任何高级工具和花哨的技巧;他们只需要一种简单的方法来提升图像分辨率而不损失任何质量 — 今天,我们将学习如何做到这一点。 生成AI图像最大的问题之一是什么?最终结果通常分辨率非常低。 这会导致很多不同的问题,特别是对于那些想要在内容或项目中使用这些…...

滑动过期机制——延长 Token有效期

文章目录 1. Flask 后端代码&#xff08;支持 WebSocket&#xff09;2. Android Studio Java 前端代码&#xff08;使用 Socket.IO&#xff09;代码说明后端前端 注意事项 前端使用 Android Studio&#xff08;Java&#xff09;和 Socket.IO 库&#xff0c;后端使用 Flask。 1…...

《JVM考古现场(二十三):归零者·重启奇点的终极奥义》

目录 楔子&#xff1a;归零者文明觉醒 上卷十维弦理论破译 第一章&#xff1a;JVM弦论代码考古 第二章&#xff1a;超膜引用解析算法 第三章&#xff1a;量子真空涨落监控 中卷归零者心法实战 第四章&#xff1a;宇宙重启倒计时引擎 第五章&#xff1a;内存奇点锻造术 第…...

k8s中sidecar死循环

序言 怎么发现我的同事们很上进呢&#xff0c;估计做了下贱的事儿吧。 伤不到我&#xff0c;不代表不疼&#xff01; sidecar产生的问题 1 背景 在k8s的环境中&#xff0c;pod的使用越来越多了&#xff0c;也就产生了sidecar容器&#xff0c;在现在的环境中&#xff0c;一个pod…...

Linux `init 4` 相关命令的完整使用指南

Linux init 4 相关命令的完整使用指南—目录 一、init 系统简介二、init 4 的含义与作用三、不同 Init 系统下的 init 4 行为1. SysVinit&#xff08;如 CentOS 6、Debian 7&#xff09;2. systemd&#xff08;如 CentOS 7、Ubuntu 16.04&#xff09;3. Upstart&#xff08;如 …...

Java Web 之 简介 100问

DAO 层的作用是什么&#xff1f; DAO 层作用&#xff1a; 与数据库直接交互&#xff0c;封装所有数据访问的细节&#xff08;即CRUD操作&#xff09;&#xff0c;不包含业务逻辑&#xff0c;只关注数据的持久化。 DAO的全拼是什么 Data Access Object&#xff0c;数据连接实…...

06-libVLC的视频播放器:推流RTMP

创建媒体对象 libvlc_media_t* m = libvlc_media_new_path(m_pInstance, inputPath.toStdString().c_str()); if (!m) return -1; // 创建失败返回错误 libvlc_media_new_path:根据文件路径创建媒体对象。注意:toStdString().c_str() 在Qt中可能存在临时字符串析构问题,建议…...

【物联网】基于LORA组网的远程环境监测系统设计

基于LORA组网的远程环境监测系统设计 演示视频: 简介: 1.本系统有一个主机,两个从机。 2.一主多从的LORA组网通信,主机和两个从机都配备了STM32F103单片机与 LoRa 模块,主机作为中心设备及WIFI网关,负责接收和发送数据到远程物联网平台和手机APP,两个从机则负责采集数…...

少儿编程路线规划

少儿编程路线规划—一文写明白 现在有很多的编程机构&#xff0c;五花八门的。我有幸也见识到了大家的营销策略。这些策略有黑有白吧&#xff0c;从业几年&#xff0c;沉淀下来一些客户角度的干货&#xff0c;分享给大家。 如果是想以很远很远的就业为目的&#xff0c;毕业就…...

第3章 垃圾收集器与内存分配策略《深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)》

第3章 垃圾收集器与内存分配策略 3.2 对象已死 Java世界中的所有对象实例&#xff0c;垃圾收集器进行回收前就是确定对象哪些是活着的&#xff0c;哪些已经死去。 3.2.1 引用计数算法 常见的回答是&#xff1a;给对象中添加一个引用计数器&#xff0c;有地方引用&#xff0…...

Docker Overlay 网络的核心工作(以跨节点容器通信为例)

Docker 的 overlay 网络是一种基于 VXLAN&#xff08;Virtual Extensible LAN&#xff09;的多主机网络模式&#xff0c;专为 Docker Swarm 集群设计&#xff0c;用于实现跨节点的容器通信。它通过虚拟二层网络&#xff0c;允许容器在不同主机上像在同一局域网内一样通信。Dock…...

用 R 语言打造交互式叙事地图:讲述黄河源区生态变化的故事

目录 🌟 项目背景:黄河源头的生态变迁 🧰 技术栈介绍 🗺️ 最终效果预览 💻 项目构建步骤 1️⃣ 数据准备 2️⃣ 构建 Leaflet 地图 3️⃣ 使用 scrollama 实现滚动触发事件 4️⃣ 使用 R Markdown / Quarto 打包发布 🎬 效果展示截图 📦 完整代码仓库 …...

Java Stream常见误区解析:五大错误与规避方法

Java Stream API以函数式编程风格提供了一种强大的数据处理方式&#xff0c;使代码更简洁和可读。然而&#xff0c;误用Stream可能导致性能低下、错误频发或代码难以维护。本文将探讨开发者在使用Java Stream时最常见的五种错误&#xff0c;并提供规避方法。 1. 在Stream处理中…...

【树莓派Pico FreeRTOS】-中断服务与二值信号量

中断服务与二值信号量 RP2040 由 Raspberry Pi 设计,具有双核 Arm Cortex-M0+ 处理器和 264KB 内部 RAM,并支持高达 16MB 的片外闪存。 广泛的灵活 I/O 选项包括 I2C、SPI 和独特的可编程 I/O (PIO)。 FreeRTOS 由 Real Time Engineers Ltd. 独家拥有、开发和维护。FreeRTO…...

构建灵活可扩展的接口抽象层:支持多种后端数据存取的最佳实践

构建灵活可扩展的接口抽象层:支持多种后端数据存取的最佳实践 在现代应用开发中,后端数据存取的需求可能非常多样化:本地数据库、云存储服务、REST API,甚至是文件系统。因此,设计一套支持多种后端数据存取的接口抽象层是提高系统灵活性和可维护性的关键。本文将详细探讨…...

Scade 语言词法介绍

Scade 6 是一种具备形式化语法与形式化语义的领域特定语言&#xff08;注1&#xff09;。自2008年发布&#xff08;注5&#xff09;起&#xff0c;在 Scade Suite 产品系列中语言定义方面到目前未产生重要的改变(注2)。在下面的内容中将介绍Scade 语言的词法(注3)。 注1&#x…...

如何配置环境变量HADOOP_HOMEM、AVEN_HOME?不配置会怎么样

以下是在不同操作系统中配置 HADOOP_HOME 和 JAVA_HOME 环境变量的方法&#xff0c;以及不配置可能产生的后果&#xff1a; 配置 HADOOP_HOME - Windows系统&#xff1a;下载并解压Hadoop安装包&#xff0c;然后右键“此电脑”&#xff0c;选择“属性”&#xff0c;点击“高级…...

YOLO学习笔记 | 基于YOLOv8的植物病害检测系统

以下是基于YOLOv8的植物病害检测系统完整技术文档,包含原理分析、数学公式推导及代码实现框架。 基于YOLOv8的智能植物病害检测系统研究 摘要 针对传统植物病害检测方法存在的效率低、泛化性差等问题,本研究提出一种基于改进YOLOv8算法的智能检测系统。通过设计轻量化特征提…...

在已有的vue项目中使用vuex

介绍 Vuex 是一个用于 Vue.js 应用程序的状态管理模式 库。它充当应用程序中所有组件的集中存储&#xff0c;其规则确保状态只能以可预测的方式进行更改。 专门在vue中实现集中式状态&#xff08;数据&#xff09;管理的一个插件对vue应用中多个组件的共享状态进行集中式的管…...

基于uniapp的鸿蒙APP大数据量性能优化

文章目录 一、问题诊断与性能瓶颈分析1.1 大数据场景下的典型性能问题1.2 性能监测工具使用1.2.1 HBuilderX内置分析器1.2.2 鸿蒙DevEco工具链1.2.3 自制性能埋点 二、数据加载优化方案2.1 分页加载实现&#xff08;带错误重试机制&#xff09;2.2 数据流优化策略2.2.1 数据压缩…...

C++ 面向对象关键语法详解:override、虚函数、转发调用和数组引用传参-策略模式

int A(参数...) override { return 某个对象.A(参数...);} 一.目标 本文将用一个简单的“数学运算器”例子&#xff0c;从零解释以下 C 语法特性&#xff1a; virtual 虚函数 override 重写关键字 函数体内部的“转发调用” 数组引用作为函数参数 适合初学者和希望加深…...

山东科技大学深度学习考试回忆

目录 一、填空&#xff08;五个空&#xff0c;十分&#xff09; 二、选择题(五个&#xff0c;十分&#xff09; 三、判断题&#xff08;五个&#xff0c;五分&#xff09; 四、论述题&#xff08;四个&#xff0c;四十分&#xff09; 五、计算题&#xff08;二个&#xff…...

sql server 学习计划

目标定位&#xff08;适用于开发人员、架构师、DBA&#xff09; 精通 SQL Server 的数据建模、T-SQL 编程、并发控制、性能优化、索引策略 掌握事务、锁机制、统计信息、执行计划 能独立完成复杂系统的数据库设计、调优与可用性设计 具备解决大数据量、高并发、长事务、数据…...

宇树机器狗go2—slam建图(1)点云格式

0.前言 上一篇番外文章教大家如何在宇树机器狗go2的gazebo仿真环境中实现简单的导航运动&#xff0c;本期文章会教大家如何让宇树的机器狗go2在仿真环境中进行slam建图时经常会遇到的一些点云格式&#xff0c;在后续的slam建图和slam算法解析的时候会经常与这些点云信息打交道…...

致远OA——自定义开发rest接口

文章目录 :apple: 业务流程 &#x1f34e; 业务流程 代码案例&#xff1a; https://pan.quark.cn/s/57fa808c823f 官方文档&#xff1a; https://open.seeyoncloud.com/seeyonapi/781/https://open.seeyoncloud.com/v5devCTP/39/783.html 登录系统 —— 后台管理 —— 切换系…...

No package docker-ce available问题的解决

安装docker时提示 rootk8s-node3 ~]# yum install -y docker-ce docker-ce-cli containerd.io Loaded plugins: fastestmirror Loading mirror speeds from cached hostfile * base: mirrors.aliyun.com * extras: mirrors.aliyun.com * updates: mirrors.aliyun.com No packag…...

群晖威联通飞牛等nas如何把宿主机硬盘挂接到可道云docker容器中

可道云系统是用户常用的一款面向个人用户的轻量级私有云存储工具&#xff0c;以高效管理和安全存储为核心&#xff0c;打造便捷的数字化办公体验。但是用户希望把原有其他磁盘中文件挂接到这个新系统中有很大的难度,主要是对linux文件系统理解有很大的误区,认为目录结构是固定的…...