【hermes-agent】Hermes Agent Prompt 构建流程超深度分析

Hermes Agent Prompt 构建流程超深度分析

源码目录 :/home/lin/project/src/github.com/hermes-agent/

核心文件 :agent/system_prompt.py(761行)、agent/prompt_builder.py(1637行)、agent/turn_context.py(1146行)、agent/turn_request_assembly.py(261行)

分析基准:严格基于源码逐行解析


目录

  • [一、Prompt 构建全景架构](#一、Prompt 构建全景架构)
  • [二、System Prompt 三层架构](#二、System Prompt 三层架构)
    • [2.1 三层划分原理](#2.1 三层划分原理)
    • [2.2 Stable Tier 稳定层](#2.2 Stable Tier 稳定层)
    • [2.3 Context Tier 上下文层](#2.3 Context Tier 上下文层)
    • [2.4 Volatile Tier 易变层](#2.4 Volatile Tier 易变层)
    • [2.5 build_system_prompt_parts() 完整组装流程](#2.5 build_system_prompt_parts() 完整组装流程)
  • 三、各组件深度解析
    • [3.1 身份与灵魂 --- _identity_parts()](#3.1 身份与灵魂 — _identity_parts())
    • [3.2 行为指导 --- _guidance_parts()](#3.2 行为指导 — _guidance_parts())
    • [3.3 技能索引 --- _skills_prompt()](#3.3 技能索引 — _skills_prompt())
    • [3.4 编码姿态 --- _coding_parts()](#3.4 编码姿态 — _coding_parts())
    • [3.5 上下文文件 --- _context_files_part()](#3.5 上下文文件 — _context_files_part())
    • [3.6 记忆系统 --- _memory_parts()](#3.6 记忆系统 — _memory_parts())
    • [3.7 平台提示 --- platform_hint()](#3.7 平台提示 — platform_hint())
    • [3.8 时间戳行 --- _timestamp_line()](#3.8 时间戳行 — _timestamp_line())
    • [3.9 插件段落 --- _frozen_plugin_prompt_sections()](#3.9 插件段落 — _frozen_plugin_prompt_sections())
  • [四、Per-Turn Prompt 构建 --- build_turn_context()](#四、Per-Turn Prompt 构建 — build_turn_context())
  • [五、API 请求组装 --- assemble_api_request()](#五、API 请求组装 — assemble_api_request())
  • [六、Prompt Cache 不变性保证](#六、Prompt Cache 不变性保证)
  • 七、完整端到端流程
  • 八、设计亮点与关键决策

一、Prompt 构建全景架构

Hermes Agent 的 Prompt 构建分为两个阶段:

  1. Session 级 System Prompt 构建:每会话一次,缓存复用,仅在上下文压缩时重建
  2. Per-Turn API 请求组装:每轮对话一次,将 system prompt + 对话历史 + 临时注入组合为 API 请求

#mermaid-svg-DDtkFqgCiL98JrqM{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-DDtkFqgCiL98JrqM .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-DDtkFqgCiL98JrqM .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-DDtkFqgCiL98JrqM .error-icon{fill:#552222;}#mermaid-svg-DDtkFqgCiL98JrqM .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-DDtkFqgCiL98JrqM .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-DDtkFqgCiL98JrqM .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-DDtkFqgCiL98JrqM .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-DDtkFqgCiL98JrqM .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-DDtkFqgCiL98JrqM .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-DDtkFqgCiL98JrqM .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-DDtkFqgCiL98JrqM .marker{fill:#333333;stroke:#333333;}#mermaid-svg-DDtkFqgCiL98JrqM .marker.cross{stroke:#333333;}#mermaid-svg-DDtkFqgCiL98JrqM svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-DDtkFqgCiL98JrqM p{margin:0;}#mermaid-svg-DDtkFqgCiL98JrqM .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-DDtkFqgCiL98JrqM .cluster-label text{fill:#333;}#mermaid-svg-DDtkFqgCiL98JrqM .cluster-label span{color:#333;}#mermaid-svg-DDtkFqgCiL98JrqM .cluster-label span p{background-color:transparent;}#mermaid-svg-DDtkFqgCiL98JrqM .label text,#mermaid-svg-DDtkFqgCiL98JrqM span{fill:#333;color:#333;}#mermaid-svg-DDtkFqgCiL98JrqM .node rect,#mermaid-svg-DDtkFqgCiL98JrqM .node circle,#mermaid-svg-DDtkFqgCiL98JrqM .node ellipse,#mermaid-svg-DDtkFqgCiL98JrqM .node polygon,#mermaid-svg-DDtkFqgCiL98JrqM .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-DDtkFqgCiL98JrqM .rough-node .label text,#mermaid-svg-DDtkFqgCiL98JrqM .node .label text,#mermaid-svg-DDtkFqgCiL98JrqM .image-shape .label,#mermaid-svg-DDtkFqgCiL98JrqM .icon-shape .label{text-anchor:middle;}#mermaid-svg-DDtkFqgCiL98JrqM .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-DDtkFqgCiL98JrqM .rough-node .label,#mermaid-svg-DDtkFqgCiL98JrqM .node .label,#mermaid-svg-DDtkFqgCiL98JrqM .image-shape .label,#mermaid-svg-DDtkFqgCiL98JrqM .icon-shape .label{text-align:center;}#mermaid-svg-DDtkFqgCiL98JrqM .node.clickable{cursor:pointer;}#mermaid-svg-DDtkFqgCiL98JrqM .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-DDtkFqgCiL98JrqM .arrowheadPath{fill:#333333;}#mermaid-svg-DDtkFqgCiL98JrqM .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-DDtkFqgCiL98JrqM .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-DDtkFqgCiL98JrqM .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-DDtkFqgCiL98JrqM .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-DDtkFqgCiL98JrqM .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-DDtkFqgCiL98JrqM .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-DDtkFqgCiL98JrqM .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-DDtkFqgCiL98JrqM .cluster text{fill:#333;}#mermaid-svg-DDtkFqgCiL98JrqM .cluster span{color:#333;}#mermaid-svg-DDtkFqgCiL98JrqM div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-DDtkFqgCiL98JrqM .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-DDtkFqgCiL98JrqM rect.text{fill:none;stroke-width:0;}#mermaid-svg-DDtkFqgCiL98JrqM .icon-shape,#mermaid-svg-DDtkFqgCiL98JrqM .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-DDtkFqgCiL98JrqM .icon-shape p,#mermaid-svg-DDtkFqgCiL98JrqM .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-DDtkFqgCiL98JrqM .icon-shape .label rect,#mermaid-svg-DDtkFqgCiL98JrqM .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-DDtkFqgCiL98JrqM .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-DDtkFqgCiL98JrqM .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-DDtkFqgCiL98JrqM :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 三层Tier结构
阶段2: Per-Turn API请求组装
阶段1: Session级 System Prompt
压缩触发
产生TurnContext
产生api_messages
注入缓存断点
active_system_prompt
active_system_prompt
build_system_prompt(agent, system_message)
缓存到 agent._cached_system_prompt
压缩后重建 invalidate_system_prompt()
build_turn_context()
build_api_messages()
assemble_api_request()
API调用
Stable Tier

身份+指导+编码姿态
Context Tier

系统消息+项目文件+工作区
Volatile Tier

技能+记忆+时间戳

核心文件职责矩阵:

文件 行数 职责
agent/system_prompt.py 761 System Prompt 三层组装、缓存管理、重建
agent/prompt_builder.py 1637 所有 Prompt 片段的常量定义、技能索引构建、上下文文件加载
agent/turn_context.py 1146 Per-Turn 上下文构建、API messages 构建、预检压缩
agent/turn_request_assembly.py 261 单次 API 请求组装:消息克隆、MoA、缓存断点注入
agent/coding_context.py 591 编码姿态探测(git状态、语言检测、工作区快照)
agent/memory_manager.py 836 记忆系统 prompt 块构建
agent/skill_utils.py 786 技能发现、过滤、索引构建
hermes_cli/default_soul.py 72 默认灵魂文件(SOUL.md)处理
hermes_cli/personality.py 141 人格配置

二、System Prompt 三层架构

2.1 三层划分原理

源码位置 :agent/system_prompt.py 行1-8(docstring)

复制代码
Three tiers are joined with "\n\n": ``stable`` (identity, guidance, env hints,
coding brief, platform hints), ``context`` (workspace snapshot, caller
``system_message``, context files) and ``volatile`` (skills index, memory,
USER.md, external memory provider, timestamp line).

三层划分的核心目的是Prompt Cache 前缀稳定性:

  • stable:会话期间不变的部分,放在最前面,保证 prefix cache 命中
  • context:项目/工作区相关,同一会话内通常不变
  • volatile:每会话可能变化的部分(技能、记忆、时间戳),放在最后

#mermaid-svg-7cz1oHK4dEwPQYOK{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-7cz1oHK4dEwPQYOK .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-7cz1oHK4dEwPQYOK .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-7cz1oHK4dEwPQYOK .error-icon{fill:#552222;}#mermaid-svg-7cz1oHK4dEwPQYOK .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-7cz1oHK4dEwPQYOK .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-7cz1oHK4dEwPQYOK .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-7cz1oHK4dEwPQYOK .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-7cz1oHK4dEwPQYOK .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-7cz1oHK4dEwPQYOK .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-7cz1oHK4dEwPQYOK .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-7cz1oHK4dEwPQYOK .marker{fill:#333333;stroke:#333333;}#mermaid-svg-7cz1oHK4dEwPQYOK .marker.cross{stroke:#333333;}#mermaid-svg-7cz1oHK4dEwPQYOK svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-7cz1oHK4dEwPQYOK p{margin:0;}#mermaid-svg-7cz1oHK4dEwPQYOK .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-7cz1oHK4dEwPQYOK .cluster-label text{fill:#333;}#mermaid-svg-7cz1oHK4dEwPQYOK .cluster-label span{color:#333;}#mermaid-svg-7cz1oHK4dEwPQYOK .cluster-label span p{background-color:transparent;}#mermaid-svg-7cz1oHK4dEwPQYOK .label text,#mermaid-svg-7cz1oHK4dEwPQYOK span{fill:#333;color:#333;}#mermaid-svg-7cz1oHK4dEwPQYOK .node rect,#mermaid-svg-7cz1oHK4dEwPQYOK .node circle,#mermaid-svg-7cz1oHK4dEwPQYOK .node ellipse,#mermaid-svg-7cz1oHK4dEwPQYOK .node polygon,#mermaid-svg-7cz1oHK4dEwPQYOK .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-7cz1oHK4dEwPQYOK .rough-node .label text,#mermaid-svg-7cz1oHK4dEwPQYOK .node .label text,#mermaid-svg-7cz1oHK4dEwPQYOK .image-shape .label,#mermaid-svg-7cz1oHK4dEwPQYOK .icon-shape .label{text-anchor:middle;}#mermaid-svg-7cz1oHK4dEwPQYOK .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-7cz1oHK4dEwPQYOK .rough-node .label,#mermaid-svg-7cz1oHK4dEwPQYOK .node .label,#mermaid-svg-7cz1oHK4dEwPQYOK .image-shape .label,#mermaid-svg-7cz1oHK4dEwPQYOK .icon-shape .label{text-align:center;}#mermaid-svg-7cz1oHK4dEwPQYOK .node.clickable{cursor:pointer;}#mermaid-svg-7cz1oHK4dEwPQYOK .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-7cz1oHK4dEwPQYOK .arrowheadPath{fill:#333333;}#mermaid-svg-7cz1oHK4dEwPQYOK .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-7cz1oHK4dEwPQYOK .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-7cz1oHK4dEwPQYOK .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7cz1oHK4dEwPQYOK .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-7cz1oHK4dEwPQYOK .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7cz1oHK4dEwPQYOK .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-7cz1oHK4dEwPQYOK .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-7cz1oHK4dEwPQYOK .cluster text{fill:#333;}#mermaid-svg-7cz1oHK4dEwPQYOK .cluster span{color:#333;}#mermaid-svg-7cz1oHK4dEwPQYOK div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-7cz1oHK4dEwPQYOK .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-7cz1oHK4dEwPQYOK rect.text{fill:none;stroke-width:0;}#mermaid-svg-7cz1oHK4dEwPQYOK .icon-shape,#mermaid-svg-7cz1oHK4dEwPQYOK .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7cz1oHK4dEwPQYOK .icon-shape p,#mermaid-svg-7cz1oHK4dEwPQYOK .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-7cz1oHK4dEwPQYOK .icon-shape .label rect,#mermaid-svg-7cz1oHK4dEwPQYOK .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7cz1oHK4dEwPQYOK .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-7cz1oHK4dEwPQYOK .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-7cz1oHK4dEwPQYOK :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} System Prompt = stable + context + volatile
Stable Tier

(缓存前缀核心)

━━━━━━━━━━━━━━━

  1. SOUL.md/默认身份

  2. Hermes帮助指导

  3. 通用行为指导

  4. 工具感知指导

  5. 模型执行纪律

  6. 编码前缀

  7. 环境提示
    Context Tier

(项目相关)

━━━━━━━━━━━━━━━

  1. 调用方system_message

  2. AGENTS.md/.hermes.md

  3. .cursorrules

  4. 编码工作区快照

  5. 环境探测

  6. Bot Mode协议

  7. Profile行

  8. 平台提示
    Volatile Tier

(每会话可能变)

━━━━━━━━━━━━━━━

  1. 技能索引

  2. 内置记忆(MEMORY.md)

  3. 用户档案(USER.md)

  4. 外部记忆Provider

  5. 插件段落

  6. 时间戳行

  7. 运行时环境

2.2 Stable Tier 稳定层

组装顺序 (build_system_prompt_parts 行617-635):

序号 组件 函数 条件 行号
1 SOUL.md 或默认身份 _identity_parts() 总是 617
2 Hermes帮助指导 HERMES_AGENT_HELP_GUIDANCE[_NO_SKILLS] 总是,后根据技能条件替换 622-628
3 通用行为指导 _guidance_parts() 有工具时 623
4 阿里巴巴身份 _alibaba_identity_part() provider=="alibaba" 629
5 编码前缀 _coding_parts()[0] 有工具时 634-635

身份选择逻辑 (_identity_parts,行488-494):

python 复制代码
def _identity_parts(agent, ctx_len):
    """SOUL.md (primary identity; cron keeps the persona while skipping cwd
    instructions, scoped to the agent's OWN home) or the default identity."""
    wants_soul = agent.load_soul_identity or not agent.skip_context_files
    _soul_content = _pb.load_soul_md(ctx_len, home_override=_agent_home(agent)) if wants_soul else None
    return ([_soul_content], True) if _soul_content else ([DEFAULT_AGENT_IDENTITY], False)

默认身份常量 (prompt_builder.py 行129-138):

python 复制代码
DEFAULT_AGENT_IDENTITY = (
    "You are Hermes Agent, built by Nous Research. Be direct: match the length "
    "of your reply to the weight of the ask --- a one-line question gets a one-line "
    "answer, and finished work gets a short report of what changed, what's verified, "
    "and what's left, never a replay of the process. No filler (\"Great question,\" "
    "\"I'd be happy to\"), no restating the request back, no re-summarizing what you "
    "already said, no narrating tool calls the user can see. Plain claims over "
    "adjectives; when unsure, say so plainly. Agree because it's right, not because "
    "the user said it. Depth is earned --- give it when the user asks for detail, "
    "teaches, or the stakes demand it, not by default."
)

Hermes帮助指导的两个变体(行140-158):

变体 条件 内容差异
HERMES_AGENT_HELP_GUIDANCE 有skill_view工具 AND hermes-agent技能已安装 提到 skill_view(name='hermes-agent')
HERMES_AGENT_HELP_GUIDANCE_NO_SKILLS 其他 只指向文档URL,不提及skill_view

替换逻辑(行626-628):

python 复制代码
# 先放入 NO_SKILLS 变体占位
stable_parts[_help_guidance_slot] = HERMES_AGENT_HELP_GUIDANCE_NO_SKILLS
# 构建技能索引后,如果条件满足则替换为完整版本
if "skill_view" in (agent.valid_tool_names or set()) and "- hermes-agent:" in skills_prompt:
    stable_parts[_help_guidance_slot] = HERMES_AGENT_HELP_GUIDANCE

2.3 Context Tier 上下文层

组装顺序(行638-648):

序号 组件 条件 说明
1 system_message 调用方传入非None 网关/CLI传入的临时系统消息
2 上下文文件 skip_context_files=False AGENTS.md/.hermes.md/.cursorrules
3 编码工作区快照 有编码工作区 git状态、语言检测
4 编码尾部指导 有编码工作区 工作区相关的行为指导
5 环境探测 _environment_probe=True 系统环境信息
6 Bot Mode协议 _bot_mode_protocol=True Bot聊天会话
7 Profile行 总是 活跃Profile名称和路径
8 平台提示 总是 平台特定行为指导

关键设计 :如果没有 编码工作区快照,则编码尾部指导和后续部分(环境探测、Bot Mode、Profile行、平台提示)被放入 stable_parts 而非 context_parts(行646-648)------保持位置一致性。

2.4 Volatile Tier 易变层

组装顺序(行649-662):

序号 组件 函数 说明
1 技能索引 _skills_prompt() 所有可用技能的名称+描述列表
2 内置记忆 _memory_parts() MEMORY.md + USER.md
3 外部记忆Provider _memory_parts() 外部记忆系统提示块
4 插件段落(after_memory) _plugin_section_blocks() 插件提供的系统提示段落
5 时间戳行 _timestamp_line() 会话开始日期+时区+模型信息
6 运行时环境 build_environment_hints() 运行时环境提示(带分隔标记)

Volatile Tier 放最后的原因(行649-651注释):

Skills are runtime-mutable, so the index leads the volatile band: on a longest-prefix backend an unchanged index stays inside the reused prefix; a changed one re-prefills from here.

2.5 build_system_prompt_parts() 完整组装流程

源码位置 :agent/system_prompt.py 行605-663
#mermaid-svg-qj85Y4GAxth5IORi{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-qj85Y4GAxth5IORi .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-qj85Y4GAxth5IORi .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-qj85Y4GAxth5IORi .error-icon{fill:#552222;}#mermaid-svg-qj85Y4GAxth5IORi .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-qj85Y4GAxth5IORi .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-qj85Y4GAxth5IORi .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-qj85Y4GAxth5IORi .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-qj85Y4GAxth5IORi .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-qj85Y4GAxth5IORi .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-qj85Y4GAxth5IORi .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-qj85Y4GAxth5IORi .marker{fill:#333333;stroke:#333333;}#mermaid-svg-qj85Y4GAxth5IORi .marker.cross{stroke:#333333;}#mermaid-svg-qj85Y4GAxth5IORi svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-qj85Y4GAxth5IORi p{margin:0;}#mermaid-svg-qj85Y4GAxth5IORi .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-qj85Y4GAxth5IORi .cluster-label text{fill:#333;}#mermaid-svg-qj85Y4GAxth5IORi .cluster-label span{color:#333;}#mermaid-svg-qj85Y4GAxth5IORi .cluster-label span p{background-color:transparent;}#mermaid-svg-qj85Y4GAxth5IORi .label text,#mermaid-svg-qj85Y4GAxth5IORi span{fill:#333;color:#333;}#mermaid-svg-qj85Y4GAxth5IORi .node rect,#mermaid-svg-qj85Y4GAxth5IORi .node circle,#mermaid-svg-qj85Y4GAxth5IORi .node ellipse,#mermaid-svg-qj85Y4GAxth5IORi .node polygon,#mermaid-svg-qj85Y4GAxth5IORi .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-qj85Y4GAxth5IORi .rough-node .label text,#mermaid-svg-qj85Y4GAxth5IORi .node .label text,#mermaid-svg-qj85Y4GAxth5IORi .image-shape .label,#mermaid-svg-qj85Y4GAxth5IORi .icon-shape .label{text-anchor:middle;}#mermaid-svg-qj85Y4GAxth5IORi .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-qj85Y4GAxth5IORi .rough-node .label,#mermaid-svg-qj85Y4GAxth5IORi .node .label,#mermaid-svg-qj85Y4GAxth5IORi .image-shape .label,#mermaid-svg-qj85Y4GAxth5IORi .icon-shape .label{text-align:center;}#mermaid-svg-qj85Y4GAxth5IORi .node.clickable{cursor:pointer;}#mermaid-svg-qj85Y4GAxth5IORi .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-qj85Y4GAxth5IORi .arrowheadPath{fill:#333333;}#mermaid-svg-qj85Y4GAxth5IORi .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-qj85Y4GAxth5IORi .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-qj85Y4GAxth5IORi .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-qj85Y4GAxth5IORi .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-qj85Y4GAxth5IORi .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-qj85Y4GAxth5IORi .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-qj85Y4GAxth5IORi .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-qj85Y4GAxth5IORi .cluster text{fill:#333;}#mermaid-svg-qj85Y4GAxth5IORi .cluster span{color:#333;}#mermaid-svg-qj85Y4GAxth5IORi div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-qj85Y4GAxth5IORi .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-qj85Y4GAxth5IORi rect.text{fill:none;stroke-width:0;}#mermaid-svg-qj85Y4GAxth5IORi .icon-shape,#mermaid-svg-qj85Y4GAxth5IORi .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-qj85Y4GAxth5IORi .icon-shape p,#mermaid-svg-qj85Y4GAxth5IORi .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-qj85Y4GAxth5IORi .icon-shape .label rect,#mermaid-svg-qj85Y4GAxth5IORi .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-qj85Y4GAxth5IORi .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-qj85Y4GAxth5IORi .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-qj85Y4GAxth5IORi :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
Volatile Tier组装
skills_prompt
_memory_parts()

MEMORY.md + USER.md + 外部Provider
插件段落(after_memory)
_timestamp_line()

会话开始日期+时区+模型
环境提示(带分隔标记)
Context Tier组装
是
否
是
否
system_message

非None?
追加system_message
_context_files_part()

AGENTS.md/.hermes.md/.cursorrules
有编码工作区?
工作区+尾部+post_workspace

→ context_parts
尾部+post_workspace

→ stable_parts
build_system_prompt_parts(agent, system_message)
获取context_length

用于上下文文件大小限制
_identity_parts()

SOUL.md or DEFAULT_AGENT_IDENTITY
放入 HELP_GUIDANCE_NO_SKILLS

记录占位位置
_guidance_parts()

通用+工具感知+模型门控指导
_skills_prompt()

构建技能索引
skill_view存在

且hermes-agent在索引中?
替换为HELP_GUIDANCE

(含skill_view指引)
保持NO_SKILLS变体
_alibaba_identity_part()

阿里巴巴provider身份
build_environment_hints()

运行时环境提示
_coding_parts()

编码前缀+工作区+尾部
_post_workspace_parts()

环境探测+BotMode+Profile+平台提示
CTX_TIER
_join_tier() 各层

stable + context + volatile
return {stable, context, volatile}

最终拼接 (build_system_prompt,行666-675):

python 复制代码
def build_system_prompt(agent, system_message=None):
    parts = build_system_prompt_parts(agent, system_message=system_message)
    agent._cached_system_prompt_static = parts["stable"]  # 缓存静态前缀
    # 显示上下文文件截断警告
    for warning in drain_truncation_warnings():
        agent._emit_status(warning)
    return "\n\n".join(p for p in (parts["stable"], parts["context"], parts["volatile"]) if p)

三、各组件深度解析

3.1 身份与灵魂 --- _identity_parts()

源码位置 :agent/system_prompt.py 行488-494

设计目的 :确定 Agent 的核心身份------优先使用 SOUL.md(用户自定义身份),否则使用默认身份 DEFAULT_AGENT_IDENTITY。

SOUL.md 加载 (prompt_builder.py load_soul_md()):
#mermaid-svg-R3tPH8RlrJ94NKPF{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-R3tPH8RlrJ94NKPF .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-R3tPH8RlrJ94NKPF .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-R3tPH8RlrJ94NKPF .error-icon{fill:#552222;}#mermaid-svg-R3tPH8RlrJ94NKPF .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-R3tPH8RlrJ94NKPF .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-R3tPH8RlrJ94NKPF .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-R3tPH8RlrJ94NKPF .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-R3tPH8RlrJ94NKPF .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-R3tPH8RlrJ94NKPF .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-R3tPH8RlrJ94NKPF .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-R3tPH8RlrJ94NKPF .marker{fill:#333333;stroke:#333333;}#mermaid-svg-R3tPH8RlrJ94NKPF .marker.cross{stroke:#333333;}#mermaid-svg-R3tPH8RlrJ94NKPF svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-R3tPH8RlrJ94NKPF p{margin:0;}#mermaid-svg-R3tPH8RlrJ94NKPF .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-R3tPH8RlrJ94NKPF .cluster-label text{fill:#333;}#mermaid-svg-R3tPH8RlrJ94NKPF .cluster-label span{color:#333;}#mermaid-svg-R3tPH8RlrJ94NKPF .cluster-label span p{background-color:transparent;}#mermaid-svg-R3tPH8RlrJ94NKPF .label text,#mermaid-svg-R3tPH8RlrJ94NKPF span{fill:#333;color:#333;}#mermaid-svg-R3tPH8RlrJ94NKPF .node rect,#mermaid-svg-R3tPH8RlrJ94NKPF .node circle,#mermaid-svg-R3tPH8RlrJ94NKPF .node ellipse,#mermaid-svg-R3tPH8RlrJ94NKPF .node polygon,#mermaid-svg-R3tPH8RlrJ94NKPF .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-R3tPH8RlrJ94NKPF .rough-node .label text,#mermaid-svg-R3tPH8RlrJ94NKPF .node .label text,#mermaid-svg-R3tPH8RlrJ94NKPF .image-shape .label,#mermaid-svg-R3tPH8RlrJ94NKPF .icon-shape .label{text-anchor:middle;}#mermaid-svg-R3tPH8RlrJ94NKPF .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-R3tPH8RlrJ94NKPF .rough-node .label,#mermaid-svg-R3tPH8RlrJ94NKPF .node .label,#mermaid-svg-R3tPH8RlrJ94NKPF .image-shape .label,#mermaid-svg-R3tPH8RlrJ94NKPF .icon-shape .label{text-align:center;}#mermaid-svg-R3tPH8RlrJ94NKPF .node.clickable{cursor:pointer;}#mermaid-svg-R3tPH8RlrJ94NKPF .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-R3tPH8RlrJ94NKPF .arrowheadPath{fill:#333333;}#mermaid-svg-R3tPH8RlrJ94NKPF .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-R3tPH8RlrJ94NKPF .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-R3tPH8RlrJ94NKPF .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-R3tPH8RlrJ94NKPF .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-R3tPH8RlrJ94NKPF .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-R3tPH8RlrJ94NKPF .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-R3tPH8RlrJ94NKPF .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-R3tPH8RlrJ94NKPF .cluster text{fill:#333;}#mermaid-svg-R3tPH8RlrJ94NKPF .cluster span{color:#333;}#mermaid-svg-R3tPH8RlrJ94NKPF div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-R3tPH8RlrJ94NKPF .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-R3tPH8RlrJ94NKPF rect.text{fill:none;stroke-width:0;}#mermaid-svg-R3tPH8RlrJ94NKPF .icon-shape,#mermaid-svg-R3tPH8RlrJ94NKPF .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-R3tPH8RlrJ94NKPF .icon-shape p,#mermaid-svg-R3tPH8RlrJ94NKPF .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-R3tPH8RlrJ94NKPF .icon-shape .label rect,#mermaid-svg-R3tPH8RlrJ94NKPF .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-R3tPH8RlrJ94NKPF .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-R3tPH8RlrJ94NKPF .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-R3tPH8RlrJ94NKPF :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 否
是
是
否
load_soul_md(ctx_len, home_override)
解析agent的profile home

_agent_home(agent)
查找 SOUL.md

/SOUL.md
文件存在?
return None

→ 使用DEFAULT_AGENT_IDENTITY
_read_text_with_timeout()

5秒超时读取

(防NFS/iCloud阻塞)
_scan_context_content()

注入扫描(scope='context')
有威胁?
return 'BLOCKED: SOUL.md contained...'
_strip_yaml_frontmatter()

去掉YAML前置块
按ctx_len截断
return content

关键设计:

  1. 超时读取 (行51-77):_read_text_with_timeout() 在守护线程上读取文件,5秒超时后返回 None------防止 iCloud Drive/OneDrive/NFS 的故障页入阻塞系统提示组装
  2. 注入扫描 (行80-93):_scan_context_content() 用 scope="context" 扫描 SOUL.md/AGENTS.md/.cursorrules 的注入威胁。匹配则阻断(返回 [BLOCKED: ...]),而非警告------因为文件内容会原样进入 prompt
  3. YAML前置块剥离 (行122-126):_strip_yaml_frontmatter() 去掉 --- 开头的 YAML 元数据块,只注入 markdown body

3.2 行为指导 --- _guidance_parts()

源码位置 :agent/system_prompt.py 行497-523

设计目的:组装通用行为指导 + 工具感知指导 + 模型特定执行纪律。每个指导块都有独立的门控条件。
#mermaid-svg-sEXYpy7G30tuA1Of{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-sEXYpy7G30tuA1Of .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-sEXYpy7G30tuA1Of .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-sEXYpy7G30tuA1Of .error-icon{fill:#552222;}#mermaid-svg-sEXYpy7G30tuA1Of .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-sEXYpy7G30tuA1Of .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-sEXYpy7G30tuA1Of .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-sEXYpy7G30tuA1Of .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-sEXYpy7G30tuA1Of .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-sEXYpy7G30tuA1Of .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-sEXYpy7G30tuA1Of .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-sEXYpy7G30tuA1Of .marker{fill:#333333;stroke:#333333;}#mermaid-svg-sEXYpy7G30tuA1Of .marker.cross{stroke:#333333;}#mermaid-svg-sEXYpy7G30tuA1Of svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-sEXYpy7G30tuA1Of p{margin:0;}#mermaid-svg-sEXYpy7G30tuA1Of .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-sEXYpy7G30tuA1Of .cluster-label text{fill:#333;}#mermaid-svg-sEXYpy7G30tuA1Of .cluster-label span{color:#333;}#mermaid-svg-sEXYpy7G30tuA1Of .cluster-label span p{background-color:transparent;}#mermaid-svg-sEXYpy7G30tuA1Of .label text,#mermaid-svg-sEXYpy7G30tuA1Of span{fill:#333;color:#333;}#mermaid-svg-sEXYpy7G30tuA1Of .node rect,#mermaid-svg-sEXYpy7G30tuA1Of .node circle,#mermaid-svg-sEXYpy7G30tuA1Of .node ellipse,#mermaid-svg-sEXYpy7G30tuA1Of .node polygon,#mermaid-svg-sEXYpy7G30tuA1Of .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-sEXYpy7G30tuA1Of .rough-node .label text,#mermaid-svg-sEXYpy7G30tuA1Of .node .label text,#mermaid-svg-sEXYpy7G30tuA1Of .image-shape .label,#mermaid-svg-sEXYpy7G30tuA1Of .icon-shape .label{text-anchor:middle;}#mermaid-svg-sEXYpy7G30tuA1Of .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-sEXYpy7G30tuA1Of .rough-node .label,#mermaid-svg-sEXYpy7G30tuA1Of .node .label,#mermaid-svg-sEXYpy7G30tuA1Of .image-shape .label,#mermaid-svg-sEXYpy7G30tuA1Of .icon-shape .label{text-align:center;}#mermaid-svg-sEXYpy7G30tuA1Of .node.clickable{cursor:pointer;}#mermaid-svg-sEXYpy7G30tuA1Of .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-sEXYpy7G30tuA1Of .arrowheadPath{fill:#333333;}#mermaid-svg-sEXYpy7G30tuA1Of .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-sEXYpy7G30tuA1Of .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-sEXYpy7G30tuA1Of .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-sEXYpy7G30tuA1Of .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-sEXYpy7G30tuA1Of .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-sEXYpy7G30tuA1Of .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-sEXYpy7G30tuA1Of .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-sEXYpy7G30tuA1Of .cluster text{fill:#333;}#mermaid-svg-sEXYpy7G30tuA1Of .cluster span{color:#333;}#mermaid-svg-sEXYpy7G30tuA1Of div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-sEXYpy7G30tuA1Of .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-sEXYpy7G30tuA1Of rect.text{fill:none;stroke-width:0;}#mermaid-svg-sEXYpy7G30tuA1Of .icon-shape,#mermaid-svg-sEXYpy7G30tuA1Of .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-sEXYpy7G30tuA1Of .icon-shape p,#mermaid-svg-sEXYpy7G30tuA1Of .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-sEXYpy7G30tuA1Of .icon-shape .label rect,#mermaid-svg-sEXYpy7G30tuA1Of .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-sEXYpy7G30tuA1Of .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-sEXYpy7G30tuA1Of .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-sEXYpy7G30tuA1Of :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 否
是
匹配
是
否
不匹配
匹配
不匹配
_guidance_parts(agent)
有工具?
return \[\]
TASK_COMPLETION_GUIDANCE

'完成工作,不写stub'
PARALLEL_TOOL_CALL_GUIDANCE

'批量独立工具调用'
_tool_guidance_block()

记忆/会话搜索/技能/Kanban指导
STEER_CHANNEL_NOTE

'中途转向标记信任规则'
_model_gate()

tool_use_enforcement?
TOOL_USE_ENFORCEMENT_GUIDANCE

'必须使用工具,不要描述'
Gemini/Gemma?
GOOGLE_MODEL_OPERATIONAL_GUIDANCE

'绝对路径/验证优先/依赖检查'
_model_gate()

execution_guidance?
execution_guidance_text()

OPENAI_MODEL_EXECUTION_GUIDANCE

(8个纪律块)
return parts

模型门控解析 (_model_gate,行40-51):

配置值 行为
True 所有模型启用
False 所有模型禁用
"true"/"always"/"yes"/"on" 所有模型启用
"false"/"never"/"no"/"off" 所有模型禁用
["gpt", "codex"] 模型名包含这些子串时启用
"auto" 或其他 匹配硬编码的默认模型列表

TOOL_USE_ENFORCEMENT_MODELS(行330):

python 复制代码
("gpt", "codex", "gemini", "gemma", "grok", "glm", "qwen", "deepseek", "muse")

EXECUTION_GUIDANCE_MODELS(行344-347):

python 复制代码
("gpt", "codex", "grok", "deepseek", "kimi", "qwen", "glm", "minimax", "mimo", "mistral", "muse")

OPENAI_MODEL_EXECUTION_GUIDANCE 包含8个纪律块(行401-465):

块名 内容
<tool_persistence> 持续使用工具直到任务完成并验证
<mandatory_tool_use> 必须用工具的场景:算术、哈希、时间、系统状态、文件、Git、当前事实
<act_dont_ask> 有默认解释时直接行动,不询问澄清
<prerequisite_checks> 执行前检查前置步骤
<verification> 完成前验证正确性、依据、格式、安全、完整性
<external_state_verification> 外部写入后回读验证
<literal_preservation> 保持标识符原样,不"修复"格式
<missing_context> 缺失上下文时用工具查找,不猜测

工具感知指导 (_tool_guidance_block,行272-296):

根据 agent.valid_tool_names 动态组合:

工具名 注入的指导
memory build_memory_guidance() --- 记忆 vs 技能路由
session_search SESSION_SEARCH_GUIDANCE --- 引用过去对话时先搜索
skill_manage SKILLS_GUIDANCE --- 非trivial工作流记录为技能
kanban_show KANBAN_GUIDANCE --- Kanban任务执行协议

3.3 技能索引 --- _skills_prompt()

源码位置 :agent/system_prompt.py 行299-312

python 复制代码
def _skills_prompt(agent):
    if not any(name in agent.valid_tool_names for name in ['skills_list', 'skill_view', 'skill_manage']):
        return ""  # 无技能工具 → 空索引
    import model_tools
    avail_toolsets = {model_tools.get_toolset_for_tool(tool_name) for tool_name in agent.valid_tool_names} - {None, ""}
    # 编码紧凑模式:非编码类别只显示名称
    from agent.coding_context import coding_compact_skill_categories
    _compact_cats = coding_compact_skill_categories(platform=agent.platform, cwd=resolve_context_cwd())
    return _pb.build_skills_system_prompt(
        available_tools=agent.valid_tool_names,
        available_toolsets=avail_toolsets,
        compact_categories=_compact_cats or None,
        skills_dir_override=_agent_skills_dir(agent))

技能索引构建 (prompt_builder.py build_skills_system_prompt):
#mermaid-svg-oShIfiebA8N98Bvx{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-oShIfiebA8N98Bvx .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-oShIfiebA8N98Bvx .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-oShIfiebA8N98Bvx .error-icon{fill:#552222;}#mermaid-svg-oShIfiebA8N98Bvx .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-oShIfiebA8N98Bvx .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-oShIfiebA8N98Bvx .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-oShIfiebA8N98Bvx .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-oShIfiebA8N98Bvx .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-oShIfiebA8N98Bvx .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-oShIfiebA8N98Bvx .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-oShIfiebA8N98Bvx .marker{fill:#333333;stroke:#333333;}#mermaid-svg-oShIfiebA8N98Bvx .marker.cross{stroke:#333333;}#mermaid-svg-oShIfiebA8N98Bvx svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-oShIfiebA8N98Bvx p{margin:0;}#mermaid-svg-oShIfiebA8N98Bvx .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-oShIfiebA8N98Bvx .cluster-label text{fill:#333;}#mermaid-svg-oShIfiebA8N98Bvx .cluster-label span{color:#333;}#mermaid-svg-oShIfiebA8N98Bvx .cluster-label span p{background-color:transparent;}#mermaid-svg-oShIfiebA8N98Bvx .label text,#mermaid-svg-oShIfiebA8N98Bvx span{fill:#333;color:#333;}#mermaid-svg-oShIfiebA8N98Bvx .node rect,#mermaid-svg-oShIfiebA8N98Bvx .node circle,#mermaid-svg-oShIfiebA8N98Bvx .node ellipse,#mermaid-svg-oShIfiebA8N98Bvx .node polygon,#mermaid-svg-oShIfiebA8N98Bvx .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-oShIfiebA8N98Bvx .rough-node .label text,#mermaid-svg-oShIfiebA8N98Bvx .node .label text,#mermaid-svg-oShIfiebA8N98Bvx .image-shape .label,#mermaid-svg-oShIfiebA8N98Bvx .icon-shape .label{text-anchor:middle;}#mermaid-svg-oShIfiebA8N98Bvx .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-oShIfiebA8N98Bvx .rough-node .label,#mermaid-svg-oShIfiebA8N98Bvx .node .label,#mermaid-svg-oShIfiebA8N98Bvx .image-shape .label,#mermaid-svg-oShIfiebA8N98Bvx .icon-shape .label{text-align:center;}#mermaid-svg-oShIfiebA8N98Bvx .node.clickable{cursor:pointer;}#mermaid-svg-oShIfiebA8N98Bvx .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-oShIfiebA8N98Bvx .arrowheadPath{fill:#333333;}#mermaid-svg-oShIfiebA8N98Bvx .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-oShIfiebA8N98Bvx .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-oShIfiebA8N98Bvx .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-oShIfiebA8N98Bvx .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-oShIfiebA8N98Bvx .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-oShIfiebA8N98Bvx .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-oShIfiebA8N98Bvx .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-oShIfiebA8N98Bvx .cluster text{fill:#333;}#mermaid-svg-oShIfiebA8N98Bvx .cluster span{color:#333;}#mermaid-svg-oShIfiebA8N98Bvx div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-oShIfiebA8N98Bvx .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-oShIfiebA8N98Bvx rect.text{fill:none;stroke-width:0;}#mermaid-svg-oShIfiebA8N98Bvx .icon-shape,#mermaid-svg-oShIfiebA8N98Bvx .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-oShIfiebA8N98Bvx .icon-shape p,#mermaid-svg-oShIfiebA8N98Bvx .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-oShIfiebA8N98Bvx .icon-shape .label rect,#mermaid-svg-oShIfiebA8N98Bvx .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-oShIfiebA8N98Bvx .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-oShIfiebA8N98Bvx .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-oShIfiebA8N98Bvx :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} platform匹配

environment匹配

未禁用
不匹配
是
否
build_skills_system_prompt()
扫描所有技能目录

get_all_skills_dirs()
遍历每个SKILL.md
解析YAML前置块

parse_frontmatter()
技能匹配条件?
包含在索引中
跳过
提取: name + description

extract_skill_description()
格式化为 '- name: description'
compact_categories

且类别在列表中?
只显示名称

(省略description)
显示完整条目
组装索引文本
return skills_index_text

关键设计:

  1. Profile隔离的技能目录 :_agent_skills_dir(agent) 从 agent 的 OWN home 解析 <home>/skills,而非环境变量
  2. 条件过滤 :skill_matches_platform() 和 skill_matches_environment() 根据技能前置块中的 platforms 和条件表达式过滤
  3. Focus模式紧凑显示:编码上下文下,非编码类别只显示名称不显示描述,节省 token

3.4 编码姿态 --- _coding_parts()

源码位置 :agent/system_prompt.py 行540-566

设计目的 :探测当前工作区的编码上下文(git状态、语言、项目结构),生成编码行为指导。返回 (prefix, workspace, trailing) 三段。

python 复制代码
def _coding_parts(agent):
    from agent.coding_context import coding_system_prompt_parts
    if not agent.valid_tool_names:
        return [], [], []  # 无工具 → 无编码指导
    cwd = resolve_context_cwd()
    cwd_key = str(cwd) if cwd is not None else ""
    pinned = getattr(agent, "_frozen_workspace_snapshot", None)
    # 会话内pin住工作区快照------重建时重放相同的bytes,避免git状态变化破坏cache前缀
    replay = pinned[1] if pinned is not None and pinned[0] == cwd_key else None
    parts = coding_system_prompt_parts(
        platform=agent.platform, cwd=cwd, model=agent.model,
        valid_tool_names=agent.valid_tool_names, workspace_block=replay)
    if replay is None:
        agent._frozen_workspace_snapshot = (cwd_key, parts[1][0] if parts[1] else "")
    return parts

三段编码指导:

段 位置 内容 稳定性
prefix stable tier 通用编码行为指导(工具优先、文件验证) 会话内不变
workspace context tier git分支、语言检测、项目结构快照 会话内pin住,重建时重放
trailing context/stable tier 工作区相关的行为指导 跟随workspace

工作区快照 pin 机制 :_frozen_workspace_snapshot 以 (cwd_key, workspace_block_text) 元组存储。重建时如果 cwd 相同则重放快照,避免 git 状态变化(如 commit、branch切换)导致 cache 前缀失效。reset_session_state() 在会话边界清除 pin。

3.5 上下文文件 --- _context_files_part()

源码位置 :agent/system_prompt.py 行586-597

设计目的:加载项目上下文文件(AGENTS.md、.hermes.md、.cursorrules),注入到 context tier。

文件发现顺序 (prompt_builder.py build_context_files_prompt):
#mermaid-svg-tdKHvTe6wZRIOiAd{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-tdKHvTe6wZRIOiAd .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-tdKHvTe6wZRIOiAd .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-tdKHvTe6wZRIOiAd .error-icon{fill:#552222;}#mermaid-svg-tdKHvTe6wZRIOiAd .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-tdKHvTe6wZRIOiAd .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-tdKHvTe6wZRIOiAd .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-tdKHvTe6wZRIOiAd .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-tdKHvTe6wZRIOiAd .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-tdKHvTe6wZRIOiAd .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-tdKHvTe6wZRIOiAd .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-tdKHvTe6wZRIOiAd .marker{fill:#333333;stroke:#333333;}#mermaid-svg-tdKHvTe6wZRIOiAd .marker.cross{stroke:#333333;}#mermaid-svg-tdKHvTe6wZRIOiAd svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-tdKHvTe6wZRIOiAd p{margin:0;}#mermaid-svg-tdKHvTe6wZRIOiAd .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-tdKHvTe6wZRIOiAd .cluster-label text{fill:#333;}#mermaid-svg-tdKHvTe6wZRIOiAd .cluster-label span{color:#333;}#mermaid-svg-tdKHvTe6wZRIOiAd .cluster-label span p{background-color:transparent;}#mermaid-svg-tdKHvTe6wZRIOiAd .label text,#mermaid-svg-tdKHvTe6wZRIOiAd span{fill:#333;color:#333;}#mermaid-svg-tdKHvTe6wZRIOiAd .node rect,#mermaid-svg-tdKHvTe6wZRIOiAd .node circle,#mermaid-svg-tdKHvTe6wZRIOiAd .node ellipse,#mermaid-svg-tdKHvTe6wZRIOiAd .node polygon,#mermaid-svg-tdKHvTe6wZRIOiAd .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-tdKHvTe6wZRIOiAd .rough-node .label text,#mermaid-svg-tdKHvTe6wZRIOiAd .node .label text,#mermaid-svg-tdKHvTe6wZRIOiAd .image-shape .label,#mermaid-svg-tdKHvTe6wZRIOiAd .icon-shape .label{text-anchor:middle;}#mermaid-svg-tdKHvTe6wZRIOiAd .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-tdKHvTe6wZRIOiAd .rough-node .label,#mermaid-svg-tdKHvTe6wZRIOiAd .node .label,#mermaid-svg-tdKHvTe6wZRIOiAd .image-shape .label,#mermaid-svg-tdKHvTe6wZRIOiAd .icon-shape .label{text-align:center;}#mermaid-svg-tdKHvTe6wZRIOiAd .node.clickable{cursor:pointer;}#mermaid-svg-tdKHvTe6wZRIOiAd .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-tdKHvTe6wZRIOiAd .arrowheadPath{fill:#333333;}#mermaid-svg-tdKHvTe6wZRIOiAd .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-tdKHvTe6wZRIOiAd .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-tdKHvTe6wZRIOiAd .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-tdKHvTe6wZRIOiAd .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-tdKHvTe6wZRIOiAd .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-tdKHvTe6wZRIOiAd .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-tdKHvTe6wZRIOiAd .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-tdKHvTe6wZRIOiAd .cluster text{fill:#333;}#mermaid-svg-tdKHvTe6wZRIOiAd .cluster span{color:#333;}#mermaid-svg-tdKHvTe6wZRIOiAd div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-tdKHvTe6wZRIOiAd .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-tdKHvTe6wZRIOiAd rect.text{fill:none;stroke-width:0;}#mermaid-svg-tdKHvTe6wZRIOiAd .icon-shape,#mermaid-svg-tdKHvTe6wZRIOiAd .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-tdKHvTe6wZRIOiAd .icon-shape p,#mermaid-svg-tdKHvTe6wZRIOiAd .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-tdKHvTe6wZRIOiAd .icon-shape .label rect,#mermaid-svg-tdKHvTe6wZRIOiAd .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-tdKHvTe6wZRIOiAd .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-tdKHvTe6wZRIOiAd .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-tdKHvTe6wZRIOiAd :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} False
True
build_context_files_prompt(cwd, skip_soul)
发现上下文文件

  1. AGENTS.md

(从cwd向上到git root)
2. .hermes.md / HERMES.md

(从cwd向上到git root)
3. .cursorrules

(cwd only)
skip_soul?
4. SOUL.md

(已在stable tier加载)
跳过SOUL.md
逐个读取(5s超时)
注入扫描(scope='context')
去YAML前置块
按ctx_len截断
用分隔标记组装
return context_files_text

关键设计:

  1. git root 边界 :AGENTS.md/.hermes.md 从 cwd 向上搜索到 git root(_find_git_root),不跨越仓库边界
  2. 无git root时只查cwd :避免在 /tmp、/home 中捡到无关文件
  3. 安装树fallback :allow_install_tree_fallback 仅对 cli/tui 启用------桌面应用固定启动目录的 fallback 会被拒绝
  4. 5秒读取超时:每个文件独立超时,NFS/iCloud 阻塞不卡死

3.6 记忆系统 --- _memory_parts()

源码位置 :agent/system_prompt.py 行460-485

python 复制代码
def _memory_parts(agent):
    parts = []
    # 内置记忆
    if agent._memory_store:
        for enabled, kind in ((agent._memory_enabled, "memory"), (agent._user_profile_enabled, "user")):
            block = agent._memory_store.format_for_system_prompt(kind) if enabled else None
            if block:
                parts.append(block)
    # 外部记忆Provider(插件化,如mem0/hindsight/honcho等)
    if agent._memory_manager:
        if _mem_exposed is None or _mem_exposed(agent):
            _ext_mem_block = agent._memory_manager.build_system_prompt()
            if _ext_mem_block:
                parts.append(_ext_mem_block)
    return parts

记忆注入结构:
#mermaid-svg-OQQrAs6QZwX3aexx{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-OQQrAs6QZwX3aexx .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-OQQrAs6QZwX3aexx .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-OQQrAs6QZwX3aexx .error-icon{fill:#552222;}#mermaid-svg-OQQrAs6QZwX3aexx .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-OQQrAs6QZwX3aexx .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-OQQrAs6QZwX3aexx .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-OQQrAs6QZwX3aexx .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-OQQrAs6QZwX3aexx .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-OQQrAs6QZwX3aexx .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-OQQrAs6QZwX3aexx .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-OQQrAs6QZwX3aexx .marker{fill:#333333;stroke:#333333;}#mermaid-svg-OQQrAs6QZwX3aexx .marker.cross{stroke:#333333;}#mermaid-svg-OQQrAs6QZwX3aexx svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-OQQrAs6QZwX3aexx p{margin:0;}#mermaid-svg-OQQrAs6QZwX3aexx .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-OQQrAs6QZwX3aexx .cluster-label text{fill:#333;}#mermaid-svg-OQQrAs6QZwX3aexx .cluster-label span{color:#333;}#mermaid-svg-OQQrAs6QZwX3aexx .cluster-label span p{background-color:transparent;}#mermaid-svg-OQQrAs6QZwX3aexx .label text,#mermaid-svg-OQQrAs6QZwX3aexx span{fill:#333;color:#333;}#mermaid-svg-OQQrAs6QZwX3aexx .node rect,#mermaid-svg-OQQrAs6QZwX3aexx .node circle,#mermaid-svg-OQQrAs6QZwX3aexx .node ellipse,#mermaid-svg-OQQrAs6QZwX3aexx .node polygon,#mermaid-svg-OQQrAs6QZwX3aexx .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-OQQrAs6QZwX3aexx .rough-node .label text,#mermaid-svg-OQQrAs6QZwX3aexx .node .label text,#mermaid-svg-OQQrAs6QZwX3aexx .image-shape .label,#mermaid-svg-OQQrAs6QZwX3aexx .icon-shape .label{text-anchor:middle;}#mermaid-svg-OQQrAs6QZwX3aexx .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-OQQrAs6QZwX3aexx .rough-node .label,#mermaid-svg-OQQrAs6QZwX3aexx .node .label,#mermaid-svg-OQQrAs6QZwX3aexx .image-shape .label,#mermaid-svg-OQQrAs6QZwX3aexx .icon-shape .label{text-align:center;}#mermaid-svg-OQQrAs6QZwX3aexx .node.clickable{cursor:pointer;}#mermaid-svg-OQQrAs6QZwX3aexx .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-OQQrAs6QZwX3aexx .arrowheadPath{fill:#333333;}#mermaid-svg-OQQrAs6QZwX3aexx .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-OQQrAs6QZwX3aexx .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-OQQrAs6QZwX3aexx .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-OQQrAs6QZwX3aexx .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-OQQrAs6QZwX3aexx .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-OQQrAs6QZwX3aexx .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-OQQrAs6QZwX3aexx .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-OQQrAs6QZwX3aexx .cluster text{fill:#333;}#mermaid-svg-OQQrAs6QZwX3aexx .cluster span{color:#333;}#mermaid-svg-OQQrAs6QZwX3aexx div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-OQQrAs6QZwX3aexx .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-OQQrAs6QZwX3aexx rect.text{fill:none;stroke-width:0;}#mermaid-svg-OQQrAs6QZwX3aexx .icon-shape,#mermaid-svg-OQQrAs6QZwX3aexx .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-OQQrAs6QZwX3aexx .icon-shape p,#mermaid-svg-OQQrAs6QZwX3aexx .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-OQQrAs6QZwX3aexx .icon-shape .label rect,#mermaid-svg-OQQrAs6QZwX3aexx .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-OQQrAs6QZwX3aexx .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-OQQrAs6QZwX3aexx .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-OQQrAs6QZwX3aexx :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Volatile Tier 中的记忆块
内置记忆

━━━━━━━━━

MEMORY.md内容

(agent._memory_store

.format_for_system_prompt('memory'))
用户档案

━━━━━━━━━

USER.md内容

(agent._memory_store

.format_for_system_prompt('user'))
外部记忆Provider

━━━━━━━━━

mem0/hindsight/honcho等

(agent._memory_manager

.build_system_prompt())

门控条件:

条件 效果
_memory_enabled=False 且 _user_profile_enabled=False build_memory_guidance() 返回空字符串
_memory_enabled=True 注入 MEMORY.md 内容
_user_profile_enabled=True 注入 USER.md 内容
_memory_manager 存在且 memory_provider_tools_exposed(agent) 注入外部Provider块

3.7 平台提示 --- platform_hint()

源码位置 :agent/system_prompt.py 行384-401

python 复制代码
def platform_hint(agent):
    platform_key = (agent.platform or "").lower().strip()
    _default_hint = PLATFORM_HINTS.get(platform_key, "")
    # 插件注册的平台
    if not _default_hint and platform_key:
        _entry = platform_registry.get(platform_key)
        _default_hint = (_entry and _entry.platform_hint) or ""
    # Telegram富消息
    if platform_key == "telegram" and _default_hint and _telegram_rich_messages_enabled():
        _default_hint = _default_hint.rstrip() + " " + TELEGRAM_RICH_MESSAGES_HINT
    # 配置覆盖
    _effective_hint = _resolve_platform_hint(agent, platform_key, _default_hint)
    # TUI嵌入式终端澄清
    if platform_key == "tui" and _effective_hint:
        _effective_hint = _tui_embedded_pane_clarifier(_effective_hint)
    return _effective_hint

内置 PLATFORM_HINTS (prompt_builder.py 行597+):

平台 提示内容示例
whatsapp Markdown自动转换为WhatsApp语法(bold , italic)
telegram 支持Markdown和HTML,消息长度限制
discord 支持Markdown,代码块语法
slack 支持mrkdwn语法
cli 纯终端,Markdown不渲染
tui Ink TUI,Markdown不渲染

配置覆盖 (_resolve_platform_hint,行54-67):

yaml 复制代码
# config.yaml
platform_hints:
  telegram:
    replace: "自定义提示文本"  # 完全替换默认
    # 或
    append: "附加提示文本"     # 追加到默认

3.8 时间戳行 --- _timestamp_line()

源码位置 :agent/system_prompt.py 行433-457

python 复制代码
def _timestamp_line(agent):
    now = _hermes_now()
    _bits = _zone_bits(now, _hermes_tz())  # IANA key + 缩写 + UTC偏移
    _zone_suffix = f" ({', '.join(_bits)})" if _bits else ""
    _start = _session_start_like(agent, now)  # 会话开始时间
    timestamp_line = f"Conversation started: {_start.strftime('%A, %B %d, %Y')}{_zone_suffix}"

    # 长会话:如果当前日期≠开始日期,添加"as of"行
    if now.strftime("%Y%m%d") != _start.strftime("%Y%m%d"):
        timestamp_line += (
            f"\nToday's date (as of the last context rebuild): {now.strftime('%A, %B %d, %Y')}"
            " --- trust this over the start date for what day it is now; query tools for exact time."
        )

    # Bot Mode永恒会话:只显示时区,不显示日期
    if getattr(agent, "_bot_chat_timeless_prompt", False):
        timestamp_line = f"Timezone: {', '.join(_bits)}" if _bits else ""

    # 运行时元数据尾部
    trailer = (
        ("Session ID", agent.session_id if agent.pass_session_id else None),
        ("Model", agent.model),
        ("Provider", agent.provider),
        ("Platform", agent.platform),
    )
    return timestamp_line + "".join(f"\n{label}: {value}" for label, value in trailer if value)

输出示例:

复制代码
Conversation started: Sunday, September 13, 2026 (Asia/Hong_Kong, HKT, UTC+08:00)
Session ID: 20260913_181018_97f8f7
Model: glm-5.2
Provider: custom
Platform: cli

会话开始时间解析优先级 (_session_start_like,行175-220):

  1. lineage-root session ID 的嵌入式时间戳 ------压缩可能轮转 session ID,每个轮转 ID 嵌入自己的铸造时间;遍历到 lineage root 恢复原始出生时间戳
  2. 当前 session ID 的时间戳 ------YYYYMMDD_HHMMSS_... 格式
  3. agent.session_start------运行时属性
  4. now------最终 fallback

关键设计 :Conversation started: 行必须在重建间byte-stable------它是 prompt cache 前缀的一部分。使用不可变源(session ID 中的时间戳)确保跨压缩/恢复/gateway轮转的字节稳定性。

3.9 插件段落 --- _frozen_plugin_prompt_sections()

源码位置 :agent/system_prompt.py 行112-134

python 复制代码
def _frozen_plugin_prompt_sections(agent):
    """Render plugin sections once per session and freeze them on the agent."""
    if hasattr(agent, "_plugin_system_prompt_sections_snapshot"):
        return agent._plugin_system_prompt_sections_snapshot  # 已冻结

    stored_prompt = getattr(agent, "_cached_system_prompt", None)
    if isinstance(stored_prompt, str) and stored_prompt:
        # 从持久化的完整prompt中恢复冻结的段落
        rendered = _restore_plugin_prompt_sections(stored_prompt)
    else:
        try:
            from hermes_cli.plugins import render_system_prompt_sections
            rendered = tuple(render_system_prompt_sections(_plugin_session_info(agent)))
        except Exception as exc:
            # 渲染失败 → 保持上一个冻结的段落(fail-open)
            rendered = getattr(agent, "_plugin_system_prompt_sections_previous", None)
            if rendered:
                logger.warning("Plugin sections failed to re-render; keeping previous")
            else:
                rendered = ()
    agent._plugin_system_prompt_sections_snapshot = rendered
    return rendered

冻结机制:

  1. 每会话渲染一次 :首次构建时调用 render_system_prompt_sections(),结果冻结到 _plugin_system_prompt_sections_snapshot
  2. 恢复时从持久化prompt解析 :_restore_plugin_prompt_sections() 从存储的完整 prompt 中通过正则匹配恢复插件段落------避免重新运行插件代码
  3. 重建失败时fail-open:渲染失败时保持上一次的段落,而非消失
  4. 段落位置 :插件段落放在 after_memory 位置(volatile tier 中记忆之后)

四、Per-Turn Prompt 构建 --- build_turn_context()

源码位置 :agent/turn_context.py 行852-1012

设计目的:每轮对话的入口函数,执行一次性设置:stdio保护、surrogate清理、system prompt恢复/构建、DB会话行创建、预检压缩、pre_llm_call钩子、持久化。
#mermaid-svg-ZzXKVWkZUwwRWYl8{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ZzXKVWkZUwwRWYl8 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ZzXKVWkZUwwRWYl8 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ZzXKVWkZUwwRWYl8 .error-icon{fill:#552222;}#mermaid-svg-ZzXKVWkZUwwRWYl8 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ZzXKVWkZUwwRWYl8 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ZzXKVWkZUwwRWYl8 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ZzXKVWkZUwwRWYl8 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ZzXKVWkZUwwRWYl8 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ZzXKVWkZUwwRWYl8 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ZzXKVWkZUwwRWYl8 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ZzXKVWkZUwwRWYl8 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ZzXKVWkZUwwRWYl8 .marker.cross{stroke:#333333;}#mermaid-svg-ZzXKVWkZUwwRWYl8 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ZzXKVWkZUwwRWYl8 p{margin:0;}#mermaid-svg-ZzXKVWkZUwwRWYl8 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ZzXKVWkZUwwRWYl8 .cluster-label text{fill:#333;}#mermaid-svg-ZzXKVWkZUwwRWYl8 .cluster-label span{color:#333;}#mermaid-svg-ZzXKVWkZUwwRWYl8 .cluster-label span p{background-color:transparent;}#mermaid-svg-ZzXKVWkZUwwRWYl8 .label text,#mermaid-svg-ZzXKVWkZUwwRWYl8 span{fill:#333;color:#333;}#mermaid-svg-ZzXKVWkZUwwRWYl8 .node rect,#mermaid-svg-ZzXKVWkZUwwRWYl8 .node circle,#mermaid-svg-ZzXKVWkZUwwRWYl8 .node ellipse,#mermaid-svg-ZzXKVWkZUwwRWYl8 .node polygon,#mermaid-svg-ZzXKVWkZUwwRWYl8 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ZzXKVWkZUwwRWYl8 .rough-node .label text,#mermaid-svg-ZzXKVWkZUwwRWYl8 .node .label text,#mermaid-svg-ZzXKVWkZUwwRWYl8 .image-shape .label,#mermaid-svg-ZzXKVWkZUwwRWYl8 .icon-shape .label{text-anchor:middle;}#mermaid-svg-ZzXKVWkZUwwRWYl8 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ZzXKVWkZUwwRWYl8 .rough-node .label,#mermaid-svg-ZzXKVWkZUwwRWYl8 .node .label,#mermaid-svg-ZzXKVWkZUwwRWYl8 .image-shape .label,#mermaid-svg-ZzXKVWkZUwwRWYl8 .icon-shape .label{text-align:center;}#mermaid-svg-ZzXKVWkZUwwRWYl8 .node.clickable{cursor:pointer;}#mermaid-svg-ZzXKVWkZUwwRWYl8 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ZzXKVWkZUwwRWYl8 .arrowheadPath{fill:#333333;}#mermaid-svg-ZzXKVWkZUwwRWYl8 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ZzXKVWkZUwwRWYl8 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ZzXKVWkZUwwRWYl8 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ZzXKVWkZUwwRWYl8 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ZzXKVWkZUwwRWYl8 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ZzXKVWkZUwwRWYl8 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ZzXKVWkZUwwRWYl8 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ZzXKVWkZUwwRWYl8 .cluster text{fill:#333;}#mermaid-svg-ZzXKVWkZUwwRWYl8 .cluster span{color:#333;}#mermaid-svg-ZzXKVWkZUwwRWYl8 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ZzXKVWkZUwwRWYl8 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ZzXKVWkZUwwRWYl8 rect.text{fill:none;stroke-width:0;}#mermaid-svg-ZzXKVWkZUwwRWYl8 .icon-shape,#mermaid-svg-ZzXKVWkZUwwRWYl8 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ZzXKVWkZUwwRWYl8 .icon-shape p,#mermaid-svg-ZzXKVWkZUwwRWYl8 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ZzXKVWkZUwwRWYl8 .icon-shape .label rect,#mermaid-svg-ZzXKVWkZUwwRWYl8 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ZzXKVWkZUwwRWYl8 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ZzXKVWkZUwwRWYl8 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ZzXKVWkZUwwRWYl8 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
build_turn_context()
install_safe_stdio()

保护stdio防OSError
parse_turn_author()

重置turn author
recover_rotated_compression_session()

恢复轮转的压缩会话
set_session_context()

绑定日志session_id

set_current_write_origin()
_refresh_mcp_tools_between_turns()

刷新MCP工具
sanitize_surrogates()

清理用户消息中的代理字符
_bind_turn_identity()

生成turn_id + task_id
_reset_per_turn_agent_state()

重置每轮agent状态
_stage_turn_user_message()

准备用户消息行
_hydrate_from_history()

从历史恢复agent状态
bind_image_token_cost()

绑定图像token成本
append_message()

追加用户消息到messages
_tick_memory_nudge()

记忆nudge检查
_cached_system_prompt

is None?
restore_or_build_system_prompt()

恢复或构建system prompt
使用缓存的system prompt
_ensure_session_row()

创建DB会话行

(在prompt构建后!)
run_turn_start_compaction()

回合开始压缩检查
_collect_pre_llm_call_context()

收集插件pre_llm_call上下文
_merge_gateway_notes()

合并网关每轮注释
_memory_turn_start_and_prefetch()

记忆系统启动+预取
_stamp_api_content_sidecar()

标记api_content边车

(注入预取+插件上下文)
_persist_turn_start()

持久化回合开始状态
_maybe_title_session_at_turn_start()

异步自动标题
return TurnContext(...)

关键设计决策:

  1. DB会话行在system prompt构建后创建 (行962,注释行885-889):如果在 prompt 构建前创建,会插入 system_prompt=NULL 的行,导致前缀 cache miss
  2. api_content边车 (行994-997):将记忆预取和插件上下文标记到用户消息的 api_content 字段------保持持久化消息原样不变,只在 API 调用时替换 content
  3. 预检压缩 (行964-973):run_turn_start_compaction() 检查是否需要压缩,在 API 调用前执行

五、API 请求组装 --- assemble_api_request()

源码位置 :agent/turn_request_assembly.py 行106-261

设计目的 :从 messages 构建 API wire copy,注入 MoA 上下文、prefill 消息、缓存断点。顺序是 load-bearing------缓存断点必须在所有消息变换之后注入。
#mermaid-svg-WXeC3LjGIlDJWQJ3{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-WXeC3LjGIlDJWQJ3 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-WXeC3LjGIlDJWQJ3 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-WXeC3LjGIlDJWQJ3 .error-icon{fill:#552222;}#mermaid-svg-WXeC3LjGIlDJWQJ3 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-WXeC3LjGIlDJWQJ3 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-WXeC3LjGIlDJWQJ3 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-WXeC3LjGIlDJWQJ3 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-WXeC3LjGIlDJWQJ3 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-WXeC3LjGIlDJWQJ3 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-WXeC3LjGIlDJWQJ3 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-WXeC3LjGIlDJWQJ3 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-WXeC3LjGIlDJWQJ3 .marker.cross{stroke:#333333;}#mermaid-svg-WXeC3LjGIlDJWQJ3 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-WXeC3LjGIlDJWQJ3 p{margin:0;}#mermaid-svg-WXeC3LjGIlDJWQJ3 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-WXeC3LjGIlDJWQJ3 .cluster-label text{fill:#333;}#mermaid-svg-WXeC3LjGIlDJWQJ3 .cluster-label span{color:#333;}#mermaid-svg-WXeC3LjGIlDJWQJ3 .cluster-label span p{background-color:transparent;}#mermaid-svg-WXeC3LjGIlDJWQJ3 .label text,#mermaid-svg-WXeC3LjGIlDJWQJ3 span{fill:#333;color:#333;}#mermaid-svg-WXeC3LjGIlDJWQJ3 .node rect,#mermaid-svg-WXeC3LjGIlDJWQJ3 .node circle,#mermaid-svg-WXeC3LjGIlDJWQJ3 .node ellipse,#mermaid-svg-WXeC3LjGIlDJWQJ3 .node polygon,#mermaid-svg-WXeC3LjGIlDJWQJ3 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-WXeC3LjGIlDJWQJ3 .rough-node .label text,#mermaid-svg-WXeC3LjGIlDJWQJ3 .node .label text,#mermaid-svg-WXeC3LjGIlDJWQJ3 .image-shape .label,#mermaid-svg-WXeC3LjGIlDJWQJ3 .icon-shape .label{text-anchor:middle;}#mermaid-svg-WXeC3LjGIlDJWQJ3 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-WXeC3LjGIlDJWQJ3 .rough-node .label,#mermaid-svg-WXeC3LjGIlDJWQJ3 .node .label,#mermaid-svg-WXeC3LjGIlDJWQJ3 .image-shape .label,#mermaid-svg-WXeC3LjGIlDJWQJ3 .icon-shape .label{text-align:center;}#mermaid-svg-WXeC3LjGIlDJWQJ3 .node.clickable{cursor:pointer;}#mermaid-svg-WXeC3LjGIlDJWQJ3 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-WXeC3LjGIlDJWQJ3 .arrowheadPath{fill:#333333;}#mermaid-svg-WXeC3LjGIlDJWQJ3 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-WXeC3LjGIlDJWQJ3 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-WXeC3LjGIlDJWQJ3 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-WXeC3LjGIlDJWQJ3 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-WXeC3LjGIlDJWQJ3 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-WXeC3LjGIlDJWQJ3 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-WXeC3LjGIlDJWQJ3 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-WXeC3LjGIlDJWQJ3 .cluster text{fill:#333;}#mermaid-svg-WXeC3LjGIlDJWQJ3 .cluster span{color:#333;}#mermaid-svg-WXeC3LjGIlDJWQJ3 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-WXeC3LjGIlDJWQJ3 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-WXeC3LjGIlDJWQJ3 rect.text{fill:none;stroke-width:0;}#mermaid-svg-WXeC3LjGIlDJWQJ3 .icon-shape,#mermaid-svg-WXeC3LjGIlDJWQJ3 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-WXeC3LjGIlDJWQJ3 .icon-shape p,#mermaid-svg-WXeC3LjGIlDJWQJ3 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-WXeC3LjGIlDJWQJ3 .icon-shape .label rect,#mermaid-svg-WXeC3LjGIlDJWQJ3 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-WXeC3LjGIlDJWQJ3 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-WXeC3LjGIlDJWQJ3 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-WXeC3LjGIlDJWQJ3 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
是
否
是
否
是
否
assemble_api_request()
build_api_messages()

构建wire copy
MoA活跃?
_append_moa_context()

追加MoA参考模型上下文
有prefill消息?
在system后插入prefill

(API-time only)
_apply_context_engine_selection()

上下文引擎选择

(请求级, fail-open)
_sanitize_api_messages()

发送时净化
evict_stale_outbound_tool_images()

驱逐过时截图
consume_pending_sanitizer_heal_notice()

消耗修复通知
_drop_thinking_only_and_merge_users()

丢弃纯thinking轮 + 合并相邻user
规范化空白

每条消息content.strip()
_canonicalize_api_tool_calls()

规范化工具调用JSON
_sanitize_messages_surrogates()

剥离孤立代理字符
启用prompt caching

且非MoA?
build_prompt_cache_plan()

注入缓存断点

(在所有变换之后!)
provider==moa?
_prepare_moa_request()

准备持久MoA请求
估算token数

approx_tokens
计算request_pressure_tokens

(anchored优先, rough fallback)
return AssembledRequest(...)

build_api_messages() 详解 (turn_context.py 行1034-1120):

python 复制代码
def build_api_messages(agent, messages, *, current_turn_user_idx,
                       ext_prefetch_cache, plugin_user_context, moa_config, active_system_prompt):
    api_messages = []
    for idx, msg in enumerate(messages):
        api_msg = _clone_message_for_send(msg)  # 结构性克隆(非浅拷贝)
        _api_content = api_msg.pop("api_content", None)  # 弹出边车
        for key in ("display_kind", "display_metadata", "_row_id"):
            api_msg.pop(key, None)  # 剥离仅显示/内部字段

        if idx == current_turn_user_idx and msg.get("role") == "user":
            # 当前用户轮:重用prologue的stamp或实时组合
            if isinstance(_api_content, str) and _api_content:
                api_msg["content"] = _api_content  # 重用stamp
            else:
                _composed = compose_user_api_content(
                    api_msg.get("content", ""), ext_prefetch_cache, plugin_user_context)
                if _composed is not None:
                    api_msg["content"] = _composed
        elif isinstance(_api_content, str) and _api_content and msg.get("role") in ("user", "assistant"):
            # 历史轮:重放边车中的精确bytes,保持cache前缀byte-stable
            api_msg["content"] = _api_content

        # reasoning内容复制(多轮推理连续性)
        agent._copy_reasoning_content_for_api(msg, api_msg)
        # 剥离trajectory-only字段
        api_msg.pop("reasoning", None)
        api_msg.pop("finish_reason", None)
        # 填充空非最终消息
        fill_empty_non_final_wire_payload(api_msg, is_final=(idx == len(messages) - 1))
        # 剥离长度续接标记
        api_msg.pop("_length_continuation_fragment", None)
        api_msg.pop("_length_continuation_nudge", None)
        # 严格API工具调用净化
        if agent._should_sanitize_tool_calls():
            agent._sanitize_tool_calls_for_strict_api(api_msg, model=...)
        api_messages.append(api_msg)

    # 系统消息 = 缓存prompt + 临时追加
    effective_system = active_system_prompt or ""
    if agent.ephemeral_system_prompt:
        effective_system = (effective_system + "\n\n" + agent.ephemeral_system_prompt).strip()
    if effective_system:
        api_messages = [{"role": "system", "content": effective_system}] + api_messages
    return api_messages, effective_system

api_content 边车机制:
#mermaid-svg-td00PqN6mqXd6Ksm{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-td00PqN6mqXd6Ksm .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-td00PqN6mqXd6Ksm .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-td00PqN6mqXd6Ksm .error-icon{fill:#552222;}#mermaid-svg-td00PqN6mqXd6Ksm .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-td00PqN6mqXd6Ksm .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-td00PqN6mqXd6Ksm .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-td00PqN6mqXd6Ksm .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-td00PqN6mqXd6Ksm .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-td00PqN6mqXd6Ksm .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-td00PqN6mqXd6Ksm .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-td00PqN6mqXd6Ksm .marker{fill:#333333;stroke:#333333;}#mermaid-svg-td00PqN6mqXd6Ksm .marker.cross{stroke:#333333;}#mermaid-svg-td00PqN6mqXd6Ksm svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-td00PqN6mqXd6Ksm p{margin:0;}#mermaid-svg-td00PqN6mqXd6Ksm .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-td00PqN6mqXd6Ksm .cluster-label text{fill:#333;}#mermaid-svg-td00PqN6mqXd6Ksm .cluster-label span{color:#333;}#mermaid-svg-td00PqN6mqXd6Ksm .cluster-label span p{background-color:transparent;}#mermaid-svg-td00PqN6mqXd6Ksm .label text,#mermaid-svg-td00PqN6mqXd6Ksm span{fill:#333;color:#333;}#mermaid-svg-td00PqN6mqXd6Ksm .node rect,#mermaid-svg-td00PqN6mqXd6Ksm .node circle,#mermaid-svg-td00PqN6mqXd6Ksm .node ellipse,#mermaid-svg-td00PqN6mqXd6Ksm .node polygon,#mermaid-svg-td00PqN6mqXd6Ksm .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-td00PqN6mqXd6Ksm .rough-node .label text,#mermaid-svg-td00PqN6mqXd6Ksm .node .label text,#mermaid-svg-td00PqN6mqXd6Ksm .image-shape .label,#mermaid-svg-td00PqN6mqXd6Ksm .icon-shape .label{text-anchor:middle;}#mermaid-svg-td00PqN6mqXd6Ksm .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-td00PqN6mqXd6Ksm .rough-node .label,#mermaid-svg-td00PqN6mqXd6Ksm .node .label,#mermaid-svg-td00PqN6mqXd6Ksm .image-shape .label,#mermaid-svg-td00PqN6mqXd6Ksm .icon-shape .label{text-align:center;}#mermaid-svg-td00PqN6mqXd6Ksm .node.clickable{cursor:pointer;}#mermaid-svg-td00PqN6mqXd6Ksm .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-td00PqN6mqXd6Ksm .arrowheadPath{fill:#333333;}#mermaid-svg-td00PqN6mqXd6Ksm .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-td00PqN6mqXd6Ksm .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-td00PqN6mqXd6Ksm .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-td00PqN6mqXd6Ksm .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-td00PqN6mqXd6Ksm .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-td00PqN6mqXd6Ksm .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-td00PqN6mqXd6Ksm .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-td00PqN6mqXd6Ksm .cluster text{fill:#333;}#mermaid-svg-td00PqN6mqXd6Ksm .cluster span{color:#333;}#mermaid-svg-td00PqN6mqXd6Ksm div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-td00PqN6mqXd6Ksm .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-td00PqN6mqXd6Ksm rect.text{fill:none;stroke-width:0;}#mermaid-svg-td00PqN6mqXd6Ksm .icon-shape,#mermaid-svg-td00PqN6mqXd6Ksm .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-td00PqN6mqXd6Ksm .icon-shape p,#mermaid-svg-td00PqN6mqXd6Ksm .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-td00PqN6mqXd6Ksm .icon-shape .label rect,#mermaid-svg-td00PqN6mqXd6Ksm .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-td00PqN6mqXd6Ksm .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-td00PqN6mqXd6Ksm .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-td00PqN6mqXd6Ksm :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} API wire copy (api_messages)
持久化消息 (messages)
build_api_messages

pop api_content
role: user

content: '帮我查天气'

api_content: '帮我查天气

External Memory

用户偏好: 北京'
role: user

content: ← api_content的值

(注入了记忆预取+插件上下文)

设计目的 :api_content 是 bookkeeping 字段(精确记录发送的 bytes),不是 provider 字段。从每个出站副本弹出。当前轮重用 prologue 的 stamp(sidecar 和 wire 不能漂移),历史轮重放边车(保持 prefix cache byte-stable)。


六、Prompt Cache 不变性保证

Hermes Agent 对 Prompt Cache 的保证是系统级的核心不变量(system_prompt.py 行1-8 docstring):

Built once per session and reused across turns (only context compression triggers a rebuild) so the upstream prefix cache stays warm.

6.1 三层 Tier 的 cache 策略

#mermaid-svg-gc0zwq1cK5ztfikL{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-gc0zwq1cK5ztfikL .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-gc0zwq1cK5ztfikL .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-gc0zwq1cK5ztfikL .error-icon{fill:#552222;}#mermaid-svg-gc0zwq1cK5ztfikL .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-gc0zwq1cK5ztfikL .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-gc0zwq1cK5ztfikL .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-gc0zwq1cK5ztfikL .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-gc0zwq1cK5ztfikL .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-gc0zwq1cK5ztfikL .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-gc0zwq1cK5ztfikL .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-gc0zwq1cK5ztfikL .marker{fill:#333333;stroke:#333333;}#mermaid-svg-gc0zwq1cK5ztfikL .marker.cross{stroke:#333333;}#mermaid-svg-gc0zwq1cK5ztfikL svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-gc0zwq1cK5ztfikL p{margin:0;}#mermaid-svg-gc0zwq1cK5ztfikL .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-gc0zwq1cK5ztfikL .cluster-label text{fill:#333;}#mermaid-svg-gc0zwq1cK5ztfikL .cluster-label span{color:#333;}#mermaid-svg-gc0zwq1cK5ztfikL .cluster-label span p{background-color:transparent;}#mermaid-svg-gc0zwq1cK5ztfikL .label text,#mermaid-svg-gc0zwq1cK5ztfikL span{fill:#333;color:#333;}#mermaid-svg-gc0zwq1cK5ztfikL .node rect,#mermaid-svg-gc0zwq1cK5ztfikL .node circle,#mermaid-svg-gc0zwq1cK5ztfikL .node ellipse,#mermaid-svg-gc0zwq1cK5ztfikL .node polygon,#mermaid-svg-gc0zwq1cK5ztfikL .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-gc0zwq1cK5ztfikL .rough-node .label text,#mermaid-svg-gc0zwq1cK5ztfikL .node .label text,#mermaid-svg-gc0zwq1cK5ztfikL .image-shape .label,#mermaid-svg-gc0zwq1cK5ztfikL .icon-shape .label{text-anchor:middle;}#mermaid-svg-gc0zwq1cK5ztfikL .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-gc0zwq1cK5ztfikL .rough-node .label,#mermaid-svg-gc0zwq1cK5ztfikL .node .label,#mermaid-svg-gc0zwq1cK5ztfikL .image-shape .label,#mermaid-svg-gc0zwq1cK5ztfikL .icon-shape .label{text-align:center;}#mermaid-svg-gc0zwq1cK5ztfikL .node.clickable{cursor:pointer;}#mermaid-svg-gc0zwq1cK5ztfikL .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-gc0zwq1cK5ztfikL .arrowheadPath{fill:#333333;}#mermaid-svg-gc0zwq1cK5ztfikL .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-gc0zwq1cK5ztfikL .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-gc0zwq1cK5ztfikL .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-gc0zwq1cK5ztfikL .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-gc0zwq1cK5ztfikL .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-gc0zwq1cK5ztfikL .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-gc0zwq1cK5ztfikL .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-gc0zwq1cK5ztfikL .cluster text{fill:#333;}#mermaid-svg-gc0zwq1cK5ztfikL .cluster span{color:#333;}#mermaid-svg-gc0zwq1cK5ztfikL div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-gc0zwq1cK5ztfikL .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-gc0zwq1cK5ztfikL rect.text{fill:none;stroke-width:0;}#mermaid-svg-gc0zwq1cK5ztfikL .icon-shape,#mermaid-svg-gc0zwq1cK5ztfikL .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-gc0zwq1cK5ztfikL .icon-shape p,#mermaid-svg-gc0zwq1cK5ztfikL .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-gc0zwq1cK5ztfikL .icon-shape .label rect,#mermaid-svg-gc0zwq1cK5ztfikL .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-gc0zwq1cK5ztfikL .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-gc0zwq1cK5ztfikL .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-gc0zwq1cK5ztfikL :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Prefix Cache 命中区间
Stable Tier

✅ 始终命中

(会话内不变)
Context Tier

✅ 通常命中

(项目切换才变)
Volatile Tier

⚠️ 可能变化

(技能/记忆更新)

6.2 七项 cache 不变性规则

规则 实现位置 说明
System prompt 每会话只构建一次 build_system_prompt 行666-675 缓存到 _cached_system_prompt
仅压缩触发重建 invalidate_system_prompt 行678-696 清除缓存 + 重载记忆 + 清除插件快照
三层有序拼接 build_system_prompt 行675 stable → context → volatile
工作区快照会话内pin住 _coding_parts 行556-562 _frozen_workspace_snapshot 防止 git 状态变化
Conversation started 行 byte-stable _session_start_like 行175-220 从 session ID 嵌入的时间戳解析(不可变源)
api_content 边车重放 build_api_messages 行1078-1085 历史轮重放精确 bytes
缓存断点最后注入 assemble_api_request 行191-216 在所有消息变换之后

6.3 invalidate_system_prompt() 重建流程

源码位置 :agent/system_prompt.py 行678-696

python 复制代码
def invalidate_system_prompt(agent):
    """Force a rebuild on the next turn (after compression)."""
    agent._cached_system_prompt = None
    agent._cached_system_prompt_static = None
    # 清除冻结的插件快照(上一个bytes作为fail-open fallback)
    if hasattr(agent, "_plugin_system_prompt_sections_snapshot"):
        agent._plugin_system_prompt_sections_previous = agent._plugin_system_prompt_sections_snapshot
        del agent._plugin_system_prompt_sections_snapshot
    # 从磁盘重载记忆(捕获本会话的写入)
    if agent._memory_store:
        agent._memory_store.load_from_disk()

重建触发条件:

  1. 上下文压缩 :压缩后调用 invalidate_system_prompt(),下一个 turn 重建
  2. 模型切换:模型改变可能需要不同的执行纪律指导
  3. 技能变更 :skill_manage 操作后技能索引变化

6.4 reconstruct_static_prefix() 恢复静态前缀

源码位置 :agent/system_prompt.py 行699-725

设计目的 :对于持久化的完整 prompt,重建 _cached_system_prompt_static 以恢复 [static, volatile] 布局。仅当存储的 prompt 字面以重建的 stable tier 开头时才使用------否则发送原始 bytes。

python 复制代码
def reconstruct_static_prefix(agent, system_message=None, *, log_label="restore"):
    stored = agent._cached_system_prompt  # 持久化的完整prompt
    existing = agent._cached_system_prompt_static
    if (not agent._use_prompt_caching or not stored
        or (existing and stored.startswith(existing))  # 已有正确的static
        or agent._static_rebuild_failed_for == stored):  # 已失败过
        return
    try:
        static = build_system_prompt_parts(agent, system_message)["stable"]
        if static and stored.startswith(static):  # 验证:存储的prompt确实以此开头
            agent._cached_system_prompt_static = static
            return
    except Exception:
        pass
    agent._cached_system_prompt_static = None  # 失败 → 不分割
    agent._static_rebuild_failed_for = stored  # 记忆失败,避免热路径重试

七、完整端到端流程

LLM API assemble_api_request() build_api_messages() prompt_builder.py system_prompt.py build_turn_context() 用户消息 LLM API assemble_api_request() build_api_messages() prompt_builder.py system_prompt.py build_turn_context() 用户消息 #mermaid-svg-mAJWhdIK5VLePAZr{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-mAJWhdIK5VLePAZr .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-mAJWhdIK5VLePAZr .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-mAJWhdIK5VLePAZr .error-icon{fill:#552222;}#mermaid-svg-mAJWhdIK5VLePAZr .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-mAJWhdIK5VLePAZr .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-mAJWhdIK5VLePAZr .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-mAJWhdIK5VLePAZr .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-mAJWhdIK5VLePAZr .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-mAJWhdIK5VLePAZr .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-mAJWhdIK5VLePAZr .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-mAJWhdIK5VLePAZr .marker{fill:#333333;stroke:#333333;}#mermaid-svg-mAJWhdIK5VLePAZr .marker.cross{stroke:#333333;}#mermaid-svg-mAJWhdIK5VLePAZr svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-mAJWhdIK5VLePAZr p{margin:0;}#mermaid-svg-mAJWhdIK5VLePAZr .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-mAJWhdIK5VLePAZr text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-mAJWhdIK5VLePAZr .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-mAJWhdIK5VLePAZr .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-mAJWhdIK5VLePAZr .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-mAJWhdIK5VLePAZr .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-mAJWhdIK5VLePAZr #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-mAJWhdIK5VLePAZr .sequenceNumber{fill:white;}#mermaid-svg-mAJWhdIK5VLePAZr #sequencenumber{fill:#333;}#mermaid-svg-mAJWhdIK5VLePAZr #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-mAJWhdIK5VLePAZr .messageText{fill:#333;stroke:none;}#mermaid-svg-mAJWhdIK5VLePAZr .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-mAJWhdIK5VLePAZr .labelText,#mermaid-svg-mAJWhdIK5VLePAZr .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-mAJWhdIK5VLePAZr .loopText,#mermaid-svg-mAJWhdIK5VLePAZr .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-mAJWhdIK5VLePAZr .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-mAJWhdIK5VLePAZr .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-mAJWhdIK5VLePAZr .noteText,#mermaid-svg-mAJWhdIK5VLePAZr .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-mAJWhdIK5VLePAZr .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-mAJWhdIK5VLePAZr .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-mAJWhdIK5VLePAZr .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-mAJWhdIK5VLePAZr .actorPopupMenu{position:absolute;}#mermaid-svg-mAJWhdIK5VLePAZr .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-mAJWhdIK5VLePAZr .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-mAJWhdIK5VLePAZr .actor-man circle,#mermaid-svg-mAJWhdIK5VLePAZr line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-mAJWhdIK5VLePAZr :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Phase 1: Per-Turn Setup Phase 2: System Prompt (缓存检查) alt 首次/压缩后 已缓存 Phase 3: 预检压缩 + 上下文注入 Phase 4: API请求组装 用户输入 "帮我分析这段代码" install_safe_stdio() sanitize_surrogates(user_message) _bind_turn_identity() → turn_id, task_id _reset_per_turn_agent_state() append_message(messages, user_msg) _cached_system_prompt is None? restore_or_build_system_prompt() build_system_prompt_parts(agent) _identity_parts() → SOUL.md or DEFAULT _guidance_parts() → 通用+工具+模型指导 _skills_prompt() → 技能索引 _coding_parts() → 编码前缀+工作区 _context_files_part() → AGENTS.md等 _memory_parts() → MEMORY.md + USER.md _timestamp_line() → 日期+时区 三层tier拼接 → _cached_system_prompt 使用 _cached_system_prompt run_turn_start_compaction() _collect_pre_llm_call_context() → plugin_user_context _memory_turn_start_and_prefetch() → ext_prefetch_cache _stamp_api_content_sidecar() → 标记边车 return TurnContext 遍历messages构建api_messages 当前轮: 重用api_content stamp 历史轮: 重放api_content (cache-stable) 拼接 system prompt到头部 api_messages, effective_system MoA上下文追加 (如有) prefill消息插入 (如有) 上下文引擎选择 _sanitize_api_messages() evict_stale_outbound_tool_images() _drop_thinking_only_and_merge_users() 空白规范化 + JSON规范化 剥离surrogates build_prompt_cache_plan() → 缓存断点 估算token压力 AssembledRequest → API调用


八、设计亮点与关键决策

8.1 三层 Tier 架构

问题:System prompt 中有些部分会话内不变(身份),有些偶尔变(项目文件),有些经常变(时间戳、技能、记忆)。

解决:按变化频率分层,stable 放前面保证 prefix cache 命中,volatile 放最后减少 cache 失效范围。

8.2 api_content 边车

问题:记忆预取和插件上下文注入到用户消息中,但持久化的消息必须保持原样(不包含注入内容)。

解决 :api_content 字段存储注入后的精确 bytes,持久化消息保持原始 content。API 调用时弹出 api_content 替换 content,历史轮重放保持 cache-stable。

8.3 工作区快照 Pin

问题:git 状态(分支、未提交变更)在会话期间会变化(commit、checkout),重建 system prompt 时会生成不同的 bytes,破坏 cache 前缀。

解决 :_frozen_workspace_snapshot 会话内 pin 住工作区快照文本。重建时如果 cwd 相同则重放快照,不重新探测 git。

8.4 插件段落冻结

问题:插件代码在重建时可能失败或产生不同输出,导致 system prompt 不稳定。

解决 :每会话渲染一次并冻结。恢复时从持久化的完整 prompt 中通过正则匹配恢复(_restore_plugin_prompt_sections),不重新运行插件代码。渲染失败时保持上一次的段落(fail-open)。

8.5 模型门控的执行纪律

问题:不同模型有不同的失败模式------GPT 系列会放弃部分结果、DeepSeek 用 prose 做数学、Gemini 不验证文件内容。

解决 :_model_gate() 根据配置值(True/False/list/"auto")和模型名匹配,动态注入相应的执行纪律指导块。

8.6 超时文件读取

问题:SOUL.md/AGENTS.md 可能在 iCloud Drive/OneDrive/NFS 上,故障页入会阻塞系统提示组装。

解决 :_read_text_with_timeout() 在守护线程上读取,5秒超时后返回 None,孤儿线程自行完成。网络文件系统阻塞不卡死启动。

8.7 注入扫描

问题:上下文文件(AGENTS.md/.cursorrules/SOUL.md)来自磁盘,可能包含恶意注入内容。cron 作业非交互运行,自动批准工具调用,注入风险更高。

解决 :_scan_context_content() 用 scope="context" 扫描威胁模式。匹配则阻断(返回 [BLOCKED: ...]),而非警告------因为内容会原样进入 prompt。

8.8 缓存断点最后注入

问题 :缓存断点标记(如 Anthropic 的 cache_control)必须在最终消息 bytes 上注入,否则同一行的 bytes 在不同轮间不同,cache miss。

解决 :assemble_api_request() 中 build_prompt_cache_plan() 在所有消息变换(净化、空白规范化、JSON规范化、surrogate剥离、thinking丢弃、user合并)之后注入------顺序是 load-bearing 的。


文档统计 :本文档严格基于 agent/system_prompt.py(761行)、agent/prompt_builder.py(1637行)、agent/turn_context.py(1146行)、agent/turn_request_assembly.py(261行) 等源码逐行分析,包含15+张Mermaid彩色图,覆盖三层Tier架构、各组件深度解析、Per-Turn构建、API请求组装、Cache不变性保证等完整流程。所有代码引用标注行号。

相关推荐
小杉泽1 小时前
MCP Tasks 输入生命周期安全:从 input_required 到可恢复执行的状态机审计
ai·ai安全
技灵AI1 小时前
Seedance 长剧生产实战:用首尾帧接戏解决角色崩脸与场景漂移(含 return_last_frame 用法与提示词模板)
人工智能·prompt·aigc·音视频
jimmyleeee2 小时前
大模型安全之三十五:间接Prompt 注入测试全景指南
安全·prompt
LucianaiB2 小时前
华为云码道 AI 编程实战:我用 CodeArts 智能体打造了一款 HarmonyOS 游戏化专注应用「智办 ZhiBan」
人工智能·ai·华为云·harmonyos·codearts·材料
宋哥转AI2 小时前
AgentScope Java 实战 03:知识与工具层——给 Agent 装上手和书架
人工智能·agent·ai编程
张忠琳2 小时前
【hermes-agent】Hermes Agent Kanban 流程超深度分析之一
ai·agent·hermes
沛东的认知和实践2 小时前
论文说根层摘要只要原文的 2.6%,我实测是 126%——彻底搞懂GraphRAG第六篇
agent
李溪白2 小时前
Agent 自主规划 —— 怎么让 Agent 自己拆解任务、动态调整策略
agent
李溪白2 小时前
LangGraph 高级用法:状态管理、条件路由、人工介入的完整案例
agent