LangChain 实践4 7-2 对话记忆 + Token统计
目标
实现多轮对话记忆 + 实时Token消耗统计,完成基础对话优化。
后端任务
- 集成 LangChain 对话记忆 ✅️
- 接入Token计数工具 ✅️
- 统计:
- 单次输入Token ✅️
- 单次输出Token ✅️
- 历史总Token ✅️
- 接口返回:回答内容 + Token统计数据 ✅️
多轮对话记忆
为什么多轮对话不用手写历史,而是用 LangChain 记忆组件
核心认知
AI 多轮对话的本质是将历史对话内容拼接至提示词中,让大模型获取上下文信息。
单纯通过手写列表管理对话历史,也能实现基础的多轮交互效果,但仅适用于简单场景。
项目选择现成组件而非手写的原因
- 标准化格式,规避低级 Bug
- 灵活扩展,降低改造成本:业务迭代时,若需要实现会话截断、历史摘要、滑动窗口等能力,只需替换对应组件即可,无需改动核心业务代码,扩展性更强
- 工程化可维护
- 配套能力完善:可无缝对接 Token 统计、会话持久化、监控告警等线上必备能力,手写实现复杂度高
主流记忆方案说明(新版生态)
结合 LangChain 新版 LCEL 架构,目前主流采用消息历史容器 + 会话管理器的组合方案
- InMemoryChatMessageHistory:内存型消息存储容器,完整保存每一轮用户与模型的对话消息,搭配会话 ID 实现多用户会话隔离,部署简单,适配短轮次日常对话场景。
- 扩展方案(长对话优化):基于消息容器封装摘要、截断逻辑,把冗长历史压缩为精简摘要,有效控制整体 Token 占用,适配长时间连续对话;该方案需要额外调用模型生成摘要,会存在少量信息损耗。
结论
手写历史适合本地 Demo、简单测试。
线上项目优先使用 LangChain 官方记忆组件,自动管理对话历史,不用手搓列表、不用手动拼 Prompt,兼顾稳定性、扩展性与工程规范。
多轮对话记忆实现方案
LangChain 提供对话记忆功能,自动管理对话历史,不用手搓列表、不用手动拼 Prompt。
多轮对话记忆实现 = 消息历史容器 + 会话管理工具
- InMemoryChatMessageHistory
- 内存消息容器,保存完整对话历史。
- RunnableWithMessageHistory
- 会话管理工具,自动读取 / 写入历史,支持多用户隔离。
实现步骤
- 定义会话历史仓库
用来给每个用户独立保存聊天记录。
from langchain_core.chat_history import InMemoryChatMessageHistory
# 全局会话仓库:session_id → 独立聊天记录
store = {}
def get_session_history(session_id: str):
if session_id not in store:
store[session_id] = InMemoryChatMessageHistory()
return store[session_id]
- 写提示词(必须留历史位置)
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
prompt = ChatPromptTemplate.from_messages([
("system", "你是智能助手"),
MessagesPlaceholder(variable_name="history"), # 历史自动放这里
("human", "{user_input}"),
])
- 构建基础链(LCEL)
from langchain_community.chat_models import ChatTongyi
from langchain_core.output_parsers import StrOutputParser
model = ChatTongyi(model="qwen-plus")
base_chain = prompt | model | StrOutputParser()
- 绑定记忆(最关键)
from langchain_core.runnables.history import RunnableWithMessageHistory
chain = RunnableWithMessageHistory(
base_chain,
get_session_history=get_session_history, # 绑定会话管理
input_messages_key="user_input", # 输入字段
history_messages_key="history" # 历史字段
)
- 调用LangChain链带session_id
# 同一 session_id 会记住历史
config = {"configurable": {"session_id": "user_123"}}
# 第一轮
chain.invoke({"user_input": "我叫瑶瑶"}, config=config)
# 第二轮 → 自动记住名字
chain.invoke({"user_input": "我是谁?"}, config=config)
session_id由前端统一生成和管理,相关内容见LangChain 多轮对话记忆:基于 session_id 实现多会话隔离
扩展能力(长对话优化)
- 限制历史长度(窗口记忆)
- 历史摘要(节省 Token)
与旧版记忆组件区别
-
旧版:ConversationBufferMemory + ConversationChain(已废弃)
-
新版:InMemoryChatMessageHistory + RunnableWithMessageHistory(当前标准)
-
新版支持多用户、可持久化、可扩展、不报错、适配 LCEL
价值
不用手搓历史,这就是 LangChain 记忆工具的价值。
实时Token消耗统计
为何进行 Token 统计
在 AI 应用开发中,仅依靠大模型接口返回 Token 数据存在明显短板。
接口的 Token 统计属于事后结果,调用完成后才能获取,无法在请求发起前预判文本、对话历史是否超出模型上下文上限,容易触发接口报错、产生无效扣费。
流式输出场景下接口甚至不会返回 Token 统计数据,且部分模型返回数值存在偏差。
本地提前精准计算 Token 数量,既能提前做文本截断、对话历史裁剪、长文本分块等预处理,规避超限问题与资源浪费,也能稳定支撑计费、风控、RAG 分块等业务逻辑,是线上 AI 项目保障稳定性、控制成本的必备工具。
如何统计Token
针对通义千问统计 token,必须使用 Qwen 官方 SentencePiece 分词器(dashscope 内置),只有官方工具才能保证计数精准、与 API 计费口径一致,满足生产环境的可靠性要求。
dashscope SDK 内置 tokenizer
依赖:pip install dashscope
from dashscope import get_tokenizer
tokenizer = get_tokenizer("qwen-plus")
text = "你好,通义千问。"
input_tokens = len(tokenizer.encode(text))
print("本次输入 token 数量:", input_tokens)
输入 Token 计算方案
- 统计对象:系统提示 + 历史对话 + 用户当前问题
- 关键处理:必须拼接成 Qwen 官方 ChatML 格式
插入固定特殊标记:<|im_start|>+ 角色 + 内容 +<|im_end|>
最后必须追加:<|im_start|>assistant - 计算方式:用通义千问 tokenizer 编码这段完整格式字符串 → 长度 = 输入 token
输出 Token 计算方案
- 统计对象:AI 流式返回的完整回答文本
- 计算方式:
- 流式接收所有内容并拼接完整
- 用同一个 tokenizer 直接编码完整文本
- 编码结果长度 = 输出 token
- 保证机制:放在
finally里,100% 会执行
历史总 Token 计算方案
- 定义:当前会话所有对话累计消耗的 token
- 公式:
历史总 token = 上一轮历史总 token + 本次输入 token + 本次输出 token - 存储:按
session_id全局保存,每次对话自动累加
多轮对话历史
后端提供基于 session_id 的会话历史查询接口,前端在页面初始化时请求并渲染历史聊天记录,实现会话刷新后的数据恢复。
数据格式约定
后端返回的聊天记录需包含以下核心字段,确保前端可直接渲染:
- content:消息文本内容
- type / role:消息角色(如 human / ai 或 user / assistant)
- (可选)id / timestamp:消息唯一标识与时间戳,用于排序和去重
前端处理逻辑
前端收到数据后,需将后端返回的 type: “human” / “ai” 映射为前端渲染所需的 role: “user” / “assistant”,确保样式正确。
加载完成后,需自动滚动到最新消息位置,保持用户体验连贯。
前端任务
- 聊天界面显示多轮对话历史 ✅️(相关内容放在后端任务部分)
- 增加统计面板:显示本次/累计Token消耗 ✅️
- 清空对话功能 ✅️
清空对话
功能描述
用户点击「清空对话」按钮后:
- 前端清空聊天界面,消息列表恢复为空
- 前端重置 session_id(生成新的 UUID)
- 前端清空本地缓存(token 统计)
- 后端无需处理(依赖 session_id 变更自动隔离新旧对话)
交付成果
✅ 多轮连贯对话
✅ 实时Token统计
✅ 对话清空/管理
运行效果
多轮对话记忆功能

删除缓存,刷新页面
接口返回token
接口返回token,包括输入token,输出token和历史总token.
页面增加token看板

多轮对话历史恢复
手动把页面的对话删除
刷新页面后,历史会话恢复

清空对话
正常聊天
清空对话

验证是否开启新对话
更多推荐



所有评论(0)