为什么同一个任务不会得到同一个解决方案
把同一个问题问 Coding Agent 五次,你可能会得到几个不同、但各自都说得通的解决方案。
这并不意味着五次尝试一定会得到五种完全不同的实现。尤其当任务定义得很窄、现有代码已经形成了强约束时,不同运行之间往往会有大量相似之处。它也不意味着 Large Language Model 是一种纯粹的随机机器,每次运行都会毫无理由地走向不同方向。
技术上更重要的结论其实是另一件事:
没有任何技术保证,同一个任务每次都会产生同一条解决路径和同一个实现。
在简单的文本生成中,这种性质早已为人熟知。但到了 Coding Agent,这件事多了一层含义。Agent 不只是生成文本或代码。它会搜索 Repository、读取文件、选择 Tools、运行测试、解释结果,并根据这些结果调整下一步行动。
因此,Agent 不只是生成一个答案。它是在构造一条通往解决方案的路径。
也正是在这里,把 Agentic Software Development 想象成一条更快的新流水线,就开始变得有问题。
流水线工作的神话
Section titled “流水线工作的神话”围绕 AI Productivity 的很多讨论,在思维上都可以简化成这样一个模型:
Ticket ↓Agent ↓可预测的处理过程 ↓Patch如果这个图景成立,那么 Coding Agent 本质上只是新一代自动化工具。我们可以不断把更多 Ticket 交给它,然后主要去衡量它把这些 Ticket 变成代码的速度和成本。
对于某些任务,现实确实可以非常接近这种模式。任务定义得越窄,现有结构对解决空间的约束越强,Agent 自己需要做出的决定越少,不同运行之间就越可能相似。
但 Agentic Work 真正变得有意思的地方,恰恰是解决路径并没有被完全预先定义的场景。
因此,Anthropic 区分了 Workflows 和 Agents:前者让 Model 和 Tools 沿着预先定义的代码路径被编排;后者则由 Model 自己决定如何处理任务以及该使用哪些 Tools。OpenAI 对 Agents 的描述也类似:Model 控制 Workflow、做出决策,并根据当前状态动态选择 Tools。
这不只是术语上的细微差别。
一个硬编码的 Workflow 在执行过程中不会重新思考哪条路线当前更合理,而 Agent 正是在做这件事。
Agent 不是一台能保证每次都以同样生产模式把 Ticket 变成 Patch 的机器。
因此,Agentic Work 与其说像流水线,不如说更像是在不断变化的 Context 下连续做出一系列决策。

