跳到主要内容

在 CloudBase 上实现流式 chatbot:三种实现路径

一句话定义:把"边生成边显示"的 chat 界面接到 CloudBase 后端有三条主路径——官方 cloudbase-agent-ui 小程序组件、手写 SSE 云函数、Vercel AI SDK;本篇按"前端是什么、要不要自定义协议"帮你选一条,并给出最小可跑代码。

预计耗时:30–60 分钟(取决于选哪条路径) | 难度:进阶

适用场景

  • 想做带流式输出体验的 chat 应用(用户边看边读,不是等几秒一次性显示)
  • 后端在 CloudBase(云函数 / 云托管)
  • 前端可以是微信小程序 / Web / Next.js / Vue / React Native
  • LLM 用混元 / DeepSeek / OpenAI 兼容协议都可

三条路径速览

路径适合谁优势限制
A. cloudbase-agent-ui(官方组件)微信小程序开发者官方组件,流式/多轮/上传文件/语音/工具调用全封装;混元/DeepSeek/Agent 三种 chatMode目前只发布了微信小程序版,Web/React 版暂无
B. 手写 SSE + 云函数已有 Web/Vue/Next 前端,协议要自定义完全可控,前后端协议自由,兼容任何端流式解析、断重连、中断要自己处理
C. Vercel AI SDK已有 Next.js / Vue 项目,全栈 React 团队useChat hook 体验好,错误/中断 SDK 内置小程序用不了;ai@4ai@5+ 协议不兼容,要锁版本

下面三条路径独立成节,按需读其中一条即可。

路径 A:用 cloudbase-agent-ui(小程序推荐主路径)

重要cloudbase-agent-ui 当前发布形态是微信小程序源码组件(不是 npm 包,也没有 React/Web 版)。本节只覆盖小程序场景;如果你的前端不是小程序,跳到路径 B 或 C。

第一步:在云开发控制台创建 AI 服务

打开微信开发者工具顶部"云开发"开通服务,或前往云开发平台。两种 AI 服务可选:

  • 接入大模型chatMode: 'model'):直接对接混元 / DeepSeek,开箱即用
  • 创建框架型 AgentchatMode: 'bot'):在 Agent 平台拖出一个智能体,绑定 prompt / 工具 / 知识库,拿到 botId 形如 agent-xxxxxxx

详见 Agent 开发文档

第二步:拷贝组件到小程序项目

cloudbase-agent-ui 不发 npm,是源码组件分发:

  1. git clone https://github.com/TencentCloudBase/cloudbase-agent-ui 或下载 GitHub Release 里的 agent-ui.zip

  2. 把仓库里的 components/agent-ui 整个目录拷贝到你小程序项目的 components/agent-ui/

  3. miniprogram/app.js 里初始化云开发环境:

    App({
    onLaunch() {
    wx.cloud.init({
    env: 'your-env-id', // CloudBase 环境 ID
    traceUser: true,
    });
    },
    });

第三步:在页面注册并使用组件

页面 .json 注册:

{
"usingComponents": {
"agent-ui": "/components/agent-ui/index"
}
}

页面 .wxml 引用:

<view>
<agent-ui
chatMode="{{chatMode}}"
agentConfig="{{agentConfig}}"
modelConfig="{{modelConfig}}"
showBotAvatar="{{showBotAvatar}}"
></agent-ui>
</view>

页面 .js 给配置(两种 chatMode 二选一):

// 方案 1:直接对接大模型(最简单)
Page({
data: {
chatMode: 'model',
showBotAvatar: true,
modelConfig: {
modelProvider: 'hunyuan-exp', // 或 'hunyuan-open' / 'deepseek'
quickResponseModel: 'hunyuan-turbos-latest', // DeepSeek 可填 'deepseek-v3.2' / 'deepseek-r1'
logo: '',
welcomeMsg: '你好,我是助手',
},
},
});
// 方案 2:对接框架型 Agent(要先在控制台创建 Agent)
Page({
data: {
chatMode: 'bot',
showBotAvatar: true,
agentConfig: {
botId: 'agent-xxxxxxx', // 控制台 Agent 列表里复制
tools: [
// 可选:前端工具,Agent 可以调
{
name: 'get_weather',
description: '获取指定城市的天气',
parameters: {
type: 'object',
properties: { city: { type: 'string' } },
required: ['city'],
},
handler: ({ city }) => `${city} 天气晴,25°C`,
},
],
},
},
});

完整 props(chatMode / agentConfig / modelConfig / showBotAvatar)和取值范围以仓库 README 当前版本为准。流式输出、多轮会话、文件上传、语音输入、工具调用都由组件内部处理,业务代码不用碰 SSE。

