把 AI 接进企业微信,本质是「消息进 → 模型答 → 消息出」的闭环,再叠加知识库、上下文记忆与人工兜底。难点不在调模型,而在工程化地把会话管好。
先拆清楚:接入 AI 是四段活
搜这个问题的人想要的通常是一个端到端的结果:客户在企业微信里发一句话,几秒后收到一条像样的回复。但落到工程上,这中间是四段可以分别失败、也应该分别验收的活 —— 消息进、上下文装配、模型生成、消息出。把它们写在同一个函数里,最典型的后果是出问题时你分不清是模型答错了、上下文漏了,还是回复压根没发出去。
- 消息进:订阅 wecomapi 的事件回调拿到消息与发送方身份,验签、快速 ACK、入队。这一段的判据是不丢、不重、不阻塞。
- 上下文装配:决定这次带哪几轮历史、检索到哪些企业知识、附加哪些业务字段。这一段决定答得准不准。
- 模型生成:调用你选定的大模型,带超时、带预算、带失败出口。这一段决定答得快不快、贵不贵。
- 消息出:调用 wecomapi 的发送接口把回复写回会话,并以调用返回为准判断是否送达。这一段决定客户到底看没看到。
四段之间只共享一样东西:会话标识。把它作为贯穿全链路的字段,后面的排障、计量与灰度才有落点。模型选型不影响这个结构 —— 业务层与模型解耦之后,换一家供应商只动第三段。
动手前要备齐的六样东西
- 1一个公网可达的服务,能接收回调、也能发起出站请求。逻辑可以很轻,但要长期在线。
- 2在控制台创建的调用密钥,以 Authorization: Bearer 的形式带在请求头上,并提前规划轮换方式,不要写进代码仓库。
- 3一个已完成接入并保持在线的账号实例。账号掉线时前三段全部正常,只有最后一步发不出去,这是最难自查的一类故障。
- 4一个模型服务,以及它的配额、限流与计费口径。这三个数会直接决定后面的超时预算怎么切。
- 5一份最小知识素材。哪怕只有二十条常见问答,也比空着上线强 —— 没有知识的模型只会用通用语气编答案。
- 6一个明确的人工出口。谁接、在哪接、接的时候能看到什么,必须在上线前就有答案,而不是等第一个投诉来了再想。
前三样属于通道,后三样属于内容与组织。通道一两天能通,真正卡住项目的几乎总是后三样。
分步实施:从一条消息跑通开始
- 1先把出向单独打通。不接模型、不接回调,直接调一次发送接口,给测试账号发一条固定文本。看到响应体 code 为 0、目标会话里确实出现了这条消息,这一步才算过。它同时验证了密钥、账号在线状态和网络出口三件事。
- 2再把入向单独打通。配置事件回调地址,往测试账号发一条消息,确认服务收到了并在毫秒级返回了 200。日志里要能看到消息正文与发送方标识;看不到就先补日志,别急着往下走。
- 3把两端接成回声。收到什么就原样发回去。这一步跑通说明骨架成立,之后所有工作都在中间那两段里。
- 4插入模型调用。用最短的提示词替换回声逻辑,同时把超时和失败兜底一起写进去 —— 失败时发一条固定话术,不要沉默。IM 里没有反应比答错更劝退。
- 5加上下文与知识。按会话加载最近若干轮历史并做预算截断,检索企业素材后一并交给模型,让回答带上出处。带出处是一句话的改动,却同时解决客户自查、坐席接手和事后统计三件事。
- 6加人工出口与频控。客户明确要求转人工、命中敏感词、模型置信不足时,停止生成、发固定话术、把会话交给坐席;同时对同一对象做发送去重与速率上限。
# 此时还没有模型、也没有回调,只确认「服务能把一条消息发进会话」
curl -i -X POST "https://manager.wecomapi.com/message/sendText" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"guid": "7db8...",
"toId": "78813...",
"content": "接入自测:这条消息由服务端发出"
}'
# 期望响应体:{ "code": 0, "msg": "success" }
# 同时从响应头里记下两项:
# x-request-id 排障时提供它即可定位到这一次调用
# x-ratelimit-remaining 长期贴近 0 说明配额被自己的任务挤掉了
# 其余端点与字段以线上接口文档为准// 示意逻辑:onMessage / session / llm / handoff 等函数与字段均为自拟,
// 只有发送这一段是真实调用;其余端点与字段以线上接口文档为准
onMessage(async (msg) => {
if (await seen(msg.eventId)) return; // 幂等:重复投递不产生第二次模型调用
const s = await session.get(msg.convId);
if (s.state !== "ai") return agentDesk.push(msg); // 人工接管期间 AI 不出声
let reply;
try {
const ctx = await buildContext(msg.convId); // 历史 + 检索结果,按预算截断
reply = await withTimeout(llm.chat({ ctx, input: msg.text }), 8000);
} catch (e) {
reply = FALLBACK_TEXT; // 失败要有出口,不能沉默
await handoff(msg.convId, "llm_error");
}
const res = await fetch("https://manager.wecomapi.com/message/sendText", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ guid: s.guid, toId: s.toId, content: reply }),
});
const { code } = await res.json(); // code 为 0 才算这次调用被受理
await log(msg.convId, { code, requestId: res.headers.get("x-request-id") });
});六步里前三步是通道验证,一天之内应该做完;后三步是内容工程,会反复迭代很久。把这两类工作混在一个迭代里排期,通常两头都做不完。
架构与数据流:谁调谁、状态存哪、失败谁重试
整条链路只有一个方向是被动的 —— 事件回调是平台推给你,其余全部由你的服务主动发起。所以重试责任有一条清晰的分界:回调进入你的服务之前,由平台按投递重试与死信机制负责;进来之后,全部由你负责。
- 调用顺序:wecomapi 事件回调进入接入层(验签、ACK、入队),会话层做幂等与上下文装配,再调模型,拿到结果后回头调 wecomapi 的发送接口回写会话。
- 状态存哪:会话历史、结构化槽位、人工接管标记与降级开关,全部存在你自己的存储里。链路上每一跳都可以无状态,唯独会话状态不能。
- 幂等键:用事件自带的唯一标识做去重键,同一条消息重复投递时只允许产生一次模型调用。这是控制成本的第一道闸,也是回调重投时最容易漏的一处。
- 重试边界:模型调用可以重试,但要带剩余时间预算,预算只剩一秒时重试没有意义,此时正确的动作是降级;发送接口按幂等语义设计,网络抖动后重试是安全的。
- 计量落点:模型用量与发送结果都挂到会话标识上。一条客户消息背后往往有多次模型调用,按消息算账会算漏。
这条链路要做到第几档,站内《企业微信接入大模型的四种架构》把直连模型、加知识库检索、加工具编排、人机协同四种成本结构逐一拆过,结论是大多数对外客服团队应该落在第二档加第四档,而不是急着上工具编排 —— 工具编排把答错升级成做错,赔付不在一个量级。
错误处理与排障:先定位断在哪一段
这条链路的故障有个特点:客户侧的表现高度一致,都是没回或者回得不对,但原因可能在四段里的任意一段。排障第一步永远是定位断点,而不是先去改提示词。
- 1客户发了消息,服务端日志里什么都没有:断在入向。依次检查回调地址是否公网可达、验签有没有把请求拒掉、账号实例是否在线。这一段的证据在你自己的接入日志里。
- 2服务端处理完了,客户没收到:断在出向。判断依据是发送调用的返回,不要把生成完成当作已送达。每次调用响应头里的 x-request-id 要落库,排查时提供这个标识即可定位到具体那一次调用。
- 3回复很慢或时有时无:先看响应头里的 x-ratelimit-remaining。这个数长期贴近零,说明配额被自己的重试或群发任务挤掉了,而不是接口变慢。其次看队列排队时长 —— 高峰期排队三秒的系统,模型再快也来不及。
- 4回复发出去了但答得不对:这一类不要从模型查起。先把这次检索到的条目原样打出来,召回本身不对时改多少提示词都没用;召回没问题再看上下文窗口里究竟装了什么。
- 每条消息从进到出记一条贯穿的处理记录:会话标识、幂等键、是否走了人工出口、模型耗时、发送返回与 x-request-id。缺了最后一项,跨系统排障就只能靠时间戳对。
- 失败必须有出口。模型超时、检索不可用、配额耗尽,都要落到一条明确的固定话术,并真的把会话推进人工队列,而不是让它停在半路。
- 降级开关做成配置,不要靠发版。真出事那天你在等发布窗口,这是最贵的一种延误。
演练一次比读十遍文档管用:把模型服务地址改成一个不通的地址,看客户侧到底收到什么。多数团队第一次演练时发现客户侧什么都没有,而监控面板一片正常。
怎么判断真的做对了
跑通了不是一个可验收的说法。下面几条都能观察、能复现,可以直接写进上线检查单。
- 1出向可验证:一次发送调用返回 code 为 0,目标会话里出现这条消息,并且日志里能查到这次调用的 x-request-id。
- 2入向幂等可验证:向测试账号连发三条消息,服务端收到三条、处理三次;再把同一条事件人为重投一次,模型调用次数不增加。
- 3失败可观察:把模型地址临时改成不通的地址,客户侧在设定时间内收到固定话术,会话进入人工队列,监控里出现一条对应的失败记录 —— 三件事缺一不可。
- 4时延有数:统计从回调到达到发送成功的端到端耗时,看 P95 而不是平均值。超过用户能接受的时间,就该改成先回执、稍后补发结果的两段式。
- 5答案可追溯:随机抽十条回答,每条都要能说清依据了哪条知识、走的哪个分支。说不清的,说明记录还不够,不是模型的问题。
- 6覆盖率有基线:自助解决率与转人工率要按触发源分开统计。没有这条基线,下次改提示词是变好还是变坏,谁也说不清。
先用一个场景跑通再扩。把模型、知识库与发送通道解耦,后面才谈得上按会话类型分流和按比例灰度。本页只负责把链路搭起来,几个被一带而过的环节站内各有一篇展开:《企业微信 AI 客服系统怎么搭》讲知识按什么单位入库、路由为什么该按答案来源分而不是按意图分;《企业微信 AI 会话上下文怎么管》讲一轮会话的边界怎么切、窗口里那三段各装什么、滚动摘要为什么会把客户没说过的话越传越实;《企业微信智能客服的转人工怎么设计》讲三类触发源的优先级、置信度为什么不能只看模型自报的那个数,以及坐席接手时手里该有什么。
常见问题
- 企业微信接入 AI 需要自己开发吗?
- 需要一个自己的服务:接收 wecomapi 的事件回调、调用大模型、再调用发送接口把回复写回会话。这段代码本身很轻,几十行就能跑通最小版本;真正的工作量在知识素材、人工出口和排障能力上。控制台负责密钥与账号接入,模型由你自选。
- 企业微信 AI 客服能接哪些大模型?
- 业务层与模型解耦之后,接哪家都可以,换供应商只动链路的第三段。wecomapi 负责消息进出与会话通道,不限制也不代管你的模型选型。建议在自己的服务里加一层模型网关,把超时、重试、配额与用量计量都收在那一层,后面换模型和做灰度才不用改业务代码。
- 接入 AI 之后还需要人工客服吗?
- 需要。常见做法是 AI 先答,客户明确要求转人工、命中敏感词、模型置信不足这三类情况交给坐席,再按真实数据逐步提高 AI 的承接比例。上线前就要定好谁接、在哪接、接的时候能看到什么,这比模型效果更早决定客户体验。
- AI 回复生成好了但客户没收到,怎么排查?
- 先确认发送调用的返回:响应体 code 为 0 才算这次调用被受理,不要把生成完成当成已送达。其次看响应头里的 x-ratelimit-remaining 是不是长期贴近零,以及账号实例是否在线。每次调用响应头里的 x-request-id 建议落库,排查时提供这个标识即可定位到具体那一次调用。
- 怎么减少 AI 答错?
- 先查召回再查提示词。把这次检索到的条目原样打出来,如果检索结果本身就不对,改提示词没有意义。其次让回答带上出处、给检索加身份过滤、对低置信问题直接转人工。持续做法是攒一份离线评测集,改动前后各跑一遍,避免系统在一次次小改动里悄悄变坏。
- 企业微信接入 AI 大概要多久?
- 通道部分通常一两天:出向发一条消息、入向收一条消息、接成回声,三步跑完骨架就成立了。之后的时间几乎都花在知识整理、转人工规则和排障能力上,这部分按场景复杂度从一两周到数月不等,也是决定线上表现的部分。
准备好动手了?
精确字段、鉴权与端点以线上文档为准;可在控制台创建密钥后联调。