确定性在传统软件中意味着什么
Section titled “确定性在传统软件中意味着什么”在软件开发中,我们习惯于这样的系统:从输入到输出的路径完全由代码定义。
简化来看:
Input ↓步骤 A ↓步骤 B ↓步骤 C ↓Output当输入相同、相关状态也没有发生变化时,我们期待得到相同的执行过程和相同的结果。
当然,传统软件也有很多可能导致差异的来源:时间、并发、外部服务、网络状态、随机值或者变化后的数据库。关键区别在于,我们通常可以把这些变化建模为显式系统状态的一部分。算法本身不会突然决定,今天与其执行步骤 B1,不如试试 B2,因为后者此刻看起来更合理。
很多自动化正是依赖这种性质。
Formatter 对同一个文件应该输出相同的格式。Compiler 在相同条件下从同样的源代码应该产生相同的结果。CI Pipeline 不应该每次运行时都重新思考“这次我觉得哪些质量检查比较有意义”。
对于这类问题,确定性非常有价值。
这也解释了为什么“流水线”这个心智模型对 Agents 如此有吸引力。几十年来,软件开发不断把重复性工作转化为确定性的流程。
但 Coding Agent 并不是这个发展过程中的另一个固定步骤。它把一个 Model 放进了控制流中,而这个 Model 会在一定边界内自行选择下一步最合理的行动。
为什么 LLM 不像传统函数那样工作
Section titled “为什么 LLM 不像传统函数那样工作”从应用角度看,Large Language Model 当然可以像函数一样被调用:
const result = model(input);但这种写法不应该让我们误以为,这个系统在概念上就等同于一个传统函数:相同的输入必然产生完全相同的输出。
Language Model 会针对当前 Context 之后可能出现的延续计算概率。简化来说,并不存在唯一的下一个 Token,而是存在一个可能延续的概率分布。其中一些概率很高,一些较低,还有很多实际上几乎不相关。
因此,Model 并不会任意行动。如果我们要求它写一个 TypeScript Function,它并不会以同样概率返回一道菜谱或一首中世纪诗歌。现有 Context 会非常强地限制什么才算合理的延续。
但在这个范围内,可能同时存在多个合理的延续。
temperature 或 top_p 这样的 Sampling 参数会影响系统如何从概率分布中进行选择。它们可以降低或增加 Varianz,但这并不是本文的重点。即便某些设置能够提升可重复性,也不应该把它们理解为“输出永远完全相同”的普遍保证。对于 Agentic Work 来说,更重要的是:当微小差异不只是影响措辞,而是开始影响 Model 选择的下一步工作时,会发生什么。
对于我们的心智模型,先记住这句话就够了:
非确定性并不意味着 Model 会任意行动。它意味着在一个解决空间内,可能存在多个合理的下一步。
在一次简单回答中,这可能只是不同的措辞。到了代码中,两种实现可能用不同结构去满足同一个需求。
而在 Agent 中,会发生更有意思的事情。
从 Token Varianz 到决策 Varianz
Section titled “从 Token Varianz 到决策 Varianz”Coding Agent 通常不会一步就生成最终 Patch。
它可能先查看目录结构,然后搜索一个类似的现有 Feature。接着,也许打开一个 Facade、一个 Store 和一个 Test。也可能从 API Contract 开始。它可能先运行已有 Test,可能先搜索某个 Symbol,也可能先查看 Git History。
这些行动都可能是合理的。
一个典型的 Agent Loop 可以被极度简化为:
Task ↓决策 ↓行动 / Tool ↓观察 ↓新的 Context ↓下一次决策这种循环正是今天许多 Agents 的核心架构模式之一。Model 根据当前状态决定接下来该做什么,观察结果,然后带着新获得的信息继续工作。
于是,Language Model 原本的输出差异,被转化成了另一种更重要的差异。
区别不再只是“同一行代码有两种写法”。真正可能发生变化的,是:
- 先检查哪个文件,
- 发起哪一次搜索,
- 先运行哪个 Test,
- 对现有架构形成什么假设,
- 把哪个错误判断为更重要,
- 下一步使用哪个 Tool。
其中某个决定,可能会让一条运行路径发现一些信息,而另一条运行路径一开始根本不会看到这些信息。
对 Agent 来说,Varianz 不只是 Output Varianz,它还可能变成 Process Varianz。
对于理解 Agentic Systems,这一点远比“Language Model 是概率式生成的”这句事实本身重要。
Context 不是静态输入块
Section titled “Context 不是静态输入块”本系列上一篇文章已经讨论过:Context 并不是 Model 理论上可能知道的一切,而是某个具体工作步骤中实际可用的信息。
在 Agent 中,这个 Context 还不是静态的。
一次行动之后,可能会多出一个搜索结果:
Search Result下一步之后,可能多出一段源代码:
已加载文件再之后可能是:
Compiler Error或者:
Test Output或者:
Git Diff每一次观察都可能改变“下一步什么最合理”的判断。
因此,Anthropic 在讨论 Agent 的 Context Engineering 时,并不只把它理解为“设计一个好的初始 Prompt”,而是把重点放在多步骤过程中:当前到底应该有哪些信息位于 Context 中。Agentic Systems 还可以采用“just in time”的方式按需加载信息,而不是一开始就把所有知识都塞进去。
这会带来一个非常重要的结果。
设想两个 Agent Run 拿到同一个任务。它们在几乎相同的条件下开始。在 Run A 中,Agent 决定先搜索 Repository 里已有的相同 Pattern。Run B 中,它直接打开新 Feature 的文件。
在这个第一步之后,两边的状态就已经不一样了。
Run A 的 Context 中此时可能出现了一个现有架构示例。Run B 的 Context 中则是本地实现的具体细节。
接下来每一边的决策,都建立在不同 Context 之上。
解决路径早期的差异,会改变后续的 Context。
而变化后的 Context,又会继续影响下一次决策。
这就是路径依赖。
路径依赖:一个小转弯,之后就是另一条路线
Section titled “路径依赖:一个小转弯,之后就是另一条路线”Path Dependence(路径依赖) 这个词听起来比它在这里实际表达的东西更理论化。
它只是意味着:一个过程之后怎么发展,会受到之前已经走过哪些步骤的影响。

