System Prompt 深度解析

很多人以为 Claude Code 有一份长长的 System Prompt,启动时一次性扔给模型。看了源码才知道,它其实是被拆成几十个独立的小碎片,运行时按需拼成最终的 system 数组。

本文先简单讲一下为啥要拆碎片,然后挑两个碎片原文做样本,让你直观感受一下工业级 Agent 的 system prompt 长啥样,有没有啥套路可以参考。

下面是从反编译源码里整理出来的 62 个 prompt 碎片,每个文件头部都有 name / description / ccVersion 注释:

后面会挑几个 Prompt 仔细讲解,这里先扫一眼有个印象就行。

为啥要拆碎片

直接给模型一坨长字符串当 system prompt 不就行了?为啥 Claude Code 要拆成几十个碎片?

主要是两个原因:

第一个是 Prompt Cache 友好。Anthropic 的 API 允许 system 字段是一个数组,数组里每段可以独立打 cache_control 标记。

把 prompt 拆成「基本不变的部分」和「每会话变的部分」,前面稳定的部分能跨会话命中 cache,后面变化的部分就算没命中也只影响后半段,token 成本可以降一大截。

实际请求长这样:

{
  "model": "claude-...",
  "system": [
    { "type": "text", "text": "You are Claude Code, ..." },
    {
      "type": "text",
      "text": "# System\n- All text you output ...\n# Doing tasks\n...",
      // 这里打缓存标记:把「第一段 + 第二段」作为一个前缀整体写入缓存
      "cache_control": { "type": "ephemeral", "ttl": "1h" }
    },
    {
      "type": "text",
      "text": "# Environment\ncwd: /Users/alice/proj\ngit: master\nCLAUDE.md: ..."
    }
  ],
  "messages": [ /* ... */ ]
}

第一段身份声明很短,不打标记。

第二段是大段行为规则,末尾打了 cache_control 缓存标记,意思是把「第一段 + 第二段」一起作为前缀写入缓存,后续请求只要这两段一字不差就能整段命中。

第三段是当前会话的环境快照(cwd、git、CLAUDE.md),每次不一样,没必要打标记。

关于 cache_control 的细节我在 给 Claude Code 发一句 Hi 会发生什么 这篇文章里有完整拆解,本文不展开。

第二个是 按需组合。Claude Code 要适配不同的操作系统(macOS / Linux / Windows)、不同的运行模式(普通模式、Plan 模式等)、不同的可选工具(比如 Chrome MCP),不同组合下需要的指令并不一样。

比如在 WSL 里运行才需要塞 WSL 相关的安全规则,启用了 Chrome MCP 才需要塞浏览器自动化的指令。如果只有一份大 prompt,要么塞很多冗余指令浪费 token,要么写一堆 if-else 拼接逻辑,很难维护。

工业级 prompt 长啥样

机制聊完了,下面是本文重点。

Claude Code 的 prompt 不是随便写写的产品文档,是按工业级标准打磨过的。我挑两个对比鲜明的碎片,把原文完整贴出来再解读。

我们理解的 Prompt 可能只是几句话,但实际 Claude Code 的 System Prompt 有上万字,详细规定 Agent 在某一方面的行为。看看他们编写 system prompt 的套路,有助于我们之后自己开发 Agent,也有助于我们写出更高质量的 prompt

案例一:Executing actions with care

第一个碎片有点长,是规范模型「执行高风险操作时该怎么办」的。原文如下(代码块里的 ... 是为了简洁省略的部分):

# Executing actions with care

Carefully consider the reversibility and blast radius of actions.
Generally you can freely take local, reversible actions like editing
files or running tests. But for actions that are hard to reverse,
affect shared systems beyond your local environment, or could
otherwise be risky or destructive, check with the user before
proceeding. The cost of pausing to confirm is low, while the cost of
an unwanted action (lost work, unintended messages sent, deleted
branches) can be very high. ...

Examples of the kind of risky actions that warrant user confirmation:
- Destructive operations: deleting files/branches, dropping database
  tables, killing processes, rm -rf, overwriting uncommitted changes
- Hard-to-reverse operations: force-pushing (can also overwrite
  upstream), git reset --hard, amending published commits, removing
  or downgrading packages/dependencies, modifying CI/CD pipelines
- Actions visible to others or that affect shared state: pushing
  code, creating/closing/commenting on PRs or issues, sending
  messages (Slack, email, GitHub), posting to external services,
  modifying shared infrastructure or permissions
- Uploading content to third-party web tools (diagram renderers,
  pastebins, gists) publishes it - consider whether it could be
  sensitive before sending, since it may be cached or indexed even
  if later deleted.

When you encounter an obstacle, do not use destructive actions as a
shortcut to simply make it go away. For instance, try to identify
root causes and fix underlying issues rather than bypassing safety
checks (e.g. --no-verify). If you discover unexpected state like
unfamiliar files, branches, or configuration, investigate before
deleting or overwriting, as it may represent the user's in-progress
work. ... Follow both the spirit and letter of these instructions -
measure twice, cut once.

这个 System Prompt 有几层:

第一层是给抽象的判断标准。开头不是直接列规则,而是先给两个抽象概念,reversibility(可逆性)和 blast radius(影响范围)。

让模型先有一把通用尺子来判断什么操作算高风险,而不是死记规则。这样遇到 prompt 里没列出来的情况,模型也能用这把尺子推断。

第二层是举例说明。光给抽象概念不够,模型的尺度可能把握不准,所以后面跟了 4 类具体例子:破坏性操作、难逆转操作、影响他人或共享状态的操作、上传内容到第三方。

