提示词构造与上下文组装
昨天(Day 08)走完了执行链路,其中 buildEmbeddedSystemPrompt 一笔带过;今天把它放大:模型到底"看到"什么?答案在 buildAgentSystemPrompt(system-prompt.ts:189)——它把技能、记忆、身份、工具、项目上下文一段段拼成系统提示。这是 Agent 行为的"总说明书",也为明天(Day 10)讲"记忆怎么注入"打基础。
系统提示是什么
系统提示(system prompt)是每次对话都放在最前面、告诉模型"你是谁、能干什么、要遵守什么"的一大段文字。它由 buildAgentSystemPrompt(system-prompt.ts:189-689)动态生成,经 createSystemPromptOverride 覆盖进 Pi 会话。
段式组装
提示由一系列 build*Section 函数拼接,每个返回字符串数组,最后 lines.filter(Boolean).join("\n")(system-prompt.ts:688):
filter(Boolean) 把空段(比如没有记忆能力时的 Memory 段)自动剔除。这种"段式组装 + 空段自动省略"让提示词既模块化又不留空洞。filter(Boolean) 剔掉——手册里不会留一页空白。buildMemorySection() 返回 "## Memory Recall\n答历史问题前先 memory_search…" → 手册里出现这一段。没有 memory 工具时:
buildMemorySection() 返回 ""(空串)→ filter(Boolean) 把它删掉 → 手册里整段消失,也不会留个孤零零的空标题。这就是"段式组装 + 空段自动省略"的实际效果。PromptMode 三档
// system-prompt.ts:12-17
type PromptMode = "full" | "minimal" | "none";
// full → 主 Agent,完整手册
// minimal → 子 Agent,仅 Tooling / Workspace / Runtime 三段(省 token)
// none → 仅一行身份(system-prompt.ts:419)
| 档位 | 发给谁 | 手册里有哪些段 | 为什么 |
|---|---|---|---|
full | 主 Agent(总管) | 全套:技能/记忆/身份/工具/上下文/协议/Runtime | 要独当一面,手册得全 |
minimal | 子 Agent(干专门小活) | 仅 Tooling / Workspace / Runtime 三段 | 不需要知道所有技能和身份规则,省 token |
none | 极轻量场景 | 仅一行身份(:419) | 能省则省 |
👶 小白:给子 Agent 也发全套手册,不是更保险吗?为什么要费劲裁剪?
👨🏫 老师:因为系统提示每次请求都要重发一遍,还占上下文窗口——它既花钱(按 token 计费)又挤占宝贵的窗口空间。一个只干"读这个文件总结一下"的子 Agent,你把 52 个技能目录、所有身份规则都塞给它,纯属浪费。就像派实习生去复印个文件,没必要先让他背完整本员工手册。full/minimal/none 就是"看活儿派手册"。
技能目录段
buildSkillsSection(system-prompt.ts:20-36,标题"## Skills (mandatory)")只放技能的目录(名称+描述+文件路径),不放技能全文:
// 提示里长这样(XML 形式,由 Pi 的 formatSkillsForPrompt 生成):
// <available_skills>
// <skill><name>weather</name><description>查天气…</description>
// <location>skills/weather/SKILL.md</location></skill>
// ...
// </available_skills>
// 指令:先扫描描述,命中后用 read 工具读对应 SKILL.md,再照做
工具清单段
工具清单由 coreToolSummaries(:240)+ toolOrder(:274)+ 规范化工具名(:301-311)生成 - name: summary 列表,告诉模型"你有这些工具、各是干嘛的"。
项目上下文注入
# Project Context 段(system-prompt.ts:616-649)注入工作区的 bootstrap 文件——这就是 README 说的"注入记忆文件":
// 注入的文件(src/agents/workspace.ts:25-33):
// AGENTS.md, SOUL.md, TOOLS.md, IDENTITY.md, USER.md, HEARTBEAT.md,
// BOOTSTRAP.md, MEMORY.md
// 若含 SOUL.md → 追加 "embody its persona and tone"(:632-636)人格设定
覆盖与钩子注入
系统提示还能被追加/覆盖:
params.extraSystemPrompt(:587):调用方额外追加。before_prompt_build钩子:扩展注入内容(受allowPromptInjection策略门控)。- 运行中
prependSystemPromptAddition(attempt.ts:2109)。 - 诊断:
buildSystemPromptReport(attempt.ts:1673)记录截断/注入/技能/工具,供排障。
今日小结 + 动手
🧠 今天你应该能回答
- 系统提示是什么?为什么是运行时动态拼的?
- 段式组装 + filter(Boolean) 的好处?
- PromptMode 三档分别用在哪、为什么子 Agent 用 minimal?
- 技能段为什么只放目录不放全文?
- Project Context 注入哪些文件?SOUL.md 起什么作用?
✋ 动手
cd /Users/bitmart/work/codes/github/openclaw
grep -n 'function build.*Section\|PromptMode' src/agents/system-prompt.ts | head -20
sed -n '616,649p' src/agents/system-prompt.ts # Project Context
sed -n '25,33p' src/agents/workspace.ts # bootstrap 文件名