从源码看 MateClaw 飞书渠道适配:消息、文件、语音、卡片和审批是怎么串起来的

这篇文章不讨论模型效果,只看一个更具体的问题:一个 Java 项目把飞书接入到 Agent 系统时,需要处理哪些工程细节。

飞书通道看起来只是"收消息、回消息",但真正接入后会遇到很多边界情况:长连接会断、事件会回放、群聊要不要 @ 才响应、文件和图片只有 file_keyimage_key、语音没有现成文本、长回复直接发文本体验不好、生成文件不能只给一个内部链接,写操作也不能默认自动执行。

MateClaw 近期围绕飞书渠道做了一组实现,比较适合作为企业 IM 接入 Agent 的源码案例来看。

一、整体链路

飞书消息进入 MateClaw 后,大致会经过四步。

第一步是通道接入。飞书可以走 WebSocket 长连接,也可以走 Event Subscription Webhook。WebSocket 模式减少了公网回调配置成本;Webhook 模式则要求配置 encrypt_key,否则启动时直接拒绝,避免未认证回调触发消息处理。

第二步是消息归一。飞书原始事件会被解析成统一的通道消息,包括发送人、群聊、消息 ID、文本内容和多模态 content parts。

第三步是 Agent 执行。归一后的消息交给后续路由和 Agent 运行时处理。如果中间触发工具调用,还会进入工具权限和审批链路。

第四步是飞书侧输出。普通文本直接发送,长内容可以走 CardKit 流式卡片,生成文件会转成飞书原生附件,高风险工具调用会生成审批卡片。

二、长连接稳定性:重点不是"能连上",而是"长期能收消息"

飞书 WebSocket 接入里,比较容易忽略的是长期运行稳定性。

MateClaw 的飞书适配里做了几类处理:

  • 启动时刷新 tenant_access_token,并设置定时刷新;
  • WebSocket 连接由适配器自己控制重连,不直接依赖 SDK 自动重连;
  • 注册多个 IM 事件的空处理器,避免未处理事件触发 HandlerNotFoundException
  • 记录最近事件时间,用静默断连看门狗发现"连接还在但不收事件"的情况;
  • 根据消息创建时间过滤旧事件,避免重连后重复处理过期消息;
  • 关闭 WebSocket 时调用 SDK 的 close(),释放连接、ping 循环和线程池。

这些处理更偏工程稳定性,不属于模型能力,但在企业 IM 场景里很重要。通道层如果偶发失联或重复执行,后面的 Agent 再强也会被放大成系统问题。

三、群聊过滤:require_mention 不能靠字符串猜

群聊里通常不希望机器人看到每句话都响应。飞书通道提供了 require_mention 配置,用于控制群聊中是否必须 @机器人 才处理。

这里的实现没有靠文本前缀判断,而是读取飞书事件里的 mentions 字段,并提前获取 bot 自己的 open_id。如果获取 bot 身份失败,会进入短时间失败缓存,避免每条群消息都同步请求一次接口。

这个细节说明一个问题:IM 通道接入不能只看消息正文。消息元数据、发送人、群聊类型、mentions 字段,都会影响后续路由。

四、文件和图片:不能只把 file_key 交给模型

飞书文件、图片、音频、视频消息通常包含资源 key。这个 key 对模型没有直接意义,后续工具也未必能直接访问飞书 CDN。

MateClaw 的处理方式是:在通道层下载资源,把下载结果写入 MessageContentPart,包括本地文件路径、文件名、content type,以及面向前端展示的临时文件 URL。

这样做的好处是后续 Agent 不再只看到 [文件][图片] 这种占位符,而是能拿到可读的文件对象。图片可以交给视觉模型,文档可以交给文件读取工具,视频和音频也能保留为后续处理材料。

五、最近文件缓存:解决"帮我看刚才那个文件"

企业 IM 里经常出现这种对话:

用户先上传一个文件

随后补一句:帮我总结一下

如果系统只处理当前这一条文本消息,Agent 会不知道"刚才那个文件"是什么。

飞书通道里有一层最近文件缓存。文件消息进入后,会按会话缓存最近几个文件,并设置过期时间。后续同一会话的文本消息进来时,适配器会把最近文件注入到当前 content parts 里。

这不是复杂算法,但很贴近日常使用习惯。它把"人类自然表达里的上下文"补成了机器可处理的上下文。

六、语音消息:先下载,再走 STT

飞书语音消息和部分其他 IM 渠道不同,事件里通常没有直接给出 ASR 文本。适配器拿到的是音频资源信息。

MateClaw 的实现是先下载音频文件,再调用 SttService 转写。转写成功后,会把文本作为一个普通 text part 插到音频 part 前面。这样后续 prompt 构建时,Agent 看到的是语音内容,而不是只有 [音频]

