跳到主要内容

在自主与可靠之间架桥 — 四类把前沿往外推的模式

这一章讲三件事: 除了「少放权保平安」,还有什么办法兼得自主与可靠; 模型跑着的时候,人从哪几个位置插手; 用户在你还没答完时又发一条消息,五种应对各自的价码。 读完你会明白:agent 的可靠性工程,一大半是「给人留介入点」的工程。

1. 先看现象:这对矛盾真的没救吗

第 6 章留下的兑换率——自主升一档、可靠降一档——本章开篇就把它画成了一条曲线: 借鉴金融里的有效前沿,每种架构都是前沿上的一个点,链在「稳而笨」那一头, agent 在「能而险」那一头1

换目标也别高兴太早。作者补了三个同样互斥的目标: 延迟(尽快拿到最终答案)、自主(少打断人)、方差(多次调用结果稳定)。 三者互相顶牛,而且推到极限会互相取消——原话的例子很损也精准: 延迟最小的应用是那个什么都不做的应用2

真正的出路只有一个方向:把前沿整体往外推——同样的可靠度换更多自主, 同样的自主换更多可靠3。下面四组手段就是四个方向的推力。

2. 顶层全景

可靠 ▲
│ 链● ●链+结构化输出
│ agent● ●agent+HITL
│ ●agent+HITL+校验
└──────────────────────────────────────▶ 自主

图说:手段不是在曲线上滑动,而是把曲线往右上角顶。
手段主治
结构化输出方差(输出格式漂移)
中间输出延迟体感(等待焦虑)
人机协作 HITL高自主架构的失控风险
输入并发(用户连发两条)策略用户等不及时的现场混乱

3. 核心原理

3.1 结构化输出:三种严格度的合同

第一章用过一个 .with_structured_output,这里把底下的牌全摊开4:

路线严格度说明
prompting(好言相求)建议级任何模型都能用,但只是请求不是保证
tool calling(函数清单)约定级微调过的模型从 schema 清单里挑一个并遵守;每个 schema 要起名、写用途、给 JSONSchema 形状
JSON mode(强制开关)强制级部分新模型直接掐掉非 JSON 输出的可能

LangChain 的统一方法把三种差异藏起来:传入 Pydantic/Zod 模式,框架自动挑该家最合适的实现; 同一个模式对象兼任校验器——模型交上来的东西不合规矩,你拿到的是 validation error 而不是一份需要自己擦屁股的畸形数据5

走查一遍书上的真实例子6:要模型讲个猫咪笑话,schema 只有 setup 与 punchline 两个字段, 输出严格成对出现:

{ setup: "Why don't cats play poker in the wild?",
punchline: "Too many cheetahs." }

两个实操细节值得钉住:每个字段的 description 要认真写——连同字段名, 这是模型决定「哪段话该进哪个格子」的唯一依据7; 低温仍是结构化任务的好搭档,降低出格的几率8

3.2 中间输出:让等待有内容

架构越复杂,一次调用越久——每见多个节点串行或成环,就是耗时的叠加信号9。 用户对几秒出结果有心理预期,超了就开始怀疑死机。解法不是让模型变快 (那是供应商的军备竞赛),而是边跑边报

第一层是节点级:stream()stream_mode 三档10:

  • updates(默认):每个节点跑完,吐 {节点名: 该节点的状态更新};
  • values:状态一变就吐整个当前状态,适合 UI 直接渲染状态形状的场合;
  • debug:checkpoint / task / task_result 三类事件全量播报,调优期专用。

三档可以组合订阅(传列表)11。第二层是 token 级: astream_events 事件流里过滤 on_chat_model_stream,把每个模型内部吐出的词实时转发给前端12。 两层分别对应「我看到它跑到哪一步了」和「我看到它在打字」两种安心。

3.3 人机协作:五个介入点

前提先钉死:必须挂 checkpointer——所有介入都依赖「上一步的现场还在」13。 这是第 5 章存档机制的第一次大显身手。

interrupt(手动急停)。用户盯着流式输出,觉得不对劲随时叫停; 状态定格在最后一个完整步14。实现上 Python 用事件对象、JS 用 AbortController, 书中连「中断会抛异常要 try-catch 接住」「aclosing 保证流被正确关闭」这类坑都点到了15

