第 17 章:会话记忆和多轮对话

0 阅读2分钟

第 17 章:会话记忆和多轮对话

本章目标

这一章让知识库助手支持多轮对话。用户可以继续追问,系统能理解上下文。

为什么需要记忆

用户不会每次都问完整问题。

第一轮:

报销超过 5000 元要怎么审批?

第二轮:

那差旅费也是这样吗?

没有会话记忆时,模型不知道“这样”指什么。

最简单的短期记忆

前端保存消息历史,并传给后端:

const messages = [
  { role: "user", content: "报销超过 5000 元要怎么审批?" },
  { role: "assistant", content: "需要部门负责人审批。" },
  { role: "user", content: "那差旅费也是这样吗?" }
];

后端调用:

const result = await model.invoke([
  { role: "system", content: KB_ASSISTANT_SYSTEM_PROMPT },
  ...messages
]);

问题改写

RAG 场景里,多轮追问最好先改写成独立问题。

原问题:那差旅费也是这样吗?
改写后:差旅费超过 5000 元是否也需要部门负责人审批?

可以用模型做问题改写:

const rewrite = await model.invoke([
  {
    role: "system",
    content: "你负责把多轮对话中的最后一个问题改写成独立问题。只输出改写后的问题。"
  },
  ...messages
]);

然后用改写后的问题去检索知识库。

消息截断

不能无限传历史。可以保留最近 N 条:

export function trimMessages<T>(messages: T[], maxMessages = 12) {
  return messages.slice(-maxMessages);
}

更高级的做法是对旧消息做摘要。

会话 ID

生产环境需要会话 ID:

export interface ChatSession {
  id: string;
  userId: string;
  title: string;
  createdAt: number;
  updatedAt: number;
}

每条消息关联 session:

export interface StoredMessage {
  id: string;
  sessionId: string;
  role: "user" | "assistant";
  content: string;
  createdAt: number;
}

小册第一版可以先用前端状态,后面再接数据库。

实战任务

完成:

  • 多轮消息传递
  • 最近 12 条消息截断
  • 追问改写函数
  • 用改写问题做 RAG 检索
  • 页面支持连续追问

常见坑

不要把所有历史都塞给模型。成本和延迟会快速上升。

不要直接用追问做检索。追问通常缺主语,检索效果会差。

不要把会话记忆当成永久知识库。用户聊天历史和企业文档是两类数据。

本章小结

多轮对话让知识库助手更接近真实产品。下一章进入 LangGraph,学习如何把 Agent 流程变得更可控。