Hermes Agent (NousResearch)

一句话定位

Hermes Agent 是 Nous Research(Hermes 模型系列 / Nomos / Psyche 的训练方)自建自用的 重型生产级 agent harness:“模型训练团队自己造 harness” 的代表案例——不是 demo,是 一个约 25,000 测试 / ~1,250 测试文件、单文件动辄数百 KB(cli.py 743KB)的大型工程化 代码库,同时承担”日常可用的 agent 产品”和”给下一代 tool-calling 模型生成/压缩训练轨迹的 数据管道”双重角色。

核心架构总览(目录结构关键路径 + 引用的 commit)

仓库地址 https://github.com/NousResearch/hermes-agent,MIT 协议(LICENSE: Copyright (c) 2025 Nous Research)。分析基于 git clone --depth 1 拉取的 main 分支 单一 commit 5431bf29214681fb2fd25254568b58c9da8ce6e0(commit message: “fix(desktop): default HERMES_DESKTOP_CWD to cwd when —cwd omitted”,commit 时间 2026-06-06 08:50:11 -0700,2026-07-07 clone)。注意:官方文档站 (hermes-agent.nousresearch.com/docs)当时已披露更新的公开版本 v0.18.0,说明 main 在 clone 之后又向前推进了;本页所有论断均以这个 SHA 为准,不代表绝对最新版。

顶层规模约 80 个条目,是”monolith-with-modules”风格的大工程:

hermes-agent/
├── agent/            — 116 files,核心 agent 内部逻辑(loop/memory/prompt/adapter/curator...)
├── tools/            — 92 files,一个工具一个文件 + 中央 registry
├── gateway/          — 43 files,消息平台接入(Discord/Feishu/HomeAssistant 等)
├── hermes_cli/       — 145 files,CLI 子命令
├── acp_adapter/       — IDE 集成(Agent Client Protocol)
├── cron/             — 定时任务调度
├── docs/             — 手写设计文档(含 observability、security、kanban 等)
├── website/          — 公开文档站(Docusaurus)源码
├── skills/、optional-skills/ — 内置 + 可选 skill 树
└── tests/            — 官方文档称 ~25,000 tests / ~1,250 files

Agent Loop(主循环 / 何时继续何时停)

核心实现:agent/conversation_loop.py::run_conversation()(起始于第 518 行,直接读源码确认)。

  • 循环条件(第 633 行): while (api_call_count < agent.max_iterations and agent.iteration_budget.remaining > 0) or agent._budget_grace_call:
  • 继续条件:assistant 响应带 tool_calls → 派发工具、以 {"role": "tool", ...} 追加结果、回到循环头部(官方 Agent Loop 文档 “Turn Lifecycle” 第 8-9 步印证)。
  • 停止条件:纯文本响应(finish_reason=stop 且无 tool_calls)→ 设置 _turn_exit_reason = "text_response(...)"break(约第 5213 行);中断请求 (第 638-643 行,interrupted=True);迭代预算耗尽(第 654-658 行, _turn_exit_reason = "budget_exhausted");此外通过 grep 发现十余种不同的 _turn_exit_reason 字符串(各类 guardrail / 空响应 / 恢复路径),诊断粒度较细。
  • 默认 iteration budget 为 90 次agent.max_turns);每个 subagent 拥有独立预算, 默认上限 50delegation.max_iterations)——因此父子 agent 的总迭代数可以超过父级 自身的上限。预算耗尽时 agent 优雅停止并返回已完成工作的摘要,而非硬崩溃。
  • 3 种 API 模式chat_completions / codex_responses / anthropic_messages)在调用 前后统一收敛为一种内部 OpenAI 风格消息格式;模式解析优先级:显式参数 > provider 特定 > base-URL 启发式 > 默认 chat_completions
  • 严格的消息角色交替规则(User→Assistant→User…,工具调用批次夹在 Assistant→Tool→Tool→…→Assistant 之间,不允许连续两个 assistant/user)。
  • 工具执行:单个工具调用内联执行;多个工具调用通过 ThreadPoolExecutor 并发执行(例外: 标记为 “interactive” 的工具如 clarify 强制串行);结果按原始调用顺序回填,不受完成顺序影响。
  • API 调用可中断:_interruptible_api_call() 在后台线程执行 HTTP 调用,主线程在 {响应就绪、中断事件、超时} 之间竞速;中断会放弃响应,不向历史注入任何部分结果。
  • 主模型失败(429/5xx/401/403)时按 fallback_providers 列表顺序回退;401/403 会先尝试 凭证刷新再判定失败。辅助任务(视觉、压缩、web 抽取)各自拥有独立的 auxiliary.* 回退链。
  • agent.api_mode == "codex_app_server" 是一种特殊旁路(第 624 行):整个 turn 交给 Codex app-server 子进程处理,此时默认 Hermes 循环完全跳过——与常规 codex_responses API 模式不同。

