Dayward AI
Week 3 · D16About 4 hours

Loading Skills: Scanning, Progressive Disclosure, and Trigger Judgment — Bringing Experience to the Table on Demand

Turn reusable experience into skill packs: implement a skill loader that reads only a summary at startup, reads the full body only when a trigger condition matches, and runs an attached script only when needed — and use real numbers to show exactly how much context this progressive disclosure saves.

Today's goals 0/3

Sign in to tick these off and save your progress.

Today's Goals

  1. Implement a skill scanner and metadata parser, and explain the directory convention and validation points
  2. Implement the three stages of progressive disclosure, and quantify its benefit in token count
  3. Design trigger judgment that avoids a skill firing too often or not firing when it should

Yesterday you plugged in someone else's tools. Today you plug in someone else's experience. When you finish, scroll back up and tick the three goals off.

Plain-Language Walkthrough

A craft card the old hand wrote, read only when needed

Every workshop has an old hand carrying rules nobody else knows: this machine idles two minutes after you switch it on, that material needs a slow feed. He is retiring, so he writes them on cards, one job per card, pinned to the wall. The new hire does not memorize the stack, and should not — it is enough that when he hits that particular job he knows there is a card about it, walks over and reads it.

For fifteen days we gave mca capability: read files, edit files, run commands, use other people's tools. Today we give it experience: why division in this repo must throw when the divisor is zero, how the tests are run here, which step comes first before a release. A model cannot know these. They are not general knowledge; they are what your team settled on after an argument one afternoon.

So why not paste them into the system prompt? Because the wall fills up. Thirty craft cards is normal for one team, a thousand words each, thirty thousand words carried on every single request — even when the user only asked what a function does. Money is one half of it. Attention is the worse half: with thirty unrelated cards spread in front of it, the model is measurably less sensitive to the one that matters.

So the real problem today is not "how do I load a document" but how do I load it only when needed, while still letting the model know the card exists. Those two look contradictory — how do you know it exists without loading it — and resolving that is exactly what progressive disclosure is for.

Skills versus tools: one supplies capability, the other supplies process

A tool is capability; a skill is process. A tool lets the Agent do what it otherwise could not — without run_command it cannot run the tests, and no amount of knowing would help. A skill makes it do correctly what it already can do: of course it can run tests, but not which command this repo uses, which output line to read first, or what counts as "did not actually run."

That division shows up cleanly in the code: not one line written today touches the tool registry. Yesterday MCP pushed five remote tools into it; today we push zero. A skill occupies no slot in the tool list, and the model never "calls" one — it merely finds, at the right moment, that a piece of text has appeared in front of it.

The upside: skills have no practical ceiling, tools do. The tool table goes out with every request, and each extra tool parks another schema in the prompt prefix; past a few dozen, the odds of picking the wrong tool rise visibly. A skill's resident cost is one line of description, and the body is never resident at all.

The price: a skill cannot be guaranteed to run. A tool call has definite semantics — the model emits a call, we execute it, bad arguments get handed back. A skill is only text; the model can read it and not follow it, and you have no mechanism that stops that. A rule that must hold should never live only in a skill: in mca, "snapshot before writing a file" is in the code (D14), not on a craft card. Craft cards govern judgment; code governs discipline.

Against yesterday: MCP solves "the tools live somewhere else," skills solve "the experience lives somewhere else." The first needs wiring, a protocol and a trust boundary; the second needs text and loading rules. The three-way comparison is in the Skills course, day six; this course does not repeat it.

Directory conventions and metadata validation

The shape of a skill pack is simple: a directory, a SKILL.md inside it, and optionally a few attachments.

TextText
skills/
  calc-conventions/
    SKILL.md
  release-notes/
    SKILL.md
    scripts/bump-version.mjs

SKILL.md opens with YAML frontmatter. The specification requires exactly two fields: name and description. This lab adds four extensions of this implementation (domain, priority, triggers, paths). They are not in the specification — only our loader knows them — and unless that is said out loud you will assume they are standard and be baffled by the next client.

