
原文版本:2026-05-13;资料复核:2026-10-13。 项目明确匹配 NousResearch/hermes-agent,源码基线
427d605d。 官方文档部分示例仍保留旧层次或旧文件范围,本文涉及具体顺序时以该提交的实现为准,不把文档示意图当成实际请求捕获。
一、核心设计原则
- 缓存提示词与调用时内容分离:本地构建缓存避免每轮重做相同工作;provider 前缀缓存还取决于路由、内容字节、TTL 与命中条件,两者不是一回事。
- SOUL.md 是默认身份入口:通常位于首个身份块;成功加载时替代内置身份,跳过 context files 的子任务可使用默认身份。排在第一位不意味着它能覆盖宿主安全规则。
- 渐进式披露:Skills 目录与完整
SKILL.md分开,可用skill_view按需载入;索引已存在时不必先调用一次skills_list才有能力使用技能。 - 项目上下文按类型选择:first match wins 指一个项目上下文类型,不等于永远只有一个文件。当前 AGENTS 类型可合并 git root 到 CWD 的目录链。
- 记忆是可重建的快照:写入可立即持久化,但已缓存提示词不会逐条原地更新;新会话、压缩后的重建等边界可刷新快照,不是必须等下次会话。
二、完整 Prompt 结构
2.1 缓存系统提示词层(Cached System Prompt Layers)
当前 build_system_prompt_parts() 返回三个 tier,最终按 stable → context → volatile 拼接:
| Tier | 当前主要内容 |
|---|---|
stable |
SOUL/默认身份、工具与模型指导、固定 pinned skills、编码操作指导 |
context |
调用者 system_message、项目上下文、工作树相关信息等 |
volatile |
Skills 索引、内置 MEMORY/USER 快照、外部记忆/插件块、profile 与时间/模型/provider 信息、运行环境 |
是否有工作树等条件会影响部分尾部指导的归属。原文十层固定顺序已不适用于该实现;官方文档中“skills 属于 stable”的一句话也与同页列表和当前源码不一致。这里区分固定 pinned skill 内容与可变的技能目录,以代码为准。
volatile 仍属于已构建并缓存的 system prompt,不代表每轮重写。近期 Skills 目录变化可以通过一次性的 turn note 提醒,完整目录在重建边界收敛。官方 Prompt Assembly。
2.2 API 调用时专属层(API-call-time-only layers)
临时 system overlay、prefill、gateway 会话增量、外部 recall 与插件 pre_llm_call 返回的上下文,可能在请求准备阶段加入而不永久改写缓存提示词。不同内容的角色载体不同:例如当前插件上下文追加到该轮 user message,不等于取得新的高优先级系统权限。
三、组装流程详解

3.1 数据流总览
CLI / gateway / 其他入口
→ AIAgent 运行准备与有效 provider/模型解析
→ 构建或复用缓存 system prompt
→ 组装历史、当前输入与临时增量
→ 检查上下文压力,按当前策略压缩
→ provider 消息适配与缓存标记
→ 模型调用 ↔ 工具校验、执行与结果回填
→ 返回答案、保存会话,必要时更新记忆
这是职责概览。Provider 通常要在创建客户端、选择协议与上下文预算时参与,不能固定说“prompt_builder 完成后才解析 provider”。当前组装主逻辑在 agent/system_prompt.py,agent/prompt_builder.py 主要负责上下文、Skills 等帮助函数。
3.2 完整组装步骤
- 确定任务、会话、工作目录与 provider/model 等运行参数。
- 追加本次输入,并准备与当前请求匹配的历史。
- 复用缓存提示词,或按三层结构重建。
- 根据实际上下文预算、已报告 usage 与压缩配置判断是否压缩。
- 把内部消息转换为该 provider 的请求结构,加入临时内容。
- 在支持路径上应用缓存策略,发出可中断请求。
- 处理工具调用、追加结果,再进入下一次模型请求;无工具调用、达到运行限制或中断时结束。
- 按入口保存状态,必要时刷新记忆/提示词。
原文把“50% 必压缩”“总是循环到无工具调用”“最终回复后才检查压缩”写成统一行为,均过于绝对。
四、系统提示词(System Prompt)构建细节

