消息存储在内存
下面我们展示一个简单的示例,其中聊天历史保存在内存中,此处通过全局 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 ChatOpenAI
temp_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_model
def trim_history_message(chain_input):
stored_message = temp_chat_history.messages;
if len(stored_message) <= 2:
return False
temp_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 ChatOpenAI
temp_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_model
def summarize_message(chain_input):
stored_message = temp_chat_history.messages;
if len(stored_message) == 0:
return False
summarize_prompt = ChatPromptTemplate.from_messages([
MessagesPlaceholder(variable_name="history"),
("user", "将上述聊天消息浓缩成一条摘要消息。尽可能包含多个具体细节(名字,天气,干什么等)。"),
])
summarize_chain = summarize_prompt | chat_model
summarize_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')]
请注意,再次调用链式模型会生成一个新的摘要,该摘要包括初始摘要以及新的消息等。您还可以设计一种混合方法,其中一定数量的消息保留在聊天历史记录中,而其他消息则被摘要。
留给各位看官老爷自己发挥吧。