diff --git a/docs/maintainers/MARCH_FEEL_AND_READABILITY.md b/docs/maintainers/MARCH_FEEL_AND_READABILITY.md new file mode 100644 index 00000000..9c91b96c --- /dev/null +++ b/docs/maintainers/MARCH_FEEL_AND_READABILITY.md @@ -0,0 +1,188 @@ +# learn-claude-code:章节可读性与文风恢复说明(给维护者) + +**读者**:本仓库维护者 / 章节作者 / Reviewer +**基准对照**:2026-03-29 `16b927c`(12 课 + `docs/zh` 心智模型短章)vs 当前 `main`(17 课根目录) +**诉求来源**:课程创始人侧反馈——当前中后章阅读感、创作感、上头感明显变差;版式固定、AI 味、行文尴尬、难读难懂。 +**状态**:本文为写作与改写的权威约束。机制正确性仍要守;**文风与信息优先级以本文为准**。 + +--- + +## 1. 一句话 + +请把章节从「工程规格书 + AI 润色课包」拉回「短讲义卡片」: + +> **问题砸人 → 一张图/ASCII → 最小代码 → 试一试** + +深度可以保留,但必须住在折叠/附录;不能占首屏和主呼吸。 + +--- + +## 2. 客观诊断(不是口味吵架) + +### 2.1 分层结论 + +| 范围 | 阅读感 | 创作感 | 上头感 | 说明 | +|------|------:|------:|------:|------| +| 2026-03 短章 | ~8.5 | ~8 | ~8 | 短、具体、对「你」说话 | +| main 早章(约 s01–s07) | ~7 | ~6 | ~6.5 | 仍可读,已被模板镀铬 | +| main 晚章(s08/s13/s15/s16…) | ~3.5 | ~2.5 | ~2 | 手册化 / 抽象词 / 标题墙 | + +### 2.2 关键数字(main 中文 README) + +- 三月章均约 **4KB / 5 个 H2**;main 中位约 **6.9KB**,重灾章 **11–18KB**。 +- 分水岭在 **s08**:此前多为「镀铬但仍短」,此后跳变。 +- 「你」:s01 仍在;多数中后章 **≈0**。 +- 全部 17 章:双语导航 + `translation-sync`;**`
` 使用率曾长期为 0**(深度只能堆主文)。 +- 重灾优先序(分诊):**s13 → s16 → s08 → s15 → s10**(s17/s09 紧随)。 + +### 2.3 根因 Top 5(不是「单纯更长」) + +1. **声音从「对你说话」变成系统旁白** +2. **taxonomy / 边界表 / 组件目录压过洞见** +3. **模板铬 + 三语文案生产线**(章感雷同) +4. **工程词典取代叙事动词**(宿主/registry/生命周期/语义 key…) +5. **覆盖焦虑**把教学文写成 runbook + +### 2.4 高 AI 味的机械尾槽(特别刺眼) + +章章同一套收尾流水线会让人一眼看出「流水线产物」: + +| 槽位 | 机械套路 | 读感 | 要求 | +|------|----------|------|------| +| 试一下 | cd → 编号 prompt →「观察重点:是否…是否…」 | QA/CI checklist | 命令 + ≤3 prompt;**禁止观察重点勾选清单** | +| 接下来 | 「现在能 X 了。但 Y 又爆了。」 | 万能悬念工厂 | **可选**;≤3 句且换写法;可整段删 | +| 相对 sN | 组件/之前/之后大表 | PR 变更表塞进教材 | **默认撤出主文**;改一句能力增量或进 `
` | + +三月同题常在「试一试」后**戛然而止**——没有「接下来」,没有观察审问;更像人写的。 + +### 2.5 同题对比(摘录) + +**三月 s01** +> 没有循环, 每次工具调用你都得手动把结果粘回去。**你自己就是那个循环。** + +**main s06(同主题后继)** +> …多数中间细节不再需要,却仍然占用上下文。 +(正确、完整、无趣;「pytest 一个词」那种 punch 没了。) + +**main s08** +> **本节将实现一条四步压缩管线。** +(预告腔直接杀死好奇心。) + +**main s15** +开篇 9 条「需要同时拥有」功能 backlog +「组件在循环中的位置」大表 + 观察重点 8 条——组件目录,不是故事。 + +**main s16** +抽象工程词命中可到数十上百;开篇像架构演进史,不像痛点。 + +--- + +## 3. 写作要求(必须遵守) + +### 3.1 主文硬指标(不含 `
`) + +| 指标 | 达标 | 重灾线 | +|------|------|--------| +| 字节 | ≤7KB | >10KB | +| 行数 | ≤180 | >260 | +| H2 | ≤7 | >10 | +| 主文代码围栏 | ≤4 | >8 | +| 主文表格 | ≤1 | ≥3 | +| 「你」(问题/试跑) | ≥1 | 全程无人称 | +| 「本节将/本章将」 | 0 | ≥1 | +| 抽象工程词* | ≤15 | >40 | +| 首屏 | 能看到「问题」/痛点 | 只见导航或目录 | + +\*词表示例:管线|拓扑|适配器|原语|宿主|生命周期|registry|schema|journal|元数据|编排|语义 + +图片:主路径 0–2 张;ASCII 能讲清就不上大图;次要图进折叠。 + +### 3.2 推荐骨架 + +```text +标题(短、可记) +一行语言切换(若课程需要)+ 一行面包屑 +格言 1 句 + Harness 层 1 行(要狠) + +## 问题 ← 2–5 句;有「你」或可感场景;禁止功能清单开场 +## 解决方案 ← 1 ASCII/1 关键图 + 2–4 句 +## 工作原理 ← ≤4 步;人话 → 再术语;短代码 +## 试一试 ← 命令 + ≤3 prompt;禁止观察重点清单 +## 接下来(可选) + +
…深度、模式库、边界、相对前章细表…
+``` + +复杂章只保证主文讲清「**这一章只加一件东西**」。 + +### 3.3 声音 + +- 先洞见,后术语。 +- 每章至少一句 punch(删掉它章就塌)。 +- 隐喻最多开篇一小段;禁止全章跟隐喻跑。 +- 中文像人讲,不要英译说明书。 +- 90 秒说不清「只加了一件东西」→ 再砍。 + +### 3.4 机制 fidelity(教学可简化,不可说错) + +以各章真实教学代码与上游产品契约为准。以 s16 为例必须诚实: + +- Dynamic = 模型写脚本(Claude Code:`script` / `scriptPath`);Saved = `name` + `args` +- 不得再暗示「模型不能提交可执行代码」仿佛是产品事实 +- `parallel`/`pipeline` 失败隔离为 null;resume = 最长未改前缀 +- 说明为何真 JS runtime 忌 `Date.now` / `Math.random` +- 本章若是 Python 教学 runtime,要标明:思想对齐,不是 bit-perfect 复刻 + +思想脊梁可参考官方文 +[A harness for every task: dynamic workflows in Claude Code](https://claude.com/blog/a-harness-for-every-task-dynamic-workflows-in-claude-code)。 + +### 3.5 Reviewer 清单 + +- [ ] 首屏出现痛点 +- [ ] 主文硬指标达标 +- [ ] 有 punch;格言与正文咬合 +- [ ] 对「你」说话 +- [ ] 无「本节将」、无标题墙、无主文组件目录 +- [ ] 无观察重点勾选、无万能「接下来」、无主文「相对 sN」大表 +- [ ] 深度在 `
` +- [ ] GitHub 预览像讲义,不像 API 手册 +- [ ] 机制正确 + +--- + +## 4. 建议改写队列 + +1. **P1 重灾**:s13_agent_teams,s16_workflow_runtime,s08_context_compact,s15_integrated_harness +2. **P2 手册化**:s10_task_system,s17_goal_loop,s09_memory,s14_mcp_plugin,s11… +3. **P4 镀铬早章**:少动结构;去预告腔、补 punch、减轻尾槽机械感 +4. **全局政策**:主文短 + `
` 分层;尾槽去流水线化 + +s16 无三月祖先:叙事节奏应对标 **s01/s06 三月短章**,不要对标 s13/s15 说明书骨架。 + +--- + +## 5. 证据与附件(工作区) + +| 文件 | 内容 | +|------|------| +| `MARCH_FEEL_RECOVERY_PLAYBOOK.md` | 恢复标准细则 | +| `DEEP_READABILITY_WHY_BAD.md` | 文风深挖与打分 | +| `MAIN_CHAPTER_TRIAGE.md` | 17 章分诊表 | +| `TEMPLATE_SLOTS_AI_SMELL.md` | 试一下/接下来/相对前章并置 | +| `MARCH_VS_MAIN_READABILITY.md` | 量化简报 | +| `AGENT_WRITING_BRIEF.md` | s16 思想/fidelity 总纲 | +| `/workspace/lcc_shots2/*.png` | GitHub 渲染截图对照 | + +三月对照 commit:`16b927c8ee7befa07caf8844d22f86ffef0aea05`。 + +--- + +## 6. 非目标 + +- 不是要求删掉三语支持或测试。 +- 不是要求章节变浅、变错。 +- 不是要求每章都用同一套「精修散文」或同一套隐喻。 +- 是要求:**可读、有创作棱角、想翻下一章**;正确性放在正确的信息层级里。 + +--- + +*维护者文档版本:2026-08-12* diff --git a/s16_workflow_runtime/README.ja.md b/s16_workflow_runtime/README.ja.md index 5b992f55..f7c0a811 100644 --- a/s16_workflow_runtime/README.ja.md +++ b/s16_workflow_runtime/README.ja.md @@ -1,114 +1,75 @@ -# s16: Workflow Runtime — レシピをコードに書く +# s16: Workflow Runtime — オーケストレーションをコードに書く [English](README.md) · [中文](README.zh.md) · [日本語](README.ja.md) -s01 → ... → s14 → [s15](../s15_integrated_harness/) → `s16` → [s17](../s17_goal_loop/) +[s15](../s15_integrated_harness/) → `s16` → [s17](../s17_goal_loop/) -> Workflow = コードに書いたオーケストレーション。トポロジはスクリプト、一歩の判断はモデル。 +> *計画をチャットの中だけに置かない。* 順番はスクリプトが持ち、一歩ごとの判断はモデルが持つ。 > -> **Harness 層**: オーケストレーション — 単一 agent ループの上で多 agent スクリプトを回す。 -> -> モデルを信じ、harness を設計する。Workflow はその一言を一段上げる。 - ---- +> **Harness 層**: オーケストレーション — 単一 agent ループの上で、マルチ agent スクリプトを走らせる。 ## 問題 -長い仕事では計画と実行が同じ chat に同居する:途中で完了宣言、自分の宿題を甘く採点、圧縮のあと静かな制約が消える。並列・安定した結果形・再開——柔らかい会話メモリでは支えきれない。 +あなたはすでに、ひとつのループの中でモデルにファイルを読ませ、コードを直し、エラーを見させることができます。ところが仕事によっては、**順番が最初から分かっている**ものがあります。次元ごとにレビューし、次に意地悪な検証、最後にまとめる——という具合です。その順番をチャットの記憶にだけ預けていると、モデルは途中で「完了」と言い、自分の宿題を甘く採点し、圧縮を何度か経ると「X を触るな」さえ消えます。 -十秒おきにシェフへ SMS を送るような催促。**Workflow** は厨房がそのまま従えるレシピだ。 +柔らかい会話は、並列も、結果の形の安定も、落ちてからの再開も支えきれません。もっとおしゃべりの上手なモデルが欲しいのではありません。**書き下ろされたオーケストレーション**が欲しいのです。 -## アイデア - -ヘルパー(サブ agent)は考える。**スクリプト**がループ・分散・マージを握る。中間結果は変数と journal に置き、会話には入れない。 - -**オーケストレーションを「知性」から「構造」へ移す。** +## 解決策 ```text - messages[] ──► Workflow(...) ──► tool_result - │ - ▼ - スクリプトが握る: agent / parallel / pipeline - │ - ▼ - 変数 + journal + あなたの会話 ──► Workflow(...) ──► 結果が一条で戻る + │ + ▼ + スクリプト: agent / pipeline / parallel + │ + ▼ + 変数 + journal(半成品はここに。スレに詰め込まない) ``` -`Workflow` ツール呼び出しひとつで開始;レシピが終わると結果がひとつ返る。 +ヘルパー(サブ agent)は相変わらず考えます。**スクリプト**がループ・分配・マージを持ちます。中間結果は変数と journal に置き、親の対話には入れません。 -
-Runtime 概要図 +一言でいうと:**オーケストレーションを「知性」から「構造」へ移す。** -![Workflow Runtime Overview](images/workflow-runtime-overview.svg) +![静的 harness と動的 workflow](images/dynamic-vs-static.png) -
+*左:汎用の固定パイプライン。右:このタスク向けに裁断したオーケストレーション。* -## ふたつの入口 +Claude Code には二つの扉があります。**動的**——モデルがこのタスク用に JS を書く(`script` / `scriptPath`)。**保存済み**——良いスクリプトを `name` + `args` で再実行。外側には SDK で先に書き切る静的オーケストレーションもあります。本章は **Python の教材用 runtime**(JS VM なし)です。考えは揃え、デモは「保存済み」の扉を使います。製品ではモデルはスクリプトを出せます——ここでは JS を走らせないだけです。 -- **Dynamic**: モデルが*この*タスク向けに JS オーケストレーションを書く(`script` / `scriptPath`)。 -- **Saved**: 良いスクリプトを `.claude/workflows/` に置き、`name` + `args` で再呼び出し。 -- **Static**(外のいとこ): SDK / `claude -p` で事前に書く——だいたい汎用寄り。 +## 仕組み -![Static vs dynamic](images/dynamic-vs-static.png) - -*左: 固定パイプライン → 汎用レポート。右: あなたのコード向けに裁断 → 具体的な提案。* - -本章は **Python のティーチング runtime**(JS VM なし)。概念は Claude Code に揃え、デモは Saved 入口。製品ではモデルが実行可能スクリプトを出せる——ここでは JS インタプリタを埋め込まないだけ。 - -```python -# teaching sketch — 完全な schema ではない -Workflow({ "name": "review-changes", "args": { "changes": "..." } }) -# Claude Code はさらに: script | scriptPath | resumeFromRunId -``` - -## 三つの動詞 +**1. 三つの動詞** ```text - agent ヘルパー一人、仕事ひとつ(schema で JSON 検証可) - pipeline item ごとに段階を進む(既定 — 障壁なし) - parallel 全部揃ってから次へ(障壁 — 控えめに) + agent 一人のヘルパー、一件の仕事(schema で次に渡せる JSON も可) + pipeline 各 item が自分の段階を進む(既定。同期しない) + parallel 全部揃ってから先へ(バリア。多用しない) ``` -失敗しても艦隊は続く: `parallel` はそのスロットが `null`;`pipeline` は**その item** と後段を落とす。マージ前にフィルタ。 +失敗時:`parallel` のその枠は `null`。`pipeline` はその item を捨てます。艦隊は丸ごと沈みません。マージ前にフィルタしてください。 -再開: journal は呼び出し順に記録;**最長の未変更プレフィックス**を再生し、最初の変更以降は実走。本物の JS runtime は `Date.now()` / `Math.random()` を禁じる。このデモは完全サンドボックスしない——それでも決定的に書く。 +**2. 再開はノートで。チャット記憶ではない** + +journal は `agent()` の**呼び出し順**で記帳します。再開は最長の未変更プレフィックスを再生し、最初の変更から先は全部ライブです。本番の JS runtime は `Date.now()` / `Math.random()` を禁じます——ノートがずれないように。教材スクリプトも決定的に書いてください。 ```text journal [A] [B] [C] [D] - resume hit hit ✂ live + 再開 命中 命中 ✂ ライブ ``` -
-公式プリミティブ・カード + 静かな動詞 +**3. サンプル一つ:Fanout + Adversarial** -![Workflow primitives](images/workflow-primitives.png) - -*`agent`;`parallel`(障壁)vs `pipeline`(ストリーミング段階)。Claude Code には `model` / `isolation` / `agentType` もある;ティーチング面は小さめ。* - -静かな動詞: `phase`、`log`、ネスト一段 `workflow`、`args`、`budget`。 - -
- -## ふたつの形 + ひとつの sample - -まずふたつ(六パターン全体は下の折りたたみ): +`review-changes` は「一つのパターン」ではありません。**Fanout** の中に **Adversarial** が入ります。次元ごとに `pipeline(audit, verify)`、検証で `parallel` に意地悪させ、残った finding だけ残します。 ```text - Fanout task ──► ● ● ● ● ══barrier══► synthesize - Adversarial worker ──► verifier×N → 残るものだけ残す -``` - -sample `review-changes` = **Fanout** の中に **Adversarial**: 次元ごとに `pipeline(audit, verify)`、`verify` 内で `parallel` 検証、`isReal` だけ残す。 - -```text - correctness ── audit ── verify ──┐ - security ── audit ── verify ──┤── confirmed - performance ── audit ── verify ──┤ - style ── audit ── verify ──┘ + correctness ── 監査 ── 検証 ──┐ + security ── 監査 ── 検証 ──┤── confirmed + performance ── 監査 ── 検証 ──┤ + style ── 監査 ── 検証 ──┘ ``` ```python -# code.py から(抜粋) +# code.py 抜粋 — 形だけ見ればよい async def sample_workflow(ctx, args): ctx.phase("Review") results = await ctx.pipeline(DIMENSIONS, audit, verify) @@ -116,71 +77,63 @@ async def sample_workflow(ctx, args): return {"confirmed": confirmed} ``` -艦隊は早止めできず、作者は審判にならず、トポロジは chat の毎ターンで書き換わらない。 +艦隊は早逃げできず、著者は自分の審判にならず、トポロジも疲れたチャットのたびに書き換わりません。
-六パターン格子 + プリミティブ対応 +よくある六つの形(パターン庫) -![Six Workflow Patterns](images/six-workflow-patterns.png) +![六種 Workflow モード](images/six-workflow-patterns.png) -| パターン | プリミティブ速写 | 使わないとき | -|----------|------------------|--------------| -| Classify-And-Act | `agent` → 分岐 → `agent` | 全部同じ扱いでよい | -| Fanout-And-Synthesize | `pipeline` / `parallel` → マージ | 一回で収まる | -| Adversarial Verification | 生成 → `parallel(verify)` → フィルタ | 間違っても安い | -| Generate-And-Filter | `parallel(gens)` → フィルタ | 答え空間がもともと狭い | -| Tournament | 一対一の審判 `agent` | 明確な尺度で一発選定可 | -| Loop Until Done | `while` + 停止 + `budget` | 仕事量が既知 | +| パターン | 人の言葉 | 原語のスケッチ | +|------|------|----------| +| Classify-And-Act | 仕分けしてから適任へ | `agent` → 分岐 → `agent` | +| Fanout-And-Synthesize | 分けてやり、またまとめる | `pipeline` / `parallel` → 統合 | +| Adversarial Verification | 狐に鶏小屋を採点させない | 産出 → `parallel(verify)` → フィルタ | +| Generate-And-Filter | まず多く作り、それから篩 | `parallel(gens)` → フィルタ | +| Tournament | 一対一で優勝を決める | 審判 `agent` | +| Loop Until Done | 「まだ新しい?」なら続ける | `while` + 停止 + `budget` | + +`review-changes` ≈ Fanout + Adversarial。調査系はよく 分配 → フィルタ → 検証 → 統合 と積みます。 + +
+ +
+動的 / 保存済み / 静的と公式原語図 ```python -# teaching sketch -kind = await ctx.agent("このチケットを分類", schema=KIND) -if kind["type"] == "billing": - return await ctx.agent("請求を処理…") +# 教材スケッチ +Workflow({ "name": "review-changes", "args": { "changes": "..." } }) +# Claude Code はさらに: script | scriptPath | resumeFromRunId ``` +![Workflow 原語](images/workflow-primitives.png) +
-信頼できない入力: quarantine +信頼できない入力:読み書きを隔離 -チケットを*読む* agent が PR を開く鍵まで持つべきではない。reader は読み取りのみ → 構造化サマリ;trusted actor はサマリだけ見て動く。 +チケットを読む agent が、同時に PR を開ける鍵を持ってはいけません。読み手は読むだけ → 要約。信頼側は要約だけ見て動きます。 ```text - backlog(非信頼)→ [quarantine: readers → 重複除去 → summary] → [trusted: actor] + バックログ → [隔離: 読 / 重複除去 / 要約] → [信頼: 実行] ``` -![Quarantine triage](images/quarantine-triage.png) - -*高権限ツールは trusted 側。バックログが眠らないなら `/loop` と組む。* +![隔離分流](images/quarantine-triage.png)
-
-s15 への掛け方 - -s15 がホストループのまま;s16 は `Workflow` ツールを足すだけ。製品ではバックグラウンド可;ティーチング CLI は前景の `demo` / `resume` で段階とキャッシュを見せる。 - -
- -## 隣人と、使わないとき - -計画を握るのは誰か。s06 一回委譲、s13 メール箱の仲間、s15 単一ループ、**s16 スクリプト + journal**、s17 は「全体ゴールは終わったか」。 - -普通のコーディングなら s15 一回、または正直な s06 で十分なことが多い。Workflow は token と調整コスト——構造が単一コンテキストより長生きすべきときだけ。 +計画を握るのは誰か。s06 は一回きりの委譲、s13 はメール箱つきの仲間、s15 は単一ループのチャット、**s16 はスクリプト + journal**、s17 は入り口で「全体は終わったか」と聞きます。普通の数ファイルの修正なら s15 か一つの s06 で足りることが多い。Workflow は token と調整のコストが要ります——**構造が一度の会話より長生きしなければならない**ときだけ手を伸ばしてください。 ## 試してみる ```bash -python s16_workflow_runtime/code.py # s15 ホスト + Workflow(本番 API) -python s16_workflow_runtime/code.py demo # 固定データ;段階を見る -python s16_workflow_runtime/code.py resume # 同じ runId;キャッシュ命中を期待 +python s16_workflow_runtime/code.py demo +python s16_workflow_runtime/code.py resume ``` -完全再開では `agents=0 tokens=0` になるはず。 +一回目は Review → Verify を見てください。同じ run の二回目は `cached` がほとんど(理想は `agents=0 tokens=0`)。完全なホストに載せるなら引数なしで `code.py` を。 -## 次へ +s15 はあいかわらずそのループです。ここに増えるのは `Workflow` ツールだけ。[s17](../s17_goal_loop/) は別の問いをします。もう止まっていい? -s16 はバッチの回し方。[s17 Goal Loop](../s17_goal_loop/) は聞く:止めるか、もう一周か。 - - + diff --git a/s16_workflow_runtime/README.md b/s16_workflow_runtime/README.md index e1e965cd..d7424f6d 100644 --- a/s16_workflow_runtime/README.md +++ b/s16_workflow_runtime/README.md @@ -1,104 +1,65 @@ -# s16: Workflow Runtime — Put the Recipe in Code +# s16: Workflow Runtime — put the plan in code [English](README.md) · [中文](README.zh.md) · [日本語](README.ja.md) -s01 → ... → s14 → [s15](../s15_integrated_harness/) → `s16` → [s17](../s17_goal_loop/) +[s15](../s15_integrated_harness/) → `s16` → [s17](../s17_goal_loop/) -> Workflow = orchestration written as code. Script owns topology; the model judges each step. +> *Don't keep the plan only in chat.* The script owns order; the model owns each judgment. > -> **Harness layer**: Orchestration — a multi-agent script above the single-agent loop. -> -> Trust the model. Engineer the harness. Workflows take that one floor up. - ---- +> **Harness layer**: orchestration — a multi-agent script on top of the single agent loop. ## Problem -On long jobs, plan and action share one chat: stop early, grade your own homework kindly, lose quiet constraints after compressions. Soft chat memory is a weak place for parallelism, stable result shapes, and resume. +You already know how to let a model read files, edit code, and read errors in one loop. Some jobs, though, have an order you **already know**: review by dimension, then adversarial checks, then merge. If that order lives only in chat, the model stops halfway and calls it done, grades its own homework too kindly, and after a few compressions even "don't touch X" disappears. -Nudging turn-by-turn is like texting the chef every ten seconds. A **workflow** is a recipe the kitchen can follow. +Soft conversation can't carry parallelism, stable result shapes, or crash-and-resume. You don't need a chattier model. You need **orchestration written down**. -## Idea - -Helpers (subagents) still think. The **script** owns loops, fan-out, and merge. Intermediates live in variables and a journal — not the conversation. - -**Orchestration moves from intelligence to structure.** +## Solution ```text - messages[] ──► Workflow(...) ──► tool_result - │ - ▼ - script owns: agent / parallel / pipeline - │ - ▼ - variables + journal + your chat ──► Workflow(...) ──► one result back + │ + ▼ + script: agent / pipeline / parallel + │ + ▼ + vars + journal (keep intermediates here, not in the thread) ``` -One `Workflow` tool call starts the run; one result comes back when it finishes. +Subagents still think; the **script** owns loops, fan-out, and merge. Intermediates live in variables and a journal, not the host dialogue. -
-Runtime overview diagram +One line: **move orchestration from intelligence to structure.** -![Workflow Runtime Overview](images/workflow-runtime-overview.svg) +![static harness vs dynamic workflow](images/dynamic-vs-static.png) -
+*Left: a generic fixed pipeline. Right: a harness cut for this task.* -## Two doors +Claude Code has two doors: **dynamic** — the model writes JS for this task (`script` / `scriptPath`); **saved** — rerun a good script with `name` + `args`. Outside sits static SDK / `claude -p` orchestration. This lesson is a **Python teaching runtime** (no JS VM): same ideas, demo on the saved door. In the product the model can submit scripts — we just don't run JS here. -- **Dynamic**: model writes a JS orchestration script for *this* task (`script` / `scriptPath`). -- **Saved**: good script under `.claude/workflows/`; call by `name` + `args`. -- **Static** (cousin outside): Agent SDK / `claude -p` written ahead — usually more generic. +## How it works -![Static vs dynamic](images/dynamic-vs-static.png) - -*Left: fixed pipeline → generic report. Right: cut for your code → a specific recommendation.* - -This chapter is a **Python teaching runtime** (no JS VM). Concepts map to Claude Code; the demo uses the Saved door. In the product the model can submit executable scripts — we just skip embedding a JS interpreter here. - -```python -# teaching sketch — not the full schema -Workflow({ "name": "review-changes", "args": { "changes": "..." } }) -# Claude Code also accepts: script | scriptPath | resumeFromRunId -``` - -## Three verbs +**1. Three verbs** ```text - agent one helper, one job (optional schema → validated JSON) - pipeline each item walks stages alone (default — no barrier) - parallel wait for every tray (barrier — use sparingly) + agent one helper, one job (optional schema → JSON you can pass on) + pipeline each item walks stages on its own (default; no barrier) + parallel wait for every result (barrier; use sparingly) ``` -On failure the fleet continues: `parallel` → `null` in that slot; `pipeline` drops **that item** and its later stages. Filter before merge. +On failure: a `parallel` slot becomes `null`; `pipeline` drops that item. The fleet does not sink. Filter before you merge. -Resume: journal records calls in invocation order; replay the **longest unchanged prefix**, then run live. Real JS runtimes ban `Date.now()` / `Math.random()`. This demo does not fully sandbox that — write deterministic scripts anyway. +**2. Resume from a notebook, not chat memory** + +The journal records `agent()` calls in **invocation order**. Resume replays the longest unchanged prefix; after the first change, everything runs live. Real JS runtimes ban `Date.now()` / `Math.random()` so the notebook can match — keep teaching scripts deterministic too. ```text journal [A] [B] [C] [D] - resume hit hit ✂ live + resume hit hit ✂ live ``` -
-Official primitive card + quieter verbs +**3. One sample: fan-out + adversarial** -![Workflow primitives](images/workflow-primitives.png) - -*`agent`; `parallel` (barrier) vs `pipeline` (streaming stages). Claude Code also has `model` / `isolation` / `agentType`; teaching surface is smaller.* - -Quieter: `phase`, `log`, nested `workflow`, `args`, `budget`. - -
- -## Two shapes + one sample - -Feel two first (full six-pattern grid in the fold below): - -```text - Fanout task ──► ● ● ● ● ══barrier══► synthesize - Adversarial worker ──► verifier×N → keep what still stands -``` - -Sample `review-changes` = **Fanout** with **Adversarial** inside: `pipeline(audit, verify)` per dimension; `parallel` verifiers; keep only `isReal`. +`review-changes` is not "one pattern". It is **Fanout** with **Adversarial** inside: `pipeline(audit, verify)` per dimension, then `parallel` verifiers, keep only findings that still stand. ```text correctness ── audit ── verify ──┐ @@ -108,7 +69,7 @@ Sample `review-changes` = **Fanout** with **Adversarial** inside: `pipeline(audi ``` ```python -# from code.py (abbreviated) +# from code.py — the shape is the point async def sample_workflow(ctx, args): ctx.phase("Review") results = await ctx.pipeline(DIMENSIONS, audit, verify) @@ -116,71 +77,63 @@ async def sample_workflow(ctx, args): return {"confirmed": confirmed} ``` -The fleet cannot stop early, the author is not the judge, and topology is not rewritten every chat turn. +The fleet can't stop early, authors don't referee themselves, and topology isn't rewritten by a tired chat turn.
-Six-pattern grid + primitive map +Six common shapes (pattern toolbox) -![Six Workflow Patterns](images/six-workflow-patterns.png) +![Six workflow patterns](images/six-workflow-patterns.png) -| Pattern | Primitive sketch | Skip when… | -|---------|------------------|------------| -| Classify-And-Act | `agent` → branch → `agent` | Same treatment for every item | -| Fanout-And-Synthesize | `pipeline` / `parallel` → merge | One pass already fits | -| Adversarial Verification | produce → `parallel(verify)` → filter | A wrong answer is cheap | -| Generate-And-Filter | `parallel(gens)` → filter | Answer space is already tiny | -| Tournament | pairwise judge `agent`s | A clear rubric picks a winner | -| Loop Until Done | `while` + stop + `budget` | Work size is known | +| Pattern | In plain words | Primitives | +|------|------|----------| +| Classify-And-Act | Sort, then hand off | `agent` → branch → `agent` | +| Fanout-And-Synthesize | Split, then merge | `pipeline` / `parallel` → synthesize | +| Adversarial Verification | Don't let the fox grade the henhouse | produce → `parallel(verify)` → filter | +| Generate-And-Filter | Many drafts, then a ruler | `parallel(gens)` → filter | +| Tournament | Pairwise to a winner | judge `agent` | +| Loop Until Done | Keep going while "anything new?" | `while` + stop + `budget` | + +`review-changes` ≈ Fanout + Adversarial. Research stacks often go fan-out → filter → verify → synthesize. + +
+ +
+Dynamic / saved / static & official primitives ```python # teaching sketch -kind = await ctx.agent("classify this ticket", schema=KIND) -if kind["type"] == "billing": - return await ctx.agent("handle billing…") +Workflow({ "name": "review-changes", "args": { "changes": "..." } }) +# Claude Code also accepts: script | scriptPath | resumeFromRunId ``` +![Workflow primitives](images/workflow-primitives.png) +
-Untrusted input: quarantine +Untrusted input: quarantine reads -The agent that *reads* tickets should not also hold PR keys. Readers stay read-only → structured summary; a trusted actor acts on the summary only. +The agent that reads tickets should not also hold the keys to open a PR. Readers only read → summary; the trusted side acts on the summary. ```text - backlog (untrusted) → [quarantine: readers → dedupe → summary] → [trusted: actor] + backlog → [quarantine: read / dedupe / summarize] → [trusted: act] ``` -![Quarantine triage](images/quarantine-triage.png) - -*High-privilege tools stay on the trusted side. Pair with `/loop` if the backlog never sleeps.* +![quarantine triage](images/quarantine-triage.png)
-
-How this hangs on s15 - -s15 stays the host loop; s16 only adds a `Workflow` tool. Product runs can be background; the teaching CLI keeps `demo` / `resume` in the foreground for phases and cache hits. - -
- -## Neighbors & when not - -Who holds the plan? s06 one-shot delegate, s13 mailbox peers, s15 one loop, **s16 script + journal**, s17 asks “is the whole goal done?” - -Ordinary coding: one s15 turn or one honest s06 often wins. Workflows cost tokens and coordination — reach for them when structure must outlast a single context. +Who owns the plan? s06 is one-shot dispatch, s13 is teammates with a mailbox, s15 is one chat loop, **s16 is script + journal**, s17 asks at the door whether the whole goal is done. For ordinary file edits, s15 or one s06 is often enough. Workflows cost tokens and coordination — reach for them when **structure must outlive a single conversation**. ## Try it ```bash -python s16_workflow_runtime/code.py # s15 host + Workflow (real API) -python s16_workflow_runtime/code.py demo # fixed fixture; watch phases -python s16_workflow_runtime/code.py resume # same runId; expect cache hits +python s16_workflow_runtime/code.py demo +python s16_workflow_runtime/code.py resume ``` -A full resume should show `agents=0 tokens=0`. +First run: watch Review → Verify. Second run on the same id: expect mostly `cached` (ideally `agents=0 tokens=0`). For the full host loop, run `code.py` with no args. -## Next +s15 is still the loop; this chapter only adds a `Workflow` tool. [s17](../s17_goal_loop/) asks a different question: should we stop? -s16 is how a batch runs. [s17 Goal Loop](../s17_goal_loop/) asks: stop, or take another turn? - - + diff --git a/s16_workflow_runtime/README.zh.md b/s16_workflow_runtime/README.zh.md index e5c0974e..3822c0ff 100644 --- a/s16_workflow_runtime/README.zh.md +++ b/s16_workflow_runtime/README.zh.md @@ -1,104 +1,65 @@ -# s16: Workflow Runtime — 把菜谱写进代码 +# s16: Workflow Runtime — 把编排写进代码 [English](README.md) · [中文](README.zh.md) · [日本語](README.ja.md) -s01 → ... → s14 → [s15](../s15_integrated_harness/) → `s16` → [s17](../s17_goal_loop/) +[s15](../s15_integrated_harness/) → `s16` → [s17](../s17_goal_loop/) -> Workflow = 写进代码的编排。脚本管拓扑,模型管每一步判断。 +> *计划别只活在嘴上。* 脚本管先后,模型管每一步判断。 > -> **Harness 层**: 编排 — 单 agent 循环之上再跑多 agent 脚本。 -> -> 信任模型,工程化 harness。Workflow 把这句话往上提一层。 - ---- +> **Harness 层**: 编排 — 在单 agent 循环之上,再跑一套多 agent 脚本。 ## 问题 -长任务里,计划和动手挤在同一段 chat:做到一半就宣布完工、自己批改自己偏甜、压缩几轮后约束悄悄丢了。并行、稳定结果形状、崩了续跑——软对话记忆扛不住。 +你已经会让模型在一个循环里读文件、改代码、看报错。可有些活你其实**早就知道先后顺序**:先分维度审,再找人挑刺,最后汇总。若顺序只靠聊天记住,模型会做到一半喊完工,自己批改自己偏甜,压几轮上下文后连「别动 X」都丢了。 -一轮轮催进度,像每隔十秒给厨师发短信。**Workflow** 是厨房能照着做的菜谱。 +软对话扛不住并行、稳定结果形状,也扛不住崩了接着跑。你需要的不是更会聊天的模型,是一份**写下来的编排**。 -## 想法 - -帮手(子 agent)仍负责想;**脚本**管循环、分发、合并。中间结果进变量和 journal,不进对话。 - -**编排从「智力」挪到「结构」。** +## 解决方案 ```text - messages[] ──► Workflow(...) ──► tool_result - │ - ▼ - 脚本管拓扑:agent / parallel / pipeline - │ - ▼ - 变量 + journal + 你的对话 ──► Workflow(...) ──► 一条结果回来 + │ + ▼ + 脚本:agent / pipeline / parallel + │ + ▼ + 变量 + journal(半成品放这儿,不塞群聊) ``` -一次 `Workflow` 工具调用开跑;菜谱做完,一条结果回来。 +帮手(子 agent)仍负责想;**脚本**管循环、分发、合并。中间结果进变量和 journal,不进主对话。 -
-运行时总览图 +一句话:**编排从「智力」挪到「结构」。** -![Workflow Runtime 总览](images/workflow-runtime-overview.svg) +![静态 harness vs 动态 workflow](images/dynamic-vs-static.png) -
+*左:通吃的固定流水线。右:为这次任务现裁的编排。* -## 两扇门 +Claude Code 里有两扇门:**动态**——模型为这次任务写 JS(`script` / `scriptPath`);**已保存**——好脚本用 `name` + `args` 再跑。门外还有用 SDK 事先写死的静态编排。本章是 **Python 教学 runtime**(不嵌 JS):思想对齐,演示走「已保存」门。模型在产品里本来就能交脚本——我们只是不在这里跑 JS。 -- **动态**:模型为*这次*任务写 JS 编排(`script` / `scriptPath`)。 -- **已保存**:好脚本进 `.claude/workflows/`,用 `name` + `args` 再调。 -- **静态**(门外表亲):SDK / `claude -p` 事先写好,偏通用。 +## 工作原理 -![静态 vs 动态](images/dynamic-vs-static.png) - -*左:固定流水线 → 泛报告。右:按你的代码现裁 → 具体建议。* - -本章是 **Python 教学 runtime**(不嵌 JS VM)。概念对齐 Claude Code;演示走「已保存」门。模型在产品里本来就能交可执行脚本——我们只是不在这里跑 JS。 - -```python -# 教学示意 — 不是完整 schema -Workflow({ "name": "review-changes", "args": { "changes": "..." } }) -# Claude Code 还接受:script | scriptPath | resumeFromRunId -``` - -## 三个动词 +**1. 三个动词** ```text - agent 一个帮手,一件事(可带 schema → 校验 JSON) - pipeline 每个 item 自己走阶段(默认,不等齐) - parallel 等齐再往下(屏障,少用) + agent 一个帮手,一件事(可带 schema,拿到能往下传的 JSON) + pipeline 每个 item 自己走完各阶段(默认,不等齐) + parallel 等所有结果齐了再往下(屏障,少用) ``` -失败时舰队继续:`parallel` 槽位变 `null`;`pipeline` 丢掉那个 item 及其后续 stage。合并前先过滤。 +谁失手:`parallel` 那个槽变成 `null`;`pipeline` 丢掉那个 item。舰队不整船沉。合并前先过滤。 -续跑:journal 按召唤顺序记;回放**最长未改前缀**,第一个改动之后全实跑。真 JS 运行时禁 `Date.now()` / `Math.random()`;本 demo 不完整沙箱——脚本仍写成确定性的。 +**2. 续跑靠本子,不靠聊天记忆** + +journal 按 `agent()` **召唤顺序**记账。续跑回放最长未改前缀;碰到第一处改动,后面全实跑。真 JS 运行时禁 `Date.now()` / `Math.random()`,免得本子对不齐——教学脚本也请写成确定性的。 ```text journal [A] [B] [C] [D] 续跑 命中 命中 ✂ 实跑 ``` -
-官方原语卡片 + 更轻的动词 +**3. 一个样本:分发 + 对抗** -![Workflow 原语](images/workflow-primitives.png) - -*`agent`;`parallel`(屏障)vs `pipeline`(流式阶段)。Claude Code 还有 `model` / `isolation` / `agentType`;教学面更小。* - -更轻:`phase`、`log`、嵌一层 `workflow`、`args`、`budget`。 - -
- -## 两种形状 + 一个样本 - -先摸两种(完整六模式见下方折叠): - -```text - Fanout task ──► ● ● ● ● ══屏障══► synthesize - Adversarial worker ──► verifier×N → 只留站得住的 -``` - -样本 `review-changes` = **Fanout** 里嵌 **Adversarial**:多维度 `pipeline(audit, verify)`,`verify` 里 `parallel` 挑刺,过滤后只留 `isReal`。 +`review-changes` 不是「一种模式」,是 **Fanout** 里嵌 **Adversarial**:多维度 `pipeline(audit, verify)`,验证里再 `parallel` 挑刺,只留站得住的 finding。 ```text correctness ── 审计 ── 验证 ──┐ @@ -108,7 +69,7 @@ Workflow({ "name": "review-changes", "args": { "changes": "..." } }) ``` ```python -# 来自 code.py(节选) +# code.py 节选 — 形状就这些 async def sample_workflow(ctx, args): ctx.phase("Review") results = await ctx.pipeline(DIMENSIONS, audit, verify) @@ -116,71 +77,63 @@ async def sample_workflow(ctx, args): return {"confirmed": confirmed} ``` -舰队不能早停、作者不当裁判、拓扑不靠 chat 每轮改写。 +舰队不能早停,作者不当裁判,拓扑也不靠 chat 每轮改写。
-六种模式网格 + 原语对照 +六种常见形状(模式库) ![六种 Workflow 模式](images/six-workflow-patterns.png) -| 模式 | 原语速写 | 什么时候别用 | -|------|----------|--------------| -| Classify-And-Act | `agent` → 分支 → `agent` | 每件都该同样处理 | -| Fanout-And-Synthesize | `pipeline` / `parallel` → 合并 | 一趟已装得下 | -| Adversarial Verification | 产出 → `parallel(verify)` → 过滤 | 答错很便宜 | -| Generate-And-Filter | `parallel(gens)` → 过滤 | 答案空间本来就小 | -| Tournament | 两两裁判 `agent` | 清晰量尺一趟能选 | -| Loop Until Done | `while` + 停止 + `budget` | 工作量已知 | +| 模式 | 人话 | 原语速写 | +|------|------|----------| +| Classify-And-Act | 先分拣再交给对的人 | `agent` → 分支 → `agent` | +| Fanout-And-Synthesize | 拆开干,再合并 | `pipeline` / `parallel` → 汇总 | +| Adversarial Verification | 别让狐狸评鸡窝 | 产出 → `parallel(verify)` → 过滤 | +| Generate-And-Filter | 先多产再筛 | `parallel(gens)` → 过滤 | +| Tournament | 两两比出冠军 | 裁判 `agent` | +| Loop Until Done | 「还有新发现?」就继续 | `while` + 停止 + `budget` | -```python -# 教学示意 -kind = await ctx.agent("给工单分类", schema=KIND) -if kind["type"] == "billing": - return await ctx.agent("处理账单…") -``` +`review-changes` ≈ Fanout + Adversarial。研究类常叠:分发 → 过滤 → 验证 → 汇总。
-不可信输入:隔离分流(quarantine) +动态 / 已保存 / 静态 & 官方原语图 -读工单的 agent 不该同时握开 PR 的钥匙。读者只读 → 结构化摘要;受信任 actor 只看摘要行动。 +```python +# 教学示意 +Workflow({ "name": "review-changes", "args": { "changes": "..." } }) +# Claude Code 还接受:script | scriptPath | resumeFromRunId +``` + +![Workflow 原语](images/workflow-primitives.png) + +
+ +
+不可信输入时:隔离读写 + +读工单的人,不该同时握着开 PR 的钥匙。读者只读 → 摘要;受信任的一侧只看摘要行动。 ```text - 积压(不可信)→ [隔离区: readers → 去重 → 摘要] → [受信任: actor] + 积压 → [隔离区: 读 / 去重 / 摘要] → [受信任: 行动] ``` ![隔离分流](images/quarantine-triage.png) -*高权限工具住在受信任一侧。积压睡不着时可配 `/loop`。* -
-
-怎样挂在 s15 上 +谁握计划?s06 一次性派工,s13 邮箱同伴,s15 单循环聊天,**s16 是脚本 + journal**,s17 在门口问整件事做完没有。普通改几个文件:s15 或一个 s06 往往够。Workflow 贵在 token 和协调——**结构必须比单次对话活得更久**时再用。 -s15 仍是宿主循环;s16 只多一个 `Workflow` 工具。产品里可后台跑;教学 CLI 用前台 `demo` / `resume` 看阶段和缓存。 - -
- -## 邻居 & 何时别用 - -谁握计划?s06 一次性委派、s13 邮箱同伴、s15 单循环、**s16 脚本 + journal**、s17 门口问「整目标做完了吗」。 - -普通改代码:s15 一轮或一个 s06 往往够。Workflow 要 token 和协调——结构必须比单个上下文活得更久时再用。 - -## 试一下 +## 试一试 ```bash -python s16_workflow_runtime/code.py # s15 宿主 + Workflow(真实 API) -python s16_workflow_runtime/code.py demo # 固定数据;看阶段 -python s16_workflow_runtime/code.py resume # 同一 runId;期待缓存命中 +python s16_workflow_runtime/code.py demo +python s16_workflow_runtime/code.py resume ``` -完整续跑应看到 `agents=0 tokens=0`。 +第一次看 Review → Verify;第二次同一 run,agent 应大量 `cached`(理想情况 `agents=0 tokens=0`)。想挂进完整宿主:不加参数直接跑 `code.py`。 -## 接下来 +s15 还是那个循环;这里只是多了一个 `Workflow` 工具。[s17](../s17_goal_loop/) 问另一个问题:该停了吗? -s16 讲一批活怎么跑。[s17 Goal Loop](../s17_goal_loop/) 问:该停,还是再来一轮? - - +