构建实时 AI 聊天机器人

使用 WebSockets 体验端到端的 AI 流式传输。学习实现模型故障转移、本地 webhook 开发以及类型安全的结构化 JSON 响应。

为什么这个示例很重要

传统的 AI 集成需要您管理复杂的基础设施:轮询响应、处理超时、管理连接状态以及构建自定义流式传输解决方案。ModelRiver 消除了所有这一切。

只需几行代码,您即可获得:

  • 真正的端到端流式传输:从用户输入到 AI 响应,一切都通过 WebSockets 实时流动
  • 零基础设施开销:无需构建或维护您自己的流式传输服务器
  • 自动故障转移:当 AI 模型不可用时具有内置的供应商回退功能
  • 结构化输出(Structured outputs):定义 JSON schema 并获得格式完美、类型安全的响应
  • 事件驱动的回调:在 AI 处理和响应传递之间注入自定义逻辑

您将构建什么

一个全栈聊天机器人应用程序,具有:

功能特征描述
实时聊天 UI具有即时消息传递功能的现代 React 界面
异步 AI 处理通过 ModelRiver 的异步 API 进行非阻塞请求
WebSocket 流式传输使用 @modelriver/client SDK 进行实时响应传递
结构化响应带有情感分析、置信度分数和待办事项的 AI 响应
自定义 ID 注入在整个生命周期中使用您自己的 UUID 跟踪对话

架构概述

React Frontend Node.js Server ModelRiver API
(Your App) (Your Backend) (AI Gateway)
AI Processing
(Background)
Webhook Event
+ ID Injection
Callback URL
(to ModelRiver)
WebSocket Stream
(Real-time Response)

魔法所在:事件驱动的回调

这就是 ModelRiver 真正闪耀的地方。当您发送异步 AI 请求时:

  1. ModelRiver 处理您的请求并生成 AI 响应。
  2. 在传递到您的前端之前,ModelRiver 会向您的后端发送一个 webhook。
  3. 您的后端丰富了该响应(例如使用自定义 ID)、验证数据或触发其他操作。
  4. 您的后端回拨到 ModelRiver 并带有被丰富的数据负载。
  5. ModelRiver 将最终响应通过 WebSocket 流式传输给您的前端。

此模式支持传统 AI API 无法实现的用例:

  • 用于数据库跟踪的自定义 ID 注入
  • 响应验证和过滤
  • 敏感内容的审批关卡
  • 多步丰富管道

分步设置指南

按照以下步骤使聊天机器人在本地运行。本指南涵盖了从克隆代码到获得您的第一个 AI 响应的所有内容。

先决条件


步骤 1:克隆和安装

Bash
git clone https://github.com/modelriver/modelriver-chatbot-demo.git
cd modelriver-chatbot-demo
 
# Install backend dependencies
cd backend && npm install
 
# Install frontend dependencies
cd ../frontend && npm install

步骤 2:设置 ModelRiver 控制台

