Hermes-Agent 系统级架构分析
分析对象:github.com/hermes-agent(hermes-agent v0.21.2,分支 main)
分析日期:2026-09-12 · 分析方式:只读分析,所有结论附「文件:行号」证据
代码规模:6056 个 Python 文件;核心目录 LOC:hermes_cli 204,498 / agent 111,118 / tools 102,568 / plugins 86,639 / gateway 83,865 / 根级 30,076
执行摘要
Hermes-Agent 是一个自我改进的个人 AI 代理:同一套 agent 内核横跨 CLI、消息网关(Telegram/Discord/Slack 等 ~34 个平台)、TUI、Electron 桌面端、ACP 协议五种界面运行,支持跨会话学习(memory + skills)、子代理委派、定时任务、真实终端与浏览器驱动,并通过插件与技能而非膨胀内核来扩展能力(AGENTS.md:12-16)。
整体架构受两条不变量约束(AGENTS.md:18-29):
架构骨架:AIAgent 窄腰内核(run_agent.py:211,14 个 Mixin 组合)+ 11 阶段同步 turn 状态机(agent/conversation_loop.py:1423,每阶段拆为 turn_*.py 兄弟文件)+ ToolRegistry 单例(tools/registry.py:368,进程级唯一,import 时自注册)+ 4 大可扩展子系统(tools / plugins / providers / gateway 平台适配器)。
整体设计思路是一个窄腰 + 厚边缘的分层架构:内核只做「跑一个 turn 循环 + 调一组工具」,所有平台、模型、记忆、上下文引擎、图像生成均以插件/ABC 形式挂载在边缘;通过 DEFAULT_CONFIG 单一 schema 源(hermes_cli/config_defaults.py:21)与三套加载器(hermes_cli/AGENTS.md:69-72)统一配置。
整体运行流程:用户消息从界面(CLI REPL / gateway 平台适配器 / TUI / desktop)进入 → 经 run_conversation(agent/conversation_loop.py:1573)→ _run_conversation_turn(:1423)进入 11 阶段同步循环(构建请求 → 预检门 → API 调用 → 响应归一化 → 工具轮/收尾)→ 工具调用经 model_tools.handle_function_call(model_tools.py:855)→ ToolRegistry.dispatch(tools/registry.py:810)→ handler 执行 → 结果回灌为 tool 消息 → 下一轮迭代,直到模型不再请求工具(finish)或预算耗尽 → finalize_turn(agent/turn_finalizer.py:435)落库并返回。
最大风险:① 4 个「上帝文件」——hermes_cli/gateway.py(6390 行/288KB,名实不符的进程管理巨石)、gateway/run.py(285KB 门面)、cli.py(4635 行/212KB REPL)、gateway/run_turn.py(221KB);② agent/ 三大压缩/辅助 god 文件(auxiliary_client.py 379KB、context_compressor.py 277KB、conversation_compression.py 216KB);③ 平台适配器自身是 god-adapter(telegram 382KB、discord 352KB、slack 339KB);④ agent↔hermes_cli 双向依赖:18 个 agent 文件反向 import hermes_cli(占 218 个的 ~8%),模块边界事实上被穿透。
重构主线:P0 拆 hermes_cli/gateway.py(按进程后端)与 cli.py↔mixin 循环回引;P1 拆 agent 三大压缩 god 文件并补模块边界 CI;P2 将根级 hermes_state_* 15 文件包化、AIAgent.__init__ 的 ~60 参数分组。
一、项目全貌
1.1 技术栈
| 项目名/版本 | hermes-agent v0.21.2 | pyproject.toml:4-5 |
| 描述 | “The self-improving AI agent — creates skills from experience, improves them during use, and runs anywhere” | pyproject.toml:6 |
| 语言 | Python >=3.11,<3.14(上限是 load-bearing:避免 3.14 的 Rust 依赖回退源码编译) | pyproject.toml:15 |
| 构建后端 | setuptools(setup.py 设 _GuardedSdist/_GuardedBdistWheel,禁止非 Nix 构建产物) | setup.py:21-46、pyproject.toml:388-389 |
| 入口脚本 | hermes = hermes_cli.main:main;hermes-agent = run_agent:main;hermes-acp = acp_adapter.entry:main | pyproject.toml:391-394 |
| 核心依赖 | 精确钉版(exact pin,反供应链攻击;Mini Shai-Hulud 蠕虫教训):openai 2.24.0、httpx 0.28.1、pydantic 2.13.4、rich、tenacity、pyyaml、jinja2、croniter、snowballstemmer、packaging 26.0 等 | pyproject.toml:19-90 |
| 依赖策略 | 核心仅含「每个 session 都用」的包;provider 特定的(anthropic、firecrawl-py、exa-py 等)放 extra,经 tools/lazy_deps.py 按用户选择后端时懒装 | pyproject.toml:34-39 |
| 依赖上限 | 全部带上限(litellm 投毒 #2796/#2810 教训);exclude-newer = 14 days + CVE 例外白名单 | pyproject.toml:292-298、:411 |
| 前端栈 | React + TS(web web/、TUI ui-tui/、Electron 桌面 apps/desktop/);package.json + eslint 共享配置 | package.json、eslint.config.shared.mjs、apps/desktop/ |
| 部署 | shell 安装器 / Docker 镜像 / Nix(pip/PyPI/Homebrew 已不支持,因 wheel 缺 bundled assets) | setup.py:5-19、Dockerfile、docker-compose.yml、nix/ |
| 测试 | scripts/run_tests.sh(强制 CI parity:凭据 unset、TZ=UTC、HERMES_HOME→temp、per-file 子进程隔离) | AGENTS.md:313-329 |
1.2 目录结构与规模
#mermaid-svg-WUNXd9afHSUBObQp{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#fff;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-WUNXd9afHSUBObQp .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-WUNXd9afHSUBObQp .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-WUNXd9afHSUBObQp .error-icon{fill:#10b981;}#mermaid-svg-WUNXd9afHSUBObQp .error-text{fill:#ef467e;stroke:#ef467e;}#mermaid-svg-WUNXd9afHSUBObQp .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-WUNXd9afHSUBObQp .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-WUNXd9afHSUBObQp .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-WUNXd9afHSUBObQp .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-WUNXd9afHSUBObQp .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-WUNXd9afHSUBObQp .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-WUNXd9afHSUBObQp .marker{fill:#6b7280;stroke:#6b7280;}#mermaid-svg-WUNXd9afHSUBObQp .marker.cross{stroke:#6b7280;}#mermaid-svg-WUNXd9afHSUBObQp svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-WUNXd9afHSUBObQp p{margin:0;}#mermaid-svg-WUNXd9afHSUBObQp .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#fff;}#mermaid-svg-WUNXd9afHSUBObQp .cluster-label text{fill:#ef467e;}#mermaid-svg-WUNXd9afHSUBObQp .cluster-label span{color:#ef467e;}#mermaid-svg-WUNXd9afHSUBObQp .cluster-label span p{background-color:transparent;}#mermaid-svg-WUNXd9afHSUBObQp .label text,#mermaid-svg-WUNXd9afHSUBObQp span{fill:#fff;color:#fff;}#mermaid-svg-WUNXd9afHSUBObQp .node rect,#mermaid-svg-WUNXd9afHSUBObQp .node circle,#mermaid-svg-WUNXd9afHSUBObQp .node ellipse,#mermaid-svg-WUNXd9afHSUBObQp .node polygon,#mermaid-svg-WUNXd9afHSUBObQp .node path{fill:#4a90d9;stroke:#2c5f8a;stroke-width:1px;}#mermaid-svg-WUNXd9afHSUBObQp .rough-node .label text,#mermaid-svg-WUNXd9afHSUBObQp .node .label text,#mermaid-svg-WUNXd9afHSUBObQp .image-shape .label,#mermaid-svg-WUNXd9afHSUBObQp .icon-shape .label{text-anchor:middle;}#mermaid-svg-WUNXd9afHSUBObQp .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-WUNXd9afHSUBObQp .rough-node .label,#mermaid-svg-WUNXd9afHSUBObQp .node .label,#mermaid-svg-WUNXd9afHSUBObQp .image-shape .label,#mermaid-svg-WUNXd9afHSUBObQp .icon-shape .label{text-align:center;}#mermaid-svg-WUNXd9afHSUBObQp .node.clickable{cursor:pointer;}#mermaid-svg-WUNXd9afHSUBObQp .root .anchor path{fill:#6b7280!important;stroke-width:0;stroke:#6b7280;}#mermaid-svg-WUNXd9afHSUBObQp .arrowheadPath{fill:#0b0b0b;}#mermaid-svg-WUNXd9afHSUBObQp .edgePath .path{stroke:#6b7280;stroke-width:2.0px;}#mermaid-svg-WUNXd9afHSUBObQp .flowchart-link{stroke:#6b7280;fill:none;}#mermaid-svg-WUNXd9afHSUBObQp .edgeLabel{background-color:#f59e0b;text-align:center;}#mermaid-svg-WUNXd9afHSUBObQp .edgeLabel p{background-color:#f59e0b;}#mermaid-svg-WUNXd9afHSUBObQp .edgeLabel rect{opacity:0.5;background-color:#f59e0b;fill:#f59e0b;}#mermaid-svg-WUNXd9afHSUBObQp .labelBkg{background-color:rgba(245, 158, 11, 0.5);}#mermaid-svg-WUNXd9afHSUBObQp .cluster rect{fill:#10b981;stroke:hsl(160.1183431953, 44.07960199%, 29.4117647059%);stroke-width:1px;}#mermaid-svg-WUNXd9afHSUBObQp .cluster text{fill:#ef467e;}#mermaid-svg-WUNXd9afHSUBObQp .cluster span{color:#ef467e;}#mermaid-svg-WUNXd9afHSUBObQp div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:#10b981;border:1px solid hsl(160.1183431953, 44.07960199%, 29.4117647059%);border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-WUNXd9afHSUBObQp .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#fff;}#mermaid-svg-WUNXd9afHSUBObQp rect.text{fill:none;stroke-width:0;}#mermaid-svg-WUNXd9afHSUBObQp .icon-shape,#mermaid-svg-WUNXd9afHSUBObQp .image-shape{background-color:#f59e0b;text-align:center;}#mermaid-svg-WUNXd9afHSUBObQp .icon-shape p,#mermaid-svg-WUNXd9afHSUBObQp .image-shape p{background-color:#f59e0b;padding:2px;}#mermaid-svg-WUNXd9afHSUBObQp .icon-shape .label rect,#mermaid-svg-WUNXd9afHSUBObQp .image-shape .label rect{opacity:0.5;background-color:#f59e0b;fill:#f59e0b;}#mermaid-svg-WUNXd9afHSUBObQp .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-WUNXd9afHSUBObQp .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-WUNXd9afHSUBObQp :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}#mermaid-svg-WUNXd9afHSUBObQp .core>*{fill:#4a90d9!important;stroke:#2c5f8a!important;color:#fff!important;}#mermaid-svg-WUNXd9afHSUBObQp .core span{fill:#4a90d9!important;stroke:#2c5f8a!important;color:#fff!important;}#mermaid-svg-WUNXd9afHSUBObQp .core tspan{fill:#fff!important;}#mermaid-svg-WUNXd9afHSUBObQp .tool>*{fill:#10b981!important;stroke:#047857!important;color:#fff!important;}#mermaid-svg-WUNXd9afHSUBObQp .tool span{fill:#10b981!important;stroke:#047857!important;color:#fff!important;}#mermaid-svg-WUNXd9afHSUBObQp .tool tspan{fill:#fff!important;}#mermaid-svg-WUNXd9afHSUBObQp .surface>*{fill:#f59e0b!important;stroke:#b45309!important;color:#fff!important;}#mermaid-svg-WUNXd9afHSUBObQp .surface span{fill:#f59e0b!important;stroke:#b45309!important;color:#fff!important;}#mermaid-svg-WUNXd9afHSUBObQp .surface tspan{fill:#fff!important;}#mermaid-svg-WUNXd9afHSUBObQp .edge>*{fill:#8b5cf6!important;stroke:#6d28d9!important;color:#fff!important;}#mermaid-svg-WUNXd9afHSUBObQp .edge span{fill:#8b5cf6!important;stroke:#6d28d9!important;color:#fff!important;}#mermaid-svg-WUNXd9afHSUBObQp .edge tspan{fill:#fff!important;}#mermaid-svg-WUNXd9afHSUBObQp .state>*{fill:#ef4444!important;stroke:#b91c1c!important;color:#fff!important;}#mermaid-svg-WUNXd9afHSUBObQp .state span{fill:#ef4444!important;stroke:#b91c1c!important;color:#fff!important;}#mermaid-svg-WUNXd9afHSUBObQp .state tspan{fill:#fff!important;}
驱动的
调用的
扩展于
落库于
状态层
hermes_state*.py15 文件 / 30K LOC会话持久化(SQLite)
扩展层(厚边缘)
plugins/241 文件 / 86K LOC22 平台/39 模型 provider/记忆/看板
skills/13 目录技能包
providers/ProviderProfile ABC
cron/scheduler.py 177KB
界面层(厚边缘)
cli.py + hermes_cli/478 文件 / 204K LOC
gateway/151 文件 / 83K LOC消息网关
tui_gateway/ + ui-tui/
web/ + hermes_cli/web_server.py
apps/desktop/ (Electron)
acp_adapter/ (ACP 协议)
工具层(窄腰)
tools/299 文件 / 102K LOC256 个工具文件
toolsets.py / model_tools.py
tools/registry.pyToolRegistry 单例
内核层(窄腰)
run_agent.pyAIAgent 门面
agent/279 文件 / 111K LOCturn 循环/压缩/prompt
hermes-agent 根目录
| agent/ | 279 | 111,118 | AIAgent 内核、turn 循环、压缩、prompt 构建 | run_agent.py:211、agent/conversation_loop.py:1423 |
| hermes_cli/ + cli.py | 478+1 | 204,498 | CLI 子命令 + 交互 REPL + 配置系统 + 插件运行时 | hermes_cli/main.py:3351、cli.py:2530 |
| tools/ | 299 | 102,568 | 窄腰工具集:256 个工具文件 + ToolRegistry 单例 | tools/registry.py:368、model_tools.py:855 |
| plugins/ | 241 | 86,639 | 厚边缘:22 平台适配器 + 39 模型 provider + 记忆/看板/图像 | plugins/platforms/、plugins/model-providers/ |
| gateway/ | 151 | 83,865 | 消息网关:17 个 run_*.py 生命周期阶段 + 12 内置平台适配器 | gateway/run.py、gateway/platforms/base.py:1797 |
| skills/ | 50 | 11,565 | 13 目录技能包(创作/研发/邮件/笔记/媒体等) | skills/ |
| cron/ | — | — | 定时调度器(scheduler.py 177KB) | cron/scheduler.py |
| 根级 *.py | 40 | 30,076 | hermes_state_* 15 文件会话持久化 + hermes_constants + trajectory_compressor | hermes_state.py、hermes_state_sessions.py |
| providers/ | 2 | 781 | ProviderProfile ABC(模型 provider 基类) | providers/base.py:39 |
1.3 构建方式与成熟度
- 构建:PEP 517 setuptools 后端(pyproject.toml:388);setup.py 主动拦截 bdist_wheel/sdist,仅 Nix 构建(HERMES_NIX_BUILD=1)可产出 wheel,editable 安装走 build_editable 不受影响(setup.py:21-46)。根级单文件模块(run_agent、hermes_state、toolsets 等)从源树动态派生 py_modules(setup.py:71-76)。
- 成熟度标志:424 行根级 AGENTS.md + 每区域 AGENTS.md 路由表(AGENTS.md:405-419);完整多语言 README(en/es/ur-pk/zh-CN);3869 行临时插件兼容清单(COMPAT_MANIFEST.md,2026-09 拆分 PR #102117 的回退垫片,2026-09-14 移除);严格的反供应链攻击依赖钉版策略;标记化 OS 测试(@pytest.mark.linux_only 等,AGENTS.md:349-373);Windows 实时进程拓扑 E2E lane(AGENTS.md:367-373)。这是一个高度成熟、工程纪律严格的大型项目。
二、模块划分与职责
按「内核窄腰 / 工具窄腰 / 界面层 / 扩展层 / 状态层」五大层划分,共 9 个一级模块。
2.1 模块总览图
#mermaid-svg-xuti72qc2pK0l1h7{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#fff;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-xuti72qc2pK0l1h7 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-xuti72qc2pK0l1h7 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-xuti72qc2pK0l1h7 .error-icon{fill:hsl(30.6293706294, 65.296803653%, 62.0588235294%);}#mermaid-svg-xuti72qc2pK0l1h7 .error-text{fill:rgb(33.5753424657, 95.4246575342, 159.9246575343);stroke:rgb(33.5753424657, 95.4246575342, 159.9246575343);}#mermaid-svg-xuti72qc2pK0l1h7 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-xuti72qc2pK0l1h7 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-xuti72qc2pK0l1h7 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-xuti72qc2pK0l1h7 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-xuti72qc2pK0l1h7 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-xuti72qc2pK0l1h7 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-xuti72qc2pK0l1h7 .marker{fill:#6b7280;stroke:#6b7280;}#mermaid-svg-xuti72qc2pK0l1h7 .marker.cross{stroke:#6b7280;}#mermaid-svg-xuti72qc2pK0l1h7 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-xuti72qc2pK0l1h7 p{margin:0;}#mermaid-svg-xuti72qc2pK0l1h7 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#fff;}#mermaid-svg-xuti72qc2pK0l1h7 .cluster-label text{fill:rgb(33.5753424657, 95.4246575342, 159.9246575343);}#mermaid-svg-xuti72qc2pK0l1h7 .cluster-label span{color:rgb(33.5753424657, 95.4246575342, 159.9246575343);}#mermaid-svg-xuti72qc2pK0l1h7 .cluster-label span p{background-color:transparent;}#mermaid-svg-xuti72qc2pK0l1h7 .label text,#mermaid-svg-xuti72qc2pK0l1h7 span{fill:#fff;color:#fff;}#mermaid-svg-xuti72qc2pK0l1h7 .node rect,#mermaid-svg-xuti72qc2pK0l1h7 .node circle,#mermaid-svg-xuti72qc2pK0l1h7 .node ellipse,#mermaid-svg-xuti72qc2pK0l1h7 .node polygon,#mermaid-svg-xuti72qc2pK0l1h7 .node path{fill:#4a90d9;stroke:hsl(210.6293706294, 25.296803653%, 47.0588235294%);stroke-width:1px;}#mermaid-svg-xuti72qc2pK0l1h7 .rough-node .label text,#mermaid-svg-xuti72qc2pK0l1h7 .node .label text,#mermaid-svg-xuti72qc2pK0l1h7 .image-shape .label,#mermaid-svg-xuti72qc2pK0l1h7 .icon-shape .label{text-anchor:middle;}#mermaid-svg-xuti72qc2pK0l1h7 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-xuti72qc2pK0l1h7 .rough-node .label,#mermaid-svg-xuti72qc2pK0l1h7 .node .label,#mermaid-svg-xuti72qc2pK0l1h7 .image-shape .label,#mermaid-svg-xuti72qc2pK0l1h7 .icon-shape .label{text-align:center;}#mermaid-svg-xuti72qc2pK0l1h7 .node.clickable{cursor:pointer;}#mermaid-svg-xuti72qc2pK0l1h7 .root .anchor path{fill:#6b7280!important;stroke-width:0;stroke:#6b7280;}#mermaid-svg-xuti72qc2pK0l1h7 .arrowheadPath{fill:#0b0b0b;}#mermaid-svg-xuti72qc2pK0l1h7 .edgePath .path{stroke:#6b7280;stroke-width:2.0px;}#mermaid-svg-xuti72qc2pK0l1h7 .flowchart-link{stroke:#6b7280;fill:none;}#mermaid-svg-xuti72qc2pK0l1h7 .edgeLabel{background-color:hsl(90.6293706294, 65.296803653%, 57.0588235294%);text-align:center;}#mermaid-svg-xuti72qc2pK0l1h7 .edgeLabel p{background-color:hsl(90.6293706294, 65.296803653%, 57.0588235294%);}#mermaid-svg-xuti72qc2pK0l1h7 .edgeLabel rect{opacity:0.5;background-color:hsl(90.6293706294, 65.296803653%, 57.0588235294%);fill:hsl(90.6293706294, 65.296803653%, 57.0588235294%);}#mermaid-svg-xuti72qc2pK0l1h7 .labelBkg{background-color:rgba(143.9999999999, 217, 73.9999999999, 0.5);}#mermaid-svg-xuti72qc2pK0l1h7 .cluster rect{fill:hsl(30.6293706294, 65.296803653%, 62.0588235294%);stroke:hsl(30.6293706294, 25.296803653%, 52.0588235294%);stroke-width:1px;}#mermaid-svg-xuti72qc2pK0l1h7 .cluster text{fill:rgb(33.5753424657, 95.4246575342, 159.9246575343);}#mermaid-svg-xuti72qc2pK0l1h7 .cluster span{color:rgb(33.5753424657, 95.4246575342, 159.9246575343);}#mermaid-svg-xuti72qc2pK0l1h7 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(30.6293706294, 65.296803653%, 62.0588235294%);border:1px solid hsl(30.6293706294, 25.296803653%, 52.0588235294%);border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-xuti72qc2pK0l1h7 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#fff;}#mermaid-svg-xuti72qc2pK0l1h7 rect.text{fill:none;stroke-width:0;}#mermaid-svg-xuti72qc2pK0l1h7 .icon-shape,#mermaid-svg-xuti72qc2pK0l1h7 .image-shape{background-color:hsl(90.6293706294, 65.296803653%, 57.0588235294%);text-align:center;}#mermaid-svg-xuti72qc2pK0l1h7 .icon-shape p,#mermaid-svg-xuti72qc2pK0l1h7 .image-shape p{background-color:hsl(90.6293706294, 65.296803653%, 57.0588235294%);padding:2px;}#mermaid-svg-xuti72qc2pK0l1h7 .icon-shape .label rect,#mermaid-svg-xuti72qc2pK0l1h7 .image-shape .label rect{opacity:0.5;background-color:hsl(90.6293706294, 65.296803653%, 57.0588235294%);fill:hsl(90.6293706294, 65.296803653%, 57.0588235294%);}#mermaid-svg-xuti72qc2pK0l1h7 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-xuti72qc2pK0l1h7 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-xuti72qc2pK0l1h7 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}#mermaid-svg-xuti72qc2pK0l1h7 .l1>*{fill:#4a90d9!important;stroke:#2c5f8a!important;color:#fff!important;}#mermaid-svg-xuti72qc2pK0l1h7 .l1 span{fill:#4a90d9!important;stroke:#2c5f8a!important;color:#fff!important;}#mermaid-svg-xuti72qc2pK0l1h7 .l1 tspan{fill:#fff!important;}#mermaid-svg-xuti72qc2pK0l1h7 .l2>*{fill:#10b981!important;stroke:#047857!important;color:#fff!important;}#mermaid-svg-xuti72qc2pK0l1h7 .l2 span{fill:#10b981!important;stroke:#047857!important;color:#fff!important;}#mermaid-svg-xuti72qc2pK0l1h7 .l2 tspan{fill:#fff!important;}#mermaid-svg-xuti72qc2pK0l1h7 .l3>*{fill:#f59e0b!important;stroke:#b45309!important;color:#fff!important;}#mermaid-svg-xuti72qc2pK0l1h7 .l3 span{fill:#f59e0b!important;stroke:#b45309!important;color:#fff!important;}#mermaid-svg-xuti72qc2pK0l1h7 .l3 tspan{fill:#fff!important;}#mermaid-svg-xuti72qc2pK0l1h7 .l4>*{fill:#8b5cf6!important;stroke:#6d28d9!important;color:#fff!important;}#mermaid-svg-xuti72qc2pK0l1h7 .l4 span{fill:#8b5cf6!important;stroke:#6d28d9!important;color:#fff!important;}#mermaid-svg-xuti72qc2pK0l1h7 .l4 tspan{fill:#fff!important;}#mermaid-svg-xuti72qc2pK0l1h7 .l5>*{fill:#ef4444!important;stroke:#b91c1c!important;color:#fff!important;}#mermaid-svg-xuti72qc2pK0l1h7 .l5 span{fill:#ef4444!important;stroke:#b91c1c!important;color:#fff!important;}#mermaid-svg-xuti72qc2pK0l1h7 .l5 tspan{fill:#fff!important;}
⑤ 状态层
④ 扩展层(厚边缘)
③ 界面层
② 工具窄腰 — ToolRegistry
① 内核窄腰 — AIAgent
构造 AIAgent
调用 run_conversation
tool_call
挂载于
挂载于
挂载于
AIAgent 门面14 Mixin
init_agent 初始化链~30 有序阶段
turn 状态机11 阶段 + 29 turn_*.py
压缩子系统3 文件 788KB
辅助客户端auxiliary_client 379KB
ToolRegistry 单例import 时自注册
toolsets 分组~30 命名组
model_tools 每调用组装+ dispatch
256 工具文件14 类别
hermes_cli 子命令argparse func= 派发
cli.py REPLHermesCLI 14 mixin
gateway 消息网关17 生命周期阶段
TUI / Web / Desktop / ACP
plugins/ 22 平台适配器+ 39 模型 provider
plugins/ 记忆/看板/图像ABC + orchestrator
skills/ 13 技能包
providers/ ProviderProfile
hermes_state_* 15 文件SQLite 会话持久化
2.2 模块①:内核窄腰 —— AIAgent(run_agent.py + agent/)
核心作用:实现同步、带工具调用的 turn 循环——接收用户消息,组装 API 请求(system prompt + 历史 + 工具 schema),调用 LLM,若有 tool_calls 则执行工具并回灌结果继续迭代,否则返回最终响应。它是所有五种界面共享的唯一内核。
关键类/函数:
| AIAgent | run_agent.py:211-215 | 门面类,14 个 Mixin 组合;__init__(:233-272)是 ~60 参数的转发器,转发到 init_agent |
| init_agent | agent/agent_init.py:2172 | 真正的初始化器,~30 步有序编排(_install_safe_stdio :2221 → _build_client :2278 → _load_tools :2280 → _init_session_state :2281 → … → _snapshot_primary_runtime :2307);文档明言「phase ORDER is load-bearing」(agent_init.py:1-8) |
| run_conversation | agent/conversation_loop.py:1573 | 公开入口,转发到 _run_conversation_turn |
| _run_conversation_turn | agent/conversation_loop.py:1423 | turn 状态机主体(详见 §2.2.2) |
| _LoopState | agent/conversation_loop.py:1276 | 贯穿各阶段的 turn 局部状态 dataclass(~60 字段) |
| _run_phase | agent/conversation_loop.py:1370 | 阶段调度器:按函数签名注入 state 字段、回写 verdict 字段 |
| ContextCompressor | agent/context_compressor.py:1678 | 上下文压缩引擎(god-class,组合 3 个 Mixin) |
| _resolve_auto_route | agent/auxiliary_client.py:4278 | 辅助侧 LLM(curator/vision/embed/title/压缩)路由解析 |
| build_api_kwargs | agent/chat_completion_helpers.py:1362 | API kwargs 构建 |
| interruptible_api_call | agent/chat_completion_helpers.py:1145 | 可中断的 API 调用 + 流解析 |
| CredentialPool | agent/credential_pool.py:922 | 轮转凭据池(耗尽/TTL) |
| build_context_files_prompt | agent/prompt_builder.py:1581 | 上下文文件/技能/记忆提示构建 |
| execute_tool_calls_concurrent | agent/tool_executor.py:1404 | 并发工具执行 |
2.2.1 AIAgent 的 14 个 Mixin
#mermaid-svg-OfSkBUvfiziWLQzk{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#fff;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-OfSkBUvfiziWLQzk .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-OfSkBUvfiziWLQzk .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-OfSkBUvfiziWLQzk .error-icon{fill:hsl(30.6293706294, 65.296803653%, 62.0588235294%);}#mermaid-svg-OfSkBUvfiziWLQzk .error-text{fill:rgb(33.5753424657, 95.4246575342, 159.9246575343);stroke:rgb(33.5753424657, 95.4246575342, 159.9246575343);}#mermaid-svg-OfSkBUvfiziWLQzk .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-OfSkBUvfiziWLQzk .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-OfSkBUvfiziWLQzk .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-OfSkBUvfiziWLQzk .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-OfSkBUvfiziWLQzk .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-OfSkBUvfiziWLQzk .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-OfSkBUvfiziWLQzk .marker{fill:#374151;stroke:#374151;}#mermaid-svg-OfSkBUvfiziWLQzk .marker.cross{stroke:#374151;}#mermaid-svg-OfSkBUvfiziWLQzk svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-OfSkBUvfiziWLQzk p{margin:0;}#mermaid-svg-OfSkBUvfiziWLQzk .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#fff;}#mermaid-svg-OfSkBUvfiziWLQzk .cluster-label text{fill:rgb(33.5753424657, 95.4246575342, 159.9246575343);}#mermaid-svg-OfSkBUvfiziWLQzk .cluster-label span{color:rgb(33.5753424657, 95.4246575342, 159.9246575343);}#mermaid-svg-OfSkBUvfiziWLQzk .cluster-label span p{background-color:transparent;}#mermaid-svg-OfSkBUvfiziWLQzk .label text,#mermaid-svg-OfSkBUvfiziWLQzk span{fill:#fff;color:#fff;}#mermaid-svg-OfSkBUvfiziWLQzk .node rect,#mermaid-svg-OfSkBUvfiziWLQzk .node circle,#mermaid-svg-OfSkBUvfiziWLQzk .node ellipse,#mermaid-svg-OfSkBUvfiziWLQzk .node polygon,#mermaid-svg-OfSkBUvfiziWLQzk .node path{fill:#4a90d9;stroke:hsl(210.6293706294, 25.296803653%, 47.0588235294%);stroke-width:1px;}#mermaid-svg-OfSkBUvfiziWLQzk .rough-node .label text,#mermaid-svg-OfSkBUvfiziWLQzk .node .label text,#mermaid-svg-OfSkBUvfiziWLQzk .image-shape .label,#mermaid-svg-OfSkBUvfiziWLQzk .icon-shape .label{text-anchor:middle;}#mermaid-svg-OfSkBUvfiziWLQzk .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-OfSkBUvfiziWLQzk .rough-node .label,#mermaid-svg-OfSkBUvfiziWLQzk .node .label,#mermaid-svg-OfSkBUvfiziWLQzk .image-shape .label,#mermaid-svg-OfSkBUvfiziWLQzk .icon-shape .label{text-align:center;}#mermaid-svg-OfSkBUvfiziWLQzk .node.clickable{cursor:pointer;}#mermaid-svg-OfSkBUvfiziWLQzk .root .anchor path{fill:#374151!important;stroke-width:0;stroke:#374151;}#mermaid-svg-OfSkBUvfiziWLQzk .arrowheadPath{fill:#0b0b0b;}#mermaid-svg-OfSkBUvfiziWLQzk .edgePath .path{stroke:#374151;stroke-width:2.0px;}#mermaid-svg-OfSkBUvfiziWLQzk .flowchart-link{stroke:#374151;fill:none;}#mermaid-svg-OfSkBUvfiziWLQzk .edgeLabel{background-color:hsl(90.6293706294, 65.296803653%, 57.0588235294%);text-align:center;}#mermaid-svg-OfSkBUvfiziWLQzk .edgeLabel p{background-color:hsl(90.6293706294, 65.296803653%, 57.0588235294%);}#mermaid-svg-OfSkBUvfiziWLQzk .edgeLabel rect{opacity:0.5;background-color:hsl(90.6293706294, 65.296803653%, 57.0588235294%);fill:hsl(90.6293706294, 65.296803653%, 57.0588235294%);}#mermaid-svg-OfSkBUvfiziWLQzk .labelBkg{background-color:rgba(143.9999999999, 217, 73.9999999999, 0.5);}#mermaid-svg-OfSkBUvfiziWLQzk .cluster rect{fill:hsl(30.6293706294, 65.296803653%, 62.0588235294%);stroke:hsl(30.6293706294, 25.296803653%, 52.0588235294%);stroke-width:1px;}#mermaid-svg-OfSkBUvfiziWLQzk .cluster text{fill:rgb(33.5753424657, 95.4246575342, 159.9246575343);}#mermaid-svg-OfSkBUvfiziWLQzk .cluster span{color:rgb(33.5753424657, 95.4246575342, 159.9246575343);}#mermaid-svg-OfSkBUvfiziWLQzk div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(30.6293706294, 65.296803653%, 62.0588235294%);border:1px solid hsl(30.6293706294, 25.296803653%, 52.0588235294%);border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-OfSkBUvfiziWLQzk .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#fff;}#mermaid-svg-OfSkBUvfiziWLQzk rect.text{fill:none;stroke-width:0;}#mermaid-svg-OfSkBUvfiziWLQzk .icon-shape,#mermaid-svg-OfSkBUvfiziWLQzk .image-shape{background-color:hsl(90.6293706294, 65.296803653%, 57.0588235294%);text-align:center;}#mermaid-svg-OfSkBUvfiziWLQzk .icon-shape p,#mermaid-svg-OfSkBUvfiziWLQzk .image-shape p{background-color:hsl(90.6293706294, 65.296803653%, 57.0588235294%);padding:2px;}#mermaid-svg-OfSkBUvfiziWLQzk .icon-shape .label rect,#mermaid-svg-OfSkBUvfiziWLQzk .image-shape .label rect{opacity:0.5;background-color:hsl(90.6293706294, 65.296803653%, 57.0588235294%);fill:hsl(90.6293706294, 65.296803653%, 57.0588235294%);}#mermaid-svg-OfSkBUvfiziWLQzk .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-OfSkBUvfiziWLQzk .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-OfSkBUvfiziWLQzk :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}#mermaid-svg-OfSkBUvfiziWLQzk .base>*{fill:#4a90d9!important;stroke:#2c5f8a!important;color:#fff!important;}#mermaid-svg-OfSkBUvfiziWLQzk .base span{fill:#4a90d9!important;stroke:#2c5f8a!important;color:#fff!important;}#mermaid-svg-OfSkBUvfiziWLQzk .base tspan{fill:#fff!important;}#mermaid-svg-OfSkBUvfiziWLQzk .mixin>*{fill:#60a5fa!important;stroke:#2563eb!important;color:#fff!important;}#mermaid-svg-OfSkBUvfiziWLQzk .mixin span{fill:#60a5fa!important;stroke:#2563eb!important;color:#fff!important;}#mermaid-svg-OfSkBUvfiziWLQzk .mixin tspan{fill:#fff!important;}
AIAgentrun_agent.py:211
ClientLifecycleMixinclient_lifecycle.py:106API client 构建/刷新
StreamDeliveryMixinstream_delivery.py:19流式 delta 下发
StatusOutputMixinstatus_output.py:16状态行/通知渲染
ApiRequestHooksMixinapi_request_hooks.py:32API 请求前后置钩子
ApiErrorSummaryMixinapi_error_summary.py:47错误摘要
InterruptControlMixininterrupt_control.py:90中断标志检查/中断
TurnExplainersMixinturn_explainers.py:209轮级用户解释
ActivityTrackingMixinactivity_tracking.py:28活动溯源
RateLimitCreditsMixinrate_limit_credits.py:32限流/额度
SessionPersistenceMixinsession_persistence.py:273SessionDB 追加/恢复
CompressionFacadeMixincompression_facade.py:186压缩子系统门面
TurnFacadeMixinturn_facade.py:19run_conversation 租约+转发
VisionMessagePrepMixinvision_message_prep.py:60视觉/图像消息准备
ReasoningParamsMixinreasoning_params.py:47reasoning_effort/echo 配置
2.2.2 Turn 状态机(11 阶段)
主循环 while (api_call_count < max_iterations and iteration_budget.remaining > 0) or _budget_grace_call(conversation_loop.py:1514),每轮迭代按下序执行阶段,每阶段是 turn_*.py 中的独立兄弟函数(共 29 个 turn_*.py 文件):
#mermaid-svg-TaZNmYVwBLF0d4EX{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#fff;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-TaZNmYVwBLF0d4EX .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-TaZNmYVwBLF0d4EX .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-TaZNmYVwBLF0d4EX .error-icon{fill:hsl(30.6293706294, 65.296803653%, 62.0588235294%);}#mermaid-svg-TaZNmYVwBLF0d4EX .error-text{fill:rgb(33.5753424657, 95.4246575342, 159.9246575343);stroke:rgb(33.5753424657, 95.4246575342, 159.9246575343);}#mermaid-svg-TaZNmYVwBLF0d4EX .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-TaZNmYVwBLF0d4EX .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-TaZNmYVwBLF0d4EX .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-TaZNmYVwBLF0d4EX .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-TaZNmYVwBLF0d4EX .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-TaZNmYVwBLF0d4EX .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-TaZNmYVwBLF0d4EX .marker{fill:#374151;stroke:#374151;}#mermaid-svg-TaZNmYVwBLF0d4EX .marker.cross{stroke:#374151;}#mermaid-svg-TaZNmYVwBLF0d4EX svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-TaZNmYVwBLF0d4EX p{margin:0;}#mermaid-svg-TaZNmYVwBLF0d4EX .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#fff;}#mermaid-svg-TaZNmYVwBLF0d4EX .cluster-label text{fill:rgb(33.5753424657, 95.4246575342, 159.9246575343);}#mermaid-svg-TaZNmYVwBLF0d4EX .cluster-label span{color:rgb(33.5753424657, 95.4246575342, 159.9246575343);}#mermaid-svg-TaZNmYVwBLF0d4EX .cluster-label span p{background-color:transparent;}#mermaid-svg-TaZNmYVwBLF0d4EX .label text,#mermaid-svg-TaZNmYVwBLF0d4EX span{fill:#fff;color:#fff;}#mermaid-svg-TaZNmYVwBLF0d4EX .node rect,#mermaid-svg-TaZNmYVwBLF0d4EX .node circle,#mermaid-svg-TaZNmYVwBLF0d4EX .node ellipse,#mermaid-svg-TaZNmYVwBLF0d4EX .node polygon,#mermaid-svg-TaZNmYVwBLF0d4EX .node path{fill:#4a90d9;stroke:hsl(210.6293706294, 25.296803653%, 47.0588235294%);stroke-width:1px;}#mermaid-svg-TaZNmYVwBLF0d4EX .rough-node .label text,#mermaid-svg-TaZNmYVwBLF0d4EX .node .label text,#mermaid-svg-TaZNmYVwBLF0d4EX .image-shape .label,#mermaid-svg-TaZNmYVwBLF0d4EX .icon-shape .label{text-anchor:middle;}#mermaid-svg-TaZNmYVwBLF0d4EX .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-TaZNmYVwBLF0d4EX .rough-node .label,#mermaid-svg-TaZNmYVwBLF0d4EX .node .label,#mermaid-svg-TaZNmYVwBLF0d4EX .image-shape .label,#mermaid-svg-TaZNmYVwBLF0d4EX .icon-shape .label{text-align:center;}#mermaid-svg-TaZNmYVwBLF0d4EX .node.clickable{cursor:pointer;}#mermaid-svg-TaZNmYVwBLF0d4EX .root .anchor path{fill:#374151!important;stroke-width:0;stroke:#374151;}#mermaid-svg-TaZNmYVwBLF0d4EX .arrowheadPath{fill:#0b0b0b;}#mermaid-svg-TaZNmYVwBLF0d4EX .edgePath .path{stroke:#374151;stroke-width:2.0px;}#mermaid-svg-TaZNmYVwBLF0d4EX .flowchart-link{stroke:#374151;fill:none;}#mermaid-svg-TaZNmYVwBLF0d4EX .edgeLabel{background-color:hsl(90.6293706294, 65.296803653%, 57.0588235294%);text-align:center;}#mermaid-svg-TaZNmYVwBLF0d4EX .edgeLabel p{background-color:hsl(90.6293706294, 65.296803653%, 57.0588235294%);}#mermaid-svg-TaZNmYVwBLF0d4EX .edgeLabel rect{opacity:0.5;background-color:hsl(90.6293706294, 65.296803653%, 57.0588235294%);fill:hsl(90.6293706294, 65.296803653%, 57.0588235294%);}#mermaid-svg-TaZNmYVwBLF0d4EX .labelBkg{background-color:rgba(143.9999999999, 217, 73.9999999999, 0.5);}#mermaid-svg-TaZNmYVwBLF0d4EX .cluster rect{fill:hsl(30.6293706294, 65.296803653%, 62.0588235294%);stroke:hsl(30.6293706294, 25.296803653%, 52.0588235294%);stroke-width:1px;}#mermaid-svg-TaZNmYVwBLF0d4EX .cluster text{fill:rgb(33.5753424657, 95.4246575342, 159.9246575343);}#mermaid-svg-TaZNmYVwBLF0d4EX .cluster span{color:rgb(33.5753424657, 95.4246575342, 159.9246575343);}#mermaid-svg-TaZNmYVwBLF0d4EX div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(30.6293706294, 65.296803653%, 62.0588235294%);border:1px solid hsl(30.6293706294, 25.296803653%, 52.0588235294%);border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-TaZNmYVwBLF0d4EX .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#fff;}#mermaid-svg-TaZNmYVwBLF0d4EX rect.text{fill:none;stroke-width:0;}#mermaid-svg-TaZNmYVwBLF0d4EX .icon-shape,#mermaid-svg-TaZNmYVwBLF0d4EX .image-shape{background-color:hsl(90.6293706294, 65.296803653%, 57.0588235294%);text-align:center;}#mermaid-svg-TaZNmYVwBLF0d4EX .icon-shape p,#mermaid-svg-TaZNmYVwBLF0d4EX .image-shape p{background-color:hsl(90.6293706294, 65.296803653%, 57.0588235294%);padding:2px;}#mermaid-svg-TaZNmYVwBLF0d4EX .icon-shape .label rect,#mermaid-svg-TaZNmYVwBLF0d4EX .image-shape .label rect{opacity:0.5;background-color:hsl(90.6293706294, 65.296803653%, 57.0588235294%);fill:hsl(90.6293706294, 65.296803653%, 57.0588235294%);}#mermaid-svg-TaZNmYVwBLF0d4EX .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-TaZNmYVwBLF0d4EX .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-TaZNmYVwBLF0d4EX :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}#mermaid-svg-TaZNmYVwBLF0d4EX .phase>*{fill:#4a90d9!important;stroke:#2c5f8a!important;color:#fff!important;}#mermaid-svg-TaZNmYVwBLF0d4EX .phase span{fill:#4a90d9!important;stroke:#2c5f8a!important;color:#fff!important;}#mermaid-svg-TaZNmYVwBLF0d4EX .phase tspan{fill:#fff!important;}#mermaid-svg-TaZNmYVwBLF0d4EX .sub>*{fill:#60a5fa!important;stroke:#2563eb!important;color:#fff!important;}#mermaid-svg-TaZNmYVwBLF0d4EX .sub span{fill:#60a5fa!important;stroke:#2563eb!important;color:#fff!important;}#mermaid-svg-TaZNmYVwBLF0d4EX .sub tspan{fill:#fff!important;}#mermaid-svg-TaZNmYVwBLF0d4EX .decision>*{fill:#f59e0b!important;stroke:#b45309!important;color:#fff!important;}#mermaid-svg-TaZNmYVwBLF0d4EX .decision span{fill:#f59e0b!important;stroke:#b45309!important;color:#fff!important;}#mermaid-svg-TaZNmYVwBLF0d4EX .decision tspan{fill:#fff!important;}#mermaid-svg-TaZNmYVwBLF0d4EX .term>*{fill:#10b981!important;stroke:#047857!important;color:#fff!important;}#mermaid-svg-TaZNmYVwBLF0d4EX .term span{fill:#10b981!important;stroke:#047857!important;color:#fff!important;}#mermaid-svg-TaZNmYVwBLF0d4EX .term tspan{fill:#fff!important;}
异常
异常
有工具调用
无工具调用
继续迭代
结束
用户消息进入run_conversation:1573
_run_conversation_turn:1423build_turn_context → _LoopState:1276
while api_call_count < max_iterand budget.remaining > 0:1514
① begin_iterationturn_iteration_prep.py:300:1515
② prepare_iterationturn_iteration_prep.py:95:1517
③ assemble_api_requestturn_request_assembly.py:106:1518
④ run_preflight_gateturn_preflight_gate.py:20:1519
⑤ announce_api_callturn_iteration_prep.py:255:1526
_run_api_retry_loop:1391内含 6 子阶段
nous_rate_limit_guard:1397
build_api_request:1403
perform_api_call:1404
check_api_response:1406
handle_api_interrupt:1412
handle_api_error:1415
⑥ apply_retry_restartsturn_iteration_prep.py:384:1536
⑦ normalize_model_responseturn_response_intake.py:115:1543
assistant_message.tool_calls?:1549
⑧ run_tool_roundturn_tool_round.py:45:1548
⑧ finish_text_responseturn_final_response.py:46:1549
⑨ handle_outer_loop_errorturn_loop_errors.py:34:1558 (仅异常)
finish_reason / budget?
⑩ finalize_turnturn_finalizer.py:435:1562
返回 result dict
2.2.3 消息流不变量(每处改动须据此评审)
| Prompt 缓存不可破坏 | agent/AGENTS.md:55-56 | 永不中途改历史/换工具集/重载记忆/重建 system prompt |
| System prompt 全程 byte-stable | agent/AGENTS.md:57 | 唯一上下文变更 = 压缩 |
| 中途注入走 user/tool 消息 | agent/AGENTS.md:58-60 | skill slash 注入为 user 消息;子目录 hints 追加到 tool 结果 |
| 严格角色轮替 | agent/AGENTS.md:61-65 | 永不连续两同角色;/steer 是 tool 结果后的独立 user 行 |
| 上下文文件仅启动时从 CWD 加载 | agent/AGENTS.md:66-69 | 带 cap;安装树 AGENTS.md 永不作为项目上下文 |
| 压缩双阈值 | agent/AGENTS.md:76-77 | gateway session 卫生 85%;agent ContextCompressor 50%(可配,per-model 覆盖,溢出后冷却) |
| 压缩算法 | agent/AGENTS.md:78-80 | 先剪旧 tool 结果(无 LLM 调用)→ 选边界 → 辅助模型生成结构化摘要;原地压缩保持稳定 session id |
2.2.4 模块①架构图
#mermaid-svg-AtVxcEW5ejMwSpu2{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#fff;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-AtVxcEW5ejMwSpu2 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-AtVxcEW5ejMwSpu2 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-AtVxcEW5ejMwSpu2 .error-icon{fill:hsl(30.6293706294, 65.296803653%, 62.0588235294%);}#mermaid-svg-AtVxcEW5ejMwSpu2 .error-text{fill:rgb(33.5753424657, 95.4246575342, 159.9246575343);stroke:rgb(33.5753424657, 95.4246575342, 159.9246575343);}#mermaid-svg-AtVxcEW5ejMwSpu2 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-AtVxcEW5ejMwSpu2 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-AtVxcEW5ejMwSpu2 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-AtVxcEW5ejMwSpu2 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-AtVxcEW5ejMwSpu2 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-AtVxcEW5ejMwSpu2 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-AtVxcEW5ejMwSpu2 .marker{fill:#6b7280;stroke:#6b7280;}#mermaid-svg-AtVxcEW5ejMwSpu2 .marker.cross{stroke:#6b7280;}#mermaid-svg-AtVxcEW5ejMwSpu2 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-AtVxcEW5ejMwSpu2 p{margin:0;}#mermaid-svg-AtVxcEW5ejMwSpu2 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#fff;}#mermaid-svg-AtVxcEW5ejMwSpu2 .cluster-label text{fill:rgb(33.5753424657, 95.4246575342, 159.9246575343);}#mermaid-svg-AtVxcEW5ejMwSpu2 .cluster-label span{color:rgb(33.5753424657, 95.4246575342, 159.9246575343);}#mermaid-svg-AtVxcEW5ejMwSpu2 .cluster-label span p{background-color:transparent;}#mermaid-svg-AtVxcEW5ejMwSpu2 .label text,#mermaid-svg-AtVxcEW5ejMwSpu2 span{fill:#fff;color:#fff;}#mermaid-svg-AtVxcEW5ejMwSpu2 .node rect,#mermaid-svg-AtVxcEW5ejMwSpu2 .node circle,#mermaid-svg-AtVxcEW5ejMwSpu2 .node ellipse,#mermaid-svg-AtVxcEW5ejMwSpu2 .node polygon,#mermaid-svg-AtVxcEW5ejMwSpu2 .node path{fill:#4a90d9;stroke:hsl(210.6293706294, 25.296803653%, 47.0588235294%);stroke-width:1px;}#mermaid-svg-AtVxcEW5ejMwSpu2 .rough-node .label text,#mermaid-svg-AtVxcEW5ejMwSpu2 .node .label text,#mermaid-svg-AtVxcEW5ejMwSpu2 .image-shape .label,#mermaid-svg-AtVxcEW5ejMwSpu2 .icon-shape .label{text-anchor:middle;}#mermaid-svg-AtVxcEW5ejMwSpu2 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-AtVxcEW5ejMwSpu2 .rough-node .label,#mermaid-svg-AtVxcEW5ejMwSpu2 .node .label,#mermaid-svg-AtVxcEW5ejMwSpu2 .image-shape .label,#mermaid-svg-AtVxcEW5ejMwSpu2 .icon-shape .label{text-align:center;}#mermaid-svg-AtVxcEW5ejMwSpu2 .node.clickable{cursor:pointer;}#mermaid-svg-AtVxcEW5ejMwSpu2 .root .anchor path{fill:#6b7280!important;stroke-width:0;stroke:#6b7280;}#mermaid-svg-AtVxcEW5ejMwSpu2 .arrowheadPath{fill:#0b0b0b;}#mermaid-svg-AtVxcEW5ejMwSpu2 .edgePath .path{stroke:#6b7280;stroke-width:2.0px;}#mermaid-svg-AtVxcEW5ejMwSpu2 .flowchart-link{stroke:#6b7280;fill:none;}#mermaid-svg-AtVxcEW5ejMwSpu2 .edgeLabel{background-color:hsl(90.6293706294, 65.296803653%, 57.0588235294%);text-align:center;}#mermaid-svg-AtVxcEW5ejMwSpu2 .edgeLabel p{background-color:hsl(90.6293706294, 65.296803653%, 57.0588235294%);}#mermaid-svg-AtVxcEW5ejMwSpu2 .edgeLabel rect{opacity:0.5;background-color:hsl(90.6293706294, 65.296803653%, 57.0588235294%);fill:hsl(90.6293706294, 65.296803653%, 57.0588235294%);}#mermaid-svg-AtVxcEW5ejMwSpu2 .labelBkg{background-color:rgba(143.9999999999, 217, 73.9999999999, 0.5);}#mermaid-svg-AtVxcEW5ejMwSpu2 .cluster rect{fill:hsl(30.6293706294, 65.296803653%, 62.0588235294%);stroke:hsl(30.6293706294, 25.296803653%, 52.0588235294%);stroke-width:1px;}#mermaid-svg-AtVxcEW5ejMwSpu2 .cluster text{fill:rgb(33.5753424657, 95.4246575342, 159.9246575343);}#mermaid-svg-AtVxcEW5ejMwSpu2 .cluster span{color:rgb(33.5753424657, 95.4246575342, 159.9246575343);}#mermaid-svg-AtVxcEW5ejMwSpu2 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(30.6293706294, 65.296803653%, 62.0588235294%);border:1px solid hsl(30.6293706294, 25.296803653%, 52.0588235294%);border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-AtVxcEW5ejMwSpu2 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#fff;}#mermaid-svg-AtVxcEW5ejMwSpu2 rect.text{fill:none;stroke-width:0;}#mermaid-svg-AtVxcEW5ejMwSpu2 .icon-shape,#mermaid-svg-AtVxcEW5ejMwSpu2 .image-shape{background-color:hsl(90.6293706294, 65.296803653%, 57.0588235294%);text-align:center;}#mermaid-svg-AtVxcEW5ejMwSpu2 .icon-shape p,#mermaid-svg-AtVxcEW5ejMwSpu2 .image-shape p{background-color:hsl(90.6293706294, 65.296803653%, 57.0588235294%);padding:2px;}#mermaid-svg-AtVxcEW5ejMwSpu2 .icon-shape .label rect,#mermaid-svg-AtVxcEW5ejMwSpu2 .image-shape .label rect{opacity:0.5;background-color:hsl(90.6293706294, 65.296803653%, 57.0588235294%);fill:hsl(90.6293706294, 65.296803653%, 57.0588235294%);}#mermaid-svg-AtVxcEW5ejMwSpu2 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-AtVxcEW5ejMwSpu2 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-AtVxcEW5ejMwSpu2 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}#mermaid-svg-AtVxcEW5ejMwSpu2 .main>*{fill:#4a90d9!important;stroke:#2c5f8a!important;color:#fff!important;}#mermaid-svg-AtVxcEW5ejMwSpu2 .main span{fill:#4a90d9!important;stroke:#2c5f8a!important;color:#fff!important;}#mermaid-svg-AtVxcEW5ejMwSpu2 .main tspan{fill:#fff!important;}#mermaid-svg-AtVxcEW5ejMwSpu2 .god>*{fill:#ef4444!important;stroke:#b91c1c!important;color:#fff!important;}#mermaid-svg-AtVxcEW5ejMwSpu2 .god span{fill:#ef4444!important;stroke:#b91c1c!important;color:#fff!important;}#mermaid-svg-AtVxcEW5ejMwSpu2 .god tspan{fill:#fff!important;}#mermaid-svg-AtVxcEW5ejMwSpu2 .support>*{fill:#60a5fa!important;stroke:#2563eb!important;color:#fff!important;}#mermaid-svg-AtVxcEW5ejMwSpu2 .support span{fill:#60a5fa!important;stroke:#2563eb!important;color:#fff!important;}#mermaid-svg-AtVxcEW5ejMwSpu2 .support tspan{fill:#fff!important;}
agent/ 内核包
继承
run_agent.pyAIAgent 门面:211
agent_init.pyinit_agent:2172 (~30 阶段)
14 个 Mixinclient_lifecycle/stream_delivery/…
conversation_loop.pyturn 状态机:1423
turn_*.py29 个阶段兄弟文件
tool_executor.py:1404并发工具执行
chat_completion_helpers.pyAPI kwargs:1362 / 可中断调用:1145
context_compressor.py:1678277KB god-class
conversation_compression.py:3558216KB 编排+提交栅栏
auxiliary_client.py:4278379KB 辅助侧 LLM 路由
prompt_builder.py:1581上下文/技能/记忆提示
credential_pool.py:922凭据轮转
model_metadata.py上下文长度/能力
agent_runtime_helpers.py轨迹/清洗
2.3 模块②:工具窄腰 —— ToolRegistry(tools/ + toolsets.py + model_tools.py)
核心作用:构成 Hermes 的「窄腰」——「每个 model tool 在每次 API 调用都下发」(AGENTS.md:27-29)。一个无依赖的 ToolRegistry 在 import 时收集所有工具的 schema + handler;model_tools.py 触发发现并组装每调用的工具列表;toolsets.py 把工具分组为命名、可按平台开关的集合。能力应在边缘(skill/plugin/MCP)扩展,而非膨胀窄腰(tools/AGENTS.md:3)。
关键类/函数:
| ToolRegistry | tools/registry.py:368 | 单例(docstring「Singleton registry」:369;实例 registry = ToolRegistry() :928);_tools 全局注册 + _scoped_tools 按 HERMES_HOME 的插件覆盖层 |
| register | tools/registry.py:596 | 注册工具(name/toolset/schema/handler/check_fn/override/scope);跨工具集 shadow 默认拒绝(:618-648);mutation bump _generation(:661)使 get_tool_definitions 失效缓存 |
| dispatch | tools/registry.py:810 | 派发工具调用到 handler |
| get_definitions | tools/registry.py:760 | 取通过 check_fn 过滤的工具 schema |
| TOOLSETS | toolsets.py:72 | ~30 个命名工具集;_HERMES_CORE_TOOLS(:11-40)= 每平台继承的默认集 |
| get_tool_definitions | model_tools.py:212 | 每调用组装:选名 → registry.get_definitions(check_fn 过滤)→ 动态 schema 重写器 → Tool Search 折叠;按 generation+config mtime+profile scope 做 LRU 缓存 |
| handle_function_call | model_tools.py:855 | 派发入口:请求中间件 → 前置钩子 → _execute_tool(:804)→ registry.dispatch(:810)→ handler |
| INLINE_TOOL_EXECUTORS | agent/inline_tool_executors.py:154 | agent 循环级工具(todo/memory/session_search/delegate_task)在 dispatch 前拦截 |
2.3.1 工具执行路径
渲染错误: Mermaid 渲染失败: Parse error on line 25:
…lt string TE–>>Loop: 回灌 tool 消息 → 下
———————-^
Expecting '+', '-', '()', 'ACTOR', got 'loop'
2.3.2 工具类别(256 文件,14 类别)
| 文件/FS | tools/file_tools.py、tools/path_security.py | 读/写/patch/搜索 |
| 终端 | tools/terminal_tool.py、tools/terminal_tool_backends.py | shell + 进程管理,可插拔后端 |
| 浏览器 | tools/browser_tool.py、tools/browser_tool_cdp.py、tools/browser_supervisor.py | 自动化(cdp/cloud/lightpanda) |
| 审批/安全 | tools/approval.py、tools/approval_floors.py、tools/url_safety.py | 安全护栏 |
| 委派 | tools/delegate_tool.py:410、tools/async_delegation.py | 子代理 spawn/批量/异步 |
| 看板 | tools/kanban_tools.py、tools/kanban_tools_schemas.py | 多代理协调 |
| MCP 客户端 | tools/mcp_tool_*.py(20+) | 外部 MCP server 工具 |
| 技能 | tools/skills_tool.py、tools/skills_hub_*.py | 技能 CRUD + hub |
| 消息 | tools/send_message_tool.py、tools/discord_tool.py | 平台外发(无 agent 可调 send_message,toolsets.py:185-186) |
| 记忆/todo | tools/memory_tool.py、tools/todo_tool.py:280 | 会话/代理状态(内联派发) |
| 视觉/媒体 | tools/vision_tools.py、tools/image_generation_tool.py、tools/tts_tool_*.py | 图像/视频/音频 |
| Web | tools/web_tools.py、tools/x_search_tool.py | 搜索 + 抽取 |
| 代码执行 | tools/code_execution_tool.py、tools/code_kernel.py | 沙箱 Python |
| 服务连接器 | tools/connections_tool.py、tools/homeassistant_tool.py | service-gated 集成 |
2.3.3 Footprint Ladder(能力扩展阶梯)
#mermaid-svg-Lu6JNTSYZlF3foiv{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#fff;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Lu6JNTSYZlF3foiv .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Lu6JNTSYZlF3foiv .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Lu6JNTSYZlF3foiv .error-icon{fill:hsl(340.1183431953, 84.07960199%, 44.4117647059%);}#mermaid-svg-Lu6JNTSYZlF3foiv .error-text{fill:rgb(46.5298507462, 236.9701492537, 173.8656716418);stroke:rgb(46.5298507462, 236.9701492537, 173.8656716418);}#mermaid-svg-Lu6JNTSYZlF3foiv .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Lu6JNTSYZlF3foiv .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Lu6JNTSYZlF3foiv .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Lu6JNTSYZlF3foiv .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Lu6JNTSYZlF3foiv .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Lu6JNTSYZlF3foiv .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Lu6JNTSYZlF3foiv .marker{fill:#0b0b0b;stroke:#0b0b0b;}#mermaid-svg-Lu6JNTSYZlF3foiv .marker.cross{stroke:#0b0b0b;}#mermaid-svg-Lu6JNTSYZlF3foiv svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Lu6JNTSYZlF3foiv p{margin:0;}#mermaid-svg-Lu6JNTSYZlF3foiv .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#fff;}#mermaid-svg-Lu6JNTSYZlF3foiv .cluster-label text{fill:rgb(46.5298507462, 236.9701492537, 173.8656716418);}#mermaid-svg-Lu6JNTSYZlF3foiv .cluster-label span{color:rgb(46.5298507462, 236.9701492537, 173.8656716418);}#mermaid-svg-Lu6JNTSYZlF3foiv .cluster-label span p{background-color:transparent;}#mermaid-svg-Lu6JNTSYZlF3foiv .label text,#mermaid-svg-Lu6JNTSYZlF3foiv span{fill:#fff;color:#fff;}#mermaid-svg-Lu6JNTSYZlF3foiv .node rect,#mermaid-svg-Lu6JNTSYZlF3foiv .node circle,#mermaid-svg-Lu6JNTSYZlF3foiv .node ellipse,#mermaid-svg-Lu6JNTSYZlF3foiv .node polygon,#mermaid-svg-Lu6JNTSYZlF3foiv .node path{fill:#10b981;stroke:hsl(160.1183431953, 44.07960199%, 29.4117647059%);stroke-width:1px;}#mermaid-svg-Lu6JNTSYZlF3foiv .rough-node .label text,#mermaid-svg-Lu6JNTSYZlF3foiv .node .label text,#mermaid-svg-Lu6JNTSYZlF3foiv .image-shape .label,#mermaid-svg-Lu6JNTSYZlF3foiv .icon-shape .label{text-anchor:middle;}#mermaid-svg-Lu6JNTSYZlF3foiv .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Lu6JNTSYZlF3foiv .rough-node .label,#mermaid-svg-Lu6JNTSYZlF3foiv .node .label,#mermaid-svg-Lu6JNTSYZlF3foiv .image-shape .label,#mermaid-svg-Lu6JNTSYZlF3foiv .icon-shape .label{text-align:center;}#mermaid-svg-Lu6JNTSYZlF3foiv .node.clickable{cursor:pointer;}#mermaid-svg-Lu6JNTSYZlF3foiv .root .anchor path{fill:#0b0b0b!important;stroke-width:0;stroke:#0b0b0b;}#mermaid-svg-Lu6JNTSYZlF3foiv .arrowheadPath{fill:#0b0b0b;}#mermaid-svg-Lu6JNTSYZlF3foiv .edgePath .path{stroke:#0b0b0b;stroke-width:2.0px;}#mermaid-svg-Lu6JNTSYZlF3foiv .flowchart-link{stroke:#0b0b0b;fill:none;}#mermaid-svg-Lu6JNTSYZlF3foiv .edgeLabel{background-color:hsl(40.1183431953, 84.07960199%, 39.4117647059%);text-align:center;}#mermaid-svg-Lu6JNTSYZlF3foiv .edgeLabel p{background-color:hsl(40.1183431953, 84.07960199%, 39.4117647059%);}#mermaid-svg-Lu6JNTSYZlF3foiv .edgeLabel rect{opacity:0.5;background-color:hsl(40.1183431953, 84.07960199%, 39.4117647059%);fill:hsl(40.1183431953, 84.07960199%, 39.4117647059%);}#mermaid-svg-Lu6JNTSYZlF3foiv .labelBkg{background-color:rgba(185, 129.0000000001, 16.0000000001, 0.5);}#mermaid-svg-Lu6JNTSYZlF3foiv .cluster rect{fill:hsl(340.1183431953, 84.07960199%, 44.4117647059%);stroke:hsl(340.1183431953, 44.07960199%, 34.4117647059%);stroke-width:1px;}#mermaid-svg-Lu6JNTSYZlF3foiv .cluster text{fill:rgb(46.5298507462, 236.9701492537, 173.8656716418);}#mermaid-svg-Lu6JNTSYZlF3foiv .cluster span{color:rgb(46.5298507462, 236.9701492537, 173.8656716418);}#mermaid-svg-Lu6JNTSYZlF3foiv div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(340.1183431953, 84.07960199%, 44.4117647059%);border:1px solid hsl(340.1183431953, 44.07960199%, 34.4117647059%);border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Lu6JNTSYZlF3foiv .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#fff;}#mermaid-svg-Lu6JNTSYZlF3foiv rect.text{fill:none;stroke-width:0;}#mermaid-svg-Lu6JNTSYZlF3foiv .icon-shape,#mermaid-svg-Lu6JNTSYZlF3foiv .image-shape{background-color:hsl(40.1183431953, 84.07960199%, 39.4117647059%);text-align:center;}#mermaid-svg-Lu6JNTSYZlF3foiv .icon-shape p,#mermaid-svg-Lu6JNTSYZlF3foiv .image-shape p{background-color:hsl(40.1183431953, 84.07960199%, 39.4117647059%);padding:2px;}#mermaid-svg-Lu6JNTSYZlF3foiv .icon-shape .label rect,#mermaid-svg-Lu6JNTSYZlF3foiv .image-shape .label rect{opacity:0.5;background-color:hsl(40.1183431953, 84.07960199%, 39.4117647059%);fill:hsl(40.1183431953, 84.07960199%, 39.4117647059%);}#mermaid-svg-Lu6JNTSYZlF3foiv .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Lu6JNTSYZlF3foiv .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Lu6JNTSYZlF3foiv :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}#mermaid-svg-Lu6JNTSYZlF3foiv .rung>*{fill:#10b981!important;stroke:#047857!important;color:#fff!important;}#mermaid-svg-Lu6JNTSYZlF3foiv .rung span{fill:#10b981!important;stroke:#047857!important;color:#fff!important;}#mermaid-svg-Lu6JNTSYZlF3foiv .rung tspan{fill:#fff!important;}#mermaid-svg-Lu6JNTSYZlF3foiv .note>*{fill:#f59e0b!important;stroke:#b45309!important;color:#fff!important;}#mermaid-svg-Lu6JNTSYZlF3foiv .note span{fill:#f59e0b!important;stroke:#b45309!important;color:#fff!important;}#mermaid-svg-Lu6JNTSYZlF3foiv .note tspan{fill:#fff!important;}
① 扩展现有代码(首选)
② CLI 命令 + skill
③ service-gated 工具check_fn (TTL 缓存)
④ plugin
⑤ MCP server (catalog)
⑥ 新核心工具(最后手段)
check_fn 只回答可达性/可选入永不回答 surfacetools/registry.py:300-340
证据:AGENTS.md:56-58、AGENTS.md:132-148;check_fn TTL 缓存实现 tools/registry.py:300-340(~30s TTL);GUI-only 工具必须放命名工具集按会话折叠(tools/AGENTS.md:47-49);multiplex/浏览器控制经 check_fn_cache_scope() 绕过缓存(tools/registry.py:244-267)。
2.3.4 模块②架构图
#mermaid-svg-S3blTaqIxhtcDk3A{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#fff;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-S3blTaqIxhtcDk3A .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-S3blTaqIxhtcDk3A .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-S3blTaqIxhtcDk3A .error-icon{fill:hsl(340.1183431953, 84.07960199%, 44.4117647059%);}#mermaid-svg-S3blTaqIxhtcDk3A .error-text{fill:rgb(46.5298507462, 236.9701492537, 173.8656716418);stroke:rgb(46.5298507462, 236.9701492537, 173.8656716418);}#mermaid-svg-S3blTaqIxhtcDk3A .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-S3blTaqIxhtcDk3A .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-S3blTaqIxhtcDk3A .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-S3blTaqIxhtcDk3A .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-S3blTaqIxhtcDk3A .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-S3blTaqIxhtcDk3A .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-S3blTaqIxhtcDk3A .marker{fill:#6b7280;stroke:#6b7280;}#mermaid-svg-S3blTaqIxhtcDk3A .marker.cross{stroke:#6b7280;}#mermaid-svg-S3blTaqIxhtcDk3A svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-S3blTaqIxhtcDk3A p{margin:0;}#mermaid-svg-S3blTaqIxhtcDk3A .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#fff;}#mermaid-svg-S3blTaqIxhtcDk3A .cluster-label text{fill:rgb(46.5298507462, 236.9701492537, 173.8656716418);}#mermaid-svg-S3blTaqIxhtcDk3A .cluster-label span{color:rgb(46.5298507462, 236.9701492537, 173.8656716418);}#mermaid-svg-S3blTaqIxhtcDk3A .cluster-label span p{background-color:transparent;}#mermaid-svg-S3blTaqIxhtcDk3A .label text,#mermaid-svg-S3blTaqIxhtcDk3A span{fill:#fff;color:#fff;}#mermaid-svg-S3blTaqIxhtcDk3A .node rect,#mermaid-svg-S3blTaqIxhtcDk3A .node circle,#mermaid-svg-S3blTaqIxhtcDk3A .node ellipse,#mermaid-svg-S3blTaqIxhtcDk3A .node polygon,#mermaid-svg-S3blTaqIxhtcDk3A .node path{fill:#10b981;stroke:hsl(160.1183431953, 44.07960199%, 29.4117647059%);stroke-width:1px;}#mermaid-svg-S3blTaqIxhtcDk3A .rough-node .label text,#mermaid-svg-S3blTaqIxhtcDk3A .node .label text,#mermaid-svg-S3blTaqIxhtcDk3A .image-shape .label,#mermaid-svg-S3blTaqIxhtcDk3A .icon-shape .label{text-anchor:middle;}#mermaid-svg-S3blTaqIxhtcDk3A .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-S3blTaqIxhtcDk3A .rough-node .label,#mermaid-svg-S3blTaqIxhtcDk3A .node .label,#mermaid-svg-S3blTaqIxhtcDk3A .image-shape .label,#mermaid-svg-S3blTaqIxhtcDk3A .icon-shape .label{text-align:center;}#mermaid-svg-S3blTaqIxhtcDk3A .node.clickable{cursor:pointer;}#mermaid-svg-S3blTaqIxhtcDk3A .root .anchor path{fill:#6b7280!important;stroke-width:0;stroke:#6b7280;}#mermaid-svg-S3blTaqIxhtcDk3A .arrowheadPath{fill:#0b0b0b;}#mermaid-svg-S3blTaqIxhtcDk3A .edgePath .path{stroke:#6b7280;stroke-width:2.0px;}#mermaid-svg-S3blTaqIxhtcDk3A .flowchart-link{stroke:#6b7280;fill:none;}#mermaid-svg-S3blTaqIxhtcDk3A .edgeLabel{background-color:hsl(40.1183431953, 84.07960199%, 39.4117647059%);text-align:center;}#mermaid-svg-S3blTaqIxhtcDk3A .edgeLabel p{background-color:hsl(40.1183431953, 84.07960199%, 39.4117647059%);}#mermaid-svg-S3blTaqIxhtcDk3A .edgeLabel rect{opacity:0.5;background-color:hsl(40.1183431953, 84.07960199%, 39.4117647059%);fill:hsl(40.1183431953, 84.07960199%, 39.4117647059%);}#mermaid-svg-S3blTaqIxhtcDk3A .labelBkg{background-color:rgba(185, 129.0000000001, 16.0000000001, 0.5);}#mermaid-svg-S3blTaqIxhtcDk3A .cluster rect{fill:hsl(340.1183431953, 84.07960199%, 44.4117647059%);stroke:hsl(340.1183431953, 44.07960199%, 34.4117647059%);stroke-width:1px;}#mermaid-svg-S3blTaqIxhtcDk3A .cluster text{fill:rgb(46.5298507462, 236.9701492537, 173.8656716418);}#mermaid-svg-S3blTaqIxhtcDk3A .cluster span{color:rgb(46.5298507462, 236.9701492537, 173.8656716418);}#mermaid-svg-S3blTaqIxhtcDk3A div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(340.1183431953, 84.07960199%, 44.4117647059%);border:1px solid hsl(340.1183431953, 44.07960199%, 34.4117647059%);border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-S3blTaqIxhtcDk3A .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#fff;}#mermaid-svg-S3blTaqIxhtcDk3A rect.text{fill:none;stroke-width:0;}#mermaid-svg-S3blTaqIxhtcDk3A .icon-shape,#mermaid-svg-S3blTaqIxhtcDk3A .image-shape{background-color:hsl(40.1183431953, 84.07960199%, 39.4117647059%);text-align:center;}#mermaid-svg-S3blTaqIxhtcDk3A .icon-shape p,#mermaid-svg-S3blTaqIxhtcDk3A .image-shape p{background-color:hsl(40.1183431953, 84.07960199%, 39.4117647059%);padding:2px;}#mermaid-svg-S3blTaqIxhtcDk3A .icon-shape .label rect,#mermaid-svg-S3blTaqIxhtcDk3A .image-shape .label rect{opacity:0.5;background-color:hsl(40.1183431953, 84.07960199%, 39.4117647059%);fill:hsl(40.1183431953, 84.07960199%, 39.4117647059%);}#mermaid-svg-S3blTaqIxhtcDk3A .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-S3blTaqIxhtcDk3A .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-S3blTaqIxhtcDk3A :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}#mermaid-svg-S3blTaqIxhtcDk3A .core>*{fill:#10b981!important;stroke:#047857!important;color:#fff!important;}#mermaid-svg-S3blTaqIxhtcDk3A .core span{fill:#10b981!important;stroke:#047857!important;color:#fff!important;}#mermaid-svg-S3blTaqIxhtcDk3A .core tspan{fill:#fff!important;}#mermaid-svg-S3blTaqIxhtcDk3A .data>*{fill:#34d399!important;stroke:#059669!important;color:#fff!important;}#mermaid-svg-S3blTaqIxhtcDk3A .data span{fill:#34d399!important;stroke:#059669!important;color:#fff!important;}#mermaid-svg-S3blTaqIxhtcDk3A .data tspan{fill:#fff!important;}#mermaid-svg-S3blTaqIxhtcDk3A .support>*{fill:#60a5fa!important;stroke:#2563eb!important;color:#fff!important;}#mermaid-svg-S3blTaqIxhtcDk3A .support span{fill:#60a5fa!important;stroke:#2563eb!important;color:#fff!important;}#mermaid-svg-S3blTaqIxhtcDk3A .support tspan{fill:#fff!important;}
工具窄腰
import 时自注册
ToolRegistry 单例registry.py:368 / 实例:928
256 工具文件tools/*.py (import 时 registry.register)
_scoped_tools 插件覆盖层按 HERMES_HOME
threading.RLock:383
_generation 计数:661 失效缓存
toolsets.py:72~30 命名工具集
_HERMES_CORE_TOOLS:11默认集
model_tools.pyget_tool_definitions:212handle_function_call:855
inline_tool_executors.py:154todo/memory/session_search/delegate
tool_executor.py:1404并发/顺序/分段执行
2.4 模块③:界面层 —— CLI(cli.py + hermes_cli/)
核心作用:提供两条入口——hermes <cmd> 子命令调度(hermes_cli/main.py)与交互式 REPL(cli.py 的 HermesCLI),并承载配置系统、插件运行时、模型目录、认证、Web 后端、goals、backup、kanban 持久层等横切设施。
关键类/函数:
| main() | hermes_cli/main.py:3351 | hermes 可执行入口:进程设置 + 自愈 + 快速路径 + _build_cli_parser(:3194)+ argparse func= 绑定 + args.func(args) |
| _forward_command | hermes_cli/main.py:1789 | 惰性转发:调用时才 importlib.import_module,避免快速路径付 import 代价 |
| HermesCLI | cli.py:2530 | 交互 REPL 主体类,组合 14 个 mixin |
| process_command | cli.py:3206 | slash 命令派发入口 |
| _SLASH_DISPATCH | cli.py:3175 | canonical -> (method_name, pass_arg) 显式映射字典 |
| COMMAND_REGISTRY | hermes_cli/commands.py:49 | slash 命令单一注册源;resolve_command(:333)解析别名→规范名 |
| DEFAULT_CONFIG | hermes_cli/config_defaults.py:21 | 配置 schema 单一源(纯数据,无类) |
| PluginManager | hermes_cli/plugins.py:1117 | 插件运行时,组合 PluginLoaderMixin/PluginDispatchMixin/PluginLedgerMixin |
| gateway_command | hermes_cli/gateway.py:5826 | 名实不符:是 gateway 进程管理(PID/systemd/launchd/重启),非网络网关;6390 行/282 函数 god-file |
2.4.1 CLI 调度流程
#mermaid-svg-AtKAoDA0aFKAc943{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#fff;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-AtKAoDA0aFKAc943 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-AtKAoDA0aFKAc943 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-AtKAoDA0aFKAc943 .error-icon{fill:hsl(217.6923076923, 92.125984252%, 55.1960784314%);}#mermaid-svg-AtKAoDA0aFKAc943 .error-text{fill:rgb(219.5039370078, 141.2381889763, 8.9960629921);stroke:rgb(219.5039370078, 141.2381889763, 8.9960629921);}#mermaid-svg-AtKAoDA0aFKAc943 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-AtKAoDA0aFKAc943 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-AtKAoDA0aFKAc943 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-AtKAoDA0aFKAc943 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-AtKAoDA0aFKAc943 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-AtKAoDA0aFKAc943 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-AtKAoDA0aFKAc943 .marker{fill:#374151;stroke:#374151;}#mermaid-svg-AtKAoDA0aFKAc943 .marker.cross{stroke:#374151;}#mermaid-svg-AtKAoDA0aFKAc943 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-AtKAoDA0aFKAc943 p{margin:0;}#mermaid-svg-AtKAoDA0aFKAc943 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#fff;}#mermaid-svg-AtKAoDA0aFKAc943 .cluster-label text{fill:rgb(219.5039370078, 141.2381889763, 8.9960629921);}#mermaid-svg-AtKAoDA0aFKAc943 .cluster-label span{color:rgb(219.5039370078, 141.2381889763, 8.9960629921);}#mermaid-svg-AtKAoDA0aFKAc943 .cluster-label span p{background-color:transparent;}#mermaid-svg-AtKAoDA0aFKAc943 .label text,#mermaid-svg-AtKAoDA0aFKAc943 span{fill:#fff;color:#fff;}#mermaid-svg-AtKAoDA0aFKAc943 .node rect,#mermaid-svg-AtKAoDA0aFKAc943 .node circle,#mermaid-svg-AtKAoDA0aFKAc943 .node ellipse,#mermaid-svg-AtKAoDA0aFKAc943 .node polygon,#mermaid-svg-AtKAoDA0aFKAc943 .node path{fill:#f59e0b;stroke:hsl(37.6923076923, 52.125984252%, 40.1960784314%);stroke-width:1px;}#mermaid-svg-AtKAoDA0aFKAc943 .rough-node .label text,#mermaid-svg-AtKAoDA0aFKAc943 .node .label text,#mermaid-svg-AtKAoDA0aFKAc943 .image-shape .label,#mermaid-svg-AtKAoDA0aFKAc943 .icon-shape .label{text-anchor:middle;}#mermaid-svg-AtKAoDA0aFKAc943 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-AtKAoDA0aFKAc943 .rough-node .label,#mermaid-svg-AtKAoDA0aFKAc943 .node .label,#mermaid-svg-AtKAoDA0aFKAc943 .image-shape .label,#mermaid-svg-AtKAoDA0aFKAc943 .icon-shape .label{text-align:center;}#mermaid-svg-AtKAoDA0aFKAc943 .node.clickable{cursor:pointer;}#mermaid-svg-AtKAoDA0aFKAc943 .root .anchor path{fill:#374151!important;stroke-width:0;stroke:#374151;}#mermaid-svg-AtKAoDA0aFKAc943 .arrowheadPath{fill:#0b0b0b;}#mermaid-svg-AtKAoDA0aFKAc943 .edgePath .path{stroke:#374151;stroke-width:2.0px;}#mermaid-svg-AtKAoDA0aFKAc943 .flowchart-link{stroke:#374151;fill:none;}#mermaid-svg-AtKAoDA0aFKAc943 .edgeLabel{background-color:hsl(-82.3076923077, 92.125984252%, 50.1960784314%);text-align:center;}#mermaid-svg-AtKAoDA0aFKAc943 .edgeLabel p{background-color:hsl(-82.3076923077, 92.125984252%, 50.1960784314%);}#mermaid-svg-AtKAoDA0aFKAc943 .edgeLabel rect{opacity:0.5;background-color:hsl(-82.3076923077, 92.125984252%, 50.1960784314%);fill:hsl(-82.3076923077, 92.125984252%, 50.1960784314%);}#mermaid-svg-AtKAoDA0aFKAc943 .labelBkg{background-color:rgba(158, 11.0000000001, 245, 0.5);}#mermaid-svg-AtKAoDA0aFKAc943 .cluster rect{fill:hsl(217.6923076923, 92.125984252%, 55.1960784314%);stroke:hsl(217.6923076923, 52.125984252%, 45.1960784314%);stroke-width:1px;}#mermaid-svg-AtKAoDA0aFKAc943 .cluster text{fill:rgb(219.5039370078, 141.2381889763, 8.9960629921);}#mermaid-svg-AtKAoDA0aFKAc943 .cluster span{color:rgb(219.5039370078, 141.2381889763, 8.9960629921);}#mermaid-svg-AtKAoDA0aFKAc943 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(217.6923076923, 92.125984252%, 55.1960784314%);border:1px solid hsl(217.6923076923, 52.125984252%, 45.1960784314%);border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-AtKAoDA0aFKAc943 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#fff;}#mermaid-svg-AtKAoDA0aFKAc943 rect.text{fill:none;stroke-width:0;}#mermaid-svg-AtKAoDA0aFKAc943 .icon-shape,#mermaid-svg-AtKAoDA0aFKAc943 .image-shape{background-color:hsl(-82.3076923077, 92.125984252%, 50.1960784314%);text-align:center;}#mermaid-svg-AtKAoDA0aFKAc943 .icon-shape p,#mermaid-svg-AtKAoDA0aFKAc943 .image-shape p{background-color:hsl(-82.3076923077, 92.125984252%, 50.1960784314%);padding:2px;}#mermaid-svg-AtKAoDA0aFKAc943 .icon-shape .label rect,#mermaid-svg-AtKAoDA0aFKAc943 .image-shape .label rect{opacity:0.5;background-color:hsl(-82.3076923077, 92.125984252%, 50.1960784314%);fill:hsl(-82.3076923077, 92.125984252%, 50.1960784314%);}#mermaid-svg-AtKAoDA0aFKAc943 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-AtKAoDA0aFKAc943 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-AtKAoDA0aFKAc943 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}#mermaid-svg-AtKAoDA0aFKAc943 .entry>*{fill:#f59e0b!important;stroke:#b45309!important;color:#fff!important;}#mermaid-svg-AtKAoDA0aFKAc943 .entry span{fill:#f59e0b!important;stroke:#b45309!important;color:#fff!important;}#mermaid-svg-AtKAoDA0aFKAc943 .entry tspan{fill:#fff!important;}#mermaid-svg-AtKAoDA0aFKAc943 .step>*{fill:#fbbf24!important;stroke:#d97706!important;color:#fff!important;}#mermaid-svg-AtKAoDA0aFKAc943 .step span{fill:#fbbf24!important;stroke:#d97706!important;color:#fff!important;}#mermaid-svg-AtKAoDA0aFKAc943 .step tspan{fill:#fff!important;}#mermaid-svg-AtKAoDA0aFKAc943 .decision>*{fill:#ef4444!important;stroke:#b91c1c!important;color:#fff!important;}#mermaid-svg-AtKAoDA0aFKAc943 .decision span{fill:#ef4444!important;stroke:#b91c1c!important;color:#fff!important;}#mermaid-svg-AtKAoDA0aFKAc943 .decision tspan{fill:#fff!important;}
Slash 调度 (REPL 内 /cmd)
命中
未命中
未注册
REPL 输入
process_commandcli.py:3206
resolve_commandcommands.py:333COMMAND_REGISTRY:49
_slash_handlercli.py:3199
查 _SLASH_DISPATCHcli.py:3175
调用 mapped method
命名约定反射_handle__commandcli.py:3201-3204
handler()
_process_unregistered_slashcli.py:3254quick→plugins→bundles→skills→prefix
子命令调度 (hermes )
hermes 可执行
main()main.py:3351
早期自愈venv 恢复/字节码清理main.py:3371-3395
快速路径termux/serve/chatmain.py:3418-3424
_build_cli_parsermain.py:3194
set_defaults func=cmd_xxxmain.py:2938/3204
args.func(args)
直接定义cmd_chat:1684cmd_gateway:1770cmd_model:1833
惰性转发 _forward_commandmain.py:1789cmd_setup/login/doctor/plugins/kanban
铁律:无 elif 阶梯(hermes_cli/AGENTS.md:14)。
2.4.2 HermesCLI 的 14 个 Mixin
| CLIProcessNotificationsMixin | cli_process_notifications.py:4 | 进程通知 |
| CLIAgentSetupMixin | cli_agent_setup_mixin.py:174 | agent 启动准备 |
| CLICommandsMixin | cli_commands_mixin.py:607 | slash 处理器主库(56 个 _handle_/_cmd_/_show_) |
| CLIBillingMixin | cli_billing_mixin.py:70 | 计费/订阅展示 |
| CLITuiMixin | cli_tui_mixin.py:94 | prompt_toolkit TUI/键绑定/覆盖层 |
| CLIStatusBarMixin | cli_status_bar_mixin.py:41 | 状态栏渲染 |
| CLIVoiceMixin | cli_voice_mixin.py:54 | 语音输入/STT |
| CLIModelSwitchMixin | cli_model_switch_mixin.py:233 | 模型/provider 切换 |
| CLISessionMixin | cli_session_mixin.py:135 | 会话管理 |
| CLIStreamMixin | cli_stream_mixin.py:45 | 流式输出渲染 |
| CLIModalMixin | cli_modal_mixin.py:80 | 模态确认面板 |
| CLITerminalMixin | cli_terminal_mixin.py:50 | 终端能力/尺寸 |
| CLIInfoMixin | cli_info_mixin.py:73 | /tools//whoami 等信息展示 |
| CLILoopsMixin | cli_loops_mixin.py:30 | REPL 循环骨架 |
2.4.3 配置系统(三套加载器)
#mermaid-svg-SpanioQpQz6cR1b5{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#fff;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-SpanioQpQz6cR1b5 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-SpanioQpQz6cR1b5 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-SpanioQpQz6cR1b5 .error-icon{fill:hsl(217.6923076923, 92.125984252%, 55.1960784314%);}#mermaid-svg-SpanioQpQz6cR1b5 .error-text{fill:rgb(219.5039370078, 141.2381889763, 8.9960629921);stroke:rgb(219.5039370078, 141.2381889763, 8.9960629921);}#mermaid-svg-SpanioQpQz6cR1b5 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-SpanioQpQz6cR1b5 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-SpanioQpQz6cR1b5 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-SpanioQpQz6cR1b5 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-SpanioQpQz6cR1b5 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-SpanioQpQz6cR1b5 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-SpanioQpQz6cR1b5 .marker{fill:#0b0b0b;stroke:#0b0b0b;}#mermaid-svg-SpanioQpQz6cR1b5 .marker.cross{stroke:#0b0b0b;}#mermaid-svg-SpanioQpQz6cR1b5 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-SpanioQpQz6cR1b5 p{margin:0;}#mermaid-svg-SpanioQpQz6cR1b5 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#fff;}#mermaid-svg-SpanioQpQz6cR1b5 .cluster-label text{fill:rgb(219.5039370078, 141.2381889763, 8.9960629921);}#mermaid-svg-SpanioQpQz6cR1b5 .cluster-label span{color:rgb(219.5039370078, 141.2381889763, 8.9960629921);}#mermaid-svg-SpanioQpQz6cR1b5 .cluster-label span p{background-color:transparent;}#mermaid-svg-SpanioQpQz6cR1b5 .label text,#mermaid-svg-SpanioQpQz6cR1b5 span{fill:#fff;color:#fff;}#mermaid-svg-SpanioQpQz6cR1b5 .node rect,#mermaid-svg-SpanioQpQz6cR1b5 .node circle,#mermaid-svg-SpanioQpQz6cR1b5 .node ellipse,#mermaid-svg-SpanioQpQz6cR1b5 .node polygon,#mermaid-svg-SpanioQpQz6cR1b5 .node path{fill:#f59e0b;stroke:hsl(37.6923076923, 52.125984252%, 40.1960784314%);stroke-width:1px;}#mermaid-svg-SpanioQpQz6cR1b5 .rough-node .label text,#mermaid-svg-SpanioQpQz6cR1b5 .node .label text,#mermaid-svg-SpanioQpQz6cR1b5 .image-shape .label,#mermaid-svg-SpanioQpQz6cR1b5 .icon-shape .label{text-anchor:middle;}#mermaid-svg-SpanioQpQz6cR1b5 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-SpanioQpQz6cR1b5 .rough-node .label,#mermaid-svg-SpanioQpQz6cR1b5 .node .label,#mermaid-svg-SpanioQpQz6cR1b5 .image-shape .label,#mermaid-svg-SpanioQpQz6cR1b5 .icon-shape .label{text-align:center;}#mermaid-svg-SpanioQpQz6cR1b5 .node.clickable{cursor:pointer;}#mermaid-svg-SpanioQpQz6cR1b5 .root .anchor path{fill:#0b0b0b!important;stroke-width:0;stroke:#0b0b0b;}#mermaid-svg-SpanioQpQz6cR1b5 .arrowheadPath{fill:#0b0b0b;}#mermaid-svg-SpanioQpQz6cR1b5 .edgePath .path{stroke:#0b0b0b;stroke-width:2.0px;}#mermaid-svg-SpanioQpQz6cR1b5 .flowchart-link{stroke:#0b0b0b;fill:none;}#mermaid-svg-SpanioQpQz6cR1b5 .edgeLabel{background-color:hsl(-82.3076923077, 92.125984252%, 50.1960784314%);text-align:center;}#mermaid-svg-SpanioQpQz6cR1b5 .edgeLabel p{background-color:hsl(-82.3076923077, 92.125984252%, 50.1960784314%);}#mermaid-svg-SpanioQpQz6cR1b5 .edgeLabel rect{opacity:0.5;background-color:hsl(-82.3076923077, 92.125984252%, 50.1960784314%);fill:hsl(-82.3076923077, 92.125984252%, 50.1960784314%);}#mermaid-svg-SpanioQpQz6cR1b5 .labelBkg{background-color:rgba(158, 11.0000000001, 245, 0.5);}#mermaid-svg-SpanioQpQz6cR1b5 .cluster rect{fill:hsl(217.6923076923, 92.125984252%, 55.1960784314%);stroke:hsl(217.6923076923, 52.125984252%, 45.1960784314%);stroke-width:1px;}#mermaid-svg-SpanioQpQz6cR1b5 .cluster text{fill:rgb(219.5039370078, 141.2381889763, 8.9960629921);}#mermaid-svg-SpanioQpQz6cR1b5 .cluster span{color:rgb(219.5039370078, 141.2381889763, 8.9960629921);}#mermaid-svg-SpanioQpQz6cR1b5 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(217.6923076923, 92.125984252%, 55.1960784314%);border:1px solid hsl(217.6923076923, 52.125984252%, 45.1960784314%);border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-SpanioQpQz6cR1b5 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#fff;}#mermaid-svg-SpanioQpQz6cR1b5 rect.text{fill:none;stroke-width:0;}#mermaid-svg-SpanioQpQz6cR1b5 .icon-shape,#mermaid-svg-SpanioQpQz6cR1b5 .image-shape{background-color:hsl(-82.3076923077, 92.125984252%, 50.1960784314%);text-align:center;}#mermaid-svg-SpanioQpQz6cR1b5 .icon-shape p,#mermaid-svg-SpanioQpQz6cR1b5 .image-shape p{background-color:hsl(-82.3076923077, 92.125984252%, 50.1960784314%);padding:2px;}#mermaid-svg-SpanioQpQz6cR1b5 .icon-shape .label rect,#mermaid-svg-SpanioQpQz6cR1b5 .image-shape .label rect{opacity:0.5;background-color:hsl(-82.3076923077, 92.125984252%, 50.1960784314%);fill:hsl(-82.3076923077, 92.125984252%, 50.1960784314%);}#mermaid-svg-SpanioQpQz6cR1b5 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-SpanioQpQz6cR1b5 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-SpanioQpQz6cR1b5 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}#mermaid-svg-SpanioQpQz6cR1b5 .src>*{fill:#f59e0b!important;stroke:#b45309!important;color:#fff!important;}#mermaid-svg-SpanioQpQz6cR1b5 .src span{fill:#f59e0b!important;stroke:#b45309!important;color:#fff!important;}#mermaid-svg-SpanioQpQz6cR1b5 .src tspan{fill:#fff!important;}#mermaid-svg-SpanioQpQz6cR1b5 .loader>*{fill:#fbbf24!important;stroke:#d97706!important;color:#fff!important;}#mermaid-svg-SpanioQpQz6cR1b5 .loader span{fill:#fbbf24!important;stroke:#d97706!important;color:#fff!important;}#mermaid-svg-SpanioQpQz6cR1b5 .loader tspan{fill:#fff!important;}#mermaid-svg-SpanioQpQz6cR1b5 .warn>*{fill:#ef4444!important;stroke:#b91c1c!important;color:#fff!important;}#mermaid-svg-SpanioQpQz6cR1b5 .warn span{fill:#ef4444!important;stroke:#b91c1c!important;color:#fff!important;}#mermaid-svg-SpanioQpQz6cR1b5 .warn tspan{fill:#fff!important;}
不合并 DEFAULT_CONFIG
DEFAULT_CONFIGconfig_defaults.py:21(单一 schema 源, 纯数据)
.env 仅密钥OPTIONAL_ENV_VARS:2429
config_migrations.py_migrate_to_N:110 起(仅主动迁移才 bump _config_version)
① load_cli_configcli.py:458(CLI: 默认+用户YAML)
② load_confighermes_cli/config.py(tools/setup/多数子命令合并 DEFAULT_CONFIG)
③ 原始 YAMLgateway/run.py + gateway/config.py(gateway 运行时)
校验require_parseable_user_config:468InvalidUserConfigError:48 / ConfigIssue:1059
⚠ 若 CLI 见到 key 而 gateway 不见= 用错加载器AGENTS.md:69-72
2.4.4 模块③架构图
#mermaid-svg-MrfcwQWLUWnk5TSO{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#fff;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-MrfcwQWLUWnk5TSO .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-MrfcwQWLUWnk5TSO .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-MrfcwQWLUWnk5TSO .error-icon{fill:hsl(217.6923076923, 92.125984252%, 55.1960784314%);}#mermaid-svg-MrfcwQWLUWnk5TSO .error-text{fill:rgb(219.5039370078, 141.2381889763, 8.9960629921);stroke:rgb(219.5039370078, 141.2381889763, 8.9960629921);}#mermaid-svg-MrfcwQWLUWnk5TSO .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-MrfcwQWLUWnk5TSO .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-MrfcwQWLUWnk5TSO .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-MrfcwQWLUWnk5TSO .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-MrfcwQWLUWnk5TSO .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-MrfcwQWLUWnk5TSO .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-MrfcwQWLUWnk5TSO .marker{fill:#6b7280;stroke:#6b7280;}#mermaid-svg-MrfcwQWLUWnk5TSO .marker.cross{stroke:#6b7280;}#mermaid-svg-MrfcwQWLUWnk5TSO svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-MrfcwQWLUWnk5TSO p{margin:0;}#mermaid-svg-MrfcwQWLUWnk5TSO .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#fff;}#mermaid-svg-MrfcwQWLUWnk5TSO .cluster-label text{fill:rgb(219.5039370078, 141.2381889763, 8.9960629921);}#mermaid-svg-MrfcwQWLUWnk5TSO .cluster-label span{color:rgb(219.5039370078, 141.2381889763, 8.9960629921);}#mermaid-svg-MrfcwQWLUWnk5TSO .cluster-label span p{background-color:transparent;}#mermaid-svg-MrfcwQWLUWnk5TSO .label text,#mermaid-svg-MrfcwQWLUWnk5TSO span{fill:#fff;color:#fff;}#mermaid-svg-MrfcwQWLUWnk5TSO .node rect,#mermaid-svg-MrfcwQWLUWnk5TSO .node circle,#mermaid-svg-MrfcwQWLUWnk5TSO .node ellipse,#mermaid-svg-MrfcwQWLUWnk5TSO .node polygon,#mermaid-svg-MrfcwQWLUWnk5TSO .node path{fill:#f59e0b;stroke:hsl(37.6923076923, 52.125984252%, 40.1960784314%);stroke-width:1px;}#mermaid-svg-MrfcwQWLUWnk5TSO .rough-node .label text,#mermaid-svg-MrfcwQWLUWnk5TSO .node .label text,#mermaid-svg-MrfcwQWLUWnk5TSO .image-shape .label,#mermaid-svg-MrfcwQWLUWnk5TSO .icon-shape .label{text-anchor:middle;}#mermaid-svg-MrfcwQWLUWnk5TSO .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-MrfcwQWLUWnk5TSO .rough-node .label,#mermaid-svg-MrfcwQWLUWnk5TSO .node .label,#mermaid-svg-MrfcwQWLUWnk5TSO .image-shape .label,#mermaid-svg-MrfcwQWLUWnk5TSO .icon-shape .label{text-align:center;}#mermaid-svg-MrfcwQWLUWnk5TSO .node.clickable{cursor:pointer;}#mermaid-svg-MrfcwQWLUWnk5TSO .root .anchor path{fill:#6b7280!important;stroke-width:0;stroke:#6b7280;}#mermaid-svg-MrfcwQWLUWnk5TSO .arrowheadPath{fill:#0b0b0b;}#mermaid-svg-MrfcwQWLUWnk5TSO .edgePath .path{stroke:#6b7280;stroke-width:2.0px;}#mermaid-svg-MrfcwQWLUWnk5TSO .flowchart-link{stroke:#6b7280;fill:none;}#mermaid-svg-MrfcwQWLUWnk5TSO .edgeLabel{background-color:hsl(-82.3076923077, 92.125984252%, 50.1960784314%);text-align:center;}#mermaid-svg-MrfcwQWLUWnk5TSO .edgeLabel p{background-color:hsl(-82.3076923077, 92.125984252%, 50.1960784314%);}#mermaid-svg-MrfcwQWLUWnk5TSO .edgeLabel rect{opacity:0.5;background-color:hsl(-82.3076923077, 92.125984252%, 50.1960784314%);fill:hsl(-82.3076923077, 92.125984252%, 50.1960784314%);}#mermaid-svg-MrfcwQWLUWnk5TSO .labelBkg{background-color:rgba(158, 11.0000000001, 245, 0.5);}#mermaid-svg-MrfcwQWLUWnk5TSO .cluster rect{fill:hsl(217.6923076923, 92.125984252%, 55.1960784314%);stroke:hsl(217.6923076923, 52.125984252%, 45.1960784314%);stroke-width:1px;}#mermaid-svg-MrfcwQWLUWnk5TSO .cluster text{fill:rgb(219.5039370078, 141.2381889763, 8.9960629921);}#mermaid-svg-MrfcwQWLUWnk5TSO .cluster span{color:rgb(219.5039370078, 141.2381889763, 8.9960629921);}#mermaid-svg-MrfcwQWLUWnk5TSO div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(217.6923076923, 92.125984252%, 55.1960784314%);border:1px solid hsl(217.6923076923, 52.125984252%, 45.1960784314%);border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-MrfcwQWLUWnk5TSO .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#fff;}#mermaid-svg-MrfcwQWLUWnk5TSO rect.text{fill:none;stroke-width:0;}#mermaid-svg-MrfcwQWLUWnk5TSO .icon-shape,#mermaid-svg-MrfcwQWLUWnk5TSO .image-shape{background-color:hsl(-82.3076923077, 92.125984252%, 50.1960784314%);text-align:center;}#mermaid-svg-MrfcwQWLUWnk5TSO .icon-shape p,#mermaid-svg-MrfcwQWLUWnk5TSO .image-shape p{background-color:hsl(-82.3076923077, 92.125984252%, 50.1960784314%);padding:2px;}#mermaid-svg-MrfcwQWLUWnk5TSO .icon-shape .label rect,#mermaid-svg-MrfcwQWLUWnk5TSO .image-shape .label rect{opacity:0.5;background-color:hsl(-82.3076923077, 92.125984252%, 50.1960784314%);fill:hsl(-82.3076923077, 92.125984252%, 50.1960784314%);}#mermaid-svg-MrfcwQWLUWnk5TSO .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-MrfcwQWLUWnk5TSO .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-MrfcwQWLUWnk5TSO :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}#mermaid-svg-MrfcwQWLUWnk5TSO .god>*{fill:#ef4444!important;stroke:#b91c1c!important;color:#fff!important;}#mermaid-svg-MrfcwQWLUWnk5TSO .god span{fill:#ef4444!important;stroke:#b91c1c!important;color:#fff!important;}#mermaid-svg-MrfcwQWLUWnk5TSO .god tspan{fill:#fff!important;}#mermaid-svg-MrfcwQWLUWnk5TSO .main>*{fill:#f59e0b!important;stroke:#b45309!important;color:#fff!important;}#mermaid-svg-MrfcwQWLUWnk5TSO .main span{fill:#f59e0b!important;stroke:#b45309!important;color:#fff!important;}#mermaid-svg-MrfcwQWLUWnk5TSO .main tspan{fill:#fff!important;}#mermaid-svg-MrfcwQWLUWnk5TSO .support>*{fill:#fbbf24!important;stroke:#d97706!important;color:#fff!important;}#mermaid-svg-MrfcwQWLUWnk5TSO .support span{fill:#fbbf24!important;stroke:#d97706!important;color:#fff!important;}#mermaid-svg-MrfcwQWLUWnk5TSO .support tspan{fill:#fff!important;}
CLI 界面层
hermes_cli/main.py:3351子命令入口
cli.py:2530HermesCLI REPL (14 mixin)
cli_*_mixin.py14 个行为 mixin
commands.py:49COMMAND_REGISTRY 单一源
hermes_cli/gateway.py:5826gateway 进程管理(288KB god-file)
config.py + config_defaults.py:21配置系统 (三加载器)
plugins.py:1117PluginManager (3 mixin)
models.py / auth.py:149模型目录 / 认证
web_server.py:138FastAPI 后端
goals.py:281GoalContract/Gate/State
kanban_db.py178KB 持久层
backup.py:118
2.5 模块④:界面层 —— 消息网关(gateway/)
核心作用:消息网关——把 agent 内核暴露到 ~34 个消息平台(Telegram/Discord/Slack 等)。gateway/run.py 是门面,生命周期阶段在 run_*.py(startup/adapters/inbound/turn/busy/notifications/shutdown…),会话在 session*.py,slash 处理在 slash_commands_*.py,适配器在 platforms/<name>.py 之上基于 platforms/base.py 的 BasePlatformAdapter。
关键类/函数:
| GatewayRunner | gateway/run.py:3332-3337 | god-object:组合 16 个 Mixin(GatewayAuthorizationMixin、GatewayTurnMixin、GatewayShutdownMixin、GatewayBusySessionMixin、GatewayStartupMixin、GatewayInboundMixin、GatewayNotificationsMixin、GatewayAdapterLifecycleMixin、GatewayAgentCacheMixin 等);隐式共享可变状态属性面庞大(run.py:3364-3384) |
| start_gateway | gateway/run.py:5249 | 异步入口;__main__ 调 asyncio.run(start_gateway(…))(run.py:5474) |
| BasePlatformAdapter(ABC) | gateway/platforms/base.py:1797 | 平台适配器 ABC;抽象方法 connect/disconnect/send/edit_message/delete_message(:2414/:2420/:2424/:2437/:2445);_active_sessions(:1863)+ _pending_messages(:1864)实现消息守卫① |
| PlatformRegistry | gateway/platform_registry.py:102 | 适配器延迟/惰性注册与工厂;create_adapter(:349)、register(:271)、register_deferred(:156),无 if/elif 链 |
| TurnRunner | gateway/run_turn_runner.py:48 | 每轮 turn 协作者;_build_fresh_agent(:1011)构造/复用 AIAgent |
| _run_agent_inner | gateway/run_turn.py:3819 | 驱动一个 turn;同步路径在 executor 内调 agent.run_conversation(run_turn.py:2192/:2196) |
| GatewayAgentCacheMixin | gateway/run_agent_cache.py:43 | 跨 turn 缓存 AIAgent(_AGENT_CACHE_MAX_SIZE=128、_AGENT_CACHE_IDLE_TTL_SECS=3600,run.py:46-47) |
| acquire_scoped_lock / release_scoped_lock | gateway/status.py:1094 / :1142 | token 锁:唯一凭据的适配器在 connect/stop 时获取/释放,防两个 profile 共享一个凭据(gateway/AGENTS.md:129-132) |
| AIAgent( 调用点 | gateway/run_turn.py:1146,2163、gateway/run_turn_runner.py:1017-1039 | gateway 构造 AIAgent 实例(model/session_id/platform/user_id/chat_id/gateway_session_key/session_db…) |
循环分层警示:run_turn_runner.py:1013 反向 from gateway.run import _checkpoint_agent_kwargs——turn 协作者回引门面,形成 facade↔collaborator 的近似循环依赖。
2.5.1 网关生命周期阶段(17 个 run_*.py)
#mermaid-svg-rwgL2QoZftNNZgcz{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#fff;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-rwgL2QoZftNNZgcz .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-rwgL2QoZftNNZgcz .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-rwgL2QoZftNNZgcz .error-icon{fill:hsl(217.6923076923, 92.125984252%, 55.1960784314%);}#mermaid-svg-rwgL2QoZftNNZgcz .error-text{fill:rgb(219.5039370078, 141.2381889763, 8.9960629921);stroke:rgb(219.5039370078, 141.2381889763, 8.9960629921);}#mermaid-svg-rwgL2QoZftNNZgcz .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-rwgL2QoZftNNZgcz .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-rwgL2QoZftNNZgcz .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-rwgL2QoZftNNZgcz .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-rwgL2QoZftNNZgcz .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-rwgL2QoZftNNZgcz .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-rwgL2QoZftNNZgcz .marker{fill:#0b0b0b;stroke:#0b0b0b;}#mermaid-svg-rwgL2QoZftNNZgcz .marker.cross{stroke:#0b0b0b;}#mermaid-svg-rwgL2QoZftNNZgcz svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-rwgL2QoZftNNZgcz p{margin:0;}#mermaid-svg-rwgL2QoZftNNZgcz .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#fff;}#mermaid-svg-rwgL2QoZftNNZgcz .cluster-label text{fill:rgb(219.5039370078, 141.2381889763, 8.9960629921);}#mermaid-svg-rwgL2QoZftNNZgcz .cluster-label span{color:rgb(219.5039370078, 141.2381889763, 8.9960629921);}#mermaid-svg-rwgL2QoZftNNZgcz .cluster-label span p{background-color:transparent;}#mermaid-svg-rwgL2QoZftNNZgcz .label text,#mermaid-svg-rwgL2QoZftNNZgcz span{fill:#fff;color:#fff;}#mermaid-svg-rwgL2QoZftNNZgcz .node rect,#mermaid-svg-rwgL2QoZftNNZgcz .node circle,#mermaid-svg-rwgL2QoZftNNZgcz .node ellipse,#mermaid-svg-rwgL2QoZftNNZgcz .node polygon,#mermaid-svg-rwgL2QoZftNNZgcz .node path{fill:#f59e0b;stroke:hsl(37.6923076923, 52.125984252%, 40.1960784314%);stroke-width:1px;}#mermaid-svg-rwgL2QoZftNNZgcz .rough-node .label text,#mermaid-svg-rwgL2QoZftNNZgcz .node .label text,#mermaid-svg-rwgL2QoZftNNZgcz .image-shape .label,#mermaid-svg-rwgL2QoZftNNZgcz .icon-shape .label{text-anchor:middle;}#mermaid-svg-rwgL2QoZftNNZgcz .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-rwgL2QoZftNNZgcz .rough-node .label,#mermaid-svg-rwgL2QoZftNNZgcz .node .label,#mermaid-svg-rwgL2QoZftNNZgcz .image-shape .label,#mermaid-svg-rwgL2QoZftNNZgcz .icon-shape .label{text-align:center;}#mermaid-svg-rwgL2QoZftNNZgcz .node.clickable{cursor:pointer;}#mermaid-svg-rwgL2QoZftNNZgcz .root .anchor path{fill:#0b0b0b!important;stroke-width:0;stroke:#0b0b0b;}#mermaid-svg-rwgL2QoZftNNZgcz .arrowheadPath{fill:#0b0b0b;}#mermaid-svg-rwgL2QoZftNNZgcz .edgePath .path{stroke:#0b0b0b;stroke-width:2.0px;}#mermaid-svg-rwgL2QoZftNNZgcz .flowchart-link{stroke:#0b0b0b;fill:none;}#mermaid-svg-rwgL2QoZftNNZgcz .edgeLabel{background-color:hsl(-82.3076923077, 92.125984252%, 50.1960784314%);text-align:center;}#mermaid-svg-rwgL2QoZftNNZgcz .edgeLabel p{background-color:hsl(-82.3076923077, 92.125984252%, 50.1960784314%);}#mermaid-svg-rwgL2QoZftNNZgcz .edgeLabel rect{opacity:0.5;background-color:hsl(-82.3076923077, 92.125984252%, 50.1960784314%);fill:hsl(-82.3076923077, 92.125984252%, 50.1960784314%);}#mermaid-svg-rwgL2QoZftNNZgcz .labelBkg{background-color:rgba(158, 11.0000000001, 245, 0.5);}#mermaid-svg-rwgL2QoZftNNZgcz .cluster rect{fill:hsl(217.6923076923, 92.125984252%, 55.1960784314%);stroke:hsl(217.6923076923, 52.125984252%, 45.1960784314%);stroke-width:1px;}#mermaid-svg-rwgL2QoZftNNZgcz .cluster text{fill:rgb(219.5039370078, 141.2381889763, 8.9960629921);}#mermaid-svg-rwgL2QoZftNNZgcz .cluster span{color:rgb(219.5039370078, 141.2381889763, 8.9960629921);}#mermaid-svg-rwgL2QoZftNNZgcz div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(217.6923076923, 92.125984252%, 55.1960784314%);border:1px solid hsl(217.6923076923, 52.125984252%, 45.1960784314%);border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-rwgL2QoZftNNZgcz .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#fff;}#mermaid-svg-rwgL2QoZftNNZgcz rect.text{fill:none;stroke-width:0;}#mermaid-svg-rwgL2QoZftNNZgcz .icon-shape,#mermaid-svg-rwgL2QoZftNNZgcz .image-shape{background-color:hsl(-82.3076923077, 92.125984252%, 50.1960784314%);text-align:center;}#mermaid-svg-rwgL2QoZftNNZgcz .icon-shape p,#mermaid-svg-rwgL2QoZftNNZgcz .image-shape p{background-color:hsl(-82.3076923077, 92.125984252%, 50.1960784314%);padding:2px;}#mermaid-svg-rwgL2QoZftNNZgcz .icon-shape .label rect,#mermaid-svg-rwgL2QoZftNNZgcz .image-shape .label rect{opacity:0.5;background-color:hsl(-82.3076923077, 92.125984252%, 50.1960784314%);fill:hsl(-82.3076923077, 92.125984252%, 50.1960784314%);}#mermaid-svg-rwgL2QoZftNNZgcz .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-rwgL2QoZftNNZgcz .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-rwgL2QoZftNNZgcz :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}#mermaid-svg-rwgL2QoZftNNZgcz .phase>*{fill:#f59e0b!important;stroke:#b45309!important;color:#fff!important;}#mermaid-svg-rwgL2QoZftNNZgcz .phase span{fill:#f59e0b!important;stroke:#b45309!important;color:#fff!important;}#mermaid-svg-rwgL2QoZftNNZgcz .phase tspan{fill:#fff!important;}
关闭
侧路
执行
入站
启动
缓存
拦截 slash
run_startup.py进程初始化
run_adapters.py连接平台适配器
run_inbound.py接收消息过两道消息守卫
run_turn.py:1146构造 AIAgent + 跑 turn
run_turn_runner.py:1017turn 运行器
run_agent_cache.py跨 turn 缓存 agent
run_busy.py运行中 slash 拦截守卫②
run_notifications.py后台进程通知
run_goals.py
run_watchers.py
run_voice.py
run_shutdown.py优雅关闭
2.5.2 两道消息守卫 + 流式契约 + token 锁
#mermaid-svg-XGShp7SHhagYEphX{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#fff;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-XGShp7SHhagYEphX .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-XGShp7SHhagYEphX .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-XGShp7SHhagYEphX .error-icon{fill:hsl(217.6923076923, 92.125984252%, 55.1960784314%);}#mermaid-svg-XGShp7SHhagYEphX .error-text{fill:rgb(219.5039370078, 141.2381889763, 8.9960629921);stroke:rgb(219.5039370078, 141.2381889763, 8.9960629921);}#mermaid-svg-XGShp7SHhagYEphX .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-XGShp7SHhagYEphX .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-XGShp7SHhagYEphX .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-XGShp7SHhagYEphX .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-XGShp7SHhagYEphX .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-XGShp7SHhagYEphX .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-XGShp7SHhagYEphX .marker{fill:#374151;stroke:#374151;}#mermaid-svg-XGShp7SHhagYEphX .marker.cross{stroke:#374151;}#mermaid-svg-XGShp7SHhagYEphX svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-XGShp7SHhagYEphX p{margin:0;}#mermaid-svg-XGShp7SHhagYEphX .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#fff;}#mermaid-svg-XGShp7SHhagYEphX .cluster-label text{fill:rgb(219.5039370078, 141.2381889763, 8.9960629921);}#mermaid-svg-XGShp7SHhagYEphX .cluster-label span{color:rgb(219.5039370078, 141.2381889763, 8.9960629921);}#mermaid-svg-XGShp7SHhagYEphX .cluster-label span p{background-color:transparent;}#mermaid-svg-XGShp7SHhagYEphX .label text,#mermaid-svg-XGShp7SHhagYEphX span{fill:#fff;color:#fff;}#mermaid-svg-XGShp7SHhagYEphX .node rect,#mermaid-svg-XGShp7SHhagYEphX .node circle,#mermaid-svg-XGShp7SHhagYEphX .node ellipse,#mermaid-svg-XGShp7SHhagYEphX .node polygon,#mermaid-svg-XGShp7SHhagYEphX .node path{fill:#f59e0b;stroke:hsl(37.6923076923, 52.125984252%, 40.1960784314%);stroke-width:1px;}#mermaid-svg-XGShp7SHhagYEphX .rough-node .label text,#mermaid-svg-XGShp7SHhagYEphX .node .label text,#mermaid-svg-XGShp7SHhagYEphX .image-shape .label,#mermaid-svg-XGShp7SHhagYEphX .icon-shape .label{text-anchor:middle;}#mermaid-svg-XGShp7SHhagYEphX .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-XGShp7SHhagYEphX .rough-node .label,#mermaid-svg-XGShp7SHhagYEphX .node .label,#mermaid-svg-XGShp7SHhagYEphX .image-shape .label,#mermaid-svg-XGShp7SHhagYEphX .icon-shape .label{text-align:center;}#mermaid-svg-XGShp7SHhagYEphX .node.clickable{cursor:pointer;}#mermaid-svg-XGShp7SHhagYEphX .root .anchor path{fill:#374151!important;stroke-width:0;stroke:#374151;}#mermaid-svg-XGShp7SHhagYEphX .arrowheadPath{fill:#0b0b0b;}#mermaid-svg-XGShp7SHhagYEphX .edgePath .path{stroke:#374151;stroke-width:2.0px;}#mermaid-svg-XGShp7SHhagYEphX .flowchart-link{stroke:#374151;fill:none;}#mermaid-svg-XGShp7SHhagYEphX .edgeLabel{background-color:hsl(-82.3076923077, 92.125984252%, 50.1960784314%);text-align:center;}#mermaid-svg-XGShp7SHhagYEphX .edgeLabel p{background-color:hsl(-82.3076923077, 92.125984252%, 50.1960784314%);}#mermaid-svg-XGShp7SHhagYEphX .edgeLabel rect{opacity:0.5;background-color:hsl(-82.3076923077, 92.125984252%, 50.1960784314%);fill:hsl(-82.3076923077, 92.125984252%, 50.1960784314%);}#mermaid-svg-XGShp7SHhagYEphX .labelBkg{background-color:rgba(158, 11.0000000001, 245, 0.5);}#mermaid-svg-XGShp7SHhagYEphX .cluster rect{fill:hsl(217.6923076923, 92.125984252%, 55.1960784314%);stroke:hsl(217.6923076923, 52.125984252%, 45.1960784314%);stroke-width:1px;}#mermaid-svg-XGShp7SHhagYEphX .cluster text{fill:rgb(219.5039370078, 141.2381889763, 8.9960629921);}#mermaid-svg-XGShp7SHhagYEphX .cluster span{color:rgb(219.5039370078, 141.2381889763, 8.9960629921);}#mermaid-svg-XGShp7SHhagYEphX div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(217.6923076923, 92.125984252%, 55.1960784314%);border:1px solid hsl(217.6923076923, 52.125984252%, 45.1960784314%);border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-XGShp7SHhagYEphX .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#fff;}#mermaid-svg-XGShp7SHhagYEphX rect.text{fill:none;stroke-width:0;}#mermaid-svg-XGShp7SHhagYEphX .icon-shape,#mermaid-svg-XGShp7SHhagYEphX .image-shape{background-color:hsl(-82.3076923077, 92.125984252%, 50.1960784314%);text-align:center;}#mermaid-svg-XGShp7SHhagYEphX .icon-shape p,#mermaid-svg-XGShp7SHhagYEphX .image-shape p{background-color:hsl(-82.3076923077, 92.125984252%, 50.1960784314%);padding:2px;}#mermaid-svg-XGShp7SHhagYEphX .icon-shape .label rect,#mermaid-svg-XGShp7SHhagYEphX .image-shape .label rect{opacity:0.5;background-color:hsl(-82.3076923077, 92.125984252%, 50.1960784314%);fill:hsl(-82.3076923077, 92.125984252%, 50.1960784314%);}#mermaid-svg-XGShp7SHhagYEphX .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-XGShp7SHhagYEphX .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-XGShp7SHhagYEphX :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}#mermaid-svg-XGShp7SHhagYEphX .guard>*{fill:#ef4444!important;stroke:#b91c1c!important;color:#fff!important;}#mermaid-svg-XGShp7SHhagYEphX .guard span{fill:#ef4444!important;stroke:#b91c1c!important;color:#fff!important;}#mermaid-svg-XGShp7SHhagYEphX .guard tspan{fill:#fff!important;}#mermaid-svg-XGShp7SHhagYEphX .stream>*{fill:#10b981!important;stroke:#047857!important;color:#fff!important;}#mermaid-svg-XGShp7SHhagYEphX .stream span{fill:#10b981!important;stroke:#047857!important;color:#fff!important;}#mermaid-svg-XGShp7SHhagYEphX .stream tspan{fill:#fff!important;}#mermaid-svg-XGShp7SHhagYEphX .lock>*{fill:#8b5cf6!important;stroke:#6d28d9!important;color:#fff!important;}#mermaid-svg-XGShp7SHhagYEphX .lock span{fill:#8b5cf6!important;stroke:#6d28d9!important;color:#fff!important;}#mermaid-svg-XGShp7SHhagYEphX .lock tspan{fill:#fff!important;}#mermaid-svg-XGShp7SHhagYEphX .bypass>*{fill:#f59e0b!important;stroke:#b45309!important;color:#fff!important;}#mermaid-svg-XGShp7SHhagYEphX .bypass span{fill:#f59e0b!important;stroke:#b45309!important;color:#fff!important;}#mermaid-svg-XGShp7SHhagYEphX .bypass tspan{fill:#fff!important;}
守卫② — Runner
守卫① — 基础适配器
控制命令绕过
控制命令绕过
token 锁
acquire_scoped_lockstatus.py:1094 (connect)
release_scoped_lockstatus.py:1142 (stop)
流式契约 (draft_stream_is_message=True)
① draft 帧 prefix-stable
② 消费者声明 finaladapter 永不猜
③ interim 带 _interim_send=True
④ edit 对账, 非 plain send
入站消息 (agent 运行中)
platforms/base.pysession_key in _active_sessions→ 排队 _pending_messages:1864
run.py / run_busy.py拦截 /stop /new /queue /status/approve /deny→ running_agent.interrupt()
必须绕过两道守卫的命令inline 派发永不走 _process_message_backgroundgateway/AGENTS.md:20-28
证据:两道守卫 gateway/AGENTS.md:20-28;流式契约四不变量 gateway/AGENTS.md:30-52(每条来自一次重复最终事故 NS-658 / #85796);token 锁 gateway/AGENTS.md:129-132。
2.5.3 平台适配器(34 个 = 12 内置 + 22 插件)
| 内置适配器 | 12 | gateway/platforms/*.py | signal、whatsapp_cloud、weixin、yuanbao、webhook、api_server 等 |
| 插件适配器 | 22 | plugins/platforms/*/ | telegram(382KB)、discord(352KB)、slack(339KB)、feishu、matrix、teams、line、irc、ntfy、wecom、simplex… |
| 基类 | 1 | gateway/platforms/base.py:1797 | BasePlatformAdapter(ABC),4263 行 |
2.5.4 模块④架构图
#mermaid-svg-tCddFpkGqfFjl0AT{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#fff;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-tCddFpkGqfFjl0AT .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-tCddFpkGqfFjl0AT .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-tCddFpkGqfFjl0AT .error-icon{fill:hsl(217.6923076923, 92.125984252%, 55.1960784314%);}#mermaid-svg-tCddFpkGqfFjl0AT .error-text{fill:rgb(219.5039370078, 141.2381889763, 8.9960629921);stroke:rgb(219.5039370078, 141.2381889763, 8.9960629921);}#mermaid-svg-tCddFpkGqfFjl0AT .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-tCddFpkGqfFjl0AT .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-tCddFpkGqfFjl0AT .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-tCddFpkGqfFjl0AT .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-tCddFpkGqfFjl0AT .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-tCddFpkGqfFjl0AT .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-tCddFpkGqfFjl0AT .marker{fill:#6b7280;stroke:#6b7280;}#mermaid-svg-tCddFpkGqfFjl0AT .marker.cross{stroke:#6b7280;}#mermaid-svg-tCddFpkGqfFjl0AT svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-tCddFpkGqfFjl0AT p{margin:0;}#mermaid-svg-tCddFpkGqfFjl0AT .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#fff;}#mermaid-svg-tCddFpkGqfFjl0AT .cluster-label text{fill:rgb(219.5039370078, 141.2381889763, 8.9960629921);}#mermaid-svg-tCddFpkGqfFjl0AT .cluster-label span{color:rgb(219.5039370078, 141.2381889763, 8.9960629921);}#mermaid-svg-tCddFpkGqfFjl0AT .cluster-label span p{background-color:transparent;}#mermaid-svg-tCddFpkGqfFjl0AT .label text,#mermaid-svg-tCddFpkGqfFjl0AT span{fill:#fff;color:#fff;}#mermaid-svg-tCddFpkGqfFjl0AT .node rect,#mermaid-svg-tCddFpkGqfFjl0AT .node circle,#mermaid-svg-tCddFpkGqfFjl0AT .node ellipse,#mermaid-svg-tCddFpkGqfFjl0AT .node polygon,#mermaid-svg-tCddFpkGqfFjl0AT .node path{fill:#f59e0b;stroke:hsl(37.6923076923, 52.125984252%, 40.1960784314%);stroke-width:1px;}#mermaid-svg-tCddFpkGqfFjl0AT .rough-node .label text,#mermaid-svg-tCddFpkGqfFjl0AT .node .label text,#mermaid-svg-tCddFpkGqfFjl0AT .image-shape .label,#mermaid-svg-tCddFpkGqfFjl0AT .icon-shape .label{text-anchor:middle;}#mermaid-svg-tCddFpkGqfFjl0AT .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-tCddFpkGqfFjl0AT .rough-node .label,#mermaid-svg-tCddFpkGqfFjl0AT .node .label,#mermaid-svg-tCddFpkGqfFjl0AT .image-shape .label,#mermaid-svg-tCddFpkGqfFjl0AT .icon-shape .label{text-align:center;}#mermaid-svg-tCddFpkGqfFjl0AT .node.clickable{cursor:pointer;}#mermaid-svg-tCddFpkGqfFjl0AT .root .anchor path{fill:#6b7280!important;stroke-width:0;stroke:#6b7280;}#mermaid-svg-tCddFpkGqfFjl0AT .arrowheadPath{fill:#0b0b0b;}#mermaid-svg-tCddFpkGqfFjl0AT .edgePath .path{stroke:#6b7280;stroke-width:2.0px;}#mermaid-svg-tCddFpkGqfFjl0AT .flowchart-link{stroke:#6b7280;fill:none;}#mermaid-svg-tCddFpkGqfFjl0AT .edgeLabel{background-color:hsl(-82.3076923077, 92.125984252%, 50.1960784314%);text-align:center;}#mermaid-svg-tCddFpkGqfFjl0AT .edgeLabel p{background-color:hsl(-82.3076923077, 92.125984252%, 50.1960784314%);}#mermaid-svg-tCddFpkGqfFjl0AT .edgeLabel rect{opacity:0.5;background-color:hsl(-82.3076923077, 92.125984252%, 50.1960784314%);fill:hsl(-82.3076923077, 92.125984252%, 50.1960784314%);}#mermaid-svg-tCddFpkGqfFjl0AT .labelBkg{background-color:rgba(158, 11.0000000001, 245, 0.5);}#mermaid-svg-tCddFpkGqfFjl0AT .cluster rect{fill:hsl(217.6923076923, 92.125984252%, 55.1960784314%);stroke:hsl(217.6923076923, 52.125984252%, 45.1960784314%);stroke-width:1px;}#mermaid-svg-tCddFpkGqfFjl0AT .cluster text{fill:rgb(219.5039370078, 141.2381889763, 8.9960629921);}#mermaid-svg-tCddFpkGqfFjl0AT .cluster span{color:rgb(219.5039370078, 141.2381889763, 8.9960629921);}#mermaid-svg-tCddFpkGqfFjl0AT div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(217.6923076923, 92.125984252%, 55.1960784314%);border:1px solid hsl(217.6923076923, 52.125984252%, 45.1960784314%);border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-tCddFpkGqfFjl0AT .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#fff;}#mermaid-svg-tCddFpkGqfFjl0AT rect.text{fill:none;stroke-width:0;}#mermaid-svg-tCddFpkGqfFjl0AT .icon-shape,#mermaid-svg-tCddFpkGqfFjl0AT .image-shape{background-color:hsl(-82.3076923077, 92.125984252%, 50.1960784314%);text-align:center;}#mermaid-svg-tCddFpkGqfFjl0AT .icon-shape p,#mermaid-svg-tCddFpkGqfFjl0AT .image-shape p{background-color:hsl(-82.3076923077, 92.125984252%, 50.1960784314%);padding:2px;}#mermaid-svg-tCddFpkGqfFjl0AT .icon-shape .label rect,#mermaid-svg-tCddFpkGqfFjl0AT .image-shape .label rect{opacity:0.5;background-color:hsl(-82.3076923077, 92.125984252%, 50.1960784314%);fill:hsl(-82.3076923077, 92.125984252%, 50.1960784314%);}#mermaid-svg-tCddFpkGqfFjl0AT .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-tCddFpkGqfFjl0AT .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-tCddFpkGqfFjl0AT :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}#mermaid-svg-tCddFpkGqfFjl0AT .god>*{fill:#ef4444!important;stroke:#b91c1c!important;color:#fff!important;}#mermaid-svg-tCddFpkGqfFjl0AT .god span{fill:#ef4444!important;stroke:#b91c1c!important;color:#fff!important;}#mermaid-svg-tCddFpkGqfFjl0AT .god tspan{fill:#fff!important;}#mermaid-svg-tCddFpkGqfFjl0AT .main>*{fill:#f59e0b!important;stroke:#b45309!important;color:#fff!important;}#mermaid-svg-tCddFpkGqfFjl0AT .main span{fill:#f59e0b!important;stroke:#b45309!important;color:#fff!important;}#mermaid-svg-tCddFpkGqfFjl0AT .main tspan{fill:#fff!important;}#mermaid-svg-tCddFpkGqfFjl0AT .plugin>*{fill:#8b5cf6!important;stroke:#6d28d9!important;color:#fff!important;}#mermaid-svg-tCddFpkGqfFjl0AT .plugin span{fill:#8b5cf6!important;stroke:#6d28d9!important;color:#fff!important;}#mermaid-svg-tCddFpkGqfFjl0AT .plugin tspan{fill:#fff!important;}
gateway/ 消息网关
挂载
plugins/platforms/ (22 适配器)
telegram382KB
discord352KB
slack339KB
feishu/matrix/teams/line/irc/…
run.py285KB 门面
17 个 run_*.py生命周期阶段
session*.py会话管理
slash_commands_*.pygateway slash (mixin)
authz_mixin.py授权
platforms/base.py:1797BasePlatformAdapter ABC(4263 行)
12 内置适配器platforms/*.py
builtin_hooks/扩展点 (无内置)
2.6 模块⑤:扩展层 —— 插件 / 技能 / Provider / Cron
核心作用:实现「能力在边缘」——所有平台适配器、模型 provider、记忆后端、上下文引擎、图像生成、看板、定时调度均以插件/ABC/技能形式挂载,内核不感知具体实现。
关键 ABC 与编排器:
| ProviderProfile | providers/base.py:39 | 模型 provider 基类;插件目录 plugins/model-providers/<name>/(39 个) |
| MemoryProvider(ABC) | agent/memory_provider.py:58 | 记忆后端 ABC;agent/memory_manager.py 编排 |
| ContextEngine(ABC) | agent/context_engine.py:47 | 上下文引擎 ABC |
| ImageGenProvider | agent/image_gen_provider.py:30 | 图像生成 provider(继承 CatalogProviderBase) |
| PluginManager | hermes_cli/plugins.py:1117 | 插件运行时:PluginLoaderMixin(plugins_loader.py:81)+ PluginDispatchMixin(plugins_dispatch.py:150)+ PluginLedgerMixin(plugins_ledger.py:62) |
| LoadedPlugin / PluginContext | hermes_cli/plugins.py:205 / :220 | 已加载插件 / 插件上下文 |
| Cron scheduler | cron/scheduler.py(177KB) | 定时任务调度;cron 会话默认 skip_memory=True(agent/AGENTS.md:100-101) |
插件发现三处来源(providers/__init__.py:1-20):① 内置 plugins/<kind>/<name>/;② 用户 $HERMES_HOME/plugins/<kind>/<name>/;③ pip 安装的 hermes_agent.plugins entry point。用户插件同名覆盖内置(last-writer-wins)。
插件种类(plugins/ 18 目录):browser、context_engine、cron_providers、dashboard_auth、disk-cleanup、google_meet、hermes-achievements、image_gen、kanban、memory、model-providers、observability、platforms、security-guidance、spotify、teams_pipeline、video_gen、web。
2.6.1 模块⑤架构图
#mermaid-svg-WwaUmodRixUF8vfM{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#fff;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-WwaUmodRixUF8vfM .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-WwaUmodRixUF8vfM .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-WwaUmodRixUF8vfM .error-icon{fill:hsl(78.3116883117, 89.5348837209%, 71.2745098039%);}#mermaid-svg-WwaUmodRixUF8vfM .error-text{fill:rgb(47.6976744187, 7.6656976744, 138.8343023257);stroke:rgb(47.6976744187, 7.6656976744, 138.8343023257);}#mermaid-svg-WwaUmodRixUF8vfM .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-WwaUmodRixUF8vfM .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-WwaUmodRixUF8vfM .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-WwaUmodRixUF8vfM .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-WwaUmodRixUF8vfM .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-WwaUmodRixUF8vfM .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-WwaUmodRixUF8vfM .marker{fill:#6b7280;stroke:#6b7280;}#mermaid-svg-WwaUmodRixUF8vfM .marker.cross{stroke:#6b7280;}#mermaid-svg-WwaUmodRixUF8vfM svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-WwaUmodRixUF8vfM p{margin:0;}#mermaid-svg-WwaUmodRixUF8vfM .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#fff;}#mermaid-svg-WwaUmodRixUF8vfM .cluster-label text{fill:rgb(47.6976744187, 7.6656976744, 138.8343023257);}#mermaid-svg-WwaUmodRixUF8vfM .cluster-label span{color:rgb(47.6976744187, 7.6656976744, 138.8343023257);}#mermaid-svg-WwaUmodRixUF8vfM .cluster-label span p{background-color:transparent;}#mermaid-svg-WwaUmodRixUF8vfM .label text,#mermaid-svg-WwaUmodRixUF8vfM span{fill:#fff;color:#fff;}#mermaid-svg-WwaUmodRixUF8vfM .node rect,#mermaid-svg-WwaUmodRixUF8vfM .node circle,#mermaid-svg-WwaUmodRixUF8vfM .node ellipse,#mermaid-svg-WwaUmodRixUF8vfM .node polygon,#mermaid-svg-WwaUmodRixUF8vfM .node path{fill:#8b5cf6;stroke:hsl(258.3116883117, 49.5348837209%, 56.2745098039%);stroke-width:1px;}#mermaid-svg-WwaUmodRixUF8vfM .rough-node .label text,#mermaid-svg-WwaUmodRixUF8vfM .node .label text,#mermaid-svg-WwaUmodRixUF8vfM .image-shape .label,#mermaid-svg-WwaUmodRixUF8vfM .icon-shape .label{text-anchor:middle;}#mermaid-svg-WwaUmodRixUF8vfM .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-WwaUmodRixUF8vfM .rough-node .label,#mermaid-svg-WwaUmodRixUF8vfM .node .label,#mermaid-svg-WwaUmodRixUF8vfM .image-shape .label,#mermaid-svg-WwaUmodRixUF8vfM .icon-shape .label{text-align:center;}#mermaid-svg-WwaUmodRixUF8vfM .node.clickable{cursor:pointer;}#mermaid-svg-WwaUmodRixUF8vfM .root .anchor path{fill:#6b7280!important;stroke-width:0;stroke:#6b7280;}#mermaid-svg-WwaUmodRixUF8vfM .arrowheadPath{fill:#0b0b0b;}#mermaid-svg-WwaUmodRixUF8vfM .edgePath .path{stroke:#6b7280;stroke-width:2.0px;}#mermaid-svg-WwaUmodRixUF8vfM .flowchart-link{stroke:#6b7280;fill:none;}#mermaid-svg-WwaUmodRixUF8vfM .edgeLabel{background-color:hsl(138.3116883117, 89.5348837209%, 66.2745098039%);text-align:center;}#mermaid-svg-WwaUmodRixUF8vfM .edgeLabel p{background-color:hsl(138.3116883117, 89.5348837209%, 66.2745098039%);}#mermaid-svg-WwaUmodRixUF8vfM .edgeLabel rect{opacity:0.5;background-color:hsl(138.3116883117, 89.5348837209%, 66.2745098039%);fill:hsl(138.3116883117, 89.5348837209%, 66.2745098039%);}#mermaid-svg-WwaUmodRixUF8vfM .labelBkg{background-color:rgba(91.9999999999, 246, 139, 0.5);}#mermaid-svg-WwaUmodRixUF8vfM .cluster rect{fill:hsl(78.3116883117, 89.5348837209%, 71.2745098039%);stroke:hsl(78.3116883117, 49.5348837209%, 61.2745098039%);stroke-width:1px;}#mermaid-svg-WwaUmodRixUF8vfM .cluster text{fill:rgb(47.6976744187, 7.6656976744, 138.8343023257);}#mermaid-svg-WwaUmodRixUF8vfM .cluster span{color:rgb(47.6976744187, 7.6656976744, 138.8343023257);}#mermaid-svg-WwaUmodRixUF8vfM div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(78.3116883117, 89.5348837209%, 71.2745098039%);border:1px solid hsl(78.3116883117, 49.5348837209%, 61.2745098039%);border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-WwaUmodRixUF8vfM .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#fff;}#mermaid-svg-WwaUmodRixUF8vfM rect.text{fill:none;stroke-width:0;}#mermaid-svg-WwaUmodRixUF8vfM .icon-shape,#mermaid-svg-WwaUmodRixUF8vfM .image-shape{background-color:hsl(138.3116883117, 89.5348837209%, 66.2745098039%);text-align:center;}#mermaid-svg-WwaUmodRixUF8vfM .icon-shape p,#mermaid-svg-WwaUmodRixUF8vfM .image-shape p{background-color:hsl(138.3116883117, 89.5348837209%, 66.2745098039%);padding:2px;}#mermaid-svg-WwaUmodRixUF8vfM .icon-shape .label rect,#mermaid-svg-WwaUmodRixUF8vfM .image-shape .label rect{opacity:0.5;background-color:hsl(138.3116883117, 89.5348837209%, 66.2745098039%);fill:hsl(138.3116883117, 89.5348837209%, 66.2745098039%);}#mermaid-svg-WwaUmodRixUF8vfM .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-WwaUmodRixUF8vfM .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-WwaUmodRixUF8vfM :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}#mermaid-svg-WwaUmodRixUF8vfM .abc>*{fill:#4a90d9!important;stroke:#2c5f8a!important;color:#fff!important;}#mermaid-svg-WwaUmodRixUF8vfM .abc span{fill:#4a90d9!important;stroke:#2c5f8a!important;color:#fff!important;}#mermaid-svg-WwaUmodRixUF8vfM .abc tspan{fill:#fff!important;}#mermaid-svg-WwaUmodRixUF8vfM .rt>*{fill:#8b5cf6!important;stroke:#6d28d9!important;color:#fff!important;}#mermaid-svg-WwaUmodRixUF8vfM .rt span{fill:#8b5cf6!important;stroke:#6d28d9!important;color:#fff!important;}#mermaid-svg-WwaUmodRixUF8vfM .rt tspan{fill:#fff!important;}#mermaid-svg-WwaUmodRixUF8vfM .plugin>*{fill:#a78bfa!important;stroke:#7c3aed!important;color:#fff!important;}#mermaid-svg-WwaUmodRixUF8vfM .plugin span{fill:#a78bfa!important;stroke:#7c3aed!important;color:#fff!important;}#mermaid-svg-WwaUmodRixUF8vfM .plugin tspan{fill:#fff!important;}#mermaid-svg-WwaUmodRixUF8vfM .skill>*{fill:#f472b6!important;stroke:#db2777!important;color:#fff!important;}#mermaid-svg-WwaUmodRixUF8vfM .skill span{fill:#f472b6!important;stroke:#db2777!important;color:#fff!important;}#mermaid-svg-WwaUmodRixUF8vfM .skill tspan{fill:#fff!important;}#mermaid-svg-WwaUmodRixUF8vfM .cron>*{fill:#10b981!important;stroke:#047857!important;color:#fff!important;}#mermaid-svg-WwaUmodRixUF8vfM .cron span{fill:#10b981!important;stroke:#047857!important;color:#fff!important;}#mermaid-svg-WwaUmodRixUF8vfM .cron tspan{fill:#fff!important;}
cron/
plugins/ (18 目录)
插件运行时
内核扩展点 (ABC)
实现
实现
实现
实现
实现
加载
skills/ (13 目录)
creative / devops / emailnote-taking / productivityresearch / social-mediasoftware-development / web
ProviderProfileproviders/base.py:39
MemoryProvider(ABC)agent/memory_provider.py:58
ContextEngine(ABC)agent/context_engine.py:47
ImageGenProvideragent/image_gen_provider.py:30
BasePlatformAdaptergateway/platforms/base.py:1797
PluginManagerplugins.py:1117
PluginLoaderMixinplugins_loader.py:81
PluginDispatchMixinplugins_dispatch.py:150
PluginLedgerMixinplugins_ledger.py:62
model-providers/ (39)
platforms/ (22 适配器)
memory/
kanban/
image_gen/
context_engine/
browser/cron_providers/dashboard_auth/…
scheduler.py (177KB)
2.7 模块⑥:状态层 —— hermes_state(根级 15 文件)
核心作用:会话持久化——SQLite 存储会话消息、元数据、schema、搜索(FTS)、压缩、WAL、可移植性、修复、网关态、用量等。15 个扁平文件位于仓库根级,是 P2 包化目标。
关键文件:
| hermes_state.py | 84KB | SessionResumeTooLargeError:98、_compression_lock_holder_process_is_dead:119 | hermes_state.py:98 |
| hermes_state_sessions.py | 84KB | workspace_key:27、_where_sql:86 | hermes_state_sessions.py:27 |
| hermes_state_messages.py | 84KB | _parse_tool_calls:69、_scrub_surrogates:89 | hermes_state_messages.py:69 |
| hermes_state_schema.py | 76KB | SessionSchemaMixin:174、reconcile_state_schema:1257 | hermes_state_schema.py:174 |
| hermes_state_search.py | 72KB | 会话搜索 | — |
| hermes_state_repair.py | 68KB | 状态修复 | — |
| hermes_state_common.py | 60KB | 共享工具 | — |
| hermes_state_gateway.py | 40KB | 网关态 | — |
| hermes_state_compression.py | 40KB | 压缩落库 | — |
| hermes_state_dbfile.py | 36KB | DB 文件管理 | — |
| hermes_state_wal.py | 32KB | WAL | — |
| hermes_state_portability.py | 32KB | 导入/导出 | — |
| hermes_state_fts.py | 24KB | 全文搜索 | — |
| hermes_state_usage.py | 24KB | 用量 | — |
| hermes_state_maintenance.py | 28KB | 维护 | — |
三、模块调用关系与数据传递
3.1 跨模块依赖矩阵
#mermaid-svg-a6s3fON6gZx2JZS6{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#fff;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-a6s3fON6gZx2JZS6 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-a6s3fON6gZx2JZS6 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-a6s3fON6gZx2JZS6 .error-icon{fill:hsl(30.6293706294, 65.296803653%, 62.0588235294%);}#mermaid-svg-a6s3fON6gZx2JZS6 .error-text{fill:rgb(33.5753424657, 95.4246575342, 159.9246575343);stroke:rgb(33.5753424657, 95.4246575342, 159.9246575343);}#mermaid-svg-a6s3fON6gZx2JZS6 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-a6s3fON6gZx2JZS6 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-a6s3fON6gZx2JZS6 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-a6s3fON6gZx2JZS6 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-a6s3fON6gZx2JZS6 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-a6s3fON6gZx2JZS6 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-a6s3fON6gZx2JZS6 .marker{fill:#0b0b0b;stroke:#0b0b0b;}#mermaid-svg-a6s3fON6gZx2JZS6 .marker.cross{stroke:#0b0b0b;}#mermaid-svg-a6s3fON6gZx2JZS6 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-a6s3fON6gZx2JZS6 p{margin:0;}#mermaid-svg-a6s3fON6gZx2JZS6 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#fff;}#mermaid-svg-a6s3fON6gZx2JZS6 .cluster-label text{fill:rgb(33.5753424657, 95.4246575342, 159.9246575343);}#mermaid-svg-a6s3fON6gZx2JZS6 .cluster-label span{color:rgb(33.5753424657, 95.4246575342, 159.9246575343);}#mermaid-svg-a6s3fON6gZx2JZS6 .cluster-label span p{background-color:transparent;}#mermaid-svg-a6s3fON6gZx2JZS6 .label text,#mermaid-svg-a6s3fON6gZx2JZS6 span{fill:#fff;color:#fff;}#mermaid-svg-a6s3fON6gZx2JZS6 .node rect,#mermaid-svg-a6s3fON6gZx2JZS6 .node circle,#mermaid-svg-a6s3fON6gZx2JZS6 .node ellipse,#mermaid-svg-a6s3fON6gZx2JZS6 .node polygon,#mermaid-svg-a6s3fON6gZx2JZS6 .node path{fill:#4a90d9;stroke:hsl(210.6293706294, 25.296803653%, 47.0588235294%);stroke-width:1px;}#mermaid-svg-a6s3fON6gZx2JZS6 .rough-node .label text,#mermaid-svg-a6s3fON6gZx2JZS6 .node .label text,#mermaid-svg-a6s3fON6gZx2JZS6 .image-shape .label,#mermaid-svg-a6s3fON6gZx2JZS6 .icon-shape .label{text-anchor:middle;}#mermaid-svg-a6s3fON6gZx2JZS6 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-a6s3fON6gZx2JZS6 .rough-node .label,#mermaid-svg-a6s3fON6gZx2JZS6 .node .label,#mermaid-svg-a6s3fON6gZx2JZS6 .image-shape .label,#mermaid-svg-a6s3fON6gZx2JZS6 .icon-shape .label{text-align:center;}#mermaid-svg-a6s3fON6gZx2JZS6 .node.clickable{cursor:pointer;}#mermaid-svg-a6s3fON6gZx2JZS6 .root .anchor path{fill:#0b0b0b!important;stroke-width:0;stroke:#0b0b0b;}#mermaid-svg-a6s3fON6gZx2JZS6 .arrowheadPath{fill:#0b0b0b;}#mermaid-svg-a6s3fON6gZx2JZS6 .edgePath .path{stroke:#0b0b0b;stroke-width:2.0px;}#mermaid-svg-a6s3fON6gZx2JZS6 .flowchart-link{stroke:#0b0b0b;fill:none;}#mermaid-svg-a6s3fON6gZx2JZS6 .edgeLabel{background-color:hsl(90.6293706294, 65.296803653%, 57.0588235294%);text-align:center;}#mermaid-svg-a6s3fON6gZx2JZS6 .edgeLabel p{background-color:hsl(90.6293706294, 65.296803653%, 57.0588235294%);}#mermaid-svg-a6s3fON6gZx2JZS6 .edgeLabel rect{opacity:0.5;background-color:hsl(90.6293706294, 65.296803653%, 57.0588235294%);fill:hsl(90.6293706294, 65.296803653%, 57.0588235294%);}#mermaid-svg-a6s3fON6gZx2JZS6 .labelBkg{background-color:rgba(143.9999999999, 217, 73.9999999999, 0.5);}#mermaid-svg-a6s3fON6gZx2JZS6 .cluster rect{fill:hsl(30.6293706294, 65.296803653%, 62.0588235294%);stroke:hsl(30.6293706294, 25.296803653%, 52.0588235294%);stroke-width:1px;}#mermaid-svg-a6s3fON6gZx2JZS6 .cluster text{fill:rgb(33.5753424657, 95.4246575342, 159.9246575343);}#mermaid-svg-a6s3fON6gZx2JZS6 .cluster span{color:rgb(33.5753424657, 95.4246575342, 159.9246575343);}#mermaid-svg-a6s3fON6gZx2JZS6 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(30.6293706294, 65.296803653%, 62.0588235294%);border:1px solid hsl(30.6293706294, 25.296803653%, 52.0588235294%);border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-a6s3fON6gZx2JZS6 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#fff;}#mermaid-svg-a6s3fON6gZx2JZS6 rect.text{fill:none;stroke-width:0;}#mermaid-svg-a6s3fON6gZx2JZS6 .icon-shape,#mermaid-svg-a6s3fON6gZx2JZS6 .image-shape{background-color:hsl(90.6293706294, 65.296803653%, 57.0588235294%);text-align:center;}#mermaid-svg-a6s3fON6gZx2JZS6 .icon-shape p,#mermaid-svg-a6s3fON6gZx2JZS6 .image-shape p{background-color:hsl(90.6293706294, 65.296803653%, 57.0588235294%);padding:2px;}#mermaid-svg-a6s3fON6gZx2JZS6 .icon-shape .label rect,#mermaid-svg-a6s3fON6gZx2JZS6 .image-shape .label rect{opacity:0.5;background-color:hsl(90.6293706294, 65.296803653%, 57.0588235294%);fill:hsl(90.6293706294, 65.296803653%, 57.0588235294%);}#mermaid-svg-a6s3fON6gZx2JZS6 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-a6s3fON6gZx2JZS6 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-a6s3fON6gZx2JZS6 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}#mermaid-svg-a6s3fON6gZx2JZS6 .layer>*{fill:#4a90d9!important;stroke:#2c5f8a!important;color:#fff!important;}#mermaid-svg-a6s3fON6gZx2JZS6 .layer span{fill:#4a90d9!important;stroke:#2c5f8a!important;color:#fff!important;}#mermaid-svg-a6s3fON6gZx2JZS6 .layer tspan{fill:#fff!important;}#mermaid-svg-a6s3fON6gZx2JZS6 .risk>*{fill:#ef4444!important;stroke:#b91c1c!important;color:#fff!important;}#mermaid-svg-a6s3fON6gZx2JZS6 .risk span{fill:#ef4444!important;stroke:#b91c1c!important;color:#fff!important;}#mermaid-svg-a6s3fON6gZx2JZS6 .risk tspan{fill:#fff!important;}
378 处 / 21 文件
146 处 / 2 文件
载插件
构造 AIAgent16 文件
16 文件
18 文件 反向!
调工具
落库
载 provider/memory/ctx 插件
载平台适配器插件
0 文件 (干净!)
hermes_cli/ + cli.py(界面层)
gateway/(界面层)
agent/ + run_agent.py(内核)
tools/(工具)
plugins/ + providers/(扩展)
hermes_state*(状态)
精确导入计数(grep ^(from|import) X):
| hermes_cli/+cli.py → agent/ | 21 文件 / 378 处 | 界面层重度向下依赖内核 |
| hermes_cli/+cli.py → gateway/ | 2 文件 / 146 处 | 主要在 hermes_cli/gateway.py(41 处,它管理 gateway 进程) |
| gateway/ → agent/+run_agent | 16 文件 | gateway 构造并运行 AIAgent(gateway/run_turn.py:1146,2163、run_turn_runner.py:1017) |
| gateway/ → hermes_cli/ | 16 文件 | gateway 复用 CLI 设施 |
| agent/ → hermes_cli/ | 18 文件(反向!) | 内核反向依赖界面层设施(auth/config/nous_account 等),模块边界事实上被穿透 |
| agent/ → gateway/ | 0 文件 | 干净——内核不依赖网关 |
证据样本:agent/account_usage.py:12-13(from hermes_cli.auth import …)、agent/agent_init.py:40(from hermes_cli.config import cfg_get)。
3.2 主流程(端到端数据传递)
LLM Providerhermes_state (SQLite)tool handlerToolRegistry.dispatch:810model_toolshandle_function_call:85511 阶段 turn_*.py_run_conversation_turn:1423AIAgentrun_conversation:1573gateway run_turn.py:1146两道消息守卫gateway run_inbound平台适配器(CLI / Telegram / …)LLM Providerhermes_state (SQLite)tool handlerToolRegistry.dispatch:810model_toolshandle_function_call:85511 阶段 turn_*.py_run_conversation_turn:1423AIAgentrun_conversation:1573gateway run_turn.py:1146两道消息守卫gateway run_inbound平台适配器(CLI / Telegram / …)#mermaid-svg-JR0NwNhzavuv0Srp{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#fff;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-JR0NwNhzavuv0Srp .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-JR0NwNhzavuv0Srp .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-JR0NwNhzavuv0Srp .error-icon{fill:hsl(30.6293706294, 65.296803653%, 62.0588235294%);}#mermaid-svg-JR0NwNhzavuv0Srp .error-text{fill:rgb(33.5753424657, 95.4246575342, 159.9246575343);stroke:rgb(33.5753424657, 95.4246575342, 159.9246575343);}#mermaid-svg-JR0NwNhzavuv0Srp .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-JR0NwNhzavuv0Srp .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-JR0NwNhzavuv0Srp .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-JR0NwNhzavuv0Srp .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-JR0NwNhzavuv0Srp .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-JR0NwNhzavuv0Srp .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-JR0NwNhzavuv0Srp .marker{fill:#374151;stroke:#374151;}#mermaid-svg-JR0NwNhzavuv0Srp .marker.cross{stroke:#374151;}#mermaid-svg-JR0NwNhzavuv0Srp svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-JR0NwNhzavuv0Srp p{margin:0;}#mermaid-svg-JR0NwNhzavuv0Srp .actor{stroke:hsl(210.6293706294, 25.296803653%, 47.0588235294%);fill:#4a90d9;}#mermaid-svg-JR0NwNhzavuv0Srp text.actor>tspan{fill:#fff;stroke:none;}#mermaid-svg-JR0NwNhzavuv0Srp .actor-line{stroke:hsl(210.6293706294, 25.296803653%, 47.0588235294%);}#mermaid-svg-JR0NwNhzavuv0Srp .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-JR0NwNhzavuv0Srp .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#fff;}#mermaid-svg-JR0NwNhzavuv0Srp .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#fff;}#mermaid-svg-JR0NwNhzavuv0Srp #arrowhead path{fill:#fff;stroke:#fff;}#mermaid-svg-JR0NwNhzavuv0Srp .sequenceNumber{fill:#c8beae;}#mermaid-svg-JR0NwNhzavuv0Srp #sequencenumber{fill:#fff;}#mermaid-svg-JR0NwNhzavuv0Srp #crosshead path{fill:#fff;stroke:#fff;}#mermaid-svg-JR0NwNhzavuv0Srp .messageText{fill:#fff;stroke:none;}#mermaid-svg-JR0NwNhzavuv0Srp .labelBox{stroke:hsl(210.6293706294, 25.296803653%, 47.0588235294%);fill:#4a90d9;}#mermaid-svg-JR0NwNhzavuv0Srp .labelText,#mermaid-svg-JR0NwNhzavuv0Srp .labelText>tspan{fill:#fff;stroke:none;}#mermaid-svg-JR0NwNhzavuv0Srp .loopText,#mermaid-svg-JR0NwNhzavuv0Srp .loopText>tspan{fill:#fff;stroke:none;}#mermaid-svg-JR0NwNhzavuv0Srp .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(210.6293706294, 25.296803653%, 47.0588235294%);fill:hsl(210.6293706294, 25.296803653%, 47.0588235294%);}#mermaid-svg-JR0NwNhzavuv0Srp .note{stroke:hsl(52.6829268293, 60%, 73.9215686275%);fill:#fff5ad;}#mermaid-svg-JR0NwNhzavuv0Srp .noteText,#mermaid-svg-JR0NwNhzavuv0Srp .noteText>tspan{fill:#333;stroke:none;}#mermaid-svg-JR0NwNhzavuv0Srp .activation0{fill:hsl(90.6293706294, 65.296803653%, 57.0588235294%);stroke:hsl(90.6293706294, 65.296803653%, 47.0588235294%);}#mermaid-svg-JR0NwNhzavuv0Srp .activation1{fill:hsl(90.6293706294, 65.296803653%, 57.0588235294%);stroke:hsl(90.6293706294, 65.296803653%, 47.0588235294%);}#mermaid-svg-JR0NwNhzavuv0Srp .activation2{fill:hsl(90.6293706294, 65.296803653%, 57.0588235294%);stroke:hsl(90.6293706294, 65.296803653%, 47.0588235294%);}#mermaid-svg-JR0NwNhzavuv0Srp .actorPopupMenu{position:absolute;}#mermaid-svg-JR0NwNhzavuv0Srp .actorPopupMenuPanel{position:absolute;fill:#4a90d9;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-JR0NwNhzavuv0Srp .actor-man line{stroke:hsl(210.6293706294, 25.296803653%, 47.0588235294%);fill:#4a90d9;}#mermaid-svg-JR0NwNhzavuv0Srp .actor-man circle,#mermaid-svg-JR0NwNhzavuv0Srp line{stroke:hsl(210.6293706294, 25.296803653%, 47.0588235294%);fill:#4a90d9;stroke-width:2px;}#mermaid-svg-JR0NwNhzavuv0Srp :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}alt[内联工具 (todo/memory)][注册工具]alt[有 tool_calls][无 tool_calls (finish)]loop[11 阶段循环 (每轮迭代)]上下文压缩 (50% 阈值) 时ContextCompressor 剪旧 tool 结果+ 辅助模型摘要= 唯一缓存破坏点用户发消息1MessageEvent2守卫①(base 队列) + 守卫②(runner 拦截 /stop)3放行 (非控制命令)4AIAgent(…).run_conversation(user_message, history, …)5_run_conversation_turn6begin→prepare→assemble→preflight→announce7perform_api_call (system_prompt + history + tools_schema)8response (tool_calls? 或 final)9handle_function_call(name, args)10内联结果11dispatch12handler(name, args)13结果 string14归一化结果15tool result16追加 tool 消息 (SessionDB)17继续迭代18final_response19finalize_turn:156220落库 final + messages21result dict {final_response, messages}22result23流式/最终消息24回复25用户
3.3 关键数据结构传递
| 用户消息 | user_message (str / multimodal) | 界面 → run_conversation → _run_conversation_turn → _LoopState.user_message | conversation_loop.py:1423-1437 |
| API 消息 | OpenAI 格式 {"role":"system|user|assistant|tool", …} | _LoopState.messages → build_api_kwargs → client.chat.completions.create | agent/AGENTS.md:46-47、chat_completion_helpers.py:1362 |
| 工具 schema | [{"type":"function","function":{…}}] | model_tools.get_tool_definitions → 注入 api_kwargs["tools"] | model_tools.py:212 |
| 工具调用 | tool_calls: [{name, args}] | model response → tool_executor → handle_function_call → registry.dispatch → handler | model_tools.py:855、tools/registry.py:810 |
| 工具结果 | JSON string (或 multimodal) | handler → registry 归一化 → 回灌为 {"role":"tool",…} 消息 | tools/registry.py:792-808 |
| turn 结果 | dict {final_response, messages, …} | finalize_turn → 返回调用方 | turn_finalizer.py:435、conversation_loop.py:1562 |
| 会话历史 | SQLite 行 → List[Dict] | hermes_state_sessions → run_conversation(conversation_history=…) | hermes_state_sessions.py |
| 推理内容 | assistant_msg["reasoning"] | model response → 存于消息 | agent/AGENTS.md:47 |
四、入口与运行方式
4.1 三个入口脚本(pyproject.toml:391-394)
| hermes | hermes_cli.main:main(main.py:3351) | 主 CLI:子命令调度(chat/gateway/setup/login/doctor/plugins/…) |
| hermes-agent | run_agent:main(run_agent.py:1434) | 直接 agent 入口 |
| hermes-acp | acp_adapter.entry:main(acp_adapter/entry.py) | ACP(Agent Client Protocol)适配器,stdout 走 JSON-RPC |
交互式 REPL 另有 cli.py:main(cli.py:4439),由 hermes 的 chat 子命令或直接调用触发。
4.2 初始化流程
#mermaid-svg-ntqg7XG5BpONWtTx{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#fff;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ntqg7XG5BpONWtTx .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ntqg7XG5BpONWtTx .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ntqg7XG5BpONWtTx .error-icon{fill:hsl(30.6293706294, 65.296803653%, 62.0588235294%);}#mermaid-svg-ntqg7XG5BpONWtTx .error-text{fill:rgb(33.5753424657, 95.4246575342, 159.9246575343);stroke:rgb(33.5753424657, 95.4246575342, 159.9246575343);}#mermaid-svg-ntqg7XG5BpONWtTx .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ntqg7XG5BpONWtTx .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ntqg7XG5BpONWtTx .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ntqg7XG5BpONWtTx .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ntqg7XG5BpONWtTx .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ntqg7XG5BpONWtTx .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ntqg7XG5BpONWtTx .marker{fill:#0b0b0b;stroke:#0b0b0b;}#mermaid-svg-ntqg7XG5BpONWtTx .marker.cross{stroke:#0b0b0b;}#mermaid-svg-ntqg7XG5BpONWtTx svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ntqg7XG5BpONWtTx p{margin:0;}#mermaid-svg-ntqg7XG5BpONWtTx .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#fff;}#mermaid-svg-ntqg7XG5BpONWtTx .cluster-label text{fill:rgb(33.5753424657, 95.4246575342, 159.9246575343);}#mermaid-svg-ntqg7XG5BpONWtTx .cluster-label span{color:rgb(33.5753424657, 95.4246575342, 159.9246575343);}#mermaid-svg-ntqg7XG5BpONWtTx .cluster-label span p{background-color:transparent;}#mermaid-svg-ntqg7XG5BpONWtTx .label text,#mermaid-svg-ntqg7XG5BpONWtTx span{fill:#fff;color:#fff;}#mermaid-svg-ntqg7XG5BpONWtTx .node rect,#mermaid-svg-ntqg7XG5BpONWtTx .node circle,#mermaid-svg-ntqg7XG5BpONWtTx .node ellipse,#mermaid-svg-ntqg7XG5BpONWtTx .node polygon,#mermaid-svg-ntqg7XG5BpONWtTx .node path{fill:#4a90d9;stroke:hsl(210.6293706294, 25.296803653%, 47.0588235294%);stroke-width:1px;}#mermaid-svg-ntqg7XG5BpONWtTx .rough-node .label text,#mermaid-svg-ntqg7XG5BpONWtTx .node .label text,#mermaid-svg-ntqg7XG5BpONWtTx .image-shape .label,#mermaid-svg-ntqg7XG5BpONWtTx .icon-shape .label{text-anchor:middle;}#mermaid-svg-ntqg7XG5BpONWtTx .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ntqg7XG5BpONWtTx .rough-node .label,#mermaid-svg-ntqg7XG5BpONWtTx .node .label,#mermaid-svg-ntqg7XG5BpONWtTx .image-shape .label,#mermaid-svg-ntqg7XG5BpONWtTx .icon-shape .label{text-align:center;}#mermaid-svg-ntqg7XG5BpONWtTx .node.clickable{cursor:pointer;}#mermaid-svg-ntqg7XG5BpONWtTx .root .anchor path{fill:#0b0b0b!important;stroke-width:0;stroke:#0b0b0b;}#mermaid-svg-ntqg7XG5BpONWtTx .arrowheadPath{fill:#0b0b0b;}#mermaid-svg-ntqg7XG5BpONWtTx .edgePath .path{stroke:#0b0b0b;stroke-width:2.0px;}#mermaid-svg-ntqg7XG5BpONWtTx .flowchart-link{stroke:#0b0b0b;fill:none;}#mermaid-svg-ntqg7XG5BpONWtTx .edgeLabel{background-color:hsl(90.6293706294, 65.296803653%, 57.0588235294%);text-align:center;}#mermaid-svg-ntqg7XG5BpONWtTx .edgeLabel p{background-color:hsl(90.6293706294, 65.296803653%, 57.0588235294%);}#mermaid-svg-ntqg7XG5BpONWtTx .edgeLabel rect{opacity:0.5;background-color:hsl(90.6293706294, 65.296803653%, 57.0588235294%);fill:hsl(90.6293706294, 65.296803653%, 57.0588235294%);}#mermaid-svg-ntqg7XG5BpONWtTx .labelBkg{background-color:rgba(143.9999999999, 217, 73.9999999999, 0.5);}#mermaid-svg-ntqg7XG5BpONWtTx .cluster rect{fill:hsl(30.6293706294, 65.296803653%, 62.0588235294%);stroke:hsl(30.6293706294, 25.296803653%, 52.0588235294%);stroke-width:1px;}#mermaid-svg-ntqg7XG5BpONWtTx .cluster text{fill:rgb(33.5753424657, 95.4246575342, 159.9246575343);}#mermaid-svg-ntqg7XG5BpONWtTx .cluster span{color:rgb(33.5753424657, 95.4246575342, 159.9246575343);}#mermaid-svg-ntqg7XG5BpONWtTx div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(30.6293706294, 65.296803653%, 62.0588235294%);border:1px solid hsl(30.6293706294, 25.296803653%, 52.0588235294%);border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ntqg7XG5BpONWtTx .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#fff;}#mermaid-svg-ntqg7XG5BpONWtTx rect.text{fill:none;stroke-width:0;}#mermaid-svg-ntqg7XG5BpONWtTx .icon-shape,#mermaid-svg-ntqg7XG5BpONWtTx .image-shape{background-color:hsl(90.6293706294, 65.296803653%, 57.0588235294%);text-align:center;}#mermaid-svg-ntqg7XG5BpONWtTx .icon-shape p,#mermaid-svg-ntqg7XG5BpONWtTx .image-shape p{background-color:hsl(90.6293706294, 65.296803653%, 57.0588235294%);padding:2px;}#mermaid-svg-ntqg7XG5BpONWtTx .icon-shape .label rect,#mermaid-svg-ntqg7XG5BpONWtTx .image-shape .label rect{opacity:0.5;background-color:hsl(90.6293706294, 65.296803653%, 57.0588235294%);fill:hsl(90.6293706294, 65.296803653%, 57.0588235294%);}#mermaid-svg-ntqg7XG5BpONWtTx .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ntqg7XG5BpONWtTx .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ntqg7XG5BpONWtTx :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}#mermaid-svg-ntqg7XG5BpONWtTx .entry>*{fill:#4a90d9!important;stroke:#2c5f8a!important;color:#fff!important;}#mermaid-svg-ntqg7XG5BpONWtTx .entry span{fill:#4a90d9!important;stroke:#2c5f8a!important;color:#fff!important;}#mermaid-svg-ntqg7XG5BpONWtTx .entry tspan{fill:#fff!important;}#mermaid-svg-ntqg7XG5BpONWtTx .init>*{fill:#60a5fa!important;stroke:#2563eb!important;color:#fff!important;}#mermaid-svg-ntqg7XG5BpONWtTx .init span{fill:#60a5fa!important;stroke:#2563eb!important;color:#fff!important;}#mermaid-svg-ntqg7XG5BpONWtTx .init tspan{fill:#fff!important;}#mermaid-svg-ntqg7XG5BpONWtTx .decision>*{fill:#f59e0b!important;stroke:#b45309!important;color:#fff!important;}#mermaid-svg-ntqg7XG5BpONWtTx .decision span{fill:#f59e0b!important;stroke:#b45309!important;color:#fff!important;}#mermaid-svg-ntqg7XG5BpONWtTx .decision tspan{fill:#fff!important;}
hermes
hermes chat
gateway run
hermes-acp
构造 agent
进程启动
哪个入口?
main():3351进程设置 + 自愈 + 快速路径
cli.py:main:4439HermesCLI REPL
gateway/run.pyrun_startup.py
acp_adapter/entry:main
_build_cli_parser:3194argparse + set_defaults func=
args.func(args)
cmd_chat / cmd_gateway / _forward_command
AIAgent(…)run_agent.py:233转发到 init_agent
init_agentagent_init.py:2172~30 有序阶段
_install_safe_stdio:2221
_resolve_api_mode:2258_finalize_routing:2259
_build_client:2278_init_fallback_chain:2279
_load_tools:2280(触发 ToolRegistry 发现)
_init_session_state:2281加载 config:2288
_init_memory:2294_parse_compression_config:2296
_resolve_context_length:2297_build_context_engine:2300
_snapshot_primary_runtime:2307
agent 就绪
agent.run_conversation()→ turn 状态机
4.3 运行时拓扑
#mermaid-svg-sRzpfwoPVocPjQgk{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#fff;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-sRzpfwoPVocPjQgk .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-sRzpfwoPVocPjQgk .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-sRzpfwoPVocPjQgk .error-icon{fill:hsl(30.6293706294, 65.296803653%, 62.0588235294%);}#mermaid-svg-sRzpfwoPVocPjQgk .error-text{fill:rgb(33.5753424657, 95.4246575342, 159.9246575343);stroke:rgb(33.5753424657, 95.4246575342, 159.9246575343);}#mermaid-svg-sRzpfwoPVocPjQgk .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-sRzpfwoPVocPjQgk .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-sRzpfwoPVocPjQgk .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-sRzpfwoPVocPjQgk .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-sRzpfwoPVocPjQgk .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-sRzpfwoPVocPjQgk .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-sRzpfwoPVocPjQgk .marker{fill:#0b0b0b;stroke:#0b0b0b;}#mermaid-svg-sRzpfwoPVocPjQgk .marker.cross{stroke:#0b0b0b;}#mermaid-svg-sRzpfwoPVocPjQgk svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-sRzpfwoPVocPjQgk p{margin:0;}#mermaid-svg-sRzpfwoPVocPjQgk .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#fff;}#mermaid-svg-sRzpfwoPVocPjQgk .cluster-label text{fill:rgb(33.5753424657, 95.4246575342, 159.9246575343);}#mermaid-svg-sRzpfwoPVocPjQgk .cluster-label span{color:rgb(33.5753424657, 95.4246575342, 159.9246575343);}#mermaid-svg-sRzpfwoPVocPjQgk .cluster-label span p{background-color:transparent;}#mermaid-svg-sRzpfwoPVocPjQgk .label text,#mermaid-svg-sRzpfwoPVocPjQgk span{fill:#fff;color:#fff;}#mermaid-svg-sRzpfwoPVocPjQgk .node rect,#mermaid-svg-sRzpfwoPVocPjQgk .node circle,#mermaid-svg-sRzpfwoPVocPjQgk .node ellipse,#mermaid-svg-sRzpfwoPVocPjQgk .node polygon,#mermaid-svg-sRzpfwoPVocPjQgk .node path{fill:#4a90d9;stroke:hsl(210.6293706294, 25.296803653%, 47.0588235294%);stroke-width:1px;}#mermaid-svg-sRzpfwoPVocPjQgk .rough-node .label text,#mermaid-svg-sRzpfwoPVocPjQgk .node .label text,#mermaid-svg-sRzpfwoPVocPjQgk .image-shape .label,#mermaid-svg-sRzpfwoPVocPjQgk .icon-shape .label{text-anchor:middle;}#mermaid-svg-sRzpfwoPVocPjQgk .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-sRzpfwoPVocPjQgk .rough-node .label,#mermaid-svg-sRzpfwoPVocPjQgk .node .label,#mermaid-svg-sRzpfwoPVocPjQgk .image-shape .label,#mermaid-svg-sRzpfwoPVocPjQgk .icon-shape .label{text-align:center;}#mermaid-svg-sRzpfwoPVocPjQgk .node.clickable{cursor:pointer;}#mermaid-svg-sRzpfwoPVocPjQgk .root .anchor path{fill:#0b0b0b!important;stroke-width:0;stroke:#0b0b0b;}#mermaid-svg-sRzpfwoPVocPjQgk .arrowheadPath{fill:#0b0b0b;}#mermaid-svg-sRzpfwoPVocPjQgk .edgePath .path{stroke:#0b0b0b;stroke-width:2.0px;}#mermaid-svg-sRzpfwoPVocPjQgk .flowchart-link{stroke:#0b0b0b;fill:none;}#mermaid-svg-sRzpfwoPVocPjQgk .edgeLabel{background-color:hsl(90.6293706294, 65.296803653%, 57.0588235294%);text-align:center;}#mermaid-svg-sRzpfwoPVocPjQgk .edgeLabel p{background-color:hsl(90.6293706294, 65.296803653%, 57.0588235294%);}#mermaid-svg-sRzpfwoPVocPjQgk .edgeLabel rect{opacity:0.5;background-color:hsl(90.6293706294, 65.296803653%, 57.0588235294%);fill:hsl(90.6293706294, 65.296803653%, 57.0588235294%);}#mermaid-svg-sRzpfwoPVocPjQgk .labelBkg{background-color:rgba(143.9999999999, 217, 73.9999999999, 0.5);}#mermaid-svg-sRzpfwoPVocPjQgk .cluster rect{fill:hsl(30.6293706294, 65.296803653%, 62.0588235294%);stroke:hsl(30.6293706294, 25.296803653%, 52.0588235294%);stroke-width:1px;}#mermaid-svg-sRzpfwoPVocPjQgk .cluster text{fill:rgb(33.5753424657, 95.4246575342, 159.9246575343);}#mermaid-svg-sRzpfwoPVocPjQgk .cluster span{color:rgb(33.5753424657, 95.4246575342, 159.9246575343);}#mermaid-svg-sRzpfwoPVocPjQgk div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(30.6293706294, 65.296803653%, 62.0588235294%);border:1px solid hsl(30.6293706294, 25.296803653%, 52.0588235294%);border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-sRzpfwoPVocPjQgk .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#fff;}#mermaid-svg-sRzpfwoPVocPjQgk rect.text{fill:none;stroke-width:0;}#mermaid-svg-sRzpfwoPVocPjQgk .icon-shape,#mermaid-svg-sRzpfwoPVocPjQgk .image-shape{background-color:hsl(90.6293706294, 65.296803653%, 57.0588235294%);text-align:center;}#mermaid-svg-sRzpfwoPVocPjQgk .icon-shape p,#mermaid-svg-sRzpfwoPVocPjQgk .image-shape p{background-color:hsl(90.6293706294, 65.296803653%, 57.0588235294%);padding:2px;}#mermaid-svg-sRzpfwoPVocPjQgk .icon-shape .label rect,#mermaid-svg-sRzpfwoPVocPjQgk .image-shape .label rect{opacity:0.5;background-color:hsl(90.6293706294, 65.296803653%, 57.0588235294%);fill:hsl(90.6293706294, 65.296803653%, 57.0588235294%);}#mermaid-svg-sRzpfwoPVocPjQgk .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-sRzpfwoPVocPjQgk .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-sRzpfwoPVocPjQgk :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}#mermaid-svg-sRzpfwoPVocPjQgk .surface>*{fill:#f59e0b!important;stroke:#b45309!important;color:#fff!important;}#mermaid-svg-sRzpfwoPVocPjQgk .surface span{fill:#f59e0b!important;stroke:#b45309!important;color:#fff!important;}#mermaid-svg-sRzpfwoPVocPjQgk .surface tspan{fill:#fff!important;}#mermaid-svg-sRzpfwoPVocPjQgk .core>*{fill:#4a90d9!important;stroke:#2c5f8a!important;color:#fff!important;}#mermaid-svg-sRzpfwoPVocPjQgk .core span{fill:#4a90d9!important;stroke:#2c5f8a!important;color:#fff!important;}#mermaid-svg-sRzpfwoPVocPjQgk .core tspan{fill:#fff!important;}#mermaid-svg-sRzpfwoPVocPjQgk .note>*{fill:#10b981!important;stroke:#047857!important;color:#fff!important;}#mermaid-svg-sRzpfwoPVocPjQgk .note span{fill:#10b981!important;stroke:#047857!important;color:#fff!important;}#mermaid-svg-sRzpfwoPVocPjQgk .note tspan{fill:#fff!important;}
共享内核
CLI
ACP
定时任务
消息网关 (gateway run)
Electron 桌面端 (apps/desktop/)
spawn detached
Electron app
hermes serve (control plane)随 app 退出而死
gateway 进程SURVIVES 桌面退出detached spawn
~34 平台 bot(Telegram/Discord/Slack/…)
cron scheduler独立会话skip_memory=True
hermes-acpJSON-RPC on stdout
hermes chat REPL
hermes 子命令
AIAgent (per-session 实例)gateway 跨 turn 缓存 (run_agent_cache.py)
ToolRegistry 单例(进程级)
SQLite 会话库(HERMES_HOME)
gateway vs desktop 生命周期gateway SURVIVES 桌面退出不可「修」为随 app 死gateway/AGENTS.md:116-125
关键生命周期不变量:hermes serve(控制面,桌面派生子进程)随 app 退出而死;消息网关 gateway run 故意存活于桌面退出(detached spawn,start_new_session/DETACHED_PROCESS,before-quit 的 SIGTERM 到不了它),保证 bot 持续运行(gateway/AGENTS.md:116-125)。Gateway 在 gateway_state.json 盖 code_sha/code_version(status.py)以便 updater 校验整个 fleet。
4.4 配置加载与 HERMES_HOME
- HERMES_HOME(默认 ~/.hermes)是所有状态的锚点;代码须用 get_hermes_home(),禁止硬编码 ~/.hermes(5 个 bug 在 PR #3575,AGENTS.md:267-272)。
- Profile(多实例)是独立岛:_apply_profile_override()(main.py:508)在任何 import 前设 HERMES_HOME;profile 间无 live 继承,–clone 覆盖「从我的默认起步」(AGENTS.md:127-138)。
- .env 仅放密钥;行为设置入 config.yaml(AGENTS.md:79-82)。
五、架构风险 / 坏味道
| R1 | 上帝文件 hermes_cli/gateway.py(6390 行/288KB/282 函数/5 类),名实不符(进程管理而非网络网关) | hermes_cli/gateway.py:1-4、:5826、:82、:99、:107 | 难维护/难测试/高合并冲突 | 高 |
| R2 | 上帝文件 gateway/run.py(285KB 门面,承担过多协调) | gateway/run.py(facade,phases 在 run_*.py) | 门面过厚,单点改动风险 | 高 |
| R3 | 上帝文件 cli.py(4635 行/212KB REPL + 模块级 helper 混入) | cli.py:2530、:807-1313 | REPL 与关闭/ANSI/终端检测混杂 | 高 |
| R4 | 上帝文件 gateway/run_turn.py(221KB turn 编排) | gateway/run_turn.py:1146,2163、:3819 | turn 逻辑过度集中 | 高 |
| R4b | god-object GatewayRunner 组合 16 个 Mixin,隐式共享可变状态属性面庞大 | gateway/run.py:3332-3337、:3364-3384 | mixin 间隐式状态耦合,难追溯属性来源 | 高 |
| R4c | facade↔collaborator 近似循环:run_turn_runner.py 反向 import gateway.run | gateway/run_turn_runner.py:1013 | 分层破坏,导入脆弱 | 中 |
| R5 | god-class ContextCompressor(277KB,组合 3 Mixin:SummaryDispatch+MicroCompaction+ContextEngine) | agent/context_compressor.py:1678 | 单类混合 3 个关注点 | 高 |
| R6 | god-module auxiliary_client.py(379KB/7601 行,路由+取消+client 工厂+进度钩子混杂) | agent/auxiliary_client.py:4278、:204、:167 | 低内聚,难测 | 高 |
| R7 | god-module conversation_compression.py(216KB,编排+提交栅栏+超时执行器+fallback 链+状态模板) | agent/conversation_compression.py:3558、:366、:661 | 多职责同居 | 高 |
| R8 | god-adapter:telegram(382KB)/discord(352KB)/slack(339KB) 平台适配器单文件过大 | plugins/platforms/telegram/adapter.py、discord/adapter.py、slack/adapter.py | 每平台单文件巨石 | 中 |
| R9 | 循环依赖 cli.py ↔ mixin:mixin 普遍 from cli import _looks_like_slash_command 等 | cli_info_mixin.py:434、cli_tui_mixin.py:1348、cli_loops_mixin.py:525、cli_modal_mixin.py:122、cli_model_switch_mixin.py:787 | 导入顺序脆弱、难独立测试 | 高 |
| R10 | 模块边界穿透:agent/ → hermes_cli/ 反向 18 文件(内核依赖界面层 auth/config) | agent/account_usage.py:12-13、agent/agent_init.py:40 | 分层被破坏,内核非自洽 | 高 |
| R11 | AIAgent.__init__ ~60 参数(凭据/路由/回调/会话/预算/凭据池…)巨参列表 | run_agent.py:233-272 | 难用/难扩展/易误传 | 中 |
| R12 | 进程全局可变状态 _last_resolved_tool_names,子代理执行期间读到的值可能 stale | model_tools.py:248、tools/AGENTS.md:52-53 | 子代理工具桥看到父代理集 | 中 |
| R13 | check_fn TTL 缓存进程级,多 session 答案泄漏;multiplex/浏览器须显式绕过 | tools/registry.py:244-267、:300-340 | 需谨慎 bypass | 中 |
| R14 | 三套配置加载器并存,key 覆盖不一致风险 | hermes_cli/AGENTS.md:69-72 | CLI 见到 gateway 不见的 key | 中 |
| R15 | 根级 hermes_state_* 15 扁平文件未包化 | hermes_state.py 等(仓库根级) | 命名空间污染、难定位 | 中 |
| R16 | hermes_cli/config.py 3849 行仅 2 类,加载/校验/路径/容器/安装检测混杂 | hermes_cli/config.py:48、:1059、:463 | 函数式巨石 | 中 |
| R17 | gateway/platforms/base.py 4263 行,BasePlatformAdapter ABC 过厚 | gateway/platforms/base.py:1797 | 基类承载过多 | 中 |
| R18 | 256 文件扁平 tools/ 目录,仅 5 子包 | tools/ 目录列举 | 可导航性成本 | 低 |
| R19 | 动态 schema 跨工具引用注入,rewriter 顺序敏感 | model_tools.py:337-467 | 顺序错则模型幻觉不存在工具 | 中 |
| R20 | cron/scheduler.py 177KB god-file | cron/scheduler.py | 调度逻辑过度集中 | 中 |
六、技术债
| D1 | 临时插件兼容垫片未回迁:2026-09 拆分 PR #102117 的 COMPAT_MANIFEST.md(3869 行)/compat_manifest.json(12551 行)仍待回退移除(2026-09-14 到期) | COMPAT_MANIFEST.md:1-4、compat_manifest.json:1-5 | 外部插件依赖旧路径;到期未回退则双套路径并存 | 高 |
| D2 | 2026-09 拆分的 turn_*.py 已完成,但 agent 压缩三件套(auxiliary_client/context_compressor/conversation_compression)未跟进拆分 | agent/turn_*.py(29 文件已拆)vs agent/auxiliary_client.py:1(379KB 未拆) | turn 循环已治理,压缩子系统仍是 god-file | 高 |
| D3 | hermes_cli/gateway.py 进程管理未按后端拆分(已有 gateway_windows.py 先例可循) | hermes_cli/gateway.py:5826、hermes_cli/gateway_windows.py | 最大单文件债务 | 高 |
| D4 | cli.py↔mixin 循环回引未抽公共模块 | cli_info_mixin.py:434 等 5 处 | 导入脆弱,阻断独立测试 | 高 |
| D5 | AIAgent.__init__ ~60 参数未分组为配置对象 | run_agent.py:233-272 | 接口臃肿,易误用 | 中 |
| D6 | hermes_state_* 根级扁平未包化为 hermes_state/ 包 | 仓库根级 15 文件 | 命名空间污染 | 中 |
| D7 | 三套配置加载器无 CI 校验 key 集合差 | hermes_cli/AGENTS.md:69-72 | 配置漂移风险 | 中 |
| D8 | 平台适配器 god-adapter(telegram/discord/slack 300KB+)未按关注点拆 | plugins/platforms/telegram/adapter.py | 平台维护成本高 | 中 |
| D9 | ToolRegistry 单例 + 进程全局可变状态(_last_resolved_tool_names、check_fn 缓存)在多 profile/多 session 下的正确性依赖 scope-key 纪律 | tools/registry.py:373-385、model_tools.py:248 | multiplex 场景需谨慎 | 中 |
| D10 | 依赖钉版维护成本:精确 pin + exclude-newer=14 days + CVE 例外白名单需持续手工维护 | pyproject.toml:411-417 | 供应链防御的持续开销 | 低 |
| D11 | gateway/platforms/base.py ABC 过厚(4263 行),新适配器继承面过大 | gateway/platforms/base.py:1797 | 适配器作者认知负担 | 中 |
七、重构建议
指导原则(沿用 AGENTS.md:53-55):「Refactor god-files into clean modules」是被鼓励的工作;大型机械式 +N/-N 抽取 PR 是「wanted work」,每个抽出行都能追溯到抽取请求本身。重构顺序遵循 turn_*.py 已验证的「门面 + 兄弟阶段文件」模式。
7.1 P0(立即,解除最大风险)
| P0-1 | 拆 hermes_cli/gateway.py 按进程后端分模块 | R1/D3 | 抽出 gateway_proc_pid.py(PID 扫描)、gateway_proc_systemd.py、gateway_proc_launchd.py、gateway_proc_s6.py、gateway_proc_windows.py(已有先例)、gateway_restart.py、gateway_snapshot.py(GatewayRuntimeSnapshot:82);门面 gateway_command:5826 仅做派发 | hermes_cli/gateway.py:1-4、hermes_cli/gateway_windows.py |
| P0-2 | 断 cli.py↔mixin 循环回引 | R9/D4 | 抽 hermes_cli/_slash_util.py 收纳被回引的模块级符号(_looks_like_slash_command、_DIM、_cprint、_panel_box_width);mixin 改从该模块 import,禁止 from cli import … | cli_info_mixin.py:434、cli_tui_mixin.py:1348、cli_loops_mixin.py:525、cli_modal_mixin.py:122、cli_model_switch_mixin.py:787 |
| P0-3 | 回迁 COMPAT 垫片(按计划 2026-09-14 回退 PR #102117 的兼容 commit) | D1 | revert 添加 compat 的 commit;scripts/check_compat_pointers.py 已禁止树内使用,回退后外部插件须已迁移 | COMPAT_MANIFEST.md:1-4 |
7.2 P1(短期,建立边界与拆 god 文件)
| P1-1 | 拆 agent 压缩三件套 | R5/R6/R7/D2 | (a) auxiliary_client.py 按关注点拆:auxiliary_routing.py(_resolve_auto_route:4278)、auxiliary_cancellation.py(:204)、auxiliary_client_factory.py(:167)、auxiliary_progress.py(:305);(b) ContextCompressor god-class 沿 3 Mixin 轴拆为独立策略类;© conversation_compression.py 抽出 commit-fence/timeout/fallback 为协调模块 | agent/auxiliary_client.py:4278、agent/context_compressor.py:1678、agent/conversation_compression.py:3558 |
| P1-2 | 拆 cli.py 模块级 helper 下沉 | R3 | _arm_exit_watchdog:682、_run_cleanup:807、_detect_light_mode 等下沉到 hermes_cli/cli_lifecycle.py / cli_terminal_detect.py;HermesCLI:2530 仅留 REPL 骨架 | cli.py:2530、:682、:807-1313 |
| P1-3 | 补模块边界 CI:禁止 agent/ import hermes_cli/ | R10 | 加 scripts/ci/check_layering.py(仿 check_compat_pointers.py)拦截 agent/*.py 中的 from hermes_cli / import hermes_cli;将 auth/config 等被反向依赖的设施下沉到 hermes_core 中立包或反转依赖 | agent/account_usage.py:12-13、agent/agent_init.py:40 |
| P1-4 | 拆 gateway/run_turn.py | R4 | 按 turn 子阶段(agent 构造 :1146、hygiene turn、turn runner 派发 :2163)拆兄弟文件,门面仅协调 | gateway/run_turn.py:1146,2163 |
7.3 P2(中期,结构优化)
| P2-1 | hermes_state_* 包化 | R15/D6 | 15 根级文件迁入 hermes_state/ 包,加 __init__.py 重导出保持外部 import 兼容 | 根级 hermes_state*.py |
| P2-2 | AIAgent.__init__ 参数分组 | R11/D5 | 引入 AgentCredentials、AgentRouting、AgentCallbacks、AgentSessionContext、AgentBudget 等配置对象,__init__ 接收分组对象而非 60 散参 | run_agent.py:233-272 |
| P2-3 | 配置加载器 key 一致性 CI | R14/D7 | 加 CI 比对 load_cli_config / load_config / 原始 YAML 的 key 集合差,差异即失败 | hermes_cli/AGENTS.md:69-72 |
| P2-4 | 平台适配器 god-adapter 拆分 | R8/D8 | telegram/discord/slack 按消息收发/媒体/命令/格式化拆兄弟文件,沿用 signal_format.py/yuanbao_media.py 先例 | plugins/platforms/telegram/adapter.py、gateway/platforms/signal_format.py |
| P2-5 | check_fn 缓存 scope 化 | R13/D9 | 默认 per-session 而非进程级;multiplex/浏览器无需显式 bypass | tools/registry.py:244-267、:300-340 |
| P2-6 | gateway/platforms/base.py ABC 瘦身 | R17/D11 | 把非通用能力下沉为可选 Mixin,BasePlatformAdapter 仅留契约 | gateway/platforms/base.py:1797 |
| P2-7 | cron/scheduler.py 拆分 | R20 | 按调度/失败处理/交付/事件拆兄弟文件 | cron/scheduler.py |
7.4 重构路线图
#mermaid-svg-3ieQYFHNAR8EzhN6{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#fff;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-3ieQYFHNAR8EzhN6 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-3ieQYFHNAR8EzhN6 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-3ieQYFHNAR8EzhN6 .error-icon{fill:hsl(340.1183431953, 84.07960199%, 44.4117647059%);}#mermaid-svg-3ieQYFHNAR8EzhN6 .error-text{fill:rgb(46.5298507462, 236.9701492537, 173.8656716418);stroke:rgb(46.5298507462, 236.9701492537, 173.8656716418);}#mermaid-svg-3ieQYFHNAR8EzhN6 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-3ieQYFHNAR8EzhN6 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-3ieQYFHNAR8EzhN6 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-3ieQYFHNAR8EzhN6 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-3ieQYFHNAR8EzhN6 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-3ieQYFHNAR8EzhN6 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-3ieQYFHNAR8EzhN6 .marker{fill:#0b0b0b;stroke:#0b0b0b;}#mermaid-svg-3ieQYFHNAR8EzhN6 .marker.cross{stroke:#0b0b0b;}#mermaid-svg-3ieQYFHNAR8EzhN6 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-3ieQYFHNAR8EzhN6 p{margin:0;}#mermaid-svg-3ieQYFHNAR8EzhN6 .mermaid-main-font{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}#mermaid-svg-3ieQYFHNAR8EzhN6 .exclude-range{fill:#eeeeee;}#mermaid-svg-3ieQYFHNAR8EzhN6 .section{stroke:none;opacity:0.2;}#mermaid-svg-3ieQYFHNAR8EzhN6 .section0{fill:hsl(340.1183431953, 84.07960199%, 44.4117647059%);}#mermaid-svg-3ieQYFHNAR8EzhN6 .section2{fill:#10b981;}#mermaid-svg-3ieQYFHNAR8EzhN6 .section1,#mermaid-svg-3ieQYFHNAR8EzhN6 .section3{fill:white;opacity:0.2;}#mermaid-svg-3ieQYFHNAR8EzhN6 .sectionTitle0{fill:rgb(46.5298507462, 236.9701492537, 173.8656716418);}#mermaid-svg-3ieQYFHNAR8EzhN6 .sectionTitle1{fill:rgb(46.5298507462, 236.9701492537, 173.8656716418);}#mermaid-svg-3ieQYFHNAR8EzhN6 .sectionTitle2{fill:rgb(46.5298507462, 236.9701492537, 173.8656716418);}#mermaid-svg-3ieQYFHNAR8EzhN6 .sectionTitle3{fill:rgb(46.5298507462, 236.9701492537, 173.8656716418);}#mermaid-svg-3ieQYFHNAR8EzhN6 .sectionTitle{text-anchor:start;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}#mermaid-svg-3ieQYFHNAR8EzhN6 .grid .tick{stroke:lightgrey;opacity:0.8;shape-rendering:crispEdges;}#mermaid-svg-3ieQYFHNAR8EzhN6 .grid .tick text{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;fill:#fff;}#mermaid-svg-3ieQYFHNAR8EzhN6 .grid path{stroke-width:0;}#mermaid-svg-3ieQYFHNAR8EzhN6 .today{fill:none;stroke:red;stroke-width:2px;}#mermaid-svg-3ieQYFHNAR8EzhN6 .task{stroke-width:2;}#mermaid-svg-3ieQYFHNAR8EzhN6 .taskText{text-anchor:middle;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}#mermaid-svg-3ieQYFHNAR8EzhN6 .taskTextOutsideRight{fill:#fff;text-anchor:start;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}#mermaid-svg-3ieQYFHNAR8EzhN6 .taskTextOutsideLeft{fill:#fff;text-anchor:end;}#mermaid-svg-3ieQYFHNAR8EzhN6 .task.clickable{cursor:pointer;}#mermaid-svg-3ieQYFHNAR8EzhN6 .taskText.clickable{cursor:pointer;fill:#003163!important;font-weight:bold;}#mermaid-svg-3ieQYFHNAR8EzhN6 .taskTextOutsideLeft.clickable{cursor:pointer;fill:#003163!important;font-weight:bold;}#mermaid-svg-3ieQYFHNAR8EzhN6 .taskTextOutsideRight.clickable{cursor:pointer;fill:#003163!important;font-weight:bold;}#mermaid-svg-3ieQYFHNAR8EzhN6 .taskText0,#mermaid-svg-3ieQYFHNAR8EzhN6 .taskText1,#mermaid-svg-3ieQYFHNAR8EzhN6 .taskText2,#mermaid-svg-3ieQYFHNAR8EzhN6 .taskText3{fill:#fff;}#mermaid-svg-3ieQYFHNAR8EzhN6 .task0,#mermaid-svg-3ieQYFHNAR8EzhN6 .task1,#mermaid-svg-3ieQYFHNAR8EzhN6 .task2,#mermaid-svg-3ieQYFHNAR8EzhN6 .task3{fill:#10b981;stroke:hsl(160.1183431953, 44.07960199%, 29.4117647059%);}#mermaid-svg-3ieQYFHNAR8EzhN6 .taskTextOutside0,#mermaid-svg-3ieQYFHNAR8EzhN6 .taskTextOutside2{fill:#fff;}#mermaid-svg-3ieQYFHNAR8EzhN6 .taskTextOutside1,#mermaid-svg-3ieQYFHNAR8EzhN6 .taskTextOutside3{fill:#fff;}#mermaid-svg-3ieQYFHNAR8EzhN6 .active0,#mermaid-svg-3ieQYFHNAR8EzhN6 .active1,#mermaid-svg-3ieQYFHNAR8EzhN6 .active2,#mermaid-svg-3ieQYFHNAR8EzhN6 .active3{fill:hsl(160.1183431953, 84.07960199%, 62.4117647059%);stroke:#10b981;}#mermaid-svg-3ieQYFHNAR8EzhN6 .activeText0,#mermaid-svg-3ieQYFHNAR8EzhN6 .activeText1,#mermaid-svg-3ieQYFHNAR8EzhN6 .activeText2,#mermaid-svg-3ieQYFHNAR8EzhN6 .activeText3{fill:#fff!important;}#mermaid-svg-3ieQYFHNAR8EzhN6 .done0,#mermaid-svg-3ieQYFHNAR8EzhN6 .done1,#mermaid-svg-3ieQYFHNAR8EzhN6 .done2,#mermaid-svg-3ieQYFHNAR8EzhN6 .done3{stroke:grey;fill:lightgrey;stroke-width:2;}#mermaid-svg-3ieQYFHNAR8EzhN6 .doneText0,#mermaid-svg-3ieQYFHNAR8EzhN6 .doneText1,#mermaid-svg-3ieQYFHNAR8EzhN6 .doneText2,#mermaid-svg-3ieQYFHNAR8EzhN6 .doneText3{fill:#fff!important;}#mermaid-svg-3ieQYFHNAR8EzhN6 .doneText0.taskTextOutsideLeft,#mermaid-svg-3ieQYFHNAR8EzhN6 .doneText0.taskTextOutsideRight,#mermaid-svg-3ieQYFHNAR8EzhN6 .doneText1.taskTextOutsideLeft,#mermaid-svg-3ieQYFHNAR8EzhN6 .doneText1.taskTextOutsideRight,#mermaid-svg-3ieQYFHNAR8EzhN6 .doneText2.taskTextOutsideLeft,#mermaid-svg-3ieQYFHNAR8EzhN6 .doneText2.taskTextOutsideRight,#mermaid-svg-3ieQYFHNAR8EzhN6 .doneText3.taskTextOutsideLeft,#mermaid-svg-3ieQYFHNAR8EzhN6 .doneText3.taskTextOutsideRight{fill:#fff!important;}#mermaid-svg-3ieQYFHNAR8EzhN6 .crit0,#mermaid-svg-3ieQYFHNAR8EzhN6 .crit1,#mermaid-svg-3ieQYFHNAR8EzhN6 .crit2,#mermaid-svg-3ieQYFHNAR8EzhN6 .crit3{stroke:#ff8888;fill:red;stroke-width:2;}#mermaid-svg-3ieQYFHNAR8EzhN6 .activeCrit0,#mermaid-svg-3ieQYFHNAR8EzhN6 .activeCrit1,#mermaid-svg-3ieQYFHNAR8EzhN6 .activeCrit2,#mermaid-svg-3ieQYFHNAR8EzhN6 .activeCrit3{stroke:#ff8888;fill:hsl(160.1183431953, 84.07960199%, 62.4117647059%);stroke-width:2;}#mermaid-svg-3ieQYFHNAR8EzhN6 .doneCrit0,#mermaid-svg-3ieQYFHNAR8EzhN6 .doneCrit1,#mermaid-svg-3ieQYFHNAR8EzhN6 .doneCrit2,#mermaid-svg-3ieQYFHNAR8EzhN6 .doneCrit3{stroke:#ff8888;fill:lightgrey;stroke-width:2;cursor:pointer;shape-rendering:crispEdges;}#mermaid-svg-3ieQYFHNAR8EzhN6 .milestone{transform:rotate(45deg) scale(0.8,0.8);}#mermaid-svg-3ieQYFHNAR8EzhN6 .milestoneText{font-style:italic;}#mermaid-svg-3ieQYFHNAR8EzhN6 .doneCritText0,#mermaid-svg-3ieQYFHNAR8EzhN6 .doneCritText1,#mermaid-svg-3ieQYFHNAR8EzhN6 .doneCritText2,#mermaid-svg-3ieQYFHNAR8EzhN6 .doneCritText3{fill:#fff!important;}#mermaid-svg-3ieQYFHNAR8EzhN6 .doneCritText0.taskTextOutsideLeft,#mermaid-svg-3ieQYFHNAR8EzhN6 .doneCritText0.taskTextOutsideRight,#mermaid-svg-3ieQYFHNAR8EzhN6 .doneCritText1.taskTextOutsideLeft,#mermaid-svg-3ieQYFHNAR8EzhN6 .doneCritText1.taskTextOutsideRight,#mermaid-svg-3ieQYFHNAR8EzhN6 .doneCritText2.taskTextOutsideLeft,#mermaid-svg-3ieQYFHNAR8EzhN6 .doneCritText2.taskTextOutsideRight,#mermaid-svg-3ieQYFHNAR8EzhN6 .doneCritText3.taskTextOutsideLeft,#mermaid-svg-3ieQYFHNAR8EzhN6 .doneCritText3.taskTextOutsideRight{fill:#fff!important;}#mermaid-svg-3ieQYFHNAR8EzhN6 .vert{stroke:navy;}#mermaid-svg-3ieQYFHNAR8EzhN6 .vertText{font-size:15px;text-anchor:middle;fill:navy!important;}#mermaid-svg-3ieQYFHNAR8EzhN6 .activeCritText0,#mermaid-svg-3ieQYFHNAR8EzhN6 .activeCritText1,#mermaid-svg-3ieQYFHNAR8EzhN6 .activeCritText2,#mermaid-svg-3ieQYFHNAR8EzhN6 .activeCritText3{fill:#fff!important;}#mermaid-svg-3ieQYFHNAR8EzhN6 .titleText{text-anchor:middle;font-size:18px;fill:rgb(46.5298507462, 236.9701492537, 173.8656716418);font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}#mermaid-svg-3ieQYFHNAR8EzhN6 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}09-1309-2009-2710-0410-1110-18拆 hermes_cli/gateway.py 进程后端 断 cli.py↔mixin 循环回引 回迁 COMPAT 垫片 (revert #102117 compat) 拆 agent 压缩三件套 拆 cli.py 模块级 helper 模块边界 CI (禁 agent→hermes_cli) 拆 gateway/run_turn.py hermes_state_* 包化 AIAgent.__init__ 参数分组 配置加载器 key 一致性 CI 平台 god-adapter 拆分 check_fn 缓存 scope 化 BasePlatformAdapter ABC 瘦身 cron/scheduler.py 拆分 P0 立即P1 短期P2 中期Hermes-Agent 重构路线图
关键结论与下一步
架构成熟且纪律严格:窄腰(AIAgent + ToolRegistry 单例)+ 厚边缘(34 平台 / 39 模型 provider / 记忆/看板/图像/上下文引擎插件 + 13 技能包)的分层清晰,受两条强不变量(prompt 缓存神圣、内核窄腰)约束(AGENTS.md:18-29)。turn 状态机已成功拆为 29 个 turn_*.py 兄弟文件(agent/conversation_loop.py:1423),是 god-file 治理的成功范本。
调度设计优秀:CLI 子命令经 argparse func= + _forward_command 惰性转发(main.py:3351/1789);slash 经 COMMAND_REGISTRY + _SLASH_DISPATCH + 命名约定双路,无 elif 阶梯(AGENTS.md:14);插件三来源发现 + last-writer-wins 覆盖(providers/__init__.py:1-20)。
最大债务集中:(a) 4 个界面层 god 文件——hermes_cli/gateway.py(6390 行)、gateway/run.py(285KB)、cli.py(4635 行)、gateway/run_turn.py(221KB);(b) 3 个 agent 压缩/辅助 god 文件——auxiliary_client.py(379KB)、context_compressor.py(277KB)、conversation_compression.py(216KB)未跟进 turn_*.py 的拆分;© cli.py↔mixin 循环回引 + agent/→hermes_cli/ 反向 18 文件使模块边界事实上被穿透;(d) 平台 god-adapter(telegram 382KB 等)。
下一步:按 P0→P1→P2 路线图执行——P0 拆 hermes_cli/gateway.py + 断 cli↔mixin 循环 + 回迁 COMPAT 垫片;P1 拆 agent 压缩三件套 + 拆 cli.py + 加模块边界 CI(禁 agent→hermes_cli)+ 拆 run_turn.py;P2 包化 hermes_state + AIAgent 参数分组 + 配置加载器一致性 CI + 平台适配器拆分 + check_fn scope 化。所有拆分沿用已验证的「门面 + 兄弟阶段文件」模式,大型机械式抽取 PR 是项目明确鼓励的「wanted work」(AGENTS.md:53-55)。
报告生成方式:只读分析(git/源码只读),所有结论附「文件:行号」证据;模块细节经 4 个并行只读子代理深挖 + 主代理亲自核验。Mermaid 图表使用彩色主题。