第四步:配置小程序服务器域名

公众平台后台 → 开发管理 → 服务器域名 → request 合法域名,加:

https://{your-envid}.api.tcloudbasegateway.com

否则上传文件 / 多会话功能不可用。

路径 B:手写 SSE + 云函数(自定义场景)

适合:协议非 AG-UI,前端不是小程序,或要深度定制前后端通信。

后端 Web 云函数

mkdir chat-backend && cd chat-backend
npm init -y
npm install express

index.js

const express = require('express');
const app = express();
app.use(express.json());

app.post('/api/chat', async (req, res) => {
const { messages } = req.body;

// 调上游 LLM(这里复用 connect-openai-api-cloud-function 的代理 URL)
const upstream = await fetch(process.env.LLM_PROXY_URL + '/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${process.env.LLM_PROXY_TOKEN}`,
},
body: JSON.stringify({
model: 'gpt-4o-mini',
messages,
stream: true,
}),
});

if (!upstream.ok) {
res.status(upstream.status).json({ error: await upstream.text() });
return;
}

res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');

const reader = upstream.body.getReader();
while (true) {
const { done, value } = await reader.read();
if (done) break;
res.write(value); // 上游已经是 SSE 格式,原样透传
}
res.end();
});

app.listen(process.env.PORT || 9000);

部署到 CloudBase Web 云函数:

tcb fn deploy chat-backend --httpFn -e your-env-id

部署后控制台「云函数 → chat-backend」配 LLM_PROXY_URL / LLM_PROXY_TOKEN,HTTP 触发器记下访问 URL,例如 https://your-env.service.tcloudbase.com/chat-backend/api/chat

前端

Web(普通 React/Vue,fetch + ReadableStream):

const res = await fetch('/api/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ messages: [{ role: 'user', content: '你好' }] }),
});

const reader = res.body.getReader();
const decoder = new TextDecoder();
let buffer = '';

while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });

// SSE 以 \n\n 分隔事件
const events = buffer.split('\n\n');
buffer = events.pop(); // 最后一个可能不完整,留到下一轮

for (const event of events) {
for (const line of event.split('\n')) {
if (!line.startsWith('data:')) continue;
const data = line.slice(5).trim();
if (data === '[DONE]') return;
try {
const obj = JSON.parse(data);
const delta = obj.choices?.[0]?.delta?.content;
if (delta) console.log(delta); // 累加到 state 即可
} catch {}
}
}
}

微信小程序(wx.request + enableChunked,不走 cloudbase-agent-ui 时):

const requestTask = wx.request({
url: 'https://your-env.service.tcloudbase.com/chat-backend/api/chat',
method: 'POST',
enableChunked: true,
data: { messages: [{ role: 'user', content: '你好' }] },
responseType: 'arraybuffer',
});

requestTask.onChunkReceived((res) => {
const text = String.fromCharCode.apply(null, new Uint8Array(res.data));
// 同上解析 SSE,把 delta 累加到 page data
});

参考 wx.request enableChunked 文档

路径 C:Vercel AI SDK(已有 Next.js 项目)

版本说明:本篇代码基于 ai@4.xai@5+ 已发布且 useChat / streamText 协议有破坏性变更。新建项目请优先参考 Vercel AI SDK 官方文档 的当前版本。

适合:已有 Next.js / Vue 项目正在用 Vercel AI SDK 4.x;或从 Vercel 迁来 CloudBase。

装包

npm install ai@4 @ai-sdk/openai

后端:Next.js Route Handler

app/api/chat/route.ts

import { createOpenAI } from '@ai-sdk/openai';
import { streamText } from 'ai';

export const runtime = 'nodejs'; // 不要用 edge,云托管跑的是 Node 容器
export const maxDuration = 60;

const llm = createOpenAI({
baseURL: process.env.LLM_PROXY_URL, // https://xxx.service.tcloudbase.com/llm-proxy/v1
apiKey: process.env.LLM_PROXY_TOKEN, // 代理鉴权 token,不是真 OpenAI key
});

export async function POST(req: Request) {
const { messages } = await req.json();

const result = await streamText({
model: llm('gpt-4o-mini'),
system: '你是一个有帮助的助手,用简体中文回答。',
messages,
});

return result.toDataStreamResponse();
}

createOpenAI({ baseURL }) 让请求打到自己的代理;真正的 OpenAI key 在云函数代理层(参考 connect-openai-api-cloud-function),前端摸不到。

