文档

构建会话记忆聊天机器人

导入现成工作流,连接 React 与 Node.js 示例,并通过安全的一次性 WebSocket 通道交付结构化回复。

此示例包含什么

这是一个由 React、Express 和 ModelRiver 构建的全栈聊天机器人示例:

功能工作方式
会话记忆第一个请求创建 session_id;后续轮次将其传回,使 ModelRiver 能注入相关对话历史。
异步 AI 请求Node.js 后端调用 /v1/ai/async,并立即向浏览器返回连接信息。
实时交付浏览器使用一次性的 ws_token 加入该请求对应的 Phoenix WebSocket 通道。
结构化回复回复包含 replysummarysentimentconfidencetopicsaction_items
后端回调ModelRiver 将 webhook_received 事件发送到后端;后端可保存或丰富结果,再调用回调地址。

示例为了演示而将消息保存在内存中。用于生产环境之前,请改为使用你的数据库。

请求流程

TEXT
1React
2 POST /chatmessage + session_id
3
4Node.js
5 POST /v1/ai/async
6
7ModelRiver AI
8
9 channel_idws_tokenwebsocket_url
10 websocket_channel session_id
11
12 webhook_received Node.js
13
14 callback_url
15
16
17
18 WebSocket React

前置条件

  • Node.js 18 或更高版本
  • 一个 ModelRiver 项目
  • 一个项目 API 密钥
  • 至少一个受支持 AI 提供商的凭据

第 1 步 — 获取应用

Bash
git clone https://github.com/modelriver/chatbot-async-app.git
cd chatbot-async-app
 
cd backend
npm install
 
cd ../frontend
npm install

第 2 步 — 导入聊天机器人模板

  1. 打开会话记忆聊天机器人模板
  2. 点击下载 JSON
  3. 打开你的 ModelRiver 项目,使用导入上传文件。
  4. 检查预览并确认导入。

模板会创建:

  • 工作流:mr_chatbot_workflow
  • 结构化输出:chatbot_response
  • 会话记忆:已启用
  • 后端流水线事件:webhook_received
  • 主模型:openai / gpt-5.6-luna
  • 备用模型:anthropic / claude-haiku-4-5-20251001

发送真实请求之前,请在项目中连接所选提供商。你也可以先在模板页面更换模型,再下载模板。

会话记忆的必要条件: 项目必须保持启用请求正文日志。禁用请求正文后,ModelRiver 无法构建对话记忆。

第 3 步 — 创建 API 密钥和 Webhook

API 密钥

创建项目 API 密钥,并将其写入 backend/.envMODELRIVER_API_KEY

Webhook

为项目创建一个已启用的 HTTP Webhook,并将密钥写入 WEBHOOK_SECRET

如果 ModelRiver 在本机 4000 端口运行,请使用:

TEXT
1http://localhost:4001/webhook/modelriver

如果使用托管版 ModelRiver,Webhook 必须能从公网访问。本地开发时,可以使用 ModelRiver CLI forward 命令或 HTTPS 隧道。

项目中也可以存在 CLI Webhook,但未运行 CLI 监听器时会显示“No CLI client connected”。这不表示 HTTP Webhook 失败;如果不使用 CLI Webhook,建议将其禁用,使请求日志更清晰。

第 4 步 — 配置环境变量

先复制示例文件:

Bash
cd backend
cp .env.example .env
 
cd ../frontend
cp .env.example .env

使用托管版 ModelRiver

backend/.env

Bash
PORT=4000
MODELRIVER_API_KEY=mr_live_your_project_key
MODELRIVER_API_URL=https://api.modelriver.com
BACKEND_PUBLIC_URL=https://your-backend-or-tunnel.example.com
WEBHOOK_SECRET=your_webhook_secret

frontend/.env

Bash
VITE_API_URL=http://localhost:4000

ModelRiver 在本机运行

ModelRiver 已使用 4000 端口,因此聊天机器人后端应使用 4001

backend/.env

Bash
PORT=4001
MODELRIVER_API_KEY=mr_live_your_local_project_key
MODELRIVER_API_URL=http://127.0.0.1:4000/api
BACKEND_PUBLIC_URL=http://localhost:4001
WEBHOOK_SECRET=your_local_webhook_secret

frontend/.env

Bash
VITE_API_URL=http://localhost:4001

前端开发端口固定为 frontend/vite.config.js 中的 3006,不会从前端 .env 文件读取。BACKEND_PUBLIC_URL 必须指向接收 /webhook/modelriver 的同一个后端。应用不使用 EVENT_NAME 环境变量;默认事件固定为 webhook_received

第 5 步 — 运行聊天机器人

在克隆的仓库中打开两个终端。

终端 1 — 后端:

Bash
cd backend
npm start

终端 2 — 前端:

Bash
cd frontend
npm run dev

打开 http://localhost:3006

如果你使用托管版 ModelRiver 和 CLI Webhook,请在第三个终端运行转发命令,端口应与 modelriver login 中配置的端口一致:

Bash
modelriver forward --port 4000

本机 ModelRiver 使用直接 HTTP Webhook 时,不需要运行 CLI。

第 6 步 — 测试会话记忆

先发送:

TEXT
1My name is Vishal. I have a bakery named Sunrise Bakes in Bangalore. Please remember this.

然后在同一会话中发送:

TEXT
1What is my name, business name, and city?

回复应能记住 VishalSunrise BakesBangalore,并且两轮消息的 UI 会话标识应保持一致。

点击新建会话,然后再次询问第二个问题。此时应创建新的 session_id,聊天机器人也不应声称知道上一会话中的信息。

session_id 如何工作