authorize(事前审批)。更常用的形态:声明 interrupt_before=['tools'], 进入工具节点前自动暂停——用户看到模型想调什么工具、带什么参数, 可以放行、可以改道、可以晾着它16。注意这个列表顺序无关、逐个都拦17

暂停之后有四种后续,各是一个明确的调用动作18:

后续怎么触发发生什么
resume(继续)None 当输入再次调用信号含义:「接着上次的非空输入继续跑」
restart(重来)带新输入调用旧状态保留,合并新输入,从首节点重跑;想彻底清场就换个 thread_id
edit(改判)get_state 查看后 update_state 打补丁产生一个新 checkpoint,再 resume 即可
fork(分叉)get_state_history 列出全部历史状态,拿任何一个的历史现场重新 invoke「回到当时,换种走法」——创意类场景的时光机19

fork 的输出顺序有个小坑:历史列表最近在前、最老在后;lazy 列表是逐个吐出的,要自己收成数组20。 真正的力量在组合:审批+改判+分叉可以叠着用21

3.4 输入并发:五策略定生死

书里管这个场景叫 double texting:答案还没出,用户已经等不及又发了一条。 LLM 本来就慢,多步架构更慢,而且即便未来变快,更快的响应只会喂养更复杂的用例—— 处理输入并发会一直是必答题22。五种策略:

策略一句话适合 / 致命伤
拒绝新输入一律打回最简单;把并发难题推给调用方
独立处理每条新输入开新 thread无限扩展;但两条调用对用户不可调和——双用户聊天天然是这种
排队 enqueue排到当前跑完再处理数量不限、结果与到达时机无关;队列可能无界膨胀,排队项看不到前答,依赖前答的输入会变陈旧
打断 interrupt抛弃当前,拿新输入重启响应最及时;细节在「留多少」:什么都不留 / 留最后完整步 / 连未完成步的半成品也抢救 / 等当前节点跑完再断23
fork-and-merge收到即分叉,完工再合并理论最优:及时+时序无关+任意并发;前提是状态可无冲突合并(如 CRDT)或由用户手工解冲突24

打断策略那条「OpenAI 消息配对」的例子是全章最锋利的工程警示: 模型请求工具调用后,必须紧跟工具结果消息;若恰好打断在两者之间,现场就是非法状态, 不防御性清理,这个 thread 从此卡死25。状态结构设计时要为「可能被腰斩」这件事留活口。

4. 作者的判断与证据

  • frontier 概念来自金融/经济学/工程的类比,作者在脚注里老实交代了出处26——是类比不是定理。
  • 「部分并发策略已实现于 LangGraph Platform」是本章唯一的产品指引,细节留给下一章27
  • 结构化输出三策略的适用对照表、五种连发消息策略的利弊表,均为定性工程经验,没有实验数据

判断(我们的,不是书里的): 本章四类手段有一个共同结构——用「多出来的信息通道」 换可靠性:结构化输出是给下游代码多一条格式契约,中间输出是给用户多一条进度感知, HITL 是给人多一个介入点,并发策略是给系统多一条时序规则。三条经验里没有一条 是「把模型变聪明」,全部是「在模型外面加协议」。 如果错,会错在: 若模型自身可靠度提升(更少的格式漂移、更少的死循环)速度快于 协议层老化,这些手段的收益会缩水成纯保险——但「保险属性」本身不会消失。

5. 边界与局限

  • 结构化输出保形不保真:schema 校验通过不代表内容正确,书未强调这个区别。
  • HITL 五模态的演示都在本地进程内;真实产品里「审批」意味着推送、超时策略与多端同步, 书中未涉及(部分由下一章的 Platform 能力补齐)。
  • fork-and-merge 提了 CRDT,但如何设计「可合并的 agent 状态」没有任何示例—— 这是五种策略里工程量最大、书里着墨最少的一个。
  • 「留半成品」的打断变体被作者自评「很可能难以推广到最简架构之外」28, 引用时要带着这条自评。

