Claude Code 源码分析 by Codex (3) – 提示工程

Arc 发布于 2026-04-01 517 次阅读


前言

基于泄露的Claude Code最新版本:v2.1.88 ,使用GPT 5.4进行分析。不绕弯子,直接复制原始回答。

Claude Code CLI 提示词工程与多智能体系统提示词分析

一句话结论

这个仓库最值得学习的地方,不是它写了一个很长的 system prompt,而是它把“提示词”拆成了一整套分层系统:主系统提示词、工具说明、技能提示、Agent 专用提示、记忆提示、压缩提示、安全分类器提示、Hook 提示、文档更新提示、建议生成提示,以及运行时插入的附件消息。它不是在“写一段好 prompt”,而是在设计一套可缓存、可分工、可恢复、可压缩、可持久化、可安全约束的提示词操作系统。

如果把它只理解成“prompt 写得比较仔细”,会低估它。更准确的理解是:它把 prompt 当成运行时架构的一部分。


研究范围与方法

这份文档不是凭印象写的,而是顺着仓库里所有主要 prompt 入口逐层梳理出来的。重点阅读的源码包括但不限于:

  • 主系统提示词与装配:src/constants/prompts.tssrc/constants/systemPromptSections.tssrc/utils/systemPrompt.tssrc/main.tsx
  • 查询主循环与运行时注入:src/query.tssrc/utils/attachments.ts
  • 文件化规则与记忆注入:src/utils/claudemd.tssrc/memdir/memdir.tssrc/memdir/teamMemPrompts.tssrc/memdir/findRelevantMemories.ts
  • 多 Agent 与协作提示:src/coordinator/coordinatorMode.tssrc/tools/AgentTool/prompt.tssrc/tools/SendMessageTool/prompt.tssrc/tools/TeamCreateTool/prompt.tssrc/utils/swarm/teammatePromptAddendum.ts
  • 专用 Agent system prompt:src/tools/AgentTool/built-in/exploreAgent.tssrc/tools/AgentTool/built-in/planAgent.tssrc/tools/AgentTool/built-in/verificationAgent.ts
  • 技能与命令式 prompt 资产:src/tools/SkillTool/prompt.tssrc/skills/loadSkillsDir.tssrc/utils/promptShellExecution.ts
  • 记忆维护与摘要:src/services/SessionMemory/prompts.tssrc/services/extractMemories/prompts.tssrc/services/compact/prompt.ts
  • 安全与微型提示:src/utils/permissions/yoloClassifier.tssrc/utils/hooks/execPromptHook.tssrc/services/MagicDocs/prompts.tssrc/services/PromptSuggestion/promptSuggestion.tssrc/utils/claudeInChrome/prompt.tssrc/buddy/prompt.ts

所以这份分析的对象,不只是“system prompt”,而是整个仓库里凡是会对模型行为产生定向约束的文字协议


先建立一个核心心智模型

1. 这个仓库没有一个 prompt,而是一个 prompt stack

在很多 AI 应用里,所谓 prompt engineering 指的是:

  1. 写一段 system prompt
  2. 拼一点上下文
  3. 把用户消息塞进去

这个仓库不是这样。它更像下面这张堆栈:

  1. 基础系统提示词:定义身份、语气、做事边界、工具观、风险观
  2. 动态系统尾部:环境、语言、输出风格、记忆说明、MCP 指令、scratchpad、token budget 等
  3. CLI/配置追加层:--system-prompt--append-system-prompt、teammate addendum、assistant addendum、proactive addendum
  4. 工具说明层:每个 tool 的 description/prompt 都是隐式 prompt
  5. 运行时附件层:nested_memoryrelevant_memoriesskill_listingagent_listing_deltamcp_instructions_delta
  6. 子代理/协调器专用 prompt:coordinator prompt、verification prompt、explore prompt、plan prompt、custom agent prompt
  7. 记忆维护 prompt:session memory update、extract memories、combined memory prompt
  8. 压缩与延续 prompt:compact prompt、away summary、session memory compact
  9. 安全与微任务 prompt:auto-mode classifier、prompt hook、prompt suggestion、Magic Docs、Chrome automation hint

这就是为什么这个仓库看起来“到处都在写 prompt”。因为在这里,prompt 不只是“发给模型的一段话”,而是整个系统的控制介质。

2. 这个仓库把 prompt 视为协议,而不是文案

这点尤其重要。

很多新手写 prompt,思路是“把意思表达清楚”。这个仓库的思路是“让模型和系统之间建立稳定协议”。

比如:

  • coordinatorMode.ts 不是在“鼓励协作”,而是在规定协调者如何解释 <task-notification> XML
  • SendMessageTool/prompt.ts 不是在“提醒可以发消息”,而是在定义“纯文本对队友不可见”
  • SessionMemory/prompts.ts 不是在“请你总结一下”,而是在定义“只能编辑既有结构,不能破坏模板”
  • compact/prompt.ts 不是在“请总结上下文”,而是在定义“只输出 <analysis><summary>,禁止工具调用”

这类 prompt 的价值,在于它减少了模型“自己脑补交互规则”的空间。


提示词是怎么进入运行时的

这一部分非常关键,因为它决定了什么叫“真正的 prompt 工程”。

1. 入口阶段:main.tsx 先做第一轮 prompt 叠加

src/main.tsx 里,CLI 参数并不是简单存起来,而是直接影响 prompt 组合:

  • --system-prompt / --system-prompt-file 可以替换自定义主 prompt
  • --append-system-prompt / --append-system-prompt-file 可以附加额外指令
  • 如果当前会话是 teammate,会自动拼上 TEAMMATE_SYSTEM_PROMPT_ADDENDUM
  • 如果启用了 Chrome 相关能力,会在合适的路径上拼 Chrome skill hint 或 Chrome prompt
  • 如果是 proactive mode,会拼一段 # Proactive Mode
  • 如果是 assistant mode,也会再叠一层 addendum

这说明运行时真正使用的 prompt,不是静态文件,而是会话模式、CLI 参数、平台能力、身份角色共同计算出的结果

2. 主系统提示词:getSystemPrompt() 不是一段大字符串,而是分节拼装

src/constants/prompts.ts 里的 getSystemPrompt() 非常值得研究。它没有把所有内容硬写成一个几千字字符串,而是拆成很多 section:

  • intro
  • system
  • doing tasks
  • actions with care
  • using your tools
  • tone and style
  • output efficiency
  • session guidance
  • memory
  • env info
  • language
  • output style
  • MCP instructions
  • scratchpad
  • function result clearing
  • summarize tool results
  • token budget

这种设计的好处至少有四个:

  1. 每个 section 的职责单一,便于调优
  2. 可以按 feature gate、环境、模型、用户类型做条件启用
  3. 可以缓存 section 结果,而不是每轮都重算整段 prompt
  4. 可以在出现坏行为时,精准修某一层,而不是重写整个提示词

这是大型 agent 系统和 demo 级 prompt 最大的差别之一。

3. 最值得学的设计:静态前缀和动态尾部的分界

src/constants/prompts.ts 里定义了:

export const SYSTEM_PROMPT_DYNAMIC_BOUNDARY =
  '__SYSTEM_PROMPT_DYNAMIC_BOUNDARY__'