每类都给了实际命令(rm -rfgit reset --hardforce-push 等),把抽象的概念落到具体的行为上。

第三层是反模式提醒。这一层最容易被忽略,但工业级 prompt 里几乎必有。原文说「遇到障碍时,不要拿破坏性动作当捷径绕过」,并且专门点名了 --no-verify 这个常见的偷懒做法()。

为啥要专门写?这估计是 Anthropic 工程师在长期使用中观察到的模型行为习惯。比如 git commit 被 pre-commit hook 拦下来,模型倾向于直接 git commit --no-verify 绕过校验,而不是去排查 hook 为啥失败。

文件冲突时直接覆盖、lock 文件占用时直接删除,也都是类似的偷懒倾向。这些都必须在 prompt 里明确告知不要这么做。

这一层结尾还顺手丢了一句英文谚语 measure twice, cut once(可以翻译为三思而后行),相当于给整段规则贴了个简短好记的标签,方便模型在相关场景下联想到前面那一大段判断。

案例二:Writing subagent prompts

第二个碎片是教模型「自己怎么给 subagent 写 prompt」。

Claude Code 经常会创建 subagent 去做调研或独立审校。subagent 启动时上下文是空的,主模型怎么把任务交代清楚直接决定 subagent 的干活质量。

如下:

## Writing the prompt

Brief the agent like a smart colleague who just walked into the
room — it hasn't seen this conversation, doesn't know what you've
tried, doesn't understand why this task matters.
- Explain what you're trying to accomplish and why.
- Describe what you've already learned or ruled out.
- Give enough context about the surrounding problem that the agent
  can make judgment calls rather than just following a narrow
  instruction.
- If you need a short response, say so ("report in under 200
  words").
- Lookups: hand over the exact command. Investigations: hand over
  the question — prescribed steps become dead weight when the
  premise is wrong.

Terse command-style prompts produce shallow, generic work.

**Never delegate understanding.** Don't write "based on your
findings, fix the bug" or "based on the research, implement it."
Those phrases push synthesis onto the agent instead of doing it
yourself. Write prompts that prove you understood: include file
paths, line numbers, what specifically to change.

这段 Prompt 也遵循固定的套路:

第一层是抽象比喻。开头不堆术语,直接给一个具体的画面:像给一个刚走进房间的聪明同事交代任务一样。

这一句话把后面所有清单的判断标准都立住了,模型只要问自己「我这样写够不够让一个新同事理解」。比喻在 prompt 里比定义更强,因为它能在模型内部激活一整套关联场景。

第二层是举例说明。光给比喻不够,模型未必能自己想清楚要塞哪些东西,所以下面紧跟了 4 条具体要交代的内容:

  • 目标和动机:想让 subagent 做成什么、为啥要做这件事
  • 已有进展:哪些方向你已经搞清楚或者排除掉了,免得 subagent 重复踩坑
  • 周边背景:任务的上下文要交代到位,subagent 才能自己做判断,而不是机械照着指令做
  • 长度要求:限制简短的输出,比如「200 字以内」,不然容易拿到一大坨没法直接用的产出

每条单独看都很短,但合起来已经覆盖了「交代一个任务」需要操心的所有维度。

Prompt 中把 subagent 拿到的任务分为两类:Lookups(明确查询)和 Investigations(开放调研)。因为这两类任务的处理方式不同。

针对 Lookups 类任务,需要指定目标,把明确的目标交给 subagent 执行。

针对 Investigations,要把问题原样交出去、不预设任何步骤,让 subagent 自己开放性思考和执行。

第四层是反模式Never delegate understanding(不要把理解这一步推给 subagent)后面跟了 based on your findings, fix the bugbased on the research, implement it

为啥要专门写?因为这正是 LLM 在创建 subagent 时的偷懒倾向,它可能会把理解分析问题这步推给 subagent,自己只当个调度员。

这种模式在 agent 协作里特别麻烦:主模型没真正搞懂问题,subagent 拿着模糊的目标做出来一份模糊的结果,主模型再拿去用,错误会逐级放大。所以 prompt 里必须明确禁止这种行为。

我的实践经验

这里可以看到,System Prompt 明确要求主 Agent 要基于自己的理解给 subagent 部署任务,这在大部分情况下是没问题的,但是在有些情况下,可能不符合我们的需求。

举个具体的例子,比如你有 100 个文档,都要按照 A -> B -> C 的固定流程来处理,你应该如何布置任务,让 Agent 给你处理完这 100 个文档?

比较好的思路是,主 Agent 来调度,具体的 A、B、C 工作交给 subagent 来做。

你可以把 A、B、C 每个步骤的具体工作总结成 Skill,然后这样给主 Agent 下达指令:

当前目录有 100 个文件,我需要按照 A -> B -> C 的串行流程处理每个文件。

对于每个文件,你不要自己处理,可以使用 subagent 来处完成 A、B、C 的工作,具体的工作流程参见 /skillA /skillB /skillC。

注意:不要基于你的理解来复述 /skillA /skillB /skillC 的内容,而应该在 Prompt 中明确要求 subagent 读取对应 skill 来完成工作。

此时,我们不希望主 Agent 基于自己的理解来复述需求,而希望 subagent 直接读取原始文档,减少信息损耗。

这种情况下可以在 Prompt 中明确要求,主 Agent 就会按照我们的要求来创建 subagent,让工作流更可控。

最后,Prompt 其实也是需要不断调整的,而且针对不同场景,可能要设计不同的 Prompt,并反复测试 Agent 的执行效果。

接下来我们会分析几个典型的 Agent 工作场景,到时候你可以留意它们的 Prompt 是怎么写的,以及如何配合其他工程手段让 Agent 的行为更可靠。