# System Prompt 工程：800 行提示词背后的设计决策


<!-- more -->


如果你以为 System Prompt 就是"给 AI 设定一个人设"，那 Claude Code 的 system prompt 会颠覆你的认知。

它不是几句话。它是一个**上千行的模板引擎**，由多个层次组成，每一层负责不同的事：身份定义、行为约束、工具说明、项目上下文、记忆注入、技能加载。有些层是静态的（每次一样），有些层是动态的（根据项目、会话、用户偏好实时注入）。

ChatGPT 里你写的 "You are a helpful assistant" 是 System Prompt 的 Hello World。Claude Code 里的 System Prompt 是生产级应用。

## 为什么 System Prompt 这么长？

先回答一个直觉性问题：提示词不是越短越好吗？少占上下文，少花钱，少干扰。

对普通对话而言，是的。但 Claude Code 不是普通对话——它是一个**在陌生环境里执行危险操作的自主代理**。System Prompt 需要回答的问题远比"你是谁"复杂：

- 你的首要目标是什么？（帮用户写代码，不是聊天）
- 你可以用什么工具？（工具定义列表）
- 哪些操作需要确认？（权限边界）
- 这个项目的约定是什么？（CLAUDE.md 注入）
- 用户之前告诉过你什么？（记忆注入）
- 你有什么专业技能？（Skill 注入）
- 出了错怎么办？（错误处理策略）
- 不确定时怎么办？（提问优先于猜测）

每个问题都需要一段明确的指令。这些指令叠加起来，就是一个长 prompt。

关键不是"长"，而是**结构**。800 行无结构的文本是噪声，800 行有结构的指令是操作系统内核。

## 分层架构：从静态到动态

Claude Code 的 system prompt 可以拆成四层，从最内层（最稳定）到最外层（最易变）：

**第一层：核心身份（Core Identity）**

这是最内层，几乎从不变化。它定义了 Agent 的基本身份和行为准则："你是一个 AI 编程助手，名叫 Claude。你的目标是帮助用户完成编程任务。"

这一层还包含通用行为约束：
- 用用户的语言回复
- 不确定时提问，不要猜测
- 解释你的推理过程
- 优先理解意图，再动手

**第二层：工具定义（Tool Definitions）**

上一篇文章我们聊过工具系统。工具的定义（名字、描述、参数 schema）就是注入到这一层的。这是 system prompt 里占比最大的部分之一——30 多个工具，每个都有结构化描述。

这一层是半动态的：核心工具始终存在，延迟工具按需加载。

**第三层：项目上下文（Project Context）**

这一层是 Claude Code 最有特色的设计之一。它会扫描项目目录，加载 `.claude/CLAUDE.md` 文件（类似 .editorconfig 的层级继承机制），把项目的特定约定注入到 prompt 里。

CLAUDE.md 可能包含：
- 项目的技术栈和架构说明
- 代码风格约定
- 测试策略
- 部署流程
- 已知问题和注意事项

这意味着同一个 Claude Code，在不同的项目里"表现不同"——不是模型变了，是注入的上下文变了。它在一个 React 项目里知道用 JSX，在一个 Rust 项目里知道用 Cargo。

**第四层：会话状态（Session State）**

最外层，最易变。包含当前会话的动态信息：
- 之前加载的记忆（Memory）
- 已激活的 Skill
- 当前权限模式
- 活跃的子代理

这一层在每次循环迭代时都可能变化。

四层叠加，就像一个洋葱。核心身份是内核，越外层越贴近当前任务的具体情境。

## 占位符与模板引擎

如果你看过 Claude Code 的 prompt.ts 源码，你会发现 system prompt 不是一个字符串，而是一个**模板**。它包含大量占位符，在运行时被替换为实际内容：

```
{git_context}
{claude_md_content}
{memory_content}
{skill_content}
{tool_definitions}
{subagent_definitions}
{deferred_tool_definitions}
```

每个占位符对应一个数据源。运行时，prompt 构建器会遍历这些占位符，从对应的模块获取内容，替换进去。如果某个数据源为空（比如项目没有 CLAUDE.md），占位符被静默移除，不会在 prompt 里留下空壳。

这种模板化设计带来了两个好处：

**可测试性。** 每一层都可以独立测试——你可以验证"CLAUDE.md 加载是否正确"而不需要跑完整的 Agent 循环。

**可组合性。** 不同的项目、不同的用户偏好、不同的权限模式，组合出不同的 prompt。同一个代码库，适配无穷多的使用场景。

## CLAUDE.md：项目宪法

CLAUDE.md 是 Claude Code 生态里最被低估的设计之一。

它的理念很简单：**每个项目都应该有一份"宪法"，告诉进入这个项目的 AI 代理该怎么干活。**

这听起来像 README，但本质不同。README 是写给人看的——它介绍项目是什么、怎么安装、怎么用。CLAUDE.md 是写给 AI 看的——它告诉 AI 这个项目的约定、约束、偏好。

一个典型的 CLAUDE.md 可能长这样：

```markdown
# 项目约定

- 使用 TypeScript，不写 JavaScript
- 组件用函数式 + Hooks，不用 Class
- 测试用 Vitest，每个新文件必须有对应测试
- 错误处理统一用 Result 模式，不用 try-catch
- Git 提交信息用 Conventional Commits 格式
```

