第三部分 · Continual Harness
第 8 章:系统提示词与资源装配
不可变的基座,可变的补充层,以及一份写给模型的 RLM 使用说明书
提示词即架构
在一个「一切皆程序」的 Agent 里,系统提示词的角色变了:它不再只是行为约束,而是编程环境的使用说明书。模型要在这里学会一整套新语法——await rlm(...) 意味着什么、结果从哪里回来、harness 状态怎么读写、什么时候该 /refine。这份说明书写得好不好,直接决定 RLM 范式是否成立。
本章拆两件事:buildSystemPrompt() 如何把七层材料装配成最终提示词;其中「harness 状态块」这一层如何把持久记忆喂给模型。装配的其余材料——技能清单、项目上下文——来自 resource-loader.ts,本章一并交代。
七层装配
core/system-prompt.ts 的 buildSystemPrompt() 在默认路径上按固定顺序拼接:
两个顺序决策有源码注释背书。子代理指引的位置:
// Appended AFTER the trained buildRlmPrompt prefix, and before the harness-state // menu, so the model reads when/why to delegate and then sees the concrete subagent // specs it can match against — the same ordering as Claude Code's Agent tool.
以及一个容易忽略的对称性:当部署者用 SYSTEM.md 整体替换基础提示词(customPrompt 路径)时,项目上下文、技能清单、child doctrine、harness 状态块照样追加。换基座是部署者的权力,但 agent 的持久记忆视野不因换基座而消失。
第一层:写给 RLM 的操作手册
buildRlmPrompt()(core/prompts/rlm.ts)是基础中的基础。它的开篇给模型定了性:"You are a general purpose agent that uses code to solve tasks."随后是工作目录、转录路径、递归深度(Recursive agent depth: N)、预装包清单与 uv pip install 指引。
最核心的一段是 IPYTHON_CONTROL_PROMPT——它教模型理解自己住在什么地方:
"IPython is the agent's long-lived notebook: a persistent control environment for reasoning, context management, state, tool orchestration, and recursive subcalls."
然后立刻划清一个模型极易混淆的边界:kernel 是控制平面,不是被测系统的运行时——
"Do not assume IPython is the native runtime of the external thing being investigated. … do not install dependencies into the IPython kernel just to make an external project import or run there. … in a Python repo use its documented commands,
uv run ...,.venv/bin/python ..."
这段话针对的是一个真实的失败模式:模型习惯性地想把项目装进自己的 kernel 里直接 import,结果环境冲突、依赖污染、语义错位。Prime 用提示词把「观察外部系统用外部系统自己的接口,IPython 只做协调」立为规矩。%%bash 是一次性子 shell、%cd/os.environ 才是持久的 shell 状态——这些第 4 章讲过的机制语义,全在这里以模型能理解的语言重述一遍。
同一层还承担了第 5–7 章机制的契约教学:await rlm(...) 在任务被接受时立即返回、永不返回孩子的答案、结果只经 agent_message 或文件到达、rlm.list_subagents() 恢复句柄、独立子代理分开 spawn 然后结束回合。模型对「准入即返回」的全部认知,来自这段反复打磨的文字。
rlm.ts 里有一段罕见的「术语治理」:Continual harness 指持久工件层(prompt/memory/skill/subagent),RLM 指运行时、kernel 与原生调用接口——并且明令模型不得发明 call_skill(...)、run_subagent(...) 之类包装器。当 API 面只有一个解释器时,模型的「自创词汇」会变成真实的行为漂移;把命名纪律写进系统提示词,是对这种漂移的第一道防线。
子代理的出生证明
深度大于 0 的会话会额外收到 buildChildAgentDoctrine():你由 parent 生成、任务标记 [task from parent]、需要回答时用 await agent_message.send(..., receiver_role="parent")。每个子代理从第一条系统提示词起就知道自己是谁、从哪来、话该往哪说。
第三层:harness 状态块
Continual Harness 的持久条目如何被模型看见?答案在 formatHarnessStateForPrompt()(core/refinement/refinement.ts)。它产出一个 # Continual Harness State 块,由 _rebuildSystemPrompt() 在每次构建时现场合并磁盘状态后注入——不是用户消息注入,是系统提示词追加。
渲染有明确的预算纪律:
| 常量 | 值 | 含义 |
|---|---|---|
DEFAULT_OVERVIEW_ENTRY_LIMIT | 6 | 每种 kind 至多渲染 6 条 |
DEFAULT_OVERVIEW_REFINEMENT_LIMIT | 5 | 至多附最近 5 条 refine 事件 |
DEFAULT_OVERVIEW_CONTENT_LIMIT | 180 | 单条内容压缩到 180 字符 |
条目行的形态:- [local:<id>] <title> (<path>, v<version>) ref=... args=...: <内容>。[local:]/[global:] 前缀是路由提示——模型据此知道读写该用哪个 scope。subagent 类条目有专属渲染:一份「任务名册」加上 await rlm('<task>') 的调用示范,源码注释自述这是 "the analogue of Claude Code's agent-type menu"。
块的顶部是使用纪律:何时 await refine.run()、local/global 的选择策略、以及第 5 章桥的调用契约。渲染参数还按能力降级——includeIpythonExamples / includeShellExamples / includeRefineExamples——没有 IPython 的会话里,提示词会明确告诉模型「这些条目只是路由与上下文提示,别尝试 await」。
Claude Code 的记忆是一个 markdown 文件,人写,整体拼入。Prime 的 harness 块是类型化条目的有预算渲染:agent 自己可写(第 9 章),每条带 kind/scope/version,注入前经过压缩与限额。项目文件(AGENTS.md/CLAUDE.md)仍然加载——在第七层 # Project Context——但那是人维护的静态知识;agent 的动态经验住在 harness 块里。两条通道,两种所有权。
资源的另一半:resource-loader
DefaultResourceLoader(core/resource-loader.ts,约 940 行)负责静态文件资源的启动期装配。它和 harness 是两条独立通道——resource-loader 不加载 harness 状态,harness 由 AgentSession 直接读盘合并。它的装载清单:
| 资源 | 来源(按优先级) | 注入点 |
|---|---|---|
| extensions | CLI 临时路径 → settings/包管理器解析(按 enabled 过滤)→ 内联 factory | 工具 / 命令 / flags / hooks |
| skills | CLI → additional → bundled(内置)→ 包解析;名字冲突首者胜 | 提示词技能段 + kernel 安装(Python 技能) |
| prompt templates | CLI → enabled → additional | 用户输入的 /name 展开 |
| themes | agentDir → 项目 .prime/agent/themes → CLI | TUI |
| 上下文文件 | 先全局 agentDir,再从 cwd 向根回溯祖先目录;每目录按 AGENTS.md → AGENTS.MD → CLAUDE.md → CLAUDE.MD 候选 | # Project Context |
| SYSTEM.md | 项目 .prime/agent/SYSTEM.md → 全局 agentDir | 整体替换基础提示词 |
| APPEND_SYSTEM.md | 同上 | 末尾追加 |
两个细节耐人寻味。上下文文件的候选列表连 AGENTS.MD 这种大写变体都收了——对现实世界里文件系统大小写行为的彻底妥协。而 SYSTEM.md 的存在说明 Prime 对「不可变基座」的界定是分层级的:对 agent 不可变,对部署者可替换——运行时没有任何路径能改写基础提示词,但部署时整份换掉是允许的。
重建时机:提示词不是写一次就完的
最终提示词由 _rebuildSystemPrompt()(agent-session.ts 约 L4276)构造,缓存在 _baseSystemPrompt 并赋给 this.agent.state.systemPrompt。触发重建的时机清单本身就是系统功能的索引:
- refine 应用后——harness 状态变了(第 9 章);
- rlm-max-depth 变更后——递归段要隐藏或显现(第 6 章);
- 工具集变更后——能力降级参数要重算;
- 会话分支切换后——分支可能带着不同的 harness 状态。
每次重建都现场重读磁盘(_loadMergedHarnessState())——harness 状态是拉模型,不是推模型:没有缓存失效问题,代价是一次文件读取。考虑到 kernel 端也在写同一个文件(rlm.harness.*),这个选择几乎是唯一正确的。
实践应用
- 把环境语义写进系统提示词,而不是指望模型自己悟。「IPython 是控制平面,不是被测系统的运行时」这类边界声明,防止的是高概率的范畴混淆。模型越强大,这类混淆造成的损失越大。
- 记忆注入要有预算。每 kind 6 条、每条 180 字符的限额,让持久状态的增长不会吃掉工作上下文。无预算的记忆最终会杀死记忆。
- 不变性要分清层级。「基础提示词不可变」是对 agent 而言的;部署者替换是另一层权力。把两层分开,才能同时拥有稳定的行为契约与可定制的部署形态。
- 共享文件用拉模型。多方写入的状态,消费方每次现读,比任何缓存失效协议都可靠。
总结
系统提示词在 Prime 里是七层装配物:训练好的 RLM 基座、子代理指引、harness 状态块、guidelines、项目上下文、技能清单、追加段。基座教模型如何住在 REPL 里,状态块把持久记忆以有预算的形式喂给它,resource-loader 则负责其余一切静态资源。基座对 agent 不可变,对部署者可替换——而无论换不换,记忆层都在。
下一章看这个记忆层如何被 agent 自己修改:/refine,一台谨慎的自我改进机器。