LangChain 系列 7:实战开发一个有记忆的聊天机器人


前面已经学习了本地模型、OpenAI 兼容接口、消息、提示词模板和输出解析器,但每个示例基本都只完成一次调用。

真正的聊天机器人还要解决一个问题:用户说完第一句话后,模型能不能在第二轮继续理解前面的内容?

例如:

用户:我叫小明。
助手:你好,小明。
用户:我叫什么名字?
助手:小明。

第二个问题中没有再次出现“小明”。模型要回答正确,就必须同时收到第一轮对话。本文将前面学过的组件组合起来,完成一个支持内存历史、SQLite 持久化和历史摘要的聊天机器人。

1. 聊天机器人的整体结构

无论历史保存在内存还是 SQLite,RunnableWithMessageHistory 的职责都一样:根据 session_id 读取历史、调用聊天链,再把本轮消息写回同一个会话。

有记忆的聊天机器人数据流

这套聊天机器人的数据流可以分成五步:

  1. 用户提交当前问题。
  2. 程序根据 session_id 找到当前会话。
  3. LangChain 读取该会话的历史消息。
  4. 提示词模板把历史消息和当前问题一起交给 Qwen3。
  5. 新的用户消息和模型回答继续写入历史记录。

这里所说的“记忆”不是模型永久学会了新知识,而是应用程序在每次调用时重新把相关历史发送给模型。

2. 准备运行环境

本章代码位于:

source/_posts/llm_learning/langchain/p07_chatbot_with_memory/

进入项目并激活 Python 3.12 虚拟环境:

cd ~/Documents/git/blog/source/_posts/llm_learning
source .venv/bin/activate
python -m pip install -r requirements.txt

启动本地 Qwen3 服务:

"$VIRTUAL_ENV/bin/python" -m mlx_lm server \
  --model model \
  --host 127.0.0.1 \
  --port 18080 \
  --chat-template-args '{"enable_thinking": false}'

另开终端确认服务返回模型列表:

curl --noproxy '*' http://127.0.0.1:18080/v1/models

本文使用的核心版本为:

langchain==1.0.3
langchain-core==1.0.2
langchain-openai==1.0.1
langchain-community==0.4.1
SQLAlchemy==2.0.45

3. 从没有记忆的单轮聊天开始

先看最普通的聊天链:

# 这个文件演示最基础的单轮聊天。
# 两次调用彼此独立,因此第二个问题不会自动得到第一次调用的内容。

import httpx
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI


# 连接项目根目录已经启动的 Qwen3 OpenAI 兼容服务。
llm = ChatOpenAI(
    base_url="http://127.0.0.1:18080/v1",
    api_key="not-needed",
    model="default_model",
    temperature=0,
    max_tokens=96,
    http_client=httpx.Client(trust_env=False),
)

# 每次调用只会收到当前填入 question 的问题。
prompt = ChatPromptTemplate.from_messages(
    [
        ("system", "你是一个中文聊天助手,请简短回答。"),
        ("human", "{question}"),
    ]
)
chain = prompt | llm

first = chain.invoke({"question": "我最喜欢的水果是苹果,请记住。"})
print("第一次回答:", first.content)

# 这里没有把第一次对话传回模型,所以模型不知道“我”喜欢什么。
second = chain.invoke({"question": "我最喜欢的水果是什么?"})
print("第二次回答:", second.content)

运行:

python langchain/p07_chatbot_with_memory/01_single_turn_chatbot.py

真实输出:

第一次回答: 好的,已记住你最喜欢的水果是苹果。
第二次回答: 你最喜欢的是哪种水果呢?可以告诉我,我很想了解哦!

第一次回答中的“已记住”只是自然语言。第二次调用只有当前问题,没有第一轮消息,所以模型实际并没有得到“苹果”这个信息。

4. 使用内存保存多轮对话

4.1 给提示词增加历史消息位置

聊天提示词中需要增加一个 MessagesPlaceholder:

prompt = ChatPromptTemplate.from_messages(
    [
        ("system", "你是一个中文聊天助手,请根据历史消息简短回答。"),
        MessagesPlaceholder(variable_name="history"),
        ("human", "{input}"),
    ]
)

最终发送给模型的顺序是:

SystemMessage
历史 HumanMessage
历史 AIMessage
当前 HumanMessage

