Hermes Agent 源码解析
第 15 讲:System Prompt 构建引擎与工具执行安全层
基于 Hermes Agent v0.16.0 源码 · 2026-06-28
一、System Prompt:Agent 的"灵魂"
前十四讲我们覆盖了 Hermes 从核心架构到认证系统的全部关键路径。这一讲我们进入 Hermes 的System Prompt 构建引擎——每次对话开始前,Hermes 如何组装一个长达数千字的系统提示词,并保证它在整个会话生命周期内字节级稳定(byte-stable),以维持上游 LLM 的 prompt cache 命中。
📦 本讲核心文件
agent/system_prompt.py(446 行)— 三层提示词组装
agent/prompt_builder.py(1624 行)— 身份、技能索引、上下文文件
agent/prompt_caching.py(79 行)— Anthropic prompt caching
agent/tool_executor.py(1428 行)— 工具执行调度
agent/tool_guardrails.py(475 行)— 工具调用安全护栏
二、三层 System Prompt 架构
system_prompt.py 实现了精妙的三层架构——stable(稳定层)、context(上下文层)、volatile(易变层),通过 \n\n 拼接。这种分层设计使得只有 volatile 层在每轮对话中变化,stable 层和 context 层在整个会话中保持不变,最大化 prompt cache 命中率。
1. Stable 层:Agent 身份与行为准则
📄 system_prompt.py (第 85-183 行)
def build_system_prompt_parts(agent, system_message=None):
stable_parts: List[str] = []
# ── 身份层 ──
# 优先加载 SOUL.md,回退到 DEFAULT_AGENT_IDENTITY
_soul_content = _r.load_soul_md()
if _soul_content:
stable_parts.append(_soul_content)
else:
stable_parts.append(DEFAULT_AGENT_IDENTITY)
# ── 帮助指南 ──
stable_parts.append(HERMES_AGENT_HELP_GUIDANCE)
# ── 任务完成指南(防幻觉)──
if agent._task_completion_guidance and agent.valid_tool_names:
stable_parts.append(TASK_COMPLETION_GUIDANCE)
# ── 工具感知行为指南 ──
tool_guidance = []
if "memory" in agent.valid_tool_names:
tool_guidance.append(MEMORY_GUIDANCE)
if "session_search" in agent.valid_tool_names:
tool_guidance.append(SESSION_SEARCH_GUIDANCE)
if "skill_manage" in agent.valid_tool_names:
tool_guidance.append(SKILLS_GUIDANCE)
# ── 工具使用强制执行 ──
# 告诉模型必须真正调用工具,而非描述意图
if _inject:
stable_parts.append(TOOL_USE_ENFORCEMENT_GUIDANCE)
# ── 模型家族特定指南 ──
if "gemini" in model_lower:
stable_parts.append(GOOGLE_MODEL_OPERATIONAL_GUIDANCE)
if "gpt" in model_lower or "grok" in model_lower:
stable_parts.append(OPENAI_MODEL_EXECUTION_GUIDANCE)
设计亮点:工具感知行为指南是动态注入的——只有当对应工具被加载时,才注入相关指南。这避免了"告诉模型使用 memory 工具"但 memory 工具实际上被禁用的情况。
2. Context 层:项目上下文文件
Context 层自动发现并注入项目上下文文件,搜索顺序为:cwd → parent → git root,支持 .hermes.md、AGENTS.md、.cursorrules 等。
📄 prompt_builder.py (第 41-61 行)
def _scan_context_content(content, filename):
"""扫描上下文文件,检测 prompt 注入攻击"""
findings = _scan_for_threats(content, scope="context")
if findings:
logger.warning("Context file %s blocked: %s", filename,
", ".join(findings))
return f"[BLOCKED: {filename} contained potential " \
f"prompt injection. Content not loaded.]"
return content
安全设计:所有上下文文件在注入前都经过 threat_patterns.py 扫描,检测经典注入 + promptware/C2 模式 + 角色扮演劫持。匹配的内容被直接 BLOCK,不会进入系统提示词。
3. Volatile 层:每轮变化的动态内容
System Prompt 三层结构
STABLE 层(会话生命周期不变)
🔹 SOUL.md / DEFAULT_AGENT_IDENTITY
🔹 HERMES_AGENT_HELP_GUIDANCE
🔹 TASK_COMPLETION_GUIDANCE
🔹 工具感知行为指南(memory/session/skill)
🔹 TOOL_USE_ENFORCEMENT_GUIDANCE
🔹 模型家族特定指南(Gemini/OpenAI/xAI)
🔹 Skills 索引(可用技能列表)
🔹 环境提示(OS/WSL/平台)
🔹 平台提示(Telegram/Discord/微信等)
CONTEXT 层(项目上下文,会话不变)
🔹 AGENTS.md / .cursorrules
🔹 .hermes.md(项目级指令)
🔹 caller-supplied system_message
VOLATILE 层(每轮可能变化)
🔹 Memory 快照(~/.hermes/memory/)
🔹 USER.md 用户画像
🔹 外部记忆 Provider 块
🔹 时间戳/会话ID/模型/提供商信息
三、Prompt Caching:Anthropic system_and_3 策略
prompt_caching.py(仅 79 行)实现了 Anthropic 的 prompt caching 策略——在消息中注入 cache_control 断点,使多轮对话的输入 token 成本降低约 75%。
📄 prompt_caching.py (第 49-79 行)
def apply_anthropic_cache_control(
api_messages, cache_ttl="5m", native_anthropic=False
):
"""system_and_3 策略:4 个 cache_control 断点
断点位置:
1. System prompt(如果存在)
2-4. 最后 3 条非 system 消息
所有断点使用相同 TTL(5m 或 1h)。
"""
messages = copy.deepcopy(api_messages)
marker = _build_marker(cache_ttl)
# 断点 1: System prompt
if messages[0].get("role") == "system":
_apply_cache_marker(messages[0], marker)
# 断点 2-4: 最后 3 条非 system 消息
non_sys = [i for i in range(len(messages))
if messages[i].get("role") != "system"]
for idx in non_sys[-remaining:]:
_apply_cache_marker(messages[idx], marker)
return messages
为什么是 4 个断点?Anthropic API 允许最多 4 个 cache_control 断点。System prompt + 最后 3 条消息覆盖了对话中最大化的前缀缓存范围。
四、Tool Executor:工具执行调度器
tool_executor.py(1428 行)是工具调用的执行引擎,支持顺序执行和并行执行两种模式,最多 8 个并发工作线程。
1. 执行模式选择
工具执行调度流程
LLM 返回 tool_calls
⬇️
检测并行候选工具
条件:工具间无依赖 + 非破坏性命令 + 非浏览器操作
⬇️
并行执行
8 线程并发
顺序执行
单线程
⬇️
结果收集 + 分类
成功/失败/取消 + 多模态结果特殊处理
⬇️
工具结果存储
持久化到 SQLite + 执行 turn budget 检查
2. 工具作用域安全检查
每个会话(子代理、Kanban worker、网关会话)都有独立的工具作用域。Tool Executor 在分发前验证工具名是否在会话的授权列表中:
📄 tool_executor.py (第 135-149 行)
def _tool_search_scoped_names(agent):
"""返回会话可调用的工具名集合
防止受限工具集的会话(子代理、Kanban worker、
网关会话)访问未被授权的工具。
结果缓存在 agent 上,当工具注册表代
号变化时(如 MCP 服务器重连)自动刷新。
"""
cached = getattr(agent, "_scoped_tool_names", None)
if cached is not None:
return cached
# 构建授权工具名集合...
return frozenset(valid_tool_names)
五、Tool Guardrails:工具调用安全护栏
tool_guardrails.py(475 行)实现了工具调用循环检测——防止模型陷入无限重试、无进度循环或破坏性操作。
1. 工具分类:幂等 vs 变异
📄 tool_guardrails.py (第 20-60 行)
IDEMPOTENT_TOOL_NAMES = frozenset({
"read_file", "search_files", "web_search",
"web_extract", "session_search",
"browser_snapshot", "browser_console",
"mcp_filesystem_read_file", ...
})
MUTATING_TOOL_NAMES = frozenset({
"terminal", "execute_code", "write_file",
"patch", "todo", "memory", "skill_manage",
"browser_click", "browser_type",
"send_message", "cronjob", "delegate_task",
})
2. 护栏配置与阈值
📄 tool_guardrails.py (第 63-124 行)
@dataclass(frozen=True)
class ToolCallGuardrailConfig:
"""工具调用循环检测阈值"""
warnings_enabled: bool = True
hard_stop_enabled: bool = False
# ── 精确失败检测 ──
exact_failure_warn_after: int = 2 # 2次相同失败后警告
exact_failure_block_after: int = 5 # 5次后阻断
# ── 同工具失败检测 ──
same_tool_failure_warn_after: int = 3 # 3次后警告
same_tool_failure_halt_after: int = 8 # 8次后停止
# ── 无进度检测(幂等工具)──
no_progress_warn_after: int = 2 # 2次无进度后警告
no_progress_block_after: int = 5 # 5次后阻断
3. 护栏决策机
📄 tool_guardrails.py (第 144-151 行)
@dataclass(frozen=True)
class ToolGuardrailDecision:
"""工具调用护栏决策"""
action: str = "allow" # allow | warn | block | halt
code: str = "allow"
message: str = ""
四种决策:
| 决策 | 含义 | 触发条件 |
|---|---|---|
| allow | 放行,正常执行 | 工具调用在正常范围内 |
| warn | 警告,注入指导文本 | 检测到可能的循环模式 |
| block | 阻断本次调用 | 超过失败阈值 |
| halt | 停止整个 turn | 严重循环,需要人工干预 |
六、架构全景图
System Prompt 构建引擎
STABLE 层
SOUL.md + 帮助指南 + 工具行为指南 + Skills 索引
CONTEXT 层
AGENTS.md + .cursorrules + .hermes.md
VOLATILE 层
Memory + USER.md + 时间戳 + Provider
⬇️
拼接 (byte-stable)
⬇️
Anthropic Cache Control 注入
system_and_3 策略 · 4 断点 · TTL 5m/1h
⬇️
Tool Executor + Guardrails
作用域检查
子代理/MCP/Gateway 权限
并行/顺序调度
8 线程并发 + 依赖分析
护栏检测
精确失败/同工具失败/无进度
⬇️
决策机
allow → warn → block → halt
七、设计模式总结
System Prompt + 工具执行层使用的设计模式:
① 分层架构 (Layered Architecture)
Stable/Context/Volatile 三层,只有 volatile
层每轮变化,最大化 prompt cache 命中率。
② 字节稳定性 (Byte-Stability)
System prompt 在会话生命周期内不变,
只有上下文压缩触发重建。
③ 动态注入 (Dynamic Injection)
工具感知指南只在对应工具加载时注入,
避免"幽灵工具"误导模型。
④ 安全扫描 (Security Scanning)
所有上下文文件注入前经过 threat_patterns 扫描,
检测 prompt 注入 + promptware/C2 模式。
⑤ 并行调度 (Parallel Dispatch)
8 线程并发执行,依赖分析决定并行候选,
破坏性命令强制串行。
⑥ 护栏决策机 (Guardrail State Machine)
allow → warn → block → halt 四级决策,
阈值可配置(config.yaml)。
⑦ 作用域隔离 (Scope Isolation)
每个会话有独立工具授权列表,
子代理/Kanban/Gateway 各自受限。
📖 系列导航
← 上一讲:第 14 讲:Hermes Agent 源码-预算控制与限流
下一讲 →:第 16 讲:Tool Executor 深度解析与安全护栏
📚 全系列:Hermes Agent 源码解析 20 讲 + OpenCode 源码解析
Hermes Agent 源码解析 · 第 15 讲 · 2026-06-28
基于 v0.16.0 · NousResearch/hermes-agent
关注公众号「AI技术推荐官」获取更多技术干货
夜雨聆风