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 构建分为两个阶段:
- Session 级 System Prompt 构建:每会话一次,缓存复用,仅在上下文压缩时重建
- 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
(缓存前缀核心)
━━━━━━━━━━━━━━━
-
Hermes帮助指导
-
通用行为指导
-
工具感知指导
-
模型执行纪律
-
编码前缀
-
环境提示
Context Tier
(项目相关)
━━━━━━━━━━━━━━━
-
调用方system_message
-
.cursorrules
-
编码工作区快照
-
环境探测
-
Bot Mode协议
-
Profile行
-
平台提示
Volatile Tier
(每会话可能变)
━━━━━━━━━━━━━━━
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
关键设计:
- 超时读取 (行51-77):
_read_text_with_timeout()在守护线程上读取文件,5秒超时后返回 None------防止 iCloud Drive/OneDrive/NFS 的故障页入阻塞系统提示组装 - 注入扫描 (行80-93):
_scan_context_content()用scope="context"扫描 SOUL.md/AGENTS.md/.cursorrules 的注入威胁。匹配则阻断(返回[BLOCKED: ...]),而非警告------因为文件内容会原样进入 prompt - 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
关键设计:
- Profile隔离的技能目录 :
_agent_skills_dir(agent)从 agent 的 OWN home 解析<home>/skills,而非环境变量 - 条件过滤 :
skill_matches_platform()和skill_matches_environment()根据技能前置块中的platforms和条件表达式过滤 - 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)
发现上下文文件
(从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
关键设计:
- git root 边界 :AGENTS.md/.hermes.md 从 cwd 向上搜索到 git root(
_find_git_root),不跨越仓库边界 - 无git root时只查cwd :避免在
/tmp、/home中捡到无关文件 - 安装树fallback :
allow_install_tree_fallback仅对 cli/tui 启用------桌面应用固定启动目录的 fallback 会被拒绝 - 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):
- lineage-root session ID 的嵌入式时间戳 ------压缩可能轮转 session ID,每个轮转 ID 嵌入自己的铸造时间;遍历到 lineage root 恢复原始出生时间戳
- 当前 session ID 的时间戳 ------
YYYYMMDD_HHMMSS_...格式 agent.session_start------运行时属性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
冻结机制:
- 每会话渲染一次 :首次构建时调用
render_system_prompt_sections(),结果冻结到_plugin_system_prompt_sections_snapshot - 恢复时从持久化prompt解析 :
_restore_plugin_prompt_sections()从存储的完整 prompt 中通过正则匹配恢复插件段落------避免重新运行插件代码 - 重建失败时fail-open:渲染失败时保持上一次的段落,而非消失
- 段落位置 :插件段落放在
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(...)
关键设计决策:
- DB会话行在system prompt构建后创建 (行962,注释行885-889):如果在 prompt 构建前创建,会插入
system_prompt=NULL的行,导致前缀 cache miss - api_content边车 (行994-997):将记忆预取和插件上下文标记到用户消息的
api_content字段------保持持久化消息原样不变,只在 API 调用时替换 content - 预检压缩 (行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()
重建触发条件:
- 上下文压缩 :压缩后调用
invalidate_system_prompt(),下一个 turn 重建 - 模型切换:模型改变可能需要不同的执行纪律指导
- 技能变更 :
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不变性保证等完整流程。所有代码引用标注行号。