4.2 使用 RunnableWithMessageHistory

完整代码如下:

# 这个文件演示如何用 RunnableWithMessageHistory 保存内存中的多轮对话。
# 消息只存在当前 Python 进程中,脚本结束后就会消失。

import httpx
from langchain_core.chat_history import InMemoryChatMessageHistory
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_openai import ChatOpenAI


llm = ChatOpenAI(
    base_url="http://127.0.0.1:18080/v1",
    api_key="not-needed",
    model="default_model",
    temperature=0,
    max_tokens=96,
    http_client=httpx.Client(trust_env=False),
)

prompt = ChatPromptTemplate.from_messages(
    [
        ("system", "你是一个中文聊天助手,请根据历史消息简短回答。"),
        MessagesPlaceholder(variable_name="history"),
        ("human", "{input}"),
    ]
)
chain = prompt | llm

store: dict[str, InMemoryChatMessageHistory] = {}


def get_session_history(session_id: str) -> InMemoryChatMessageHistory:
    """取得指定会话的消息历史,没有时就创建一份。"""
    if session_id not in store:
        store[session_id] = InMemoryChatMessageHistory()
    return store[session_id]


chatbot = RunnableWithMessageHistory(
    chain,
    get_session_history,
    input_messages_key="input",
    history_messages_key="history",
)

xiaoming_config = {"configurable": {"session_id": "xiaoming"}}
other_config = {"configurable": {"session_id": "other-user"}}

answer1 = chatbot.invoke({"input": "我叫小明。"}, config=xiaoming_config)
print("小明第 1 轮:", answer1.content)

answer2 = chatbot.invoke({"input": "我叫什么名字?只回答名字。"}, config=xiaoming_config)
print("小明第 2 轮:", answer2.content)

answer3 = chatbot.invoke({"input": "我叫什么名字?"}, config=other_config)
print("另一个会话:", answer3.content)

真实输出:

小明第 1 轮: 你好,小明!很高兴认识你。有什么我可以帮你的吗?
小明第 2 轮: 小明
另一个会话: 你还没有告诉我你的名字呢。

这里有三个重要参数:

  • input_messages_key=”input”:当前用户输入使用哪个字段。
  • history_messages_key=”history”:历史消息应该放入提示词的哪个占位符。
  • session_id:本次调用属于哪个会话。

相同 session_id 可以读取相同历史,不同 session_id 之间互相隔离。

5. 使用 SQLite 持久化聊天历史

内存历史有一个明显问题:Python 进程停止后,字典中的消息全部消失。要让程序重新启动后仍能找到历史,可以使用 SQLChatMessageHistory。

两种历史实现都通过 session_id 隔离会话,主要差别在数据生命周期:

内存历史与 SQLite 历史的数据生命周期对比

session_id 只是历史记录的分组键,不是登录认证。实际应用仍然要由自己的用户系统生成并保护会话标识,不能直接信任浏览器随意提交的值。

5.1 创建 SQLite 历史

from pathlib import Path
from langchain_community.chat_message_histories import SQLChatMessageHistory

project_root = Path(__file__).resolve().parents[2]
database_path = project_root / "data" / "chat_history.db"
database_path.parent.mkdir(parents=True, exist_ok=True)
connection = f"sqlite:///{database_path}"


def get_session_history(session_id: str) -> SQLChatMessageHistory:
    return SQLChatMessageHistory(session_id=session_id, connection=connection)

本文使用的 langchain-community==0.4.1 推荐传入 connection=。一些旧资料使用 connection_string=,运行时会出现弃用提示。

5.2 验证重新创建聊天链后仍能回忆

完整示例位于 03_sqlite_chatbot.py。核心调用如下:

def create_chatbot() -> RunnableWithMessageHistory:
    return RunnableWithMessageHistory(
        prompt | llm,
        get_session_history,
        input_messages_key="input",
        history_messages_key="history",
    )


session_id = "sqlite-demo"
config = {"configurable": {"session_id": session_id}}
get_session_history(session_id).clear()

chatbot = create_chatbot()
first = chatbot.invoke({"input": "我家养了一只叫豆豆的猫。"}, config=config)
print("第 1 轮:", first.content)