记忆与上下文管理(压缩、长期记忆、会话持久化)

  • 两文件有界记忆agent/memory_manager.py + 官方文档确认):MEMORY.md(agent 自己的 笔记,2,200 字符 / 约 800 token 上限)与 USER.md(用户画像,1,375 字符 / 约 500 token 上限),均存于 ~/.hermes/memories/
  • 冻结快照模式:记忆仅在会话开始时渲染进 system prompt 一次,会话中途不再变化(保持 LLM prefix cache 稳定);agent 的实时写入立即落盘,但要到下一个会话才会出现在 prompt 里——文档中明确点名的设计取舍。
  • 记忆工具动作:addreplace(基于 old_text 子串匹配)、remove(子串匹配)——无 read 动作,因为记忆内容始终已在上下文里。
  • 硬性容量约束:超出字符上限的写入会被拒绝,并返回结构化错误列出当前条目,强制 agent 在 同一 turn 内先整理/精简再重试——无静默自动压缩。
  • 重复条目拒绝(精确匹配去重),以及对记忆条目的安全扫描(防提示注入/凭证外泄模式、 隐藏 Unicode),因为记忆内容会在下次会话重新进入 system prompt。
  • 会话持久化:SQLite(hermes_state.py,275KB)+ FTS5 全文检索(~/.hermes/state.db)。 session_search 工具查询实际存储的消息(无 LLM 摘要/截断)——与 MEMORY.md/USER.md 系统 相互独立。压缩事件会创建带新 lineage ID 的”子”会话(父子链保留)。
  • 上下文压缩:通过 ContextEngine 抽象基类(agent/context_engine.py,231 行)插件化 ——should_compress()compress() 生命周期,可通过 context.engine 配置切换(默认 "compressor")。默认实现 ContextCompressoragent/context_compressor.py,3045 行) 对中间轮次做有损摘要。
  • 压缩触发阈值(按官方文档):API 调用前预检查若超过上下文窗口 50%;网关层在轮次之间 做更激进的自动压缩,阈值 85%。记忆先落盘(防数据丢失),再摘要中间轮次,末尾 N 条消息 完整保留(compression.protect_last_n,默认 20),工具调用/结果对不会被拆分。
  • 可选外部记忆 provider 走插件系统(agent/memory_provider.py ABC)——同一时间只允许一个 外部 provider 生效(拒绝第二次注册,避免工具 schema 膨胀/后端冲突)。发现 Honcho (github.com/plastic-labs/honcho)集成散布在 toolsets.pycli.pytools/lazy_deps.pytools/mcp_tool.pyhermes_cli/plugins.py 等多个文件中,文档 描述为 “dialectic user modeling”。

