Claude Code 核心团队血泪复盘:后悔使用/构建 Agent 时没早点刷到
AI 工程师多少都经历过这样的绝望时刻——第一天,看完各种酷炫的 Demo,满怀信心地开始写一个 Agent,你觉得 AGI 就在眼前;到了第三天,你给它加了十几个工具,想让它接管代码库,它却突然变成了一个「智障」——死循环、幻觉、明明有工具却死活不用…
导致 Agent 变蠢的,往往是我们这些喜欢面向接口设计、喜欢二分断点调试 bug,不懂儿童心理学的「程序员」。
01. 主动探索 > 被动接受
传统提示词工程师直觉是:写一个事无巨细的 System Prompt,把项目背景、API 文档、代码规范一股脑全塞进去。
Claude 团队最早期也是这么干的——RAG。结果索引检索 bug 多,反倒让 Agent 失去了 Agency。
后来,他们做了一个改变:给一个 Grep 的搜索工具,与其外设 RAG 注入上下文,不如让 Agent 自己去代码库里翻。
渐进式披露(Progressive Disclosure):不一次性塞满上下文,而是让 Agent 通过工具和文件系统,按需、递归地发现它需要的信息。
结果模型不仅精准找到了需要的信息,还在搜索的过程中自动建立起了整个项目关于某任务的认知上下文。

这种渐进式披露模式后来成为了 Skills 的设计原则和整个 Harness 工程的基石。
02. 趁手工具,实验迭代
设计 Tool Set,是构建 Agent 最微妙最艺术的环节,这确定了 Agent 可供使用的工具范围。

例如,Claude Code 团队曾想给增加一个「向用户提问对齐」的能力。
-
第 1 次尝试:复用现有
ExitPlanTool计划工具,加了一个参数,让模型计划完顺便把问题带上。结果 Agent 不知道该先写计划,还是该先提问。两个复杂意图纠缠在一起,导致幻觉失控。 -
第 2 次尝试:增加系统提示词,让模型在输出中用特定语法(比如
[?])提问。结果命中极其不稳定,经常漏符号,或自己发明新语法。 -
第 3 次尝试:单独写一个干净的
AskUserQuestion工具。Agent 可随时自主调用,调用时前端弹窗阻塞直到用户回答。结果还不错。
最好的工具设计,不是让模型**「被迫」按照你的格式输出;而是顺应它的直觉,让它在遇到困难时「本能」**地想要拿起这个工具。
但另一方面,随着基模变强,工具设计可能过时,需要实验迭代。
例如,在 Claude Code 早期,模型易在长任务中迷失,忘记要做什么。在这样的背景下,团队为 Agent 设计了一个 TodoWrite 工具:
- 让模型开始时写 Todo 列表逐项勾掉;
- 每隔 5 轮对话加入系统消息,比如,「你还有这些 Todo 没做」。
*这个设计在当时很有效。*但随着 Opus 4.5 发布,情况变了。
- 新模型不仅不需要被提醒 Todo,多余的系统提醒反而开始干扰它:模型不敢主动修改或扩展 Todo 列表,即使任务情况已经发生了变化。
- 更重要的是,Opus 4.5 使用 Subagents 能力更强,但多个 Subagent 无法协调共享同一个 Todo 列表。
如果说 Todos 是贴在自己桌上的便签;Tasks 就是团队看板。
因此,Tasks 因此诞生,使用 CLAUDE_CODE_TASK_LIST_ID=project-name claude,把多个 CC 实例指向同一个 Task List(对 claude -p 和 Agent SDK 同样有效):
-
依赖关系:Task A 未完成,Task B 不能开始。这让复杂项目的执行顺序有了保证,多个 Agent 不会同时踩到同一个依赖。
-
持久化:Tasks 存储在
~/.claude/tasks,任何 session 和 Subagent 都能读写;Session 关闭、重启,Tasks 依然在。 -
多 Agent 跨会话广播:一个 Subagent 实例更新了某 Task 状态,所有当前正在运行的 session 都能实时感知这个变化。
03. 面向缓存命中率设计
传统缓存是基于 Key-Value 的,在 LLM API 中,Prompt Caching 是基于前缀匹配的。
想象成搭积木:底层是 System Prompt 和 Tool Schema,上面是历史对话,最顶层是当前的问题。
只要你动了底层的一块积木,上面所有的积木(也就是之前聊过的几十万 Tokens)都要重新全价计算。

这带来了四个极其反直觉的案例。
教训一:按需调用工具?
很多工程师直觉是:给模型当前任务(意图)需要的工具,不给多余的。多节约 token,为什么要给模型它不会用到的工具?
但在 Prompt Caching 的框架下,如果你在会话进行到一半时发现需求变了要修改工具集,那缓存就失效了。

