Hermes Agent + 钉钉适配文档(重点解决图片引用识别问题)

更新日期: 2026-04-29

适用: Hermes Agent + DingTalk (钉钉) Stream Mode


目录

  1. 概述
  2. 运行行为
  3. 前置依赖
  4. 钉钉机器人创建
  5. [Hermes 配置](#Hermes 配置)
  6. [启动 Gateway](#启动 Gateway)
  7. 消息处理机制
  8. 图片识别功能
  9. [DashScope 视觉配置](#DashScope 视觉配置)
  10. 故障排查
  11. 安全注意事项
  12. 关键文件

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:创建应用

  1. 访问 钉钉开发者后台
  2. 登录钉钉管理员账号
  3. 点击 应用开发企业内部应用创建应用
  4. 填写:
    • 应用名称 :如 Hermes Agent
    • 描述:可选
  5. 创建后进入 凭证与基本信息 ,获取 Client ID (AppKey)和 Client Secret(AppSecret)

注意: Client Secret 仅在创建时显示一次,丢失后需重新生成。

步骤 2:启用机器人能力

  1. 在应用设置页面,点击 添加能力机器人
  2. 启用机器人能力
  3. 消息接收模式 中选择 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 标准协议):

连接流程:

  1. POST /v1.0/gateway/connections/open --- 获取 WebSocket URL 和 ticket
  2. WebSocket 连接 wss://wss-open-connection.dingtalk.com:443/connect?ticket=...
  3. 订阅 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 会:

  1. 从消息中提取 downloadCode
  2. 通过两步骤下载图片(见下方流程)
  3. 保存为本地临时文件
  4. 将图片 base64 编码后发送给视觉模型
  5. 将分析结果作为回复发送给用户

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
相关推荐
小民AI实战笔记1 小时前
GitHub Actions + 钉钉,半小时搭个免费的热榜推送机器人
人工智能·aigc·ai编程
Flittly1 小时前
【LangGraph新手村系列】(3)PostgreSQL 持久化检查点:让状态跨越进程与重启
人工智能·python·langchain
麦芽糖02191 小时前
大模型二 Agent入门实战(AI私厨)
人工智能
拾贰_C1 小时前
【OpenClaw | openai | QQ】 配置QQ qot机器人
运维·人工智能·ubuntu·面试·prompt
码途漫谈1 小时前
Easy-Vibe开发篇阅读笔记(二)——前端开发之Figma与MasterGo入门
人工智能·笔记·ai·开源·ai编程·figma
Jmayday1 小时前
Pytorch:CNN理论基础
人工智能·pytorch·cnn
阿瑞说项目管理2 小时前
2026 智造升级:制造企业 Agent 从 0 到 1 落地指南,五大场景拆解实战路径
人工智能·agent·智能体·企业级ai
Mr_sst2 小时前
infra-ai模块宏观设计解析:业务与模型之间的中间层核心架构
大数据·人工智能·ai·llama
Slow菜鸟2 小时前
Codex CLI 教程(五)| AI 驱动项目从零到一:面向 Java 全栈工程师打造个人 ECC(V2版)
java·开发语言·人工智能