注释写得很明确:这个标记前面的内容可以作为跨组织缓存的稳定前缀,后面是用户/会话相关动态内容。

再配合 src/constants/systemPromptSections.ts 里的:

  • systemPromptSection():可缓存 section
  • DANGEROUS_uncachedSystemPromptSection():会打破 cache 的 section

这一套设计非常高级。它说明作者已经把 prompt engineering 拉到了推理质量 + 成本 + 延迟共同优化的层面。

很多系统只问“这样写模型更聪明吗”,这个仓库还在问:

  • 这样写会不会让 prompt cache 失效?
  • 哪些信息必须每轮刷新?
  • 哪些东西可以做成稳定前缀?
  • 如何把动态列表从 tool description 挪到 attachment,避免 schema 变动导致 cache bust?

这是非常成熟的系统工程视角。

4. 最终仲裁:buildEffectiveSystemPrompt() 决定谁覆盖谁

src/utils/systemPrompt.ts 里把优先级写得很清楚:

  1. overrideSystemPrompt 直接替代所有
  2. coordinator mode 下使用 coordinator prompt
  3. 主线程自定义 agent prompt
  4. 用户自定义 system prompt
  5. 默认 system prompt
  6. appendSystemPrompt 最后追加

这解决了一个很常见但经常被忽视的问题:当系统里有很多 prompt 来源时,最终谁说了算?

如果没有这一层明确仲裁,系统很容易出现:

  • coordinator 指令和默认 prompt 冲突
  • agent prompt 不知道该替换还是叠加
  • 用户 CLI 指令被默认 prompt 部分吞掉

这个文件其实是在做“提示词路由策略”。

5. 真正送进查询循环之前,还会再叠一层系统上下文与附件

src/query.ts 里,system prompt 在发 API 之前还会经过:

  • appendSystemContext(systemPrompt, systemContext)
  • prependUserContext(messagesForQuery, userContext)
  • getAttachmentMessages(...)

这意味着系统不是只有“system + user messages”,而是还有一类极其重要的运行时注入:attachment messages。

这就是后文会反复提到的核心观点:

这个仓库里最重要的 prompt,很多时候不是 system prompt,而是运行时附件。


主系统提示词的工程技巧

1. 它不是泛泛而谈,而是在针对具体失败模式下药

src/constants/prompts.ts 里有很多段落,一看就知道不是凭感觉写的,而是在对抗模型的特定坏习惯。

比如:

  • “不要超出用户请求范围去重构”
  • “不要为不可能发生的场景加错误处理”
  • “不要创建一次性 helper 抽象”
  • “如果你没跑验证,就不要假装验证过”
  • “不要把失败的测试说成通过”
  • “只有在真正卡住时才问用户”

这类指令的意义在于,它们不是抽象的“be helpful”,而是对模型真实常见失败模式的硬矫正:

  • 过度工程
  • 伪完成
  • 虚假验证
  • 因为怕出错而频繁上抛给用户
  • 把编码风格建议误当成需求

这种 prompt 写法的好处是:不是在给模型增加人格,而是在减模型的系统性偏差。

2. prompt 里有不少“反过度防御”设计

一个很微妙但很成熟的点是,这个仓库并不是一味让模型更保守,而是在做平衡。

例如它同时要求:

  • 对危险动作要谨慎
  • 但不要因为一点摩擦就立刻问用户
  • 要忠实汇报失败
  • 但已经验证成功的事情不要过度退缩式表述
  • 不要引入额外复杂度
  • 但也不要半成品交付

这其实是在对抗另一种失败模式:过度保守导致系统没法真的做事。

很多 prompt 只会越写越谨慎,最后把模型写成“什么都想确认一下”。这个仓库显然在努力避免这一点。

3. BRIEF_TOOL_PROMPT 体现了一个常被忽视的设计点:用户真正看到什么

src/tools/BriefTool/prompt.ts 里有一句很有代表性:

“Text outside this tool is visible in the detail view, but most won't open it — the answer lives here.”

还有一段更有价值:

  • 能立即回答就直接回答
  • 如果需要先看点东西,就先用一行 ack
  • 长任务遵循 ack -> work -> result
  • 中间只在真的有新信息时给 checkpoint

这说明 prompt 工程不只是让模型“回答正确”,而是让模型适配产品交互现实。

它在训练模型理解:

  • 什么文本用户实际会看到
  • 什么信息应该先回
  • 什么更新属于噪声
  • 何时应该给阶段性反馈

这类 prompt 的好处非常实际:减少“模型正在干活但用户以为它卡住了”的体验问题。

4. 环境信息不是随便拼进去,而是结构化注入

computeEnvInfo()computeSimpleEnvInfo() 会把这些内容明确告诉模型:

  • working directory
  • 是否 git repo
  • 是否 worktree
  • platform
  • shell
  • OS version
  • model knowledge cutoff
  • Claude 产品线与最新模型建议

同时,enhanceSystemPromptWithEnvDetails() 还会额外告诉 Agent:

  • bash 每次会重置 cwd,所以要用绝对路径
  • 最终回答里要给绝对路径
  • 不要用 emoji

很多系统把环境信息当“附带说明”,这里把它做成了行为约束的一部分。好处是:

  • 降低路径错误
  • 减少“在错误目录执行命令”
  • 让子 Agent 的输出更容易被主线程消费

这套系统真正厉害的地方:工具说明本身就是 prompt

很多人只盯 system prompt,会漏掉一个极其重要的事实:在 tool-calling 系统里,工具说明就是另一层 prompt

而这个仓库把这一点用到了非常深。

1. SkillTool/prompt.ts:把技能调用做成“阻塞性协议”

这里最有代表性的句子是:

  • “When a skill matches the user's request, this is a BLOCKING REQUIREMENT”
  • “NEVER mention a skill without actually calling this tool”
  • “If you see a <command-name> tag ... the skill has ALREADY been loaded”

这三句非常厉害。

它不是说“技能可能有帮助”,而是把技能调用变成一个协议:

  • 命中了技能就必须先调 tool
  • 不允许嘴上提技能但不执行
  • skill 已加载时不能重复触发

这么写的好处是:

  1. 防止模型“知道有 skill 但偷懒不调用”
  2. 防止重复载入 skill 造成上下文膨胀
  3. 把技能系统从“建议性机制”提升为“执行性机制”

2. AgentTool/prompt.ts:这是多 Agent 世界的操作手册

这份 prompt 几乎像一本小册子,尤其是下面几类句子特别值得学:

  • “Don't peek.” 不要去读后台 fork 的输出文件
  • “Don't race.” 后台 fork 没返回之前,不要猜它的结果
  • “Writing a fork prompt... the prompt is a directive”
  • “Never delegate understanding.”

这几句几乎可以当作多智能体 prompt 工程的黄金法则。

为什么 “Don't peek” 很重要

如果主代理去偷看后台子代理的 transcript:

  • 会把大量中间工具噪声拉回主上下文
  • 会破坏“fork 的意义就是把噪声隔离出去”
  • 会让主代理在不完整结果基础上提前行动