这段内容对人类开发者来说是"提醒"，对 AI Agent 来说是**硬约束**。它被注入 system prompt，等同于模型的行为准则。

CLAUDE.md 还有层级继承机制——根目录的 CLAUDE.md 对所有子目录生效，子目录可以覆盖或追加。这和 .gitignore、.editorconfig 的逻辑一样，但对 AI Agent 的意义更大：它是**项目知识的形式化载体**。

这个设计的深层含义是：**项目的规范不应该只存在于人的脑子里，它应该被写成机器可读的格式。** 这不是新概念（配置文件早就存在了），但 CLAUDE.md 是第一个专门为 AI Agent 设计的"项目宪法"。

## 行为约束：怎么让 AI 不瞎搞

System prompt 里有一大块内容专门用来约束模型行为。这不是"建议"，是"规则"。包括但不限于：

- **先理解再行动。** 不要接到需求就立刻改代码。先读相关文件，理解现有架构，再动手。
- **最小变更原则。** 只改需要改的部分，不要重构不相关的代码。
- **解释你的决定。** 每次修改都要说明为什么这么改。
- **失败时回退。** 如果操作导致错误，尝试恢复到操作前的状态。
- **不要编造文件内容。** 如果没读过某个文件，不要假装知道它的内容。

这些约束的存在，说明了一个事实：**模型默认行为不理想。** 不加约束的模型倾向于"过度热情"——它太想帮忙了，以至于没搞清楚状况就动手，改了一堆不该改的东西。

System prompt 里的行为约束就像给一辆跑车装上 ABS 和 ESP。引擎很强，但如果没有刹车和稳定控制系统，开得越快死得越快。

## 延迟注入：不是所有信息都需要一开始就有

和工具系统的渐进式披露类似，system prompt 也采用了**延迟注入**策略。不是所有信息都在第一次调用时就塞进去，而是根据对话进展逐步注入。

比如 Skill 内容——用户有 10 个 Skill（写作规范、架构设计方法、测试策略……），但当前任务只需要"写作规范"。那其他 9 个 Skill 的内容就不注入，等模型明确需要时再加载。

再比如记忆——用户的历史记忆可能有几十条，但当前任务只相关其中 3 条。system prompt 只注入这 3 条，而不是全量注入。

延迟注入的核心原则是：**信息越相关，越早注入；信息越不相关，越晚注入（或永不注入）。** 这本质上是一个信息检索问题——在海量可用信息中找到最相关的子集。

## System Prompt 的本质

写到这里，我们可以回答一个根本问题：System Prompt 到底是什么？

它不是"人设"。它不是"提示"。它是一个**运行时配置**——和程序的配置文件一样，它定义了 Agent 在当前环境下的行为边界、可用资源、操作规则。

只不过这个配置文件的格式是自然语言，解析器是大语言模型。

这也是为什么 System Prompt Engineering 越来越像软件工程——你需要模块化、模板化、可测试、可维护。你不只是"写一段话"，你是在"构建一个框架"。

## 延伸阅读：BYF 的 PromptPlan 与 Ephemeral Injection

[BYF](https://github.com/ByronFinn/byf) 在 system prompt 工程上做了比 Claude Code 更极致的结构化处理。它有一个专门的 `PromptPlan` 模块，将 system prompt 拆分为**有序的具名块**（`PromptBlock[]`），每块附带 `CacheScope` 标注——`global`（跨会话稳定）、`project`（项目内稳定）、`session`（每会话变化）、`none`（不缓存）。这四块架构确保了：

1. **全局块（Block 0）**只包含纯 Agent 规则——身份、原则、安全——不包含任何 per-session 变量，让 OpenAI 的 `prompt_cache_key` 真正跨会话稳定。
2. **环境块单独成块**，`BYF_OS`、`BYF_SHELL`、`BYF_WORK_DIR` 等变量不污染全局缓存。
3. **工具按稳定性排序**，内置工具在前、MCP 工具在后，确保缓存边界不会随外部服务连接状态而坍缩。

BYF 还引入了**临时注入（Ephemeral Injection）**——一种更精细的上下文管理策略。时间戳和权限模式状态等每步变化的动态内容，不再持久写入对话历史（`_history`），而是通过 `getEphemeral()` 在每轮请求时重新渲染，追加在历史末尾的 `before_user` 位置。这样做的效果是：**动态内容对缓存前缀零影响**——整个 system prompt + 对话历史可以被缓存，只有末尾几十 token 的"新鲜"内容逐轮变化。

这个设计和 Claude Code 的分层注入思路一致，但 BYF 的"四块架构 + ephemeral 注入"在缓存优化上走得更远，是从"让 prompt 更长但有结构"到"让 prompt 的哪些部分能被缓存"的精细化管理。

## 下一篇

System prompt 定义了 Agent 的行为规则，但规则需要执行机制。上一篇文章聊了工具系统，这篇文章聊了行为约束，下一篇文章我们要看这两者的交汇点：**权限与安全系统**——Agent 什么时候可以直接行动、什么时候必须问用户、什么时候应该被禁止。5 级权限模型背后的设计逻辑，本质上是一个信任校准问题。

---

> 本系列基于 [Claude Code 官方源码](https://github.com/anthropics/claude-code)项目进行架构分析，聚焦设计思想而非代码实现。

