更新日期: 2026-04-29
适用: Hermes Agent + DingTalk (钉钉) Stream Mode
目录
- 概述
- 运行行为
- 前置依赖
- 钉钉机器人创建
- [Hermes 配置](#Hermes 配置)
- [启动 Gateway](#启动 Gateway)
- 消息处理机制
- 图片识别功能
- [DashScope 视觉配置](#DashScope 视觉配置)
- 故障排查
- 安全注意事项
- 关键文件
1. 概述
Hermes Agent 通过钉钉的 Stream Mode(DWClient 协议)作为聊天机器人运行。连接方式为长活 WebSocket,无需公网 IP、域名或 Webhook 服务器。回复使用钉钉 Session Webhook API 以 Markdown 格式发送。
核心特性:
- Stream Mode WebSocket 连接(NAT/防火墙穿透)
- 支持私聊(DM)和群聊
- 自动会话隔离(群聊按用户独立上下文)
- 图片自动下载 + 视觉模型分析
- 消息去重(5 分钟窗口)
- 自动重连(指数退避:2s → 5s → 10s → 30s → 60s)
- Markdown 格式回复(上限 20,000 字符)
2. 运行行为
| 场景 | 行为 |
|---|---|
| 私聊(DM) | 回复每条消息,无需 @ 提及。每个 DM 有独立会话。 |
| 群聊 | 仅在被 @ 时回复。未被 @ 的消息会被忽略。 |
| 多用户共享群 | 默认按用户隔离会话上下文。两人同一群的对话不共享记录。 |
会话模型:
yaml
# ~/.hermes/config.yaml
group_sessions_per_user: true # 默认:群聊按用户隔离
设为 false 时,整个群共享一个会话上下文。
3. 前置依赖
3.1 Python 包
bash
pip install dingtalk-stream httpx websockets
| 包 | 用途 |
|---|---|
dingtalk-stream |
钉钉官方 SDK(WebSocket 实时消息) |
httpx |
异步 HTTP 客户端(Session Webhook 回复) |
websockets |
WebSocket 连接 |
3.2 环境变量检查
bash
# 必须
DINGTALK_CLIENT_ID=<AppKey>
DINGTALK_CLIENT_SECRET=<AppSecret>
# 安全限制(必须设置)
DINGTALK_ALLOWED_USERS=<User-ID>
4. 钉钉机器人创建
步骤 1:创建应用
- 访问 钉钉开发者后台
- 登录钉钉管理员账号
- 点击 应用开发 → 企业内部应用 → 创建应用
- 填写:
- 应用名称 :如
Hermes Agent - 描述:可选
- 应用名称 :如
- 创建后进入 凭证与基本信息 ,获取 Client ID (AppKey)和 Client Secret(AppSecret)
注意: Client Secret 仅在创建时显示一次,丢失后需重新生成。
步骤 2:启用机器人能力
- 在应用设置页面,点击 添加能力 → 机器人
- 启用机器人能力
- 在 消息接收模式 中选择 Stream 模式(推荐,无需公网 URL)
步骤 3:获取钉钉 User ID
Hermes 使用钉钉 User ID 控制谁能与机器人交互。
获取方式:
- 联系钉钉组织管理员 --- User ID 在管理后台 通讯录 → 成员 中配置
- 或查看日志:启动 Gateway 后发送消息,日志中会记录
sender_id
5. Hermes 配置
方式 A:交互式设置(推荐)
bash
hermes gateway setup
选择 DingTalk,然后按提示粘贴 Client ID、Client Secret 和允许的用户 ID。
方式 B:手动配置
~/.hermes/.env:
bash
# 必须
DINGTALK_CLIENT_ID=your-app-key
DINGTALK_CLIENT_SECRET=your-app-secret
# 安全限制
DINGTALK_ALLOWED_USERS=user-id-1
# 多用户(逗号分隔)
# DINGTALK_ALLOWED_USERS=user-id-1,user-id-2
~/.hermes/config.yaml:
yaml
group_sessions_per_user: true
平台启用配置:
yaml
platforms:
dingtalk:
enabled: true
6. 启动 Gateway
前台启动
bash
hermes gateway
后台启动(tmux 持久化)
bash
tmux new-session -d -s hermes-gw \
"env DINGTALK_CLIENT_ID=xxx DINGTALK_CLIENT_SECRET=xxx \
PYTHONUNBUFFERED=1 ./venv/bin/python -m gateway.run"
连接确认日志
[Dingtalk] Connected via Stream Mode (WebSocket)
[Dingtalk] WebSocket URL obtained, endpoint=wss://wss-open-connection.dingtalk.com:443/connect
[Dingtalk] WebSocket connected
Gateway running with 3 platform(s)
7. 消息处理机制
7.1 DWClient 协议
Hermes 使用 DWClient 协议(钉钉 Stream Mode 标准协议):
连接流程:
POST /v1.0/gateway/connections/open--- 获取 WebSocket URL 和 ticket- WebSocket 连接
wss://wss-open-connection.dingtalk.com:443/connect?ticket=... - 订阅 topics:
CALLBACK---/v1.0/im/bot/messages/get(机器人消息)EVENT---*(所有事件)
消息格式:
json
{
"type": "CALLBACK",
"headers": {
"messageId": "...",
"topic": "/v1.0/im/bot/messages/get"
},
"data": "{...}"
}
7.2 消息类型处理
| 消息类型 | 结构 | 处理方式 |
|---|---|---|
| 普通文本 | {"content": "hello"} |
直接提取文本 |
| 富文本/@ | {"content": {"text": "@机器人 你好"}, "atUsers": [...]} |
提取 text 字段 |
| 引用文本 | {"isReplyMsg": true, "repliedMsg": {"content": {"text": "原文"}}} |
提取被引用文本 |
| 引用图片 | {"isReplyMsg": true, "repliedMsg": {"msgType": "picture", "content": {"downloadCode": "..."}}} |
下载图片 + 视觉分析 |
| 直接图片 | msgtype="image", downloadCode="..." |
下载图片 + 视觉分析 |
| richText 图片 | msgtype="richText", content: {richText: [{downloadCode: "..."}]} |
下载图片 + 视觉分析 |
7.3 回复机制(Session Webhook)
每条入站消息携带 sessionWebhook URL:
https://oapi.dingtalk.com/robot/sendBySession?session=xxx
回复 payload 格式:
json
{
"msgtype": "markdown",
"markdown": {
"title": "Hermes",
"text": "# 回复内容\n\n这里是 Markdown 格式的回复。"
}
}
注意:
- 每个 Session Webhook 有过期时间(
sessionWebhookExpiredTime) - 如果机器人重启或 Webhook 过期,需要用户重新发送消息获取新的 Webhook
- 缓存最多保存 500 个
chat_id → session_webhook映射
7.4 ACK 机制
每条消息处理后必须发送 ACK 确认:
json
{
"code": 200,
"headers": {
"contentType": "application/json",
"messageId": "..."
},
"message": "OK",
"data": "{}"
}
未发送 ACK 的消息会被钉钉服务器重试。
7.5 心跳保活
- WebSocket 连接每 30 秒发送 ping
- 10 秒超时未收到 pong 则断开重连
- 服务端
KEEPALIVE消息无需回复 - 服务端
disconnect消息触发正常断开流程
7.6 消息去重
使用 5 分钟滑动窗口去重,防止同一条消息被重复处理。
8. 图片识别功能
8.1 功能说明
当用户在钉钉中引用一张图片并 @Tina时,Hermes 会:
- 从消息中提取
downloadCode - 通过两步骤下载图片(见下方流程)
- 保存为本地临时文件
- 将图片 base64 编码后发送给视觉模型
- 将分析结果作为回复发送给用户
8.2 图片下载流程(两步骤)
Step 1: POST https://api.dingtalk.com/v1.0/robot/messageFiles/download
Body: {"robotCode": "<client_id>", "downloadCode": "<code>"}
Header: x-acs-dingtalk-access-token: <token>
→ Response: {"downloadUrl": "https://..."}
Step 2: GET <downloadUrl>
→ Response: 图片二进制数据
8.3 Access Token 获取
POST https://api.dingtalk.com/v1.0/oauth2/accessToken
Body: {"appKey": "<client_id>", "appSecret": "<client_secret>"}
→ Response: {"accessToken": "...", "expireIn": 7200}
Token 缓存 7200 秒(提前 60 秒刷新)。
8.4 文件命名安全
downloadCode 可能包含 /、+ 等文件系统非法字符,清理方式:
python
safe_code = "".join(c if c.isalnum() else "_" for c in download_code[:16])
tmp = NamedTemporaryFile(suffix=f"_dingtalk_{safe_code}.jpg")
8.5 媒体类型
| 场景 | media_types 值 |
说明 |
|---|---|---|
| 引用图片 | ["image/jpeg"] |
必须是 image/* 前缀才能被视觉流程识别 |
| 直接图片 | ["image/jpeg"] |
同上 |
关键修复 : 早期版本使用
"image"而非"image/jpeg",导致run.py中的mtype.startswith("image/")检查失败,视觉分析不被触发。
9. DashScope 视觉配置
9.1 问题背景
当主配置使用阿里云 Coding Plan 的 Anthropic 兼容端点时,视觉分析会返回 404 Not Found:
openai.NotFoundError: Error code: 404
根本原因 :resolve_vision_provider_client 创建的 AsyncOpenAI 客户端使用 OpenAI Chat Completions 信封,但端点 /apps/anthropic 期望 Anthropic Messages API 格式。两者不兼容。
9.2 正确配置
~/.hermes/config.yaml:
yaml
# 主聊天 --- 使用 Anthropic 兼容端点
model:
default: qwen3.5-plus
provider: custom
base_url: https://coding.dashscope.aliyuncs.com/apps/anthropic
api_mode: anthropic_messages
api_key: sk-sp-...
# 视觉分析 --- 使用 OpenAI 兼容端点
auxiliary:
vision:
base_url: https://coding.dashscope.aliyuncs.com/v1
model: qwen3.5-plus
api_key: sk-sp-... # 同一个 Coding Plan API Key
timeout: 120
9.3 端点对比
| 端点 | 协议 | 图片支持 | 用途 |
|---|---|---|---|
/apps/anthropic |
Anthropic Messages API | 需适配层 | 主聊天(文本) |
/v1 |
OpenAI Chat Completions | 原生支持 | 视觉分析(图片) |
dashscope.aliyuncs.com/compatible-mode/v1 |
OpenAI Chat Completions | 原生支持 | 按量付费端点 |
9.4 qwen3.5-plus 视觉能力
- 原生多模态理解(多模态推理、OCR、物体识别)
- 支持格式:JPEG、PNG、GIF、WEBP
- Base64 编码上限:10 MB
- 图片格式:
data:image/jpeg;base64,...
10. 故障排查
机器人不回复消息
| 可能原因 | 排查方法 |
|---|---|
| 机器人能力未启用 | 钉钉开发者后台 → 应用设置 → 确认「机器人」能力已开启 |
| User ID 未配置 | 检查 DINGTALK_ALLOWED_USERS 是否包含你的 ID |
| Stream 模式未选择 | 确认消息接收模式为「Stream 模式」 |
| Gateway 未运行 | `ps aux |
dingtalk-stream 未安装
bash
pip install dingtalk-stream httpx websockets
DINGTALK_CLIENT_ID 和 DINGTALK_CLIENT_SECRET required
确认 ~/.hermes/.env 中设置了这两个变量,或启动命令中传递了环境变量。
Stream 断连 / 重连循环
- 自动重连间隔:2s → 5s → 10s → 30s → 60s
- 检查凭证是否有效、应用是否被停用、网络是否允许 WebSocket 出站
No session_webhook available
- 每次入站消息提供一个新的 Session Webhook
- Webhook 过期或机器人重启后,需用户重新发送消息
- 这是钉钉的正常限制,机器人只能回复最近收到的消息
视觉分析返回 404
确认 auxiliary.vision.base_url 设置为 OpenAI 兼容端点:
yaml
auxiliary:
vision:
base_url: https://coding.dashscope.aliyuncs.com/v1 # 不是 /apps/anthropic
model: qwen3.5-plus
图片下载失败
查看日志中的具体错误:
| 日志信息 | 原因 | 解决 |
|---|---|---|
Failed to get access token |
API 凭证错误或网络不通 | 检查 Client ID/Secret |
Image download URL request failed HTTP 404 |
downloadCode 无效或过期 | 重新发送图片 |
No downloadUrl in response |
API 响应异常 | 检查钉钉 API 状态 |
Error downloading image |
临时文件创建失败 | 检查 /tmp 权限 |
发送图片后机器人无反应
日志特征:
msgtype=richText, text_val=None
Empty message! msgtype=richText, text_raw={} (type=dict)
原因 :钉钉直接发送的图片消息使用 msgtype=richText 结构,而非引用图片的 msgtype=text 结构。旧版适配器只处理 text 字段,未能识别 content.richText 中的图片。
解决:升级适配器,新增 richText 图片处理逻辑:
python
# 提取 content.richText 中的图片
content = data.get("content", {})
if isinstance(content, dict):
rich_text = content.get("richText", [])
for block in rich_text:
img_code = block.get("downloadCode") or block.get("pictureDownloadCode")
if img_code:
local_path = await self._download_image(img_code)
# ... 下载后走视觉分析流程
break
诊断命令
bash
# 检查 Gateway 进程
ps aux | grep gateway.run
# 查看实时日志
tail -f ~/.hermes/logs/gateway.log
# 查看错误日志
tail -f ~/.hermes/logs/errors.log
# 查看 agent 日志(含视觉分析)
tail -f ~/.hermes/logs/agent.log
# 检查 tmux 会话
tmux ls
tmux attach -t hermes-gw
11. 安全注意事项
必须设置
bash
DINGTALK_ALLOWED_USERS=<可信用户 ID>
未设置时,Gateway 默认拒绝所有用户(安全措施)。
凭证保护
- Client Secret 仅在创建时显示一次
- 不要将凭证提交到 Git
- 不要公开分享
sk-sp-...API Key
权限限制
授权用户拥有完整的 Agent 能力,包括工具使用和系统访问。仅添加可信用户。
12. 关键文件
Windows (开发环境)
| 文件 | 路径 |
|---|---|
| 钉钉适配器 | hermes-agent\gateway\platforms\dingtalk.py |
| 配置 | ~/.hermes/config.yaml |
| 环境变量 | ~/.hermes/.env |
WSL (运行环境)
| 文件 | 路径 |
|---|---|
| 项目根目录 | /home/x/hermes-agent/ |
| 配置 | /home/x/.hermes/config.yaml |
| 环境变量 | /home/x/.hermes/.env |
| 日志目录 | /home/x/.hermes/logs/ |
| Gateway 日志 | /home/x/.hermes/logs/gateway.log |
| Agent 日志 | /home/x/.hermes/logs/agent.log |
| 错误日志 | /home/x/.hermes/logs/errors.log |
| tmux 会话 | hermes-gw |
技能文档
| 文件 | 用途 |
|---|---|
x_skill.md |
X知识库(含图片识别能力说明) |
hermes_dingtalk.md |
本文档 --- 钉钉集成完整参考 |
版本历史
| 版本 | 日期 | 更新内容 |
|---|---|---|
| 1.0 | 2026-04-29 | 初始版本 --- 整合钉钉 Stream Mode 配置、图片识别、DashScope 视觉端点配置 |
| 1.1 | 2026-04-29 | 新增 richText 消息类型的图片处理(直接发送图片不再显示 Empty message) |