从0打造编码Agent 阅读 + 实验 ≈ 35 分钟 页内实验 ×3
A0 工作流还是 Agent:什么时候需要 agent,什么时候一段代码就够? 第 2 课

多加几步、加分支,能覆盖所有情况吗?

加步骤、加分支
第 1 课的两种失败各补一条路:没过就带着新报错再来,格式不对就拒绝写回。Anthropic 的五种 workflow 模式就是这样一个工具箱,每条路都在设计时画好。可 agent 真实走出来的路,是在运行时一条条长出来的:同一配置重跑 4 次 parse_date(三个 seed,seed 1 跑了两次)走出 4 条不同的工具序列;第二处改哪一行,取决于第 5 轮测试输出里才出现的那条报错。把「下一步做什么」交给模型,就是 agent——它和 workflow 之间不是一道墙,是一条光谱。
❓

本课一问:第 1 课的流程在两个任务上失败了,原因各不相同。那就每遇到一类失败,给流程加一步、加一条分支——这样补下去,能覆盖所有情况吗?

这一课先不碰:把决定权交给模型要付什么代价(第 3 课);模型「会决定下一步」这个能力从哪来(岔路 B1);那个循环具体怎么写、什么时候停(A1)。

从已有出发

第 1 课拆出两层失败:parse_date 缺的是运行时才出现的信息(第二处 bug 的报错要等第一处修好才有),slugify 败给了程序自己的写回规则。补法也写好了:测试没过就带着新报错再调一次模型(由退出码决定);回复格式不对就拒绝写回。两处都还是代码在决定下一步。

从已有出发 · 累计数据流 点已学环节看它回答什么
每一轮循环都经过

贯穿任务不变:修好 test_parse_date。这一课第一次拿出 agent 轨的录制——同一个任务,模型自己决定每一步走出来的路。

先自己设计:每遇到一类失败,就加一条路

「加一条路」有现成的工具箱。Anthropic 把常见的 workflow 归成五种模式,每种配一张架构图;把它们和 agent 放在一起,按「这条边由谁决定」上色:

五种 workflow 与 agent · 边色 = 谁决定 代码决定(设计时写好)模型决定(运行时选)运行时才定下次数或去向

拓扑取自 Anthropic《Building effective agents》(2024-12-19;2026-09-26 核对原文仍在,页首加了「工具生态已变」的说明)的 workflow 与 agent 架构图,边色是本课按「这条边由谁决定」标的;「何时用」是对原文的转述。有录制的两格用本课项目 T2(Qwen3-8B-MLX-4bit,seed 1)现算。

🎯

试三件事:① 点 Evaluator-optimizer:第 1 课那条「没过就再来」的回边就是它,只是评估者换成了测试的退出码——评估者是代码时,回边是灰的;② 点 Routing 和 Orchestrator-workers:这两种 workflow 里已经有橙色边(模型选岔口、模型定子任务),分野不是一道墙;③ 点 Agent,选 parse_date 按 ▶ 走一遍:这是 T2 录下来的真实 8 轮,每一轮高亮它走的那条边。

照这个思路,把第 1 课的流程补成:跑测试 → 发报错与源文件 → 覆写 → 重跑,没过就带着新报错再来,最多 3 次;回复没有 FILE: 就不写回。下一次遇到新的失败(比如要先读另一个文件、要装一个依赖),再加一条分支。问题是:要加到什么时候?下面把 agent 轨真实走出来的路一条条摆出来。

  1. ① 朴素方案
  2. ② 它在哪失败
  3. ③ 改进方案
朴素方案每遇到一类新失败,就预先写一条分支
路径树 · 真实运行的工具调用序列,一条条插进前缀树 分叉点(下一步在运行时才定)停

同一个任务、同一个模型、同一套 harness 配置(v2 提示词、思考开、不设上限或 30 轮——都没到),只有 seed 不同:A1 录的 seed 1 / 2 / 3,加上 T2 又录的一次 seed 1。

对照:workflow 轨每个任务都走同一条、设计时写好的路径 跑测试 → 收集源文件 → 调模型 → 覆写 → 重跑(5 步,全由代码决定)。

数据:本课项目的真实运行(agent-traces.json 的 A1 录制 + agent-t2-workflow.json 的 T2 录制;Qwen3-8B-MLX-4bit,2026-09-26)。节点只记工具名(同名工具的参数可能不同);被截断、被丢弃的轮次没有调用,不占节点。树与三个计数都在浏览器里现算。

🎯

