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

news2025/4/21 5:07:26

消息存储在内存

      下面我们展示一个简单的示例,其中聊天历史保存在内存中,此处通过全局 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')]

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2339208.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【差分隐私相关概念】瑞丽差分隐私(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图像最大的问题之一是什么?最终结果通常分辨率非常低。 这会导致很多不同的问题,特别是对于那些想要在内容或项目中使用这些…

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

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

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

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

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

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

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

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

在已有的vue项目中使用vuex

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

宇树机器狗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文件系统理解有很大的误区,认为目录结构是固定的…

Uniapp:列表选择提示框

目录 一、出现场景二、效果展示三、具体使用 一、出现场景 在项目的开发过程中&#xff0c;有这样一种场景&#xff0c;就是点击按钮走后续的逻辑之前还需要选择前提条件&#xff0c;就一个条件的情况下如果使用弹出框就显示比较多余&#xff0c;列表选择提示框刚好能够满足我…

【python】django sqlite版本过低怎么办

方法一&#xff1a;下载最新版本 复制上面的内容的链接 在服务器上进行操作 wget https://sqlite.org/2025/sqlite-autoconf-3490100.tar.gz tar -zxvf sqlite-autoconf-3490100.tar.gz cd sqlite-autoconf-3490100 ./configure --prefix/usr/local make && make in…

山东大学软件学院创新项目实训开发日志(18)之对话自动生成标题设为用户第一次对话发的文字

本次功能的实现主要集中在后端&#xff0c;前端代码不用改变&#xff1a; 前端界面效果展示&#xff1a;

【机器学习】PCA-奇异值分解-上采样与下采样-傅里叶变换

1. PCA 主成分分析 主成分分析&#xff08;PCA&#xff09;是一种常用的数据降维方法。 它通过找到数据中方差最大的方向&#xff08;主成分&#xff09;&#xff0c;将原始高维数据映射到较低维空间&#xff0c;同时尽可能保留原始信息。 数学实现上&#xff0c;通常通过协方…

【day8】调用AI接口,生成自动化测试用例

1、项目结构建议 project/ ├── api_docs/ # 存放接口文档 │ └── XX系统.swagger.json ├── ai_generator/ # AI测试用例生成模块 │ └── test_case_generator.py ├── tests/ # 生成的测试用例 │ └── test_user_api.py ├── conftest.py # pytest配置 ├─…

【Tauri2】026——Tauri+Webassembly

前言 不多废话 直言的说&#xff0c;笔者看到这篇文章大佬的文章 【04】Tauri 入门篇 - 集成 WebAssembly - 知乎https://zhuanlan.zhihu.com/p/533025312尝试集成一下WebAssembly&#xff0c;直接开始 正文 准备工作 新建一个项目 安装 vite的rsw插件和rsw pnpm instal…

Notepad++中将文档格式从Windows(CR LF)转换为Unix(LF)

在Windows中用记事本写了一个.sh的Linux运行脚本&#xff0c;是无法直接在Linux中执行&#xff0c;需要首先把文本编码格式转换为Unix的&#xff0c;特别是换行符这些&#xff0c;转换步骤如下&#xff1a; 1、打开文档 在Notepad中打开需要转换的文件。 2、进入文档格式转换…

Linux常见工具如yum、vim、gcc、gdb的基本使用,以及编译过程和动静态链接的区别

目录 一、工具的本质 二、一些常用的工具 1.yum 2.vim 1&#xff09;vim的三种基本模式&#xff1a; 2&#xff09;vim的基本操作 ①命令模式下的基本操作&#xff1a; ②插入模式&#xff1a; ③底行模式&#xff1a; 3&#xff09;vim的配置&#xff1a;让他变得更好用 3.gcc…