# my_hermes_agent **Repository Path**: ckdkambh/my_hermes_agent ## Basic Information - **Project Name**: my_hermes_agent - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-09-16 - **Last Updated**: 2026-09-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # my_hermes_agent ## ⭐ 你要做的只有 3 件事(其余文件全部不用管) > 要做「机器人只能跟指定一个人对话」的话,另外加 `.env` 两行 —— 见下方 > [环境变量要改吗?](#环境变量要改吗) 里的「可选」那段。 ### 1. 替换 1 个文件 把仓库根目录的 [`adapter.py`](adapter.py) 整份复制过去,覆盖这个路径: ``` %LOCALAPPDATA%\hermes\hermes-agent\plugins\platforms\dingtalk\adapter.py ``` ```powershell $H = "$env:LOCALAPPDATA\hermes" Copy-Item "$H\hermes-agent\plugins\platforms\dingtalk\adapter.py" "$H\adapter.py.bak" # 先备份 Copy-Item "D:\code\hermes\adapter.py" "$H\hermes-agent\plugins\platforms\dingtalk\adapter.py" -Force ``` ### 2. 改 1 个配置文件的 2 行 打开 `%LOCALAPPDATA%\hermes\config.yaml`,确认 `gateway.platforms.dingtalk.extra` 下面是这两行: ```yaml card_template_id: <公司那个模板的 ID> card_commit_variables: false ``` > `card_commit_variables: false` 是这次修复的关键。**不加它,卡片会「有打字机动画但最终空白」。** ### 3. 重启网关 ```powershell & "$H\bin\hermes.exe" gateway stop & "$H\bin\hermes.exe" gateway run --accept-hooks ``` 然后钉钉发一句话验证。日志里应该能看到一行 `finalize=True` 且 **len 等于回复字数**(不是 0)。 --- ## 环境变量要改吗? ### 必做:确认这两个是**公司那个钉钉应用**的值 仓库里没有真值(公开仓库不该有): ``` DINGTALK_CLIENT_ID=... DINGTALK_CLIENT_SECRET=... ``` ### 可选:让机器人**只能跟某一个人**对话 `.env` 里加这两行(`C:\Users\A\AppData\Local\hermes\.env`): ``` DINGTALK_ALLOW_ALL_USERS=false DINGTALK_ALLOWED_USERS=0402091118551727040202 ``` 第一行是开关,**不关掉上面那个白名单完全不生效**(任何钉钉用户都能指挥你的 Agent)。 > ⚠️ **第二行填 userId,不是工号。** 钉钉回调里**根本不带工号**;那串 > `$:LWCP_v1:$...` 加密 senderId **在网关层也匹配不上**。两个填错都表现为 > 「机器人对谁都装死」。 > > **userId 从哪来:** 私有云智能体平台能拿到就直接用;本机可翻 > `%LOCALAPPDATA%\hermes\sessions.json`,找到你自己那条会话,`user_id` 字段里 > 那串纯数字就是它(v1.4 之后;v1.3 及以前它在 `user_id_alt` 里)。 > 原理见 [`AGENTS.md`](AGENTS.md) 的 v1.4 说明。 ### `DINGTALK_HOME_CHANNEL` 不用配(v1.5 起自动) 以前第一次对话时,机器人会回一句: ``` 📬 No home channel is set for Dingtalk. ... Type /sethome to make this chat your home channel, or ignore to skip. ``` 给领导演示时这句话很难看。**v1.5 起不用管**:第一个**通过白名单的私聊**会被 自动记为 home channel(静默写进 `.env` 的 `DINGTALK_HOME_CHANNEL`), 上面那句提示不会再出现,定时任务也有投递目标了。`config.yaml` **不会被改动**。 如果你想**自己指定**一个目标(或关掉这个自动行为),在 `.env` 里手工写上 `DINGTALK_HOME_CHANNEL=<目标 chat_id>` 即可 —— 已配置的会被优先尊重, 自动逻辑直接跳过。 --- ## 仓库里其它文件要不要管? | 文件 / 目录 | 管不管 | |---|---| | **`adapter.py`** | ✅ **就它。这是唯一要替换的** | | `config-backup/` | ⬜ 参考。 `config.yaml` 是完整配置,想整份覆盖也行(模板 ID 得换成公司的) | | `hermes-handover-2026-09-16/` | ⬜ 排障文档 + 模板分析,**出问题了再去翻** | | `SECURITY-KEY-ROTATION.md` | ⚠️ 密钥轮换清单,**该做还没做** | | `_install/`、`Hermes-部署说明.md`、其它 | ⬜ 历史记录,不用管 | --- ## 这个仓库是什么 Hermes Agent(Nous Research)在本机的部署产物与钉钉接入改造。**不是 Hermes 上游源码**, 是围绕它做的配置、**改造后的完整源码文件**、排障记录与交接材料。 > 交付方式:**不做补丁。** 需要改的文件一律以**完整源文件**形式存放在本仓库, > 部署时整份覆盖即可。理由见下方「为什么不用补丁」。 ```bash git clone git@gitee.com:ckdkambh/my_hermes_agent.git ``` > 更新时间:2026-09-19(GMT+8)| adapter.py 版本 **v1.5**,MD5 `005a20e1b50d3a1155d1dab51b4221a8` > 上游版本:Hermes Agent `v2026.8.19` / 版本号 `0.20.5` / commit `fcbd1076a93841fa88855acce810e342a5b78101` > > ⚠️ **本仓库是公开的,不含任何密钥。** 曾有一次误提交把密钥推上去过, > 已重写历史并清理,**请务必执行 [`SECURITY-KEY-ROTATION.md`](SECURITY-KEY-ROTATION.md) 里的轮换清单**。 --- ## 从哪开始看 | 你是来干什么的 | 看这个 | |---|---| | **照上面 3 步部署** | 就是这个 README,不用看别的 | | 🔐 **把泄露的密钥轮换掉** | [`SECURITY-KEY-ROTATION.md`](SECURITY-KEY-ROTATION.md) | | **接手继续干(新机器 / 新 AI)** | [`hermes-handover-2026-09-16/HANDOVER.md`](hermes-handover-2026-09-16/HANDOVER.md) ← **主文档** | | **只想把配置搬过去** | [`config-backup/README.md`](config-backup/README.md) | | **钉钉 AI 卡片模板问题** | [`hermes-handover-2026-09-16/DINGTALK-AI-CARD-TEMPLATE.md`](hermes-handover-2026-09-16/DINGTALK-AI-CARD-TEMPLATE.md) | | **当前在用的卡片模板**(ID / 导出 / 变量契约 / 实测结论) | [`hermes-handover-2026-09-16/card-templates/`](hermes-handover-2026-09-16/card-templates/) | | **拿到公司模板后先跑这个** | `python hermes-handover-2026-09-16/export_dingtalk_card_template.py`(按 ID 拉模板变量声明并判定契约)
`python hermes-handover-2026-09-16/analyze_card_template.py <模板导出.json>`(分析组件树/状态绑定) | | **想知道踩过哪些坑** | HANDOVER.md §7(18 条),或 [`Hermes-部署说明.md`](Hermes-部署说明.md) | --- ## 目录结构 ``` . ├── README.md ← 本文件 ├── adapter.py ★ ★ 唯一权威源码:改好的钉钉适配器全文 ├── SECURITY-KEY-ROTATION.md ← 泄露密钥轮换清单(该做还没做的事) ├── .gitignore ← 密钥文件拦截规则 ├── .gitattributes ← 换行符规则(凭据/日志逐字节保真) ├── Hermes-部署说明.md ← 部署过程说明(较早版本,细节以 HANDOVER.md 为准) │ ├── hermes-handover-2026-09-16/ ★ 交接主包 │ ├── HANDOVER.md ← 13 节完整交接文档 │ ├── DINGTALK-AI-CARD-TEMPLATE.md ← 卡片模板机制 / 变量契约 / 自建模板规格 │ ├── export_dingtalk_card_template.py ← 按模板 ID 拉取声明变量并判定契约 │ ├── analyze_card_template.py ← 分析模板导出的组件树与状态绑定 │ ├── card-templates/ ← 当前在用的模板导出 + 分析结果 + 说明 │ └── files/ │ ├── config.yaml.final │ └── env.final.redacted 脱敏版 .env │ ├── config-backup/ ★ 配置快照(已脱敏,无密钥) │ ├── README.md ← 每一项配置为什么这么写 + 远端要替换哪些文件 │ ├── config.yaml 当前生效配置(= %LOCALAPPDATA%\hermes\config.yaml) │ ├── config.yaml.bak-premodel migrate 后 / 改模型前的原始版本 │ ├── .env.template 环境变量模板(占位符,需自己填真值) │ ├── adapter.py 适配器全文副本(与根目录 adapter.py 同源) │ └── SOUL.md │ ├── _install/ 安装与启动日志(排查证据,已脱敏) │ ├── install.log / install2.log 官方安装器输出 │ ├── dingtalk-deps.log 钉钉依赖安装 │ └── hermes-gateway{,2..9}.log 历次网关启动 │ └── .workbuddy/memory/ 逐日工作记录(2026-09-15 / 2026-09-16 / 2026-09-17) ``` 以下内容**不在本仓库内**(`.gitignore` 拦截): - `hermes-agent-v2026.8.19/`(692MB 上游源码快照) - `_local-secrets/`(`.env` / `auth.json` / `channel_directory.json` / `gateway_state.json` 的本地副本) --- ## 这个环境在做什么 Hermes 原生支持钉钉平台(`plugins/platforms/dingtalk/adapter.py`),但上游的 **钉钉 AI 卡片 V2 收尾流程**有 bug:收尾帧(`isFinalize=true`)被发成**空内容**, 而 V2 卡片的可见正文只由 streaming 帧承载 → **卡片播完动画变空白,且所有卡 API 都返回 2xx**,日志里看不出任何异常,极难排查。 改动全部集中在 `adapter.py` 一个文件里(仓库根目录那份就是完整成品源码): 1. **建卡时补全 `cardParamMap`** —— 写上 `content` / `config` / `quoteContent` / `flowStatus:"2"` / `hasAction` / `stop_action`,并加降级重试。 2. **收尾帧带上正文**(v1.2 的关键改动)—— 最后打字机帧 → 刷帧 → **关流帧携带全文**。 早先版本以为「关流帧的正文会被丢弃」,于是改走 `PUT /v1.0/card/instances` 落地; 用 OpenClaw 在同模板、同 AppKey 上的实证日志证明该理论不成立,遂改回钉钉文档规定的做法。 3. **补打字机帧 + 去重守卫** —— finalize 时先发一帧非最终帧驱动逐字动画, 再加 `_last_card_commit` 拦住同一轮被两个调用方重复提交。 `PUT /v1.0/card/instances` 的落地仍保留,但**可用 `card_commit_variables` 开关关闭**: 当模板的「完成」态渲染的是被流式组件占用的 `content` 变量时,落地反而会把卡片切成 空白状态,此时必须设为 `false`(当前生效配置就是 `false`,见 [`config-backup/README.md`](config-backup/README.md) §2.3)。 源码之外还有两处**纯配置**修复:模型路由(400/401)与 `busy_input_mode: queue` (两个问题的答案同卡覆盖)。 ### 为什么不用补丁 补丁在这个场景有三个实打实的坏处,所以现在**只发完整源文件**: 1. **换行符会毁掉补丁。** 本机 Git for Windows 默认 `core.autocrlf=true`,clone 会把 补丁里的 LF 改写成 CRLF,而目标文件是 LF → 上下文对不上,补丁打不上(实测踩过, 见 HANDOVER.md §7 坑 #18)。 2. **上游一动就失效。** `adapter.py` 是 Hermes 自己的源码,上游每次改动都可能让补丁失配, 只能人工比对。 3. **补丁文件本身就是泄露面。** 历史上那份 `03-dotenv.patch` 因为含 `.env` 的上下文行, 把真实密钥一起带进了公开仓库(详见 [`SECURITY-KEY-ROTATION.md`](SECURITY-KEY-ROTATION.md))。 完整源文件没有这些问题:整份覆盖即生效,用 md5 就能判断是否替换到位。 (旧的补丁与补丁工具已在 2026-09-17 从版本库移除;需要考古可用 `git log --diff-filter=D --name-only` 找回。) 细节、证据日志、SDK 符号核对、回滚方法全部在 [`hermes-handover-2026-09-16/HANDOVER.md`](hermes-handover-2026-09-16/HANDOVER.md)。 --- ## 快速上手 ```bash H="$LOCALAPPDATA/hermes" # 0) 备份将被覆盖的两个文件(回滚用) cp "$H/hermes-agent/plugins/platforms/dingtalk/adapter.py" "$H/adapter.py.bak" cp "$H/config.yaml" "$H/config.yaml.bak" # 1) 覆盖适配器 —— 完整源文件,整份替换,不打补丁 cp adapter.py "$H/hermes-agent/plugins/platforms/dingtalk/adapter.py" # 校验一致性(应与 .gitattributes 约定同为 LF): # Windows: Get-FileHash "$H\hermes-agent\plugins\platforms\dingtalk\adapter.py" -Algorithm MD5 # 期望 005a20e1b50d3a1155d1dab51b4221a8 # 2) 恢复配置(.env 需自己从模板填真值,仓库里没有) cp config-backup/config.yaml "$H/config.yaml" cp config-backup/.env.template "$H/.env" # 然后编辑 .env 填入真值 # 3) 重启网关 "$H/bin/hermes.exe" gateway stop "$H/bin/hermes.exe" gateway run --accept-hooks ``` --- ## 四个必须知道的注意事项 1. 🔐 **密钥曾经泄露到公开仓库。** 已重写 git 历史并清理,但**旧密钥必须轮换**才算真的安全 (历史可能已被抓取)。清单见 [`SECURITY-KEY-ROTATION.md`](SECURITY-KEY-ROTATION.md)。 2. **`hermes update` 会 stash 掉本地对源码的改动。** `config.yaml` 里 `updates.non_interactive_local_changes: stash`,升级后你对 `adapter.py` 的修改会被静默 stash 掉,空卡片 bug 复发。升级后**必须重新覆盖一次** `adapter.py`。 3. **`DINGTALK_ALLOW_ALL_USERS=true` 值得收紧。** 当前允许任意钉钉用户驱动你的 Agent, 会消耗模型额度并可能触发工具调用。 4. **OpenClaw 计划任务仍开着。** `\OpenClaw Gateway` 会在登录时拉起 OpenClaw, 与 Hermes 抢同一个钉钉应用(同 appKey 多实例 → 消息随机分发)。 已决定停用 OpenClaw、复用其钉钉应用给 Hermes,建议禁用该任务。