第 13 章
上下文与技能:system prompt、CLAUDE.md、skills
你写了 CLAUDE.md,它为什么没读?
先说一个我排查了半天的事故。
同一份代码,在我笔记本上跑得好好的,部署到服务器就变了:Agent 开始猜测试命令,还把一个内部约定忘了。
我以为是模型差异,查了版本、温度、提示词,最后发现是 setting_sources 没写。
它的默认值是 None,意思是“全部加载”:用户目录、项目、本地设置都读。我笔记本的用户目录里有一份 CLAUDE.md,服务器上没有。Agent 在本地“知道”的规则,服务器上根本不存在。
同一份代码,两个行为。不是模型的问题,是上下文来源的问题。
这一章讲三件事:system_prompt 到底要不要写、CLAUDE.md 从哪来、Skill 什么时候该用。
13.1 system_prompt 默认是空的
很多人以为 SDK 会自带 Claude Code 的系统提示。不会。
system_prompt 不设,就是空。你拿到的是一个“没有角色、没有工作流、没有完成标准”的 Agent;它能不能干活,全看你给了什么。
三种写法:
# 1. 从零写
system_prompt="You are a coding agent. Read before editing; run tests before done."
# 2. 用 Claude Code 的预设,再追加
system_prompt={
"type": "preset",
"preset": "claude_code",
"append": "This repo uses unittest, not pytest.",
}
# 3. 不写(空)
# system_prompt 不传怎么选?
- 从零写:你有明确的工作流、输出格式、领域规则,不想继承 Claude Code 的行为。
- preset + append:你想要 Claude Code 那套“先读、后改、跑测试”的默认行为,只补项目约定。
- 不写:只做很短的任务,或者你完全靠
CLAUDE.md/Skill 提供上下文。
但不管选哪种,CLAUDE.md 都和 system_prompt 无关。你写不写系统提示,都不影响 CLAUDE.md 加不加载;决定它的是 setting_sources。
13.2 CLAUDE.md 不在系统提示里
这是第二个容易误解的点。
CLAUDE.md 不是拼在 system prompt 里的。它由 CLI 加载,放在第一条用户消息里,每次请求都带全文。
所以:
- 它每一轮都付钱,和系统提示一样;
- 它能被缓存,只要前缀稳定;
- 它不是“项目文档仓库”,几百字够了,长文档按需读。
taskflow 的 CLAUDE.md 只有三行:
# taskflow
- Fix `taskflow/`, not `tests/`.
- Run `python3 -m unittest discover -s tests -v` before claiming done.
- Keep the behavior covered by `tests/test_core.py`.这三行是“每轮都要”的规则。项目结构、命令大全、历史决策,不该塞这里;它们应该放在 README 或 Skill 里,按需读。
13.3 setting_sources 的默认值是个陷阱
setting_sources 控制加载哪些设置来源:
| 值 | 加载什么 |
|---|---|
None(默认) | 全部:user + project + local |
[] | 不加载任何设置 |
["project"] | 项目级:CLAUDE.md、.claude/settings.json、.claude/skills/ |
["user"] | 用户目录的设置 |
["local"] | 本地私有设置 |
None 的“全部加载”是 CLI 的默认行为,不是 SDK 的“安全默认”。
在开发者笔记本上,这通常没问题;部署到服务器、CI、多租户环境,就会变成“行为随机器变”。你本地用户目录里的 CLAUDE.md、权限规则、技能,服务器上没有;反过来,服务器上的配置也可能污染你的 Agent。
所以生产环境要显式写:
options = ClaudeAgentOptions(
cwd=str(ROOT),
setting_sources=["project"], # 只要项目级
)setting_sources=[] 不是“关掉一切”,它是“不加载设置来源”。工具、模型、你显式传的 system_prompt 都还在;只是 CLAUDE.md、项目权限、Skill 不会被发现。
这也是第 12 章那个坑的另一面:你把 setting_sources=[] 当隔离,结果 Skill 和 CLAUDE.md 也一起没了。
13.4 get_context_usage:先看钱花在哪
SDK 提供了一个 get_context_usage(),能看上下文各部分的占用:
async def show_usage(client):
usage = await client.get_context_usage()
print(usage["totalTokens"], usage["categories"])它不会告诉你“哪句话最有用”,但能告诉你“系统提示、工具、消息、记忆各占多少”。
第 6 章我们是自己数字符;SDK 里用这个接口更准。先看总量和分类,再决定是删工具、缩 CLAUDE.md,还是压缩历史。
13.5 Skill:目录常驻、正文按需
Skill 的本质是“一本手册”:
- 目录常驻:每个 Skill 的
name+description在上下文里,模型知道“有这么一本”; - 正文按需:模型调用
Skill工具,才把SKILL.md的正文加载进来。
目录大概是这样:
taskflow/
CLAUDE.md
.claude/
skills/
fix-failing-test/
SKILL.md
add-feature/
SKILL.mdfix-failing-test/SKILL.md:
---
name: fix-failing-test
description: 修复 tests/ 下失败的测试。用户说“修好这个失败测试”“让测试变绿”时使用。不要改测试,改实现。
---
# 修复失败测试
1. 先读 README.md 和失败的测试,确认期望行为。
2. 复现失败,读完整报错。
3. 定位实现,做最小修改;不要改测试。
4. 跑全量测试;失败就回到第 2 步。
5. 总结:改了什么、为什么、还有没有没覆盖的边界。name 要唯一,description 要写“什么时候用”。模型就是靠这两行决定“要不要翻这本手册”。
挂到 SDK 上:
options = ClaudeAgentOptions(
cwd=str(ROOT),
setting_sources=["project"], # 让 CLI 发现 .claude/skills/
skills=["fix-failing-test", "add-feature"],
system_prompt={
"type": "preset",
"preset": "claude_code",
"append": "Read CLAUDE.md and the relevant SKILL.md before acting.",
},
tools=["Read", "Glob", "Grep", "Edit", "Bash", "Skill"],
allowed_tools=["Read", "Glob", "Grep", "Skill"],
permission_mode="acceptEdits",
)skills 可以是 None(由 setting_sources 决定发现哪些)、"all"、或者名单。名单是上下文过滤,不是沙箱:没列出的 Skill 模型看不见,但这不构成安全边界。
13.6 什么时候用 Skill,什么时候别用
这是这一章最重要的一张表:
| 需求 | 用什么 | 为什么 |
|---|---|---|
| 步骤固定、结果确定 | 工具 / 代码 | 确定性最高,不靠模型记步骤 |
| 步骤多、需要判断、不必每次全讲 | Skill | 目录常驻、正文按需,省上下文 |
| 一次性的隔离子任务 | 子 Agent | 独立历史,只回结论 |
| 必须拦截/改写/强制 | Hook | 在 Harness 层执行,模型绕不过 |
| 每轮都要的短规则 | CLAUDE.md / system prompt | 常驻,必须每次看到 |
| 长文档、项目百科 | README,按需读 | 常驻太贵,模型也不一定看 |
几个判断例子:
- “跑测试” → 工具/命令,不是 Skill。步骤固定,代码执行更可靠。
- “修失败测试的流程” → Skill。它需要判断:读哪个测试、改哪行、什么时候停。
- “调研一个库” → 子 Agent。过程长、结论短。
- “没跑测试不许结束” → Stop Hook。不能靠模型自觉。
- “不要改 tests/” →
CLAUDE.md+ 权限。前者提醒,后者兜底。
Skill 不是“提示词模板”。它和工具、子 Agent、Hook 是四种不同的机制;选错了,不是“效果差一点”,是“根本管不住”。
13.7 插件:一箱 Skill 的分发方式
当你有跨项目复用的 Skill,或者要分发给团队,才需要插件。
一个仓库里两个 Skill,不需要插件;.claude/skills/ 就够了。插件解决的是“打包、版本、分发”,不是“让 Skill 生效”。
先别急着做插件。等你有第三个项目也要用同一套 Skill 时,再考虑。
13.8 Skill 的坑
第一,description 每轮都付钱。 正文按需,目录常驻。十个 Skill、每个描述 100 字,就是每轮 1,000 字。Skill 不是越多越好。
第二,正文可能被跳过。 模型看到目录,不一定会调用 Skill。你可以在系统提示里写“先读相关 Skill”,或者把 description 写得足够具体。别假设它一定会翻手册。
第三,白名单不是沙箱。 skills=["..."] 只是让模型看不见其他 Skill;它不阻止模型用 Read 去读 .claude/skills/ 下的文件。要真隔离,用权限和路径。
第四,setting_sources=[] 会把 Skill 一起关掉。 这是最常见的“我配了 Skill 但没生效”。
第五,别把 init 帧的某个字段当“模型可见”。 这次实测里 commands 是 None,但 init 的 data 有 skills、slash_commands、tools;CLI 的斜杠命令列表和模型可见的 Skill 目录不是一回事。要验证,得看真实调用,不是看一个字段。
13.9 跑一遍
完整代码在 code/sdkagent/ch13_skills.py。
python3 -m pip install "claude-agent-sdk==0.2.168"
export ANTHROPIC_API_KEY="..."
cd code
python3 sdkagent/ch13_skills.py2026-10-21 的最终成功运行(SDK 0.2.168 + CLI 2.1.296 + deepseek-flash):
[init] keys: ['agents', 'analytics_disabled', ..., 'skills', 'slash_commands', 'tools', ...]
[tool] Skill {'skill': 'fix-failing-test'}
[tool] Bash {'command': 'ls -la .../taskflow && find ...'}
[tool] Read {'file_path': '.../README.md'}
[tool] Read {'file_path': '.../tests/test_core.py'}
[tool] Read {'file_path': '.../taskflow/core.py'}
[tool] Bash {'command': 'python3 -m unittest discover -s tests -v 2>&1'}
[tool] Edit {'file_path': '.../taskflow/core.py', ...}
[tool] Bash {'command': 'python3 -m unittest discover -s tests -v 2>&1'}
[result] success结果:先调 Skill,再读 README/测试/实现,跑失败,改实现,跑绿;2/2 通过,没改测试。
这次跑之前,我踩了两个真实的坑:
max_turns=8不够。 它在“列目录、看.claude、跑测试”上花了几轮,第 8 轮被error_max_turns截断;改成 12 才够。真实模型不是按你的“理想步骤数”走。Bash不在allowed_tools里,它跑不了测试。 第一次成功改完代码,但测试命令被权限拦住;它只能说“按代码逻辑应该通过”,不能声称跑绿。把Bash放进白名单后,才真的跑测试。
这两个坑都不是 Skill 本身的问题,是“上下文/技能”和“权限/轮数”的配合问题。真实运行里,它们经常一起出现。
说明:commands 在这次 init 里是 None;skills/slash_commands/tools 都在 data 里。别拿一个字段推断“模型看不看得见”,要看真实调用。
13.10 下一章:长任务
上下文有了,Skill 有了,但任务一长,还是会满。
第 14 章讲 SDK 里的长任务:自动压缩、PreCompact、外置笔记、子 Agent、resume/checkpoint,以及“哪些是 SDK 自带,哪些还要你自己补”。
本章代码 上下文与 Skill:https://github.com/kevinkelin/claude-agent-sdk-book/blob/master/code/sdkagent/ch13_skills.py 两个 Skill:https://github.com/kevinkelin/claude-agent-sdk-book/tree/master/code/lab/taskflow/.claude/skills 项目规则:https://github.com/kevinkelin/claude-agent-sdk-book/blob/master/code/lab/taskflow/CLAUDE.md 说明:本章代码未在本机运行(环境没装 SDK/凭据);
setting_sources默认值和 Skill 发现规则以你的版本/实测为准。