NEW

免费试用已开放

立即开始

自动化 · 回调 · Webhook

企业微信机器人怎么开发

更新于 2026-06-098 分钟

一个可用的企业微信机器人 = 稳定的消息收发 + 清晰的规则路由 + 与业务系统的对接。把这三块解耦,机器人才好维护、好扩展。

「机器人」不是一个接口,是三段链路

搜「企业微信机器人怎么开发」的人,多半在找一个叫「机器人接口」的东西。实际上没有这么一个接口:一个能用的机器人是三段链路拼起来的 —— 事件回调把消息推给你,你的服务判断该回什么,再调用发送接口把结果送回会话。三段各自会失败、各自有不同的重试责任,把它们混在一个函数里写,是后面所有难排查问题的共同源头。

  • 接入层:订阅消息与群成员等事件,把不同来源统一收口成一种内部结构;出向只对外暴露「发一条消息」这一个动作。
  • 路由层:按指令、关键词或上下文决定交给哪个处理器。处理器只返回「要回什么」,不自己负责发出去。
  • 业务层:对接工单、CRM、知识库或大模型,产出回复内容。这一层可以完全不知道消息来自企业微信。

群机器人和单聊机器人共用这套结构,差异集中在消息来源、是否需要被 @ 才响应、以及要不要带群内上下文这三处。把来源在接入层归一化之后,路由层和业务层可以整段复用。博客「企微机器人开发完整流程」把这三层各自的契约拆得更细,也写了会话标识取错时会出现的那类症状。

站点声明的能力里,事件回调覆盖消息、成员、客户与群事件订阅,发送侧覆盖文本、图片、文件、链接卡片与富文本模板。具体某个事件类型或消息类型是否可用,以线上接口文档为准。

动手之前先备齐四样东西

  • 一份可用的凭证。在控制台 https://console.wecomapi.com 创建密钥,所有接口共用 Authorization: Bearer 这一套鉴权。
  • 一个公网可达的 https 地址用来接收回调。本地开发时本机地址不可达,需要先做内网穿透拿到外网地址,再填进控制台。
  • 一处能持久化的存储,至少要存原始事件报文、去重键和处理状态。用内存变量顶替,重启一次就丢一批消息。
  • 一条结构化日志。每次出向调用记录接口、耗时、业务 code,以及响应头里的 x-request-id。

这四样里最常被跳过的是第三样。回调是平台推过来的,投递失败会重投,但重投的次数和窗口不由你决定,落盘是你这一侧唯一能自己兜住的东西。

分六步搭出第一个能回话的机器人

  1. 1先单向打通发送。不接回调,直接用一条 curl 往测试会话发文本,确认 HTTP 200 且响应体 code 为 0。请求体是 guid、toId、content 三个字段,含义以线上接口文档为准。这一步成了,说明凭证、网络与账号状态都没问题。
  2. 2挂上回调地址。在控制台填入 https 地址,然后手动给机器人发一条消息,看服务端日志有没有收到 POST。收不到就先排查地址可达性,不要急着写解析逻辑。
  3. 3加验签,并确认通过率是 100%。验签写错的典型表现是偶发 401 而不是全挂,所以要打点看比例,不能只凭「我这条通了」下结论。
  4. 4把入口改成只做落盘加 ACK,业务逻辑挪到消费侧。再发一条消息,应该能在存储里看到一条带去重键的记录,而不是只在控制台看到一行日志。
  5. 5接上路由和一个最简单的处理器,比如回显。发一条消息,观察一次完整往返:入站记录一条、出站日志一条、会话里看到回复。三处对不上就说明中间断了。
  6. 6最后才接业务层。工单、知识库或大模型都在这一步进来,前面五步搭好的结构一行都不用改。
示意:先用一条命令确认发送通道是通的bash
# -i 打印响应头:x-request-id 报障时要带上,x-ratelimit-remaining 是余量
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": "bot ping" }'

# 期望 HTTP 200,响应体 { "code": 0, "msg": "success" }

数据流:谁调谁、状态存哪、谁负责重试

一次完整往返里,只有一个方向是平台主动发起的:事件回调。出向的发送永远是你主动调。这一点决定了两侧的重试责任完全不同。

  • 入向由平台重试。你能做的只有快速返回 2xx,以及落盘之后再 ACK。落盘失败时干脆不要 ACK,让平台重投比你自己悄悄丢掉安全得多。
  • 出向由你重试。发送失败要不要重发、重发几次、按什么键去重,全在你这一侧决定,平台不会替你补。
  • 状态分两处存。原始事件与处理状态放持久化存储,多轮对话的会话上下文放另一份带过期时间的存储。两者生命周期不同,塞进同一张表会互相拖累。

队列按会话分区,可以同时拿到会话内有序和会话间并行。博客「企业微信消息处理为什么必须走队列」把 ACK 与业务处理为什么必须解耦、重放和死信怎么设计讲得更细。

示意:入口只落盘,业务在消费侧回写javascript
// 示意逻辑:验签算法与事件字段名以线上接口文档为准

