Hand-Building a Skill Runtime: Scanning, Frontmatter Parsing, Injecting the System Prompt, Reading the Body on Demand
Actually build three-stage progressive disclosure: scan a directory to discover skills, parse frontmatter leniently, splice names and descriptions into a directory injected into the system prompt, and read the full body only on activation.
Today's Goals
- Implement a skill scanner that handles scope priority and name collisions
- Leniently parse SKILL.md frontmatter, degrading gracefully rather than crashing on malformed input
- Inject the directory into the system prompt, and implement an activation entry point that reads the body on demand
For four days you stood on the writing side: choosing a subject, writing the description, deciding when to add a script. Today you switch roles and write the program that reads skills. Not so you can build a client, but because once you have implemented it, you can point at the line of runtime code behind every recommendation of the last four days — why the description is the only trigger surface, why the body should stay within a few hundred lines, why the name must equal the directory name, all of it going from "the spec says so" to "the code does not work otherwise." Once you have read the walkthrough and finished the lab, scroll back to the top and tick off the three goals.
Plain-Language Walkthrough
The three stages are three functions in code
On day one, at the filing cabinet, you were the new hire coming to look things up. Today you become the person who keeps the cabinet: taking stock of which folders are in it, copying the line on each cover onto an index card by the door, and pulling out the matching folder only when someone asks about that subject.
Those three jobs are progressive disclosure's three stages, and in code they map cleanly onto three functions.
Discovery: scan a few conventional directories, find every folder containing a SKILL.md, and read out each one's metadata. This runs once at session start.
Disclosure: splice every skill's name and description into a catalog and put it in the system prompt. Not one word of any body comes in. That catalog is the model's only basis for judging whether there is something worth opening.
Activation: only once the model says it wants a skill do you read that one full SKILL.md and put its body into the context.
The genuinely valuable insight hides between steps two and three: disclosure's cost is paid every turn, and activation's cost is paid once. The system prompt is resent with every request, so every extra word in the catalog gets multiplied by the number of turns. A body enters the context only on the turn it is activated and then stays as a history message without being billed again. That asymmetry explains nearly every hard constraint in the spec — why the description has a length limit and the body does not, why the description must state trigger conditions rather than usage instructions.
Scanning: find every folder containing a SKILL.md
The rule is one line: a directory containing a SKILL.md is a skill. No registry, no manifest, no compilation.
What is genuinely hard is the traversal itself. Three things must be handled.
A depth limit and a skip list. Recurse blindly in a real project and you will scan tens of thousands of directories, the overwhelming majority of them inside node_modules. Set a limit of four or five levels and keep a skip set holding out .git, node_modules, and build output. This is not an optimization but a question of usability — scanning sits on the session startup path, and two extra seconds is a cost paid every time.
Stop when you find one. Once a directory has a SKILL.md, do not drill further. Skills do not nest, and continuing only makes you try its references/ as another skill directory.
Multiple scopes and name collisions. At least two places must be scanned: project level and user level. As day two covered, the prevailing cross-client convention is project level beating user level, while Claude Code orders enterprise, personal, and project from high to low — the specific precedence follows whichever implementation you build, and what matters is fixing one and staying consistent rather than leaving it to chance.
How a collision is handled matters more than which direction it goes. The worst approach is silent discard: a user edits the skill in the project, restarts, and behavior has not changed at all, because what is in effect is the user-level copy. They will suspect the file did not save and suspect a client cache, and never think of a same-named copy somewhere else. So a shadowed skill must leave a warning, and it must print both paths — that log line is the first place to look when diagnosing this.
Parsing: how lenient should it be
SKILL.md's structure is extremely simple: a stretch of YAML between two triple-dash lines and body text for the rest. But you are parsing files other people wrote, and malformed input is the norm.
A key decision arises here: on encountering non-conforming content, do you refuse to load or load with degradation? The answer is lenient, with exactly one exception.
The only hard rejection is a missing description. Without it, this skill has no trigger surface in the discovery stage, will never be selected, and keeping it in the catalog merely wastes tokens. Skip that one outright and record an error-level diagnostic.
Everything else warns and still loads. A name that disagrees with the directory name, a name using capitals or underscores, a description over the limit — all affect quality without affecting whether the skill can be used. Your runtime is not a validator; the user installed this skill to get work done, not to pass a check.
The most common malformation is a bare colon in the YAML. A line like description: use when the user says: help me commit has a colon in the middle that makes a proper parser treat the line as invalid and throw out the entire file. The right fallback order is: try a full YAML parser first, and on failure drop back to a dumb read-by-line approach that extracts only the few scalar fields you recognize. That way a skill with mispunctuated text still works instead of disappearing entirely.
The code below is the piece of the runtime most worth reading closely: separating the frontmatter, then validating leniently by the rules above. Note that every continue means "record it and let it through."
export type Diagnostic = { level: 'warn' | 'error'; path: string; message: string }
export type Skill = { name: string; description: string; location: string; body: string }
const NAME_RE = /^[a-z0-9]+(-[a-z0-9]+)*$/
export function splitFrontmatter(raw: string): { front: string; body: string } | null {
const m = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/.exec(raw)
return m ? { front: m[1], body: m[2].trim() } : null
}
// The read-by-line fallback: used when the proper YAML parse fails, curing unquoted colons in values
function readField(front: string, key: string): string | undefined {
const m = new RegExp(`^${key}:\\s*(.+)$`, 'm').exec(front)
return m?.[1].trim().replace(/^["']|["']$/g, '')
}
export function parseSkill(location: string, dir: string, raw: string) {
const diagnostics: Diagnostic[] = []
const parts = splitFrontmatter(raw)
if (!parts)
return { skill: null, diagnostics: [{ level: 'error', path: location, message: 'no frontmatter' }] }
const name = readField(parts.front, 'name') ?? dir
const description = readField(parts.front, 'description')
if (!description) {
// The only hard rejection: no description means no trigger surface, so it could never be selected
diagnostics.push({ level: 'error', path: location, message: 'missing description, skipping' })
return { skill: null, diagnostics }
}
// The three below only warn and still load — that is lenient validation
if (name !== dir)
diagnostics.push({ level: 'warn', path: location, message: `name disagrees with the directory: ${name}` })
if (!NAME_RE.test(name))
diagnostics.push({ level: 'warn', path: location, message: `name breaks the naming rules` })
if (description.length > 1024)
diagnostics.push({ level: 'warn', path: location, message: 'description over the limit' })
return { skill: { name, description, location, body: parts.body }, diagnostics }
}import re
from dataclasses import dataclass
NAME_RE = re.compile(r"^[a-z0-9]+(-[a-z0-9]+)*$")
FM_RE = re.compile(r"^---\r?\n(.*?)\r?\n---\r?\n?(.*)$", re.S)
@dataclass
class Skill:
name: str
description: str
location: str
body: str
def split_frontmatter(raw: str):
m = FM_RE.match(raw)
return (m.group(1), m.group(2).strip()) if m else None
def read_field(front: str, key: str):
# The read-by-line fallback: used when the proper YAML parse fails, curing unquoted colons in values
m = re.search(rf"^{key}:\s*(.+)$", front, re.M)
return m.group(1).strip().strip("\"'") if m else None
def parse_skill(location: str, dirname: str, raw: str):
diagnostics: list[dict] = []
parts = split_frontmatter(raw)
if parts is None:
return None, [{"level": "error", "path": location, "message": "no frontmatter"}]
front, body = parts
name = read_field(front, "name") or dirname
description = read_field(front, "description")
if not description:
# The only hard rejection: no description means no trigger surface, so it could never be selected
diagnostics.append({"level": "error", "path": location, "message": "missing description, skipping"})
return None, diagnostics
# The three below only warn and still load — that is lenient validation
if name != dirname:
diagnostics.append(
{"level": "warn", "path": location, "message": f"name disagrees with the directory: {name}"}
)
if not NAME_RE.match(name):
diagnostics.append({"level": "warn", "path": location, "message": "name breaks the naming rules"})
if len(description) > 1024:
diagnostics.append({"level": "warn", "path": location, "message": "description over the limit"})
return Skill(name, description, location, body), diagnosticsDisclosure: the catalog is the only thing you pay for every turn
The discovery stage's product is a catalog injected into the system prompt. It looks like this:
The following skills provide specialized instructions for particular tasks.
When a task matches a description, read the file at its location before continuing.
Relative paths inside a skill body resolve against that skill's directory.
<available_skills>
<skill>
<name>commit-message</name>
<description>Use when the user is committing code or writing a commit message…</description>
<location>.agents/skills/commit-message/SKILL.md</location>
</skill>
</available_skills>Three design points.
Each entry has only a name, a description, and a location. A body entering here cancels progressive disclosure — you would pay, every turn, for the full bodies of twenty skills, nineteen of them unrelated to the current task. The location cannot be omitted: the model needs it to know which file to read, and its parent directory is the base for resolving every relative path in the body.
Omit the whole section when there are no skills. Hand the model an empty list and it will only be confused, having spent a few dozen tokens on rules it cannot use.
Compute the catalog's cost and print it in the startup log. That is a very cheap and very useful habit. You will immediately see which skill's description runs too long — one description taking half the catalog means it is most likely writing usage instructions rather than trigger conditions. A rough estimate of about four characters per token is enough; switch to a real tokenizer when precision matters.
Activation: file-reading style versus a dedicated tool
Once the model decides to use a skill, how does the body enter the context? Two routes.
File-reading style: the catalog gave a location, and the model reads it with the file-reading tool it already has. The upside is no new mechanism at all, so any agent with file-reading capability supports skills immediately; that is why this format spread across dozens of clients. The cost is that the model may read the wrong path or read only half, and you have no explicit hook for deduplication or protection.
Dedicated tool style: give the model a tool named something like "open a skill," taking a name as its argument. The upside is that activation becomes an observable, interceptable call — you can deduplicate there, run a permission check, and put the skill directory and a resource listing into the return value. The cost is one more tool definition, and it requires the host to be willing to open a dedicated path for skills.
Neither is absolutely right, and the deciding factor is whether you control the host. Writing your own agent, use the tool style and take full control; building a general implementation to drop into somebody else's client, file-reading style's compatibility is irreplaceable.
Whichever route, the injected content should be wrapped in a structured tag and should add two things inside it: the absolute path of the skill directory (telling the model where relative paths resolve) and a listing of file names under scripts/, references/, and assets/.
Living a little longer: deduplication and compaction protection
With the steps above working, a demo holds up. But two problems surface once a session runs long.
Repeated activation. The model may select the same skill a second time in one session, having forgotten it already read it. The same instructions appearing twice in the context wastes tokens and easily interferes with itself when the two copies word things slightly differently. The fix is simple: keep an activated set and return immediately on a hit rather than injecting again.
Getting compacted away. When a long session triggers compaction, early messages get replaced by a summary. If a skill body falls in the compacted range, no error is raised at all — the model simply reverts quietly to how it behaves without the skill. What the user sees is "it was fine earlier and later it stopped following the convention," which is the hardest class of problem in this mechanism to diagnose.
So the message produced by activation should carry a protected marker, and compaction should keep the whole section or at least re-inject it once. The marker itself is simple; the hard part is remembering to add it. That whole craft of long-running sessions — compaction, memory files, subagent isolation — is covered far more systematically in the sister course, day 4 of Context Engineering in 5 Days. Today all you need is: a skill body is a high-priority retention item during compaction.
Source Reading
Today's lab is a runtime you can read line by line, six files with one job each, under four hundred lines in total. Read them in this order.
parse.ts is the foundation and the one whose details most repay attention: separating the frontmatter, the read-by-line fallback, and that set of warn-but-do-not-reject checks. Look closely at the single place that returns null, which is the missing description case.
scan.ts is traversal plus collisions. Two things are easy to miss inside findSkillDirs: returning as soon as SKILL.md is found (no drilling further), and the skip set and depth limit. scanSkills advances in scope order, first in wins, and a shadowed skill is recorded in shadowed with a warn-level diagnostic added.
catalog.ts has only two functions, buildCatalog splicing the catalog and catalogCost computing each entry's cost. It is so short it hardly looks important, but it is the only cost of the three stages repeated every turn, and it is worth staring at its output for a while.
activate.ts is the activation entry point: deduplication, tag wrapping, resource listing, and stamping the compaction-protected marker. listResources walks only the three conventional directories and takes only file names.
select.ts is the runtime's only network egress. One discipline of design matters here: which skill to select is a judgment made by the model, not by the runtime. A runtime doing its own keyword matching degrades the description's semantic judgment into string hits, and day three's whole trigger-testing apparatus loses its meaning. The naive match in offline mode exists only to make the flow runnable, and whether a description is written well must be verified with a real model, with the offline switch off.
index.ts strings the three stages together for one run and prints eight self-checks at the end. Run the solution first to see all eight green, then run the starter to see how they go red — which self-checks each of the five exercise points maps to becomes obvious.
Hands-On Lab
The six skills in fixtures/ are deliberately broken: one missing a description, one whose name disagrees with its directory, one with a bare colon in its description, one with only a README, and a same-named pair to demonstrate shadowing. Look through them yourself before running, guess which will load and which will only leave warnings, then run and check your answers.
- Get the solution running and see how many skills were scanned, what the catalog looks like, and which message the body landed in after activation.
- Complete the scanning function in the starter, handling a project-level and user-level name collision and printing one shadowed warning.
- Complete the frontmatter parsing so a missing description is skipped and a name disagreeing with the directory only warns.
- Complete the catalog injection and cost estimate so a structured list of available skills appears in the system prompt.
- Complete the deduplication in the activation entry point and verify only activated bodies entered the context.
Interview Questions
Today's 3 questions are in the question bank below, weighted toward the implementation points of the runtime's three stages, lenient parsing and collision handling, and activation styles and context protection. Expand each one and read the analysis before the key points — practicing the derivation beats memorizing bullets. The high-frequency labels for the domestic and overseas markets are there so you can pick by target market.
Checklist and Tomorrow
- Implement a skill scanner that handles scope priority and name collisions
- Leniently parse SKILL.md frontmatter, degrading gracefully rather than crashing on malformed input
- Inject the directory into the system prompt, and implement an activation entry point that reads the body on demand
- Explain the asymmetry that disclosure is paid every turn while activation is paid once, and which spec constraints it explains
- All 5 acceptance criteria of the lab pass
- Answer at least 2 of the 3 interview questions without looking at the key points
Tomorrow (D6) we go from one person's folder to a team's capability pack: how a set of skills is packaged as a plugin, how a marketplace publishes it, how versions are bumped, and the table this course has owed you for five days — what function calling, MCP, and Skills each actually govern, and when to reach for which. Having now seen from the runtime's side how a skill gets loaded, that table will read much more easily tomorrow.
Interview questions
If you implemented skill support in your own agent, what happens in the discovery stage versus the activation stage, and why split them?如果让你自己给一个 Agent 实现 skill 支持,发现阶段和激活阶段各要做什么?为什么要分成两步?
Common in ChinaCommon overseasIntermediate#agent-skills#runtime#progressive-disclosureHow to reason about it · think before answering
- This tests whether progressive disclosure is a mechanism you could build, not a slogan. Repeating the three stage names is not enough; say what each stage reads and where it writes.
- Discovery: scan the conventional directories, find every folder containing SKILL.md, parse out name and description, and assemble a catalog injected into the system prompt. No body text enters here; each entry carries only name, description and location.
- Activation: once the model judges that a task matches a description, read that full SKILL.md into context, along with the skill directory path and a list of bundled resource files.
- The reason for the split is an asymmetry in cost: disclosure is paid every turn, activation is paid once. The system prompt is resent with every request, so each extra character in the catalog is multiplied by the number of turns.
- That asymmetry also explains the spec's hard limits: descriptions are capped and bodies are not, and descriptions must state trigger conditions rather than usage instructions, because the description is the part that keeps costing money.
- Expected follow-up: can the location field be dropped? No. The model needs it to know which file to read, and its parent directory is the base for every relative path in the body.
分析过程 · 先想清楚再作答
- 这题在考你有没有把渐进式加载当成一个可实现的机制,而不是一句口号。只复述「发现、激活、执行」三个词是不够的,要落到每一步读了什么、写进了哪里。
- 发现:扫描约定目录,把所有含 SKILL.md 的文件夹找出来,解析出名字与描述,拼成一份清单注入系统提示。**这一步正文一个字都不进来**,清单里只有名字、描述、位置三样。
- 激活:模型判断当前任务命中了某条描述,才去读那一份完整的 SKILL.md,把正文放进上下文,同时告诉它技能目录在哪、附带哪些资源文件。
- 分两步的理由是成本结构不对称,这是本题的核心句:**披露的成本每一轮都要付,激活的成本只付一次。** 系统提示随每次请求重发,清单每多一个字都要乘会话轮数;正文只在被激活的那一轮进上下文,之后作为历史消息留着。
- 由这条不对称性可以顺手解释规范里的硬约束:为什么描述有长度上限而正文没有,为什么描述必须写触发条件而不是使用说明——描述是每轮都在花钱的那一段。
- 可预期的追问是「位置这一项能不能省」。不能:模型要靠它知道去读哪个文件,而且它的父目录是正文里所有相对路径的解析基准。
Key points
- Discovery scans directories, parses name and description, and injects a catalog into the system prompt with no body text.
- Activation reads the full SKILL.md and adds the skill directory plus a list of bundled resource filenames.
- The split exists because disclosure is paid every turn while activation is paid once.
- That asymmetry explains why descriptions are length-capped and must state triggers rather than usage.
- The location field is required: it is both the read target and the base for relative paths.
答题要点
- 发现阶段扫描目录、解析名字与描述、拼成清单注入系统提示,正文不进来。
- 激活阶段才读完整 SKILL.md,并附上技能目录与资源文件名清单。
- 分两步的根据是披露每轮付费、激活只付一次这条不对称性。
- 这条不对称性解释了描述为什么有长度上限、为什么要写触发条件而不是使用说明。
- 清单里位置字段不能省,它既是读取目标也是相对路径的解析基准。
When your runtime parses a SKILL.md that violates the spec, do you refuse to load it or degrade gracefully? And how do you handle a name collision across scopes?你的运行时解析到一份不合规范的 SKILL.md,是拒绝加载还是降级加载?另外,两个作用域里有同名 skill 时你怎么处理?
Common in ChinaCommon overseasIntermediate#agent-skills#runtime#error-handlingHow to reason about it · think before answering
- Both halves share one stance: a runtime exists to get work done, not to validate. State that first.
- For loose loading, give a decidable boundary. The only hard rejection is a missing description: without it the skill has no trigger surface, can never be selected, and only wastes catalog tokens.
- Everything else warns and still loads: a name that differs from the directory, a name using capitals or underscores, an over-long description. These hurt quality but not usability.
- Cite the most common malformation as evidence: an unquoted colon inside a YAML value makes a strict parser reject the whole file. The right fallback order is full YAML parsing first, then a line-wise field reader that extracts only the scalar fields you know.
- For collisions, the direction matters less than the handling. The cross-client convention is project over user, while Claude Code orders enterprise, personal, then project. Both are defensible; pick one and stay consistent.
- The worst handling is silent discard. The user edits the project copy, nothing changes, and they suspect caching or a failed save rather than a same-named skill elsewhere. Always log a warning that prints both paths.
- Expected follow-up: does loose loading let bad skills in? These are different layers. Looseness is format tolerance; safety comes from source trust and tool permissions, not from schema validation.
分析过程 · 先想清楚再作答
- 两个小问共用一个立场:**运行时是给人干活的,不是校验器。** 先把这句说出来,后面两半都好答。
- 宽松加载这一半要给出可判定的边界,不能只说「尽量宽松」。**唯一的硬性淘汰是缺 description**——少了它这个 skill 在发现阶段没有触发面,永远不会被选中,留在清单里只是白占 token。
- 其余一律只告警仍然加载:名字与目录名不一致、名字用了大写或下划线、描述超过上限。它们影响质量,不影响能不能用。
- 举一个最常见的畸形做证据:YAML 值里没加引号的冒号会让正规解析器判整行非法,进而拒绝整个文件。正确的兜底顺序是先用完整 YAML 解析,失败了再退回按行取值,只抠出认识的那几个标量字段。
- 同名冲突这一半,方向不是重点,**处理方式才是**。跨客户端通行约定是项目级压过用户级,但 Claude Code 的顺序是企业级、个人级、项目级由高到低,两种都合理,关键是固定一种并保持一致。
- 最糟的做法是静默丢弃:用户改了项目里那份,行为一点没变,他会去怀疑缓存和保存,就是不会想到别处有个同名的。**必须留一条警告并把两个路径都打出来**,那条日志是排查这类问题的第一现场。
- 可预期的追问是「宽松会不会把坏 skill 放进来」。答案是这两件事的层次不同:宽松说的是格式容错,安全靠的是来源信任与工具权限,不能拿格式校验当安全边界。
Key points
- A runtime is not a validator; degrade by default.
- The only hard rejection is a missing description, which leaves no trigger surface.
- Name mismatches, invalid names and over-long descriptions warn but still load.
- Parse with full YAML first, then fall back to line-wise field reading for unquoted colons.
- Fix one collision priority, keep it consistent, and never discard silently: log both paths.
答题要点
- 立场是运行时不是校验器,默认降级加载。
- 唯一硬性淘汰是缺 description,因为它没有触发面、永远不会被选中。
- 名字不一致、名字不合规、描述超长都只记诊断仍然加载。
- 解析顺序是先完整 YAML、失败再按行取值兜底,专治值里没加引号的冒号。
- 同名冲突要固定一种优先级并保持一致,绝不静默丢弃,警告里要带上两个路径。
Once a skill body is in context, how do you keep it effective across a long session? And would you activate skills by file read or by a dedicated tool?skill 的正文进了上下文之后,长会话里怎么保证它不失效?激活方式上文件读取和专用工具你会选哪个?
Common in ChinaCommon overseasDeep dive#agent-skills#runtime#long-sessionHow to reason about it · think before answering
- This is about the gap between a working demo and something you can ship. The first half is long-session failure modes, the second is the activation mechanism trade-off.
- Two long-session problems. Duplicate activation: the model forgets it already read the skill and selects it again, so the same instructions appear twice, wasting tokens and creating conflicts where the wording differs. Fix it with a set of already-activated names.
- The worse problem is compaction. Summarizing early messages can drop the skill body, and nothing errors: the model quietly reverts to its behavior without the skill. Users report that it stopped following the convention later in the conversation, and it is the hardest failure here to diagnose.
- The fix is to mark the activated message as protected so compaction preserves it, or to re-inject it afterward. The marker is trivial; remembering to set it is not.
- For the second half give criteria, not a preference. File-read activation adds no new mechanism, so any agent that can read files supports skills immediately, which is why the format spread across dozens of clients. The cost is no clean hook for dedup or protection, and the model can read the wrong path.
- A dedicated tool turns activation into an observable, interceptable call where you can dedupe, check permissions, and return the skill directory and resource list together. The cost is another tool definition and host cooperation. The criterion is whether you control the host.
- Expected follow-up: should resource files be read during activation? No, list filenames only. The value of three stages is that the third usually never happens.
分析过程 · 先想清楚再作答
- 这题考的是「演示能跑」和「上线能用」之间那段距离。前半是长会话的失效模式,后半是激活机制的取舍。
- 长会话有两个问题。第一个是重复激活:模型忘了自己读过,第二次又选中同一个 skill,同一段指令出现两遍既浪费又容易在措辞出入时互相干扰。修法是维护一个已激活集合,命中就直接返回。
- 第二个问题更要命——**被压缩掉**。压缩会把早期消息换成摘要,skill 正文落在那个区间里**不会报任何错**,模型只是悄悄退回没有这个 skill 的行为。用户看到的现象是「聊到后面它又不按规范写了」,这是这套机制里最难查的一类问题。
- 解法是给激活出来的那条消息打一个受保护标记,压缩时整段保留,或者在压缩后重新注入一次。标记本身很简单,难的是记得给它。
- 后半的取舍要给判据而不是偏好。文件读取式零新增机制,任何有读文件能力的 Agent 都能立刻支持,这正是这个格式能在几十家客户端铺开的原因;代价是没有明确钩子做去重和保护,模型还可能读错路径。
- 专用工具式把激活变成一次可观测可拦截的调用,能在这一步做去重、权限检查、连技能目录与资源清单一起返回;代价是多一个工具定义,且要求宿主愿意开这条通路。**判据是你控不控得住宿主**:自己写 Agent 用工具式,做通用实现用文件读取式。
- 可预期的追问是「资源文件要不要在激活时一起读进来」。不要,只列文件名。三阶段的全部价值就在于第三阶段大多数时候不会发生。
Key points
- Dedupe with a set of activated skills or the same instructions appear twice and conflict.
- Losing a skill body to compaction raises no error; the model silently reverts, which is the hardest failure to spot.
- Mark the activated message as compaction-protected, or re-inject after compaction.
- File-read activation adds no mechanism and has the best compatibility but offers no hook for dedup or protection.
- A dedicated tool is observable and interceptable; choose by whether you control the host, and in both cases list resource filenames without reading them.
答题要点
- 重复激活要靠已激活集合去重,否则同一段指令会出现两遍并互相干扰。
- 压缩掉 skill 正文不会报错,模型只会悄悄退回原行为,是最难查的失效。
- 激活出来的消息要打受保护标记,压缩时保留或事后重新注入。
- 文件读取式零新增机制、兼容性最好,但没有去重与保护的钩子。
- 专用工具式可观测可拦截,判据是你控不控得住宿主;两者都只列资源文件名,不预读内容。