跳到主要内容
本文尚未翻译

本文暂时显示英文原文,中文翻译正在进行中。翻译完成后将自动更新。

原文链接:English Version

Prompt Assembly

Hermes deliberately separates:

  • cached system prompt state
  • ephemeral API-call-time additions

This is one of the most important design choices in the project because it affects:

  • token usage
  • prompt caching effectiveness
  • session continuity
  • memory correctness

Primary files:

  • run_agent.py
  • agent/prompt_builder.py
  • tools/memory_tool.py

Cached system prompt layers

The cached system prompt is assembled in roughly this order:

  1. agent identity — SOUL.md from HERMES_HOME when available, otherwise falls back to DEFAULT_AGENT_IDENTITY in prompt_builder.py
  2. tool-aware behavior guidance
  3. Honcho static block (when active)
  4. optional system message
  5. frozen MEMORY snapshot
  6. frozen USER profile snapshot
  7. skills index
  8. context files (AGENTS.md, .cursorrules, .cursor/rules/*.mdc) — SOUL.md is not included here when it was already loaded as the identity in step 1
  9. timestamp / optional session ID
  10. platform hint

When skip_context_files is set (e.g., subagent delegation), SOUL.md is not loaded and the hardcoded DEFAULT_AGENT_IDENTITY is used instead.

Concrete example: assembled system prompt

Here is a simplified view of what the final system prompt looks like when all layers are present (comments show the source of each section):

# Layer 1: Agent Identity (from ~/.hermes/SOUL.md)
You are Hermes, an AI assistant created by Nous Research.
You are an expert software engineer and researcher.
You value correctness, clarity, and efficiency.
...

# Layer 2: Tool-aware behavior guidance
You have persistent memory across sessions. Save durable facts using
the memory tool: user preferences, environment details, tool quirks,
and stable conventions. Memory is injected into every turn, so keep
it compact and focused on facts that will still matter later.
...
When the user references something from a past conversation or you
suspect relevant cross-session context exists, use session_search
to recall it before asking them to repeat themselves.

# Tool-use enforcement (for GPT/Codex models only)
You MUST use your tools to take action — do not describe what you
would do or plan to do without actually doing it.
...

# Layer 3: Honcho static block (when active)
[Honcho personality/context data]

# Layer 4: Optional system message (from config or API)
[User-configured system message override]

# Layer 5: Frozen MEMORY snapshot
## Persistent Memory
- User prefers Python 3.12, uses pyproject.toml
- Default editor is nvim
- Working on project "atlas" in ~/code/atlas
- Timezone: US/Pacific

# Layer 6: Frozen USER profile snapshot
## User Profile
- Name: Alice
- GitHub: alice-dev

# Layer 7: Skills index
## 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: Structured code review workflow
- test-driven-development: TDD methodology
research:
- arxiv: Search and summarize arXiv papers
</available_skills>

# Layer 8: Context files (from project directory)
# Project Context
The following project context files have been loaded and should be followed:

## AGENTS.md
This is the atlas project. Use pytest for testing. The main
entry point is src/atlas/main.py. Always run `make lint` before
committing.

# Layer 9: Timestamp + session
Current time: 2026-03-30T14:30:00-07:00
Session: abc123

# Layer 10: Platform hint
You are a CLI AI Agent. Try not to use markdown but simple text
renderable inside a terminal.

How SOUL.md appears in the prompt

SOUL.md lives at ~/.hermes/SOUL.md and serves as the agent's identity — the very first section of the system prompt. The loading logic in prompt_builder.py works as follows:

# From agent/prompt_builder.py (simplified)
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") # Security scan
content = _truncate_content(content, "SOUL.md") # Cap at 20k chars
return content

When load_soul_md() returns content, it replaces the hardcoded DEFAULT_AGENT_IDENTITY. The build_context_files_prompt() function is then called with skip_soul=True to prevent SOUL.md from appearing twice (once as identity, once as a context file).

If SOUL.md doesn't exist, the system falls back to:

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.

How context files are injected

build_context_files_prompt() uses a priority system — only one project context type is loaded (first match wins):

# From agent/prompt_builder.py (simplified)
def build_context_files_prompt(cwd=None, skip_soul=False):
cwd_path = Path(cwd).resolve()

# Priority: first match wins — only ONE project context loaded
project_context = (
_load_hermes_md(cwd_path) # 1. .hermes.md / HERMES.md (walks to git root)
or _load_agents_md(cwd_path) # 2. AGENTS.md (cwd only)
or _load_claude_md(cwd_path) # 3. CLAUDE.md (cwd only)
or _load_cursorrules(cwd_path) # 4. .cursorrules / .cursor/rules/*.mdc
)

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

# SOUL.md from HERMES_HOME (independent of project context)
if not skip_soul:
soul_content = load_soul_md()
if soul_content:
sections.append(soul_content)

if not sections:
return ""

return (
"# Project Context\n\n"
"The following project context files have been loaded "
"and should be followed:\n\n"
+ "\n".join(sections)
)

Context file discovery details

PriorityFilesSearch scopeNotes
1.hermes.md, HERMES.mdCWD up to git rootHermes-native project config
2AGENTS.mdCWD onlyCommon agent instruction file
3CLAUDE.mdCWD onlyClaude Code compatibility
4.cursorrules, .cursor/rules/*.mdcCWD onlyCursor compatibility

All context files are:

  • Security scanned — checked for prompt injection patterns (invisible unicode, "ignore previous instructions", credential exfiltration attempts)
  • Truncated — capped at 20,000 characters using 70/20 head/tail ratio with a truncation marker
  • YAML frontmatter stripped.hermes.md frontmatter is removed (reserved for future config overrides)

API-call-time-only layers

These are intentionally not persisted as part of the cached system prompt:

  • ephemeral_system_prompt
  • prefill messages
  • gateway-derived session context overlays
  • later-turn Honcho recall injected into the current-turn user message

This separation keeps the stable prefix stable for caching.

Memory snapshots

Local memory and user profile data are injected as frozen snapshots at session start. Mid-session writes update disk state but do not mutate the already-built system prompt until a new session or forced rebuild occurs.

Context files

agent/prompt_builder.py scans and sanitizes project context files using a priority system — only one type is loaded (first match wins):

  1. .hermes.md / HERMES.md (walks to git root)
  2. AGENTS.md (CWD at startup; subdirectories discovered progressively during the session via agent/subdirectory_hints.py)
  3. CLAUDE.md (CWD only)
  4. .cursorrules / .cursor/rules/*.mdc (CWD only)

SOUL.md is loaded separately via load_soul_md() for the identity slot. When it loads successfully, build_context_files_prompt(skip_soul=True) prevents it from appearing twice.

Long files are truncated before injection.

Skills index

The skills system contributes a compact skills index to the prompt when skills tooling is available.

Why prompt assembly is split this way

The architecture is intentionally optimized to:

  • preserve provider-side prompt caching
  • avoid mutating history unnecessarily
  • keep memory semantics understandable
  • let gateway/ACP/CLI add context without poisoning persistent prompt state