试三件事:① 先猜一个数,再一次次点「加入下一次运行」:第 2 次加进来时树就分叉了;② 点 pd-v2-unlimited-s2 这枚芯片,看它从哪一个节点开始和别人走不同的路;③ 注意第 1 次和第 4 次运行都是 seed 1、配置只差一个两次都没用上的轮数上限,却也不在同一个叶子上——为什么,第 3 课回答。

改进:把「下一步」交给模型,并把它放回光谱上

agent 并不是 workflow 的反面。smolagents 的文档把「agent」直接定义成模型输出控制程序流程的程序,并且写明 agency 是连续的光谱、不是 0 或 1 ;OpenAI 的入门指南也把「集成了模型、但不让模型控制流程执行」的应用排除在 agent 之外 。把同一个修 bug 任务按六档画出来:

控制权光谱 · 下一步由谁决定 代码决定(设计时写好)模型决定(运行时选)运行时才定下次数或去向

六档与代码骨架逐字取自 smolagents 文档《Introduction to agentic systems》(Apache-2.0;2026-09-26 核对),原文写明 agency 是连续的光谱、不是 0 或 1。控制流图是本课把同一个任务画成六档的结构示意,不含任何数字;第 0 档与第 3 档对应本课项目真实录制的两条轨道。

🎯

把滑杆推到 ★★☆ Multi-step Agent,注意图里第一次出现了指回自己的边:模型 → 执行工具 → 回到模型。骨架里那个橙色的 llm_should_continue(),就是图里两条橙边的来源——转不转、往哪转,都由模型的输出决定。再往右两档,是 A7 的子 agent 和 A2 岔路里的 Code Mode。

光谱还解释了一个常见的困惑:Anthropic 的 routing、orchestrator-workers 明明有模型在做决定,为什么还叫 workflow?因为模型只在预先画好的岔口里选一条(routing),或者只决定这一轮派几个工人(orchestrator-workers);路的形状仍是设计时写好的。到 Multi-step Agent 这一档,连「还要不要继续、下一步是哪条」都交了出去。所谓分野,是光谱上的一个位置,不是两个互不相通的类别 。

极小真数字:要预先写出几个判断

把运行的工具序列插进一棵前缀树:共同的开头合并,下一步不一样的地方分叉。一个节点有几个出口(几种不同的下一步,算上「停」),代码要复现它就得写几减一个判断。同一配置重跑的 4 次运行:

同配置 4 次 parse_date 运行的前缀树:要预先写出的判断数
= + +
👆 公式里带下划线的每一项都可以点——点哪项,讲哪项。
不同路径 4 条、分叉点 3 个,与上面组件现算的一致(同一份数据,浏览器里再算一遍;node 单测与独立的 Python 计算也对得上)。树里只记工具名,同名工具的参数(读哪个文件、跑什么命令、改哪一行)还没算进来——算进来,分叉只会更多。

其中最能说明问题的是 T2 那一次的第 5 → 6 轮。第 5 轮它重跑了测试,输出里第一次出现:

AssertionError: datetime.date(2024, 2, 5) != datetime.date(2024, 3, 5)

第 6 轮它就去改 month = MONTHS.index(name.lower()),补上 + 1。这条边的依据,在第 4 轮之前的任何时刻都不存在。

⚠

常见误解一:「agent 和 workflow 是二选一。」 上面的光谱里,routing 与 orchestrator-workers 这两种 workflow 已经有模型决定的边;smolagents 分了六档,Karpathy 把它叫作滑杆。真正要回答的是「把哪几条边交给模型」,而不是「用不用 agent」。

常见误解二:「有 while 循环就是 agent。」 第 1 课那条「测试没过就再来,最多 3 次」的回边也是循环,可转不转由退出码决定、每一轮做什么也写死了——它仍在第 0 档。判断的标准是谁决定下一步,不是代码里有没有循环。

🔍 路径不同,都是因为「需要」不同吗?

不全是。分叉有两种来源:一种是看了输出才知道(第 5 → 6 轮那种,报错指向了第二处);另一种是采样的随机性——同一个模型、同一份上下文,先 list_dir 还是先读测试文件,本来就可能不一样。前一种是 agent 的价值,后一种是它的代价。更让人意外的是,连 seed 相同、配置相同的两次运行也会分叉:第 3 课的孪生运行会找到那几个不一样的 token。

证据三问

问本课的回答
① 它解决什么问题?当下一步取决于运行时的输出、路没法事先写死时,让路在运行时长出来
② 为什么可能有效?每一轮模型都以最新的环境反馈(测试输出、文件内容)为条件决定下一步,分支条件不必事先写出
③ 实验上呢?同一配置重跑:4 次运行 4 条路;T2 的 4 个任务 4 条;parse_date 第二处的修改依据在第 5 轮才出现