Plan Mode 是最好的例子:进入 Plan Mode 时,直觉是「换成只有读取工具的工具集」。但 Claude Code 的实现中,工具集永不改变,缓存始终有效。CC 是把 EnterPlanMode 和 ExitPlanMode 本身做成工具,当用户触发 Plan Mode 时,给模型发送一条系统消息,说明当前处于计划模式、应该只读取文件、完成计划后调用 ExitPlanMode。
这个设计还带来了一个额外好处:EnterPlanMode 是 Agent 可自主调用的工具,因此在在遇到复杂问题时,其可主动进入 Plan Mode。
为了缓存命中率,可用工具来模拟状态转换,而不是修改工具集本身来反映状态。

随着 MCP 生态扩张,一个 Agent 可以接入的工具数量急剧增长;7 个以上的 MCP 服务器,光工具描述就消耗了 70k tokens。这是一个真实的工程问题:API调用中,不能无限制塞工具描述,又不能在需要某个工具时才临时调用添加。
工具懒加载:保证 Prompt Caching 的前缀稳定性并提供 Tool Search 原子工具。
即当 MCP 工具描述超过上下文预算的 10% 时,工具被替换成轻量级 stub(只保留工具名和 defer_loading: true 标记)。当 Agent 需要某个工具时,可以自主调用 ToolSearchTool 来搜索并加载完整的工具定义。
教训二:切换到便宜模型省钱
假设你和 Opus 聊了 100k tokens,现在有个很简单的问题,想切 Haiku 省钱。
这个想法听起来符合直觉,但切换到 Haiku 意味着它没有这 100k tokens 的缓存,所有这些 tokens 都需要以全价重新处理,很可能比直接让 Opus 回答还贵。
如果需要切换模型,正确方式是用 Subagent 做 handoff:让 Opus 准备一条交接摘要,把任务背景和当前进展压缩成简短说明,启动一个低成本高速度的模型(比如指定了 haiku 的 Built-In Explore)接手;这样新启动的 Subagent 需要处理的上下文极短,不仅真正节省了 Token 成本,还能避免被主对话长历史中积累的无关信息干扰,提高了单次子任务的响应速度和专注度。

其次,Subagent 需考虑「最小权限原则」,限制模型的爆炸半径。例如,笔者观察到 CC 内置的 Explore 和 Plan 不仅在系统提示词中被反复使用大写字母(CRITICAL、STRICTLY PROHIBITED)禁止修改文件,也在代码层面上通过黑名单 disallowedTools 剥夺了写入和编辑的工具权限,屏蔽了 CLAUDE.md(omitClaudeMd: true)以防被项目特定规则带偏。
教训三:日期/文件修改写在系统提示词
更好的方式是:在下一条用户消息或工具结果里,用一个标记(例如,<system-reminder>)把 update 注入进去,让模型知道「btw,今天是周三」或「xx文件已被用户改动」。
教训四:用专门系统提示词做压缩
对话上下文将满,需要压缩把对话历史总结为摘要,继续新 session。

一个朴素实现是:用不同的 System Prompt 和无工具请求调用 API 生成摘要。然而,这破坏了缓存,这个请求和主对话完全没有共同前缀,所有 tokens 都需要全价处理。
更好的方式是 Cache-Safe Forking:使用完全相同的 System Prompt、工具定义和上下文,把主对话的所有历史消息也附上,*在最后追加压缩指令。*保证前缀缓存直接命中,只有最后的压缩指令是新 tokens。
05. Skills 是 Agent 的避坑指南

