Zero 架构 · 第五课
第一课的循环第一行就播种了对话:
SeedMessagesWithImages(buildSystemPrompt(options), prompt, images)
(loop.go:130)。
那个 buildSystemPrompt 决定了模型「开机」时看到的一切 —— 它的人格、可用的
约定、当前工作目录、安全策略。这一课拆开它。
对齐 mission:系统提示常被当成一坨硬编码字符串。Zero 把它做成可组合、可预算、 可缓存的分段结构 —— 这本身就是一个值得学的架构决策。
buildSystemPrompt 在每次运行开头只调用一次,结果作为唯一的
system 回合,被之后每一个回合复用。
system_prompt.go:62
函数体极其朴素 —— 一个字符串切片,每段独立判断「要不要加」,最后用 \n\n
拼起来:
system_prompt.go:70
sections := []string{core} // 核心「编码工艺」指令
if a := modelPromptAddendum(model); a != "" { ... } // 按模型家族的小贴士
if s := sessionRuntimeContext(options); ... {} // 当前 provider/model
if p := approvedCommandPrefixContext(...); ... {} // 已批准的命令前缀
if seed := workspaceSeedContext(cwd); ... {} // 工作区种子
if ws := workspaceContext(cwd); ... {} // cwd/OS/shell/git/项目约定/repo map
if d := specialistDelegationContext(...); ... {} // 可委派的 specialist 列表
if style := responseStyleContext(...); ... {} // /style 选择的回复风格
if policy := confirmationPolicy; ... {} // 安全确认策略(embed)
return strings.Join(sections, "\n\n")
关键约定:每个分段函数在无内容时返回空串,拼装处用 if != "" 跳过。
所以「没有 git 仓库」「没有 specialist」「风格是默认」这些情况不占一个字节 ——
缺席的上下文零成本,而不是留一堆「N/A」占位。
核心指令和确认策略是两个 Markdown 文件,用 //go:embed 直接嵌进二进制:
system_prompt.md 和 confirmation_policy.md。
system_prompt.go:19
这样「编码工艺」这类不变内容能被产品人员当文档来读、来改,而不必混在 Go 字符串里。
workspaceContext 现算 cwd、runtime.GOOS、按 OS 给出 shell 语法
(Windows cmd 还是 /bin/sh)、git 分支,再拼上项目约定和 repo map。
system_prompt.go:190
modelPromptAddendum)
同一份核心提示,针对不同模型家族追加一小段 <model_guidance>:
system_prompt_models.go:42
update_plan」。这是「一个核心 + 少量厂商特调」的策略 —— 不为每家写一整套提示,只补差异。
projectGuidelines)
这正是 Zero 尊重 CLAUDE.md 式约定的地方。它按名字找项目上下文文件 ——
AGENTS.md / ZERO.md / .zero/AGENTS.md
(大小写无关,兼容 AGENTS.MD),沿 gitRoot→cwd 的目录链逐级读取。
system_prompt.go:38、
system_prompt.go:223
但它有严格的字节预算:每文件 8 KiB、总计 32 KiB,超了就 UTF-8 安全地
截断并标 … (truncated)。
system_prompt.go:42
一个巨大的 AGENTS.md 不能把整个上下文窗口吃光 —— 用户约定重要,但不能无限膨胀。
buildSystemPrompt 每次运行调用几次,为什么?
当前工作区没有 git、没有 specialist,系统提示里会怎样?
为什么项目约定文件(AGENTS.md 等)要有每文件 8KiB / 总 32KiB 的预算?
less +62 ../zero/internal/agent/system_prompt.go # buildSystemPrompt 拼装骨架
less +190 ../zero/internal/agent/system_prompt.go # workspaceContext 动态部分
less +42 ../zero/internal/agent/system_prompt_models.go # 按家族的 addendum
bat ../zero/internal/agent/system_prompt.md # 核心「编码工艺」指令本体
读的顺序:先看 buildSystemPrompt 那份 sections 清单(:70),
认出「一段 = 一个可选关注点」;再随便点开一两个分段函数,验证「无内容→空串→被跳过」这个
一致的约定。最后读一遍 system_prompt.md 本体,看核心人格到底怎么写。
workspaceSeedContext 和 repo map 是什么、怎么生成的?」、
「confirmation_policy.md 里的安全策略和第二课的权限关卡怎么呼应?」,
或者「prompt 缓存在 provider 层是怎么落地的?」(那会引向 provider 适配那一课)。