# 重新创建对象,历史仍然从 SQLite 中读取。
chatbot = create_chatbot()
second = chatbot.invoke({"input": "我的猫叫什么名字?只回答名字。"}, config=config)
print("重新创建聊天链后的回答:", second.content)

history = get_session_history(session_id)
print("数据库消息数量:", len(history.messages))

真实输出:

第 1 轮: 豆豆真可爱,它有什么特别的地方吗?
重新创建聊天链后的回答: 豆豆
数据库消息数量: 4

两轮对话会保存两条 HumanMessage 和两条 AIMessage,所以数据库中共有 4 条消息。

6. 历史越来越长怎么办

多轮历史不能无限加入提示词。消息越多,占用的上下文越大,推理时间和内存消耗也会增加。

常见处理方式包括:

  • 只保留最近几轮消息。
  • 对较早的消息生成摘要。
  • 完整消息仍保存在数据库中,摘要只用于当前模型输入。

本文把后两种方式组合起来:旧消息生成摘要,最近消息保留原始对象,而 SQLite 继续保存全部消息。这样既能控制本次模型上下文,也不会因为生成摘要而丢失原始记录。

6.1 生成旧消息摘要

summary_prompt = ChatPromptTemplate.from_messages(
    [
        ("system", "请把下面的聊天历史压缩成一段简短事实摘要,不要遗漏姓名、喜好和地点。"),
        ("human", "{history_text}"),
    ]
)
summary_chain = summary_prompt | llm | StrOutputParser()

这里使用上一章学习的 StrOutputParser,因为摘要链只需要普通字符串,而不是带完整元数据的 AIMessage。

6.2 只压缩本次模型输入

recent_message_count = 4


def prepare_context(values: dict) -> dict:
    history = values.get("history", [])
    if len(history) <= recent_message_count:
        return {"input": values["input"], "summary": "暂无。", "history": history}

    old_messages = history[:-recent_message_count]
    recent_messages = history[-recent_message_count:]
    summary = summary_chain.invoke({"history_text": messages_to_text(old_messages)})
    print("生成的历史摘要:", summary)
    return {"input": values["input"], "summary": summary, "history": recent_messages}

完整链为:

answer_chain = RunnableLambda(prepare_context) | answer_prompt | llm

chatbot = RunnableWithMessageHistory(
    answer_chain,
    get_session_history,
    input_messages_key="input",
    history_messages_key="history",
)

执行顺序是:

  1. RunnableWithMessageHistory 从 SQLite 读取完整历史。
  2. prepare_context 把旧消息转换成摘要,只保留最近 4 条原始消息。
  3. answer_prompt 组合摘要、最近消息和当前问题。
  4. Qwen3 生成回答。
  5. RunnableWithMessageHistory 把本轮问题和回答继续写入 SQLite。

需要特别区分“模型本次看到的上下文”和“数据库实际保存的历史”:

历史摘要只压缩模型上下文而不删除 SQLite 原始消息

摘要链只处理较早的消息,回答链看到的是“摘要 + 最近消息 + 当前问题”。调用结束后,本轮完整的 HumanMessage 和 AIMessage 仍然追加到 SQLite。

6.3 真实运行结果

运行:

python langchain/p07_chatbot_with_memory/04_chatbot_with_summary.py

关键输出:

第 1 轮用户:我叫小明。
第 2 轮用户:我最喜欢的水果是苹果。
第 3 轮用户:我现在住在杭州。
生成的历史摘要: 用户小明与助手进行了初次交流,助手向小明问好并表示愿意提供帮助。
第 4 轮用户:请说出我的名字、喜欢的水果和居住城市。
第 4 轮助手:你的名字是小明,喜欢的水果是苹果,现在居住在杭州。
SQLite 中保留的完整消息数量: 8

摘要中保留了姓名,最近消息中保留了水果和城市,因此模型能回答完整。更重要的是,数据库仍然有 8 条原始消息。

7. 查看数据库中的完整历史

运行:

python langchain/p07_chatbot_with_memory/05_inspect_chat_history.py

输出中的消息顺序为:

1. HumanMessage:我叫小明。
2. AIMessage:你好,小明!很高兴认识你。
3. HumanMessage:我最喜欢的水果是苹果。
4. AIMessage:苹果确实是个很棒的选择!
5. HumanMessage:我现在住在杭州。
6. AIMessage:杭州是个非常美丽的地方。
7. HumanMessage:请说出我的名字、喜欢的水果和居住城市。
8. AIMessage:你的名字是小明,喜欢的水果是苹果,现在居住在杭州。