TextText
---
name: calc-conventions
description: "Numeric conventions for the calc module. Follow it when editing src/calc.js or discussing divide-by-zero behavior."
domain: calc
priority: 20
triggers: [divide, division by zero, divisor is 0, zero divisor]
paths:
  - src/calc.js
  - test/calc.test.js
---

The first decision in parsing that header is not to pull in a YAML library. Beyond the zero-dependency rule, full YAML is far more expressive than skill metadata needs, and the surplus is all risk — anchors, merge keys, and the famous implicit conversions (the string no becoming boolean false), each producing "the same SKILL.md behaves differently in two clients." So we implement a stated subset of four shapes: bare scalar, quoted scalar, inline array, indented multi-line array. One hard line: a line we do not understand is an error, never a guess. Guessing means the author got it wrong and nobody told him, so the skill quietly never fires — the hardest failure in a skill system to track down.

The second decision hides in a line that looks like one line: how you split the header from the body. The intuitive version splits on --- and takes the middle piece, and it is wrong — a horizontal rule is legal Markdown, and any document of length has one. Splitting on the delimiter shreds the body, presenting as "half the skill text mysteriously vanished," while small files look fine. Scan by line instead: the first line must be ---, then find the first --- after it standing alone on its own line.

src/skills/frontmatter.ts
export function splitFrontmatter(text: string): { head: string; body: string } {
  const lines = text.replace(/^/, '').split('\n')
  if ((lines[0] ?? '').trim() !== FENCE) {
    throw new FrontmatterError('the first line of the file must be ---')
  }
  // From line two, find the first --- standing alone. Splitting on the
  // delimiter would treat a horizontal rule in the body as a boundary too.
  const end = lines.findIndex((line, index) => index > 0 && line.trim() === FENCE)
  if (end === -1) throw new FrontmatterError('the frontmatter has no closing --- line')
  return {
    head: lines.slice(1, end).join('\n'),
    body: lines.slice(end + 1).join('\n').replace(/^\n+/, ''),
  }
}

The validation layer's rule is skip it and say why — neither silent skipping nor throwing all the way out. The first leaves the author believing his pack works; the second lets one broken pack stop mca starting at all. This is yesterday's discipline about an offline server restated: when one part breaks, the worst outcome should be one missing part. The lab ships a pack with no description; at startup it is skipped, and the log names the directory, the missing field, and why it cannot be omitted.

Three-stage loading

Back to the contradiction. Progressive disclosure answers it by splitting "knowing it exists" from "knowing what it says," then adding a third thing.

nothing matched some matched Scan the skills directory at startup Stage one resident summaryone line per skill name plus purpose What did this sentence match Stage two injects 0 characters Stage two full text on demandloaded by score through two gates Stage three attachments on demanda command line only never the script The model decides whether to run itrunning it is a tool call
Mermaid source
mermaidmermaid
flowchart TD
  A[Scan the skills directory at startup] --> B[Stage one resident summary<br/>one line per skill name plus purpose]
  B --> C{What did this sentence match}
  C -->|nothing matched| D[Stage two injects 0 characters]
  C -->|some matched| E[Stage two full text on demand<br/>loaded by score through two gates]
  E --> F[Stage three attachments on demand<br/>a command line only never the script]
  F --> G[The model decides whether to run it<br/>running it is a tool call]

The three stages have different criteria, and that is the point worth memorizing, because the criterion decides the shape of the cost:

StageCriterionShape of the cost
One, resident summarywhat is installed on this machinepaid every request, regardless of what the user said
Two, full text on demandwhat this sentence matchedpaid per turn; nothing matched means zero
Three, attachment on demandthe model decides it needs itnot an injection at all, but a tool call

Stage one is one line per skill, its description. That line is the entire basis on which the model judges whether to use the skill, so it should say "when to use me," not "what I am" — how to write one is covered in the Skills course, day two; here we only consume it. The summaries append to the system prompt: their criterion is independent of what the user said, which makes them inherently resident.

