Hermes Agent 原理与实战——第6章 Prompt 系统、SOUL.md 与身份个性

本章将深入拆解 Hermes 的 Prompt Builder 模型、SOUL.md身份文件、Personality 预设系统、冻结快照机制以及 Prompt Cache 命中率优化策略,帮助你从根本上理解并掌控 Agent 的"身份设定与行为逻辑的核心根源"。

Hermes Agent 原理与实战——第6章 Prompt 系统、SOUL.md 与身份个性

在前面的章节中,我们已经了解了 Hermes Agent 的整体架构、安装配置以及多入口调用方式。无论用户通过 CLI、Telegram Bot 还是 API Server 与 Hermes 交互,最终所有的请求都会汇聚到 AIAgent 这个核心对象。而 AIAgent 要做的第一件事,就是构建系统提示词(System Prompt)——它决定了 Agent 是谁、知道什么、能做什么、以什么风格回应用户。

本章将深入拆解 Hermes 的 Prompt Builder 模型、SOUL.md 身份文件、Personality 预设系统、冻结快照机制以及 Prompt Cache 命中率优化策略,帮助你从根本上理解并掌控 Agent 的身份设定与行为逻辑的核心根源

6.1 Prompt 系统的核心设计理念

Hermes 刻意将以下两者分离:

  • 缓存的系统 prompt(提示词) 状态
  • 每次 API 调用时临时添加的内容。

这种分离不是随意的工程选择,而是经过深思熟虑的设计决策,它权衡了以下多个维度:

  • Token 用量:稳定的系统前缀可以被提供商侧的缓存机制复用,避免每次请求都重复计费;
  • Prompt 缓存效果:频繁变动的内容放在临时层,不变的内容放在缓存层,最大化缓存命中率;
  • 会话连续性:冻结的快照确保同一对话中 Agent 对记忆和身份的认知保持一致;
  • 记忆正确性:避免会话中途记忆更新导致系统提示词反复变化,进而引发模型行为漂移。

理解这一设计理念是掌握后续所有槽位机制的前提。Hermes 的 Prompt 组装大致可分为两大阶段:

  1. 系统提示词组装阶段:在会话开始时(或需要重建时),agent/prompt_builder.py 按固定顺序拼装多个"槽位"(Slot),生成一个相对稳定的系统提示前缀。这个前缀在 Claude、OpenRouter 等支持 prompt caching 的平台上会被标记为缓存边界。
  2. API 调用阶段:在每次调用模型时,除了已缓存的系统前缀,还会临时附加工具 schema、ephemeral 提示层、当前轮次的用户消息等内容。这些临时内容不参与长期缓存,或仅参与短期缓存。

这种"稳定前缀 + 临时后缀"的双层架构,让 Hermes 在长对话中既能保持身份与记忆的稳定,又能灵活响应每轮的具体上下文。

6.2 Prompt Builder 槽位模型详解

agent/prompt_builder.py 是 Hermes Prompt 系统的核心实现文件。它使用一种固定顺序的槽位模型(Slot Model)来组装系统提示词。可以把每个槽位理解为系统提示词中的一个"乐高积木"——每个积木职责清晰、位置固定、可独立开关。

Hermes的 prompt 系统可分为两个部分:已缓存的系统 prompt 层 API 调用时临时附加的层 。以下是所有槽位按注入顺序排列的完整视图。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
┌─────────────────────────────────────────────────────────────────────┐
                     已缓存的系统 prompt                             
  (这些层在会话开始时拼装一次,随后保持冻结,适合长期缓存)              
├─────────────────────────────────────────────────────────────────────┤
  Slot 1: Agent Identity(身份标识)                                   
           ~/.hermes/SOUL.md 或内置默认身份                           
├─────────────────────────────────────────────────────────────────────┤
  Slot 2: Tool-aware Behavior Guidance(工具感知行为指导)              
           持久记忆说明、session_search 提示、工具使用强制等           
├─────────────────────────────────────────────────────────────────────┤
  Slot 3: Honcho Static Block(可选)                                  
           Honcho Memory Provider 激活时的静态人格/上下文数据          
├─────────────────────────────────────────────────────────────────────┤
  Slot 4: Optional System Message(可选系统消息)                       
           来自 config.yaml  API 参数的系统消息覆盖                  
├─────────────────────────────────────────────────────────────────────┤
  Slot 5: Frozen MEMORY Snapshot(冻结的记忆快照)                     
           ~/.hermes/memories/MEMORY.md 的会话起始快照                 
├─────────────────────────────────────────────────────────────────────┤
  Slot 6: Frozen USER Profile Snapshot(冻结的用户画像快照)            
           ~/.hermes/memories/USER.md 的会话起始快照                   
├─────────────────────────────────────────────────────────────────────┤
  Slot 7: Skills Index(技能索引)                                     
           已启用技能的 {name, description, category} 列表             
