给工单 Agent 加了个飞书入口,用户在群里 @ 机器人,Agent 处理工单。头一周踩坑不少,事件重复消费,同一句话机器人回了三遍。长任务没做异步,用户等到超时也没见回答。token 没缓存,每次回复都现取,接口调用多了一半。这篇把坑和解法整理成一份接入清单。
先给架构和结论。
| 环节 | 坑 | 解法 |
|---|---|---|
| 订阅握手 | challenge 校验没回 | 原样回传 challenge 字段 |
| 事件去重 | 同一事件重复消费 | event_id 做幂等键 |
| 凭证 | token 频繁现取 | 缓存加提前刷新 |
| 回复 | 长任务拖到超时 | 先 ack,完成后主动推送 |
一、订阅握手
飞书事件订阅第一次配置时,平台会向回调地址发一个带 challenge 字段的请求,服务端原样把这个字段塞回响应体就行。我头一次接的时候按普通 POST 请求处理,把整包 JSON 校验了一遍,challenge 反而没返回,订阅一直配不上。握手过了,才有后面的故事。
二、事件去重
飞书的事件推送是至少一次语义,网络重试会让同一个事件到达多次。不做幂等,用户一句话,机器人回三遍。
python
def handle_event(ev):
if redis.set(f"ev:{ev.event_id}", 1, nx=True, ex=600):
process(ev)
return ack()
用 event_id 做幂等键,setnx 抢锁,抢到才处理。过期时间设十分钟,既挡住重试风暴,也不怕服务重启丢状态。
三、凭证管理
tenant_access_token 有效期两小时,接口有频率限制。每次回复现取一次 token,量一上来就撞限流。解法是进程内缓存,快过期时提前五分钟刷新,别等撞了限流再想起来。
四、异步回复
Agent 处理工单要十几秒,回调接口卡在那等结果,飞书那边超时重试,事件重复,越等越乱。改成两段式,回调接口收到事件先 ack,任务进队列,处理完走消息接口主动推送结果。
超长回答也别硬塞一条消息。拆成卡片,头部给结论,展开看细节,群里阅读体验好很多。
五、限流与重试
群聊场景机器人回复有频率限制,批量通知务必合并。消息接口报限流时按指数退避重试,这套方法在扛量篇里展开过,原样适用。
六、总结
| 环节 | 一句话 |
|---|---|
| 握手 | challenge 原样回传 |
| 去重 | event_id 幂等,setnx 加过期 |
| 凭证 | token 缓存,提前刷新 |
| 回复 | 先 ack 后处理,结果主动推送 |
| 限流 | 批量合并,退避重试 |
结合系列。MCP Server 篇讲工具怎么写,传输方式篇讲通道怎么选,这篇把 Agent 从"能调通"推到"能挂在 IM 上用"。扛量篇的限流退避在消息接口上原样适用,结构化输出篇的校验在工单入库前也原样适用。
来源。接入流程按飞书开放平台官方文档(事件订阅、消息接口)与自己的项目实践整理,踩坑记录来自接入第一周的真实线上问题。
觉得有用点个关注。