Hermes 钉钉媒体收发助手
将以下提示词粘贴到你的 AI 对话框中:
请根据 https://skillhub.cn/install/skillhub.md,安装 @user_9ab57f1a/hermes-dingtalk-media。
技能介绍
要解决的问题
Hermes 对接钉钉机器人时,助手按惯例返回 MEDIA:/absolute/path/to/file,期望对方收到一张独立图片或一个文件附件。但实际结果是消息发出去了,钉钉侧却没有出现对应的媒体卡片。排查起来很容易误判:回调日志显示网关已经触发发送逻辑,上传接口也返回了 media_id,可群里的机器人就是不显示图片。
常见诱因有两个。一是把一对一私聊回复打到了 privateChatMessages/send,这条路径对 Hermes 的媒体回复并不友好,即使上传成功也可能抛 resource.not.found。二是没有把入站回调上下文保留下来,发送时只能退回到配置文件里的 app/client id,一旦 robotCode 或接收人不对,同样报 resource.not.found 或「robot 不存在」。
技能如何工作
这个技能不重写 Hermes 核心,而是补齐两处:路由规则和辅助脚本。
路由规则
- 群聊媒体走
POST https://api.dingtalk.com/v1.0/robot/groupMessages/send,接收方填openConversationId。 - 一对一媒体走
POST https://api.dingtalk.com/v1.0/robot/oToMessages/batchSend,接收方填userIds: [sender_staff_id]。 robot_code优先取回调消息里的message.robot_code,只在没有回调上下文时才用配置兜底。- 用
chat_id把回调上下文串起来,因为它同时提供了conversation_type、robot_code和sender_staff_id。 - 本地文件先上传到
POST https://oapi.dingtalk.com/media/upload,图片扩展名用type=image,其他用type=file,multipart字段名为media,拿到media_id后再发。
辅助脚本
scripts/fix_dingtalk_media_support.py:检查 Hermes 源码树里是否已实现钉钉媒体逻辑、依赖的 Python 模块是否齐全、语法是否正确,并可选择重启某个 profile 的网关。仓库根路径通过--repo-root传入,也可以用环境变量HERMES_REPO指定,脚本本身不含任何凭据。scripts/send_dingtalk_file.py:用某个 profile 的凭据,通过机器人媒体接口直发一个本地图片或文件,用来验证原生投递是否正常,同样不改动 Hermes 源码。一对一发送时需要传入钉钉staff/user id。
在 Hermes 侧要落地的实现点
gateway/platforms/dingtalk.py:实现send_image_file、send_document、send_video;把入站downloadCode解析成本地缓存文件;如果关闭或重连会卡住,改用可取消的 Stream 循环替代dingtalk_stream.DingTalkStreamClient.start(),并在打开连接前调用pre_start()。tools/send_message_tool.py:支持解析钉钉MEDIA:前缀;没有回调上下文的直接发送场景下,一对一媒体必须显式给出钉钉 user id,否则默认走群发。agent/prompt_builder.py:加入钉钉平台提示,说明原生媒体可以用MEDIA:/absolute/path/to/file发送。
适用边界与注意点
这个技能的前提是回调已经能正常到达 Hermes。如果日志里连「DingTalk callback received」和 platform=dingtalk 都没有,说明问题出在 Stream Mode、网络连通性或网关进程上,应该先修连接,而不是继续调媒体逻辑。
判读失败信号时要分清层次:上传之后才报 resource.not.found,说明上传是成功的,问题在发送端点或 robotCode/接收人;一对一私聊里报 HTTP 400 resource.not.found,就该从 privateChatMessages/send 切到 oToMessages/batchSend;robot 不存在 基本都是 robotCode 取错,回到回调里拿。
另外,群消息接口的鉴权字段叫 access_token,机器人新接口用的是 robotCode,两套写法不要混用。日志里记录媒体路由时记得脱敏,例如只输出 Sending DingTalk media via oto robot API; robot_code_source=callback recipient_source=sender_staff_id。
使用场景
- Hermes 机器人回复里写了 `MEDIA:/absolute/path`,但用户没收到图片;需要用 `send_dingtalk_file.py` 从当前 profile 单独直发一次,确认问题在路由还是上传。
- 一对一私聊的媒体回复被打到 `privateChatMessages/send`,上传成功却报 `resource.not.found`;需要改走 `oToMessages/batchSend`,并从回调里取 `sender_staff_id`。
- 修改了 `gateway/platforms/dingtalk.py`、`send_message_tool.py`、`prompt_builder.py` 后,需要运行 `fix_dingtalk_media_support.py` 验证实现是否完整、语法是否通过。
- 群里的机器人发文件提示 `robot 不存在`;需要确认发送时是否用了回调里的 `message.robot_code`,而不是配置里可能过期的 `robotCode`。
适合人员
- 正在搭建 Hermes 钉钉机器人并要让 `MEDIA:` 图片/文件真正独立送达的后端工程师。
- 维护多个 Hermes profile 网关、需要不改核心代码就能验证各 profile 媒体直发的开发者。
- 正在修改 Hermes 平台层源码,需要快速跑通实现检查和语法校验的开发者。
- 排查 Hermes 到钉钉媒体链路问题,需要按上传与发送两段日志区分失败点的工程师。