摘要:同一段盲测提示词,手写到第三遍,我把它固化成了自己的第一个自定义 agent。这篇讲造岗的全套手艺:什么时候该把重复劳动抽象成岗,一份 agent 定义文件的三个读者各读什么,字段拼错和方言差异怎么防,以及 trigger-tester 从需求句到宿主登记回执的可跟跑全程。
共约 4664 字 · 阅读约 12 分钟
老规矩,声明放在最前面:公司、客户和一切可对号入座的身份不出现。标本实名保留——code-architect 和 visual-judge 是本机插件缓存里公开可查的文件,trigger-tester 是我自己写的岗;文中的推算和没验证的账,照实标注。
1、开场:同一张纸条,我写了四遍
本以为二十多天学下来,我的 agent 库多少攒了点存货。实际盘点:~/.claude/agents和~/.zcode/agents两个目录全空,一个文件都没有,机器上跑着的 agent 全部来自插件。二十多天、十二课,我只当过岗的用户,没当过岗的作者。
这周,空目录里落进了第一个文件:我自己写的第一个自定义 agent,trigger-tester。让它发生的不是什么规划,是一张纸条——同一张「触发盲测纸条」,一个月里碰了四遍,前三次手写:
- 第 2 课作业盲测五跑,手写第一遍;
- 第 9 课搭评测臂,第二遍;
- 第 11、12 课工坊六跑,第三遍;
- 到这次番外,第四遍,我没再手写,纸条变成了一个文件。
翻笔记对时间,四条记录拉得很开:10 月 5 日、10 月 20 日、10 月 23 日、10 月 25 日。每一遍同构,只往前拱一小步:第一遍只有协议,第二遍加了两臂纪律,第三遍补上靶场路径,第四遍,整张纸条固化成了岗。
skill-creator 的判据,从 skill 侧搬到 agent 侧,一字不用改:
“Look for repeated work… Write it once.”
出处:skill-creator 官方判据。
不造岗的账我也顺手算了:每臂重新手写一遍协议,漏一条——比如环境中立律,臂与臂的环境必须等价——就废一臂。第 9 课那笔污染抓包,追下去就是手写纪律漏了项;一次漏项,一臂数据作废。同一张纸条写到第三遍,就该把它造成岗了。
2、先拆清题:subagent 还剩哪一块
起草前有人问过我两句:「当时是规划了几篇额外的啊?」「应该就是 skill 里关于 subagent 的内容吧,分几篇幅呢?」我的回答是:番外只此一篇。这个追问里藏着一个误会,而且我猜不止问的人有——把「造岗」当成要学一整套 subagent 新体系。拆开看,这个大主题里四样东西,三样前面都过了手:
- 派工,Agent 工具怎么调、指令怎么写——第 7 课教过;
- 评测装置,差分、串行靶场、环境中立——第 9 课已经在用;
- 实战跑批,多臂并发跑真实任务——第 11、12 课工坊六跑干的就是这个;
- 自定义,写一份让宿主肯登记的定义文件——四样里只有这个没碰过,本篇全部内容就是它。
只此一篇是裁定过的,理由也直白:subagent 独占的素材只有一课的量,单线养不起第二学期;本机嵌套是零层,机制先瘸了一条腿——那条腿怎么瘸的,第 6 节细说。
误会从哪来的,我复盘过:十二课里,岗一直以「别人的岗」的面目出现,插件的 feature-dev、documents,个个替我干活,看起来像个要整学期才能啃下来的体系。真拆完,你要新学的只有一件事:写一份宿主肯认的定义文件,加一段替你把门的正文。剩下的,是你已经会的东西换个岗位用。
那 skill 和 agent 的分界,官方一句话:
“Consider Skills instead when you want reusable prompts or workflows that run in the main conversation context rather than isolated subagent context.”(如果你想要的是在主对话上下文里跑、而不是在隔离的 subagent 上下文里跑的可复用提示词或工作流,那就改用 Skills。)
出处:官方 Subagents 文档。
skill 卖知识,岗卖隔离。知识进主对话就够用,隔离才是新开会话的理由。判据反过来读也成立:频繁来回、要共享上下文、延迟敏感,占上任何一条,就别造岗。这条分界线,第 7 节造岗时它是主判据。
3、一份定义文件,三个读者
我原以为造岗得先啃一张 19 字段的大表。动笔当天把官方文档现查了一遍:字段确实 19 个,camelCase 命名,但必填只有 name 和 description 两个,其余全是配置项——比我第 7 课备课那回核对时又多出几张新面孔,maxTurns、memory、isolation 这些。字段面还在扩,这更说明全表不用背。全表不进正文,正文只回答一个问题:这份文件写出来,谁在读。
读者有三个,各读各的那一份。委派者(主对话)读 description,这一段常驻上下文,它靠这个决定派不派、怎么派;运行时读 tools、model 这些配置字段,spawn 的第 0 轮装配一次,之后不再看;被委派者(分身)读 body。官方对 body 的说法:
“Subagents receive only this system prompt plus basic environment details like the working directory, not the Claude Code system prompt.”(Subagent 只收到这份系统提示词,外加工作目录之类的基本环境信息,收不到 Claude Code 的系统提示词。)
出处:官方 Subagents 文档。
你在主对话里攒下的一切常识,分身一件都带不过去。body 是分身的全部世界观,不是配置的补充。
拿一次 HTTP 请求打比方:
- description 是网关路由表里的那一行,决定流量导不导过来;
- tools 和 model 是部署配置,容器起来时装配一次;
- body 是服务自己的世界观,它看不见网关的任何配置。
写错读者,各有各的症状:description 写成操作手册,委派者干脆不派;body 写成招聘广告,分身不知从哪儿下手。
三个读者也解释了钱花在哪。description 是唯一常驻的长文字段,主对话走到哪它跟到哪,所以预算关全冲它来,第 4 节细说;body 只在派发后的分身会话里活着,再长也不占主对话一个字。常驻的按字收费,一次性出场的按次收费,这笔账第 4 节就要算。
三层语言观来自另一条追问,有人问我:「这一课还有什么其他语言学习的吗?」有,比想的多。一份定义文件是三种格式语言在协作:
- YAML 头,给机器的配置;
- Markdown 正文,自然语言编程,写的不是代码,是分身的世界观;
- JSON 尾,body 里约定的交卷契约,一行定格式,机器可判分。
这条追问逼出来一句话:你在用自然语言编程。这是本篇最反直觉的一句。语言选择顺带交代:description 和 body 写中文成立——委派者做的是语义领地判断,没有关键词匹配器,这是它大概率成立的理由;trigger-tester 从 description 到 body 全中文,但中文派发会不会命中,这台机器还没跑过一臂,我不替它打包票。跨宿主我没验过,英文更稳是推断,不是实测。
body 的位置我还了一笔旧账:第 12 课终测我把 skill 正文记成了系统提示词——skill 正文是对话层的一条消息,agent body 是系统层,一层之差,这回算把账还上。
4、字段的坑:静默忽略与第四道预算关
字段拼错,官方文档的答案写得很平静:
“ignores a field it doesn’t recognize without reporting an error.”(不认识的字段直接忽略,不报错。)
出处:官方 Subagents 文档。
skill 侧我踩过静默忽略,agent 侧更毒。disallowedTools 这个黑名单字段,误写成 kebab-case 的 disallowed-tools:不报错,无日志,黑名单静默消失,权限面反而变宽。像防火墙规则没加载,端口默认全开,你还以为拦着。skill 拼错丢的是特性,agent 拼错丢的是闸。写对了没人夸,写错了没人骂,全靠自己当场验。
命名规约差异是同族的坑,两处:
- 字段名各叫各的:skill 的 allowed-tools,agent 的 disallowedTools;
- 命名风格相反:一边 kebab-case,一边 camelCase。
互抄零生效。两边文档我都读过,写这节时还是把两套写法对着核了一遍才敢落笔。
防法只有一个:写完当场验。字段表对一遍只是第一步,真验是派一发看行为——黑名单在不在,分身一试便知。第 7 节的落位体检先过静态一遍,行为要在真派发里见。
预算还有第四道关。skill 侧三道关,第 6 课拆过:
- 单字段 1024,直接丢弃;
- 拼接 1536,截断;
- 菜单呈现,约 250。
agent 侧多一道:description 全库合计超 15,000 tokens,启动警告。量法变了,不是单岗,是全库合计,跟 1536 那道拼接关同族、量纲更大;岗造得越多,每岗分到的预算越薄。
这笔账生态里有人替我算过。GitHub 上 wshobson/agents 一个仓库攒了上百个岗——发文时 100,我检索当日目录已列 202——作者自己回头在 dev.to 发文复盘,标题就叫:
《I Built 100 Claude Code Subagents: The 12 That Actually Earn Their Context》:100 个里只有 12 个配留在上下文。
粗算一下,上百个岗的 description 全装进去,大概率把 15,000 的合计关直接顶爆。先说清楚:这是推算,不是实测。但「岗不在多」这四个字,有算术撑着。
这道关的脾气也温和:超线是启动警告,不是拒绝。可警告意味着账已经超了,薄下去的是每个岗在派发现场的可见度——常驻的东西挤成一堆,委派者挑花眼,谁的 description 都记不牢。
最后一条坑反着来:tools 字段空着不写,不是漏,是继承全部工具。省略也是决策。trigger-tester 正是吃了这条,细节放第 7 节。
5、两份标本,两种 body 哲学
文档讲原则,标本讲手艺。本机插件缓存里正好躺着一对现成教材,我把两份定义文件全文读了一遍,逐字段对质。code-architect,feature-dev 插件,官方出品:5 个字段;tools 一串逗号隔开,无写入、无 Bash(TodoWrite、KillShell 属会话管理类)——白名单即权限面,清单写死,分身伸不出清单外的手;body 是过程型的标准长相,三步流程,后面挂一张输出清单,教「怎么干」,全文件 34 行。
visual-judge,documents 插件:6 个字段,比 code-architect 多出的一个正是 thoughtLevel: max(本机方言,第 6 节细数);tools 是 YAML 列表,收窄到 Read 和 Bash 两件;body 是契约型——验收标准、分类法,末尾一行一页的 JSON verdict。它几乎不教「怎么干」,只规定「交什么」;输出格式本身就是纪律,因为格式是机器判的,含糊不得。
两种哲学不是风格偏好,是任务性质决定的。
- 探索类任务,路要模型自己走,步骤卡死反而走不动,所以 code-architect 只教原则加输出清单;
- 验收类任务,要的是可比对的产物,多一句自由发挥都是噪音,所以 visual-judge 把话说到每页一行。
你想学哪一种,先看你的岗接的是什么活。
最有意思的是 description。visual-judge 的 description 写到约 1200 字符,按 skill 侧的标准算超载。我原以为是作者手滑,对完三读者才回过味来:它的读者是委派者,常驻主对话,要在每次派发决策的现场替它说话——写短了,委派者想不起它。
两份标本摆在一起,结论就出来了:body 形态跟着任务走。探索类任务说不死「怎么干」,过程型;验收类任务说得死「交什么」,契约型。判断就一句:description 写给委派者,body 写给分身——前者管何时派、怎么派,后者管怎么干、或交什么。
6、本机方言卡:官方认的,不等于你的机器认的
文档是通用语法,你这台机器说的是方言。我盘出三处,一处比一处扎手:
- 推理档字段,官方叫 effort,本机叫 thoughtLevel;
- 落位目录,官方说 ~/.claude/agents,本机读的是 .zcode/agents;
- 嵌套,官方说岗可以再派岗,默认最多 3 层;本机是 0 层。
前两处照文档写,轻则字段无效,重则文件落错地方没人读。第三处是硬伤,来历要说清:第 7 课差分首跑撞见的,第 9 课又用三跑复验——三次跑,分身的工具清单里都没有 Agent 工具。套娃是零层,子进程里没有再 fork 的能力。
这条直接改了设计。岗只能当臂,当不了指挥;照官方文档设计「岗再派岗」的编排,在这台机器上是死信。指挥逻辑没有消失,它被摁回了主对话:串行还是并发、失败要不要重派,这些决定都由主对话做,装置只负责把每一次派发做干净。trigger-tester 因此是单臂装置:n 条查询,主对话派 n 次,它的 body 里没有任何派工语句。装置固化的是协议,不是自动化编排——主对话当唯一的指挥,岗当纯粹的臂,是这台机器上唯一跑得通的拓扑。
所以可移植写法只剩一条:跨宿主只写共识四字段,name、description、tools、model;方言字段想写,只写给单一宿主,当它是一次性的。生态里真有人这么干:wshobson/agents 那套岗,给 Claude Code、Codex CLI、Cursor 等 7 个宿主各生成一版定义。「同一岗、多宿主方言」不是我的推断,是别人用脚本在养的日常。
7、实战:trigger-tester 从 0 到 1
四遍纸条的最后一遍,不走手写,走固化。下面是全程,可跟跑。
7.1 目标
把盲测纸条变成一个岗。需求句一句写死:每次派发,给分身一条查询,加一句领地说明;分身按自己的常识干活;首个工具调用即判定触发;一行 JSON 交卷。输入一句话,输出一行 JSON,中间不设脚本。不设脚本是刻意的:skill 侧靠脚本锁行为,这里要量的偏偏就是模型裸着拿到纸条后的第一反应,脚本一插手,量的就不是它了。
7.2 思路
先回答为什么是岗不是 skill。盲测铁律:每臂必须全新会话——一防上下文残留,二守环境中立律。上下文残留长什么样,第 2 课见过:上一臂读过文件、试过命令,下一臂在上下文里直接捡现成,盲测就成了开卷考。skill 在主对话内跑,永远做不到自我隔离;agent 每次派发天然全新。我要的恰好就是隔离,判据不用绕,隔离需求本身就是造岗判据的正用。
7.3 实现
按造岗五步走,一列到底:
- 需求句一句写死:输入、分身干什么、交什么,一口气说完;
- 判据核对:官方判据至少中一,反判据(频繁来回、共享上下文、延迟敏感)任中即止,盲测装置过;
- 字段决策:description 写正例,再补三条负边界,什么场景不许派,白纸黑字——比如「只测触发,不评质量」就是一条,评质量是另一套装置的活;tools 刻意省略,盲测臂要的是干净的一般智力,限了工具反而会改变触发概率——「不改文件」也因此只写进 body 红线,没用 disallowedTools 关写闸,关闸会改变分身的工具面,盲测量的是自然状态,这是取舍不是疏忽;model 不写,继承;方言字段一个不写;
- body 契约:按纸条自足四件套写——身份、任务、边界、输出格式,纸条外的话等于没说;
- 落位体检:必填两个字段齐,camelCase 拼写逐个对,目录写本机读取位——这个岗落的是项目级,<仓库根>/.zcode/agents/,和代码同一个仓;用户级 ~/.zcode/agents 是否同样被读,我没验,不替你打包票。(还有一条「嵌套核对」:本机零层嵌套,第 6 节已交代,这里并进了第 3、4 步——方言字段不写,body 里不写派工语句。)
五步里第 2 步最容易跳。重复劳动没攒到三遍就抽象成岗,是为固化而固化,产物多半没人用。
body 里两条值得单独抄出来。一条是 attempted 判定:环境拒了但判定已发生,照样记触发——第 2 课实测过,分身的环境拒了 Skill 调用,触发判定照样发生,判定和执行必须分家。这一档值得单列:触发率是个分数,分母不能丢:环境拒了执行,判定本身已经发生,丢掉这个样本,分母就偏了。
另一条是红线三道:
- 不读被测文件,防泄盲;
- 不改文件;
- 工具调用不超过 10 次。
三条缺一,盲测就不盲了。第 3 步那三条负边界,则是写给委派者的:它常驻主对话,正例告诉它何时派,负边界告诉它何时忍住——误派一次,盲测数据就脏一组。
7.4 验证
文件落进 .zcode/agents/,宿主给了我一张回执,原样抄在下面:
“New agent types are available: trigger-tester.”
目录写对了没有,不用猜,登记回执就是活证据。第 1 节那笔双空盘点,到这儿改账:盘的是用户级,这个岗落的是项目级,反正是同一本账——agent 库 0→1,第一个岗是我自己的。
至于触发判得准不准,这个岗自己证明不了——它生下来就是给第 9 课那套差分装置当臂的。登记回执只证明宿主认了它;准不准,哪天真拿它跑盲测,一臂就知道。
收尾:带走三样,临走三问
带走三样:
- 一条判据:同一张纸条写到第三遍就该造成岗;分界记一句,skill 卖知识,岗卖隔离。
- 一套框架:三读者——description 给委派者,body 给分身,配置字段给运行时;字段只写各宿主都认的。
- 一张检查单:需求句、判据核对、字段决策、body 契约、落位体检,五步走完,等宿主登记回执。
临走三问,凭记忆答,别往上翻:
- 同事这周第三次原样重写同一段派工提示词,而且他不想让这段流程留在主对话的上下文里。抽成 skill 还是造成岗?依据说一条。
- 同事的岗派发总不合预期,他把完整的操作步骤全写进了 description,正文反而没话。哪个读者被喂饱了,哪个读者饿着?
- 你把岗文件放进了 ~/.claude/agents/,宿主不报错,agent 列表里也没有它。五步检查单里,你先查哪一步?
三道都答对了,这一课才算真的带走。
第 12 课结尾,番外什么时候开留了个活口。今天填上:番外就是这一篇,当时挂的四个模块(固定岗何时造、字段的三读者、system prompt 工艺、trigger-tester 装置)一篇之内都交了。主线十二课,加番外一篇,系列到此全部收官。
如果这篇文章对你有帮助,欢迎转发给需要的同事和朋友。