这说明摘要没有替换或删除数据库中的消息。

8. 本章完整代码

下面的代码与 llm_learning 项目中的脚本保持一致。

01_single_turn_chatbot.py

# 这个文件演示最基础的单轮聊天。
# 两次调用彼此独立,因此第二个问题不会自动得到第一次调用的内容。

import httpx
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI


# 连接项目根目录已经启动的 Qwen3 OpenAI 兼容服务。
llm = ChatOpenAI(
    base_url="http://127.0.0.1:18080/v1",
    api_key="not-needed",
    model="default_model",
    temperature=0,
    max_tokens=96,
    http_client=httpx.Client(trust_env=False),
)

# 每次调用只会收到当前填入 question 的问题。
prompt = ChatPromptTemplate.from_messages(
    [
        ("system", "你是一个中文聊天助手,请简短回答。"),
        ("human", "{question}"),
    ]
)
chain = prompt | llm

first = chain.invoke({"question": "我最喜欢的水果是苹果,请记住。"})
print("第一次回答:", first.content)

# 这里没有把第一次对话传回模型,所以模型不知道“我”喜欢什么。
second = chain.invoke({"question": "我最喜欢的水果是什么?"})
print("第二次回答:", second.content)

02_in_memory_chatbot.py

# 这个文件演示如何用 RunnableWithMessageHistory 保存内存中的多轮对话。
# 消息只存在当前 Python 进程中,脚本结束后就会消失。

import httpx
from langchain_core.chat_history import InMemoryChatMessageHistory
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_openai import ChatOpenAI


llm = ChatOpenAI(
    base_url="http://127.0.0.1:18080/v1",
    api_key="not-needed",
    model="default_model",
    temperature=0,
    max_tokens=96,
    http_client=httpx.Client(trust_env=False),
)

# history 会在运行时被替换成当前会话的历史消息列表。
prompt = ChatPromptTemplate.from_messages(
    [
        ("system", "你是一个中文聊天助手,请根据历史消息简短回答。"),
        MessagesPlaceholder(variable_name="history"),
        ("human", "{input}"),
    ]
)
chain = prompt | llm

# 字典中的每个 session_id 对应一份独立的内存历史。
store: dict[str, InMemoryChatMessageHistory] = {}


def get_session_history(session_id: str) -> InMemoryChatMessageHistory:
    """取得指定会话的消息历史,没有时就创建一份。"""
    if session_id not in store:
        store[session_id] = InMemoryChatMessageHistory()
    return store[session_id]


chatbot = RunnableWithMessageHistory(
    chain,
    get_session_history,
    input_messages_key="input",
    history_messages_key="history",
)

xiaoming_config = {"configurable": {"session_id": "xiaoming"}}
other_config = {"configurable": {"session_id": "other-user"}}

answer1 = chatbot.invoke({"input": "我叫小明。"}, config=xiaoming_config)
print("小明第 1 轮:", answer1.content)

answer2 = chatbot.invoke({"input": "我叫什么名字?只回答名字。"}, config=xiaoming_config)
print("小明第 2 轮:", answer2.content)

# 换一个 session_id 后,不会读取小明的历史消息。
answer3 = chatbot.invoke({"input": "我叫什么名字?"}, config=other_config)
print("另一个会话:", answer3.content)

03_sqlite_chatbot.py

# 这个文件演示如何把聊天历史保存到 SQLite。
# 即使重新创建聊天链,只要 session_id 相同,就能继续读取原来的消息。

from pathlib import Path

import httpx
from langchain_community.chat_message_histories import SQLChatMessageHistory
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_openai import ChatOpenAI


# 数据库统一放在 llm_learning/data 目录。
project_root = Path(__file__).resolve().parents[2]
database_path = project_root / "data" / "chat_history.db"
database_path.parent.mkdir(parents=True, exist_ok=True)
connection = f"sqlite:///{database_path}"

llm = ChatOpenAI(
    base_url="http://127.0.0.1:18080/v1",
    api_key="not-needed",
    model="default_model",
    temperature=0,
    max_tokens=96,
    http_client=httpx.Client(trust_env=False),
)

