Anatomy of a skill
Anatomy of a skill / 技能解剖学
This started with a surprise, not with a plan. I needed a survey of the state of the art on a topic I was considering working on, so I ran deep-research, a skill that ships with Claude Code, and it came back with a report I didn’t expect from a single run —27 sources, 123 claims extracted, 25 verified, and of those 18 confirmed and 7 refuted— with an executive summary, caveats, and open questions. What struck me wasn’t the volume (I already covered the 109 agents in the previous post) but that all of it came out of a single skill. I wanted to see how it was built.
这一切始于一个惊喜,而非预先的计划。我当时需要对我正在考虑研究的一个课题进行现状调研,于是我运行了 Claude Code 自带的 deep-research 技能。令我意外的是,单次运行就产出了一份详尽的报告——包含 27 个来源、提取出的 123 条声明、25 条经过验证的声明(其中 18 条确认,7 条反驳),以及执行摘要、注意事项和待解决的问题。让我震惊的不是数据量(我在上一篇文章中已经讨论过 109 个智能体),而是这一切竟然都源自同一个技能。我迫切想知道它是如何构建的。
It wasn’t easy to find: it doesn’t live in .claude/skills/ or in ~/.claude. It’s compiled into the Claude Code binary as a bundled workflow, and a comment in the code tells its origin: “Ported from bughunter architecture”. It’s 349 lines of JavaScript. Reading them was the research.
这并不容易找到:它既不在 .claude/skills/ 目录下,也不在 ~/.claude 中。它被编译进了 Claude Code 的二进制文件中,作为一个捆绑的工作流存在。代码中的一条注释揭示了它的起源:“移植自 bughunter 架构”。它总共 349 行 JavaScript 代码。阅读这些代码本身就是一次研究过程。
How a prompt is written / 提示词是如何编写的
Inside there are three prompts, and all three follow the same shape: a role in the title, the context (the original question plus the specific input), a task as a numbered checklist, an explicit decision criterion, and the output format. The verifier is the clearest one:
代码中包含三个提示词,它们都遵循相同的结构:标题中的角色设定、上下文(原始问题加上特定输入)、作为编号清单的任务、明确的决策标准以及输出格式。其中验证器(verifier)的部分最为清晰:
(Code snippet omitted for brevity)
It’s exactly the framework I use when I review one of my own prompts —role, context, task, format, constraints— but with two details I don’t usually include: the decision criterion is written as a rule (refuted=false only if…) and ties resolve by default toward the conservative side (“Default to refuted=true if uncertain”). A prompt that doesn’t say what to do when in doubt leaves that decision to the model, and that’s where the tidy-but-unfounded answers show up.
这正是我在审查自己的提示词时所使用的框架——角色、上下文、任务、格式、约束——但它有两个我通常不会包含的细节:决策标准被写成了一条规则(仅在……时 refuted=false),并且在出现平局时默认倾向于保守的一方(“如果不确定,默认 refuted=true”)。如果一个提示词没有说明在怀疑时该怎么做,就会把决定权留给模型,而这正是那些看起来整洁却毫无根据的答案出现的原因。
How a skill is built / 技能是如何构建的
A skill is more than a long prompt. What I saw in the file falls into six pieces:
- Trigger metadata. The meta declares the name, the description, and the five phases, but the key is
whenToUse: it’s what the model reads to decide whether to invoke the skill, and it includes a prior instruction —if the question is underspecified, ask two or three clarifying questions before starting. - Tuning constants at the top. No magic numbers buried in the code.
- One schema per agent. Each of the five agent types returns JSON validated against a schema. That’s what makes the pipeline composable: one agent’s output is the next one’s typed input.
- Prompts as functions.
SEARCH_PROMPT(angle),FETCH_PROMPT(source, angle),VERIFY_PROMPT(claim, v): they take the input and return the text. The prompt isn’t copied, it’s instantiated. - Explicit orchestration. Search and fetch go through
pipeline(): each angle moves on to fetching its sources as soon as it’s done, without waiting for the others. Before verification there’s a barrier —and the comment says so: “Barrier here is intentional”— because the pool of claims has to be complete before it can be ranked. Then, nestedparallel(): 25 claims × 3 votes. - Defensive design. Every early exit (no claims, everything refuted, failed synthesis) returns a useful result with stats instead of throwing. A null vote counts as an abstention, not as a free pass.
一个技能不仅仅是一个长提示词。我在文件中看到它由六个部分组成:
- 触发元数据:元数据声明了名称、描述和五个阶段,但关键在于
whenToUse:这是模型用来决定是否调用该技能的依据,它包含一条前置指令——如果问题描述不清,在开始前先提出两到三个澄清问题。 - 顶部的调优常量:代码中没有隐藏的“魔法数字”。
- 每个智能体对应一个模式(Schema):五种智能体类型中的每一种都返回经过模式验证的 JSON。这使得流水线具有可组合性:一个智能体的输出是下一个智能体的类型化输入。
- 作为函数的提示词:
SEARCH_PROMPT(angle)、FETCH_PROMPT(source, angle)、VERIFY_PROMPT(claim, v):它们接收输入并返回文本。提示词不是被复制的,而是被实例化的。 - 显式编排:搜索和获取过程通过
pipeline()进行:每个角度一旦完成,就会立即开始获取其来源,而无需等待其他角度。在验证之前有一个屏障——代码注释明确写道:“此处的屏障是有意为之”——因为必须在声明池完整后才能进行排名。随后是嵌套的parallel():25 条声明 × 3 次投票。 - 防御性设计:每一个提前退出(无声明、全部被反驳、合成失败)都会返回带有统计信息的有用结果,而不是直接报错。空票被视为弃权,而不是默认通过。
And the final result carries agentCalls: the skill computes its own cost (1 + angles + sources + claims × 3 + 1). That’s where the 109 came from.
最终结果还携带了 agentCalls:该技能会计算自身的成本(1 + 角度 + 来源 + 声明 × 3 + 1)。这就是 109 这个数字的由来。
Trimming the pattern / 简化模式
The proof that I understood the pattern was reusing it. A couple of later runs on that same topic got cut off by the session limit before verification, and instead of repeating them in full I wrote reverify-linea-c: same meta, same constants, same verdict schema, and the same three-vote verifier, but with no Scope, Search, or Fetch —those phases were replaced by an array of 22 already-extracted claims. The whole skeleton inherited, one array of my own. All 22 came back confirmed.
我理解这一模式的证明就是对其进行复用。后来针对同一课题的几次运行在验证前被会话限制中断了,我没有重复整个过程,而是编写了 reverify-linea-c:相同的元数据、相同的常量、相同的判定模式以及相同的投票验证器,但去掉了范围(Scope)、搜索(Search)和获取(Fetch)阶段——这些阶段被一个包含 22 条已提取声明的数组所取代。整个骨架被继承了下来,只加上了我自己的一个数组。最终,所有 22 条声明都被确认了。
What I take away: a good skill isn’t a long prompt, it’s a short, well-formed prompt, instantiated many times by an orchestration that knows where to wait and where not to, and that measures what it spends. And it reads in an afternoon.
我的总结是:一个好的技能不是一个长提示词,而是一个简短、结构良好的提示词,通过一种知道何时等待、何时不等待,并且能衡量自身开销的编排方式进行多次实例化。而且,它只需要一个下午就能读完。