字节跳动二面,AI 工程化岗位。
学员进去之前信心满满——自己写过五六个 Claude Code Skill,GitHub 上还开源了一个下载量还不错的,简历上写的是"精通 Agent Skills 的设计与开发"。
前面聊架构聊得挺好。面试官话锋一转:"你写过不少 Skill,那你说说——你遇到的触发问题最多的是什么?"
学员说:"嗯……有些时候它该触发不触发,有些时候不该触发它瞎触发,调 description 能改善,但也不是每次都灵。"
面试官点点头,没接话。打开笔记本电脑,调出自己的 Claude Code,现场写了一个极其简单的 Skill,装进去,然后打了一行字:
"请帮我整理一下这个项目的 README。"
然后抬起头,把屏幕转过来:"你看,你这个 Skill 刚才又触发了。"
学员一愣。面试官写的那个 Skill 明明是一个 数据库 Schema 管理工具,和整理 README 没有半点关系——但它就是触发了。
面试官笑着往椅背上一靠:
"Description 里写的是 'Helps with project files.'——你猜 Claude 觉得 '整理 README' 算不算 project files?它当然算。你一个数据库 Schema 工具,description 写这么宽,Claude 不在无关请求里反复触发它才怪。你写过那么多 Skill,这个问题从来没发现过?"
学员说那一刻他后背是真出汗了。自己写了那么多个 Skill,每次遇到误触发就微调一下 description 碰运气,从来没有系统地想过——Claude 到底是怎么判断"该不该触发这个 Skill"的?什么情况下它会误触发?什么情况下它该触发却不触发?
面试官接着追了三个问题,一个比一个刁:
"第一个问题:undertriggering 和 overtriggering,你分别是怎么解决的?说具体方法,别说'调 description'这种空话。"
"第二个问题:你的 Skill 和另一个 Skill 能力重叠的时候,Claude 怎么选?你凭什么确保选的是你那个?"
"第三个问题:如果你的 Skill 在某个特定场景下永远不该触发,你怎么让 Claude 记住这个规则?写在 description 里有用吗?"
三个问题,学员越答声音越小。这场面试后面虽然还在继续,但他说他心里已经知道结果了。回来之后他把这三个问题原封不动甩给我,说:"哥,这几个问题,我是真不会。你得好好给我讲讲。"
今天这篇,就是我对这三个问题的完整拆解。
一、第一个误区:Description 不是"功能描述",是"触发指令"
先搞清楚根上的问题。
大部分人写 description 的思路是:"我这个 Skill 是干什么的"——比如"Extract tables from PDF files"、"Manage database schemas"、"Analyze design files"。这是一种功能描述思维,好像 description 是写给用户看的说明书。
但 description 不是写给用户看的,是写给 Claude 的触发指令。
Claude 拿到用户请求后,会在上下文里扫描所有 Skill 的 description,做一次语义匹配。"这个用户的请求,跟这个 description 描述的场景,是不是同一件事?"
所以你写 "Helps with project files",Claude 的理解是:只要用户提到了任何和 project files 沾边的事,就触发这个 Skill。"整理 README"是不是 project files?是。那就触发。"修复 package.json"是不是?也是。那就继续触发。你的 Skill 就这么在一个完全无关的请求里被反复激活,白占了上下文。
正确的写法应该是:Description = 做什么 + 何时触发 + 何时不触发。
一句话说清楚"我这个 Skill 处理哪一类请求、不处理哪一类请求",让 Claude 既有"触发信号"也有"抑制信号"。
❌ 坏写法 — 太宽,Claude 在无关场景也会触发
description: Helps with project files.
✅ 好写法 — 明确触发条件 + 排除条件
description: Manages database schema migrations and versioning. Use when the user asks to create, alter, or rollback database tables. Not for general file management or documentation tasks (use docs Skill instead).
有了这个基础认知,我们再来看三个具体问题。
二、Undertriggering 和 Overtriggering,分别怎么修
这是面试官的第一问,也是最核心的一问。
Undertriggering(欠触发):该触发的时候不触发。用户提了一个应该由你这个 Skill 处理的请求,Claude 没选它。
Overtriggering(过触发):不该触发的时候瞎触发。跟你的 Skill 八竿子打不着的请求,Claude 给激活了。
两种问题看似相反,但本质原因一样——description 和用户真实请求之间的语义边界没划清楚。
先看 undertriggering 的修法。欠触发最常见的原因是:description 里用了太抽象的词,Claude 匹配不上。
举个例子。你写了一个代码审查 Skill,description 是 "Performs comprehensive code review with security analysis." 用户说"帮我看看这个 PR 有没有 SQL 注入风险",Claude 可能就没触发——因为用户的话里出现了"PR"、"安全"、"注入"这些具体词,而你的 description 里只有"code review"和"security analysis"。匹配度不够,Claude 判断"不够相关",就不触发。
修法:把你期望用户会说出口的真实短语,直接写进 description。
不要只写抽象的"code review",要写用户实际会说的话:
✅ 修复后的 description
description: Reviews pull requests for security vulnerabilities, code quality, and best practices. Use when the user asks to "review this PR", "check for injection risks", "audit this code", "find security issues", or "review the diff".
把关键词从"代码审查"扩成用户真实会说的场景列表,命中率会大幅提升。
再看 overtriggering 的修法。过触发的核心原因是:description 里的词覆盖面太广,和其他请求产生了意外重叠。
前面那个 "Helps with project files" 就是典型。还有一个常见案例:写了一个"API 文档生成"的 Skill,description 里写了 "Generates API documentation"。结果用户说"帮我看一下这个 API 的返回格式",也在触发——因为"API"这个关键词重叠了。但用户只是"看一下",并不需要"生成文档"。
修法:加负面条件(Negative Conditions)——在 description 里明确写什么时候不该触发。
✅ 加负面条件的 description
description: Generates complete API documentation from code comments and route definitions. Use when the user requests to "generate docs", "document the API", "create API reference", or "write OpenAPI spec". Not for: quick looks at API format, debugging a single endpoint, or general code review. For those cases, use the api-debug Skill instead.
"Not for" 直接告诉 Claude 边界在哪。还给了一个具体的替代 Skill 名——这就更进一步了。Claude 读到 "Not for quick looks at API format" 的时候,这条抑制信号会降低匹配分数,使它不容易误触发。
面试官特别认可这个"负面条件 + 替代 Skill"的组合写法。他说他只在一两个人的简历里见过。
三、两个 Skill 能力重叠,Claude 怎么选
面试官的第二问,也是暴露真实经验的一问。
大部分面试者会回答"把 description 写得清楚一点就行"——听起来对,但等于没说。面试官要的是具体到方法论层面的答案。
真实情况是:当两个 Skill 的 description 语义重叠到一定程度时,Claude 的选择是不确定的。即使你写得再好,它也可能选错。你怎么应对这个不确定性?
这里有三种策略,由浅到深:
策略一:在 description 里直接声明优先级
最直接的办法。两个 Skill 能力重叠时,在其中一个的 description 里写明"此类请求优先使用另一个 Skill"。
比如你有一个"数据可视化" Skill 和一个"报表生成" Skill,两者都能处理图表请求。那你可以在"数据可视化"的 description 里写:
description: Creates interactive data visualizations and charts. Use when the user asks for "charts", "plots", "graphs", "dashboards". For printable report layouts with tables and formatting, prefer "report-gen" Skill.
这不是"把问题推给另一个 Skill",而是给 Claude 一个清晰的决策分支——"请求 A 归我,请求 B 归它"。Claude 的多工具决策逻辑里,描述中提到的"另一条路径"会被当成约束条件来处理。
策略二:合并——不要嫌一个 Skill 做的事多
很多人有一个执念:Skill 要"单一职责",一个 Skill 只做一件事。但太细的拆分必然导致重叠。当两个 Skill 经常在同一个 session 里被同时触发,或者用户搞不清该用哪个时,最务实的做法就是合二为一。
你会发现 Anthropic 官方仓库里的很多 Skill,scope 并不小——一个 web-development Skill 涵盖了前端、后端、部署、配置。这不是他们不懂"单一职责",而是他们知道重叠带来的不确定性比一个稍大的 Skill 更消耗 token——两个 Skill 反复误触来回拉锯,上下文浪费远比多加载一份 SKILL.md 正文更多。
判断要不要合并的标准:如果你在三次对话中,有两次需要同时用到两个 Skill,或者每次都要手动纠正 Claude 选错了——那就合并。反之,如果两个 Skill 的触发场景在 80% 的情况下是互斥的,就保持分离。
策略三:在 SKILL.md 正文里放"排他性指令"
如果前两个策略都不够,还有最后一招:在 SKILL.md 正文里写明确的排他指令。
因为 description 只有 1024 字符的限制,有些复杂场景的边界说不清楚。但 SKILL.md 正文空间大得多——等正文加载进来后,你可以明确地写:
## 排他规则 当同时触发了本 Skill 和 "data-pipeline" Skill 时: - 如果用户明确提到了 "ETL"、"管道"、"数据同步"——优先由 data-pipeline 处理 - 如果用户说的是 "可视化"、"图表"、"看板"——优先由本 Skill 处理 - 如果用户没有明确指向——先询问用户倾向哪个
正文被加载后,Claude 就能看到这条排他规则,并在后续推理中遵守它。这比在 description 里挤字符要灵活得多。
面试官听到"排他性指令"的时候眉毛挑了一下——他没想到学员能答到这个深度。当然,这是回来复盘之后学员才想出来的答案,当时他一个都没说出来。
四、"在某个场景下永远不该触发"——负面条件到底有没有用
面试官第三问,也是很多人心里没底的一问。
人直觉上觉得"我在 description 里写了 Not for X,Claude 肯定就能记住吧?"——但 Claude 对 description 的处理不是硬编码的规则匹配,是语义匹配。你再怎么强调"不要用于 X",在那个请求的语义和你的 description 高度相似的时候,它仍然可能触发。
举个例子。你写了一个"代码优化" Skill,description 里写了一大串负面条件:"Not for refactoring, not for formatting, not for fixing lint errors." 结果用户说"帮我 format 一下这个文件",你的 Skill 还是触发了——因为在 Claude 看来,"optimize"、"code" 这些正面信号的权重,可能大于"Not for formatting"这个负面信号。
那负面条件到底有没有用?
答案是有用,但不是有 100% 的效果。它是一个概率性的抑制信号——它能显著降低触发概率,但不能保证 100% 不触发。
那如果一定要 100% 确保某个场景下不触发呢?靠 description 做不到,要靠 Skill 本身的激活机制来兜底。
具体怎么做?一个最常用的方案是:在 SKILL.md 正文头部加一道"看门狗"检查。
## ⚠️ 前置检查 在执行任何操作之前,先判断用户的请求是否真的需要数据库 Schema 管理: - 用户的请求是否涉及 CREATE / ALTER / DROP TABLE? - 用户的请求是否涉及 migration 版本管理? - 用户的请求是否涉及字段类型变更? 如果以上问题的答案全部为"否",说明本 Skill 被误触发了。 **请立即停止执行并向用户说明被误触发的情况,建议用户使用其他 Skill。**
这样即使 description 没拦住、Skill 被误触发了,正文加载进来之后 Claude 读到的第一件事就是"确认一下你是不是被误触发了"。它会自己去检查,发现不匹配就主动退出。这是兜底机制——description 层没防住的,正文层再防一道。
面试官对这道题的期待,其实就是能答到这个"双层防护"的层次。可惜学员当时只在 description 层面绕圈子,没想过正文里还能加看门狗。
面试怎么答 Skill 的触发问题链
如果面试官把这套题甩给你,按下面这个节奏答:
第一步,先纠正"description 是功能描述"这个误区(20 秒)。
开口就说:"Description 不是写给用户看的说明书,是写给 Claude 的触发指令。它要回答的不是'这个 Skill 能干什么',而是'什么情况下 Claude 应该激活这个 Skill'。好的 description = 做什么 + 何时触发 + 何时不触发。"
第二步,区分 undertriggering 和 overtriggering 的解法(1 分钟)。
欠触发的核心是关键词不够具体——把用户真实会说出的短语列进去。过触发的核心是边界太宽——加负面条件(Not for...)+ 推荐替代 Skill。最好能补一句"可以用 'When would you use this Skill?' 问 Claude 来调试 description"。
第三步,讲 Skill 重叠时的策略(30 秒)。
三层递进:① description 里声明优先级和替代 Skill;② 频繁重叠就合并,不要硬拆;③ 正文里写排他性指令。提一句"Anthropic 的官方 Skill 很多 scope 不小,因为合比拆更稳定"。面试官听到这句会觉得你真的研究过。
第四步,讲负面条件的局限性和兜底方案(30 秒)。
"Description 里的负面条件是概率性的抑制信号,不能保证 100% 拦截。所以在 SKILL.md 头部加看门狗检查——正文加载后先让 Claude 自我校验是否被误触发,发现不对就主动退出。"
这四个点说下来,面试官基本能确认你是亲手写过 Skill、被触发问题折磨过、然后系统性解决过的人——不是只会照着官方模板写个 hello world。
写在最后
回头看字节面试官的三连问,它其实有一条很清晰的进阶路径:
先用"误触发"这个你大概率踩过的坑,探你的底层认知——description 到底是为谁写的→
再用"重叠选择"看你有没有做过多个 Skill 之间的协调设计→
最后用"永远不触发"看你知不知道软约束的边界在哪、怎么兜底。
每一问都在追问同一个东西:你到底是在"写指令",还是在"设计一个让 AI 稳定决策的系统"?
两者的区别,就是你面前摆着 Claude 这个"执行者"——写指令的人只管告诉它做什么;设计系统的人还会去想"Claude 如果理解错了怎么办"、"两个指令矛盾了它怎么选"、"有没有办法让它一定选对"。
面试官心里门儿清,三句话就能听出你是哪一种。