prompt = ChatPromptTemplate.from_messages(
    [
        ("system", "你是一个中文聊天助手,请根据历史消息简短回答。"),
        MessagesPlaceholder(variable_name="history"),
        ("human", "{input}"),
    ]
)


def get_session_history(session_id: str) -> SQLChatMessageHistory:
    """从 SQLite 中取得指定 session_id 的历史消息。"""
    return SQLChatMessageHistory(session_id=session_id, connection=connection)


def create_chatbot() -> RunnableWithMessageHistory:
    """创建一个会自动读取和写入 SQLite 历史的聊天链。"""
    return RunnableWithMessageHistory(
        prompt | llm,
        get_session_history,
        input_messages_key="input",
        history_messages_key="history",
    )


session_id = "sqlite-demo"
config = {"configurable": {"session_id": session_id}}

# 清理同名演示会话,让脚本每次运行都得到清晰结果。
get_session_history(session_id).clear()

chatbot = create_chatbot()
first = chatbot.invoke({"input": "我家养了一只叫豆豆的猫。"}, config=config)
print("第 1 轮:", first.content)

# 模拟程序重新创建聊天链,数据库中的历史仍然存在。
chatbot = create_chatbot()
second = chatbot.invoke({"input": "我的猫叫什么名字?只回答名字。"}, config=config)
print("重新创建聊天链后的回答:", second.content)

history = get_session_history(session_id)
print("数据库文件:", database_path)
print("数据库消息数量:", len(history.messages))

04_chatbot_with_summary.py

# 这个文件演示如何在历史较长时生成摘要。
# SQLite 始终保留完整历史,只有发送给模型的上下文会被压缩。

from pathlib import Path

import httpx
from langchain_community.chat_message_histories import SQLChatMessageHistory
from langchain_core.messages import BaseMessage
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.runnables import RunnableLambda
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_openai import ChatOpenAI


project_root = Path(__file__).resolve().parents[2]
database_path = project_root / "data" / "chat_history.db"
database_path.parent.mkdir(parents=True, exist_ok=True)
connection = f"sqlite:///{database_path}"

llm = ChatOpenAI(
    base_url="http://127.0.0.1:18080/v1",
    api_key="not-needed",
    model="default_model",
    temperature=0,
    max_tokens=128,
    http_client=httpx.Client(trust_env=False),
)

# 旧消息超过限制后,只保留最近 4 条原始消息。
recent_message_count = 4

summary_prompt = ChatPromptTemplate.from_messages(
    [
        ("system", "请把下面的聊天历史压缩成一段简短事实摘要,不要遗漏姓名、喜好和地点。"),
        ("human", "{history_text}"),
    ]
)
summary_chain = summary_prompt | llm | StrOutputParser()

answer_prompt = ChatPromptTemplate.from_messages(
    [
        ("system", "你是中文聊天助手。历史摘要:{summary}"),
        MessagesPlaceholder(variable_name="history"),
        ("human", "{input}"),
    ]
)


def messages_to_text(messages: list[BaseMessage]) -> str:
    """把消息对象转换成便于摘要模型阅读的文本。"""
    lines = []
    for message in messages:
        role = "用户" if message.type == "human" else "助手"
        lines.append(f"{role}{message.content}")
    return "\n".join(lines)


def prepare_context(values: dict) -> dict:
    """生成旧消息摘要,并保留最近几条原始消息。"""
    history = values.get("history", [])
    if len(history) <= recent_message_count:
        return {"input": values["input"], "summary": "暂无。", "history": history}

    old_messages = history[:-recent_message_count]
    recent_messages = history[-recent_message_count:]
    summary = summary_chain.invoke({"history_text": messages_to_text(old_messages)})
    print("生成的历史摘要:", summary)
    return {"input": values["input"], "summary": summary, "history": recent_messages}


def get_session_history(session_id: str) -> SQLChatMessageHistory:
    """从 SQLite 中取得完整历史。"""
    return SQLChatMessageHistory(session_id=session_id, connection=connection)


# RunnableWithMessageHistory 先注入完整历史,再由 prepare_context 压缩模型输入。
answer_chain = RunnableLambda(prepare_context) | answer_prompt | llm
chatbot = RunnableWithMessageHistory(
    answer_chain,
    get_session_history,
    input_messages_key="input",
    history_messages_key="history",
)