├─────────────────────────────────────────────────────────────────────┤
  Slot 8: Context Files(上下文文件)                                  
           .hermes.md / AGENTS.md / CLAUDE.md / .cursorrules         
├─────────────────────────────────────────────────────────────────────┤
  Slot 9: Timestamp + Session ID(时间戳与会话标识)                    
           当前时间与可选会话 ID                                       
├─────────────────────────────────────────────────────────────────────┤
  Slot 10: Platform Hint(平台提示)                                   
           CLI/Discord/Telegram 等平台的格式指导                       
├─────────────────────────────────────────────────────────────────────┤
                     API 调用时临时附加的层                             
           (这些层不进入长期缓存,或仅作短期缓存)                         
├─────────────────────────────────────────────────────────────────────┤
  Slot 11: Personality Override(个性覆盖层)                           
           /personality 命令激活的临时预设                             
├─────────────────────────────────────────────────────────────────────┤
  Slot 12: Tool Schemas(工具模式定义)                                
           当前启用 toolset  JSON Schema 描述                        
├─────────────────────────────────────────────────────────────────────┤
  Slot 13: Ephemeral Layers(临时层)                                  
           预算告警、上下文压力提示、压缩提示、prefill 消息等           
└─────────────────────────────────────────────────────────────────────┘

6.2.1 缓存的系统 prompt(提示词)层

缓存的系统提示词包含10个槽位,下面对各个槽位详细说明。

Slot 1:Agent Identity(身份标识)

这是系统提示词中最核心、最优先部分。充当代理的身份标识,它回答了一个根本问题:“这个 Agent 是谁?”

Identity 槽位的内容来源有两个,按优先级排列:

  1. 用户自定义身份~/.hermes/SOUL.md(或 $HERMES_HOME/SOUL.md)的内容。如果该文件存在且有有效内容,它将完全替换内置默认身份,原样注入到系统提示词的第一位。
  2. 内置默认身份:如果 SOUL.md 不存在、为空或无法加载,系统回退到硬编码的默认身份:
1
2
3
4
5
6
7
You are Hermes Agent, an intelligent AI assistant created by Nous Research.
You are helpful, knowledgeable, and direct. You assist users with a wide
range of tasks including answering questions, writing and editing code,
analyzing information, creative work, and executing actions via your tools.
You communicate clearly, admit uncertainty when appropriate, and prioritize
being genuinely useful over being verbose unless otherwise directed below.
Be targeted and efficient in your exploration and investigations.

这个槽位的设计有几个关键约束:

  • 仅从 HERMES_HOME 加载:Hermes 不会在当前工作目录中查找 SOUL.md。这样做是为了保证个性的可预测性——如果你从任意目录启动 Hermes,不会随项目切换而意外改变。
  • 安全扫描与截断SOUL.md 在注入前会经过提示词注入扫描(检查不可见 unicode、“ignore previous instructions(忽略前面指令)” 等恶意模式),并在超过上限(默认 20,000 字符)后,保留前 70% + 后 20%,中间用截断标记代替。
  • 防止重复:由于 SOUL.md 已经在 Slot 1 作为身份注入,build_context_files_prompt() 会传入 skip_soul=True,确保它在 Slot 8(上下文文件)中不再出现第二次。
  • 子 Agent 回退:当设置了 skip_context_files(例如子 Agent 委托上下文),不会加载 SOUL.md,而是使用硬编码的 DEFAULT_AGENT_IDENTITY,避免子 Agent 继承过于具体的父身份。

Slot 2:Tool-aware Behavior Guidance(工具感知行为指导)

这一层向模型解释它拥有哪些持久能力、应该如何使用工具。内容包括:

  • 持久记忆指导:告知模型它有跨会话的持久记忆,应使用 memory 工具保存持久事实(如:用户偏好、环境细节、工具特性、稳定约定),并保持记忆内容紧凑;
  • 会话搜索指导:当用户引用过去的对话或模型怀疑存在跨会话上下文时,应先用 session_search 检索,而不是直接问用户;
  • 工具使用强制(Tool-use Enforcement):对于 GPT/Codex 等模型,注入 “You MUST use your tools to take action — do not describe what you would do” 这类指导,防止模型"光说不练"。

这一层是行为协议层——它不定义 Agent 是谁,而是定义 Agent 应该怎么工作。

Slot 3:Honcho Static Block(可选)

当 Honcho Memory Provider 被激活时,这一层会注入 Honcho 提供的静态人格/上下文数据块。Honcho 是一个可选的辩证用户建模后端,能把零散的"用户偏好"整合成更结构化的上下文。如果未启用 Honcho,这一层为空。

Slot 4:Optional System Message(可选系统消息)

来自 config.yamlagent.system_prompt 配置或 API 调用参数传入的系统消息覆盖。这是一个通用扩展槽,允许部署方或用户在不修改核心代码的前提下,向系统提示词注入额外的指令文本。