2.1 创建项目

  1. 前往 console.modelriver.com
  2. 点击 New Project 并为其命名(例如 my-chatbot

2.2 连接 AI 供应商(Provider)

  1. 导航到项目中的 Providers
  2. 点击 Add Provider 并选择其中一个(例如 OpenAI,Anthropic)。
  3. 输入您供应商的 API 密钥并保存。

2.3 创建结构化输出(Structured output)

这定义了 AI 响应的 JSON 格式。导航到 Structured OutputsCreate Structure

  • 名称(Name): chatbot_response
  • 示例数据(Sample data): 粘贴此响应示例:
JSON
1{
2 "reply": "The AI's direct response",
3 "summary": "Brief summary of the conversation",
4 "sentiment": "positive | negative | neutral | mixed",
5 "confidence": 0.95,
6 "topics": ["topic1", "topic2"],
7 "action_items": [
8 { "task": "Description", "priority": "high | medium | low" }
9 ]
10}
  • 点击 Build schema from sample data:这将自动生成带有示例的类型化 JSON 模式(schema),以提高 AI 输出的准确性。
  • 保存(Save)该结构。

2.4 创建工作流(Workflow)

导航到 WorkflowsCreate Workflow

设置
名称(Name)mr_chatbot_workflow(必须与代码中的默认值匹配)
供应商(Provider)选择您所连接的供应商
模型(Model)选择一个模型(例如 gpt-5-miniclaude-haiku-4-5
结构化输出(Structured output)选择 chatbot_response
事件名称(Event name)new_chat(触发 webhook 回调)

保存(Save)该工作流。


2.5 创建 API 密钥

导航到 API KeysCreate Key

  1. 输入 密钥名称(key name)(例如 chatbot-dev)。
  2. 设置 过期时间(expiration)(如果是开发可以选 "永不(Never)",测试就选短一些的时间)。
  3. 点击 Create Key
  4. 立即复制密钥:您将无法再次看到它。

2.6 为本地开发创建 Webhook

导航到 WebhooksCreate Webhook

  1. 选择 Localhost (CLI) 作为 webhook 类型:这让 CLI 能够接收 webhook。
  2. Secret 字段留空以自动生成一个,或输入您自己的字符串。
  3. 启用该 webhook 并点击 Create webhook
  4. 复制此密钥(secret):这是您之后在 .env 中设置 WEBHOOK_SECRET 时所需要的。

步骤 3:配置环境

使用步骤 2 中的值创建 backend/.env

Bash
MODELRIVER_API_KEY=your_api_key_from_step_2.5
PORT=4000
BACKEND_PUBLIC_URL=http://localhost:4000
WEBHOOK_SECRET=your_webhook_secret_from_step_2.6
EVENT_NAME=new_chat

重要提示: EVENT_NAME 必须与您的工作流中的事件名称相匹配。


步骤 4:设置本地 Webhook 转发

您的 localhost:4000 无法从外部互联网直接访问,因此 ModelRiver 无法直接发送 webhook。CLI 解决了这个问题。

安装并进行身份验证:

Bash
npm install -g @modelriver/cli
modelriver login

开始监听:

Bash
modelriver forward

您将会看到:

Connected to ModelRiver
Forwarding webhooks http://localhost:4000/webhook/modelriver

关于安全性的注意事项: 在生产环境中,您应该验证 webhook 签名,以确保请求确实来自 ModelRiver。


步骤 5:运行应用程序

打开三个终端窗口(Tabs)

终端 1:CLI(已经在运行):

Bash
modelriver listen

终端 2:后端(backend):

Bash
cd backend && npm start

终端 3:前端(frontend):

Bash
cd frontend && npm run dev

步骤 6:测试!

  1. 在您的浏览器中打开 http://localhost:3006
  2. 输入一条消息并点击发送。
  3. 见证奇迹时刻:
    • 您的消息被发送到您的后端系统。
    • 后端向 ModelRiver 发出异步请求。
    • ModelRiver 将请求交由 AI 处理并同时发送了一则 webhook。
    • CLI 将收到的 webhook 转发到您本地的 localhost。
    • 您的后端向响应中注入了相关数据(enriched response),随后予以回调触发。
    • 前端最终通过 WebSocket 连接接收被编排完成后的结构化响应内容。

此时您应该会看到一条已经包含了情感分析识别结果、主题归类以及具体待办选项的一条格式化返回数据!


关键实现细节

发送 AI 请求(后端)

JAVASCRIPT
1// POST /chat endpoint
2app.post('/chat', async (req, res) => {
3 const { message, workflow = 'mr_chatbot_workflow' } = req.body;
4
5 // Generate custom IDs for tracking
6 const conversationId = uuidv4();
7 const messageId = uuidv4();
8
9 // Send async request to ModelRiver
10 const response = await fetch(`${MODELRIVER_API_URL}/v1/ai/async`, {
11 method: 'POST',
12 headers: {
13 'Authorization': `Bearer ${MODELRIVER_API_KEY}`,
14 'Content-Type': 'application/json'
15 },
16 body: JSON.stringify({
17 workflow,
18 messages: [{ role: 'user', content: message }],
19 delivery_method: 'websocket',
20 webhook_url: `${BACKEND_PUBLIC_URL}/webhook/modelriver`,
21 events: ['webhook_received'],
22 metadata: { conversationId, messageId }
23 })
24 });
25
26 // Return WebSocket connection details to frontend
27 const data = await response.json();
28 res.json({
29 channel_id: data.channel_id,
30 websocket_url: data.websocket_url,
31 ws_token: data.ws_token
32 });
33});

接收 AI 响应(前端)

JSX
1import { useModelRiver } from '@modelriver/client';
2 
3function ChatApp() {
4 const { connect, message, status } = useModelRiver();
5
6 const sendMessage = async (text) => {
7 // Get WebSocket details from your backend
8 const res = await fetch('/chat', {
9 method: 'POST',
10 body: JSON.stringify({ message: text })
11 });
12 const { websocket_url, ws_token, channel_id } = await res.json();
13
14 // Connect to ModelRiver WebSocket
15 connect({ websocket_url, ws_token, channel_id });
16 };
17
18 // message updates in real-time as AI responds
19 return (
20 <div>
21 <div className="ai-response">{message}</div>
22 <button onClick={() => sendMessage('Hello!')}>Send</button>
23 </div>
24 );
25}

处理 Webhook(后端)

JAVASCRIPT
1// Webhook 端点在 AI 响应到达前端之前接收它
2app.post('/webhook/modelriver', async (req, res) => {
3 const { channel_id, ai_response, callback_url } = req.body;
4
5 // 取回您的自定义 ID
6 const pending = pendingRequests.get(channel_id);
7
8 // 使用您的自定义 ID 丰富响应
9 const enrichedData = {
10 id: pending.messageId,
11 conversation_id: pending.conversationId,
12 ...ai_response.data
13 };
14
15 // 将丰富(enriched)后的数据发回 ModelRiver
16 await fetch(callback_url, {
17 method: 'POST',
18 headers: { 'Content-Type': 'application/json' },
19 body: JSON.stringify(enrichedData)
20 });
21
22 res.status(200).json({ success: true });
23});

了解响应(Response)

当配置了结构化输出时(如步骤 2.3中所示),前端会自动渲染:

字段显示方式
Reply主要的 AI 响应(突出显示)
Sentiment视觉指示器(正面、中性、负面)
Confidence颜色编码的进度条
Topics交互式标签按钮
Action items带有颜色指示的优先级列表

API 参考(API reference)

后端端点

端点(Endpoint)方法描述
/chatPOST发送聊天消息,返回 WebSocket 连接的详细信息
/webhook/modelriverPOST接收来自 ModelRiver 的 webhook
/conversations/:idGET获取对话历史记录
/healthGET健康检查端点

请求参数

JSON
1{
2 "message": "User's message (required)",
3 "workflow": "Workflow name (optional, default: mr_chatbot_workflow)",
4 "conversationId": "Existing conversation ID (optional)"
5}

是什么让这与众不同

能力特征(Capability)传统做法使用 ModelRiver
实时流式传输构建自定义 WebSocket 服务器SDK 处理一切事物
本地开发ngrok、localtunnel 等软件modelriver listen
供应商故障转移手动进行代码编写实现自动化且内置的服务
响应验证在前端进行后期人工处理事件驱动的回调功能
结构化输出(Structured output)复杂的 Prompt 参数调校工程定义 schema 并直接获取出具有预设格式内容的 JSON
自定义 ID 跟踪跨服务进行追踪比较困难原生的元数据注入

下一步(Next steps)

既然现在您已经获得了一个正常运行的聊天机器人,请探索这些领域:


资源(Resources)