同时这一步是 best-effort:如果 STT 没有配置、下载失败或转写失败,消息仍然可以继续流转,只是退化成音频占位。

七、输出侧:流式卡片和原生附件

普通 IM 机器人经常把长回答一次性发出去,或者把生成文件变成一个内部链接。这两种方式在飞书里都不够自然。

MateClaw 飞书通道做了两类输出适配。

第一类是 CardKit 流式卡片。长回复生成时,先创建一个 streaming card,然后持续 append 内容,最后关闭 streaming 状态。这样用户看到的是飞书卡片内逐步更新的内容。

第二类是生成文件回传。适配器会识别回答里的生成文件 URL,通过 GeneratedFileScrubber 取到文件字节,再用飞书媒体上传能力发成原生附件。卡片或文本里则保留文件名提示,而不是直接暴露内部下载地址。

八、飞书原生工具:从消息通道走向工具通道

飞书通道不只是消息收发,也可以把飞书 OpenAPI 暴露为 Agent 工具。

当前飞书工具提供器里可以看到几个代表性工具:

  • feishu_calendar_list_events:读取日历事件;
  • feishu_doc_read:读取飞书文档纯文本;
  • feishu_doc_create:创建飞书文档。

其中读取类工具默认可用,写入类工具默认关闭,并会被标记为需要更高等级管控。工具输入输出走 JSON,便于模型后续分支处理,也便于系统做审计。

九、ToolGuard 审批卡片

Agent 能调用工具后,权限问题会立刻出现。

例如创建飞书文档、发送消息、修改配置这类操作,不能简单等同于模型"回复一句话"。MateClaw 的处理方式是让高风险工具调用进入 ToolGuard,再由飞书卡片承载审批动作。

审批卡片里会展示工具名、风险等级、参数摘要,并提供批准和拒绝按钮。用户点击后,卡片事件会回到后端处理,审批完成后卡片状态也会更新。

这里的价值不在于按钮本身,而在于把"工具调用"变成了一个可暂停、可确认、可追踪的运行流程。

十、源码里能看到的分层

从模块职责看,飞书接入没有全部堆在一个方法里,而是拆成了几层:

  • 飞书适配层:负责事件接入、消息解析、媒体下载、消息发送;
  • 卡片层:负责普通卡片、流式卡片、审批卡片;
  • 媒体层:负责上传、大小策略、生成文件清理;
  • 工具层:负责把飞书日历、文档等能力注册为 Agent 工具;
  • 审批层:负责写操作和高风险工具调用的人类确认。

这种拆法的好处是:消息通道、工具通道、审批通道可以各自演进。后续要扩展企微、钉钉、Slack,也可以复用其中一部分通道抽象。

十一、总结

飞书渠道适配看上去是一个"IM 机器人"问题,实际涉及连接稳定性、事件去重、群聊过滤、多模态资源下载、语音转写、最近文件上下文、流式卡片、原生附件、工具注册和审批治理。

MateClaw 近期这组实现的特点,是把这些问题放在同一条运行链路里处理:消息进来时尽量还原上下文,Agent 执行时保留工具边界,结果返回时尽量使用飞书原生形态。

如果只做问答机器人,很多细节可以省掉;但如果要让 Agent 进入企业协作系统,这些通道层细节迟早都要补上。

参考链接

相关推荐
Miss roro2 小时前
通用OA能不能替代专业法务系统?钉钉飞书和律杏法务云的实测对比
java·钉钉·飞书·法律科技·企业诉讼管理·法务管理系统
广州灵眸科技有限公司20 小时前
瑞芯微RV1126B开发板(EASY-EAI-PI2) 开发套件组装上电
网络·数据库·人工智能·算法·飞书
睡醒了叭1 天前
小白超详细记录-openclaw本地部署与飞书连接(windows)
飞书
蓝速科技1 天前
蓝速科技智能会议预约屏:打通钉钉飞书,终结会议室“撞车”难题
科技·钉钉·飞书
XLYcmy3 天前
面向Agent权限系统的快速审计工具
python·网络安全·ai·llm·飞书·agent·字节跳动
NiceCloud喜云5 天前
Claude Code 跑 HyperFrames 实测:本地生成 AI 视频素材全流程
java·运维·人工智能·自动化·json·音视频·飞书
逆向命运5 天前
PC企微搜索手机号窗口绕过
c语言·汇编·c++·飞书·企业微信
NiceCloud喜云6 天前
Claude Code Routines 实战:三种触发器跑通云端自动化编码
android·运维·数据库·人工智能·自动化·json·飞书
不正经绣才6 天前
【扣子Coze教程】1分钟提取500+小红书笔记评论,自动同步飞书(附源码)
飞书·教程·工作流·coze·扣子