抓包 Claude Code:system prompt、tools 与 skills 的分层设计
发布时间:
平时一直用 Claude Code,但不搞清楚它背后怎么跟 API 交互的,用起来总觉得不是“知其所以然”,非常难受。于是抓了个包,看看消息结构到底长什么样。后来干脆不自己造轮子了。。一开始用的是 claude-devtools,封装层太多,我想看的原始 JSON 全被包进去了,看不到裸请求。后来换成蚂蚁同学写的 cc-viewer(我也参与贡献了🤣),直接看原始api。
为什么 tools 放 system、skills 放 user 里边
这是整个设计里最值得讲的部分。这个位置还变过,之前 skills 是写在 system message 的,后来我忘了是哪个版本以后,就移到了 user message 层。
首先,tools 和 skills 都有个 Skill tool,但 Skill tool 只是 tools 数组里的第 18 号工具,和 Bash、Read 一样,是个协议级函数。它的作用是:模型给出一个 skill 名字,harness 去磁盘上找到对应的 SKILL.md,把内容读出来,作为一条 user message 注入上下文。所以 Skill tool 本身在 system/tools 层,但 skills 的具体内容全在 user message 层。
为什么 skills 不直接做成 tools?如果把 30 个 skills 全定义成 top-level tools,每个都要写 JSON Schema、description、parameters,那 tools 数组直接爆炸。而且 skills 是插件/项目动态发现的,每台机器不一样,今天 20 个明天装个新插件变 30 个,全塞进 tools 字段里,每次请求光 tool definitions 就吃掉大量 token。更关键的是,skills 本质上不是”能力”,是”方法”。Bash、Read、Edit 是模型能调用的真实能力——执行命令、读文件、改文件。Skills 是告诉模型遇到什么场景该用什么方法——比如 systematic-debugging 教模型怎么排障,brainstorming 教模型怎么先想清楚再动手。一个属于”能做什么”,一个属于”该怎么做”。前者必须放协议层,后者放上下文就够了。
那 skills 为什么放第一条 user message、不放真正的 system prompt?我自己想的是, system prompt 是 Anthropic 官方写的,对所有 Claude Code 用户都一样。Skills 是用户环境决定的——你装了 superpowers 插件就有 brainstorming 和 TDD,没装就没有。如果把 skills 写进 system prompt,等于每个用户的 system prompt 都不一样,这做不到,也不该做到。所以 harness 的处理是:system prompt 不动,skills 的索引(name + description)在每次会话开始时作为 <system-reminder> 注入第一条 user message。模型看到了就知道有哪些 skills 可用,需要时再调 Skill tool 加载完整内容。
有个特殊例外:using-superpowers 这个 skill 不是只放索引,而是全文注入。他现在从之前的一个人的项目,现在好像变成了基建一样,codex 和 claude 都是官方推荐装的 skills 的核心位。它是 bootstrap skill——教模型”必须先检查 skills”。如果它自己也要通过 Skill tool 加载,就死循环了:模型需要先知道必须调 Skill tool,才能加载 using-superpowers;但模型又需要 using-superpowers 来知道必须调 Skill tool。所以 SessionStart hook 直接把全文灌进第一条 user message。代价是每次对话开局就要为它付 token,但是好在我们缓存命中很高,DeepSeek 基本能达到99.5% 。
这套设计本质上是在做软硬分层。tools 是硬约束:结构化 schema,模型不可能绕开,要调 Bash 就必须填 command 字段。Skills 是软约束:自然语言塞在 user message 里,模型可能看也可能跳过。
我看到的日志里就有例子——我给的任务是看两个仓库的代码结构,按 using-superpowers 的规则 “even a 1% chance” 就该先调 Skill,但 assistant 的 thinking 直接是 “Let me explore both projects”,发了两个 Bash tool_use 就开干了。把规则写在 user message 里,不等同于模型会严格执行。以后自己设计 agent 框架的时候,什么东西放协议层、什么东西放 prompt 层,这个边界要提前想清楚。
system prompt
先看真正 API 层的 system prompt。这是请求里 system role 的内容,不是后面 user message 里那些 <system-reminder> 标签。抓出来的结构大概是:
You are Claude Code, Anthropic's official CLI for Claude.
You are an interactive agent that helps users with software engineering tasks.
Use the instructions below and the tools available to you to assist the user.
IMPORTANT: Assist with authorized security testing...
IMPORTANT: You must NEVER generate or guess URLs...
# System
- All text you output outside of tool use is displayed to the user...
# Doing tasks
- The user will primarily request you to perform software engineering tasks...
# Executing actions with care
Carefully consider the reversibility and blast radius of actions...
# Using your tools
- Prefer dedicated tools over Bash when one fits...
# Tone and style
- Only use emojis if the user explicitly requests it...
# Text output (does not apply to tool calls)
Assume users can't see most tool calls or thinking...
# Session-specific guidance
- If you need the user to run a shell command themselves...
- Use the Agent tool with specialized agents when...
- For broad codebase exploration or research that'll take more than 3 queries...
# auto memory
You have a persistent, file-based memory system at...
# Environment
You have been invoked in the following environment:
- Primary working directory: /Users/hanwenbo/PycharmProjects/hwb96.github.io-src
- Is a git repository: true
- Platform: darwin
- Shell: zsh
- OS Version: Darwin 25.3.0
- You are powered by the model deepseek-v4-pro...
# Context management
When working with tool results, write down any important information...
system prompt 里的内容分几块:模型身份声明(”你是 Claude Code”)、行为准则(安全边界、URL 猜测禁令)、工具使用规范、语气要求、会话特有指引(Agent 工具的使用场景、代码探索策略)、以及运行时环境信息(工作目录、git 状态、操作系统、当前模型)。这些是 Anthropic 官方写的,每次请求都带,不动。
26 个自带工具
tools 字段里定义了 Claude Code 自带的所有工具。抓出来的完整列表:
1. Agent - 启动子 agent 处理复杂任务
2. AskUserQuestion - 向用户提问
3. Bash - 执行 shell 命令
4. CronCreate - 创建定时任务
5. CronDelete - 删除定时任务
6. CronList - 列出所有定时任务
7. Edit - 编辑文件(精确字符串替换)
8. EnterPlanMode - 进入计划模式
9. EnterWorktree - 创建隔离的 git worktree
10. ExitPlanMode - 退出计划模式
11. ExitWorktree - 退出 worktree
12. Monitor - 后台监控长任务输出
13. NotebookEdit - 编辑 Jupyter notebook
14. PushNotification- 推送桌面通知
15. Read - 读取文件
16. RemoteTrigger - 调用 claude.ai remote-trigger API
17. ScheduleWakeup - 调度 /loop 的下次唤醒
18. Skill - 加载一个 skill
19. TaskCreate - 创建任务
20. TaskGet - 获取任务详情
21. TaskList - 列出所有任务
22. TaskOutput - 获取后台任务输出
23. TaskStop - 停止后台任务
24. TaskUpdate - 更新任务状态
25. WebFetch - 抓取网页内容
26. WebSearch - 搜索网页
每个工具都是带完整 JSON Schema 的函数定义。比如 Bash:
{
"name": "Bash",
"description": "Executes a given bash command and returns its output...",
"parameters": {
"type": "object",
"properties": {
"command": {"type": "string", "description": "The command to execute"},
"timeout": {"type": "number", "description": "Optional timeout in milliseconds"},
"description": {"type": "string"},
"run_in_background": {"type": "boolean"},
"dangerouslyDisableSandbox": {"type": "boolean"}
},
"required": ["command"]
}
}
这些 tools 是 API 协议层的定义,模型拿到的是结构化 schema,不是自然语言描述。它不会”理解” Bash 是干什么的然后决定用不用——它的训练目标就是:当需要执行命令时,生成一个符合 Bash schema 的 tool_use。