第 5 篇

让 AI Demo 更像真实产品:历史记录与多轮对话怎么做?

前面我们已经做了一个最小 AI 学习助手。

但它还有一个明显问题:

每次提问都是独立的,AI 不知道前面聊过什么。

这篇来解决这个问题:给 Demo 加上历史记录和多轮对话。


一、单轮问答的问题

单轮问答的流程是:

用户提问

调用 AI API

返回回答

这种方式适合简单工具,但不像真实聊天产品。

比如用户第一轮问:

什么是数据库索引?

AI 回答后,用户继续问:

那它为什么能提高查询速度?

如果没有上下文,AI 不一定知道“它”指的是数据库索引。

所以我们需要保存历史消息。

二、多轮对话的基本原理

多轮对话并不神秘。

核心就是:

  1. 保存用户问题;
  2. 保存 AI 回答;
  3. 下一次请求时,把最近几轮历史一起发给模型;
  4. 模型基于上下文继续回答。

流程如下:

用户发送新问题

读取会话历史

拼接 messages

调用 Chao AI API

返回 AI 回答

保存本轮问答

返回给前端展示

三、消息数据结构

一个最简单的消息结构可以这样设计:

{
  conversationId: "会话 ID",
  role: "user 或 assistant",
  content: "消息内容",
  createdAt: "创建时间"
}

字段说明:

字段 含义
conversationId 标识属于哪个会话
role 消息角色,用户或 AI
content 消息内容
createdAt 创建时间

四、先用内存保存历史

正式项目可以用数据库,但学习阶段可以先用内存模拟。

创建 memory.js:

const conversations = {};

function getMessages(conversationId) {
  return conversations[conversationId] || [];
}

function addMessage(conversationId, message) {
  if (!conversations[conversationId]) {
    conversations[conversationId] = [];
  }

  conversations[conversationId].push({
    ...message,
    createdAt: new Date().toISOString()
  });
}

function getRecentMessages(conversationId, limit = 6) {
  const messages = getMessages(conversationId);
  return messages.slice(-limit);
}

module.exports = {
  getMessages,
  addMessage,
  getRecentMessages
};

这里的 limit = 6 表示只保留最近 6 条消息参与上下文。

这样可以避免上下文太长。

五、改造后端接口

在 server.js 中引入:

const {
  addMessage,
  getRecentMessages
} = require("./memory");

新增聊天接口:

app.post("/api/chat", async (req, res) => {
  try {
    const { conversationId, message } = req.body;

    if (!conversationId || !message) {
      return res.status(400).json({
        error: "conversationId 和 message 不能为空"
      });
    }

    const history = getRecentMessages(conversationId, 6);

    const messages = [
      {
        role: "system",
        content: "你是一个适合大学生使用的 AI 学习助手,请结合上下文回答。"
      },
      ...history.map(item => ({
        role: item.role,
        content: item.content
      })),
      {
        role: "user",
        content: message
      }
    ];

    const response = await fetch(process.env.CHAO_API_BASE_URL, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "Authorization": `Bearer ${process.env.CHAO_API_KEY}`
      },
      body: JSON.stringify({
        model: process.env.CHAO_MODEL,
        messages
      })
    });

    const data = await response.json();

    const answer = JSON.stringify(data);

    addMessage(conversationId, {
      role: "user",
      content: message
    });

    addMessage(conversationId, {
      role: "assistant",
      content: answer
    });

    res.json({
      conversationId,
      answer
    });
  } catch (error) {
    console.error(error);
    res.status(500).json({
      error: "多轮对话调用失败"
    });
  }
});

六、前端如何生成 conversationId

前端可以简单生成一个会话 ID:

let conversationId = localStorage.getItem("conversationId");

if (!conversationId) {
  conversationId = `conv_${Date.now()}`;
  localStorage.setItem("conversationId", conversationId);
}

发送消息时带上:

await fetch("http://localhost:3000/api/chat", {
  method: "POST",
  headers: {
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    conversationId,
    message: userInput
  })
});

这样后端就知道这条消息属于哪个会话。

七、为什么不能无限保存上下文

很多初学者会想:既然要多轮对话,那我是不是把所有历史都发给模型?

不建议。

原因有三个:

  1. 上下文太长会增加成本;
  2. 太多无关历史会影响回答质量;
  3. 请求体过大可能导致调用失败。

常见做法:

  • 只保留最近几轮;
  • 对更早内容做摘要;
  • 重要信息单独存储;
  • 无关内容不传给模型。

学习阶段可以先用:

getRecentMessages(conversationId, 6)

也就是最近 3 轮对话。

八、正式项目建议用数据库

内存保存有一个问题:

服务重启后,历史记录会丢失。

正式项目可以换成数据库,比如:

  • 用户表;
  • 会话表;
  • 消息表。

消息表可以这样设计:

字段 类型 说明
id string 消息 ID
conversation_id string 会话 ID
role string user / assistant
content text 消息内容
created_at datetime 创建时间

九、总结

多轮对话的本质不是模型自动记住一切,而是应用层帮它管理上下文。

核心步骤:

  1. 保存历史消息;
  2. 读取最近几轮;
  3. 接 messages;
  4. 用 Chao AI API;
  5. 保存新一轮问答。

做到这一步,你的 AI Demo 就不再只是一次性问答工具,而更像一个真实聊天产品。

下一篇继续升级:给 Demo 加一个简单 RAG 知识库,让 AI 基于资料回答问题。

写在最后的话:
感谢大家对 Chao AI 的支持,对于近期一些常见问题我们在此统一解答:
1️⃣ Chao AI 网址是chaohub.cn/official,注册账号只需要在 Ask Anything 里表明注册账户意图即可注册;
2️⃣ 高等级用户目前我们会在每月月底邀请月活较高的用户优先使用😊;
3️⃣ 近期访问量较大,如果出现卡顿等问题请刷新页面或更换模型重试,敬请谅解,我们也在积极优化中;
4️⃣ 如使用过程中发现 Bug 或有更好的建议,可以在话题中反馈,您的建议一旦被采纳我们会升级您为高等级用户。

欢迎大家评论、点赞、转发、留言。
私信我可进咱们的共创社群,一起发现更多美好吧!

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