session_id = "summary-demo"
config = {"configurable": {"session_id": session_id}}
get_session_history(session_id).clear()

questions = [
    "我叫小明。",
    "我最喜欢的水果是苹果。",
    "我现在住在杭州。",
    "请说出我的名字、喜欢的水果和居住城市。",
]

for number, question in enumerate(questions, start=1):
    answer = chatbot.invoke({"input": question}, config=config)
    print(f"第 {number} 轮用户:{question}")
    print(f"第 {number} 轮助手:{answer.content}")

full_history = get_session_history(session_id)
print("SQLite 中保留的完整消息数量:", len(full_history.messages))

05_inspect_chat_history.py

# 这个文件用于查看 SQLite 中保存的完整聊天历史。
# 默认查看 04_chatbot_with_summary.py 创建的 summary-demo 会话。

import argparse
from pathlib import Path

from langchain_community.chat_message_histories import SQLChatMessageHistory


parser = argparse.ArgumentParser(description="查看指定 session_id 的聊天历史")
parser.add_argument("session_id", nargs="?", default="summary-demo")
args = parser.parse_args()

project_root = Path(__file__).resolve().parents[2]
database_path = project_root / "data" / "chat_history.db"
connection = f"sqlite:///{database_path}"

history = SQLChatMessageHistory(session_id=args.session_id, connection=connection)
print("会话 ID:", args.session_id)
print("消息数量:", len(history.messages))

for number, message in enumerate(history.messages, start=1):
    print(f"{number}. {type(message).__name__}{message.content}")

9. 常见问题

9.1 模型仍然记不住上一轮

依次检查:

  • 提示词中是否包含 MessagesPlaceholder(“history”)。
  • history_messages_key 是否也是 history。
  • 两次调用是否使用相同 session_id。
  • 是否通过 RunnableWithMessageHistory 调用,而不是直接调用原始 chain。

9.2 不同用户看到了相同历史

通常是所有请求都使用了同一个固定 session_id。实际页面应为每个用户或浏览器会话生成独立值,下一篇 Gradio 文章会处理这个问题。

9.3 重启后历史消失

InMemoryChatMessageHistory 只存在内存中。需要跨进程保存时,应改用 SQLChatMessageHistory 或其他持久化历史实现。

9.4 摘要后数据库消息减少

不要为了写入摘要而调用 clear() 清空正式会话,也不要用一条摘要消息覆盖原始消息。正确做法是完整历史继续留在数据库中,只在构造本次模型输入时使用摘要。

9.5 本地接口请求出现 502

/v1/models 可以访问,只能证明 HTTP 服务已经启动并能返回模型列表。如果聊天请求返回 502,应先检查 MLX-LM Server 终端中的模型加载或推理错误,再使用最小 /v1/chat/completions 请求确认聊天接口是否正常。

只有直接请求聊天接口成功、Python 调用仍然失败时,才继续检查客户端是否读取了系统代理。本章使用:

http_client=httpx.Client(trust_env=False)

它可以防止本地请求被系统代理或 VPN 转发,但不能修复模型服务端的加载或推理异常。

10. 小结

这一章把前面学习的内容第一次组合成了完整功能:

  • ChatOpenAI 连接本地 Qwen3。
  • ChatPromptTemplate 组织系统消息、历史消息和当前问题。
  • MessagesPlaceholder 为历史消息预留位置。
  • RunnableWithMessageHistory 自动读取和写入历史。
  • session_id 隔离不同会话。
  • SQLChatMessageHistory 将消息持久化到 SQLite。
  • StrOutputParser 提取历史摘要文本。
  • RunnableLambda 在回答前整理模型上下文。
知识点 作用 本文返回或保存的内容
InMemoryChatMessageHistory 保存当前进程中的消息 BaseMessage 列表
SQLChatMessageHistory 将消息持久化到 SQLite 完整用户消息和模型消息
MessagesPlaceholder 在提示词中插入历史 多条消息对象
RunnableWithMessageHistory 自动读取、注入和写入历史 包装后的 Runnable
session_id 区分不同会话 字符串会话标识
StrOutputParser 提取摘要字符串 str
历史摘要 压缩发送给模型的旧上下文 摘要 + 最近消息

文章作者: hnbian
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 hnbian !
评论
  目录