也就是说,这不是一个礼貌问题,而是一个上下文污染控制问题

为什么 “Don't race” 很重要

多 Agent 系统里一个非常常见的坏行为是:主代理启动了子代理,然后马上自己脑补一句“应该已经查到是 X 了”。

这个仓库明确禁止这种行为。因为在异步任务系统里,最危险的不是没结果,而是提前伪造结果

为什么 “Never delegate understanding” 很重要

这是我认为整个仓库里最高级的一条 prompt 原则之一。

它明确反对这种写法:

  • “Based on your findings, fix the bug”
  • “根据研究结果去实现”

因为这其实是在把“理解”继续外包给下一个 agent。正确做法是:

  1. 研究 agent 给出发现
  2. 协调者自己读懂
  3. 协调者把发现综合成一份具体 spec
  4. 再交给实现 agent

这条原则的好处巨大:

  • 保证协调者真的掌握了问题
  • 避免误解在 agent 链条里层层传递
  • 提高后续 prompt 的精确性

这就是为什么 coordinatorMode.ts 里会把 “Always synthesize” 写成最重要的工作。

3. SendMessageTool/prompt.ts:把通信媒介说死

这里最重要的一句是:

“Your plain text output is NOT visible to other agents — to communicate, you MUST call this tool.”

这是多 Agent prompt 里最应该写、但很多系统都没写清楚的一句话。

多智能体系统最怕的不是模型不会沟通,而是模型对“谁能看到什么”理解错了。

如果没有这句,模型很容易产生以下误判:

  • 我在普通文本里说“你去做 A”,其他 agent 应该能看到吧
  • 我对用户说“让 researcher 去检查”,researcher 应该知道吧

这个 prompt 直接消灭了这种歧义。

它的好处是:

  • 通信通道单一,便于系统追踪
  • 模型不会把普通回答当成队内广播
  • 邮箱/消息总线才能成为唯一可信协作介质

4. TeamCreateTool/prompt.ts:把团队协作的日常误解提前消掉

这份 prompt 很长,但它解决的其实是多 Agent 系统里最常见的“日常认知错误”:

  • idle 不是故障
  • 队友发完消息立刻 idle 是正常流
  • message 会自动送达,不需要手动查 inbox
  • 不要用 terminal 工具去窥探队友状态
  • 不要发结构化 JSON 状态消息,普通文本加 TaskUpdate 即可

这类内容看起来不像“高级 prompt”,其实非常高级。因为它不是在提升模型智力,而是在减少多 Agent 协作中的协议误解成本

很多多 Agent 系统失败,不是因为模型不会写代码,而是因为它们对工作流状态机理解错了。

5. claudeInChrome/prompt.ts:工具提示里甚至包含故障排查哲学

这份 prompt 里有几个很有代表性的规则:

  • 每次 session 一开始先 tabs_context_mcp
  • 不要触发 JS alert / prompt / confirm
  • 如果复杂度失控或重试 2-3 次失败,就停下来问用户
  • tool search 开启时,必须先用 ToolSearch 加载 Chrome 工具

这说明工具 prompt 不仅在教“怎么调用参数”,还在教:

  • session startup 的正确顺序
  • 工具的已知故障模式
  • 何时停止探索,避免陷入 rabbit hole

这种写法的好处是把高频踩坑前置成文字协议,而不是事后让模型从报错里自己学。

6. 技能在这个仓库里不是“文本模板”,而是“带元数据的 prompt 程序”

这一点如果不专门点出来,很容易被忽略。

src/skills/loadSkillsDir.ts 读取 skill markdown 时,不只是把正文读出来,而是会解析 frontmatter 中的大量元数据。parseSkillFrontmatterFields() 涉及的字段包括:

  • description
  • allowed-tools
  • arguments
  • when_to_use
  • model
  • disable-model-invocation
  • user-invocable
  • hooks
  • executionContext
  • agent
  • effort
  • shell
  • paths

这意味着 skill 在这个系统里不是“几段说明文字”,而是一个带执行属性的 prompt 资产。它同时定义:

  • 什么时候应该被触发
  • 被触发后能用哪些工具
  • 是否允许用户直接调用
  • 应该在哪种 agent / shell / effort 下运行
  • 是否受路径匹配约束
  • 是否有 hook

再结合 src/utils/promptShellExecution.ts! 内联命令与代码块命令替换能力,可以得到一个很强的结论:

这里的 skill 更像“声明式的 prompt 程序”,而不是普通的 markdown 帮助文档。

为什么这个设计好?

  1. skill 可以被模型发现、被用户调用、被系统调度,而不是只存在于开发者脑海里。
  2. prompt 资产的权限、上下文、执行环境都有显式元数据,不需要在正文里含糊表达。
  3. 同样一份 skill 正文,可以在不同会话里以不同方式装配,不必复制多套 prompt。
  4. paths frontmatter 还让 skill 能像 .claude/rules 一样具备条件激活能力,这意味着“对某类文件生效的 prompt”不必每轮都硬塞进 system prompt。

这对多智能体系统特别有启发:
当 prompt 资产开始增多时,最好的做法不是把它们继续堆进一个大 system prompt,而是把 prompt 资产模块化、元数据化、可寻址化


多 Agent 提示词工程:这个仓库到底是怎么写协作 prompt 的

如果只看运行时实现,你会以为这个仓库的多 Agent 核心在于任务、队列、mailbox、task-notification。
但如果只看 prompt,你又会发现另一半真相:这套运行时之所以能工作,是因为提示词把每个角色的认知边界定义得非常明确。

1. 协调者 prompt 的本质:不是让主代理更强,而是让主代理别做错位工作

src/coordinator/coordinatorMode.ts 一上来就写:

  • 你是 coordinator
  • 你的职责是帮助用户、指挥 workers、综合结果、跟用户沟通
  • 能直接回答的问题就直接答,不要滥 delegate
  • worker 结果和系统通知不是 conversation partners

这几句的价值很高。它把协调者从一个“万能大脑”收缩成一个明确角色:

  • 不是所有事情都自己做
  • 也不是所有事情都外包
  • 自己最重要的工作是综合和编排

很多多 Agent 系统没有先把 coordinator 的角色说清楚,导致主代理要么事无巨细亲自做,要么把一切都扔给 worker。

2. <task-notification> 协议为什么要写进 prompt

代码层面,worker 结果会以用户消息形态回流。
prompt 层面,coordinator system prompt 非常明确地解释:

  • 这类消息虽然长得像 user-role message,但不是用户在说话
  • 它们以 <task-notification> 开头
  • 里面的 <task-id><status><summary><result> 都有明确含义

这样写的好处是:

  1. 模型不需要自己猜“这是谁发的”
  2. 模型知道什么时候该综合结果,什么时候该继续某个 worker
  3. 系统可以把内部事件伪装成标准消息流,而不必新造一套完全不同的 API

也就是说,prompt 在这里承担的是协议解释器的功能。

3. “Always synthesize” 是整个多 Agent prompt 系统的中心句

coordinatorMode.ts 中最关键的部分,不是“并发是你的超能力”,而是:

“Always synthesize — your most important job”

并且它还给出正反例:

坏例子:

  • “Based on your findings, fix the auth bug”

