AI客服接入教程:将Coze智能客服嵌入网站的10行代码和一键部署方案
把 Coze 智能客服接入网站,主流做法就三步:建 Bot、取 API 令牌、写前端悬浮窗调用流式接口。标题里的"10 行代码"是量级说法,实际行数取决于你选模板还是手写。
动手之前:Coze 客服有几种接入形态
Coze 国内版叫扣子(coze.cn),海外版在 coze.com。它本身提供了几条落地路径,选错会白折腾:
- 网页分享链接 / iframe 嵌入:发布后拿到一个 H5 地址,用
<iframe>塞进页面。改动最小,但样式难统一、移动端体验一般、对话数据你拿不到,适合临时试水。 - Bot as API:把 Bot 发布为 API 服务,用
bot_id加令牌调用,前端自己画界面,可控性最好,也是本文主线。 - 官方 Chat SDK:挂一段脚本自动生成对话入口,大致就是标题里说的"一键部署"那种省事方案,代价是可定制项少、样式跟着官方走。
- OpenAI 兼容接口:
https://api.coze.cn/v1/chat/completions,把model字段填成 Bot ID,能直接套用现成的聊天前端组件。
先想清楚你的站点是静态托管还是有自己的后端服务器,这决定了密钥放在哪里,也决定了成本量级。
环节一:在扣子控制台把 Bot 调成能上岗的客服
- 打开 coze.cn → 工作空间 → 创建智能体,命名成"官网客服"之类便于区分。
- 在"人设与回复逻辑"里写清边界:能回答哪些范围、答不了怎么引导转人工、禁止承诺什么。别写"你是专业的客服助手"这种空话,直接列 FAQ 边界。
- 挂知识库:把产品文档、常见问题整理成 md 或 txt 上传,分片按 500~800 字一段,标题层级写清楚,召回命中率会明显好一些。
- 需要实时数据的场景(订单查询、物流追踪)用工作流接你自己的接口;纯知识问答不必上插件。
- 在调试区把边界问法都问一遍,尤其是"帮我退款""你们老板电话多少"这类越权问题。
环节二:申请令牌,把密钥挡在服务端
进入扣子 → 个人设置 → 个人访问令牌,新建令牌并勾选 bot.chat 权限,复制保存,它只完整显示一次。
有一条底线:令牌不能出现在前端代码里。浏览器直接 fetch 打 api.coze.cn,等于把额度和配额公开挂在页面上,被扒走刷量是分分钟的事。正确做法是自己加一层代理,服务端只做三件事——校验用户身份、补上密钥、把 SSE 流原样转发。
// /api/chat —— 令牌放部署平台的环境变量:COZE_TOKEN / COZE_BOT_ID
export async function POST(req) {
const { messages, userId } = await req.json()
const r = await fetch('https://api.coze.cn/v3/chat', {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.COZE_TOKEN}`, 'Content-Type': 'application/json' },
body: JSON.stringify({
bot_id: process.env.COZE_BOT_ID,
user_id: userId,
stream: true,
auto_save_history: true,
additional_messages: messages.map(m => ({ role: m.role, content: m.content, content_type: 'text' })),
}),
})
return new Response(r.body, { headers: { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache' } })
}
环境变量入口:Vercel 在 Project → Settings → Environment Variables;宝塔面板在 Node 项目的环境变量页;Cloudflare Workers 用 wrangler secret put COZE_TOKEN。
流程节点三:前端对话壳与流式渲染
上面那段代理配合下面这段前端,加起来大致就是标题里提到的"10 行代码"量级,实际会多出注释和错误处理。服务端返回的是 SSE 流,事件形如 event: conversation.message.delta,数据体里的 type 为 answer 时取 content.text 追加渲染。
const res = await fetch('/api/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ messages, userId }),
})
const reader = res.body.getReader()
const dec = new TextDecoder()
let buf = ''
while (true) {
const { value, done } = await reader.read()
if (done) break
buf += dec.decode(value, { stream: true })
const lines = buf.split('\n'); buf = lines.pop()
for (const line of lines) {
if (!line.startsWith('data:')) continue
const evt = JSON.parse(line.slice(5).trim())
if (evt.type === 'answer' && evt.content?.type === 'text') bubble.append(evt.content.text)
}
}
界面本身不复杂:右下角一个悬浮球,点击展开 360×520 的对话面板,消息区用 white-space: pre-wrap 保留换行,底部输入框回车发送。移动端给面板设 max-height: 70vh,避免被软键盘顶飞。想省掉写壳的功夫,可以走官方 Chat SDK 或 OpenAI 兼容接口套现成组件,代价是交互逻辑受制于人。
阶段四:部署上线与自检
用 Nginx 反代时,SSE 必须关掉缓冲,否则回复会憋到最后一次性吐出来:
location /api/chat {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Connection '';
proxy_buffering off;
proxy_cache off;
chunked_transfer_encoding on;
proxy_read_timeout 300s;
}
上线前的自检项:
- 全站 HTTPS 可用,混合内容会直接拦掉请求;
- 手机端键盘弹出后输入框仍然可见可点;
- 接口超时与 401 都有兜底话术,别把堆栈甩给访客;
- 面板底部注明"回答由 AI 生成,仅供参考",隐私政策里写清对话数据用途;
- 有跨境调用时留意数据出境与备案要求,以当地监管口径和平台条款为准。
常见故障:从现象倒推原因
- 回复一次性蹦出来:Nginx 或 CDN 开了缓冲,先关 buffer,再确认回源走 HTTP/1.1。
- 控制台报 CORS:请求地址写成自己的域名,不要直连 Coze 的开放域。
- 401 或权限不足:令牌没勾
bot.chat,或者bot_id用的是草稿机器人 ID,要在发布渠道里确认已发布为 API。 - 答非所问:知识库分片太碎、问答混排,重切文档后重新发布即可。
- 费用超预期:多轮对话会重复携带历史消息,历史轮数建议截断到最近 6~10 轮;具体计费口径以扣子控制台的用量页面为准。