假设一个 Repository 里有两个类似 Feature。旧 Feature 使用的是已经过时的结构,新 Feature 则符合当前架构。
一个 Agent 先发现了新的 Feature,于是沿用了它的 Pattern。
另一个 Agent 在搜索时先遇到了旧 Feature,并把它理解为本地惯例。
这两个判断在当时都可能说得通。Agent 并不会自动知道两种实现背后的历史演化。
从这一刻开始,它们能够看到的线索已经不一样了。之后的搜索可能使用不同关键词。不同文件开始显得相关。不同依赖关系被继续检查。最终 Patch 也会以不同方式成形。
一个很小的早期差异,可能在多个步骤中被不断放大。
这并不意味着两个 Agent 一定会越走越远。Test、Compiler Error 或明确的架构规则,都可能让两条路径后来重新收敛到同一个可接受范围。
但这并不是一个保证。
因此,如果我们只看最终 Output,然后问“为什么同一个 Input 突然得到两种不同解决方案”,就会忽略 Agent 实际发生的过程。Agent 的工作是由一整串决策和观察组成的。
同一个任务并不自动意味着同一个状态
Section titled “同一个任务并不自动意味着同一个状态”比较不同运行时,还有一个容易被忽略的问题。
两个完全相同的 Prompt,并不自动意味着两个 Agent Run 真正在数学上以完全相同的状态开始或继续。
可能存在差异的包括:
- 已经加载的文件,
- 搜索结果,
- 外部 Tools 的结果,
- Repository 状态,
- 已存在的 Session History,
- 可用的 Memories,
- 已激活的 Skills,
- 缓存信息,
- Test 或 Compiler 输出。
如果使用 Web-based Tools,甚至两个 Run 之间外部世界本身都可能已经变化。
不过,这个论点也不应该被无限放大。由此得出“Agent Run 根本无法比较”同样是错误的。好的 Evaluation 恰恰会尽可能控制初始状态。Anthropic 例如建议,在 Agent Evals 中为每个 Trial 使用隔离且干净的环境,从而避免残留文件、Cache 或共享状态带来额外 Varianz。
即使我们尽可能控制了这些外部影响,核心结论仍然成立:
即使初始条件非常接近,解决路径也不必完全相同。
因为 Agent 在允许的解决空间内仍然拥有决策空间。
来自我的架构实验室的一次观察
Section titled “来自我的架构实验室的一次观察”我并不只是理论上研究这种行为。在我的架构实验室里,我会有意识地让 Coding Agents 构建大量小型应用和重复出现的 Features。一部分当然是为了试验技术和架构方案,但与此同时,我也利用这些项目去理解:Coding Agent 的 Guardrails 到底要精确到什么程度。
我尤其关注 Agent Files 和 Skills。
Agent Files 会描述架构规则,例如:有哪些 Layer?State 应该放在哪里?哪些依赖被允许?哪些 Pattern 应该使用,哪些应该明确避免?
Skills 则描述重复出现的工作方式:一个特定 Slice 应该如何构建?CRUD Feature 需要经过哪些步骤?修改后要执行哪些检查?
在一款用于培训 Seminar 的 Demo App 中,我曾经达到过一个让我在架构上非常满意的状态。结构清晰,多个 Features 都遵循同样的原则,对应的 Agent Files 和 Skills 也已经存在。
然后,我让 Agent 再构建一个 Feature。业务上并没有什么特别复杂的地方,结构上甚至还是一个 CRUD 场景——也就是项目中已经有示例和工作指引的那种任务。
结果却突然出现了一种我自己绝不会用这种方式设计的架构。真正有意思的地方,不是 Agent 产出了明显混乱的代码。恰恰相反:这个方案内部是合理的,单个决策也能解释得通。它不是一堆随机代码,而是一条一致的解决路径,只是最终没有走到我预期的架构上。
Agent 只是很早就在某个地方走上了另一条路。也许它先拿另一套已有结构作为参考,也可能某个早期判断让之后不同的文件显得更重要。具体是哪一次转弯其实没有那么关键,真正重要的是后果:那次决策产生了另一个 Context,之后的决策又建立在这个 Context 之上。
这不是关于一般行为的科学证明,只是一个实际案例。但它对我来说很有启发,因为最明显的解释恰恰 不是“Agent 缺少 Guardrails”。
Agent Files、Skills 和已有架构示例全部都存在。即便如此,这些边界之内依然存在决策空间。
Guardrails 可以限制解决空间,但不会自动让决策路径变成确定性的。
这个区别在实际使用 Coding Agents 时非常重要。
非确定性不等于不可控
Section titled “非确定性不等于不可控”到了这里,很容易产生一种错误印象。
如果 Agent 的路径无法被精确预测,人们可能会由此得出结论:Agentic Systems 从根本上就是不可控的。
这个推论并不成立。
非确定性和任意性是两回事。
在软件架构中,我们其实非常熟悉这个原则。一个团队通常不会规定每个开发者必须以什么顺序打开哪些文件。但我们依然可以非常精确地规定:一个可接受的解决方案应该满足什么条件。
控制 Agent 的方式也类似。
我们可以通过明确的 Requirements 去缩小解决空间。架构规则可以禁止不允许的依赖。Agent Files 可以提供项目特定的 Convention。Skills 可以描述已经验证过的工作流程。
然后,还有传统软件工程中更加硬的边界。
Type System 会直接拒绝某些状态。Compiler 可以拒绝无效代码。Linter 可以检查架构或质量规则。Tests 可以验证业务行为。Architecture Tests 可以发现禁止的依赖。Evals 可以检查重复出现的行为要求。Reviews 最终还能评估那些不适合完全自动化的方面。