好例子:

  • 直接给文件路径、行号、成因、要改什么、完成标准

这是因为作者非常清楚一个多 Agent 系统的核心瓶颈不是“有没有更多 worker”,而是协调层是否真的理解了上游发现

如果协调者不综合,只会发生三件事:

  1. 错误会跨 agent 传播放大
  2. prompt 越传越空泛
  3. worker 会被迫承担理解、规划和执行三种职责,导致输出发散

所以这句 prompt 实际上是在稳定整个 agent pipeline。

4. teammatePromptAddendum 的作用:定义“你不是直接对用户讲话的主角”

src/utils/swarm/teammatePromptAddendum.ts 很短,但很关键:

  • 你是团队里的 agent
  • 纯文本不会被队友看见
  • 必须用 SendMessage
  • 用户主要和 team lead 交互

这段 prompt 的价值在于“角色降噪”。

如果不给 teammate 这层提醒,worker 很容易进入一种错误心态:

  • 既想回应用户
  • 又想给队友发进度
  • 又不知道自己的文本到底被谁看见

这类歧义会直接降低协作效率。短 prompt 在这里反而比长 prompt 更有效,因为它只解决一个问题:角色边界

5. 专用 agent prompt 的写法:不是“更聪明”,而是“更窄、更狠、更反作弊”

Explore Agent

src/tools/AgentTool/built-in/exploreAgent.ts 里几乎全部内容都在强调:

  • 只读
  • 禁止任何写入、临时文件、重定向、heredoc
  • 只能做 read-only bash
  • 快速返回
  • 尽可能并行搜索

这是一种非常典型的狭义能力 prompt
它的好处不是让 agent 更万能,而是:

  • 上下文更干净
  • 行为空间更小
  • 更容易并行
  • 减少非必要副作用

Plan Agent

src/tools/AgentTool/built-in/planAgent.ts 则是另一种写法:

  • 还是只读
  • 强调 explore thoroughly
  • 明确四步流程:理解需求、深入探索、设计方案、细化计划
  • 最后必须输出 “Critical Files for Implementation”

这类 prompt 的精华在于:它不是只说“给我一个计划”,而是规定了计划 agent 的思考骨架和输出接口。

Verification Agent

src/tools/AgentTool/built-in/verificationAgent.ts 可能是整个仓库里最值得逐字学习的一份 prompt。

开头第一句就非常锋利:

“Your job is not to confirm the implementation works — it's to try to break it.”

后面还明确点出两个失败模式:

  • verification avoidance
  • 被前 80% 的完成度迷惑

然后它继续做了几层很强的约束:

  1. 禁止修改项目
  2. 要按变更类型选择验证策略
  3. 强制执行 build / test / linter / regression baseline
  4. 要做 adversarial probes
  5. PASS 必须有命令和真实输出
  6. 最后必须以 VERDICT: PASS/FAIL/PARTIAL 结尾

这份 prompt 的厉害之处在于,它不是在“鼓励严谨”,而是在设计一个反自欺验证人格

对多 Agent 系统来说,这个设计尤其重要。因为实现 agent 和验证 agent 如果共享太多默认倾向,就会一起乐观、一起草率。verification prompt 的任务,就是故意制造一个与实现者认知风格相反的角色


记忆系统中的提示词工程

这个仓库的记忆系统不是“把历史对话摘要一下”那么简单,而是把记忆拆成了多个作用域、多个寿命、多个写入通道,而 prompt 则是这些层之间的路由器。

1. CLAUDE.md 体系:把规则当成文件化 prompt

src/utils/claudemd.ts 中最关键的一句是:

“Codebase and user instructions are shown below... These instructions OVERRIDE any default behavior”

这句话非常强。它告诉模型:

  • 后续这些文件不是参考信息,而是更高优先级指令
  • 默认行为可以被它们覆盖