Slot 5 & 6:Frozen MEMORY & USER Profile Snapshots(冻结快照)

这两个槽位分别注入 ~/.hermes/memories/MEMORY.md~/.hermes/memories/USER.md会话起始快照

  • MEMORY.md:存储环境约定、工作习惯、项目上下文等关键事实。默认上限约 2,200 字符(约 800 tokens)。
  • USER.md:存储用户画像信息,如姓名、GitHub 账号、技术栈偏好等。默认上限约 1,375 字符(约 500 tokens)。

之所以称为"冻结快照",是因为:

  • 它们在会话开始时读取并注入系统提示词;
  • 会话中途,即使用户或 Agent 通过 memory 工具修改了磁盘上的 MEMORY.mdUSER.md已构建的系统提示词也不会立即更新
  • 新的记忆内容要等到新会话开始、或强制重建 prompt 时才生效。

这种冻结机制有两个目的:

  1. 保持 prompt cache 稳定:如果每轮都因为记忆更新而修改系统前缀,缓存会不断失效,导致 token 成本飙升;
  2. 避免同一对话中的认知漂移:同一轮对话中,Agent 对"我是谁"和"用户是谁"的认知应当保持一致。

Slot 7:Skills Index(技能索引)

当 skills 工具可用时,这一层会向 prompt 贡献一个紧凑的技能索引。格式类似:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
## Skills (mandatory)
Before replying, scan the skills below. If one clearly matches your task,
load it with skill_view(name) and follow its instructions.

<available_skills>
  software-development:
    - code-review: 结构化代码审查工作流
    - test-driven-development: TDD 方法论
  research:
    - arxiv: 搜索并总结 arXiv 论文
</available_skills>

Skills 系统采用渐进式披露(Progressive Disclosure)策略:Level 0 只注入索引(约 3k token),Level 1 在模型调用 skill_view() 时注入该技能的全文(SKILL.md),Level 2 在技能内部引用文件时按需加载。这避免了把所有技能文档一次性塞进上下文窗口。

Slot 8:Context Files(上下文文件)

这一层加载项目级的上下文指令文件。Hermes 使用优先级系统——只加载一种项目上下文类型(先匹配先赢):