// 入口只做三件事:验签、落盘、ACK;业务一律在消费侧
app.post("/wecom/callback", async (req, res) => {
  if (!verifySignature(req)) return res.sendStatus(401);
  const saved = await inbox.append(req.body);  // 没落盘就别 ACK,让平台重投
  res.sendStatus(saved ? 200 : 500);
});

// 消费侧:去重 → 归一化 → 路由 → 回写
async function consume(item) {
  if (await seen(item.dedupeKey)) return;   // 重复投递直接丢弃
  const msg = normalize(item.raw);
  if (msg.fromSelf) return;                 // 自己发的不处理,否则会自激
  const reply = await route(msg);
  if (!reply) return;

  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: msg.guid, toId: msg.chatId, content: reply }),
  });

  const body = await res.json();
  log.info({ ok: body.code === 0, reqId: res.headers.get("x-request-id") });
}

错误处理与排障

这一节是自建机器人上线后最花时间的地方,也是开发阶段最容易被整段跳过的地方。顺路径的代码通常一两天就写完了,剩下的工夫都花在这里。

先把失败分层看。一次出向调用至少有三层结果:传输层(超时、连接被重置)、HTTP 状态、以及响应体里的 code。三层要分开记 —— HTTP 200 但 code 非 0 是很常见的一种失败,只看状态码会把它算成成功。

  • 参数或内容不合法:重试多少次结果都一样,直接失败,并把调用点记下来。
  • 凭证或账号状态问题:修一次再试一次,仍然失败就升级告警,不要无限重试。
  • 触发容量限制:退避,而且降的是这个账号的整体速率,不是只把这一条排到后面。响应头 x-ratelimit-remaining 可以用来提前减速,而不是等被拒了才反应。
  • 超时:结果未知,不等于失败。写操作超时后盲目重发可能造成重复发送,应该先按幂等键确认上一次到底成没成。

具体哪个 code 属于哪一类,以线上接口文档为准,不要按码值死记。博客「企业微信接口报错怎么分类处理」讲的就是按「该做什么」而不是按码值来组织这张映射表。

排障时最有用的是响应头 x-request-id。每次调用都把它记进日志,报障时带上,比描述「大概几点钟有条消息没发出去」有效得多。手动复现时用 curl -i 把响应头一起打出来,博客「企业微信接口调试全流程」里有一段可以直接交接给别人的命令写法。

几乎所有人都会踩一次的坑:机器人处理了自己发出去的消息,形成自激循环。在归一化那一步就把来源是自己的消息丢掉,比让每个处理器各写一遍判断可靠。

怎么判断它真的做对了

「发一条能收到回复」不算验证通过,那只证明顺路径是通的。上线前至少要能观察到下面这几件事。

  • 重复投递不会造成重复回复。把同一条事件手动重投一次,会话里应该只多出一条回复,日志里应该看到第二次被去重丢弃。
  • 重启不丢消息。发消息的同时重启一次进程,恢复后那条消息仍然被处理完,说明落盘确实发生在 ACK 之前。
  • 下游变慢不会拖垮入口。把业务层人为拖到几秒,回调入口的响应时间应该基本不变;如果跟着变慢,说明业务逻辑还留在请求生命周期里。
  • 失败可追溯。随便挑一条最近的出向调用,能凭 x-request-id 在日志里定位到它,并且知道它对应哪条入站事件。
  • 静默能被发现。事件量掉到零时得有人知道。这类静默检测按同比基线判断比按绝对值可靠,博客「企业微信集成怎么做监控告警」列了值得长期盯的几个指标。

这几条都过了,再往里加业务逻辑就是安全的。反过来先堆业务、事后补这些,通常要返工一遍。

常见问题

企业微信机器人开发需要自己搭服务器吗?
需要一个公网可达、能接收回调并调用发送接口的服务。逻辑可以很轻,但公网可达、幂等去重和限流这三件省不掉。函数计算也可以,前提是有地方落盘。
群机器人和单聊机器人开发上有什么区别?
结构一致,差异只在消息来源、是否需要被 @ 才响应、要不要带群内上下文这三处。在接入层把来源归一化之后,路由层和业务层不用区分。
企业微信机器人怎么接入大模型?
在业务层做。路由命中后把用户消息和会话历史交给模型,拿到文本再走同一个发送动作下发。换模型时接入层和路由层不需要改动。
机器人为什么会重复回复同一条消息?
事件被重复投递又没有去重。响应超时或返回非 2xx 都会触发重投。用事件唯一标识做幂等键,并把处理器名字拼进 key,不同处理器才不会互相顶掉。
接口返回成功了,对方却没收到消息,怎么排查?
先分清「请求被受理」和「消息已送达」。响应体 code 为 0 说明调用被接受,送达情况要看事件回调;排查时带上响应头 x-request-id 定位这次调用。

准备好动手了?

精确字段、鉴权与端点以线上文档为准;可在控制台创建密钥后联调。

相关指南