Chapter 01 · 范式觉醒
范式觉醒:从 GoF 失效到双轴框架
起点页给了一句话本质:所有模式都是同一个原子循环在两根轴上的取值。这一章把这句话拆开——先讲为什么旧范式(GoF)会失效、新机制(分布式系统)从哪来,再立起两根轴并教你用它定位与选型,最后用逆向五步法去验证它。
本章你将建立的 schema
- 为什么 GoF 的确定性契约在概率 actor 上失效,该用分布式系统的哪些机制(幂等、Saga、补偿、重放)补位。
- 「认知功能 × 执行拓扑」两根轴各回答什么问题、为什么正交、为什么能装下所有模式。
- 用坐标系给一个已知模式定位,用两个诊断问题把业务需求反推成模式族。
- 逆向五步法:从可观察行为逐层向内,反推一个成熟 Agent 产品的架构。
这门课要解决一个具体的认知问题:上千个 agent 工程问题,看上去千奇百怪,背后是不是有有限的结构?答案是有。所有这些问题都落在同一个原子循环上——一次 LLM 调用接收一段 context、产出 token,这些 token 被解析成动作并执行,结果作为新的 observation 回填进下一轮 context。28 个设计模式,全部是对这个循环施加约束的不同方式。
1.1范式之变:GoF 失效与分布式系统补位
GoF 设计模式假设 actor 是确定的、控制流编译期已知;LLM agent 两条假设都不成立——它是概率节点,控制流在运行时由模型自己决定。
写过十年面向对象的工程师,手里有一套强大的设计直觉:Strategy 换算法、State 管状态、Chain of Responsibility 串处理链。把这套直觉直接搬到 agent 上,是大多数人踩的第一个、也是最贵的认知错误。它不是不够用,而是会主动误导——因为它的两条地基假设在 agent 上都不成立。
GoF 那 23 个模式,全部建立在两条前提上:actor 是确定的(同样的输入给出同样的输出),控制流编译期已知(哪个分支走哪里,写代码时就定死了)。LLM agent 把这两条都打破。第一,执行是概率性的——模型做的是 token 概率的模式匹配,不是逻辑推断。第二,控制流在运行时由模型自己决定——Anthropic 对 agent 的定义就是"LLM 动态地指挥自己的流程和工具使用"。
底层机制(比"非确定"深一层):很多人以为非确定性是采样噪声,把 temperature 设成 0 就能消掉。这是错的。Thinking Machines 在 2025-09 实测:Qwen3-235B 在 temperature=0 下,同一个 prompt 跑 1000 次产生了 80 个不同的补全,而且分叉发生在第 102 个 token。根因不是浮点不结合律本身,而是服务器负载让 batch size 浮动,导致 RMSNorm/矩阵乘/注意力的归约(reduction)顺序变化,浮点结果随之变化(batch 不变性缺失)。含义很重:你连"同输入同输出"这条最底层的契约都拿不到,GoF 那套基于确定性的设计失去基础。
那该用什么范式补位?答案是分布式系统。一旦 actor 不可靠、步骤之间有网络/工具副作用、又没有跨步骤的事务,你面对的失效集合——部分失败、超时、重复投递、最终一致——正好就是分布式系统十五年前解决过的那一套。这套机制是直接可映射的:消息队列对应 agent 步骤间的任务缓冲;幂等键防止重复的 LLM 调用产生重复副作用;熔断器对应主模型限流时切备用模型;Saga + 补偿处理多步工作流中途失败的回滚;两阶段提交对应不可逆动作前的人工审批门;背压对应并行 LLM 调用的并发上限。下图是这套机制里最常用的一种:Saga 补偿。
| 把 agent 当成 | 带来什么 | 为什么没选 / 选中 |
|---|---|---|
| 确定的 OOP 对象 (GoF:Strategy / State / Command) | 模式成熟、直觉熟悉,上手零成本 | 概率 + 运行时控制流两条假设都不成立,确定性契约不再成立;同名映射(Chain≈routing)是"同名不同物" |
| 更大的 RAG / 写死的 workflow | 可预测、可调试,多数生产场景够用 | 只覆盖固定路径,处理不了"模型自主决定步数与停止"——这正是 agent 的定义性能力 |
| 分布式系统里的不可靠节点 (幂等 / Saga / 补偿 / 背压 / 重放) | 失效集合精确对应;崩溃可恢复、重试安全、部分失败可补偿 | 选中——这是与 agent 真实失效结构匹配的范式 |
下面这段伪码把这套机制收成一个可复用的步骤骨架:幂等键挂在"逻辑操作"上而不是每次推理调用上,外部 oracle 验证而非自检,副作用登记补偿、持久化以便崩溃重放。
# 分布式系统的机制用于 agent:每个有副作用的步骤 = 幂等键 + 外部验证 + 补偿 + 重放
def run_step(saga, step, inputs):
# 1) 幂等键挂在"逻辑操作"上,不是每次 LLM 调用
key = sha256(f"{step.name}:{stable_json(inputs)}")
if cached := store.get(key): # 重试策略:命中即 no-op,绝不重算
return cached # (这是 retry,不是 sampling)
# 2) LLM 这步非确定 → 拿环境 ground truth 校验,失败再"采样"换答案
for attempt in range(MAX_SAMPLES): # 采样策略:换答案,独立于上面的重试
out = llm_call(step.prompt, inputs)
if validate(out, against=env): # 独立验证:不靠模型自检
break
else:
saga.compensate_all() # 都不过 → 逆序补偿已完成步骤
raise StepFailed(step)
# 3) 副作用必须可补偿、可重放(否则崩溃重放会重复执行)
effect = step.apply(out)
saga.register_compensation(step.undo, effect) # 登记逆操作
store.put(key, out) # 持久化:崩溃后重放跳过本步
return out
任何依赖"这步 LLM 输出可复现"的设计都会偶发失败:同 prompt 在 temperature=0 下实测 1000 次产生了 80 种结果(batch 不变性缺失)。别把"关掉采样"当成"拿到了确定函数"——你连重放一致性这个最基本的分布式假设都站不稳,所以每个有副作用的步骤都得显式幂等。
一个 agent 步骤解析 LLM 输出失败了,你想"重试一次换个答案"。如果这个步骤已经调用过支付 API、并且你用同一个缓存键来做这次重试,会发生什么?
展开答案(先停 10 秒再点)
缓存会命中,一直返回那个坏输出——因为你把两种相反语义混进了一个键。retry 要的是"同样的结果从缓存返回、绝不重算";sampling 要的是"重新生成一个不同答案"。它们必须用不同的键。更糟的情况是:如果没有幂等键,重试会让支付 API 被重复调用——这是把后端世界里几乎无害的"重试一下"直觉,错误地搬到了有副作用 + 非确定输出的 agent 步骤上。
这正是为什么分布式系统的 retry policy 和 sampling policy 是两个独立旋钮,混进一个参数是多数 agent 失效的源头。
1.2双轴框架(上):两根正交的轴
两根轴回答两个不同的问题——纵轴(认知功能)问"一步循环内部在做什么",横轴(执行拓扑)问"循环之间如何连、谁决定下一步"。
§1.1 立住了地基:agent 是概率分布式节点。这一节在地基上立起坐标系。把 1000 个工程问题压缩成有限模式,靠的就是发现:任何一个具体的 agent 设计问题,本质都在回答两件正交的事。
纵轴 = 认知功能,由 CoALA(Cognitive Architectures for Language Agents,TMLR 2024)给出权威划分,回答"这一步循环内部在做什么"。它把动作切成两类:"不碰世界"的内部动作(推理 reasoning、检索 retrieval、学习 learning,只更新工作记忆)和"碰世界"的外部动作(grounding,接到对话/物理/数字环境,有不可逆副作用)。感知、记忆、推理、行动、反思这五个认知功能,本质是这个内部决策周期里被调用的不同动作类型。下图是这个周期的机制。
横轴 = 执行拓扑,由 Anthropic 的工作流构建块和 LangGraph 的多智能体抽象锚定,回答"多个 LLM 调用在控制流上如何连线、谁决定下一步"。最根本的分界是 workflow(控制流写死在代码里)对 agent(LLM 在循环里自主决定)。在这条轴上,从简单到复杂依次是:单 agent 循环(ReAct)、规划-执行(强模型先出计划、弱模型执行)、编排者-工人(中心 LLM 运行时动态分解派发)、多 agent(network 对等 handoff/supervisor 中心调度)。拓扑的本质是 context 的拓扑——每个 box 是一个独立的上下文窗口,箭头是 token 的传递。
为什么两根轴正交:同一套认知功能可以塞进任意拓扑。一个 worker 内部仍然要跑完整的"提议-评估-选择-执行"周期;把 reflection 从单 loop 提到 evaluator-optimizer 拓扑,改的是"谁来反思",不改"反思要产出什么"。所以"这一步做什么"和"循环怎么连"是两个能独立拨动的旋钮——这就是双轴框架的全部威力,也是它能装下所有模式的原因。
| 组织方式 | 优势 | 为什么没选 / 选中 |
|---|---|---|
| 扁平清单 (chaining / routing / … 一字排开) | 上手快、直接对应 Anthropic 原文 | 把正交的两件事(一步做什么 vs 多步怎么连)混进一个维度,且无法回答"下一个模式在哪" |
| 按框架分类 (LangGraph / CrewAI 怎么写) | 贴近落地、能直接抄 | 框架会过时,且学到的是 API 不是设计;换框架就重学 |
| 认知功能 × 执行拓扑 双轴 | 能解释"为什么是这些模式"、暴露空白格子、跨框架可迁移 | 选中——代价是要先理解两个抽象轴,但一次投入终身复用 |
有人说要给 agent 加一个"反思模块",做法是把 perception / memory / reasoning / action / reflection 实现成五个平行的独立模块。按 CoALA 的机制看,这个设计哪里会出冗余?
展开答案(先停 10 秒再点)
perception 和 reflection 在 CoALA 里都不是独立的格子。perception 是 grounding 动作的环境反馈(observation 回填),reflection 是 learning(把经历写回长期记忆)+下一周期 retrieval(重新注入)的组合。把它们做成与 reasoning/action 平级的独立模块,会造出和"工作记忆回写""检索"重复的冗余路径。常见的"感知-记忆-推理-行动-反思"五并列方块图,其实是对 CoALA 的简化误读——五个是认知功能,不是五个实现模块。
1.3双轴框架(下):定位与反推选型
给业务问题问两个诊断问题——步数能否预先列举(定横轴)、是否需要反思/跨任务记忆(定纵轴)——两个坐标的交点就是模式族。
§1.2 立起了坐标系。这一节让它干活:把已知模式钉到坐标上,再反过来用坐标反推选型。关键是,两根轴各自绑定一个真实的成本机制,不是装饰性的分类标签。
横轴绑定"LLM 调用次数 × 串/并行",直接决定延迟和 token 账单。ReAct 的铁律是"每个工具调用 = 一次大模型调用",所以延迟线性累加、还容易近视(一次只为一个子问题规划)。Plan-Execute 把规划从执行剥离——规划器只在开头和重规划时调用,执行步可用廉价模型,成本约"1× 强模型 + N× 弱模型",N>3 时常优于 ReAct。多 agent 把成本推到极限:Anthropic 的研究系统比单 agent 强 90.2%,但烧了约 15× 的 token,而且 token 用量本身解释了约 80% 的性能方差——多 agent 的本质是"用 token 买并行探索的广度"。
纵轴绑定"有限上下文窗口怎么花"。Anthropic 把它重述为注意力预算:transformer 的 n² 注意力让 context 越长召回越差(context rot)。纵轴往上爬的每一档,都是一种对抗 context rot 的记忆机制。下图把几个已知模式钉在了这个坐标系上。
反推选型只问两个诊断问题,各定一根轴的坐标。下面的伪码把这个判断显式化。
# 把一个业务问题投影到「认知功能 × 执行拓扑」坐标系
def locate_pattern(task):
# —— 横轴:由"步数能否预先列举"决定 ——
if steps_enumerable_upfront(task):
topo = "plan_execute" if not subtasks_independent(task) else "parallel"
else: # 步数不可预测 → 反应式
topo = "orchestrator" if dynamic_subtasks(task) else "react"
# —— 纵轴:由"单遍能否做对 / 是否需记忆"决定 ——
cog = "reactive"
if needs_iteration_with_feedback(task): cog = "reflective" # evaluator-optimizer / Reflexion
if needs_cross_task_memory(task): cog = "memory" # 情景记忆 / 压缩 / 笔记
return (cog, topo) # 坐标交点 = 推荐模式族
# 选错坐标 → 可预测的失败模式:
# (reactive, plan_execute) 用在高动态环境 → 计划僵化、频繁 replan
# (reactive, react) 用在可预列举任务 → 近视卡循环、延迟白付
# 过度上 (memory, orchestrator) → 上下文分散、决策冲突、token 爆炸
| 模式 | 该用在 | 用错场景 → 失败模式 |
|---|---|---|
| ReAct(单循环·反应) | 步数不可预测、强环境依赖的探索 | 用在步骤明确的任务 → 每步一次大模型调用、近视卡循环、延迟白付 |
| Plan-Execute(规划-执行) | 步骤可预先分解、追求低延迟低成本 | 用在高动态环境 → 开头的全局计划迅速过期,频繁 replan 反而更慢 |
| Reflexion(反思+记忆) | 有明确成功/失败信号、允许多次重试 | 用在无清晰奖励信号的任务 → Self-Reflection 空转、生成漂亮但无用的自我安慰 |
| Orchestrator-Workers(编排) | 子任务无法预先列举、可并行的广度探索 | 用在强依赖任务(编码调试)→ 子 agent 互不知情、决策冲突、约 15× token |
2025-06,Anthropic 公布了强 90.2% 的多 agent 研究系统、力推 orchestrator-workers;同期 Cognition 发表《Don't Build Multi-Agents》,说多 agent 因上下文分散、子 agent 互不知情而脆弱,编码调试这类强依赖任务尤其不该拆。两个顶级团队对同一个坐标区直接对撞——说明"右上角"目前没有标准答案。读到后面第 07 章协作时,带着这个张力去看。
1.4逆向五步法:从产品反推架构
你只能看到 LLM API 边界上的字节流;逆向就是从这层字节流逐层向内反推:行为 → 工具 → 系统提示 → 控制流 → 架构。
前三节给了你一套坐标系和一套来自分布式系统的设计方法。这一节给你验证它们的工具:拿一个成熟 Agent 产品,把它的架构拆出来。这既能让你"带读他人代码",也是检验你是否真懂双轴框架的试金石——能逆向,才算真懂。逆向的本质约束是:无论产品内部多复杂,它和模型之间只能通过 LLM API 边界上的字节流交互,所有"架构"都是从这层字节流向内反推。
第 2 步是杠杆点:把 base URL 指向本地 mitmproxy(ANTHROPIC_BASE_URL=http://localhost:8000),截获请求体里完整的 tools 数组(名字 + JSON schema + description)——这是最高保真的信号。第 3 步用频率分析:系统提示被 prompt caching 固定,每轮高频复现,可自动剥离出静态骨架 vs 动态注入(<system-reminder>、<env>、todo 重载)。第 4 步从 message role 序列重建状态机:assistant[tool_use] → user[tool_result] 反复出现即 ReAct 环;出现 Task 工具即 orchestrator-workers。
逆向最反直觉的一个发现:很多产品的多 agent 协调逻辑根本不在代码里,而是写在系统提示的自然语言指令里。工程师以为会看到调度器或消息总线,实际是 prompt。所以"控制流一半在代码、一半在 prompt"——盯着可执行逻辑找状态机会漏掉一半。
抄系统提示既脆(对方一改就失效)又有法律风险(系统提示里可埋"金丝雀令牌",一旦出现在你的产品输出里就能溯源到你)。逆向的真正价值,是还原"为什么这样设计"——把对方的控制流选择、记忆分层、工具边界、护栏策略提炼成可迁移的设计原则,套到你自己不同的任务约束上。验证假设要靠三类探测:提示泄漏、输出反演(output2prompt,只用正常输出就能拼回提示,绕过拒答防御)、行为差分(固定探针集做 A/B 假设检验,对抗采样随机性)。
你逆向一个网页版的 Deep Research 产品,抓了它和服务器之间的最外层请求,发现全程只有一个 agent 在说话。能据此断定它是单 agent 架构吗?
展开答案(先停 10 秒再点)
不能。服务端 agent 的内部子调用发生在服务器内部,你抓的最外层包看不到那些子 agent 的并行探索。把"最外层只有一路输出"当成"单 agent",是把 UI 行为误当架构。判断多 agent 的更可靠信号是行为指纹:中间过程的信息密度突然骤降、主线程上下文异常干净——子 agent 烧几万 token 探索、只回传 1000–2000 token 摘要,这种"摘要式返回"才是子 agent 架构的指纹。能抓包的本地 CLI 类产品(如 Claude Code)才能看到内部的 Task 子调用。
§本章 self-check
先合上教程,把你能想到的答案写在纸上或编辑器里。写完再点开答案对照——直接点开等于把这一节当再读一遍。
- GoF 设计模式的两条地基假设是什么?LLM agent 分别怎么打破它们?
- 为什么
temperature=0不能保证 LLM 输出可复现?根因是什么(一个词)? - 双轴框架的两根轴各回答什么问题?用一句话说清它们为什么正交。
- 反推选型的两个诊断问题是什么?各定哪一根轴的坐标?
- 逆向五步法里,为什么"抓工具集"这一步性价比最高?
答案(先做完再展开)
- 两条假设:actor 确定(同输入同输出)、控制流编译期已知。agent 用概率执行打破第一条,用"模型运行时自主决定下一步"打破第二条。
- 因为服务器负载让 batch size 浮动,导致归约(reduction)顺序变化、浮点结果变化。根因一个词:batch 不变性(缺失)。
- 纵轴(认知功能)问"一步循环内部在做什么",横轴(执行拓扑)问"循环之间如何连、谁决定下一步"。正交是因为:同一套认知功能可以原样塞进任意拓扑,"做什么"和"怎么连"能独立拨动。
- 问题一:"步数能否预先列举?"定横轴(能→规划-执行/并行,不能→反应式/编排)。问题二:"是否需要反思/跨任务记忆?"定纵轴(需要迭代→reflective,需要跨任务→memory)。
- 因为工具的 JSON schema 必须原样发给模型,所以一个反向代理就能拿到最高保真的信号(名字+schema+description),无需任何破解;其余各层的保真度都低于它。
给一个"自愈式测试修复"agent 定坐标
设想一个 agent:拿到一个失败的单元测试,反复"读报错 → 改代码 → 重跑测试",直到通过或放弃。请把它定位到双轴坐标系的哪个格子,并指出它和 ReAct 的关键差别在哪一根轴上、为什么这个差别让它在"测试修复"上比纯 ReAct 更可靠。
提示(卡住再展开)
横轴上它仍是单 agent 反应式循环(每轮一次推理)。真正的差别在纵轴:它有一个外部 oracle(单元测试的通过/失败)作为反馈信号。回想 §1.3 表里 Reflexion 那一行的适用条件——"有明确成功/失败信号"。这个外部 ground truth 正是后面第 06 章"自愈循环"可靠的根本原因,也是为什么同样的反思放到没有 oracle 的开放任务上会失灵。