优先级 文件 搜索范围 说明
1 .hermes.mdHERMES.md 从 CWD 向上至 git 根目录 Hermes 原生项目配置
2 AGENTS.md 仅 CWD(及子目录) 常见 agent 指令文件
3 CLAUDE.md 仅 CWD Claude Code 兼容性
4 .cursorrules.cursor/rules/*.mdc 仅 CWD Cursor IDE 兼容性
  • 项目上下文文件使用优先级系统 —— 仅加载一种类型(第一个匹配优先):.hermes.mdAGENTS.mdCLAUDE.md.cursorrules。SOUL.md 始终独立加载。
  • AGENTS.md 是分层的:如果子目录也有 AGENTS.md,所有都会合并。
  • 安全扫描:检查提示词注入模式(不可见 Unicode、“忽略之前的指令”、凭据窃取尝试);
  • 截断处理:使用 70/20 头部/尾部分割方式,加上截断标记,限制在 context_file_max_chars 个字符内。上限随模型上下文窗口缩放(20,000 字符下限,500K 上限);config.yaml 中显式设置的 context_file_max_chars 始终优先生效。
  • 剥离 YAML frontmatter.hermes.md 的 frontmatter 会被移除(保留供未来配置覆盖使用)。

关于 SOUL.md 和项目上下文文件的分工,本章后文会详细展开。

详细代码位于 agent/prompt_builder.py文件中:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 来自 agent/prompt_builder.py(简化版)
def build_context_files_prompt(cwd=None, skip_soul=False):
    cwd_path = Path(cwd).resolve()

    # 优先级:先匹配者胜 —— 只加载一种项目上下文
    project_context = (
        _load_hermes_md(cwd_path)       # 1. .hermes.md / HERMES.md(向上遍历到 git 根目录)
        or _load_agents_md(cwd_path)    # 2. AGENTS.md(仅当前工作目录)
        or _load_claude_md(cwd_path)    # 3. CLAUDE.md(仅当前工作目录)
        or _load_cursorrules(cwd_path)  # 4. .cursorrules / .cursor/rules/*.mdc
    )

    sections = []
    if project_context:
        sections.append(project_context)

    # SOUL.md 来自 HERMES_HOME(独立于项目上下文)
    if not skip_soul:
        soul_content = load_soul_md()
        if soul_content:
            sections.append(soul_content)

    if not sections:
        return ""

    return (
        "# 项目上下文\n\n"
        "以下项目上下文文件已加载,应当遵循:\n\n"
        + "\n".join(sections)
    )

Slot 9 & 10:Timestamp / Session ID 与 Platform Hint

  • 时间戳:注入当前时间(如 Current time: 2026-03-30T14:30:00-07:00),让 Agent 具备时间感知能力,对 cron 调度、时效性判断等任务至关重要。

  • 会话 ID:可选注入,用于追踪和调试。

  • 平台提示:平台提示(上述第 10 层)是 Hermes 针对不同平台(如 Telegram、WhatsApp、Slack、CLI 等)注入的环境专属指令——例如「你正处于终端环境,请勿使用 Markdown」。内置默认值定义在 PLATFORM_HINTSagent/system_prompt.py)中;插件注册的平台则通过平台注册表自行提供提示内容。

    管理员可在 config.yaml 中通过顶层 platform_hints 键,对单个平台的提示进行追加或替换,且不影响其他平台:

    1
    2
    3
    4
    5
    6
    7
    8
    
    platform_hints:
      whatsapp:
        append: >
          当需要表格输出时,请调用 table_formatting 技能,
          而不是输出 Markdown 表格。
      slack:
        replace: "你位于 Slack 中。保持回复紧凑,避免使用宽表格。"
      telegram: "优先发送简短消息;拆分较长的回答。"   # 简写形式 = 追加
    
    • append — 保留内置提示,并在其后追加额外文本。
    • replace — 完全替换内置提示。
    • 裸字符串 — 等同于 append 的简写。
    • 当同时存在 appendreplace 时,replace 优先。
    • 格式错误的条目会被防御性地忽略,并回退到未修改的默认值,因此配置值错误不会导致提示词组装崩溃或跨平台泄露。

    覆盖规则在系统提示词构建阶段解析(包括会话启动,以及压缩重建时——因为压缩会重建提示词)。对于固定配置,其生成的提示具有字节级稳定性,与内置提示共存,不会破坏提示词缓存。

6.2.2 仅 API 调用时添加的层

这部分内容被刻意地作为缓存系统提示词的一部分持久化,在每次调用模型时临时附加。

Slot 11:Personality Override(个性覆盖层)

/personality 命令激活的临时预设。它不属于已缓存的系统 prompt 的持久部分,而是作为会话级覆盖层在运行时注入。这意味着:

  • 切换 personality 不会重建已缓存的系统前缀;
  • Personality 的效果是"叠加"在 SOUL.md 基础语气之上的临时模式切换;
  • 新会话默认回到 SOUL.md 定义的基础身份。

Slot 12:Tool Schemas(工具模式定义)

在每次 API 调用前,model_tools.py 会收集当前启用的所有工具的 JSON Schema 描述,作为 tools 参数传入。这部分内容通常较长(尤其是启用大量工具集时),且可能因用户通过 /tools 命令开关工具而动态变化,因此不适合放入需要长期缓存的系统前缀中。

Slot 13:Ephemeral Layers(临时层)

Ephemeral Layers(临时层)是 Hermes Prompt 系统中"变"的部分。它们刻意不作为已缓存系统 prompt 的一部分持久化,只在单次 API 调用时附加。

Ephemeral Layers 主要包括:

  • ephemeral_system_prompt:通过环境变量 HERMES_EPHEMERAL_SYSTEM_PROMPT 传入的临时系统提示。适用于需要给某一轮次添加特殊指导,但不想永久修改系统提示的场景;
  • Prefill 消息:开发者或上游系统传入的前置提示,用于引导模型以特定格式开始回复;
  • Gateway 派生的会话上下文覆盖层:消息网关根据当前平台或用户状态动态生成的临时指导;
  • Honcho 动态召回:注入当前轮次用户消息的后续轮次 Honcho 召回内容(与 Slot 3 的 Honcho 静态块不同);
  • 上下文预算告警:当对话长度接近压缩阈值时,注入的提示模型注意上下文压力的消息;
  • 迭代预算耗尽提示:当 Agent 达到 max_turns 上限时,注入要求模型收尾的提示,并允许一次宽限调用。

pre_llm_call 插件上下文也同样走 API 调用时路径:它被追加到当前轮次的用户消息中,而不会写入缓存的系统提示词。当多个插件返回上下文时,Hermes 会将这些上下文块拼接起来。

为什么分离出来

如果把这些临时内容也塞进已缓存的系统前缀,会带来两个问题:

  1. 缓存污染:每一轮都不同的内容会让整个系统前缀失去缓存价值;
  2. 语义混杂:临时告警、轮次特定指导与持久身份/记忆混在一起,会让系统提示词变得冗长且不稳定。

通过将 Ephemeral Layers 分离到 API 调用阶段,Hermes 确保了:

  • 稳定前缀真正稳定,可以被高效缓存;
  • 临时指导灵活多变,不影响跨会话的缓存复用;
  • 系统提示词的语义结构清晰可维护。

这种"稳定前缀 + 临时后缀"的分离,保持了稳定前缀对缓存的稳定性,是 Hermes Prompt 系统设计的精髓所在。

6.2.3 具体示例:组装后的系统提示词

以下是所有层都存在时,最终系统提示词的简化视图(注释说明了各部分的来源):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# 第 1 层:代理身份(来自 ~/.hermes/SOUL.md)
你是 Hermes,由 Nous Research 创建的 AI 助手。
你是一位专家级软件工程师和研究员。
你重视正确性、清晰性和效率。
...

# 第 2 层:工具感知行为指导
你拥有跨会话的持久化记忆。使用记忆工具保存持久化的事实:
用户偏好、环境详情、工具特性以及稳定的约定。
记忆会在每一轮对话中注入,因此请保持其紧凑,
聚焦于将来仍然有用的事实。
...
当用户引用之前对话中的内容,或者你怀疑存在相关的跨会话上下文时,
请使用 session_search 来调取,不要让对方重复自己说过的话。

# 工具使用强制要求(仅适用于 GPT/Codex 模型)
你必须使用工具来采取行动——不要仅仅描述你会做什么或计划做什么,
而不实际去执行。
...

# 第 3 层:Honcho 静态块(激活时)
[Honcho 个性/上下文数据]

# 第 4 层:可选的系统消息(来自配置或 API)
[用户配置的系统消息覆盖]

# 第 5 层:冻结的 MEMORY 快照
## 持久化记忆
- 用户偏好 Python 3.12,使用 pyproject.toml
- 默认编辑器是 nvim
- 正在进行项目 "atlas",位于 ~/code/atlas
- 时区:US/Pacific

# 第 6 层:冻结的 USER 画像快照
## 用户画像
- 姓名:Alice
- GitHub:alice-dev

# 第 7 层:技能索引
## 技能(必读)
在回复之前,请浏览以下技能。如果某个技能明显匹配你的任务,
请使用 skill_view(name) 加载它,并遵循其指示。
...
<available_skills>
  software-development:
    - code-review: 结构化代码审查工作流
    - test-driven-development: TDD 方法论
  research:
    - arxiv: 搜索并总结 arXiv 论文
</available_skills>

# 第 8 层:上下文文件(来自项目目录)
# 项目上下文
以下项目上下文文件已加载,应当遵循:

## AGENTS.md
这是 atlas 项目。使用 pytest 进行测试。主入口点是
src/atlas/main.py。提交前始终运行 `make lint`。

# 第 9 层:时间戳 + 会话
当前时间:2026-03-30T14:30:00-07:00
会话:abc123

# 第 10 层:平台提示
你是一个 CLI AI Agent。尽量避免使用 markdown,
使用可在终端中渲染的简单文本。

6.3 SOUL.md:Agent 的主要身份标识

Hermes Agent 的个性完全可自定义。SOUL.md主要身份标识——它是系统提示词(prompt)中的第一项内容,定义了 Agent 是谁。

6.3.1 什么是 SOUL.md

SOUL.md 是一个存放在 HERMES_HOME(默认 ~/.hermes/)中的持久角色文件。SOUL.md 是 Agent 的主要身份标识,它占据系统提示词的第 1 个槽位,完全替代硬编码的默认身份块。这意味着:

  • 编辑 SOUL.md 的效果,等同于重写 Agent 的"自我介绍";
  • 它是每用户/每实例级别的身份标识,而不是每项目级别;
  • 它的内容原样注入,不会在周围添加任何包装语言。

准确路径:

1
2
3
~/.hermes/SOUL.md
# 或使用自定义主目录时:
$HERMES_HOME/SOUL.md

Hermes 仅从 HERMES_HOME 加载 SOUL.md, 不会在当前工作目录中查找 SOUL.md

此设计的原因:

这样可以保持个性的可预测性。如果 Hermes 从你启动它的任意目录加载 SOUL.md,你的个性可能会在不同项目之间意外改变。通过仅从 HERMES_HOME 加载,个性归属于 Hermes 实例本身。这也让用户更容易理解:“编辑 ~/.hermes/SOUL.md 来更改 Hermes 的默认个性。”

6.3.2 自动初始化行为

Hermes 在启动时会自动检查 SOUL.md 是否存在:

  • 如果不存在,Hermes 会自动创建一个初始文件(内容基于内置默认身份);
  • 如果已存在,已有的用户 SOUL.md 文件不会被覆盖;
  • 如果 SOUL.md 存在但为空/仅含空白/无法读取,Hermes 将回退到内置的默认身份
  • 如果 SOUL.md 有内容,该内容在经过安全扫描和截断处理后将原样注入。
  • SOUL.md 不会在上下文文件部分重复出现——它仅作为身份标识出现一次。
  • 当设置了 skip_context_files(如子 Agent 委托上下文),不会加载 SOUL.md,而是使用精简的默认身份。

这使 SOUL.md 成为真正的每用户或每实例身份标识,而不仅仅是一个附加层。

加载逻辑(简化版)如下:

1
2
3
4
5
6
7
8
9
# 来自 agent/prompt_builder.py(简化版)
def load_soul_md() -> Optional[str]:
    soul_path = get_hermes_home() / "SOUL.md"
    if not soul_path.exists():
        return None
    content = soul_path.read_text(encoding="utf-8").strip()
    content = _scan_context_content(content, "SOUL.md")  # 安全扫描
    content = _truncate_content(content, "SOUL.md")       # 截断上限随模型上下文窗口缩放(20k 为下限);配置覆盖优先
    return content

6.3.3 SOUL.md 应该写什么

SOUL.md 的核心定位是持久的语气和个性指导。适合写入的内容包括:

  • 语气:正式还是随意?热情还是冷静?
  • 沟通风格:偏好多长回复?喜欢列表还是段落?
  • 直接程度:应该委婉还是直截了当?
  • 默认交互风格:主动提问还是等待指令?
  • 风格上应避免的内容:比如讨厌空洞的寒暄、拒绝过度道歉;
  • 如何处理不确定性、分歧或模糊情况:遇到不确定的信息时,是猜测还是明确承认不知道?

不适合写入 SOUL.md 的内容:

  • 一次性项目说明(“这个项目的入口文件是 src/main.py”)
  • 文件路径或端口配置
  • 代码库规范或技术栈要求
  • 临时工作流细节

这些内容属于项目上下文文件(AGENTS.md.hermes.md),而不是 SOUL.md

一个实用的判断规则:

如果它应该随你到处适用,属于 SOUL.md;如果它属于某个项目,属于 AGENTS.md

6.3.4 优质 SOUL.md 示例

一个好的 SOUL.md 应该:

  • 跨上下文稳定:在不同上下文中保持稳定,无论你在写代码、查资料还是闲聊,这个身份都应该适用;
  • 足够宽泛:适用于多种对话场景,不要窄到只能做某一类任务;
  • 足够具体:能实质性地塑造语气,不要泛泛而谈"你是一个有用的助手",要给出可操作的指导;
  • 聚焦沟通与身份:而非特定任务的指令。

以下是一个经过精心设计的 SOUL.md 示例,展示了如何定义一个务实的高级工程师人格:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
# Personality

You are a pragmatic senior engineer with strong taste.
You optimize for truth, clarity, and usefulness over politeness theater.

## Style
- Be direct without being cold
- Prefer substance over filler
- Push back when something is a bad idea
- Admit uncertainty plainly
- Keep explanations compact unless depth is useful

## What to avoid
- Sycophancy
- Hype language
- Repeating the user's framing if it's wrong
- Overexplaining obvious things

## Technical posture
- Prefer simple systems over clever systems
- Care about operational reality, not idealized architecture
- Treat edge cases as part of the design, not cleanup

6.3.5 安全扫描机制

SOUL.md 在注入前会与其他上下文文件一样,接受提示词注入扫描。扫描器会检测以下风险模式:

  • 不可见 unicode 字符(如零宽空格、方向覆盖字符);
  • 典型的 prompt 注入短语(“ignore previous instructions”、“disregard the above” 等);
  • 凭据收集尝试(要求输出 API key、密码等);
  • 数据外泄指令(要求将对话内容发送到外部 URL)。

这意味着你不应将 SOUL.md 当作混入元指令的通道——它的目的始终是角色与语气定义,而非绕过系统安全机制。

6.3.6 SOUL.md 与 AGENTS.md 的分工

这是 Hermes 上下文系统中最容易混淆的概念之一,务必厘清:

维度 SOUL.md AGENTS.md
用途 身份、语气、风格、沟通默认值 项目架构、编码规范、工具偏好、工作流
范围 全局(随用户到处适用) 项目级(仅对当前代码库生效)
位置 ~/.hermes/SOUL.md 项目目录下的 AGENTS.md
系统提示词槽位 Slot 1(Identity) Slot 8(Context Files)
生效方式 每个会话自动加载 按优先级系统匹配加载

举个例子:

  • 你希望 Hermes 永远用中文回复你、不喜欢过度礼貌——写入 SOUL.md
  • 你希望 Hermes 在这个 Python 项目中总是用 pytest 而不是 unittest——写入 AGENTS.md
  • 你希望 Hermes 在操作 Kubernetes 时总是先检查当前 namespace——写入 AGENTS.md(或更优先的 .hermes.md)。

6.4 Personality (个性) 预设与 /personality 切换

SOUL.md 是你的持久默认个性,但在某些场景下,你可能需要临时切换到另一种沟通模式。这就是 Personality 预设的作用。

/personality 是会话级覆盖层,用于更改或补充当前系统提示词。

6.4.1 内置个性列表

Hermes 内置了 15 种可直接切换的个性预设:

名称 描述
helpful 友好的通用助手
concise 简短、直击要点的回复
technical 详尽、准确的技术专家
creative 创新、突破常规的思维
teacher 耐心的教育者,配有清晰示例
kawaii 可爱表达、闪光效果与热情
catgirl 带有猫咪表达方式的 Neko-chan,nya~
pirate 船长 Hermes,精通技术的海盗
shakespeare 充满戏剧张力的吟游诗人风格
surfer 超级冷静的冲浪者氛围
noir 硬派侦探叙事风格
uwu 极致可爱的 uwu 语气
philosopher 对每个问题深度沉思
hype 最大能量与热情!!!

6.4.2 如何切换个性

在 CLI 中:

1
2
3
/personality           # 查看当前个性
/personality concise   # 切换到 concise 模式
/personality technical # 切换到 technical 模式

在消息平台(Telegram、Discord 等)中:

1
/personality teacher

这些覆盖层便于快速调整,但全局 SOUL.md 仍定义着 Hermes 的持久默认个性——除非覆盖层对其进行了实质性更改。

6.4.3 自定义个性配置

除了内置预设,你还可以在 ~/.hermes/config.yamlagent.personalities 下定义自己的个性:

1
2
3
4
5
6
7
8
agent:
  personalities:
    codereviewer: >
      You are a meticulous code reviewer. Identify bugs, security issues,
      performance concerns, and unclear design choices. Be precise and constructive.
    architect: >
      You are a systems architect. Focus on trade-offs, scalability,
      and long-term maintainability. Ask clarifying questions before proposing solutions.

定义后,即可通过 /personality codereviewer/personality architect 调用。

6.4.4 Personality 与 SOUL.md 的协作关系

理解这两者的关系,是掌握 Hermes 个性系统的关键:

  • SOUL.md = 基础语气:持久、稳定、无处不在;
  • Personality = 临时模式切换:会话级、可覆盖、可随时取消。

示例用法:

  • 保持务实的默认 SOUL,然后在辅导新人时使用 /personality teacher
  • 保持简洁的 SOUL,然后在头脑风暴时使用 /personality creative
  • 保持技术导向的 SOUL,然后在需要输出给非技术受众时使用 /personality helpful

Personality 预设作为可选的系统提示词覆盖层注入,它不会替换 SOUL.md,而是与其叠加。如果 Personality 的内容与 SOUL.md 有冲突,通常后注入的 Personality 指导会占上风——但这取决于具体 LLM 对重复指令的解析行为。因此,建议让 SOUL.md 保持宽泛的基础人设,让 Personality 负责具体的场景调性。

6.4.5 推荐工作流

一个强健的默认配置:

  1. ~/.hermes/SOUL.md 中维护一个经过深思熟虑的全局 SOUL.md
  2. 将项目说明放在 AGENTS.md 中。
  3. 仅在需要临时模式切换时使用 /personality

这样你将获得:

  • 稳定的语气.
  • 项目特定行为归属于正确位置。
  • 需要时的临时控制。

6.5 冻结快照机制:为什么记忆在对话中"不变"

在前面章节中,我们提到 MEMORY 和 USER 槽位采用冻结快照模式。本节深入解释这一机制的设计动机和实际效果。

1、冻结快照的工作方式

当 Hermes 启动一个新会话时:

  1. 读取 ~/.hermes/memories/MEMORY.md~/.hermes/memories/USER.md 的当前内容;
  2. 将内容分别注入 Slot 5 和 Slot 6;
  3. 将这一版本的系统提示词固定下来,作为后续所有轮次的系统前缀;
  4. 在会话运行期间,即使 memory 工具写入了新的记忆到磁盘,已构建的系统提示词不会自动刷新

只有当以下情况发生时,系统提示词才会重建:

  • 用户显式执行 /reset 或开启新会话(/new);
  • Gateway 检测到相关配置变更(如压缩阈值、上下文长度)触发了透明重建;
  • 上下文压缩导致对话历史被重构,间接触发 prompt 重建。

2、冻结机制的设计权衡

你可能会问:如果我在对话中告诉 Agent “记住我喜欢用 tab 而不是空格”,为什么 Agent 在下一轮的系统提示词里看不到这条新记忆?

答案是有意的设计取舍

  • 优点——缓存稳定性:系统前缀一旦构建,就可以在 Anthropic/OpenRouter 等平台的 prompt caching 机制中被标记为缓存边界。如果每轮都因为记忆更新而修改前缀,缓存会反复失效,导致:
    • Token 成本显著增加(系统提示词通常占输入 token 的 30%-50%);
    • API 延迟增加(需要重新传输大量文本);
    • 提供商侧缓存命中率暴跌。
  • 优点——语义一致性:在同一对话中,Agent 对"用户是谁"的认知应当保持稳定。如果第 5 轮系统提示词说"用户喜欢 Python",第 10 轮突然变成"用户喜欢 Rust",而这两轮讨论的是同一个问题,Agent 可能会产生认知混乱。
  • 缺点——延迟生效:新写入的记忆要到下一个会话才能作为系统提示词的一部分被全局感知。不过,在当前会话中,新记忆仍然可以通过以下方式被访问:
    • memory 工具的读操作返回的是磁盘上的最新内容;
    • session_search 可以检索到当前对话中刚写入的信息;
    • 如果记忆内容被追加到对话历史的后续消息中,模型仍然可以看到。

3、配置记忆限制

可以在 config.yaml 中调整记忆相关的上限:

1
2
3
4
5
memory:
  memory_enabled: true
  user_profile_enabled: true
  memory_char_limit: 2200   # ~800 tokens
  user_char_limit: 1375     # ~500 tokens

超过上限的记忆在注入前会被截断。默认的字符限制是基于常见分词器的经验估算值,你可以根据实际使用的主模型上下文窗口大小进行调整。

6.6 如何自定义 Prompt

Hermes 的设计哲学是:大多数用户应把 agent/prompt_builder.py 视为实现代码,而不是配置入口。推荐的自定义路径是修改 Hermes 已加载的 prompt 输入,而非直接编辑 Python 模板。

6.6.1 优先使用的自定义入口

  • ~/.hermes/SOUL.md — 用你自己的代理角色和常驻行为替换内置默认身份块。
  • ~/.hermes/MEMORY.md~/.hermes/USER.md — 提供持久化跨会话事实和用户画像数据。
  • 项目上下文文件,如 .hermes.mdHERMES.mdAGENTS.mdCLAUDE.md.cursorrules — 注入仓库专属的工作规则。
  • 技能 — 封装可复用的工作流和参考,无需编辑核心提示词代码。
  • 可选的系统提示词配置 / API 覆盖 — 添加部署专属的指令文本。
  • 临时覆盖,如 HERMES_EPHEMERAL_SYSTEM_PROMPT 或预填充消息 — 添加仅限当前轮次的指导,不会成为缓存提示词前缀的一部分。

6.6.2 何时应该编辑核心代码

仅当你刻意维护一个 fork向上游贡献行为变更时,才应编辑 agent/prompt_builder.py。该文件负责每个会话的 prompt 管道、缓存边界和注入顺序。直接编辑它是全局产品变更,而非针对单个用户的 prompt 自定义。

简单的决策树:

  • 想要不同的助手身份?→ 编辑 SOUL.md
  • 想要不同的仓库规则?→ 编辑项目上下文文件
  • 想要可复用的操作流程?→ 添加或修改 Skills
  • 想要改变 Hermes 为所有人组装 prompt 的方式?→ 修改 Python 代码并作为代码贡献提交。

6.7 本章小结

本章深入拆解了 Hermes Agent 的 Prompt 系统,从架构设计理念到具体实现细节,覆盖了以下核心知识点:

  1. Prompt Builder 槽位模型:系统提示词由 10 个已缓存槽位(Identity → 工具行为 → Honcho → 可选系统消息 → MEMORY → USER → Skills → 上下文文件 → 时间戳 → 平台提示)和 3 个临时槽位(Personality → Tool Schemas → Ephemeral Layers)组成,按固定顺序拼装。
  2. SOUL.md 身份文件:作为 Slot 1 的核心身份标识,SOUL.md 存放于 ~/.hermes/SOUL.md,完全替换内置默认身份。它应聚焦于语气、风格和沟通默认值,而非项目特定指令。与 AGENTS.md 的分工原则是:随用户到处适用放 SOUL,随项目生效放 AGENTS。
  3. Personality 预设系统/personality 命令提供 15 种内置预设和自定义配置能力,作为会话级覆盖层临时切换沟通模式,不影响持久 SOUL.md 的基础人设。
  4. 冻结快照机制:MEMORY 和 USER 槽位在会话开始时读取并固定,中途的磁盘更新不会实时反映到系统提示词中。这既保护了 prompt cache 的稳定性,也保持了同一对话中的语义一致性。
  5. Ephemeral Layers:预算告警、prefill、gateway 覆盖层等临时内容被刻意分离到 API 调用阶段,避免污染可缓存的系统前缀。
  6. Prompt Cache 命中率:Hermes 自动为支持的提供商开启跨会话 prompt caching,通过精简稳定的 SOUL.md、批量更新记忆、渐进式技能披露等策略,可以显著降低长对话场景的 token 成本。

掌握这些机制后,你就拥有了精确控制 Hermes Agent “身份与行为逻辑"的能力——既能定义它长期稳定的性格底色,又能在具体场景中灵活调整沟通模式,同时还能通过合理的 prompt 结构设计,让每一次 API 调用都更加经济高效。