目标

实现多轮对话记忆 + 实时Token消耗统计,完成基础对话优化。


后端任务

  1. 集成 LangChain 对话记忆 ✅️
  2. 接入Token计数工具 ✅️
  3. 统计:
    • 单次输入Token ✅️
    • 单次输出Token ✅️
    • 历史总Token ✅️
  4. 接口返回:回答内容 + Token统计数据 ✅️

多轮对话记忆

为什么多轮对话不用手写历史,而是用 LangChain 记忆组件

核心认知

AI 多轮对话的本质是将历史对话内容拼接至提示词中,让大模型获取上下文信息。

单纯通过手写列表管理对话历史,也能实现基础的多轮交互效果,但仅适用于简单场景。

项目选择现成组件而非手写的原因

  • 标准化格式,规避低级 Bug
  • 灵活扩展,降低改造成本:业务迭代时,若需要实现会话截断、历史摘要、滑动窗口等能力,只需替换对应组件即可,无需改动核心业务代码,扩展性更强
  • 工程化可维护
  • 配套能力完善:可无缝对接 Token 统计、会话持久化、监控告警等线上必备能力,手写实现复杂度高

主流记忆方案说明(新版生态)

结合 LangChain 新版 LCEL 架构,目前主流采用消息历史容器 + 会话管理器的组合方案

  • InMemoryChatMessageHistory:内存型消息存储容器,完整保存每一轮用户与模型的对话消息,搭配会话 ID 实现多用户会话隔离,部署简单,适配短轮次日常对话场景。
  • 扩展方案(长对话优化):基于消息容器封装摘要、截断逻辑,把冗长历史压缩为精简摘要,有效控制整体 Token 占用,适配长时间连续对话;该方案需要额外调用模型生成摘要,会存在少量信息损耗。

结论

手写历史适合本地 Demo、简单测试。

线上项目优先使用 LangChain 官方记忆组件,自动管理对话历史,不用手搓列表、不用手动拼 Prompt,兼顾稳定性、扩展性与工程规范。


多轮对话记忆实现方案

LangChain 提供对话记忆功能,自动管理对话历史,不用手搓列表、不用手动拼 Prompt。

多轮对话记忆实现 = 消息历史容器 + 会话管理工具

  • InMemoryChatMessageHistory
    • 内存消息容器,保存完整对话历史。
  • RunnableWithMessageHistory
    • 会话管理工具,自动读取 / 写入历史,支持多用户隔离。
实现步骤
  1. 定义会话历史仓库

用来给每个用户独立保存聊天记录。

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]
  1. 写提示词(必须留历史位置)
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder

prompt = ChatPromptTemplate.from_messages([
    ("system", "你是智能助手"),
    MessagesPlaceholder(variable_name="history"),  # 历史自动放这里
    ("human", "{user_input}"),
])
  1. 构建基础链(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()
  1. 绑定记忆(最关键)
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"            # 历史字段
)
  1. 调用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 实现多会话隔离

扩展能力(长对话优化)
  1. 限制历史长度(窗口记忆)
  2. 历史摘要(节省 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 流式返回的完整回答文本
  • 计算方式
    1. 流式接收所有内容并拼接完整
    2. 用同一个 tokenizer 直接编码完整文本
    3. 编码结果长度 = 输出 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”,确保样式正确。
加载完成后,需自动滚动到最新消息位置,保持用户体验连贯。


前端任务

  1. 聊天界面显示多轮对话历史 ✅️(相关内容放在后端任务部分)
  2. 增加统计面板:显示本次/累计Token消耗 ✅️
  3. 清空对话功能 ✅️

清空对话

功能描述

用户点击「清空对话」按钮后:

  1. 前端清空聊天界面,消息列表恢复为空
  2. 前端重置 session_id(生成新的 UUID)
  3. 前端清空本地缓存(token 统计)
  4. 后端无需处理(依赖 session_id 变更自动隔离新旧对话)

交付成果

✅ 多轮连贯对话
✅ 实时Token统计
✅ 对话清空/管理


运行效果

多轮对话记忆功能

在这里插入图片描述

删除缓存,刷新页面
在这里插入图片描述


接口返回token

接口返回token,包括输入token,输出token和历史总token.
在这里插入图片描述


页面增加token看板

在这里插入图片描述


多轮对话历史恢复

手动把页面的对话删除
在这里插入图片描述
刷新页面后,历史会话恢复

在这里插入图片描述


清空对话

正常聊天
在这里插入图片描述

清空对话

在这里插入图片描述

验证是否开启新对话
在这里插入图片描述

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