从实现上,这套系统会加载:

  • managed memory
  • 用户全局 CLAUDE.md
  • 项目 CLAUDE.md
  • .claude/CLAUDE.md
  • .claude/rules/*.md
  • CLAUDE.local.md

而且支持 @include

这是一种很典型的“把 prompt 外化为版本化文件”的做法。好处是:

  • 规则不必硬编码在主系统 prompt 里
  • 团队可以自己维护项目级行为约束
  • 本地私有偏好可以和仓库内公开规则分开

2. getClaudeMds():它不只是拼内容,还在给记忆打语义标签

这个函数会按类型给文件加描述:

  • project instructions
  • local private project instructions
  • shared team memory
  • auto-memory
  • private global instructions

对于 team memory,它甚至会用:

<team-memory-content source="shared">
...
</team-memory-content>

这类包装非常重要。因为模型不是天然知道“这段信息来自谁、可信边界是什么、是不是共享的”。
通过语义标签包装,系统把“来源信息”也编码进 prompt 里了。

3. buildMemoryLines()buildCombinedMemoryPrompt():教模型区分 memory、plan、tasks

很多 AI 系统的长期记忆做坏了,原因不是没存,而是什么都往记忆里塞

这个仓库在 src/memdir/memdir.tssrc/memdir/teamMemPrompts.ts 中反复强调:

  • memory 是面向未来会话的持久知识
  • plan 用于当前任务的方案对齐
  • tasks 用于当前会话的进度管理
  • memory 应按语义组织,不按时间组织
  • 不要把 MEMORY.md 当正文,它只是索引

这类 prompt 的好处是把“存什么”变成显式分类问题,而不是让模型凭直觉决定。

4. findRelevantMemories.ts:相关记忆召回也靠 prompt

这里非常值得学。
系统没有把所有 memory 一股脑塞回上下文,而是先扫描 frontmatter/header,然后再发一个 side query:

  • 给模型用户 query
  • 给模型 memory manifest
  • 要求最多选 5 个“明确有帮助”的记忆
  • 不确定就不要选
  • 最近刚在用的工具文档不要再选,除非里面是 gotchas/warnings

这其实是在做“记忆检索前的 LLM 选择器”。

为什么这么写好?

  1. 便宜:先看 metadata,不先读正文
  2. 克制:只选明显相关的
  3. 避免把上下文窗口浪费在泛用说明上
  4. 把“活跃使用工具时,普通 API 文档是噪声”这种经验显式写进 prompt

这是对“记忆召回不是越多越好”的非常成熟理解。

5. attachments.ts:相关记忆不是改 system prompt,而是以附件注入

这一步是整个记忆 prompt 工程里最值得抄的一点之一。

findRelevantMemories() 选出文件后,attachments.ts 会把它们包装成 relevant_memories attachment,而不是把这些东西硬并进主 system prompt。

这样做的好处:

  • 不会把主 prompt 变成每轮波动的大字符串
  • 附件可以按 turn 逐步出现
  • compact 之后旧附件消失,系统可以重新 surface
  • 不同类上下文有了结构化类型,而不是都混在一段文字里

这就是“attachment is prompt”的最好例子。

6. Session Memory 的 prompt:约束写法远比“会总结”重要

src/services/SessionMemory/prompts.ts 里那段 update prompt 非常有代表性。它反复强调:

  • 这条 instruction 不属于真实会话
  • 只能更新 notes file
  • 不能改 section header
  • 不能改斜体模板说明
  • 不要提 note-taking 本身
  • 要写得具体、信息密度高
  • 每个 section 有上限
  • Current State 必须始终更新

这个设计说明作者知道一个事实:
总结 agent 最容易犯的错,不是不会总结,而是破坏结构。

因此这个 prompt 的目标不是提升文采,而是提升结构稳定性和可持续更新性。

7. 长期记忆抽取 prompt:它在约束“操作顺序”

src/services/extractMemories/prompts.ts 也很有意思。它不只说“请更新 memory”,还告诉模型:

  • 你有有限 turn budget
  • Edit 需要先 Read
  • 最优策略是第一轮并行读,第二轮并行写
  • 不要浪费 turn 去再调查代码、grep 源码、跑 git
  • 只根据最近若干消息提取

这说明 prompt 在这里不仅在定义内容标准,也在定义执行策略

这是非常高级的 prompt 工程:把工具约束、turn budget 和最优操作顺序编码成策略提示。

8. Team memory prompt:把 scope 说清楚,是共享记忆系统成败的关键

src/memdir/teamMemPrompts.ts 明确区分:

  • private memory
  • team memory

并强调:

  • team memory 是共享和同步的
  • 不要把敏感数据写进 team memory
  • 用户说忽略 memory 时,要当 MEMORY.md 为空

这类 prompt 的价值非常实际:

  • 降低越权共享
  • 降低“私有偏好被错误写进团队记忆”的风险
  • 给模型一个对“共享知识边界”的明确判断框架

压缩、摘要与上下文延续:prompt 工程不止发生在开头

很多团队理解 prompt engineering 时,只关注“第一次发给模型的 system prompt”。这个仓库告诉你,真正的 agent 系统里,上下文压缩和继续工作时的提示词同样重要

1. compact/prompt.ts:先下死命令,不准调工具

这份 prompt 一上来就是:

  • “CRITICAL: Respond with TEXT ONLY”
  • “Do NOT call any tools”
  • “Tool calls will be rejected and will waste your only turn”
  • “Your entire response must be plain text: an <analysis> block followed by a <summary> block”

这是很典型的工程化 prompt 写法。

如果不写这段,压缩 agent 可能会:

  • 尝试重新读文件
  • 试图跑 bash
  • 因为被拒绝而浪费唯一一次摘要机会

这就说明:当你把原模型 prompt 迁移到一个新任务时,第一件事不是“给任务说明”,而是清除原行为惯性

2. 压缩 prompt 不是泛化总结,而是为“继续开发”设计的结构化摘要

它要求总结包含:

  • Primary Request and Intent
  • Key Technical Concepts
  • Files and Code Sections
  • Errors and fixes
  • Problem Solving
  • All user messages
  • Pending Tasks
  • Current Work
  • Optional Next Step

这不是一般聊天摘要会写的内容。它是为代码代理继续工作而设计的摘要 schema。

尤其是:

  • 要列出所有 user messages
  • 要保留文件与代码片段
  • 要包含当前正在做的事
  • Optional Next Step 必须与最近显式请求直接对齐

这类 prompt 的好处是:压缩后的上下文仍然能支撑连续开发,而不仅仅是“知道之前聊过什么”。

3. Session memory compact 体现出另一层想法:不是每次都重新做一般性总结

从相关实现可以看出,这个系统会在某些情况下优先使用 session memory 作为压缩延续材料,而不是总重新跑一般 compact。

这背后的思想很重要:

  • 普通摘要适合一次性压缩
  • session memory 适合长期维护当前任务的“工作态”

也就是说,它把“压缩历史”和“维护工作态”拆成了两个 prompt 机制,而不是混成一种总结。

4. 为什么要保留 skill/plan 等 prompt 资产穿越 compact

相关实现里有明确注释:compact 后需要保留 invoked skill 内容和 plan mode 相关上下文。

这件事非常关键。因为在一个复杂 agent 系统里,被压掉的不仅是“事实”,还可能是当前行为所依赖的规则资产。

如果 compact 之后 skill prompt 丢了,会发生什么?

  • 模型还记得“要做 commit”
  • 但忘了具体 skill 的行为规则和操作边界

这会导致行为漂移。

所以真正的 prompt 工程不只是“让 prompt 生效”,还要让 prompt 在上下文压缩后继续生效。


两个完整流转案例:把 prompt 如何接力讲透

前面讲的是分层原则。下面用两条完整链路,把这套仓库里的 prompt 是怎样在运行时接力的讲清楚。

案例一:一次“协调者 -> 研究 worker -> 实现 worker -> 验证 worker”的多 Agent 回合

假设用户说:“修一下 auth 模块里的空指针问题。”

第 1 步:主线程 prompt 先确定你是谁

系统先由 main.tsxgetSystemPrompt()buildEffectiveSystemPrompt() 装配出最终 prompt。

如果当前会话处在 coordinator mode,那么默认主 prompt 会被 getCoordinatorSystemPrompt() 替换。此时主模型学到的第一件事不是“怎么写代码”,而是:

  • 你是 coordinator
  • worker 结果通过 <task-notification> 回来
  • 你要综合,不要乱转发
  • 并发是你的超能力

也就是说,在第一步,系统先用 prompt 确定角色身份

第 2 步:工具说明决定你如何理解协作动作

同一个 API 请求里还会带上:

  • AgentTool 的 prompt
  • SendMessageTool 的 prompt
  • 可能的 TeamCreateTool / TaskCreateTool prompt

所以 coordinator 不只是“知道自己是协调者”,还知道:

  • AgentTool 用于拉起 worker
  • 不能偷看 fork 输出
  • 不能猜 worker 还没返回的结果
  • 普通文本不会发给其他 agent
  • 真要通信必须用 SendMessage

也就是说,第二步不是任务内容,而是建立协作动作语义

第 3 步:运行时附件补充动态上下文

query.ts 在主循环里会继续注入 attachment:

  • agent_listing_delta
  • skill_listing
  • nested_memory
  • relevant_memories
  • teammate_mailbox
  • 可能还有 mcp_instructions_delta

这些附件让 coordinator 知道:

  • 当前有哪些 agent 类型
  • 最近有哪些相关记忆
  • 当前有哪些技能已经可用
  • 是否有队友新消息

这一步的关键在于:这些东西没有硬塞进稳定 system prompt,而是变成运行时动态上下文。

第 4 步:coordinator 根据 prompt 学到的规则发起研究 worker

协调者如果写得好,应该像 coordinatorMode.tsAgentTool/prompt.ts 要求的那样:

  • 不说“去看看 auth 怎么回事”
  • 而是给研究问题、范围、输出格式
  • 并尽量让研究 worker 保持只读

例如它可能调用:

  • 一个 Explore agent 查空指针来源
  • 一个 Explore agent 查 auth tests

这里 prompt 的作用是让 coordinator 明白:
研究要并行,研究 prompt 要自包含,研究 worker 是“搜与读”,不是“顺手开始改”。

第 5 步:worker 返回后,<task-notification> 通过用户消息形态回流

实现层面,这个通知是一条 user-role message。
prompt 层面,coordinator prompt 已经预先告诉模型如何解释它。

于是 coordinator 不会把它当作用户需求,而会把它当作内部结果:

  • 读取 <status>
  • 读取 <summary>
  • 如果 <result> 有具体文件与行号,就将其纳入综合

这一步非常关键。因为如果没有 prompt 解释器,模型会把内部结果和外部用户消息混在一起。

第 6 步:协调者做最关键的动作,综合

这一步正是 Always synthesize 发挥作用的地方。

正确的 coordinator 不会继续写:

  • “根据你的发现修一下”

而会重写成:

  • src/auth/validate.ts:42 存在空指针
  • Session.user 在过期场景可为空
  • 需要在访问 user.id 前加 null check
  • 为空时返回 401 和明确错误文案
  • 跑相关 tests 并汇报 commit

这就是 prompt 工程对多 agent 质量的真正影响点:
研究结果经过协调者重新编码之后,才变成高质量实现 prompt。

第 7 步:实现 worker 与验证 worker 使用完全不同的人格 prompt

实现 worker 更像执行者。
验证 worker 则会套用 verificationAgent.ts 里那套明显更敌对的 prompt:

  • 不是确认能不能行,而是试图打破它
  • 不能只看代码
  • 必须给命令和输出
  • 必须做 adversarial probes

这一步说明多 Agent 系统最重要的不是数量,而是角色 prompt 之间的差异性

如果实现者和验证者用的是同一套 prompt,只是任务描述不同,那它们很可能共享同样的盲点。

第 8 步:压缩之后,系统还要保证协作态不丢

一旦会话被 compact,系统还会保留:

  • invoked_skills
  • plan_mode
  • 运行中的 async agent 信息

这意味着 prompt 工程并没有在 worker 启动那一刻结束。它还在处理一个更难的问题:

历史上下文被压缩之后,系统怎么还能记得自己正在和哪些 agent 协作、依赖哪些 skill 规则、处在什么模式里?

这就是为什么我前面说,这个仓库里的 prompt engineering 已经是 runtime 级别的。

案例二:一次“当前会话 -> session memory -> 长期记忆 -> 下轮召回”的记忆流转

再看一条记忆链路,会更能看出 prompt 在这里不是辅助,而是骨架。

第 1 步:基础记忆规则先进入上下文

在主 system prompt 装配时,loadMemoryPrompt() 会把 memdir.tsteamMemPrompts.ts 生成的记忆说明装进系统上下文。

它会告诉模型:

  • memory 在哪里
  • 目录已经存在,不要先 mkdir
  • MEMORY.md 只是索引
  • memory 和 plan/tasks 的区别
  • 什么值得记
  • 什么不该记
  • private/team scope 的边界

也就是说,在任何写记忆动作发生之前,模型已经被 prompt 训练出一套“记忆哲学”。

第 2 步:会话进行中,session memory prompt 维护“当前工作态”

当系统认为需要维护 session continuity 时,会启一个受限子任务,用 SessionMemory/prompts.ts 的 prompt 去更新 session notes。

这套 prompt 很严格:

  • instruction 不属于真实对话
  • 只能更新 notes file
  • 不许改 section headers 和斜体说明
  • 要保留模板
  • 要写得密、具体
  • Current State 必须更新

这一步写出来的不是长期记忆,而是当前会话的工作态快照。

第 3 步:长期记忆抽取器处理“哪些内容值得跨会话保存”

在另一个时机,extractMemories/prompts.ts 会驱动 memory extraction subagent。

这时 prompt 再次切换:

  • 只基于最近若干消息
  • 不要再去查代码
  • 第一轮并行读可能要改的 memory files
  • 第二轮并行写
  • 更新现有 memory,避免重复

这说明“session memory”和“long-term memory”不是一个 prompt 干两种活,而是两套职责不同的 prompt。

第 4 步:下一轮相关召回不是全文注入,而是 metadata 先筛

当后续用户问题与过去经验可能相关时,系统不会粗暴地把整个 memory 目录灌回去。
它会先扫描 frontmatter,再用 findRelevantMemories.ts 的 selector prompt 做一次“最多 5 个”的相关性选择。

这一步极其关键,因为它把“长期记忆很多”转化成了“上下文里只出现当前真正相关的少数记忆”。

第 5 步:最终通过 relevant_memories attachment 注入,而不是污染主 system prompt

被选中的记忆以 attachment 方式进入当前 query。
于是这条链路就完整了:

  1. system prompt 教会模型什么是 memory
  2. session memory prompt 维护当前工作态
  3. extract prompt 维护长期可复用知识
  4. selector prompt 负责召回筛选
  5. attachment 负责运行时注入

这就是一个非常完整的 prompt 管道。
它不是一份“记忆 prompt”完成全部,而是多份 prompt 各司其职,共同维持记忆系统。


安全、分类器与其他微型 prompt

越成熟的系统,越不会只有一个大 prompt。它会有很多小 prompt,分别承担很具体的任务。

1. 自动审批分类器:这是“二级模型治理”

src/utils/permissions/yoloClassifier.ts 很值得看。
它在做的不是主任务生成,而是:

  • 构造 classifier system prompt
  • 把用户的 CLAUDE.md 包成“这属于用户意图的一部分”
  • 用 XML <block> / <reason> / <thinking> 做结构化输出
  • 对 PowerShell 特殊危险模式做额外 deny guidance

比如它会把:

  • iex (iwr ...)
  • Remove-Item -Recurse -Force
  • 修改 $PROFILE

明确映射到既有危险类别。

这类 prompt 的好处在于:

  1. 安全判断与主代理解耦
  2. 可以针对 shell 语义单独维护规则
  3. classifier 可以更短、更明确、更可诊断

这里非常值得吸收的经验是:
不要试图让主模型同时做所有事情。把某些高风险判断下放给专门 prompt,是更稳定的。

2. execPromptHook.ts:prompt hook 是最小闭环评审器

这段代码的 system prompt 很小,只要求返回:

  • {"ok": true}
  • {"ok": false, "reason": "..."}

这就是非常典型的“微型判定 prompt”。

它的好处是:

  • 任务边界极窄
  • 输出 schema 稳定
  • 失败可以明确定位为 parse/schema 问题
  • 适合做轻量 gating

很多系统在做自动检查时喜欢让模型生成长解释,这其实不利于稳定集成。这里的做法更工程化。

3. PromptSuggestion:提示建议也被设计成一个受限 fork

src/services/PromptSuggestion/promptSuggestion.ts 里的建议 prompt 很有意思:

  • 它要预测“用户接下来会自然打什么”
  • 明确说不是“你觉得用户应该做什么”
  • 要通过那个 “Would they think ‘I was just about to type that’?” 的测试
  • 禁止 evaluative text、问题句、Claude-voice、新点子、多句输出

而实现上,它还做了几件很专业的事:

  • 用 forked agent 跑
  • canUseTool 全拒绝
  • 不覆盖会影响 cache key 的参数
  • 甚至注释里明确提到一次错误设置 effort 导致 cache hit 率大跌

这说明作者把 prompt suggestion 当成一个小型、便宜、严格受限的子任务,而不是“顺手让主模型猜一句”。

4. MagicDocs:文档更新 prompt 的哲学是“更新现状,而不是写变更史”

src/services/MagicDocs/prompts.ts 里的提示写得非常清楚:

  • 这条 instruction 不属于真实会话
  • 只在有 substantial new information 时更新
  • 文档要反映 current state
  • 不要写 changelog 风格
  • 不要文档化每个函数和参数
  • 要聚焦 WHY / HOW / WHERE TO START / patterns / gotchas

它的价值在于,它把“文档代理”从一个爱写长篇流水账的模型,收缩成一个维护项目导读的模型。

5. promptShellExecution.ts:prompt 在这个仓库里甚至可以执行 shell

这件事必须单独强调。
skill/markdown prompt 里支持:

  • 代码块形式: `! command `
  • 行内形式:!command``

然后系统会:

  • 做 permission check
  • 调用 Bash 或 PowerShell tool
  • 把输出替换回 prompt 文本

这意味着这个仓库里的 prompt 有时候不是静态文案,而是可执行 prompt 程序

这个设计非常强,但也很危险,所以它同时做了权限约束。

从经验上说,这给我们的启发是:

  • prompt 可以包含动态生成上下文
  • 但一旦 prompt 可执行,就必须把它纳入工具权限系统
  • 否则“技能 prompt”就会变成一条隐形命令执行链

6. buddy/prompt.ts:再小的产品交互,也最好用 prompt 明确角色

Companion prompt 很短,但它做了一件重要的小事:

  • 告诉主模型“旁边那个小动物不是你”
  • 用户点名 companion 时,你别抢答

这再次说明:prompt 不总是为了复杂推理,有时它只是为了消灭 UI 角色混淆。


运行时附件:这个仓库里被低估最严重的 prompt 资产

如果只看 system prompt,你会漏掉一半精华。
src/utils/attachments.ts 说明了真正的运行时 prompt 生态。

1. 附件类型本身就是语义通道

这个文件里有很多 attachment 类型:

  • nested_memory
  • relevant_memories
  • skill_listing
  • agent_listing_delta
  • mcp_instructions_delta
  • teammate_mailbox
  • team_context
  • companion_intro
  • 以及各种 task / hook / diagnostics / reminder

这说明系统没有把所有信息混成普通文字,而是做了类型化上下文注入

这对 prompt 工程的意义非常大:

  1. 模型能通过 wrapper/格式感知“这是什么”
  2. 系统能精准控制哪些附件重发、哪些去重、哪些 compact 后重建
  3. 动态内容可以离开主 system prompt,减少 cache bust

2. skill_listingagent_listing_delta 是非常漂亮的 prompt-cache 优化

相关注释写得很明白:

  • agent list 若直接写在 tool description 里,会随着 MCP/plugin/permission 变化而导致 tool schema 变化,进一步导致 prompt cache 失效
  • skill listing 也会按预算格式化,只发新 skills

这说明作者已经非常明确地把“动态清单”从静态 prompt 中抽离出来,变成 attachment delta。

这是整个仓库最值得产品化系统抄走的经验之一:

动态枚举信息,不要硬塞在稳定 prompt 里。

因为这类信息变化最频繁,又最容易让缓存崩掉。

3. relevant_memories 的 surfacing 很克制

实现上还控制了:

  • 每 turn 最多若干文件
  • 每文件字节上限
  • session 总字节上限
  • 已经 surfaced 过的不再重复
  • 已读文件不再重复

这说明 prompt 工程真正成熟时,已经不是“什么信息都想给模型”,而是知道任何额外上下文都是成本


这套仓库给 AI 多智能体系统带来的 prompt 工程经验教训

下面这一部分,是把前面源码层面的观察提炼成可迁移经验。

经验 1:不要追求一个完美大 prompt,要设计 prompt stack

单一大 prompt 的问题是:

  • 难调
  • 难缓存
  • 难定位哪条指令生效
  • 难做角色切换

这个仓库的做法是把 prompt 拆成层:

  • 稳定身份层
  • 动态环境层
  • 工具协议层
  • 角色层
  • 记忆层
  • 压缩层
  • 安全层

好处是每层都能单独进化。

经验 2:提示词要围绕失败模式写,而不是围绕理想人格写

最好的例子就是:

  • 主系统提示词里对抗过度工程、假验证、乱加抽象
  • verification prompt 里对抗 verification avoidance
  • AgentTool 里对抗 “偷看后台输出” 和 “脑补子任务结果”

写 prompt 时最有价值的问题不是“理想中的 agent 应该怎样”,而是:

  • 它最常怎么做错?
  • 哪些错一再重复出现?
  • 哪种错最昂贵?

经验 3:多 Agent 协作最先要写清楚的,不是任务内容,而是谁能看到什么

这个仓库反复强调:

  • 纯文本对队友不可见
  • worker 结果通过 <task-notification> 回来
  • 用户主要和 team lead 交互
  • 自动消息送达,不用手查 inbox

这是多智能体系统最值得优先明确的协议。

因为一旦“可见性模型”不清楚,后面所有协作都会出问题。

经验 4:协调者最重要的职责是综合,不是转发

这是 coordinatorMode.tsAgentTool/prompt.ts 共同传达的核心:

  • 不要把“根据研究结果修复”继续下发
  • 先读懂,再改写成具体 spec

一个多 Agent 系统如果没有这一层,实际上只是把模糊任务来回踢皮球。

经验 5:专用 agent prompt 要窄、要狠、要带反作弊条款

最好的例子是 verification agent。

它不是简单说“请认真验证”,而是:

  • 指出你会偷懒
  • 指出你会被前 80% 迷惑
  • 规定必须有命令和输出
  • 规定最终 verdict 格式

这类 prompt 的好处是把“专职人格”建立得足够稳定。

经验 6:工具说明是一级 prompt,不要把它当参数文档

SkillToolAgentToolSendMessageToolTeamCreateTool 都证明了这一点。

在 tool-calling 系统里,模型往往比读 system prompt 更频繁地使用工具说明来决定动作。

所以工具说明应该包含:

  • 何时调用
  • 何时不要调用
  • 调用后的可见性和结果流向
  • 常见误解
  • 产品级约束

经验 7:动态列表不要嵌在静态 prompt 里

agent_listing_deltaskill_listingmcp_instructions_delta 是很好的工程范例。

对大系统来说,提示词质量不只是语义问题,也是缓存问题。任何频繁变化的文本,只要放进主 prompt 或 tool schema,都会带来连锁成本。

经验 8:记忆系统的关键不是能写,而是知道何时写、写到哪、写什么、不写什么

这个仓库在记忆 prompt 里做得非常细:

  • 区分 private/team
  • 区分 memory/plan/tasks
  • 指定索引文件和正文文件
  • 指定语义组织而非时间组织
  • 允许 forget
  • 要求在相关时召回,但用户说忽略就必须忽略

这比“请记住用户偏好”成熟太多。

经验 9:摘要 prompt 必须为“继续工作”而设计,不是为“回顾聊天”而设计

compact/prompt.ts 的 schema 明显是面向继续开发,而不是普通聊天摘要。

如果你的 agent 要跨长上下文持续工作,那么 compact prompt 应该优先保留:

  • 当前任务
  • 用户最近意图
  • 关键文件
  • 错误与修复
  • 当前工作态
  • 下一个动作

经验 10:很多好 prompt 的本质是消灭歧义,不是增加聪明

例如:

  • “不要用普通文本和队友通信”
  • “idle 不是错误”
  • “这个 instruction 不是实际会话的一部分”
  • “只更新已有 sections,不要改模板”

这类 prompt 看起来平平无奇,但对稳定性贡献极大。

经验 11:如果 prompt 里允许执行命令,就必须把 prompt 纳入权限系统

promptShellExecution.ts 是一个非常好的提醒。

一旦 prompt 资产可以执行 shell,它就不再只是文本,而是可执行程序。
这时安全边界、审计、权限继承都必须跟上。

经验 12:小模型、分类器、hook prompt 不要追求“会说话”,要追求结构稳定

execPromptHook.ts 返回 JSON,yoloClassifier 返回 XML block/reason,都是这个思路。

这种 prompt 不是让模型发挥文采,而是让它成为一个稳定组件。

经验 13:写 prompt 时要考虑产品界面,不能只考虑模型

BriefTool 的 ack -> work -> result,就是典型例子。
好的 prompt engineering 会考虑:

  • 用户看得到什么
  • 用户会不会以为卡住
  • 队友消息是否自动送达
  • 普通文本和工具消息如何分工

这才是真正落地的 agent 产品 prompt。

经验 14:不要把所有“智能”塞进主模型,拆出专用小任务 prompt

这个仓库里至少有这些专用 prompt 任务:

  • memory selection
  • session memory update
  • long-term memory extraction
  • prompt suggestion
  • permission classification
  • compact summary
  • Magic Docs maintenance
  • hook condition evaluation

这说明一个成熟系统不是“大模型干一切”,而是“很多小 prompt 任务各司其职”。

经验 15:提示词要与运行时设计互相配合,而不是彼此独立

这是整仓库最深的一条经验。

例如:

  • prompt cache boundary 和 systemPromptSection 的缓存机制配合
  • task-notification XML 和 coordinator prompt 配合
  • mailbox / SendMessage 协议和 teammate addendum 配合
  • relevant memories attachment 和 memory selector prompt 配合
  • session memory 文件结构和 update prompt 配合
  • compact summary schema 和 query resume 路径配合

这说明真正强的 prompt engineering,从来不是单独写文案,而是和 runtime 一起设计。


一个更高层的判断:这个仓库的 prompt 哲学是什么

如果用一句更抽象的话概括,我会这样总结:

这个仓库把 prompt 从“语言技巧”提升成了“运行时治理手段”。

具体表现为:

  • 用 prompt 定义角色,不让 agent 乱位
  • 用 prompt 定义协议,不让消息语义漂移
  • 用 prompt 约束写入,不让记忆和摘要破坏结构
  • 用 prompt 规定输出 schema,方便系统消费
  • 用 prompt 对抗模型已知失败模式
  • 用 prompt 配合 cache 和 attachment,控制成本与稳定性

换句话说,这个仓库并不迷信某种“神奇写法”。
它真正擅长的是:知道什么时候该把什么规则写进哪一层 prompt。


对构建 AI 多智能体系统的直接建议

如果你要从这个仓库里提炼出一套可以直接落地的 prompt 设计清单,我建议至少照着下面做:

  1. 先画 prompt 分层图,不要一上来写大 system prompt。
  2. 把动态枚举信息从静态 prompt 中抽走,改成运行时附件。
  3. 为 coordinator、worker、verifier、planner 分别写角色 prompt,不要共用一套人格。
  4. 明确写出“谁能看到什么”,尤其是纯文本、工具消息、系统事件的可见性边界。
  5. 对多 Agent 系统,强制协调者综合研究结果,禁止“based on your findings”式懒惰转发。
  6. 给 verifier 写反自欺 prompt,要求命令、输出、verdict 都结构化。
  7. 为记忆系统写清楚类型、作用域、索引方式,以及 memory 与 plan/tasks 的边界。
  8. 为压缩和续航单独设计 prompt,不要让普通聊天摘要承担开发延续任务。
  9. 对任何小任务都优先考虑用短 prompt + 结构化输出,而不是长篇自由解释。
  10. 把 prompt 设计和缓存策略、上下文预算、附件系统、权限系统一起考虑。

附:本仓库中的主要 prompt 表面清单

下面这份清单适合以后继续扩写或做二次研究。

核心系统 prompt

  • src/constants/prompts.ts
  • src/constants/systemPromptSections.ts
  • src/utils/systemPrompt.ts
  • src/main.tsx

运行时附件与上下文注入

  • src/query.ts
  • src/utils/attachments.ts
  • src/utils/claudemd.ts

多 Agent / 协作相关 prompt

  • src/coordinator/coordinatorMode.ts
  • src/tools/AgentTool/prompt.ts
  • src/tools/SendMessageTool/prompt.ts
  • src/tools/TeamCreateTool/prompt.ts
  • src/utils/swarm/teammatePromptAddendum.ts

专用 Agent system prompt

  • src/tools/AgentTool/built-in/exploreAgent.ts
  • src/tools/AgentTool/built-in/planAgent.ts
  • src/tools/AgentTool/built-in/verificationAgent.ts
  • src/tools/AgentTool/built-in/generalPurposeAgent.ts

记忆相关 prompt

  • src/memdir/memdir.ts
  • src/memdir/teamMemPrompts.ts
  • src/memdir/findRelevantMemories.ts
  • src/services/SessionMemory/prompts.ts
  • src/services/extractMemories/prompts.ts

压缩与摘要 prompt

  • src/services/compact/prompt.ts
  • src/services/compact/sessionMemoryCompact.ts
  • src/services/awaySummary.ts

工具说明与技能 prompt

  • src/tools/SkillTool/prompt.ts
  • src/tools/BriefTool/prompt.ts
  • src/utils/claudeInChrome/prompt.ts
  • src/skills/loadSkillsDir.ts
  • src/utils/promptShellExecution.ts

安全、分类器、微任务 prompt

  • src/utils/permissions/yoloClassifier.ts
  • src/utils/hooks/execPromptHook.ts
  • src/services/MagicDocs/prompts.ts
  • src/services/PromptSuggestion/promptSuggestion.ts
  • src/buddy/prompt.ts

最后的总结

如果你要一句最凝练的判断,我会说:

这个仓库真正的 prompt engineering 能力,不在于“会写很长的指令”,而在于它理解了以下事实:

  • prompt 是分层的
  • prompt 是协议
  • prompt 必须和 runtime 一起设计
  • prompt 不只是给主模型,也给分类器、摘要器、记忆器、验证器、建议器、文档代理
  • prompt 不只是让模型变强,更重要的是让系统变稳

这也是它对 AI 多智能体系统最有价值的启发:
多智能体系统的难点,不是多开几个 agent,而是让每个 agent 在正确的 prompt 边界内工作,并且让这些边界与消息协议、记忆机制、压缩机制、权限机制严丝合缝地对齐。

It is my final heart.
最后更新于 2026-04-01