Tool Set 确定了 Agent 可供使用的工具范围,Skill Set 进一步确定了工具调用顺序模式的确定性,对齐偏好。
| 参数 | 必填 | 说明 |
|---|---|---|
name |
否 | 省略时使用目录名,只允许小写字母、数字和连字符 |
description |
建议 | 使用时机,Claude 据此判断何时应用。超过 250 字符会被截断,关键信息放前面。 |
argument-hint |
否 | 自动补全时显示的参数提示,例如 [issue-number] 或 [filename] [format] |
disable-model-invocation |
否 | 设为 true 可禁止 Claude 自动加载此 skill,改为手动用 /name 触发。默认 false |
user-invocable |
否 | 默认 true。设为 false 则从 / 菜单中隐藏,适合不应由用户直接调用的 skill。 |
allowed-tools |
否 | 无需询问权限即可使用的工具列表 |
model |
否 | 激活时使用的模型 |
effort |
否 | 激活时的推理 effort,会覆盖会话级别的设置。默认继承会话设置。 |
context |
否 | 设为 fork 时在独立的子 agent 上下文中运行 |
agent |
否 | context: fork 时指定使用的子 agent 类型 |
hooks |
否 | 限定在此 skill 生命周期内的 hooks 配置 |
paths |
否 | Glob 模式,限制此 skill 仅在匹配的文件路径下自动激活。支持逗号分隔的字符串或 YAML 列表。 |
shell |
否 | skill 内 !`command` 块使用的 shell。可选 bash(默认)或 powershell。使用 PowerShell 需设置环境变量 CLAUDE_CODE_USE_POWERSHELL_TOOL=1 |
2026 年 1 月,Slash Commands 和 Skills 正式合并。2026 年 3 月,skill-creator 全新升级,新增多智能体 benchmark 评估能力。
下面是迭代 Skills 的最佳实践:
- **少写怎么做,多写坑在哪。**识别 Agent 最容易犯的错误(Gotchas),一个好的 Skill 的 Gotchas 区会随着使用不断增长。
- 用好
assets、references和scripts。Set Up 可以使用config.json,必要时可提示使用AskUserQuestion工具询问用户。 - 不要事无巨细规定执行步骤,提供一定的灵活度,尽量聚焦于能让 Claude 跳出常规思维模式的信息。程序员写 API 文档喜欢罗列功能,但大模型通晓默认常识性观点,只是不懂领域知识。
- 在 Description 写清楚触发时机,这个字段不是给「人」看的功能摘要。
- 在 scripts 文件夹提供支持组合链式调用的 CLI/helper functions。
- Skill 可以用文件系统存储历史记录。比如
standup-post把每次发出的日报存在standups.log里,下次运行时 Claude 会先读取历史,然后告诉你"和昨天相比,今天新增了 X、完成了 Y"。注意:升级 Skill 时可能会删除 Skill 目录里的文件,建议把持久化数据存在${CLAUDE_PLUGIN_DATA}提供的稳定目录里。 - 可包含只在该 Skill 被调用时才激活的 Hooks,持续到 session 结束,例如
/careful 激活后,通过 PreToolUse Hook 阻止。
| Skill | 功能 | 适用场景 |
|---|---|---|
/careful |
通过 PreToolUse 匹配器拦截 rm -rf、DROP TABLE、force-push、kubectl delete 等危险命令 |
明确在操作生产环境时启用,常驻会令人抓狂 |
/freeze |
阻止任何不在指定目录下的 Edit/Write 操作 | 调试时防止"本想加日志,却不小心修了不相关的代码" |
Skills 分发观测
-
小团队使用代码仓库自动共享,大团队建立内部 Plugin Marketplace。
-
用
PreToolUseHook 记录每个 Skill 的调用频率。 关注「高频但不够好,值得投入打磨」技能和「从不被触发,可能 description 写得不够好,或者根本没人用」。 例如,https://gist.github.com/ThariqS/24defad423d701746e23dc19aace4de5。
06. Playground
Playground:让 Agent 生成交互式的 HTML 小工具,支持拖拽、点击、调整参数,然后把操作结果以 prompt 的形式输出,粘贴回 Agent 继续对话。
对于用纯文本难描述清楚的任务可以采用 Playground 方法,例如:调整一个 UI 组件的视觉设计、讨论系统架构的某个节点、平衡游戏里某个角色的能力数值。

对于 PM 而言,与其长篇大论写 PRD 画 Mockup,不如让 CC 做一个核心逻辑的 MVP 原型,再决定是否要投入资源真做。
Finally
Agent 没有放之四海而皆准的科学公式;设计 Agent Harness,是一门科学,也是一门艺术。

试想如果把你关在一个小黑屋里解一道复杂数学题,你希望手边有什么工具?
- 如果你只会心算,你需要纸和笔;
- 如果你懂进阶操作,一个科学计算器更好;
- 如果你懂编程,一台带有 Python 环境的电脑才是大杀器。

不要像 Agent 一样活着,但也不要拒绝共情 Agent。
请试着像 Agent 一样思考,这才是走向 AGI 前夜,每个超级个体的必修课。
参考资料
- Tool Search now in Claude Code
- We’re turning Todos into Tasks in Claude Code
- Merging Slash Commands into Skills in Claude Code
- Making Playgrounds using Claude Code
- How we use Claude Code in Slack
- Lessons from Building Claude Code: Prompt Caching Is Everything
- Lessons from Building Claude Code: Seeing like an Agent
- Lessons from Building Claude Code: How We Use Skills
- Claude Agent SDK Workshop
- Improving skill-creator: Test, measure, and refine Agent Skills
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)