后端:Web 云函数版(非 Next.js 前端用这个)

const express = require('express');
const { createOpenAI } = require('@ai-sdk/openai');
const { streamText } = require('ai');

const app = express();
app.use(express.json({ limit: '5mb' }));

const llm = createOpenAI({
baseURL: process.env.LLM_PROXY_URL,
apiKey: process.env.LLM_PROXY_TOKEN,
});

app.post('/api/chat', async (req, res) => {
const { messages } = req.body;
const result = await streamText({
model: llm('gpt-4o-mini'),
messages,
onFinish: async ({ text }) => {
// 可选:流式结束后落库
},
});
result.pipeDataStreamToResponse(res);
});

app.listen(process.env.PORT || 9000);

部署:

tcb fn deploy chat-backend --httpFn -e your-env-id

控制台配 LLM_PROXY_URL / LLM_PROXY_TOKEN

前端:useChat

app/chat/page.tsx

'use client';
import { useChat } from 'ai/react';

export default function ChatPage() {
const { messages, input, handleInputChange, handleSubmit, isLoading, error, stop } = useChat({
api: '/api/chat', // 跨服务时填 Web 云函数 URL
});

return (
<div style={{ maxWidth: 720, margin: '0 auto', padding: 24 }}>
{messages.map((m) => (
<div key={m.id} style={{ margin: '12px 0' }}>
<strong>{m.role === 'user' ? '你' : '助手'}</strong>
<span>{m.content}</span>
</div>
))}

{error && <div style={{ color: 'red' }}>出错了:{error.message}</div>}

<form onSubmit={handleSubmit} style={{ display: 'flex', gap: 8 }}>
<input
value={input}
onChange={handleInputChange}
placeholder="说点什么..."
disabled={isLoading}
style={{ flex: 1, padding: 8 }}
/>
<button type="submit" disabled={isLoading}>发送</button>
{isLoading && <button type="button" onClick={stop}>停止</button>}
</form>
</div>
);
}

Vue 用 @ai-sdk/vueuseChat,API 形态相同。

部署

Next.js 一体走云托管:

tcb cloudrun deploy --port 3000

详见 Next.js 部署到云托管。云托管「服务设置 → 环境变量」加 LLM_PROXY_URL必须以 /v1 结尾,因为 @ai-sdk/openai 会自动拼 /chat/completions)和 LLM_PROXY_TOKEN

怎么选

你的情况
微信小程序前端A(cloudbase-agent-ui)
想最快做出 chat 界面,不想自己实现协议A(小程序)或 C(Web)
协议必须自定义,或前端是非 Next 的 Web/RNB
已有 Next.js + Vercel AI SDK 项目C
都想试,从哪开始小程序:A;Web:C 起步,需要定制再切 B

常见错误

Symptom原因修复
前端没有"边出边显示",等几秒一次性出现SSE 没真生效 / 中间层把 stream 缓冲了后端确认 Content-Type: text/event-stream + Cache-Control: no-cache;自定义域名/CDN 关闭响应缓冲;先用 *.service.tcloudbase.com 默认域名验证
路径 A:组件不显示或报"agent-ui 未注册"页面 .json 没声明 usingComponents,或 components/agent-ui 没拷贝检查目录结构 + 页面 usingComponents 路径
路径 A:botId 不对,提示找不到 Agent控制台没创建 Agent 或 ID 拼错云开发控制台 → AI → Agent 列表复制实际 ID(形如 agent-xxxxxxx
路径 A:request not in domain list服务器域名没加白名单公众平台后台 → 服务器域名 → 加 https://{envid}.api.tcloudbasegateway.com
路径 B:小程序 wx.requesturl not in domain list同上同上,加 Web 云函数访问域名(*.service.tcloudbase.com
路径 B:后端流断了前端不知道上游 5xx 让 SSE 中断后端 catch 里写 data: {"error":"..."}\n\nend() 通知前端
路径 C:useChat is not exported from 'ai/react'ai 不是 v4(v5+ import 路径变了)npm install ai@^4 @ai-sdk/openai@^1;不要混 v5 路径
路径 C:useChat 发的是 GET 不是 POST,404App Router Route Handler 没导出 POST检查 route.tsexport async function POST 拼写
路径 C:Failed to fetch 跨域前端域名 ≠ 后端域名,CORS 没加Express 加 CORS 中间件:Access-Control-Allow-Origin / Methods / Headers,处理 OPTIONS
路径 C:长会话报 context_length_exceeded全量历史消息发了前端只发最近 N 条,或后端做摘要压缩

相关文档

下一步