第10章:测试与工程化 —— 用组合测试托住复杂 Agent
一、测试规模告诉我们项目的真正难点
Hermes 快照包含约 2,625 个 Python 测试文件,覆盖 tests/agent、tests/gateway、tests/acp、tests/cli、tests/tools、tests/cron、tests/security 和大量 provider/platform 专项。测试分布本身就是架构地图:
provider × model × credential × tool × platform
× context × concurrency × operating system
Agent 的 bug 很少是“函数返回值错了”这么单纯,更多是“某个边界组合后,状态顺序错了”。
二、测试按边界而不是按文件组织
从文件名可以读出几类契约:
test_context_compressor_*:压缩边界、来源、游标、回退和抗抖;test_file_safety_*:路径、凭证、容器 mirror 和跨 profile 隔离;test_credential_pool_*:路由、轮换、OAuth 写回和 provider boundary;test_gateway_*:stream、turn lease、session sidecar、delivery;test_acp_*:auth、approval、session、provenance、MCP;test_memory_*:异步/同步、session switch、write bridge 和 provider;test_moa_*、test_auxiliary_*:辅助模型、并发和上下文预算。
最值得学习的是测试命名方式:它们把 bug 的业务概念写进文件名,让回归测试成为设计文档的一部分。
三、项目元数据也是安全设计
pyproject.toml 的依赖被分成 core 和 optional extras:provider-specific、搜索、图像、TTS、voice、wake、memory、messaging、MCP 等能力按需安装;默认依赖使用精确版本,lockfile 再固定传递依赖。
这不是纯粹的发布偏好:
- 核心安装更小,供应链暴露面更少;
- 不使用的 provider 不会在 import 阶段破坏启动;
- lazy dependency 能在能力真正启用时安装;
- 测试可以验证 extra 与 lazy dependency 的一致性。
四、启动和更新的自愈策略
hermes_cli/main.py 的早期 bootstrap/recovery、hermes_bootstrap.py、_early_recovery.py 和 update/install 逻辑都说明 Hermes 把“升级中断”当成正常故障模式处理。
当 git pull 或依赖安装只完成一半时,入口仍要尽量做到:
- 先用 stdlib-only 模块确认恢复标记;
- 修复 venv 或可恢复的缺失模块;
- 再导入重依赖;
- 将无法恢复的原因呈现给
doctor/用户。
这是长期运行工具很重要的原则:更新路径本身也要具备降级路径。
五、跨平台不是 if/else 清单
项目同时支持 Linux、macOS、Windows、WSL2、Termux,并且命令可能在 local/Docker/SSH/cloud sandbox 执行。跨平台逻辑被分布在:
hermes_cli/_subprocess_compat.py、windows_ssh_runtime.py;tools/environments/*;tools/path_security.py、file_safety.py;- Docker/Nix/Termux 配置;
gateway/platforms/*和声媒介适配器。
关键不是“每个函数都判断系统”,而是把平台差异压到 environment、process、filesystem、transport 和 adapter 契约。
六、观测:既看得到问题,又不泄露秘密
hermes_logging.py、agent/monitoring/、gateway/monitoring/、trace_upload.py、relay metrics 和 session insights 共同提供运行观测。可观测性需要回答:
- 现在卡在模型、工具、网络还是投递?
- 这个 turn 消耗了多少 token/时间/费用?
- 哪个 credential/profile/backend 失败?
- 是否发生压缩、重试、failover 或权限阻断?
但日志不能直接复制原始 prompt、API key、用户 memory 或 tool arguments。redact.py、message sanitization、provenance 和 bounded projection 是必要的安全边界。
七、给新增功能的工程检查表
新增一个能力时,至少问:
- 它属于哪一个生命周期阶段?
- 它拥有哪种状态,状态存在哪里?
- 它是否会产生外部副作用?副作用前是否有授权/审计/回滚?
- 它进入 prompt、transcript、日志还是 sidecar?
- 它如何在 CLI、Gateway、ACP 和 Cron 中复用或明确不复用?
- 失败时是 retry、fallback、degrade 还是 user-visible error?
- session 重启、进程终止、线程复用和跨平台会发生什么?
- 是否添加了对应边界命名的测试?
八、最终结论:Hermes 的工程主题
Hermes Agent 的复杂度不是偶然堆出来的;它来自一个明确目标:让 Agent 变成可以长期运行、跨入口、可回滚、可观察、可学习的系统。
十章合起来可以压缩成一句话:
用统一的 Agent Loop 产生行动,用工具/环境边界控制副作用,用 Context Engine 控制有限注意力,用 Session/Gateway 保证长期连续性,再用 Memory/Skills/Cron/Delegation 把一次性对话扩展成可复用工作系统。