演进地图:你在这里

什么时候需要 agent:每一代都被上一代的某个真数字逼出来
  1. 朴素 单次调用:报错 + 源文件发给模型,要回整份新文件,覆写后重跑 本课项目 workflow.py · 5 步全由代码决定
    解决了只调一次模型,每一步都写在代码里:可预期、好测、便宜。
    卡在T2 workflow 轨 2 / 4 通过。parse_date 只改了报错指到的那一行,重跑才露出第二处(datetime.date(2024, 2, 5) != datetime.date(2024, 3, 5));slugify 的回复只有函数片段,兜底规则把 130 字符的片段当整份文件写回(原文件 365 字符),丢了 import re。
  2. 第一代 加步骤、加分支:五种 workflow 模式 prompt chaining · routing · parallelization · orchestrator-workers · evaluator-optimizer 你在这里
    解决了每一类失败补一条预先写好的路径:测试没过就带着新报错再来一次(评估者是测试的退出码)、回复格式不对就拒绝写回。
    卡在分支要在设计时写出来,可真实路径是在运行时长出来的:T2 agent 轨 4 个任务走出 4 条不同的工具序列;同一配置重跑 4 次 parse_date(seed 1 / 2 / 3 / 1),走出 4 条。
  3. 第二代 把「下一步做什么」交给模型:while + 工具 = agent 本课项目 loop.py(A1 细讲) 你在这里
    解决了边在运行时长出来:T2 agent 轨 3 / 4 通过,包括 workflow 没修好的 parse_date 与 slugify。
    卡在代价:workflow 已经能解决的任务上 token word_freq ×5.3、leap_year ×4.5,其中 word_freq 的 agent 反而没修好;同配置、同 seed 重跑,2 / 4 个任务在中途分叉。
  4. 第三代 先判别再选轨道:能用一段代码就不用 agent 三条判别标准 · 先直接调 API
    解决了录制前写下三条标准并逐个预测,录制后对照:预测对 3 / 4。
    卡在预测错的 slugify 不是任务本身需要 agent,而是 workflow 自己的写回规则出了错(回复没有 FILE: 标记时的兜底)。每条轨道每个任务只跑 1 次:只能当例子,不能当比率。
  5. 岔路 混合:把固定编排写成 agent 的一个工具 learn-claude-code s16 Workflow Runtime
    解决了编排形状固定的部分(并行审查、逐条验证、断点续跑)写进脚本;模型只决定「要不要调这个 workflow、传什么参数」。
    卡在同一个仓库里两种说法并存:README :11 与 :41 说 agency 来自训练、不是编排;:239 说编排形状固定就写进代码。界线画在哪,没有实验回答。
  6. 岔路 通用 agent 切换到编码姿态 Hermes coding_context.py
    解决了同一个循环,识别出代码工作区后注入编码说明(约 577 token,Qwen3 tokenizer 计)与工作区快照;focus 模式才会收窄工具集与技能索引。
    卡在判定靠文件标记:本课任务仓单独拷出来(README.md、dateparse.py、test_dateparse.py,没有 .git)在默认 auto 下被判成 general;放在项目目录里原地跑,却因为往上 3 层有个 requirements.txt 被判成 coding。姿态在会话开始解析一次,中途不变。
数字口径:本课项目 T2 双轨录制(Qwen3-8B-MLX-4bit,本机 mlx_lm.server,seed 1,2026-09-26;4 个任务各跑 1 次,只说明现象会发生,不估计比率);孪生运行 = A1 里与 T2 agent 轨配置相同、seed 相同的那次运行;Hermes 一行是 pin 56dc01d9 的原实现在临时目录里实跑的判定。

一句话

📌
workflow 的边在设计时画好,agent 的边在运行时长出来。
🚦

先停在这里:记住光谱上那条「第一次指回自己的边」:转不转、往哪转,由模型的输出决定。
再深一层:岔路 agency 是训练出来的,还是编排出来的?——模型为什么会「决定下一步」,两种针锋相对的说法,以及同一个仓库里自己的转向。

下一个缺口

把决定权交给模型,代价是什么? → 第 3 课:把「下一步做什么」交给模型,要付什么代价?

agent 轨修好了 workflow 没修好的 parse_date。可路在运行时长出来,也就意味着你事先不知道它会走几步、花多少、每次走得一不一样。

你可能会问