这正是“控制一条解决路径”和“控制一个解决空间”之间的根本区别。
我们无法保证 Agent 会走哪一条路,但可以非常精确地定义哪些路径是可接受的。
对我来说,这才是更有意义的 Engineering 视角。目标不必是用越来越长的 Prompt 把 Agent 变成一个劣化版的确定性 Workflow,让每一条解决路径都完全相同。目标是定义可接受解决方案的空间。
这个空间描述得越清楚、技术上的约束越强,不同 Run 在这个范围内选择不同路线就越不成问题。
多个解决方案不自动意味着错误
Section titled “多个解决方案不自动意味着错误”Varianz 经常只在两个结果不一样时才被拿出来讨论,因此很容易带上负面色彩。
但在软件开发里,不同的解决方案本来就是很常见的事情。
把同一个 Ticket 给五个有经验的开发者,也很难得到五个完全相同的 Pull Request。有人会扩展已有抽象,有人会认为本地实现更简单。有人从 Data Model 开始,有人从 Use Case 开始。有些差异需要在 Review 里讨论,另一些只是不同但合理的 Trade-off。
这里把人类开发者拿来比较,并不是为了把 Model 行为人格化。它只是为了说明:当一个问题本身存在多个合法答案时,Varianz 并不自动意味着质量差。
两个 Agent Run 可以生成不同但都正确的实现。它们可以暴露不同 Trade-off,可以对 Bug 采用不同假设却都找到根因,也可以提供不同架构方案,从而让讨论本身变得可能。
在这种情况下,Varianz 不是缺陷,而是开放解决空间的一部分。
真正出现问题,是当这些差异越过了系统在意的边界:业务 Requirements 有时满足、有时不满足;架构规则不能稳定遵守;Security Constraints 在不同 Run 之间发生变化;或者某个流程需要的 Reproducibility 超出了 Agentic System 能稳定提供的程度。
因此,真正有用的问题不是:
“为什么 Agent 这次做得不一样?”
而是:
“它这次做得不一样的部分,是否仍然处于我们可接受的边界内?”
这是一个更有生产力的质量问题。
这对 Evaluation 意味着什么
Section titled “这对 Evaluation 意味着什么”从这个角度看,也更容易理解为什么一次非常亮眼的 Agent Run 只能提供有限证据。
如果一个系统是概率式的,会做出多个决策,并且之前的行动还会改变后续 Context,那么一次成功运行首先只能说明:这一次运行成功了。
它并不能自动证明,类似任务也能稳定地被同样好地解决。
因此,Anthropic 在当前 Agent Eval 指南中明确把一次任务尝试称为一个 Trial。由于 Model Output 在不同 Run 之间可能不同,需要多个 Trials 才能更稳健地判断行为。这里尤其有意思的是两个不同问题:至少成功一次的概率有多高,以及更严格的——Agent 能在多次尝试中多一致地成功。
对于普通软件开发,这并不意味着以后每个小 Ticket 都要让 Agent 实现五十遍。
关键是思维框架。
如果我想知道一个新的 Agent File 是否能可靠传递某条架构规则,那么一次成功尝试只能算一个很弱的信号。
如果我开发一个用于重复 CRUD 工作的 Skill,就不应该只用我写这个 Skill 时恰好使用的那个任务去测试它。
如果我评估一个 Agent Model 或 Repository 中的新 Harness,我需要具有代表性的任务,而不只是一次幸运运行。
Evaluation 也不应该不必要地规定一条完全固定的解决路径。这里同样要区分路径和结果。Anthropic 指出,在 Agent Evals 中,如果僵硬地检查 Tool Calls 的固定顺序,可能会惩罚本来合理的解决路径。因此,只要可能,就应该检查目标结果和关键 Constraints 是否满足,而不是规定每一步必须怎么走。
这和实际软件开发非常吻合。
对于 Coding Agent,这些标准例如可以是:
- 行为有 Tests 覆盖,
- Public API 未改变,
- 不存在禁止的 Layer Dependencies,
- Type Check 成功,
- Linter 成功,
- Security Rules 满足,
- 业务 Use Case 完整实现。
只要路径本身没有引入重要风险,Agent 如何到达这个状态可以是次要的。
如果一个系统是概率式且具有路径依赖,那么单次成功运行通常不足以成为可靠的质量结论。
这并不是要求无限制地做 Evaluation,而只是意味着:不要把一次好的 Demo 和 Agent 的整体质量混为一谈。
什么时候 Workflow 才是更好的选择
Section titled “什么时候 Workflow 才是更好的选择”由此我们会回到一个在 Agent 热潮中很容易被忘记的问题:
这个任务真的需要 Agentic 方式来完成吗?
对于 LLM-based Systems,Anthropic 明确建议首先选择能够解决问题的最简单方案。对于定义非常清楚的任务,Workflows 能提供更高的可预测性和一致性;只有在确实需要灵活性和 Model-driven Decisions 时,Agents 才真正有意义。
这不是 Agents 的局限,而是正常的架构工作。
如果我非常明确地知道,每次 Commit 之后都必须执行:
Format→ Lint→ Test→ Build→ Deploy那我就不需要一个 Agent 每次重新决定哪一步可能比较合理。
如果一个文件可以按照清晰规则转换,那么 Script 很可能更适合。
如果一个 Generator 能根据已知 Schema 生成可重复的 Boilerplate Code,那 Generator 就是正确工具。
Agent 的优势出现在真正需要做决策的地方。
系统中哪些部分和这个 Bug 有关?
哪一个已有实现是最好的参考?
哪个 Test 能帮助验证某个假设?
局部 Fix 足够吗,还是这个 Bug 暗示了结构性问题?
多个可能方案中,哪一个最符合现有 Constraints?
如果这些问题都能用完全确定性的流程解决,那么前提就是我们早已把答案写进了软件。
因此,可以得到一条很简单的 Engineering 原则:
正确解决路径越明确,就越没有理由把这条路径交给 Agent 自己决定。
自动化和 Agentic Work 因此并不是互相竞争的概念。
一个好的系统会把两者结合起来。
确定性机制负责保障那些已知且可以验证的部分。Agent 只在决策自由真正产生价值的地方拥有空间。
Agentic Work 不是流水线工作
Section titled “Agentic Work 不是流水线工作”流水线的比喻描绘了一个很舒服的世界:上面塞入一个 Ticket,Agent 负责处理,下面掉出一个 Patch。如果一次成功了,我们似乎只需要继续增加 Tickets 和 Agents。
但这个图景低估了 Agent 最关键的特性。它会解释当前 Context,决定下一步,执行行动,观察结果,然后再次做决定。早期不同的分支会让不同信息变得可见;变化后的 Context 又可能让下一步看起来不同。这就是路径依赖如何形成的。
这解释了为什么同一个任务不必产生同一个实现,同时也不意味着这种 Varianz 自动是负面的。多条路径都可能得到正确结果。架构规则、Agent Files、Skills、Tests、Compiler、Type Systems 和 Evals 可以把解决空间限制得足够小,使不同路径最终仍然落在同一组质量边界之内。
因此,Agentic Software Development 需要另一种控制观:不是每一步都必须提前定义。真正重要的是,我们知道什么结果可以接受,以及在通往结果的过程中哪些边界不能被越过。
Agentic Work 不是流水线工作。
而非确定性首先只能解释:为什么两个都合理的 Run 可能走上不同路线。
它还不能解释为什么 Agent 在其中一条路上明明是错的,却可以表现得极其自信。它也不能解释,为什么某些假设或架构决定可能在多次修改中逐渐固化,尽管它们最初从未被有意设计。
毕竟,如果所有不同方案都正确,那么“不同”本身并没有那么可怕。
为什么 Agents 会 Hallucinate,以及单次偏差如何逐渐形成长期 Drift,就是本系列接下来要讨论的问题。
来源与延伸阅读
Section titled “来源与延伸阅读”- Anthropic: Building effective agents — 区分预先定义的 Workflows 和 Model-driven Agents,并讨论何时选择哪一种方式。Building effective agents
- Anthropic: Effective context engineering for AI agents — 讨论多步骤 Agent Loop 中作为动态状态的 Context,以及“just in time”加载信息的方法。Effective context engineering for AI agents
- Anthropic: Demystifying evals for AI agents — 讨论 Trials、不同 Run 之间的 Varianz、隔离 Eval Environment,以及相比僵硬 Trajectory 更关注 Outcome 的 Evaluation 方法。Demystifying evals for AI agents
- OpenAI: A practical guide to building AI agents — 将 Agents 描述为由 LLM 控制 Workflow 执行并动态选择 Tools 的系统。A practical guide to building AI agents