工具体系(定义/调用协议/注册/权限)

  • 中央自注册 registrytools/registry.py。每个工具文件在模块级调用 registry.register(...)discover_builtin_tools()(第 58 行)遍历 tools/*.py, 用 AST 解析检查是否存在模块体(非函数内部)的顶层 registry.register() 调用 (_module_registers_tools,第 43 行,刻意只匹配模块体语句以避免误发现),再 importlib.import_module() 逐个导入匹配模块。无需手工维护工具清单。
  • ToolEntry 对象字段:name、toolset、schema、handler、check_fn(可用性探测)、 requires_env、is_async、description、emoji、max_result_size_chars、 dynamic_schema_overrides(在 get_definitions() 时应用的可调用对象,使 schema 文本能 反映实时配置,例如 delegate_task 的描述会体现实际配置的 max_concurrent_children)。
  • check_fn 结果 TTL 缓存 30 秒,并带 60 秒的失败宽限机制——一次瞬时探测失败(例如一次 docker version 超时)在宽限窗口内沿用上一次已知良好的”可用”结论,避免工具状态抖动; 代码注释明确指出这是为修复 issue 5304(delegate_task 子 agent 因单次探测抖动 而丢失工具)。
  • 约 92 个工具文件,官方架构文档称”70+ 已注册工具,约 28 个 toolset”。
  • 按文件名可见的工具类别:文件系统、终端/执行、浏览器(5 后端)、web、消息平台 (Discord/Feishu/HomeAssistant)、媒体(图像/视频生成、TTS、转写)、 记忆/skill(memory_tool.pyskill_manager_tool.pyskills_hub.pyskills_sync.pyskills_ast_audit.pyskills_guard.py)、安全 (tirith_security.pythreat_patterns.pyurl_safety.pyosv_check.py)、 委派(delegate_tool.pyasync_delegation.py)、审批 (approval.pywrite_approval.pyslash_confirm.py)、MCP (mcp_tool.pymcp_oauth.py)、kanban/计划(kanban_tools.py)、cron (cronjob_tools.py)。
  • 4 个工具在 registry 之前被 agent 层拦截(官方 Agent Loop 文档):todomemorysession_searchdelegate_task——这些直接修改 agent 状态并返回合成工具结果,完全绕过 handle_function_call()/registry 派发。
  • 派发流程(官方文档 “Execution Flow”):从 registry 解析 handler → 触发 pre_tool_call 插件钩子 → 危险指令检查(tools/approval.py)→ 若危险,调用审批回调并阻塞等待用户响应 → 用参数+task_id 执行 handler → 触发 post_tool_call 钩子 → 追加 {"role": "tool", "content": result}

Prompt 设计(系统提示结构、动态组装)

  • agent/system_prompt.py(536 行,全文读完)+ agent/prompt_builder.py(1971 行) 完成组装。
  • 三层结构,以 \n\n 拼接(模块 docstring 明确记录):
    • stable —— 身份(SOUL.md 或 DEFAULT_AGENT_IDENTITY)、工具指引、computer-use 指引、 Nous 订阅信息块、工具使用强制规则 + 逐模型操作指引、skills prompt、Alibaba 模型名兼容 workaround、环境提示、平台提示。
    • context —— 调用方提供的 system_message + TERMINAL_CWD 下发现的上下文文件 (AGENTS.md / .cursorrules 等)。
    • volatile —— 记忆快照、USER.md 画像、外部记忆 provider 信息块、 时间戳/会话/模型/provider 行。
  • prompt 稳定性作为明确设计原则(官方架构文档 “Design Principles” 表):“System prompt doesn’t change mid-conversation. No cache-breaking mutations except explicit user actions (/model)“。每个会话只构建一次,跨轮次复用;只有上下文压缩会触发重建。这是为了保持上游 provider 的 prefix cache 热度(成本/延迟优化),docstring 中明确引用了内部开发文档 references/system-prompt-invariant.mdreferences/self-improvement-loop.md (这两份引用文档未包含在这次 shallow clone 里,未验证是否公开)。
  • 逐 provider 的工具 schema 消毒印证了 prompt/schema 与具体模型的协同设计: agent/gemini_schema.py::sanitize_gemini_schema()(全文读过)剥离 Gemini FunctionDeclaration.parameters 不接受的 OpenAPI/JSON-Schema 键(如 $schemaadditionalProperties 被剥离;约 24 个键的白名单,如 typeformatenumanyOfpropertyOrdering)。同名兄弟文件 agent/moonshot_schema.py(未打开细读)为 Moonshot/Kimi 做同类处理。
  • Anthropic 专属的 prompt caching:agent/prompt_caching.py 应用 cache_control 断点 (conversation_loop.py 约第 881 行注释:“inject cache_control breakpoints (system + last 3 messages)”)。

Router / 编排(任务分解、多 agent、子 agent)

  • 子 agent 委派tools/delegate_tool.py(3445 行)——delegate_task 工具,是 4 个 agent 级拦截工具之一。默认扁平:MAX_DEPTH = 1(第 125 行)——父级(depth 0)可生成子级 (depth 1);孙级除非在配置里调高 delegation.max_spawn_depth 否则被拒绝。设计上没有硬性 深度/并发上限——_get_max_concurrent_children()(第 354 行)和 _get_max_spawn_depth() (第 467 行)读配置且有合理默认值,但极端值只记警告日志而非硬性截断。
  • 子 agent 隔离:_register_subagent/_unregister_subagent/interrupt_subagent/ list_active_subagents(第 169-217 行)——每个被委派的子 agent 都有独立 subagent_id 追踪,可单独中断。
  • 异步/后台委派变体:tools/async_delegation.py(531 行)。此外官方文档 Key Features 提到 “Programmatic Tool Calling via execute_code collapses multi-step pipelines into single inference calls”——指通过 code_execution_tool.py 执行的 Python 脚本内以 RPC 方式调用工具,让 agent 无需每次都经 LLM 往返即可批量调用工具,是区别于 delegate_task 的 “零上下文成本”编排机制。
  • Mixture-of-Agents (MoA)agent/moa_loop.py(1073 行)——由 /moa 斜杠命令触发的 模式。明确不是 model tool;它把某一个用户轮次标记为 MoA-enabled,常规 Hermes agent loop 仍然掌控工具调用/轮次终止,而 moa_loop.py 在每次迭代前用 ThreadPoolExecutor (上限 _MAX_REFERENCE_WORKERS = 8)并发调用参考模型获取”顾问意见”并作为上下文喂入。各 参考模型有独立的成本/用量核算(_RefAccounting),不同 provider/模型的顾问按各自费率 计价,不并入聚合器的成本。
  • 可观测性文档确认专门的子 agent 生命周期钩子:subagent_start/subagent_stop,带 父子会话+轮次 ID 关联字段(parent_session_idchild_session_idparent_subagent_idchild_subagent_idchild_rolechild_goalchild_summaryduration_ms)。
  • 在已读文件范围内未发现独立于 LLM 之外的”规划器”/DAG 任务分解组件——任务分解看起来 完全由 LLM 通过工具调用(delegate_task)驱动,没有单独的编排层。hermes_cli/kanban_decompose.pytools/kanban_tools.py 命名上暗示可能存在 kanban 式任务分解,但本轮未读,留待后续验证。

Skill / 插件体系

  • Skill 被明确定义为”程序性记忆”tools/skill_manager_tool.py docstring):区别于 MEMORY.md/USER.md 的”宽泛、陈述性”记忆,skill 是”狭窄、可执行”的,记录如何完成某类具体任务。
  • 目录布局:~/.hermes/skills/<skill-name>/SKILL.md + 可选的 references/templates/scripts/assets/ 子目录;也可按 category-name/another-skill/ 分组。
  • skill_manage 工具动作:createedit(全量重写 SKILL.md)、patch(在 SKILL.md 或任意支持文件内做定向查找替换)、deletewrite_fileremove_file
  • 仓库自带两棵 skill 树:skills/(始终可用,含 apple、autonomous-ai-agents、 computer-use、creative、data-science、dogfood、email、github 等类别)与 optional-skills/(需显式安装,含 blockchain、communication、devops、finance、 gaming、health、mcp、migration、payments 等)。
  • 开放标准兼容:README/文档声明兼容 agentskills.io 开放 skill 标准,并有公开的 “Skills Hub” 供社区贡献(可移植/可分享)。
  • 独立的插件系统(官方架构文档):三种发现来源——~/.hermes/plugins/(用户级)、 .hermes/plugins/(项目级)、pip entry points。插件通过上下文 API (ctx.register_hook(...))注册工具/钩子/CLI 命令。两类”单选”插件槽位:记忆 provider (plugins/memory/)与上下文引擎(plugins/context_engine/),同一时间各自只能有一个生效。

自进化能力(自我改进 / 学习型记忆 / eval 驱动纠错)

  • The Curatoragent/curator.py,1976 行):官方称为”后台 skill 维护编排器”。 以辅助模型任务的形式运行,由空闲状态触发(无常驻 cron 守护进程)—— maybe_run_curator() 在 agent 空闲距上次 curator 运行超过 interval_hours (默认 DEFAULT_INTERVAL_HOURS = 24*7,即每周;DEFAULT_MIN_IDLE_HOURS = 2DEFAULT_STALE_AFTER_DAYS = 30DEFAULT_ARCHIVE_AFTER_DAYS = 90)时触发。
  • Curator 职责(自身 docstring):根据派生的活动时间戳自动转换 skill 生命周期状态;派生一个 后台 review agent(fork 出的 AIAgent),可以通过 skill_manage 对 agent 创建的 skill 做 pin/archive/consolidate/patch;curator 状态(last_run_atpaused 等)持久化在 .curator_state
  • 明确的强不变式(代码注释而非仅文档层面):只触碰 agent 创建的 skill(通过 tools/skill_usage.is_agent_created 检查);从不自动删除,只归档(可恢复);已 pin 的 skill 跳过所有自动转换;使用独立的”辅助 client”,确保 review fork 不会碰到主会话的 prompt cache。
  • 学习图谱agent/learning_graph.py,328 行):为桌面 UI 构建”可见化学习”图——节点是 已学习/画像类 skill(非内置)加上 MEMORY.md/USER.md 记忆片段;skill-skill 边来自声明的 related_skills;memory-skill 边由词法重叠推导得出。这是对同一套自我改进数据的 可视化/内省层,不是独立的学习机制。
  • “自我改进 agent”的说法有具体落地:(a) skill 在正常工具使用过程中从经验中创建 (skill_manager_tool);(b) curator 周期性地用自己的辅助 LLM 调用来 review/整合/归档这批 skill;(c) 记忆条目跨会话持久化事实;(d) session_search + FTS5 提供跨会话回忆,不依赖有界 的 MEMORY.md。
  • 未发现 harness 内的 RL/eval 驱动权重更新——这是程序性/陈述性记忆的自我策展,不是 harness 内的模型微调。模型层面的改进(如果有)通过轨迹导出管道(见”轨迹利用”一节)流向 Nous Research 独立的训练栈(Atropos,文档中提到的独立 RL 环境项目,不属于本仓库)。

可观测性(日志 / trace 格式)

  • 一等公民、带版本号的 observer-hook 契约docs/observability/README.md 全文读完 ——明确设计为面向 Langfuse、OpenTelemetry 风格采集器、“NeMo Relay” 的稳定、后端中立集成面。
  • Payload 版本标签:telemetry_schema_version = "hermes.observer.v1",由插件管理器注入 每个钩子 payload。
  • 钩子 fail-open:回调中的异常会被捕获、记为 warning 日志,agent loop 继续运行——钩子 不能使 agent 崩溃。
  • 关联 ID 表(文档化、稳定):session_idtask_idturn_idapi_request_id (不透明、按 provider 尝试次数区分的 ID,文档明确警告”不要解析其字符串格式”)、 api_call_counttool_call_idparent_session_id/child_session_idparent_subagent_id/child_subagent_idparent_turn_id
  • 已文档化的事件族:会话生命周期(on_session_start/on_session_end/ on_session_finalize/on_session_reset,文档特别注明 on_session_end 是 turn-scoped,不一定是会话的最终生命周期边界);轮次级 LLM 钩子 (pre_llm_call/post_llm_call);请求级 API 钩子 (pre_api_request/post_api_request/api_request_error,各带详细字段,含脱敏后的 请求/响应 payload、耗时、token 用量、finish_reason);工具生命周期 (pre_tool_call/post_tool_call/transform_tool_result,带 status 枚举 ok/error/blocked/cancelled);审批生命周期 (pre_approval_request/post_approval_response,仅观察,不能从这些钩子预先应答/否决); 子 agent 生命周期(subagent_start/subagent_stop)。
  • 少数钩子被明确列为”影响行为”的例外(不属于纯观察契约):pre_llm_call (可返回字符串/字典注入临时上下文)、pre_tool_call(可返回 {"action": "block", "message": ...} 阻止工具执行)、transform_tool_result (可替换工具结果字符串)、transform_llm_output(可替换最终 assistant 文本)。这是刻意 设计的窄口径逃生舱,其余全部是纯遥测。
  • 另有 hermes_logging.py(31KB)作为进程级日志实现,本轮未深读,留待后续核实具体日志 格式/文件轮转细节。

安全与权限(审批门、密钥管理)

  • 7 层安全模型(官方 /docs/user-guide/security,全文读完):
    1. 用户授权(消息平台的 allowlist、DM 配对)
    2. 危险指令审批(human-in-the-loop)
    3. 容器隔离(Docker/Singularity/Modal 加固配置)
    4. MCP 凭证过滤(MCP 子进程的环境变量隔离)
    5. 上下文文件扫描(对项目文件如 AGENTS.md/.cursorrules 的提示注入检测)
    6. 跨会话隔离(会话之间不能互相触碰数据;cron 任务存储路径加固防路径穿越)
    7. 输入消毒(终端工具的工作目录参数按白名单校验,防 shell 注入)
  • 危险指令检测tools/approval.py::DANGEROUS_PATTERNS(第 546 行起,全文读过)——一份 精心维护的大型正则列表,覆盖 rm -rf / 各变体、Windows cmd/powershell 破坏性删除 (刻意锚定避免对含”del”的良性路径误判)、chmod 777/全局可写、chown -R rootmkfsdd if=、写入 /dev/sd*、不带 WHERE/TRUNCATE 的 SQL DROP/DELETE FROMsystemctl stop/restart/disable/maskkill -9 -1pkill -9killall 的 SIGKILL 变体、fork bomb、-c 方式的 shell 调用、curl|wget 管道到 shell、命令替换式 远程内容执行(eval $(curl ...))、base64/base32/base16 解码管道到 shell(对混淆有 感知)、xxd -r 反向 hex 转 shell。
  • 审批模式approvals.mode 配置项):manual(默认,始终提示)、smart (辅助 LLM 风险评估——低风险自动批准,高风险自动拒绝,不确定则升级为人工提示)、off (等价于 --yolo,完全不提示)。
  • YOLO 模式:3 条激活路径(--yolo CLI 参数、/yolo 斜杠切换命令、 HERMES_YOLO_MODE=1 环境变量)。在模块导入时即冻结,专门防止运行中的 skill 通过中途设置 环境变量来悄悄提权(tools/approval.py 第 29-32 行左右有明确安全注释)。激活期间有持续 视觉提醒(红色横幅 + 状态栏标记)。
  • 硬线阻止列表UNRECOVERABLE_BLOCKLIST,文档中提及,与 DANGEROUS_PATTERNS 不是 同一份列表):一份小而固定、代码内置的灾难性指令集合(含 --no-preserve-rootrm -rf /、fork bomb、对已挂载根分区执行 mkfsdd 清零物理磁盘、把不可信 URL 管道到 rootfs 顶层的 sh),任何机制都无法绕过——不是 --yolo,不是 approvals.mode: off,不是 cron 的 approve 模式,也不是用户点击”always allow”。这是 比 YOLO 模式更底层的一道地板,在审批层看到指令之前就已触发。
  • 用户可编辑的拒绝规则approvals.deny):fnmatch glob 模式,大小写不敏感,匹配 归一化/去混淆后的指令变体(因此 git pu""sh --force 这类引号花招无法绕过),在 --yolo//yolo/approvals.mode: off 之前检查。文档中明确描述为 “yolo-with-exceptions”。容器隔离的后端完全跳过这套防护栈,因为其中运行的任何东西都无法 触达宿主机。
  • 代码本身(非仅文档)中的并发安全处理相当成熟:_hermes_interactive_ctx 使用 contextvars.ContextVar 而非全局环境变量,专门规避一个代码注释中直接引用 CVE 编号 (GHSA-96vc-wcxf-jjff)的竞态问题——共享 ThreadPoolExecutor 上的并发 ACP 会话可能出现 一个会话的 finally 块环境变量还原覆盖另一个会话的设置,导致危险指令悄悄落入非交互式 自动批准路径。
  • 审批钩子在遥测契约中仅供观察(见”可观测性”一节)——插件无法用它们预先应答或绕过审批 提示;只有 pre_tool_call 能在指令到达审批层之前将其拦下。

沙箱与执行隔离

  • 6 种终端执行后端,代码(tools/environments/base.pylocal.pydocker.pyssh.pysingularity.pymodal.pymanaged_modal.pymodal_utils.pyfile_sync.py)与官方文档/README 均确认:local、Docker、SSH、 Singularity、Modal、Daytona。
  • tools/terminal_tool.py 模块 docstring 明确将其框定为一个权衡谱系:local = 最快但无 隔离(直接宿主机执行);Docker = 隔离但需要 Docker daemon;Modal = 云沙箱(直连或经 “managed gateway”模式)。README 中特别指出 Daytona 和 Modal 提供无服务器持久化——环境 空闲时休眠,“会话之间几乎零成本”。
  • 云沙箱注意事项在工具 docstring 中直接写明:持久化文件系统能跨沙箱重建保留工作状态, 但不保证同一个存活的沙箱/长期运行进程能挺过清理、空闲回收或 Hermes 退出——即文件系统 持久化不等于进程持久化。
  • Docker 的网络出站隔离是单独文档化的加固层(docs/security/network-egress-isolation.md, 全文读过):默认 network_mode: host 给予无限制出站访问;文档提供一份 docker-compose.override.yml 模式,把一个无默认路由/无外网的 internal Docker 网络 (agent/dashboard/gateway 都在这里)与一个可访问外网的 egress 网络(只有 egress-proxy 如 squid/envoy 驻留、带 allowlist)分离,gateway 服务双宿以接收入站平台消息,同时让 agent 核心不直接暴露在公网。明确框定为防御提示注入驱动的数据外泄 (工具生成的 shell 命令通过 curl/wget/原始 HTTP)——是终端后端边界之外的第二层。
  • 架构文档系统图中还提到 5 种浏览器自动化后端(“Browser (5 backends)”),本轮未逐一点名; tools/browser_camofox.pytools/browser_cdp_tool.py 暗示至少包含一个基于 Camoufox 的隐身后端和一个原生 CDP 后端,留待后续验证。

与模型的协同设计

  • 3 种内部 API 模式(chat_completionscodex_responsesanthropic_messages)各自 有专门的适配/转换代码,说明 harness 是为适应真实的逐 provider API 形态差异而构建,而非 强行统一为一种线格式:agent/anthropic_adapter.py(Anthropic Messages API)、 agent/codex_responses_adapter.py(OpenAI Responses API)、 agent/bedrock_adapter.pyagent/vertex_adapter.pyagent/azure_identity_adapter.pyagent/gemini_native_adapter.py (文件名/存在性通过 ls 确认,未逐一深读)。
  • 逐 provider 工具 schema 消毒:全文读过 agent/gemini_schema.py,确认 Gemini 的 FunctionDeclaration.parameters 只接受 OpenAPI/JSON-Schema 的一个严格子集 (硬编码约 24 个键的白名单),因此 Hermes 的 OpenAI 风格工具 schema 在发给 Google 之前 会被剥离/重写。同名兄弟文件 agent/moonshot_schema.py 为 Moonshot/Kimi 的 schema 特性 做同类处理(未打开细读)。
  • agent/model_metadata.py(上下文长度、token 估算)和 agent/models_dev.py (与社区 models.dev 注册表的集成)构成一个 provider/模型能力数据库——未深读,但存在与 命名本身证实 harness 追踪逐模型的上下文窗口大小及(隐含的)能力标记,用于驱动压缩阈值和 提示注入决策(例如 prompt_builder.py 导出的 GOOGLE_MODEL_OPERATIONAL_GUIDANCEOPENAI_MODEL_EXECUTION_GUIDANCETOOL_USE_ENFORCEMENT_MODELS 等逐模型操作指引块)。
  • 推理/思考内容作为一等公民的独立字段处理:官方 Agent Loop 文档指出扩展思考模型的推理内容 存于 assistant_msg["reasoning"](不与 content 混在一起),通过专门的 reasoning_callback 暴露。
  • 分散在多个小文件中的 provider 特定处理:agent/lmstudio_reasoning.py (LM Studio 推理格式特性)、agent/portal_tags.py(Nous Portal 特定标签)、 agent/nous_rate_guard.py(Nous 特定限流)——证实该 harness 由同一个运营模型服务门户 (Nous Portal)的团队构建,围绕其具体行为做协同设计,与 README 中”由模型训练团队打造” 的说法一致(Nous Research 同时训练 Hermes 模型系列、Nomos、Psyche)。
  • 容错/可靠性逻辑(agent/retry_utils.pyagent/rate_limit_tracker.pyagent/credential_pool.pyagent/credential_sources.py)将 provider 不稳定性作为 一等公民问题处理,有逐 (provider, 凭证池条目) 的重试计数逻辑(conversation_loop.py 第 612-617 行直接可见:agent._auth_pool_refresh_counts 每轮重置,内联注释引用了 GitHub issue #26080,修复一个持续 401 导致无限刷新凭证的 bug)。

轨迹利用(session/trajectory 是否反哺训练/评测)

  • 是——明确的一等公民训练数据管道,这是证据最充分的维度之一。
  • agent/trajectory.py(56 行,全文读完):save_trajectory()ShareGPT 格式{"conversations": [...], "timestamp": ..., "model": ..., "completed": bool}) 将对话追加到 trajectory_samples.jsonl(成功)或 failed_trajectories.jsonl (失败)——成功和失败两类结果都被捕获,不只是成功案例。还有 convert_scratchpad_to_think()——把 <REASONING_SCRATCHPAD> 标签归一化为 <think> 标签,即主动将推理轨迹重新格式化为特定的训练友好标签约定。
  • batch_runner.py(57KB,读过模块 docstring):专门的并行批处理运行器,用于”跨数据集 多个 prompt 运行 agent”,带 checkpoint/续跑能力,以及”以正确格式保存轨迹(from/value 对)“和”跨所有批次聚合工具使用统计”。这是一个专门构建的规模化合成轨迹生成系统,不是附带 日志。
  • trajectory_compressor.py(69KB,模块 docstring 全文读过):专门针对训练数据的后处理 压缩管道——保护首轮(system/human/first-gpt/first-tool)和末尾 N 轮原样不变,只压缩中间 区域至目标 token 预算,用单条摘要消息替换,同时保持 agent 后续工具调用完整(“model continues working after summary”)。带有采样数据集百分比(--sample_percent)和设置 目标 token 预算的 CLI 参数。这强烈暗示这些轨迹直接进入 SFT/工具调用模型的训练数据准备流程 ——不仅仅是留存审计。
  • 官方文档(README/首页)声明:“Research-ready — Batch trajectory generation, trajectory compression for training the next generation of tool-calling models”, 以及单独的 “RL training with Atropos”——Atropos 被指名为一个独立的 Nous Research RL 环境仓库(不属于 hermes-agent),大概率消费这里导出的轨迹数据; hermes-agent 自身的职责是生成/压缩,而非 RL 循环本身。
  • 存在专门的官方文档页 /docs/developer-guide/trajectory-format(描述”来自 agent 会话 的 ShareGPT 格式轨迹,用于训练数据生成”,按架构文档的 Major Subsystems 列表), 本轮未抓取/深读——留待后续验证的高价值项。
  • 与临时轨迹导出不同:session_search/SQLite+FTS5(“记忆”一节)是面向存活 agent 的 运行时回忆机制,不属于这条训练轨迹管道,两个系统在 dossier 中不应混为一谈。

与同类 harness 的关键差异(1-3 条)

以下先给出基于本次调研可支撑的初步观察,完整的跨 harness 系统对比留待 synthesis 阶段:

  1. “模型训练团队自建 harness”这一身份贯穿多处代码细节——Nous Portal 专属限流/标签代码 (nous_rate_guard.pyportal_tags.py)、面向”下一代 tool-calling 模型”训练的显式 ShareGPT 轨迹导出+压缩管道(trajectory.py/batch_runner.py/ trajectory_compressor.py),以及与独立 RL 项目 Atropos 的明确分工,构成了一条从 “agent 产品使用”到”模型训练数据”的完整闭环叙事,这在同类开源 harness 中较为罕见(多数 harness 或者不做轨迹导出,或者做了但不像这里这样有专门的压缩/采样后处理工具)。
  2. skill 自我维护(Curator)机制的成熟度——不是简单的”agent 可以创建 skill 文件”,而是 有独立的、按空闲时间触发的后台 review agent,带严格的不变式(只碰 agent 创建的 skill、 从不自动删除只归档、pin 后跳过自动转换),比多数 harness 里”skill = 静态 markdown 文件” 的做法更进一步。
  3. 安全模型的层次感明显强于常规实现:DANGEROUS_PATTERNS(可配置审批门)与 UNRECOVERABLE_BLOCKLIST(任何模式都无法绕过的地板)分层设计,加上针对已知 CVE (GHSA-96vc-wcxf-jjff)的 contextvars 竞态修复,显示出比”agent 产品”更贴近”生产基础设施” 的安全工程投入。

原始源码定位

  • repo: https://github.com/NousResearch/hermes-agent
  • commit/version analyzed: 5431bf29214681fb2fd25254568b58c9da8ce6e0(2026-06-06 提交, 2026-07-07 clone;官方文档站彼时已披露更新版本 v0.18.0,本 SHA 非绝对最新)
  • 关键文件列表(相对路径):
    • agent/conversation_loop.py(5294 行,主循环 run_conversation()
    • agent/system_prompt.py(536 行)、agent/prompt_builder.py(1971 行)
    • agent/context_engine.py(231 行)、agent/context_compressor.py(3045 行)
    • agent/memory_manager.py(1086 行)
    • agent/learning_graph.py(328 行)、agent/curator.py(1976 行)
    • agent/trajectory.py(56 行)、agent/moa_loop.py(1073 行)
    • agent/gemini_schema.pyagent/moonshot_schema.py
    • agent/anthropic_adapter.pyagent/bedrock_adapter.pyagent/vertex_adapter.pyagent/codex_responses_adapter.pyagent/gemini_native_adapter.pyagent/azure_identity_adapter.py
    • agent/model_metadata.pyagent/models_dev.py
    • agent/lmstudio_reasoning.pyagent/portal_tags.pyagent/nous_rate_guard.py
    • agent/retry_utils.pyagent/rate_limit_tracker.pyagent/credential_pool.pyagent/credential_sources.py
    • tools/registry.py(766 行)、tools/approval.py(2985 行)
    • tools/delegate_tool.py(3445 行)、tools/async_delegation.py(531 行)
    • tools/terminal_tool.pytools/environments/(base/local/docker/ssh/singularity/modal/managed_modal/modal_utils/file_sync)
    • tools/skill_manager_tool.py
    • hermes_state.py(275KB,SQLite+FTS5 会话存储)
    • batch_runner.py(57KB)、trajectory_compressor.py(69KB)
    • docs/observability/README.mddocs/session-lifecycle.mddocs/security/network-egress-isolation.md
    • acp_adapter/(server.py、session.py、permissions.py、edit_approval.py、tools.py、 events.py、provenance.py、auth.py)
    • cron/(jobs.py、scheduler.py、scheduler_provider.py、lifecycle_guard.py、 blueprint_catalog.py、suggestion_catalog.py)
    • 未深读、留待后续验证:tools/mcp_tool.pytools/kanban_tools.pyhermes_cli/kanban_decompose.pyhermes_logging.py

一手源存档(sources/)

/Users/zhao/projects/self-wiki/ai-research/sources/harness/hermes-agent/

  • NOTES.md —— 完整调研笔记(含全部文件路径、行号引用、summary)
  • code-snapshots/
    • approval_excerpt.pycontext_engine.pyconversation_loop_excerpt.pycurator_excerpt.pydelegate_tool_excerpt.pygemini_schema.pylearning_graph_excerpt.pymemory_manager_excerpt.pyskill_manager_tool_excerpt.pysystem_prompt.pyterminal_tool_excerpt.pytools_registry.pytrajectory.pytrajectory_compressor_excerpt.py
    • network-egress-isolation.mdobservability-README.mdsession-lifecycle.md (从仓库 docs/ 保存的完整文档)
  • official-docs/(Docusaurus 文档站抓取):
    • agent-loop.mdarchitecture.mddocs-index.mdmemory.mdsecurity.mdsession-storage.mdskills.md

尚未抓取(stage 2 遗留项,未来若需更深覆盖可补):官方文档站的 /docs/llms-full.txt(1.8MB 全站单文件 dump)、prompt-assemblycontext-compression-and-cachinggateway-internalsprovider-runtimetools-runtimeacp-internalscron-internalstrajectory-formatmemory-provider-plugin 等页面;源码侧 tools/mcp_tool.pykanban_tools.py/ kanban_decompose.pyhermes_logging.py