Stage two injects the matching bodies as one message. Two details: the role is user, not system — this is material for this turn, not a standing rule, and as system it would be weighted like a permanent instruction in every later turn (the same call as day eight's reference injection); and it goes ahead of the reference injection, because the relationship is "rules first, then material" — by the time the model reads src/calc.js, the convention that division by zero must throw should already be in front of it.

Stage three is attachments. The release-notes skill ships a version-bumping script, and when we inject its body we give one command line plus one sentence of explanation, and read not a character of the script. That is the definition: a script is there to be run, not read aloud to the model. Three hundred lines poured into the context invites the model to understand it and write its own version, far worse than running it. And give a directly runnable command line, not a path — given a path the model reads the file first, and stage three collapses back into stage two.

One misreading deserves clearing up: progressive disclosure saves context, not disk IO. The scan does read every SKILL.md in full, since reaching the frontmatter means reading the file head anyway. Reading a local file is microseconds; putting that text into every request is what you pay for by the token and what eats the window. Keeping the two apart stops you writing pointless optimizations like lazy file handles in the name of saving tokens.

Trigger judgment: the two kinds of mistake do not cost the same

Judgment uses three conditions, ordered by trust: an explicit mention by the user (a hundred points), a path match (ten each), a keyword match (three each). The score only ranks and breaks ties, and the priority field never takes part in deciding whether something matched at all — let it in and a high-priority skill shows up in a sentence with nothing to do with it.

What deserves real thought is the mistakes. Over-triggering and missing a trigger do not cost the same. Over-triggering spends a little context and is visible — the injection ledger shows an extra line and you know at once. A missing trigger means the model answers from general knowledge, answers plausibly, and nothing hints that a convention went unread; you find out at code review, when division by zero once again returns infinity.

So the default posture is "better one card too many," with the brake in the budget layer rather than the judgment layer. This runs against instinct: most people write the triggers tightly, the skills never fire, and the conclusion becomes "this whole mechanism is useless."

One trap catches almost everybody: triggers in a whitespace-separated language must match on word boundaries. Without them, a trigger written test is matched by latest and contest — the single largest source of false positives. Scripts written without spaces between words can only match as substrings, having no word boundary to appeal to. This must not be left to the skill author, who is thinking about what his skill is for, not about whether that string hides inside other words.

src/skills/trigger.ts
export function hasWord(text: string, word: string): boolean {
  const target = word.trim()
  if (target === '') return false
  const haystack = text.toLowerCase()
  const needle = target.toLowerCase()
  // Anything non-ASCII matches as a substring: scripts written without
  // spaces between words give us no word boundary to appeal to.
  if (/[^\x20-\x7e]/.test(target)) return haystack.includes(needle)
  // A boundary here only means "neither side is alphanumeric", so a
  // multi-word trigger such as node --test still matches.
  const escaped = needle.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
  return new RegExp(`(^|[^a-z0-9])${escaped}($|[^a-z0-9])`).test(haystack)
}

After judging, print why each skill matched. Not decoration: when a trigger is missed, those lines are the user's only evidence for whether the trigger words were too narrow, the paths wrong, or the skill never installed at all.

Skills conflict: what to do when two match at once

The lab ships two contradictory calc conventions: the current one says divide by zero must throw, the archived one says it returns infinity. Their triggers overlap, so "fix the divide-by-zero problem" matches both.

Load both and the result is not that the model knows more; it picks one at random and you cannot tell which. A skill conflict is contradiction, not duplication, so both gates are mandatory:

Gate one, domain exclusion. Make the author declare a domain, and load only the highest-scoring skill per domain. Far more reliable than hoping the model reconciles two contradictory rule sets — it cannot, it will just choose one.

Gate two, a total character budget. What does not fit does not load, and the reason must be stated: silent dropping makes people believe the skill is written wrong when the budget was too small, and those two problems have opposite fixes.

src/skills/inject.ts
for (const hit of hits) {
  // Gate one: one skill per domain, the highest score. Two contradictory
  // conventions on the table only make the model pick one at random.
  const winner = domains.get(hit.skill.domain)
  if (winner) {
    decisions.push({ hit, loaded: false, why: `same ${hit.skill.domain} domain as ${winner}, which won` })
    continue
  }
  const block = await renderSkill(hit.skill)
  // Gate two: the budget. Say why something was left out, or it gets
  // mistaken for a broken skill.
  if (used + block.length > budget) {
    decisions.push({ hit, loaded: false, why: `body is ${block.length} chars, only ${budget - used} left` })
    continue
  }
  domains.set(hit.skill.domain, hit.skill.id)
  used += block.length
  blocks.push(block)
  decisions.push({ hit, loaded: true, why: `expanded, ${block.length} chars` })
}

How much it saves: three loading strategies side by side

A mechanism that saves nothing is not worth keeping, and you only see that by putting the numbers side by side. The figures below come from this lab's self-test, all four skill packs in the repository, reproducible offline. One caveat: these tokens are estimates, from the default coefficients of day twelve — today runs entirely offline, so there is no real usage to calibrate against. Two runs on one machine give identical results, but the number equals no model's actual tokenization. Exact counting and calibration were covered that day; here we only borrow its estimator.

Loading strategyTokens due this turnAgainst full loading
Full loading, all four bodies in the system prompt at once1399baseline
Progressive, one sentence matching two skills1050 (177 resident plus 873 this turn)saves 24.9%
Progressive, this sentence matching nothing177 (resident summaries only)saves 87.3%

Two conclusions hide in that table.

First, the bulk of the benefit lands on the turns that match nothing. In a real session most turns have nothing to do with any craft card — the user asks what a function does, or has a log read. Those save 87%, while the matching turn saves 25%. Progressive disclosure does not make every turn cheaper; it makes the irrelevant turns free.

Second, this ledger improves as the skill count grows. Full loading grows linearly with it. Stage one also grows linearly, with a coefficient an order of magnitude smaller — one line of description against a page of body. Stage two depends only on how many skills this sentence matched, not on the total. Four packs save 25%; with thirty, the matching turn still loads two or three. That is where this design earns its keep.

Back to the division we started with: when the lab finishes, the tool list is eight entries from start to end, not one more, and the code the model produced matches the team's convention to the character. That is the difference between experience and capability.

Source Reading

Hands-On Lab

🧪 D16 lab: a skill loader with progressive disclosure and trigger judgment

Code location: labs/my-coding-agent-21days/day-16-skill-loader

The lab ships five skill packs: two contradictory calc conventions, a testing handbook, a release procedure with an attached script, and one deliberately broken pack missing its description. All five exercises are "the intuitive version works perfectly on small data" traps: splitting frontmatter on the delimiter, loading a pack with a missing field, reading the script body into the context, judging on keywords with no word boundaries, and loading every skill that matched. The starter passes seven of fourteen unmodified.

  1. Rewrite the frontmatter split to scan by line and watch the first check turn green — it was failing on a Markdown horizontal rule in the body.
  2. Add the four metadata validations and watch the broken pack get skipped, with the missing field named in the log.
  3. Change attachments from "read the body" to "one command line plus one sentence" and watch the script's function names disappear from the injected text.
  4. Add path conditions and word boundaries to the judgment, then see the path reason appear among the hits and test stop matching latest.
  5. Add the domain and budget gates, run MOCK=1 SELFTEST=1 pnpm start to see 14/14 passed, then use the README's pipe commands to compare what the three loading strategies each cost.

Acceptance is five ticks: the self-test prints 14/14 passed; the broken pack is skipped with the missing field named; only the higher-scoring of the two contradictory calc conventions reaches the table; the attachment contributes a command line while its body never enters the context; and a sentence matching nothing injects zero characters at stage two.

Interview Questions

Today's three questions test implementation judgment about progressive disclosure and where a skill's boundary lies, not "what are Skills":

  1. How many stages does progressive disclosure have? What is each stage's criterion?
  2. Where is the boundary between a skill and a tool? How do you choose for the same job?
  3. How do you design a skill's trigger conditions? Which is harder to diagnose, over-triggering or missing a trigger?

Full prompts, analyses and key points are in this course's day-sixteen question bank. Question three discriminates most — most answer "write accurate trigger words," and few can say that the two kinds of mistake cost differently, so the default posture should be one card too many, and explain why.

Checklist and Tomorrow

  • I can state the division between skills and tools, and why no line of today's code touches the tool registry
  • I can state the price that a skill cannot be guaranteed to run, and which rules must not live only in a skill
  • I can explain why we skip a full YAML library, and what our subset's hard line is
  • I can say why splitting frontmatter on the delimiter always breaks, and why only long documents expose it
  • I can state each stage's criterion, and how it decides the shape of the cost
  • I can explain that progressive disclosure saves context, not disk IO
  • I can say why the stage-two injection uses the user role rather than system
  • I can explain why stage three gives a command line instead of only a path
  • I can say why over-triggering and missing a trigger cost differently, and where the brake belongs
  • I can state that ASCII triggers need word boundaries while unspaced scripts only match substrings, and why that is not the author's choice
  • I can explain that a skill conflict is contradiction rather than duplication, and what each gate blocks

Tomorrow is D17, "Subagents and Parallelism: Independent Context, a Tool Allowlist, Worktree Isolation, and Result Aggregation." The contrast is worth thinking about in advance: today puts a piece of text in front of the model at the right moment; tomorrow sends a whole model out to work. Both are "on demand," and the cost differs by two orders of magnitude.

Interview questions

  • How many stages does progressive disclosure for skills have, and what triggers each one?技能的渐进披露具体分几个阶段?每个阶段的判据是什么?
    Common in ChinaCommon overseasBasic#progressive-disclosure#skills

    How to reason about it · think before answering

    1. This tests whether you have implemented a loader yourself. People who only used one answer "summary first, then full text"; people who built one start from the fact that each stage has a different trigger, because the trigger determines the shape of the cost.
    2. How to break it down - describe each stage as when it happens, what it injects, and how it is billed, then point out that the three triggers are unrelated to each other.
    3. Stage one is the resident summary. Its trigger is what is installed on this machine, independent of what the user says, so it lives in the system prompt and is paid for on every request. It carries only the name and a one-line description, and that line is the model's entire basis for deciding whether the skill is relevant.
    4. Stage two is the body on demand. Its trigger is what this particular sentence matched, so it is billed per turn and costs nothing when nothing matches. The injected message should have the user role, not system - it is material for this turn, not a standing rule, and a system role would make it carry equal weight on every later turn.
    5. Stage three is attachments on demand. Its trigger is the model deciding to use one, so it is not an injection at all but a tool call. The body should list a directly runnable command line plus one sentence of explanation and never the script's contents - given only a path, the model will read the script first and stage three collapses back into stage two.
    6. Easy to get wrong - progressive disclosure saves context, not disk IO. Scanning actually reads the whole file, because reaching the frontmatter means reading the head of it; what is saved is the cost of shipping that text with every request.
    7. Likely follow-ups - which turns carry most of the savings; how the arithmetic changes as skill count grows; whether the stage-two injection should persist in conversation history.

    分析过程 · 先想清楚再作答

    1. 这题在考「你是不是自己实现过一个加载器」。只用过的人会答「先读摘要再读全文」,实现过的人会先说每个阶段的判据不同,因为判据决定了成本的形状。
    2. 怎么拆:三个阶段各说一遍「什么时候发生、注入什么、成本怎么算」,最后点一句三者的判据互不相同。
    3. 阶段一是摘要常驻:判据是「这台机器上装了什么」,与用户说什么无关,所以它拼在系统提示里、每一次请求都付钱。内容只有名字加一行 description,这一行是模型判断要不要用它的全部依据。
    4. 阶段二是全文按需:判据是「这一句话命中了什么」,所以按轮计费,没命中就是零。注入的消息角色应该是 user 不是 system——它是本轮的资料不是长期规则,写成 system 会让它在后面每一轮都被当成同等权重的指令。
    5. 阶段三是附件按需:判据是「模型自己决定要用」,所以它根本不是注入而是一次工具调用。注入全文时只给能直接跑的命令行加一句说明,脚本内容一个字都不读——只给路径的话模型会先读一遍,阶段三就退化回阶段二了。
    6. 一条容易说错的:渐进披露省的是上下文不是磁盘 IO。扫描时整份文件其实都读出来了,因为要拿 frontmatter 就得读文件头;省的是「把这段文字塞进每一次请求」那部分成本。
    7. 可预期的追问:收益大头在哪些轮次上;技能数量增长时这套账怎么变;阶段二的注入要不要进会话历史。

    Key points

    • Three stages - resident summary, body on demand, attachment on demand - triggered by what is installed, what this sentence matched, and what the model decides to run
    • Stage one is one line per skill in the system prompt, paid on every request; it is the model's entire basis for judging relevance
    • Stage two is billed per turn and costs nothing when nothing matches; inject it as a user message, not a system one
    • Stage three is a tool call rather than an injection - hand over a command line, never the script body
    • What is saved is context, not disk IO; most of the saving comes from turns that match nothing at all

    答题要点

    • 三个阶段:摘要常驻、全文按需、附件按需,判据分别是「装了什么」「这句命中了什么」「模型决定要用」
    • 阶段一每技能一行,拼进系统提示,每次请求都付;它是模型判断相关性的全部依据
    • 阶段二按轮计费,没命中就是零;注入用 user 角色而不是 system
    • 阶段三不是注入而是一次工具调用,只给命令行不给脚本内容
    • 省的是上下文不是磁盘 IO;收益大头在「这一轮什么也没命中」的那些轮次上
  • Where is the line between a skill and a tool, and how do you decide which one a given capability should be?技能和工具的边界在哪?同一件事你怎么决定做成技能还是做成工具?
    Common in ChinaCommon overseasIntermediate#skills-vs-tools#agent-design

    How to reason about it · think before answering

    1. This is an architecture judgment question. Saying "a skill is a document and a tool is a function" covers the form but not the cost; candidates who can articulate that a skill cannot be guaranteed to execute have clearly made the trade-off in practice.
    2. How to break it down - give one criterion, then one benefit and one cost for each, then a counter-example that must be a tool.
    3. The criterion in one line - a tool is a capability, a skill is a procedure. A tool lets the agent do something it otherwise cannot (without a shell tool it simply cannot run tests); a skill lets it do correctly what it already can (it knows how to run tests, but not which command this repo uses or which line of output to read first).
    4. The benefit of a skill is that it does not occupy a slot in the tool list. The tool table ships with every request, and past a few dozen tools the odds of picking the wrong one rise noticeably. A skill's resident cost is one line of description and its body is not resident at all, so skill count is practically unbounded.
    5. The cost of a skill is that execution is not guaranteed. A tool call has defined semantics - the model emits it, we really run it, and bad arguments are pushed back. A skill is just text; the model may read it and not comply, and nothing you own can stop that.
    6. So the counter-example is clear - a rule that must be enforced should not live only in a skill. Snapshot before writing, approval before dangerous commands, truncation of oversized results: all of those belong in code. The card governs judgment, the code governs discipline.
    7. Worth mentioning a middle case - executable scripts bundled with a skill. Formally they are attachments of the skill, but running one goes through the tool-call path, so the skill carries "when to run it" while the tool carries "running it".
    8. Likely follow-ups - what wins when a skill contradicts the system prompt; whether skills deserve a mandatory flag; whether skill description text counts as untrusted input.

    分析过程 · 先想清楚再作答

    1. 这题在考架构判断。答「技能是文档、工具是函数」只说到了形式,说不到代价;能说出「技能不能保证被执行」的人明显是做过取舍的。
    2. 怎么拆:先给一句判据,再各说一条好处与一条代价,最后给一个必须做成工具的反例。
    3. 判据一句话:工具是能力,技能是流程。工具让 Agent 做到它本来做不到的事(没有跑命令的工具它就是跑不了测试),技能让它把已经能做的事做对(它会跑测试,但不知道这个仓库跑哪条命令、输出先看哪一行)。
    4. 技能的好处是不占工具清单的位置:工具表要随每次请求发出去,几十个工具之后选错工具的概率会明显上升;技能的常驻成本只有一行描述,全文根本不常驻,所以技能数量几乎没有上限。
    5. 技能的代价是不能保证被执行:工具调用有确定语义,模型发出调用就真的会执行、参数不对还会被挡回来;技能只是一段文字,模型可以读了不照做,而你没有任何机制拦得住。
    6. 所以反例很清楚:一条必须被执行的纪律不该只写成技能。写文件前拍快照、危险命令要审批、结果超长要截断,这些都得写进代码。手艺卡管的是判断,代码管的是纪律。
    7. 还有一类中间态值得提:技能带的可执行脚本。它形式上是技能的附件,实际执行时走的是工具调用那条路,等于用技能承载「什么时候该跑」,用工具承载「跑起来」。
    8. 可预期的追问:技能里写的规矩和系统提示里写的规矩冲突了听谁的;要不要给技能加一个「强制执行」标记;技能的描述文本算不算不可信输入。

    Key points

    • A tool is a capability and a skill is a procedure - one enables what was impossible, the other makes the already-possible correct
    • Skills take no slot in the tool list and scale almost without limit; the tool table ships on every request and a bloated one causes mis-selection
    • The cost of a skill is that execution is not guaranteed - the model may read it and ignore it, and nothing stops that
    • Rules that must be enforced belong in code - snapshots, approval gates and truncation should never be skills alone
    • Bundled executable scripts are the middle case - the skill says when to run, the tool call does the running

    答题要点

    • 工具是能力、技能是流程:前者让它做到本来做不到的事,后者让它把已经能做的事做对
    • 技能不占工具清单的位置,数量几乎没有上限;工具表随每次请求发出,多了会让模型选错
    • 技能的代价是不能保证被执行,模型可以读了不照做,没有任何机制拦得住
    • 必须被执行的纪律要写进代码:拍快照、审批、截断都不该只写成技能
    • 技能附带的可执行脚本是中间态:技能管「什么时候跑」,工具调用管「跑起来」
  • How would you design skill trigger conditions, and which is harder to diagnose - over-triggering or under-triggering?技能的触发条件怎么设计?过度触发和漏触发哪个更难查?
    Common in ChinaCommon overseasDeep dive#trigger-design#false-positives

    How to reason about it · think before answering

    1. The crux is "which is harder to diagnose". Most people answer "write precise trigger words", which dodges the trade-off. The real answer is that the two failure modes have asymmetric costs, so the default stance should itself lean one way.
    2. How to break it down - answer the harder one first, derive the design stance from it, and only then discuss condition types and implementation traps.
    3. Under-triggering is harder, and not by a small margin. Over-triggering costs a little context and is visible - the injection report gains a line and you know immediately. Under-triggering means the model answers from generic knowledge, plausibly, with nothing anywhere hinting that a convention went unread; you find out at code review when it once again returns infinity on divide-by-zero.
    4. That yields the stance - be generous at the matching layer and put the real brake in the budget layer. This is the opposite of most people's instinct, which is to tighten trigger words until the skill never fires, ending in the conclusion that the whole mechanism is useless.
    5. Condition types rank by trustworthiness - an explicit mention by the user is the most reliable, a path match is next and rarely false-positives, and keywords are the most error-prone. Scores should only order and break ties; an author-declared priority must never decide whether something matched, or a high-priority skill will surface in a completely unrelated sentence.
    6. One implementation trap catches everyone - ASCII trigger words must match on word boundaries, or "test" fires on "latest" and "contest"; Chinese can only match as a substring because there is no whitespace tokenization. And that distinction must not be left to the skill author, who is thinking about what the skill does rather than where the word might otherwise appear.
    7. Finally observability - match reasons must be printed. When something fails to trigger, those lines are the user's only evidence for whether the word was too narrow, the path was wrong, or the skill never loaded at all. Without them the mechanism cannot be tuned.
    8. Likely follow-ups - what to do when two contradictory skills both match; whether the model should choose which to load; whether matching could be delegated to a small model call.

    分析过程 · 先想清楚再作答

    1. 这题的题眼是「哪个更难查」。多数人会答「触发词要写准」,那是在回避取舍;真正的答案是两类误判的代价不对称,所以默认姿态本身就该是偏向某一边的。
    2. 怎么拆:先答那个更难查的,再由它推出设计姿态,最后才说具体的条件类型与实现坑。
    3. 漏触发更难查,而且难得不是一个量级。过度触发的代价是花掉一点上下文,而且看得见——注入报账里会多出一行,你当场就知道它上桌了。漏触发是模型按通用知识回答,答得像模像样,而没有任何东西提示你有一份约定没被读到;你只有在代码评审时才会发现它又把除零写成了返回无穷大。
    4. 由此推出设计姿态:判定层宁可多上一张,真正的刹车放在预算层。这和多数人的直觉相反——第一反应是把触发词写严,结果技能常年不触发,最后得出「这套东西没用」的结论。
    5. 条件类型按可信度分三类:用户显式点名最可信、路径命中次之(很难误报)、关键词最容易误报。分数只用来排序与打破平局,作者声明的优先级绝不该参与「有没有命中」,否则一个高优先级技能会在完全不相干的话里上桌。
    6. 实现上有一个人人都踩的坑:英文触发词必须按词边界匹配,否则 test 会被 latest、contest 命中;中文只能按子串,因为没有空格分词。而且这条分界不能交给技能作者自己选——他写触发词时想的是「我这个技能是干嘛的」,不是「这个词会不会出现在别的句子里」。
    7. 最后是可观测性:命中原因必须打出来。漏触发时用户唯一的依据就是那几行——到底是词写窄了、路径写错了,还是这条技能压根没装上。没有这几行,这套机制就没法调。
    8. 可预期的追问:同时命中两条互相矛盾的技能怎么办;要不要让模型自己决定加载哪一条;触发判定能不能交给一次小模型调用。

    Key points

    • Under-triggering is harder - over-triggering shows up in the injection report, while a missed trigger leaves no trace and only surfaces at review
    • Hence the stance - be generous when matching and put the brake in the budget layer instead
    • Rank conditions by trustworthiness - explicit mention, path match, keyword; an author's priority only breaks ties and never decides a match
    • Match ASCII trigger words on word boundaries (otherwise "test" fires on "latest") and Chinese as substrings; this must not be left to the skill author
    • Always print match reasons, or a missed trigger gives no way to tell a narrow word from a wrong path from a skill that never loaded

    答题要点

    • 漏触发更难查:过度触发在注入报账里看得见,漏触发没有任何提示,只能在评审时发现
    • 由此定姿态:判定层宁可多上一张,刹车放在预算层而不是判定层
    • 三类条件按可信度排:显式点名、路径命中、关键词;作者声明的优先级只打破平局,不决定是否命中
    • 英文触发词按词边界匹配(否则 test 命中 latest),中文按子串;这条不能交给技能作者自己选
    • 命中原因必须打出来,否则漏触发时无从判断是词写窄了、路径写错了还是技能没装上

Comments