Weave Anthropic harness essay into s16 teaching spine

Ground the progressive chapter in why custom harnesses exist, the three
single-window failure modes, dynamic vs static, tasteful patterns, when
not to use workflows, and sharp neighbors (s06/s13/s15/s17).

Co-authored-by: Xinlu Lai <CrazyBoyM@users.noreply.github.com>
This commit is contained in:
Cursor Agent
2026-08-12 13:47:06 +00:00
parent e28bec6dd4
commit bab23aabf7
3 changed files with 191 additions and 53 deletions

View File

@@ -7,6 +7,8 @@ s01 → ... → s14 → [s15](../s15_integrated_harness/) → `s16` → [s17](..
> *“一轮轮聊天像每隔十秒给厨师发一条短信。Workflow 是厨房能照着做的菜谱。”*
>
> **Harness 层**: 编排 — 在单 agent 循环之上,跑一套多 agent 脚本。
>
> 信任模型,工程化 harness。Workflow 就是编排层上的 harness 工程。
---
@@ -14,40 +16,52 @@ s01 → ... → s14 → [s15](../s15_integrated_harness/) → `s16` → [s17](..
普通“模型当总指挥”的对话就是这样。**Workflow** 是写好的菜谱厨房runtime按谱做帮手子 agent负责判断中间结果放在台面上的碗里 —— 不塞进群聊记录。
## 问题在哪
## 为什么需要 harness
从 s01 到 s15每一轮都由模型决定下一步调用什么工具。当“下一步取决于刚才发现了什么”时这很合适
默认的 Claude Code harness 很擅长“写代码那种形状”的工作:改、跑、看报错、再试 —— 都在同一个循环里
有些任务的形状事先就知道:
有些活需要**叠一层定制 harness**深度调研、安全分析、agent teams、大规模 code review。你可以事先用 SDK 手写那层 harness也可以 —— 这就是动态的想法 —— 让 Claude **为这次任务现场写一个 harness**,跑完,好用的再存下来。
- 按多个维度审查很多文件
- 先调研,再验证,再合并
- 用同一种方式迁移 N 个模块
课程的口号往上提一层:每一步里信任模型;步骤之间的结构,靠工程来定。
如果模型只能把计划“记”在 `messages[]` 里,会发生三件事:编排噪音占满上下文、中途计划漂移、崩了就得把做完的活重做一遍。
## 问题:一个窗口,三种走偏
你需要并行、稳定的结果形状,以及能续跑。把这三样只寄存在对话历史里,太脆弱
从 s01 到 s15模型在**同一个**上下文里既规划又执行。当“下一步取决于刚才发现了什么”时,这很合适。当任务又长、又要大规模并行、又要求死板结构、或需要对抗验证时,就会变脆
Claude Code 的设计者给单窗口里常见的三种失败起了名字。用大白话说:
| 失败模式 | 感觉起来像什么 |
|----------|----------------|
| **Agentic laziness偷懒收工** | 五十项审查做到三十五,就说“做完了” |
| **Self-preferential bias自我偏爱** | 让它检查自己的结论时,总觉得自己更对 —— 狐狸给鸡窝打分 |
| **Goal drift目标漂移** | 原来的“别动 X”在多轮对话和压缩之后渐渐淡掉 |
对话历史也很难同时扛住并行、稳定的结果形状、以及续跑。审查很多文件、先调研再验证、按同一方式迁移 N 个模块 —— 这些活的**形状**事先就知道,更需要那三样。
## 一句话说清想法
**把计划写进代码。** 子 agent 仍然负责判断;脚本负责循环、分发和合并。中间结果存在变量里,不进对话。
**把编排从“靠聪明”挪到“靠结构”。**
子 agent 仍然负责判断 —— 各自干净的上下文、专注的目标。**脚本**负责循环、分发和合并。中间结果存在变量(和 journal不进对话。分开的帮手 + 脚本掌握的控制流,就是对抗偷懒、自我检查偏差和漂移的办法。
![Workflow Runtime 总览](images/workflow-runtime-overview.svg)
一次 `Workflow` 工具调用启动这次脚本运行。运行中会发出生命周期和进度事件;最后一条工具结果带回启动信息、结果和任务状态。
## 两扇门
## 两扇门 — 以及动态 vs 静态
Claude Code 对“工作流怎么启动”是诚实的
Claude Code 用两扇门走进同一间厨房
| 门 | 你传什么 | 什么时候用 |
|----|----------|------------|
| **动态Dynamic** | 一段编排用的 JavaScript`script`,或之后的 `scriptPath` | 模型为**这次任务**现写菜谱 |
| **已保存Saved** | `name` + `args` | 好用的菜谱放进例如 `.claude/workflows/`,按名字再跑 |
同一间厨房。动态是“现在写菜谱”已保存是“从卡片盒里抽一张”。
同一间厨房。动态是“现在写菜谱”已保存是“从卡片盒里抽一张”—— 一次漂亮动态运行留下来的可复用残渣
**本课是一个 Python 教学运行时。** 用同样的想法,但每行你都能读懂。演示按名字注册一个已保存的 workflow概念和 Claude Code 的脚本世界一一对应。我们**不会**再说“模型不能提交可执行代码”——那是对 Claude Code 的误述。这里只是不嵌入完整的 JS 解释器
本课之外还有表亲:**静态** harness事先写好的 Agent SDK / `claude -p` 编排)。静态的要覆盖所有边角,所以往往更泛用。动态的是为*这次*任务量身定做;合身了再存成 saved
**本课是一个 Python 教学运行时。** 同样的想法,每行你都能读懂。演示按名字注册一个已保存的 workflow概念和 Claude Code 的脚本世界一一对应。我们**不会**再说“模型不能提交可执行代码”——那是对 Claude Code 的误述。这里只是不嵌入完整的 JS 解释器。
```python
# 教学适配器已保存这扇门name + args
@@ -90,6 +104,18 @@ results = await ctx.pipeline(DIMENSIONS, audit, verify)
confirmed = [f for r in results if r for f in r["confirmed"]]
```
## 有品味的模式(不是清单倾销)
把模式想成菜谱风格。示例 `review-changes` 主要用了三种:
| 模式 | 大白话 | 在示例里 |
|------|--------|----------|
| **Fan-out-and-synthesize分发再汇总** | 拆开干,每人一张干净桌子,再合并 | 四个维度在 `pipeline` 里审计,最后合成确认列表 |
| **Adversarial verification对抗验证** | 第二个帮手专门来挑刺 | 每条 finding 先过 verify agent 才作数 |
| **Generate-and-filter生成再过滤** | 先产出候选,只留通过检验的 | findings 进来 → 只留 `isReal` |
同一工具箱里还有别的风格,以后会遇到:**classify-and-act**(按类型分流)、**tournament**(比武再选冠军)、**loop-until-done**(直到没有新发现再停)。只有当额外成本能换来更清楚或更稳妥的结果时,才上模式。
## 让答案机器能读
如果帮手回来写散文,下一阶段就很难把 finding 和 verdict 一一对应。传入 `schema`:运行时要求 JSON、做校验不对就**重试一次**。再不对,这次调用报错(见下面的空值隔离)。
@@ -142,7 +168,7 @@ journal: [A ✓] [B ✓] [C ✓] [D ✓]
## 跟着示例走:`review-changes`
四个审查维度走同一条两阶段路径:
四个审查维度走同一条两阶段路径 —— 先分发,再对抗验证,再过滤
```text
correctness ── 审计 ── 验证 ──┐
@@ -151,8 +177,8 @@ performance ── 审计 ── 验证 ──┤
style ── 审计 ── 验证 ──┘
```
1. **Review** — 每个维度的审计员返回结构化 findings。
2. **Verify** — 每条 finding 交给对抗性检查(在 verify 阶段里用 `parallel`)。
1. **Review** — 每个维度的审计员返回结构化 findings(干净桌子 → 少串味)
2. **Verify** — 每条 finding 交给对抗性检查(在 verify 阶段里用 `parallel`,作者不当裁判
3. 只保留被标成真实的问题,再按严重程度排序。
```python
@@ -177,6 +203,26 @@ s15 仍是宿主循环。s16 只多一个工具:`Workflow`。模型(或你
主循环不会变成 workflow 引擎。它只是多借一把工具,就像借 `bash``task` 一样。
## 邻居们:谁握着计划?
Workflow 不是“多派几个 agent”。它改的是**谁拥有拓扑结构**。
| 邻居 | 谁握着计划 | 中间结果住哪 | 最适合 |
|------|------------|--------------|--------|
| [s06 子 Agent](../s06_subagent/) | 模型,一次性 | 除最终摘要外丢掉 | 隔离一个脏的子任务 |
| [s13 Agent Teams](../s13_agent_teams/) | Lead 模型逐轮 + 邮箱 | 共享任务 / 消息 | 长跑同伴、偏人类协作 |
| [s15 Agent Harness 集成](../s15_integrated_harness/) | 模型在一个循环里 | 对话 `messages[]` | 累积型 coding agent |
| **s16 Workflow** | **脚本** | **脚本变量 + journal** | 已知 / 大规模结构化分发 + 验证 |
| [s17 Goal Loop](../s17_goal_loop/) | 停止边界上的判断器 | 对话当证据 | “整个目标做完了吗?” |
更便宜的替代方案经常就够用skill / prompt 当软计划、一小段多 agent 闲聊、手写静态 SDK 编排,或者干脆更大的单轮模型调用。当结构必须比单个上下文活得更久时,再伸手去拿 workflow —— 不是因为“专家团”听起来很酷。
## 什么时候*别*用 workflow
Workflow 要花 token也有协调成本。大多数普通写代码的活**不需要**五人评审团。
问问自己:这件事真的需要更多算力和定制 harness 吗?如果普通的 s15 一轮(或一个 s06 子 agent就够就停在那儿。克制也是设计思想的一部分 —— 并行和分工必须赚回自己的成本。
## 试一下
```bash
@@ -202,6 +248,6 @@ python s16_workflow_runtime/code.py resume # 同一个 runId前缀应全部
**s16 = 一批活怎么跑。s17 = 整个目标算不算做完。**
[s17 Goal Loop](../s17_goal_loop/) 会问一个独立判断器:该停,还是再来一轮?
[s17 Goal Loop](../s17_goal_loop/) 会问一个独立判断器:该停,还是再来一轮?可重复的 workflow 若还需要硬性完成条件,可以和它配对。
<!-- translation-sync: zh@v11, en@v11, ja@v11 -->
<!-- translation-sync: zh@v12, en@v12, ja@v12 -->