第三部分 · Continual Harness

第 8 章:系统提示词与资源装配

不可变的基座,可变的补充层,以及一份写给模型的 RLM 使用说明书

提示词即架构

在一个「一切皆程序」的 Agent 里,系统提示词的角色变了:它不再只是行为约束,而是编程环境的使用说明书。模型要在这里学会一整套新语法——await rlm(...) 意味着什么、结果从哪里回来、harness 状态怎么读写、什么时候该 /refine。这份说明书写得好不好,直接决定 RLM 范式是否成立。

本章拆两件事:buildSystemPrompt() 如何把七层材料装配成最终提示词;其中「harness 状态块」这一层如何把持久记忆喂给模型。装配的其余材料——技能清单、项目上下文——来自 resource-loader.ts,本章一并交代。

七层装配

core/system-prompt.tsbuildSystemPrompt() 在默认路径上按固定顺序拼接:

图 1:系统提示词的七层装配。注意两处刻意:子代理指引紧跟基础提示词(先讲何时委派,再给规格菜单);harness 状态块在 customPrompt 路径下同样追加——换掉基座不等于失去记忆。

两个顺序决策有源码注释背书。子代理指引的位置:

// 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_LIMIT6每种 kind 至多渲染 6 条
DEFAULT_OVERVIEW_REFINEMENT_LIMIT5至多附最近 5 条 refine 事件
DEFAULT_OVERVIEW_CONTENT_LIMIT180单条内容压缩到 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.md / AGENTS.md 对照

Claude Code 的记忆是一个 markdown 文件,人写,整体拼入。Prime 的 harness 块是类型化条目的有预算渲染:agent 自己可写(第 9 章),每条带 kind/scope/version,注入前经过压缩与限额。项目文件(AGENTS.md/CLAUDE.md)仍然加载——在第七层 # Project Context——但那是人维护的静态知识;agent 的动态经验住在 harness 块里。两条通道,两种所有权。

资源的另一半:resource-loader

DefaultResourceLoadercore/resource-loader.ts,约 940 行)负责静态文件资源的启动期装配。它和 harness 是两条独立通道——resource-loader 加载 harness 状态,harness 由 AgentSession 直接读盘合并。它的装载清单:

资源来源(按优先级)注入点
extensionsCLI 临时路径 → settings/包管理器解析(按 enabled 过滤)→ 内联 factory工具 / 命令 / flags / hooks
skillsCLI → additional → bundled(内置)→ 包解析;名字冲突首者胜提示词技能段 + kernel 安装(Python 技能)
prompt templatesCLI → enabled → additional用户输入的 /name 展开
themesagentDir → 项目 .prime/agent/themes → CLITUI
上下文文件先全局 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.*),这个选择几乎是唯一正确的。

实践应用

  1. 把环境语义写进系统提示词,而不是指望模型自己悟。「IPython 是控制平面,不是被测系统的运行时」这类边界声明,防止的是高概率的范畴混淆。模型越强大,这类混淆造成的损失越大。
  2. 记忆注入要有预算。每 kind 6 条、每条 180 字符的限额,让持久状态的增长不会吃掉工作上下文。无预算的记忆最终会杀死记忆。
  3. 不变性要分清层级。「基础提示词不可变」是对 agent 而言的;部署者替换是另一层权力。把两层分开,才能同时拥有稳定的行为契约与可定制的部署形态。
  4. 共享文件用拉模型。多方写入的状态,消费方每次现读,比任何缓存失效协议都可靠。

总结

系统提示词在 Prime 里是七层装配物:训练好的 RLM 基座、子代理指引、harness 状态块、guidelines、项目上下文、技能清单、追加段。基座教模型如何住在 REPL 里,状态块把持久记忆以有预算的形式喂给它,resource-loader 则负责其余一切静态资源。基座对 agent 不可变,对部署者可替换——而无论换不换,记忆层都在。

下一章看这个记忆层如何被 agent 自己修改:/refine,一台谨慎的自我改进机器。