提示词组装
Hermes 有意将以下内容分离:
- 缓存的系统提示词状态
- 临时的 API 调用时附加内容
这是项目中最重要的设计选择之一,因为它影响:
- token 用量
- 提示词缓存效果
- 会话连续性
- 内存正确性
主要文件:
run_agent.pyagent/prompt_builder.pytools/memory_tool.py
缓存的系统提示图层
缓存的系统提示词大致按以下顺序组装:
- 代理身份 —
HERMES_HOME中的SOUL.md(如果可用),否则回退到prompt_builder.py中的DEFAULT_AGENT_IDENTITY - 工具感知的行为指导
- Honcho 静态块(激活时)
- 可选的系统消息
- 冻结的 MEMORY 快照
- 冻结的 USER 配置快照
- 技能索引
- 上下文文件(
AGENTS.md、.cursorrules、.cursor/rules/*.mdc)— 如果已在步骤 1 中作为身份加载,则此处不包含 SOUL.md - 时间戳 / 可选的会话 ID
- 平台提示
当设置了 skip_context_files(例如子代理委派)时,不加载 SOUL.md,使用硬编码的 DEFAULT_AGENT_IDENTITY 代替。
具体示例:组装后的系统提示词
以下是所有图层都存在时最终系统提示词的简化视图(注释显示每个部分的来源):
# 第 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
- 正在 ~/code/atlas 上开发项目 "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 代理。尽量不使用 markdown,使用
终端内可渲染的纯文本。
SOUL.md 在提示词中的出现方式
SOUL.md 位于 ~/.hermes/SOUL.md,作为代理的身份——系统提示词的第一部分。prompt_builder.py 中的加载逻辑如下:
# 来自 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") # 上限 2 万字符
return content当 load_soul_md() 返回内容时,它会替换硬编码的 DEFAULT_AGENT_IDENTITY。然后调用 build_context_files_prompt() 时使用 skip_soul=True,以防止 SOUL.md 出现两次(一次作为身份,一次作为上下文文件)。
如果 SOUL.md 不存在,系统回退到:
你是 Hermes Agent,由 Nous Research 创建的智能 AI 助手。
你乐于助人、知识渊博且直接。你帮助用户完成各种
任务,包括回答问题、编写和编辑代码、分析信息、
创意工作以及通过工具执行操作。你清晰地沟通,
在适当的时候承认不确定性,并优先追求真正有用而不是
啰嗦,除非下面另有指示。在探索和调查中要有针对性和高效性。
上下文文件的注入方式
build_context_files_prompt() 使用优先级系统——仅加载一种项目上下文类型(首个匹配胜出):
# 来自 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)
# 来自 HERMES_HOME 的 SOUL.md(独立于项目上下文)
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)
)上下文文件发现详情
| 优先级 | 文件 | 搜索范围 | 备注 |
|---|---|---|---|
| 1 | .hermes.md、HERMES.md | 当前工作目录向上到 git 根目录 | Hermes 原生项目配置 |
| 2 | AGENTS.md | 仅当前工作目录 | 通用代理指令文件 |
| 3 | CLAUDE.md | 仅当前工作目录 | Claude Code 兼容性 |
| 4 | .cursorrules、.cursor/rules/*.mdc | 仅当前工作目录 | Cursor 兼容性 |
所有上下文文件:
- 经过安全扫描 — 检查提示注入模式(不可见 Unicode、“忽略之前的指令”、凭据窃取尝试)
- 被截断 — 上限为 20,000 字符,使用 70/20 头部/尾部比例并带有截断标记
- YAML frontmatter 被剥离 —
.hermes.md的 frontmatter 被移除(保留用于未来的配置覆盖)
仅 API 调用时附加的图层
这些有意不作为缓存系统提示词的一部分持久化:
ephemeral_system_prompt- 预填充消息
- 网关派生的会话上下文覆盖
- 后续轮次的 Honcho 召回注入到当前轮次的用户消息中
这种分离保持了稳定前缀的可缓存性。
内存快照
本地内存和用户配置数据在会话启动时作为冻结快照注入。会话中的写操作会更新磁盘状态,但在新会话或强制重建之前不会改变已构建的系统提示词。
上下文文件
agent/prompt_builder.py 使用优先级系统扫描和清理项目上下文文件——仅加载一种类型(首个匹配胜出):
.hermes.md/HERMES.md(遍历到 git 根目录)AGENTS.md(启动时的当前工作目录;子目录在会话期间通过agent/subdirectory_hints.py逐步发现)CLAUDE.md(仅当前工作目录).cursorrules/.cursor/rules/*.mdc(仅当前工作目录)
SOUL.md 通过 load_soul_md() 单独加载用于身份槽。当它成功加载时,build_context_files_prompt(skip_soul=True) 防止它出现两次。
长文件在注入前会被截断。
技能索引
当技能工具可用时,技能系统向提示词贡献一个紧凑的技能索引。
支持的提示词定制接口
大多数用户应将 agent/prompt_builder.py 视为实现代码,而非配置接口。支持的定制方式是通过修改 Hermes 已加载的提示词输入,而不是直接编辑 Python 模板。
优先使用这些接口
~/.hermes/SOUL.md— 用您自己的代理角色和常驻行为替换内置的默认身份块。~/.hermes/MEMORY.md和~/.hermes/USER.md— 提供应被快照到新会话中的持久化跨会话事实和用户配置数据。- 项目上下文文件,如
.hermes.md、HERMES.md、AGENTS.md、CLAUDE.md或.cursorrules— 注入仓库特定的工作规则。 - 技能 — 打包可重用工作流和参考信息,无需编辑核心提示词代码。
- 可选的系统提示词配置 / API 覆盖 — 添加部署特定的指令文本,无需分支 Hermes。
- 临时覆盖,如
HERMES_EPHEMERAL_SYSTEM_PROMPT或预填充消息 — 添加轮次范围内的指导,不应成为缓存提示词前缀的一部分。
何时改为编辑代码
仅在您有意维护分支或贡献上游行为变更时,才编辑 agent/prompt_builder.py。该文件为每个会话组装提示词管道、缓存边界和注入顺序。直接编辑是全局产品变更,而非面向用户的提示词定制。
换句话说:
- 如果您想要不同的助手身份,请编辑
SOUL.md - 如果您想要不同的仓库规则,请编辑项目上下文文件
- 如果您想要可重用的操作流程,请添加或修改技能
- 如果您想要改变 Hermes 为所有人组装提示词的方式,请更改 Python 并将其视为代码贡献
为什么提示词组装要这样拆分
该架构有意优化以:
- 保持提供商侧的提示词缓存
- 避免不必要地修改历史记录
- 保持内存语义清晰易懂
- 让网关/ACP/CLI 添加上下文而不污染持久化提示词状态