Polish s07 prose (zh/en/ja) with the s08 narrative technique

The problem section now opens by carrying over from s06's sub-Agent (a
sub-Agent taking over needs the task's rules; where do those rules come
from). The solution leads with a why-not (let the Agent read_file the
docs itself, but it doesn't know what files exist) and an anchor: 目录常驻,
内容按需 / catalog always resident, content on demand. Level 1's code block
now ends with a cascade hook (you only get the name and one-line
description, the full content is still out of reach -> Level 2). `messages`
and `tool_result` get backticks; one stray dash becomes a comma. All three
languages synced to v3; web docs regenerated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Haoran 2026-06-27 03:45:16 +08:00
parent ae58af86f7
commit 354473dcbb
4 changed files with 43 additions and 25 deletions

View file

@ -3,7 +3,7 @@
[中文](README.zh.md) · [English](README.md) · [日本語](README.ja.md)
s01 → s02 → s03 → s04 → s05 → s06 → `s07` → [s08](../s08_context_compact/) → s09 → ... → s20
> *"Load when needed, don't stuff the prompt"* — tool_result で注入、system prompt には詰め込まない。
> *"Load when needed, don't stuff the prompt"*`tool_result` で注入、system prompt には詰め込まない。
>
> **Harness レイヤー**: 知識 — 必要に応じて読み込み、コンテキストに詰め込まない。
@ -11,7 +11,9 @@ s01 → s02 → s03 → s04 → s05 → s06 → `s07` → [s08](../s08_context_c
## 課題
プロジェクトには React コンポーネント仕様、SQL スタイルガイド、API 設計ドキュメントがある。Agent にこれらの仕様を自動的に守らせたい。最も直接的な方法は、すべて system prompt に詰め込むこと:
前章では Agent が大きなタスクをサブ Agent に渡せるようになった。だがサブ Agent が引き継ぐとき、そのタスクの決まりを知る必要があるReact コンポーネントを変更するなら component spec を、SQL を書くなら style guide を守る。その決まりはどこから来るのか?
プロジェクトには React コンポーネント仕様、SQL スタイルガイド、API 設計ドキュメントがある。最も直接的な方法は、すべて system prompt に詰め込むこと:
```python
SYSTEM = (
@ -30,7 +32,9 @@ SYSTEM = (
![Skill Overview](images/skill-overview.svg)
前章の最小フック構造、`todo_write`、サブ Agent を維持し、本章は新規の `load_skill` ツールに注目する。起動時にスキルカタログを SYSTEM prompt に注入し、実行時に完全な内容を読み込むツールを登録する。使ったときだけトークンを消費。
折衷案は、ドキュメントを複数のファイルに分け、必要なものを Agent 自身に `read_file` させることだ。だが Agent はそもそもどんなファイルが読めるか分からない。「何があるか」を先に知らなければ、「どれを使うか」は選べない。
そこで 2 層に分ける:**カタログは常駐、内容はオンデマンド。** 前章のフック構造、`todo_write`、サブ Agent はそのまま残し、本章で `load_skill` ツールを 1 つ加える。起動時にスキルのカタログ(名前 + 一言の説明)を SYSTEM prompt に入れる。毎ターン携帯するが軽い。実行時に Agent が実際にあるスキルを使うとき、`load_skill` を呼んで完全な内容を取り出す。トークンを使うのはそのときだけだ。
2 層設計:
@ -90,6 +94,8 @@ def build_system() -> str:
SYSTEM = build_system()
```
だが Agent は毎ターン、名前と一言の説明しか受け取らない。実際に SQL スタイルガイドを使うとなると、あの 1500 行の完全な内容にはまだ手が届かない。→ 第 2 層。
**第 2 層load_skill**Agent が「SQL スタイルガイドが必要」と判断し、`load_skill("sql-style")` を呼び出す。レジストリを通じて検索し、ファイルパスを経由しないため、パストラバーサルのリスクがない。SKILL.md の内容は `tool_result` を通じて注入され、既存の file および bash ツールを通じて、参照される `references/``scripts/``assets/` へのその後のアクセスも含められる。
```python
@ -100,7 +106,7 @@ def load_skill(name: str) -> str:
return skill["content"]
```
重要な違い:スキル内容は system prompt の一部ではなく、ツール結果として現在の messages に入る。後続の呼び出しでは履歴とともに携帯され、コンテキスト圧縮、切り捨て、またはセッション終了まで保持される。これは s08 の compact と自然に接続する:オンデマンド読み込みにより、無関係なドキュメントが system prompt に入らなくなる。compact が「捨てるべきものをどう捨てるか」を解決する。
重要な違い:スキル内容は system prompt の一部ではなく、ツール結果として現在の `messages` に入る。後続の呼び出しでは履歴とともに携帯され、コンテキスト圧縮、切り捨て、またはセッション終了まで保持される。これは s08 の compact と自然に接続する:スキル内容は system prompt ではなく `tool_result` として `messages` に入り、compact が「捨てるべきものをどう捨てるか」を解決する。
---
@ -135,7 +141,7 @@ python s07_skill_loading/code.py
## 次へ
load_skill で起動時のトークン浪費は解消した。しかし別の問題が待っているAgent が 30 分連続で作業すると、messages リストが中間プロセスで埋め尽くされる。古い tool_result、期限切れのファイル内容、コンテキストを占領しているが価値を生まない。
load_skill で起動時のトークン浪費は解消した。しかし別の問題が待っているAgent が 30 分連続で作業すると、`messages` リストが中間プロセスで埋め尽くされる。古い `tool_result`、期限切れのファイル内容、コンテキストを占領しているが価値を生まない。
→ s08 Context Compact4 層圧縮戦略。安価な層を先に実行、高価な層を後に実行。
@ -179,4 +185,4 @@ Claude Code の SKILL.md YAML frontmatter は `parseSkillFrontmatterFields()`
</details>
<!-- translation-sync: zh@v2, en@v2, ja@v2 -->
<!-- translation-sync: zh@v3, en@v3, ja@v3 -->

View file

@ -3,7 +3,7 @@
[中文](README.zh.md) · [English](README.md) · [日本語](README.ja.md)
s01 → s02 → s03 → s04 → s05 → s06 → `s07` → [s08](../s08_context_compact/) → s09 → ... → s20
> *"Load when needed, don't stuff the prompt"* — Inject via tool_result, not system prompt.
> *"Load when needed, don't stuff the prompt"* — Inject via `tool_result`, not system prompt.
>
> **Harness Layer**: Knowledge — load on demand, don't fill the context.
@ -11,7 +11,9 @@ s01 → s02 → s03 → s04 → s05 → s06 → `s07` → [s08](../s08_context_c
## The Problem
Your project has a React component spec, a SQL style guide, and an API design doc. You want the Agent to follow these specs automatically. The most straightforward idea: stuff them all into the system prompt:
The last chapter let the Agent hand a big task off to a sub-Agent. But when the sub-Agent takes over, it needs to know the rules of that task: editing React components means following the component spec, writing SQL means following the style guide. Where do those rules come from?
Your project has a React component spec, a SQL style guide, and an API design doc. The most straightforward idea: stuff them all into the system prompt:
```python
SYSTEM = (
@ -30,7 +32,9 @@ SYSTEM = (
![Skill Overview](images/skill-overview.svg)
The minimal hook structure, `todo_write`, and sub-Agent from the previous chapter are preserved. This chapter focuses on the new `load_skill` tool. At startup, inject the skill catalog into the SYSTEM prompt; at runtime, register one more tool to load full content, spending tokens only when used.
A middle-ground idea is to split the docs into separate files and let the Agent `read_file` whichever it needs. But the Agent has no idea which files exist to read; it has to know "what's there" before it can pick "which one to use".
So split it into two levels: **catalog always resident, content on demand.** The hook structure, `todo_write`, and sub-Agent from the previous chapter stay; this chapter adds a `load_skill` tool. At startup, the skill catalog (name + one-line description) goes into the SYSTEM prompt, carried every turn but light; at runtime, when the Agent actually needs a skill, it calls `load_skill` to pull the full content, spending those tokens only then.
Two-level design:
@ -90,6 +94,8 @@ def build_system() -> str:
SYSTEM = build_system()
```
But every turn the Agent only gets the name and one-line description; to actually use the SQL style guide, those 1500 lines of full content are still out of reach. → Level 2.
**Level 2: load_skill**: the Agent decides "I need the SQL style guide" and calls `load_skill("sql-style")`. Lookup goes through the registry, not file paths, eliminating path traversal risk. The SKILL.md content is injected via `tool_result`, and can include later access to referenced `references/`, `scripts/`, or `assets/` through the existing file and bash tools.
```python
@ -100,7 +106,7 @@ def load_skill(name: str) -> str:
return skill["content"]
```
The key distinction: skill content is not part of the system prompt. It enters the current messages as a tool result. Subsequent calls carry it along with the history until context compaction, truncation, or session end. This naturally connects to s08's compact: on-demand loading keeps irrelevant docs out of the system prompt, compact solves "how to drop what you should."
The key distinction: skill content is not part of the system prompt. It enters the current `messages` as a `tool_result`. Subsequent calls carry it along with the history until context compaction, truncation, or session end. This naturally connects to s08's compact: skill content enters `messages` as a `tool_result` rather than the system prompt, and compact solves "how to drop what you should."
---
@ -135,7 +141,7 @@ What to watch for: Does the Agent know available skills from the SYSTEM catalog?
## What's Next
load_skill eliminated the startup token waste. But another problem looms: after the Agent works for 30 minutes, the messages list fills up with intermediate process. Old tool_results, stale file contents, occupying context but adding no value.
load_skill eliminated the startup token waste. But another problem looms: after the Agent works for 30 minutes, the `messages` list fills up with intermediate process. Old `tool_result`s, stale file contents, occupying context but adding no value.
→ s08 Context Compact: A four-layer compaction strategy. Cheap layers run first, expensive layers run last.
@ -179,4 +185,4 @@ The complete field list changes across versions; above are the core fields relev
</details>
<!-- translation-sync: zh@v2, en@v2, ja@v2 -->
<!-- translation-sync: zh@v3, en@v3, ja@v3 -->

View file

@ -3,7 +3,7 @@
[中文](README.zh.md) · [English](README.md) · [日本語](README.ja.md)
s01 → s02 → s03 → s04 → s05 → s06 → `s07` → [s08](../s08_context_compact/) → s09 → ... → s20
> *"用到时再加载, 别全塞 prompt 里"* — 通过 tool_result 注入, 不塞 system prompt。
> *"用到时再加载, 别全塞 prompt 里"* — 通过 `tool_result` 注入, 不塞 system prompt。
>
> **Harness 层**: 知识 — 按需加载, 不堆满上下文。
@ -11,7 +11,9 @@ s01 → s02 → s03 → s04 → s05 → s06 → `s07` → [s08](../s08_context_c
## 问题
你的项目有一套 React 组件规范、一份 SQL 风格指南、一份 API 设计文档。你希望 Agent 自动遵守这些规范。最直接的想法,全塞进 system prompt
上一章 Agent 能把大任务拆给子 Agent 了。但子 Agent 接手时,得知道这个任务的规矩:改 React 组件要守组件规范,写 SQL 要守风格指南。这些规范从哪来?
你的项目里有一套 React 组件规范、一份 SQL 风格指南、一份 API 设计文档。最直接的想法,全塞进 system prompt
```python
SYSTEM = (
@ -22,7 +24,7 @@ SYSTEM = (
)
```
6500 行 system prompt。Agent 每次调用 LLM 都带着这些文档——不管是在改 CSS 颜色还是修 SQL 查询。99% 的内容和当前任务无关,白白消耗 token。
6500 行 system prompt。Agent 每次调用 LLM 都带着这些文档,不管在改 CSS 颜色还是修 SQL 查询。99% 的内容和当前任务无关,白白消耗 token。
---
@ -30,7 +32,9 @@ SYSTEM = (
![Skill Overview](images/skill-overview.svg)
保留上一章的最小 hook 结构、`todo_write` 和子 Agent本章重点转向新增的 `load_skill` 工具。启动时把技能目录注入 SYSTEM prompt运行时多注册一个工具加载完整内容用到才花 token。
一个折中的想法,是把文档拆成几个文件,用哪个让 Agent 自己 `read_file`。但 Agent 根本不知道有哪些文件可读,它得先知道"有什么",才谈得上"用哪个"。
所以分两层:**目录常驻,内容按需。** 上一章的 hook 结构、`todo_write` 和子 Agent 都保留,本章新增一个 `load_skill` 工具。启动时把技能目录(名字 + 一句话描述)注入 SYSTEM prompt每轮都带、但很轻运行时 Agent 真要用某个技能,再调 `load_skill` 把完整内容取过来,用到才花那部分 token。
两层设计:
@ -39,7 +43,7 @@ SYSTEM = (
| 1. 目录 | system prompt | 启动时注入harness 扫描 skills/ | ~100 tokens/skill每轮都带 |
| 2. 内容 | tool_result | Agent 调用 load_skill 时SKILL.md 可指引后续的 read_file/bash 调用,用于按需访问额外资源 | ~2000 tokens/skill按需 |
dispatch 机制不变load_skill 通过 `TOOL_HANDLERS[block.name]` 分发。
dispatch 机制不变,`load_skill` 通过 `TOOL_HANDLERS[block.name]` 分发。
---
@ -90,6 +94,8 @@ def build_system() -> str:
SYSTEM = build_system()
```
但 Agent 每轮只拿到名字和一句话描述,真要用 SQL 风格指南,那 1500 行的完整内容还够不到。→ 第二级。
**第二级load_skill**Agent 决定"我需要 SQL 风格指南",调用 `load_skill("sql-style")`。通过注册表查找不走文件路径没有路径遍历风险。SKILL.md 内容通过 `tool_result` 注入,并可通过现有的 file 和 bash 工具进一步访问引用的 `references/``scripts/``assets/`
```python
@ -100,7 +106,7 @@ def load_skill(name: str) -> str:
return skill["content"]
```
关键区别:技能内容不是 system prompt 的一部分,它作为一次工具结果进入当前 messages。后续调用会随历史一起携带直到上下文压缩、截断或会话结束。这和 s08 的 compact 自然衔接:技能内容以 tool_result 形式进入 messages不塞 system promptcompact 解决"该丢的怎么丢"。
关键区别:技能内容不是 system prompt 的一部分,它作为一次工具结果进入当前 `messages`。后续调用会随历史一起携带,直到上下文压缩、截断或会话结束。这和 s08 的 compact 自然衔接:技能内容以 `tool_result` 形式进入 `messages`,不塞 system promptcompact 解决"该丢的怎么丢"。
---
@ -135,7 +141,7 @@ python s07_skill_loading/code.py
## 接下来
load_skill 解决了启动时的 token 浪费。但另一个问题来了Agent 连续工作 30 分钟后messages 列表塞满了中间过程。旧的 tool_result、过时的文件内容占着上下文但不产生价值。
`load_skill` 解决了启动时的 token 浪费。但另一个问题来了Agent 连续工作 30 分钟后,`messages` 列表塞满了中间过程。旧的 `tool_result`、过时的文件内容,占着上下文但不产生价值。
s08 Context Compact → 四层压缩策略。便宜的先跑,贵的后跑。
@ -168,7 +174,7 @@ Claude Code 的 SKILL.md YAML frontmatter 由 `parseSkillFrontmatterFields()`
### 三、两级加载的精确实现
1. **Catalog启动时**`getSkillDirCommands()` 扫描目录 → 注册为 `Command` 对象,只包含元数据。`getSkillListingAttachments()` 把技能列表格式化为附件,预算为上下文窗口的 ~1%(上限 8000 字符)。
2. **Load调用时**:模型调 `Skill` 工具(输入字段是 `skill` + 可选 `args`,教学版用 `name`)→ `getPromptForCommand()` 展开完整 SKILL.md 内容 → `SkillTool` 返回的 tool_result 展示文本只是 `"Launching skill: {name}"`,真正的技能内容通过 `newMessages` 注入对话。教学版把两者合并为"通过 tool_result 注入"是一种简化;加载后的 SKILL.md 仍可作为指引,帮助模型后续通过现有 file/bash 工具访问相关资源。
2. **Load调用时**:模型调 `Skill` 工具(输入字段是 `skill` + 可选 `args`,教学版用 `name`)→ `getPromptForCommand()` 展开完整 SKILL.md 内容 → `SkillTool` 返回的 `tool_result` 展示文本只是 `"Launching skill: {name}"`,真正的技能内容通过 `newMessages` 注入对话。教学版把两者合并为"通过 tool_result 注入"是一种简化;加载后的 SKILL.md 仍可作为指引,帮助模型后续通过现有 file/bash 工具访问相关资源。
### 教学版的简化是刻意的
@ -179,4 +185,4 @@ Claude Code 的 SKILL.md YAML frontmatter 由 `parseSkillFrontmatterFields()`
</details>
<!-- translation-sync: zh@v2, en@v2, ja@v2 -->
<!-- translation-sync: zh@v3, en@v3, ja@v3 -->

File diff suppressed because one or more lines are too long