浏览器不会自行生成 ModelRiver 会话 ID:

  1. 第一次 /chat 请求不发送 session_id
  2. ModelRiver 创建会话,并在异步响应正文中返回 session_id
  3. 后端将该 ID 返回给 React。
  4. React 在该会话的每一条后续消息中发送同一个 ID。
  5. 新建会话会清除它,使下一个请求创建新会话。

客户端生成的 conversationId 只是应用元数据,不能替代 ModelRiver 的 session_id

后端请求示例:

JAVASCRIPT
1const payload = {
2 workflow: 'mr_chatbot_workflow',
3 messages: [{ role: 'user', content: message }],
4 delivery_method: 'websocket',
5 webhook_url: `${BACKEND_PUBLIC_URL}/webhook/modelriver`,
6 events: ['webhook_received'],
7 metadata: {
8 conversation_id: conversationId,
9 message_id: messageId,
10 original_prompt: message
11 }
12};
13 
14if (sessionId) payload.session_id = sessionId;
15 
16const { data } = await axios.post(
17 `${MODELRIVER_API_URL}/v1/ai/async`,
18 payload,
19 { headers: { Authorization: `Bearer ${MODELRIVER_API_KEY}` } }
20);

WebSocket 交付如何工作

应用直接使用 Phoenix 客户端,因为每个 ws_token 只能使用一次。收到最终回复后,应用会主动断开连接,避免客户端使用已消耗的令牌进行重试。

JAVASCRIPT
1import { Socket } from 'phoenix';
2 
3const socket = new Socket(websocket_url, {
4 params: { token: ws_token },
5 reconnectAfterMs: () => 60_000
6});
7 
8const channel = socket.channel(websocket_channel);
9 
10socket.onOpen(() => {
11 channel.join();
12});
13 
14channel.on('response', payload => {
15 const status = payload.meta?.status || payload.status;
16 renderResponse(payload);
17 
18 if (status === 'completed' || status === 'success') {
19 channel.leave();
20 socket.disconnect();
21 }
22});
23 
24socket.connect();

必须使用该请求返回的准确 websocket_urlwebsocket_channel。不要存储或重复使用 ws_token

后端回调如何工作

ModelRiver 为 webhook_received 事件发送 task.ai_generated Webhook。后端会:

  1. 使用 WEBHOOK_SECRET 验证 X-ModelRiver-SignatureX-ModelRiver-Timestamp
  2. ai_response.data 读取结构化结果。
  3. 添加应用自己的消息 ID。
  4. 使用项目 API 密钥将结果 POST 到 callback_url
JAVASCRIPT
1const callbackPayload = {
2 data: {
3 ...webhook.ai_response.data,
4 id: messageId
5 },
6 task_id: messageId,
7 metadata: webhook.ai_response.meta || {}
8};
9 
10await axios.post(webhook.callback_url, callbackPayload, {
11 headers: {
12 Authorization: `Bearer ${MODELRIVER_API_KEY}`,
13 'Content-Type': 'application/json'
14 }
15});

最终回调负载随后会发送到浏览器的 WebSocket 通道。

结构化回复

导入的 chatbot_response 包含:

字段类型用途
reply字符串向用户展示的主要回答
summary字符串本轮对话的简短描述
sentimentpositiveneutralnegativemixed情感标签
confidencehighmediumlow置信度指示器
topics字符串数组主题标签
action_items{ task, priority } 数组存在时显示后续操作

前端同时支持分类值和数值型置信度,但可下载模板统一使用 highmediumlow

后端端点

端点方法说明
/chatPOST启动异步请求并返回 WebSocket 与会话信息
/webhook/modelriverPOST验证并处理 ModelRiver Webhook 事件
/conversations/:idGET返回示例保存在内存中的会话记录
/healthGET显示后端与 ModelRiver 配置状态

POST /chat 接受:

JSON
1{
2 "message": "What did I tell you about my business?",
3 "workflow": "mr_chatbot_workflow",
4 "conversationId": "optional-application-conversation-id",
5 "session_id": "optional-modelriver-session-uuid",
6 "events": ["webhook_received"]
7}

只有 message 是必填项;React 客户端会自动管理 session_id

生产环境检查清单

部署此示例之前:

  • 将内存 Map 替换为持久化存储。
  • 强制配置 WEBHOOK_SECRET;生产环境绝不能接受未签名 Webhook。
  • 将 CORS 限制为已部署前端的来源。
  • 后端与 WebSocket 端点都使用 HTTPS。
  • 在服务端将会话 ID 与已认证用户绑定。
  • 为数据库写入和回调添加幂等与重试处理。
  • API 密钥和 Webhook 密钥只能保存在后端。
  • 监控请求日志、失败的 Webhook 交付、回调和会话摘要。

故障排除

WebSocket connection error 或 HTTP 403

  • 确认前端使用最新 ws_token,且不会用它重新连接。
  • 确认前端使用返回的 websocket_channel,而不是在浏览器中自行拼接通道名。
  • ModelRiver 在本机运行时,WebSocket 主机优先使用 127.0.0.1,避免 macOS 的 IPv6 解析问题。
  • 修改前端 .env 后重新启动前端。

聊天机器人没有记忆

  • 确认 mr_chatbot_workflow 已启用会话记忆
  • 确认项目已启用请求正文日志。
  • 确认第二次 /chat 请求发送了第一次返回的同一个 session_id
  • 确认两轮之间没有点击新建会话

HTTP Webhook 成功,但 CLI Webhook 失败

没有 CLI 客户端连接时,已启用的 cli://localhost Webhook 会失败。请将其禁用或运行 CLI;成功的 HTTP Webhook 仍可正常完成请求。

相关资源