6. 可带走的

  1. 三目标(延迟/自主/方差)互斥,别许「全都要」的愿,先排序再选手段;
  2. 结构化输出三档严格度:求它→清单→强制开关;Pydantic/Zod 模式兼任运行时校验器;
  3. 字段 description 是模型装箱的唯一说明书,写它就是写代码;
  4. stream 三档订阅:updates 看进度、values 渲染、debug 排查;token 级用 astream_events;
  5. 一切 HITL 的前提是 checkpointer;interrupt_before=['tools'] 是最常用的审批位;
  6. 恢复动词语义:None 输入=继续,新输入=重跑,换 thread_id=清场;
  7. get_state_history 是时光机——拿历史现场重新 invoke,即可「换一种答案」;
  8. 并发策略默认两档起步:不同用户→独立 thread;同用户连发→enqueue 或 interrupt;
  9. 状态设计要预演「被打断在任意两行之间」:工具调用-结果配对是最容易卡死的裂缝;
  10. fork-and-merge 是终点形态,但先问自己:我的状态能无冲突合并吗——不能就别上。

7. 原文地图

主题原书章原文位置
权衡回顾与前沿类比Chapter 8text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:7(搜「trade-off between agency」) · text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:13(搜「frontier」)
三目标定义Chapter 8text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:17(搜「Latency」)
目标互斥与极值取消Chapter 8text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:29(搜「does nothing at all」)
外推目标Chapter 8text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:33(搜「shift the frontier outward」)
四手段清单Chapter 8text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:35(搜「Streaming/intermediate output」)
结构化输出三策略Chapter 8text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:59(搜「ask the LLM (very nicely)」) · text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:63(搜「fine-tuned to pick from a list」) · text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:67(搜「valid JSON document」)
统一接口与校验Chapter 8text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:69(搜「with_structured_output」)
Joke schema 与实拍输出Chapter 8text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:79(搜「punchline」) · text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:129(搜「cheetahs」)
字段 description 的作用Chapter 8text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:90(搜「decide what part of the output」)
低温与校验报错Chapter 8text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:136(搜「invalid output」)
多步=耗时叠加Chapter 8text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:146(搜「nodes) connected in sequence」)
流式伞定义Chapter 8text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:148(搜「while it is still running」)
stream_mode 三档Chapter 8text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:241(搜「values」) · text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:243(搜「debug」)
token 级事件Chapter 8text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:272(搜「on_chat_model_stream」)
HITL 前提:checkpointerChapter 8text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:317(搜「key to enabling human-in-the-loop」)
interrupt 模式Chapter 8text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:319(搜「manually interrupts」)
事件/信号实现细节Chapter 8text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:385(搜「aclosing」)
authorize 与 interrupt_beforeChapter 8text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:389(搜「hand off control to them」) · text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:434(搜「order is not important」)
resume 的 None 语义Chapter 8text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:438(搜「null input (or None」)
restart 合并语义与换 thread 清场Chapter 8text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:498(搜「merge it with the new input」) · text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:500(搜「change the thread_id」)
edit state 打补丁Chapter 8text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:504(搜「update_state」)
fork 时光机Chapter 8text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:534(搜「get an alternative answer」) · text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:559(搜「most recent first」)
混用的力量Chapter 8text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:561(搜「mixing them」)
double texting 为什么要答Chapter 8text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:565(搜「more and more complex use cases」)
拒绝与独立处理Chapter 8text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:569(搜「Refuse concurrent inputs」) · text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:575(搜「two separate and unreconcilable」)
排队的利弊Chapter 8text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:581(搜「arbitrary number」) · text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:589(搜「may be stale」)
打断四变体Chapter 8text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:593(搜「abandon processing」)
OpenAI 消息配对失效例Chapter 8text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:613(搜「immediately followed by tool messages」)
fork-and-merge 与 CRDTChapter 8text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:619(搜「conflict-free replicated data types」)
半成品自评与 Platform 预告Chapter 8text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:599(搜「not generalize beyond the simplest」) · text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:621(搜「LangGraph Platform」)
小结Chapter 8text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:625(搜「partially beat the odds」)

Footnotes

  1. 出处:「Chapter 8. Patterns to Make the Most of LLMs」第 13 段(text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:13,搜「a frontier」);类比出处见该页脚注 1(text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:633,搜「efficient frontier」)。

  2. 出处:「Chapter 8. Patterns to Make the Most of LLMs」第 29 段(text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:29,搜「does nothing at all」)。

  3. 出处:「Chapter 8. Patterns to Make the Most of LLMs」第 33 段(text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:33,搜「For the same level of reliability」)。

  4. 出处:「Chapter 8. Patterns to Make the Most of LLMs」第 57–68 段(text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:59,搜「suggestion to the LLM and not as a guarantee」)。

  5. 出处:「Chapter 8. Patterns to Make the Most of LLMs」第 138 段(text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:138,搜「validation error will be returned」)。

  6. 出处:「Chapter 8. Patterns to Make the Most of LLMs」第 104–130 段(输出在第 127 行,text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:128,搜「poker」)。

  7. 出处:「Chapter 8. Patterns to Make the Most of LLMs」第 90 段(text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:90,搜「This is key because」)。

  8. 出处:「Chapter 8. Patterns to Make the Most of LLMs」第 136 段(text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:136,搜「Low temperature is usually a good fit」)。

  9. 出处:「Chapter 8. Patterns to Make the Most of LLMs」第 146 段(text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:146,搜「increasing」)。

  10. 出处:「Chapter 8. Patterns to Make the Most of LLMs」第 231–249 段(text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:241,搜「current state of the graph」)。

  11. 出处:「Chapter 8. Patterns to Make the Most of LLMs」第 251–253 段(text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:251,搜「combine these modes」)。

  12. 出处:「Chapter 8. Patterns to Make the Most of LLMs」第 255–297 段(text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:297,搜「each token」)。

  13. 出处:「Chapter 8. Patterns to Make the Most of LLMs」第 303–317 段(text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:317,搜「remembering the previous state」)。

  14. 出处:「Chapter 8. Patterns to Make the Most of LLMs」第 319–325 段(text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:319,搜「as of the last complete step」)。

  15. 出处:「Chapter 8. Patterns to Make the Most of LLMs」第 385 段(text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:385,搜「abort exception」)。

  16. 出处:「Chapter 8. Patterns to Make the Most of LLMs」第 389–395 段(text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:389,搜「tool confirmation」)。

  17. 出处:「Chapter 8. Patterns to Make the Most of LLMs」第 434 段(text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:434,搜「order is not important」)。

  18. 出处:「Chapter 8. Patterns to Make the Most of LLMs」第 436–500 段(resume 第 436 行起,restart 第 461 行起)。

  19. 出处:「Chapter 8. Patterns to Make the Most of LLMs」第 532–534 段(text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:534,搜「visited again」)。

  20. 出处:「Chapter 8. Patterns to Make the Most of LLMs」第 559 段(text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:559,搜「sorted with the most recent first」)。

  21. 出处:「Chapter 8. Patterns to Make the Most of LLMs」第 561 段(text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:561,搜「true power」)。

  22. 出处:「Chapter 8. Patterns to Make the Most of LLMs」第 565 段(text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:565,搜「will continue to be a challenge」)。

  23. 出处:「Chapter 8. Patterns to Make the Most of LLMs」第 593–601 段(text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:595,搜「Keep nothing」)。

  24. 出处:「Chapter 8. Patterns to Make the Most of LLMs」第 617–619 段(text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:619,搜「best option overall」)。

  25. 出处:「Chapter 8. Patterns to Make the Most of LLMs」第 613 段(text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:613,搜「defensively clean up」)。

  26. 出处:「Chapter 8. Patterns to Make the Most of LLMs」脚注 1(text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:633,搜「Pareto front」)。

  27. 出处:「Chapter 8. Patterns to Make the Most of LLMs」第 621 段(text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:621,搜「covered in Chapter 9」)。

  28. 出处:「Chapter 8. Patterns to Make the Most of LLMs」第 599 段(text/11-ch08-chapter-8-patterns-to-make-the-most-of-llms.txt:599,搜「likely to not generalize」)。