欢迎光临
我们一直在努力

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)
    未经允许不得转载:171主机测评 » LangChain 实践4 7-2 对话记忆 + Token统计
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址