乐于分享
好东西不私藏

Hermes Agent 源码-System Prompt 构建引擎与工具执行安全层

Hermes Agent 源码-System Prompt 构建引擎与工具执行安全层

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技术推荐官」获取更多技术干货

相关学习资料