4.1 SOUL.md(Agent Identity)
通常从选定 profile 的 HERMES_HOME/SOUL.md 读取,不取项目 CWD 的同名文件。没有可用内容时采用内置身份;skip_context_files 等路径也可能直接用默认身份。
当前加载器还区分用户自写与 profile distribution 提供的 SOUL:前者命中扫描模式时警告后仍加载,后者可被阻断。不能把这项扫描说成“任何身份文件都已净化”。
截断上限也不再固定为 20k:默认会随模型上下文预算变化,有 20,000 字符下限和动态上界,显式 context_file_max_chars 优先。可概括为:
# 行为示意,不是可直接复制运行的完整实现
content = read_selected_profile_soul()
content = scan_with_source_trust(content)
content = truncate_to_effective_context_budget(content)
identity = content or DEFAULT_AGENT_IDENTITY
SOUL.md 是人格与行为输入,不是工具授权表。实际审批和文件/工具权限仍由宿主执行。加载器源码。
4.2 Memory 系统(MEMORY.md & USER.md)
| 文件 | 用途 | 默认字符预算 |
|---|---|---|
MEMORY.md |
跨任务仍有价值的环境事实等笔记 | 2,200 |
USER.md |
稳定用户资料与偏好 | 1,375 |
默认位置是所选 home 的 memories/。预算可配置,不能把字符数直接按固定比例换成 token,中文尤其不同。记忆工具源码。
Frozen Snapshot 的意义:
- 当前缓存块保持稳定,减少无谓前缀变更。
- memory 工具写入持久存储后,工具返回可以展示新状态,但不逐条原地改写缓存块。
invalidate_system_prompt()在压缩后清除缓存并从磁盘重载,因此同一会话也能在重建时看到更新。- 保存到磁盘、快照出现和模型正确使用,仍是不同环节。
容量满时应整合或替换,不要把任务专属流程不断堆进常驻记忆;可复用任务经验更适合 Skills。
4.3 Context Files(项目上下文)
当前按第一个非空类型选择:
| 优先级 | 类型 | 当前范围 |
|---|---|---|
| 1 | .hermes.md / HERMES.md |
CWD 向上到 git root,最近匹配 |
| 2 | AGENTS 类型 | git root 到 CWD 的目录链;非 git 项目只检查 CWD |
| 3 | CLAUDE.md / claude.md |
CWD |
| 4 | .cursorrules 与 .cursor/rules/*.mdc |
CWD,可组合多个规则文件 |
AGENTS 类型每层按首个非空 AGENTS.override.md / AGENTS.md / agents.md 选择,再合并不同目录的内容。因此“只加载一个项目上下文文件”和“AGENTS 只看 CWD”都已经不准确。 子目录 hint 的渐进发现是另一层机制,不替代启动时目录链。
所有这些文件都属于指令输入。扫描主要识别已知注入/隐藏字符/外传模式;项目文件命中时可能替换成 BLOCKED 标记,不能保证覆盖所有攻击,也可能误报。用户自写 SOUL 的告警后加载是另一信任路径,不要混为“扫描通过”。
4.4 Skills Index
通常先有紧凑索引,再按任务加载全文与参考资料:
已有索引或 skills_list()
→ skill_view(name)
→ skill_view(name, path)(需要具体参考时)
不是必须依次调用三次的固定流程,也没有永远约 3k token 的目录大小。目录由技能数量、描述、平台/应用资格、toolset 条件和预算决定。当前还有可明确固定加载的 pinned skills,所以“任何技能全文都绝不注入”也过于绝对。
常见 frontmatter:
---
name: my-skill
description: Brief task-specific procedure
---
# Skill Title
...
更复杂的资格字段须按安装版本的 Skills schema 检查,不能把示例当成所有字段均有效的证明。Skills 文档。
4.5 完整组装示例
以下按当前实现概括,不再复制旧十层模板:
[stable]
选定身份 → 通用工具/模型指导 → 固定技能与编码操作指导
[context]
调用者 system_message → 项目规则 → 工作树与相关操作信息
[volatile,仍在缓存 system prompt 中]
Skills 索引 → MEMORY / USER 快照 → 外部记忆与插件块
→ profile、时间/会话/模型信息 → 运行环境
[该次 API 请求另加]
当前输入、历史、工具结果、临时 overlay / recall
这些是缓存与组装顺序,不是安全优先级列表。system_message 是这里接收的一份输入,不等于必然替换整段身份或突破宿主规则。OpenAI 模型的角色权限由消息角色与指令层级定义,不能从 SOUL 在 slot #1 推出最高权限。OpenAI 官方角色说明。
五、Provider Runtime Resolution
5.1 支持的 Providers
项目提供 Nous、OpenRouter、Anthropic、OpenAI、Gemini 及其他托管/本地路由。具体 provider ID、授权方式、模型目录和功能以当前 resolver 与配置为准,不保留“200+ 模型”这样的动态数字,也不承诺任意 OpenAI-compatible endpoint 都完整支持工具、图片或压缩。
5.2 API Modes
Hermes 用 chat_completions、codex_responses、anthropic_messages 等内部模式名选择消息适配路径。codex_responses 是 Hermes 的内部路由名称,不是 OpenAI 官方另一个名为 Codex Responses 的 API 标准。
OpenAI Responses 使用 typed input/output items,工具调用与结果也有专门 item;不能把内部 role: tool 消息直接等同于最终 Responses 请求。Hermes Provider 文档、OpenAI Responses 迁移说明。
5.3 Resolution 优先级
显式运行参数、配置、环境变量、登录状态和 provider 默认值共同决定有效路由;具体模型覆盖和辅助调用各有规则。不再用一个四层列表概括所有入口。调试时应检查解析后的 provider、model、api mode 与有效上下文预算,不能只看配置文件里的模型名。
六、双重压缩系统
Hermes 有 gateway hygiene 与 agent 内上下文压缩两条防线,但当前还有原生/server-side 路径及模型特例,不能把“双层 50% / 85%”当固定契约。
6.1 Gateway Session Hygiene(高水位检查)
gateway 在 agent 开始前做粗略估算与保护,文档中的典型阈值是 85%。是否触发还取决于入口、预算与当前会话状态。
6.2 Agent ContextCompressor(可配置阈值)
compression.threshold 默认配置仍为 0.50,但当前实现/文档还有小窗口阈值下限、模型/provider 覆盖、原生压缩与证据门控。小于 512K 的窗口有 75% 阈值下限,部分路由有特例,因此不能写“一到一半必压缩”。
API usage 能提供较准确计数,但并不是每个时刻都有最新 usage;新工具结果、恢复历史等仍需估算。官方压缩说明。
6.3 压缩配置
compression:
enabled: true
threshold: 0.50
target_ratio: 0.20
protect_last_n: 20
这是局部配置示例。target_ratio 在 legacy 模式控制受保护尾部相对阈值预算的比例,不等于所有算法统一保留“完整窗口 20%”;当前 lean 等模式另有策略。
6.4 压缩算法(职责视图)
- 必要时缩短旧工具输出,但保持调用/结果配对和关键约束。
- 选择摘要与保护尾部边界,具体由 context engine 与配置决定。
- 调用摘要模型提炼目标、约束、进展、决定、文件与后续步骤。
- 将摘要、保留历史和当前请求组成可继续的输入;处理失败、重试和缓存重建。
原文的四阶段与结构化摘要可作为设计理解,不能据此断言每条旧工具输出超过 200 字符就必清空,或固定保留前 3 条。压缩有损,不保证旧偏好、工具细节或所有约束都保留。
七、Prompt Caching(Anthropic)
当前 agent/prompt_caching.py 仍管理 Anthropic cache-control,但默认布局已不只是旧的 system_and_3。
7.1 缓存断点策略
| 条件 | 当前布局 |
|---|---|
| 有匹配的 static system prefix | 稳定系统前缀 + 完整系统提示尾部 + 最近两个可缓存的非系统消息 |
| 无匹配 static prefix | 一个 system 断点 + 最近三个可缓存的非系统消息 |
工具 schema、native/代理路由和可承载 marker 的消息类型还会影响最终计划。不能直接把“倒数三条任意消息”当固定位置。缓存源码。
7.2 缓存标记格式
{"type": "ephemeral"}
{"type": "ephemeral", "ttl": "1h"}
这些是标记示例,具体 TTL、路由支持与费用由 provider 决定;本文没有验证账号实际命中或账单。
7.3 缓存感知设计模式
尽量保持前缀字节稳定;中间内容变化可能减少后续可复用长度。压缩后是否保住稳定前缀、多久重建命中,要看实际请求与缓存指标。原文的“约省 75%”“1-2 轮恢复”来自项目描述/经验,不是本文实测,也不是所有会话保证,因此不保留固定收益承诺。
八、API Mode 与消息格式
8.1 内部消息格式
内部常用兼容 Chat Completions 的角色结构:
{"role": "user", "content": "..."}
{"role": "assistant", "content": "...", "tool_calls": []}
{"role": "tool", "tool_call_id": "...", "content": "..."}
这不是最终 provider 请求的统一 schema。Responses items、Anthropic content blocks 等都需适配。
8.2 消息交替规则
运行时需维护工具调用/返回的有效结构,部分模型模板要求更严格的角色交替,适配器可能合并或规范化相邻消息。不能据此宣布所有 OpenAI/Anthropic/自定义 API 都禁止连续 user 或 assistant;这是 provider/模板兼容问题,不是通用消息角色定律。
九、关键源码文件
| 文件 | 用途 |
|---|---|
run_agent.py |
AIAgent 对外入口与运行协调 |
agent/system_prompt.py |
三层系统提示组装、缓存失效与重建 |
agent/prompt_builder.py |
SOUL、项目文件、Skills 等加载帮助函数 |
agent/context_compressor.py |
上下文压缩 |
agent/prompt_caching.py |
cache-control 计划与消息标记 |
tools/memory_tool.py |
记忆工具与 home/预算解析 |
hermes_state.py |
会话/状态持久化 |
gateway/run.py |
gateway 分发与运行协调 |
源码引用均锁定本次提交;文件名与方法可能继续变化。
十、与 OpenClaw 的关键差异
| 方面 | Hermes | OpenClaw |
|---|---|---|
| 身份 | 通常 SOUL 作为首个身份块 | workspace persona,载体按 harness 区分 |
| Skills | 索引、skill_view,另有 pinned skills | 目录与按需 read,原生宿主有不同载体 |
| 项目上下文 | 先选类型,再可能合并目录链 | 多种 Bootstrap 与原生项目发现 |
| Memory | 缓存快照,可在压缩重建刷新 | 有界注入/按需工具/主动召回,按 harness 区分 |
| 压缩 | gateway + context engine,含路由特例 | 内置/插件/native 路径,各有边界 |
| 缓存 | 本地 system 缓存与 provider 策略 | 稳定前缀、刷新与 provider/harness 缓存机制 |
| Provider 协议 | 多种适配模式 | 同样不是只支持 chat completions |
| 子代理 | 有独立上下文与运行配置 | 也可有模型/工具/上下文覆盖,不能简单说只继承父级 |
这里比较的是实现思路,不是功能排名;两个项目的版本和选用 harness 对结论影响很大。OpenClaw 官方 System Prompt。
十一、相关文档索引
2026-10-13 资料核对:已浏览官方文档并读取上述提交的 system_prompt、prompt_builder、memory_tool、prompt_caching 源码与压缩文档;OpenAI/Codex 相关先读本地 openai-docs 技能,再查官方角色和 Responses 资料。文档旧示例与源码冲突处已明确按源码修正。未运行 Hermes、provider API、压缩或费用测试。