Claude Code源码分析之提示词工程
在 LLM 应用开发中,系统提示词(System Prompt)的管理往往是一个容易被忽视的工程痛点。随着应用复杂度的提升,传统的单体字符串拼接方式会导致代码难以维护,并且在面对长上下文时,容易产生不必要的高额 Token 消耗和延迟。
本文基于 Claude Code 泄露出来的源代码(主要覆盖 src/QueryEngine.ts 及 src/utils/ 等核心模块),从工程视角拆解其提示词管理的系统架构。其核心设计理念包括:极致的模块化解耦、对 Prompt Caching(提示词缓存)的深度优化、多级路由调度机制以及并发的上下文组装。
1. 提示词的模块化注册机制
对于复杂应用,单体字符串形式的 Prompt 极易引发代码冲突与逻辑混乱。Claude Code 在这方面采用了类似中间件的模块化注册机制。这种设计使得新增能力(如引入特定的 MCP Server 指令)时,只需在组装层增加相应的 Section,无需在数千行的主字符串中修改,符合软件设计的开闭原则(OCP)。
在 src/constants/systemPromptSections.ts 中,各类规则和指令被抽象为独立的片段,通过工厂函数 systemPromptSection(name, compute) 进行集中注册与管理。以下是几个关键模块的核心提示词节选:
getSimpleIntroSection(AI 的基础身份与边界)1
2
3You are an interactive agent that helps users with software engineering tasks...
IMPORTANT: Assist with authorized security testing... Refuse requests for destructive techniques...
IMPORTANT: You must NEVER generate or guess URLs for the user unless you are confident...解析:开篇明义并注入强烈的安全约束,防止越权或恶意利用。
getSimpleDoingTasksSection(任务分解与执行原则)1
2
3
4
5# Doing tasks
- In general, do not propose changes to code you haven't read. If a user asks about or wants you to modify a file, read it first.
- Do not create files unless they're absolutely necessary... prefer editing an existing file...
- If an approach fails, diagnose why before switching tactics... Escalate to the user with AskUserQuestionTool only when you're genuinely stuck...
- The right amount of complexity is what the task actually requires—no speculative abstractions... Three similar lines of code is better than a premature abstraction.解析:这部分是工程师思维的直接体现:强调要“先读代码再修改”、抵制“过度设计/过早抽象”、失败时要“先诊断再尝试”而不是瞎改。
getActionsSection(风险行为的拦截与确认规范)1
2
3
4# 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... check with the user before proceeding.
Examples... Destructive operations: deleting files/branches, dropping database tables...
When you encounter an obstacle, do not use destructive actions as a shortcut... only take risky actions carefully, and when in doubt, ask before acting.解析:引入了“爆炸半径(blast radius)”和“可逆性(reversibility)”的概念。指导 Agent 什么是安全的自由操作,什么是必须拦截并申请权限的危险操作。
getOutputEfficiencySection(输出精简控制)1
2
3
4# Output efficiency
IMPORTANT: Go straight to the point. Try the simplest approach first... Be extra concise.
Keep your text output brief and direct. Lead with the answer or action, not the reasoning...
If you can say it in one sentence, don't use three...解析:直接终结了大模型的“废话文学”,要求直接输出动作/结果,而不是长篇大论的思考过程。
graph TD
A[build System Prompt] --> B(Module: Intro)
A --> C(Module: Task Constraints)
A --> D(Module: Action Guidelines)
A --> E(Module: Tool Usage)
A --> F(Module: MCP Instructions)
B --> G[System Prompt Payload]
C --> G
D --> G
E --> G
F --> G2. 基于 Prompt Caching 的边界隔离设计
Anthropic API 的 Prompt Caching 机制要求前置提示词必须足够稳定,才能有效命中缓存。为了最大化缓存命中率,降低 API 开销(可达 90% 的成本优化和极低的 TTFT 首字延迟),Claude Code 在架构上进行了强隔离设计。
在 src/constants/prompts.ts 中,系统专门引入了一个常量 SYSTEM_PROMPT_DYNAMIC_BOUNDARY(动态边界标记)来划分和组装提示词:
- 静态缓存区(Static/Global Scope):此区域放置稳定不变或极少变动的规约,如基础的人设、代码编写规范、全局通用安全指令等。这部分在多轮对话以及多会话中可稳定命中缓存。
- 动态易变区(Dynamic/Volatile Scope):置于边界标记之后。这里包含了随时间或操作高频变动的环境状态,如当前的工作区 Git 状态、动态 Scratchpad 内的思考缓冲、以及高频更新的工具描述信息。
- 缓存防护策略:针对某些极度易变的状态变量,系统提供了显式的绕过接口(如
DANGEROUS_uncachedSystemPromptSection)。该机制确保开发者在临时注入高频变动变量时,不会破坏上层庞大的静态缓存使其失效。
sequenceDiagram
participant Engine as QueryEngine
participant Cache API as Anthropic API
Engine->>Engine: 构造静态规约模块 (Static Sections)
Note over Engine: 插入 SYSTEM_PROMPT_DYNAMIC_BOUNDARY
Engine->>Engine: 追加动态上下文 (Time, Git Scope)
Engine->>Cache API: 发送整段请求负载
Note right of Cache API: 命中静态缓存区块<br/>仅计算尾部动态部分的 Token
Cache API-->>Engine: 返回流式响应3. 提示词也有优先级
Claude Code 支持标准终端模式、受限 REPL 模式、系统编排模式(Coordinator)以及各类专门的子智能体(Sub-agent,如 ExploreAgent)。应对这些重叠度很大的场景,它提供了一套五个级别的优先级机制(位于 src/utils/systemPrompt.ts 的 buildEffectiveSystemPrompt() 方法中):
提示词优先级从高至低逻辑如下:
- **Priority 0 (Override / 绝对覆盖)**:最高权限。用于非常特殊的对话环节(如强行要求 AI 执行拦截确认、或只允许输出固定格式的受限场景)。一旦命中此级,之前所有的基础人设和工具规矩都会被直接丢弃,模型被迫只能严格执行这个“绝对指令”。
- **Priority 1 (Coordinator Mode / 编排者模式)**:这是一种“幕后大脑”模式。当系统面对一个庞大、复杂的任务时,不会让一个 AI 蛮干,而是启动一个后台的“管家”(Coordinator)来把任务拆分给多个不同子 Agent。此时,大模型会被注入一套专门的“调度员”提示词,让它不要去死磕代码,而是专心做进度统筹和任务派发。
- **Priority 2 (Agent Profiles / 子智能体专属指令)**:专门为执行单项技能的“打工人”准备。比如当系统唤起“代码探索者(ExploreAgent)”时,会自动把“只读不写”、“忽略外部依赖”等专用的约束条件,覆盖或者追加到它脑海里,让它更聚焦。
- **Priority 3 (CLI Injection / 用户命令行干预)**:留给玩家的自定义后门。用户在终端输入命令启动时,夹带的
--system-prompt "全程用中文回答"这种指令会被插在这里。由于优先级高于兜底默认值,大模型会乖乖听话。 - **Priority 4 (Fallback Base / 默认兜底底座)**:最常态的情况。如果没发生上述 0-3 级的事情,系统就会老老实实地调用前面的各种静态缓存模块和动态状态模块,拼成一套标准的“打底”提示词去干活。
系统还保留了 appendSystemPrompt 变量通道,主要用于强制性的安全策略指令下发补充。
4. 上下文收集机制
当所有的指令、规矩都准备就绪、真正向大模型发起网络请求之前,流程会汇聚到核心引擎 QueryEngine(位于 src/QueryEngine.ts)。为了让大模型在接手任务时不至于像个“瞎子”,代码(定义于 src/context.ts 中)会启动两个并行的收集任务,我们称之为“双轨情报收集”:
- 第一条轨道:探测物理环境(System Context)。这部分旨在给大模型投递当前代码仓库的“实时快照”。系统会静默执行一系列操作,提取出当前的 Git 分支名、最新的 Commit Hash,以及你目前工作区还没提交的文件差异(Diff)摘要。这也是为什么你在用 Claude Code 时,它总能神奇地知道你“刚才敲了哪些代码还没提交”的秘密。
- 第二条轨道:注入业务与时间认知(User Context)。大模型本身是不知道今天星期几的,所以系统会在这里把高精度的当前时间戳硬塞进 Prompt 里。更关键的是,它会自动扫描你项目根目录下是否存在一个名为
CLAUDE.md的文件。这个文件就像是“新员工入职手册”,你提前写在里面的代码架构规范、命名约定等,都会在这个阶段作为“角色背景”喂给模型。
底层代码通过 fetchSystemPromptParts() 函数并发拉取完这“两条轨道”的数据后,会将这些“现场情报”与前面所说的那套多个优先级处理后的提示词进行最终的拼接,最后才封装成 HTTP 请求发往LLM API。
总结
Claude Code 的源码表明,现代工程化的 Prompt 不再是一个巨大且脆弱的字符串工厂,而应该是一个多优先级分发处理的模块。一方面对职责模块进行了细粒度拆分,满足了多样化的智能体运行模式需求;另一方面又针对模型基础设施(如 Prompt Cache)进行了静态提示词与动态提示词的隔离。这种在架构和底层交互层面上兼顾扩展性与性能的最佳实践,对于企业级 AI 工具架构的设计极具参考价值。





