智能体框架:抽象运行循环,但不外包正确性与控制权
从模型适配、工具注册、状态图、持久化和追踪,到抽象泄漏、版本迁移与逃生口,判断何时框架值得引入。
- 手写最小循环暴露真实需求
- 列出必须能力与失败模式
- 选择最薄候选框架
- 用领域schema隔离状态
- 实现重试取消和权限
- 保留原始请求与逃生口
- 故障注入并比较TCO
- 用契约轨迹灰度升级
1先理解最小循环,再选择框架定位
一个 Agent 的核心运行时其实只需要极少量的代码,少到可以把它完整写在白板上。它的最小循环只包含六个动作:组装上下文、调用模型、解析工具意图、执行工具、记录观察、判断完成还是继续。
组装上下文,是把系统提示、当前对话历史、可用的工具说明和外部检索到的信息拼成一次完整的模型输入。调用模型,是把这份输入交给语言模型,拿到一次输出。解析工具意图,是识别模型这次输出里是否表达了“我要调用某个工具”的意图,以及要调用的工具名称和参数。执行工具,是把解析出的参数真正传给对应的函数或外部接口。记录观察,是把工具执行后返回的结果追加进上下文,作为下一轮模型的输入。判断完成或继续,是决定这轮结果已经满足用户目标、可以结束,还是需要带着新的观察再走一轮。
这六个动作首尾相接,就构成了 Agent 的最小运行循环。它不需要任何框架,用几十行顺序代码就能实现。理解这个循环,是选择智能体框架的前提,因为所有框架本质上都在做同一件事:把这六个步骤产品化。
框架在产品化的过程中,会在最小循环之上叠加额外的能力。它可能加入路由,根据意图把请求分发到不同的工具或不同的子 Agent;可能加入检查点,在关键步骤保存状态,便于失败后从中间恢复;可能加入人工中断,在敏感操作前暂停等待人类确认;还可能加入追踪,记录每一轮循环的输入输出以便观测和调试。这些能力有价值,但它们都是对那六个步骤的增强,而不是对六个步骤的替代。
因此,智能体框架可以被定义为一类开发组件,它把模型调用、工具循环、状态保存和追踪封装成一个运行时。它的输入有两个:一个是团队已经理解的最小 Agent 循环,另一个是团队明确遇到的具体工程痛点。它的输出,是更少的样板代码,以及可选的路由、检查点和观测能力。
这里的关键在于因果方向:框架减少的是实现摩擦,而不是定义业务成功。一个写工具循环要重复上千行的团队,引入框架能立竿见影地省掉样板代码;一个连“为什么需要 Agent、需要它解决什么问题”都说不清的团队,引入框架并不会让目标变得清晰。当需求本身模糊时,一个厚重的框架不会提供答案,它只会把一个原本可见的循环封装成不可见的调用栈,让团队更难看懂系统实际在做什么。所以正确的顺序永远是先写下并理解那个最小循环,再判断框架能在其中替你承担哪一部分。
2框架通常横跨五个不同层分层
“支持 Agent”是一句被过度压缩的话,它可能只意味着框架能调用某个模型接口,也可能意味着框架带了一整套持久化工作流。要比较两个都声称“支持 Agent”的框架,需要把它们拆开来看。多数框架的能力横跨五个不同的层,每层有自己的典型职责,也有一条共同的边界:框架不该替业务做决定。
第一层是模型适配。这一层负责请求的构造、流式输出的处理,以及把工具描述转换为模型能理解的工具 schema。它回答的问题是:这个模型能不能胜任当前任务。框架在这一层把“调一次模型”的细节封装好,但它不能替团队判断模型是否适合任务,这个判断始终属于使用方。
第二层是运行循环。它负责调用模型、执行工具、处理终止条件。这是最小循环里最核心的部分。框架在这一层能提供的是把循环跑起来并让它可靠地停下,但它不能替业务决定“什么算完成”、什么风险预算是可接受的。完成条件与风险预算必须由业务自己定义。
第三层是状态与图。它负责节点、分支和检查点的表达与持久化。框架可以用状态图把多步流程显式化,在检查点保存中间状态,但它不能替业务定义领域对象的语义。状态图只是载体,领域里“一个订单处于什么状态、状态之间如何合法迁移”这些语义仍然属于业务。
第四层是扩展生态。它包含连接器、记忆和插件。这一层决定了框架能与多少外部系统对接、能开箱即用地获得多少能力。框架能提供生态的广度,但不能替业务承担供应链信任。引入一个插件等于引入一段第三方代码,信任与否的判断权在团队手里。
第五层是观测与部署。它包含 trace、队列和评测。这一层让系统在生产中可见、可排布、可度量。框架能产出 trace 和指标,但不能替业务判断哪些指标代表真实价值。指标是观测的手段,价值是业务的目标。
把框架拆成这五层之后,“支持 Agent”这个模糊的说法就可以被替换成五个具体问题:它支持哪些模型适配方式、运行循环怎么配置终止条件、状态图怎么表达和持久化、生态里有多少可信任的连接器、观测部署能给出什么指标。逐层比较,才能避免把完全不同程度的能力当成同一件事。
最后要记住一个不对称的关系:层级覆盖得越多,并不等于越适合生产。一个把五层全部覆盖的框架,可能每层都只有浅层实现;一个只扎实覆盖运行循环和观测的框架,反而可能更适合真实的线上系统。无论框架覆盖多少层,业务仍然必须自己拥有目标、权限、领域语义和价值指标——这四样东西,框架一层都替不了。
| 层 | 典型职责 | 不应替业务决定 |
|---|---|---|
| 模型适配 | 请求、流式、工具schema | 模型是否适合任务 |
| 运行循环 | 调用、工具、终止 | 完成条件与风险预算 |
| 状态/图 | 节点、分支、检查点 | 领域对象语义 |
| 扩展生态 | 连接器、记忆、插件 | 供应链信任 |
| 观测/部署 | trace、队列、评测 | 指标是否代表真实价值 |
3完整示例:从手写客服循环到持久状态图案例推演
判断“加一个框架”是否值得,最可靠的依据不是框架的宣传,而是它能不能让一个原本靠手写 if/else 支撑的循环变得更清楚。用一个客服系统的完整演进过程来说明。
初版客服只有一条主路径:分类、查订单、回答。这个阶段用顺序的 if/else 手写循环是最透明的——代码短,控制流一眼可见,任何一层框架都只会增加理解成本,不会带来收益。真正的转折发生在需求开始叠加之后。
第一个新增需求是退款需要人工批准。这意味着循环里多出了一个不能由模型自行完成的分支:提出退款动作之后,必须停下来等待人类确认,而不是继续往下走。第二个需求是等待回调可以跨天。一次退款回调可能要隔一天才返回,进程不能一直阻塞等待,也不能因为进程重启就丢掉“正在等待回调”这个事实。第三个需求是失败后从检查点恢复。一旦中途出错,系统不能从头重跑整个对话,而必须从出错的步骤之前继续。第四个需求是不同租户的工具权限不同。同一个工具循环,不同租户能调用的工具集合不一样,权限判断不能写死在全局。
这四件事叠加之后,纯粹的顺序 if/else 开始失效:控制流不再是一条直线,而是有持久等待、有人工断点、有恢复点、有按租户分叉的路径。这时把手写循环中的隐含状态显式化才有价值。把状态建模为 {case_id, intent, order, proposed_action, approval, result} 这样一组字段:case_id 标识案件,intent 记录分类结果,order 保存查到的订单,proposed_action 保存模型提议的下一步动作,approval 记录人工批准的状态,result 保存最终结果。然后用显式的节点表示这些状态,用条件边表示状态之间的合法迁移,让“等待回调”这种跨天的持久等待成为图上一个明确的节点,而不是隐藏在代码某个 await 里的隐式状态。到这一步,框架才开始提供实质价值。
迁移本身要按一个克制的顺序进行,而不是一步到位地引入框架的所有功能。第一步,先用两个真实案例,把手写循环产生的事件轨迹原样记录下来——每一步的输入、工具调用和结果。第二步,在框架里复现这同一条状态转移路径,先不加入任何框架独有的额外功能,目标是让框架版的行为与手写版完全一致。第三步,做故障注入:中途杀掉进程,然后验证系统能从批准前的状态恢复,并且不会重复发起退款。这个验证直接决定迁移是否成功,因为重复退款是不可接受的业务事故。第四步,把原始请求、token 消耗、工具参数和最终业务状态放在一起比较,确认框架版没有悄悄改变行为。第五步,保留一条不经过框架、直接调用工具和模型的降级路径,保证框架出问题时业务仍能继续。
验收标准只有一条:框架版必须在恢复或调试上带来真实的改善。如果框架版只是把同一套逻辑画成了更漂亮的图,却不能改善失败恢复,也不能让排错更容易,那么它就没有证明迁移的价值。图画得漂亮从来不是验收标准,恢复和诊断的改善才是。
4状态属于领域,消息只是一个视图状态设计
把全部业务数据塞进框架的 message 列表,是最容易让迁移彻底锁死的一种做法。message 列表里存的是对话历史的自然语言文本,它的设计目的是为了生成下一轮回复,而不是为了承载可查询、可迁移、可约束的业务事实。一旦订单 ID、批准状态、证据和幂等键只藏在自然语言历史里,它们就变成了散落在句子中间的字符串,无法被数据库索引,无法做约束校验,也无法在换框架时成块地搬走。
正确的方向是反过来:领域状态拥有自己的、带版本号的 schema,而消息只是从这个状态派生出来的一个视图。订单 ID、批准状态、证据和幂等键这些数据,先在领域 schema 里有一个权威的、结构化的位置,然后在需要生成模型输入时,再把它们投影成消息。这样消息列表随时可以从状态重建,删掉整个历史都不会丢任何业务事实。
版本化是这里的关键机制。领域 schema 会随业务演进,旧的持久化数据用的是旧版本。框架的检查点不直接保存全部业务对象,而是保存状态版本和事件引用。恢复时,系统根据版本号执行相应的迁移,把旧版本的数据转换成新版本,再继续运行。这一机制让“换模型、换消息格式、换框架”这三件事都变成局部的替换:模型和消息格式只是状态到文本的投影方式,框架只是运行时外壳,业务数据库和它里面的领域对象始终不动,不需要重写。
并发是另一个必须显式处理的场景。当多个分支同时更新状态时,如果用一个全局可变字典承载状态,两个用户的操作就会互相串写——一个人写入的字段被另一个人的更新覆盖。因此并发分支的更新需要明确的归并规则:每个分支产生自己的更新,再按预先定义好的规则合并进权威状态,而不是各自直接改写同一个共享对象。
综合起来,领域状态设计的输入是订单、批准、证据、幂等键和版本号这些业务实体,输出是一套独立于框架消息的 schema 以及配套的迁移记录。消息从状态派生,检查点保存版本和事件引用,并发分支按明确规则归并。框架的 message 列表始终只是一个视图,它不能成为业务的唯一真相,也不能成为跨租户共享的字典。
5原创图:框架在领域控制与外部系统之间提供运行时可视化
这张分层图回答一个根本问题:在智能体系统的架构里,哪一层应该随时能被替换,哪一层必须由应用自己拥有。答案可以用一条清晰的界线划分。
图的最上层是应用自己拥有的领域层。这一层定义目标、状态、权限和验收。目标是系统要达成的业务结果,状态是版本化的领域数据,权限是每个租户能做什么的约束,验收是判定一次运行是否成功的标准。这四样东西决定系统的正确性和控制权,因此必须留在应用的领域层,不能外包给任何框架。
图的中间是框架运行时。它提供运行循环、图、检查点和追踪。运行循环负责反复执行模型调用和工具调用,图负责表达状态和状态之间的迁移,检查点负责在关键位置保存可恢复的状态,追踪负责让每次运行可见。这些是纯机制性的能力,可替换,也正是框架存在的价值所在。
图的下方是适配层,把模型、工具、存储和队列这些外部系统接入运行时。模型适配处理请求和流式输出,工具适配把工具描述转换成模型可理解的 schema 并执行调用,存储适配承载领域状态和检查点,队列适配承载跨进程的异步任务。
这条分界意味着:应用拥有目标、权限、验收和 schema;框架只承载循环、图、检查点和追踪。框架在整个架构里是一个可替换的运行时,而不是正确性的来源。
这里要精确理解“可替换”的含义。说框架可替换,指的是它的接口与测试隔离做得足够好,使得在需要时可以换掉它而不牵连领域层;它并不意味着任意两个框架可以无损互换。一个框架的循环调度策略、检查点格式、追踪数据结构都可能与另一个不同,替换仍然需要迁移工作。可替换描述的是边界清晰,而不是承诺零成本。
6重试、取消和终止语义必须可见可靠性
框架提供的“自动重试”是一个容易被高估的便利,因为它可能在用户看不见的地方悄悄重复副作用。要正确使用重试,必须区分调用类型。
模型调用和只读工具的重试策略可以比较宽松:一次模型请求因为网络抖动失败,重试一遍是安全的,因为模型调用本身不改变外部世界。但写工具完全不同。一次退款请求超时了,可能退款实际上已经成功,只是响应没有回来;此时自动重试就会发起第二次退款,造成重复扣款。因此写操作必须带上幂等键——一个唯一标识这次操作的键,让外部系统在收到重复请求时能够识别并去重;同时要配合结果查询,在超时后先问一句“刚才那次到底成功了没有”,再决定是否重发。
取消语义必须完整地传播。用户点了取消,这个信号不能只停在一个地方,它要一路传下去:传向正在进行的模型流、正在执行的工具、以及由它们派生的子任务。如果取消只在入口处生效,而已经在跑的工具和子任务继续执行,用户以为取消了,副作用却还在发生。
终止条件同样需要显式定义。当达到步数上限、耗尽预算、连续多轮无进展、或者已满足完成条件时,运行必须明确终止。最危险的是默认的无限重试:它会把一次永久性错误反复重放,不断消耗 token 和预算,同时制造重入——同一个操作被反复触发。成本在沉默中膨胀,而日志里只留下一个笼统的“失败”。
所以框架应当暴露每次物理尝试的完整信息:退避策略、超时、以及最终状态,而不是把一个多轮尝试的过程压缩成一句“失败”。只有看到每一次尝试,运维者才能判断这是一次暂时性错误的正常重试,还是一次永久性错误被反复放大。
把可靠性的语义整理一下:它的输入是调用类型、错误、幂等键、预算和取消信号;输出是重试、查询、取消或终止这几类动作。模型与只读工具可以按错误做有界重试;写工具要先查询结果并去重;取消要传播到子任务。而框架的“自动重试”在本质上只是“再次调度一次”,它不判断错误是否永久,也不替你消除副作用——它可能放大永久错误和副作用。
7抽象泄漏不是例外,而是调试入口逃生口
统一的模型接口是框架最吸引人的卖点之一:写一次代码,换任意模型。但它也经常把新能力或错误细节遮住。原因是不同供应商在工具调用、缓存、推理、流式事件和错误码这些方面并不完全同构——它们表面都叫“调用模型”,底层行为和返回结构各不相同。
面对这种差异,框架有两种常见做法,各有各的代价。一种是最低公分母适配器:只暴露所有供应商都支持的最小能力集,其余一概丢弃。这样做的代价是丢能力——某家模型特有的缓存提示、推理参数或流式事件在适配层就被抹掉了。另一种是厚封装:把供应商的差异翻译成统一的内部表示。这看起来更完整,但翻译过程可能改写参数或消息,把模型真正收到的东西变得和业务代码以为的不一样,排错时对不上。
因此框架必须留出抽象逃生口。具体来说,要保留原始请求和响应、保留供应商的扩展字段、保留一条绕过适配器直接调用模型的路径,并且让适配器可插拔、可替换。业务代码则有一个反向的约束:不得依赖框架内部那些不可导出的对象。一旦业务代码攥住框架的私有内部实现,框架的适配器就无法替换,逃生口就形同虚设。
初学者最容易混淆的一点是:接口统一不等于语义等价。两个模型都能被同一个统一接口调用,不代表它们对同一条指令产生同样语义的结果。换一个适配器、换一个模型之后,之前通过的任务评测和安全评测都可能失效。所以更换适配器之后,必须重新运行任务评测、安全评测和成本评测,不能因为“接口没变”就假定行为没变。
抽象逃生口的输入是供应商的原始请求响应、扩展字段和框架的适配结果,输出是可诊断的差异和直接调用路径。它解决的问题,正是统一接口遮蔽新能力和错误细节的问题。抽象泄漏在这里不是需要被修复的缺陷,而是调试的入口:当结果不符合预期时,正是这些泄漏出来的原始细节告诉你,差异到底发生在哪一层。
8插件与连接器扩大供应链和权限面安全
安装一个社区工具包,看起来只是引入一个便利函数,实际上等于在进程里执行第三方代码。连接器的代码和你的业务代码跑在同一个进程里,它可能读取环境变量、读取文件、访问网络;它注册的回调可能修改系统状态;它附带的默认提示词也可能注入隐藏的规则,悄悄改变模型的行为。这些动作在安装时不会全部摊开给你看,只有在运行后才可能暴露。
所以插件治理要先从锁定供应链开始。锁定版本与哈希,确保今天安装的包和昨天审查的是同一个;审查维护者的背景、许可证和它依赖的其他包,因为风险会沿依赖链传递。安装成功或者来源流行,都不能证明供应链可信——流行只说明用的人多,不说明没人检查过它。
运行环境要用最小权限沙箱。连接器只获得它完成任务所必需的那一点点权限,而不是整个进程的权限。工具要逐项允许,而不是默认全部放行。开发环境和生产环境的凭证必须分开,绝不能让一个只该在测试里跑的工具读到生产的密钥。
升级框架本身也是一次重新审查的机会。框架的新版本可能改动隐式的默认值,也可能附带迁移脚本。隐式默认值变了,意味着没有显式配置的地方行为会悄悄改变;迁移脚本本身也是要执行的代码。所以升级时要把这两样重新审计一遍,而不是只盯着变更日志的标题。
把插件治理的机制整理一下:它的输入是连接器代码、依赖、权限、提示词和生产凭证;输出是允许、沙箱限制、版本锁定或拒绝这四种处置。社区包可能读取环境、网络和文件,因此必须逐工具允许,并在每次升级时重新审计默认值。这里的核心结论是:供应链的可信度必须靠审查建立,不能靠安装成功或来源流行来推断。
9选型实验要测故障,而不仅是开发速度决策
评估框架时,最常见的偏差是只测开发速度:一个框架用两天就能搭起原型,另一个要五天,于是两天的那家看起来赢了。但这个比较漏掉了更长周期里的成本。真正的问题是:框架帮你省下的两天开发时间,会不会被它后来每月升级和故障排查多花的成本全部抵消甚至倒亏。
选型实验要用 time-box 的方式,在限定时间内对两个代表性任务做试验,然后比较一组指标,而不只是“多久写出第一版”。这组指标包括:首次实现时间、任务成功率、P95 延迟、token 与工具成本、trace 覆盖率、进程崩溃后的恢复能力、人工中断的处理,以及升级回归的成本。首次实现时间只回答“上手快不快”,其余指标回答“它能否稳定地跑下去、出问题时能否查得清、升级时会不会破”。
把总拥有成本写成一个求和式。选定时间范围内的总拥有成本 TCO 等于五项之和:初始开发成本 Cdevelop、日常运行成本 Crun、故障诊断成本 Cdiagnose、升级迁移成本 Cmigrate、供应商锁定成本 Clockin。每一项对应一个明确的来源——写第一版、每天跑、出了故障去查、版本升级和迁移、以及未来想离开时被锁住的代价。这个公式的价值在于强迫团队把“省下的开发时间”放回全部成本里一起看,而不是只看其中一项。
这里有一个决定性的判据:如果故障发生时无法定位到原始的模型请求、工具参数和状态版本,那么框架再丰富的组件也是负资产。组件越多,运行时越复杂,故障点越多;而一旦追踪能力跟不上,这些组件只会让排错变成在大片不透明的调用栈里猜。一个能快速开发但无法诊断的系统,长期成本远高于一个开发稍慢但透明可查的系统。
因此选型的原则是:选择能满足当前必须能力的最薄一层框架,同时为退出成本设一个上限。最薄层意味着不引入用不到的能力,退出成本上限意味着在决策时就考虑好将来离开要付多少代价。选型实验的输入是代表性任务、候选框架以及这五类成本,输出是在同一预算约束下的质量、延迟、恢复能力和 TCO。time-box 实验要主动注入崩溃和人工中断,并且要求能定位原始请求和状态。最后要清醒地看待 TCO 公式:它是一个决策框架,不是精确预测,各项的权重和时间范围必须由团队根据自己的实际估计来设定。
10迁移靠契约测试,不靠重新看一遍 Demo演进
框架大版本升级之后,最容易悄悄变化的行为往往不在显眼的功能列表里。一个升级可能改动工具调用的参数格式、改动终止条件的默认值、改动重试策略,或者改动提示词的隐式默认注入。这些变化不会让系统立刻崩溃,却会让行为悄悄偏移。靠重新看一遍 Demo 是发现不了它们的——Demo 只展示主路径,故障边界和权限边界都不在里面。
可靠的迁移方式是把契约测试作为地基。先冻结一组关键轨迹,也就是把真实运行中发生过的重要案例固化下来。每条轨迹要记录:输入状态、期望的工具调用序列、权限拒绝的情况、人工暂停、超时、恢复,以及最终的业务状态。这七样东西共同构成一份契约,描述“在这些输入下,系统应该做出这一系列动作并到达这个结果”。
升级时,用新框架重放这些轨迹,比较的是事件与副作用,而不是自然语言是否逐字一致。模型输出的措辞可以不同——只要它调用了正确的工具、保持了同样的权限边界、产生了同样的副作用、到达了同样的业务状态,措辞变化就是可接受的。反过来,如果权限拒绝被忽略、状态跳过了某一步、副作用多了一次或少了一次,那么即使自然语言看起来一模一样,也是迁移失败。
迁移的执行顺序也有讲究。状态 schema 要先迁移:先把领域数据升级到新版本并验证通过,再让新实例接手。运行过程中,新旧实例要分开——不要一边跑新代码一边读旧数据,也不要新旧实例共享同一个状态存储,以免互相污染。然后灰度上线:小流量切换到新框架,观察成本、延迟和失败分布的变化,确认没有恶化后再逐步扩大。
把迁移契约测试的输入输出整理一下:输入是冻结的状态、工具序列、权限拒绝、人工暂停、超时、恢复和业务结果;输出是旧版与新版在事件与副作用上的差异。顺序是先迁移 schema,再分开运行新旧实例并灰度观察。验收的底线是:自然语言不必逐字相同,但权限、状态和副作用边界不得静默变化。
11自建与引入不是二选一,而是逐层购买抽象架构决策
“自建还是引入”经常被当成一个非此即彼的选择,实际上它应该是逐层购买抽象的过程。一个团队需要追踪能力但不需要状态图,这并不意味着必须采用一个完整的 Agent 框架——追踪和状态图是两样不同的东西,可以分开来买。
按层做决策是核心方法。模型 SDK、工具 schema、追踪、队列、持久图这些能力,可以分别来自不同的组件,不必捆绑在一个框架里。先购买那些难以稳定自建的横切能力,比如可观测后端——自己维护一个追踪系统成本极高,而成熟的追踪后端开箱即用;同时保留团队自己写的简单领域循环,因为它承载着业务语义,没必要交给框架。只有当状态恢复、人工中断或图路由这些需求反复出现时,才引入对应的运行时。
一个按需求升级的选择序列可以这样展开。当需求只是单轮工具调用,最小选择是供应商 SDK 加一个薄封装,暂时不需要更多;升级信号是“多模型兼容真实发生”——当团队真的需要跑第二个模型时,才考虑引入模型适配层。当需求变成多步短循环,最小选择是手写循环加 trace,此时升级信号是“恢复或分支难以维护”——一旦手写循环里的恢复逻辑和分支开始失控,就该引入状态能力。当需求涉及跨天状态,最小选择是持久工作流或状态图,升级信号是“人工任务与版本迁移”出现——有了人工断点和 schema 迁移,才真正需要持久状态图。当需求是多角色协作,最小选择是任务队列加契约,升级信号是“动态委派确有收益”——只有当任务真的需要动态分派给不同角色时,才引入协作运行时。
每一层决策都必须记录在案。决策记录要写明采用的原因、被拒的方案、退出条件以及所有者。没有这条记录,框架就会因为惯性而不是价值不断扩张:今天为了追踪引入一个组件,明天它顺手带了状态图,后天团队开始使用状态图,没人再问这些能力是否还被需要。逐层购买抽象的输入是真实需求、已有组件和升级信号,输出是 SDK、追踪、队列、状态图等能力的最薄组合。先购买横切能力,只有恢复、中断或图路由反复出现时才加厚运行时。自建与引入的混合,说明责任是可拆分的;要阻止的,是框架在没有退出条件的情况下凭借惯性不断扩张。
| 需求 | 最小选择 | 升级信号 |
|---|---|---|
| 单轮工具调用 | 供应商SDK+薄封装 | 多模型兼容真实发生 |
| 多步短循环 | 手写循环+trace | 恢复/分支难维护 |
| 跨天状态 | 持久工作流/状态图 | 人工任务与版本迁移 |
| 多角色协作 | 任务队列+契约 | 动态委派确有收益 |
12把因果链连起来综合
把前面各节的内容连成一条因果链,就能看到这个概念怎样从一个初始问题一路走到可验证的实践。整条链条从手写开始,到灰度升级结束,每一步都是前一步逼出来的。
第一步,手写最小循环,用它暴露真实需求。不急着选框架,先亲手写出组装上下文、调用模型、解析工具意图、执行工具、记录观察、判断完成或继续这六个动作。只有当真实的业务需求在手写代码里暴露出来——比如需要人工批准、需要跨天等待、需要从检查点恢复——团队才知道自己到底缺什么。
第二步,列出必须具备的能力与失败模式。把需求翻译成一张清单:需要哪些工具、哪些权限边界、哪些重试与取消语义、哪些状态迁移,同时把可能出错的场景——崩溃、超时、重复副作用、并发串写——明确写下来。这张清单是后续所有决策的依据。
第三步,选择最薄的候选框架。按五个层逐层比较,只引入能解决清单上问题的能力,不引入用不到的层。选择满足当前必须能力的最薄一层,并为退出成本设上限。
第四步,用领域 schema 隔离状态。把订单、批准、证据、幂等键和版本号放进独立的、版本化的领域状态,消息只从状态派生。这样业务真相不依赖框架的消息列表,未来换框架不牵连业务数据库。
第五步,实现重试、取消和权限。区分模型调用、只读工具和写工具的重试策略,给写操作加幂等键和结果查询;让取消信号一路传播到模型流、工具和子任务;按租户逐项允许工具,并隔离开发与生产凭证。
第六步,保留原始请求与逃生口。保留供应商的原始请求响应和扩展字段,保留绕过适配器直接调用的路径,让适配器可插拔,同时禁止业务代码依赖框架的不可导出内部对象。接口统一不等于语义等价,换适配器后必须重跑评测。
第七步,故障注入并比较 TCO。在 time-box 实验里主动注入崩溃和人工中断,比较首次实现时间、任务成功率、P95 延迟、token 与工具成本、trace 覆盖、恢复能力和升级回归,再把这些与总拥有成本公式放在一起算,确认框架不是在拿开发速度换长期的诊断和迁移成本。
第八步,用契约轨迹灰度升级。冻结一组关键轨迹——输入状态、期望工具序列、权限拒绝、人工暂停、超时、恢复和最终业务状态——用新版本重放并比较事件与副作用,先迁移 schema,再分开运行新旧实例,灰度观察成本、延迟和失败分布后再扩大。自然语言不必逐字一致,但权限、状态和副作用边界不得静默变化。
这条链从“先理解最小循环”出发,终点是“可验证的灰度升级”。它不要求一步到位地引入框架,也不把框架当作正确性的来源;框架始终只是运行循环、图、检查点和追踪这些机制性能力的载体,而目标、权限、验收和领域语义永远留在团队自己手里。
- LLM Powered Autonomous Agents Survey:智能体组件与挑战综述
- AutoGen:多智能体框架设计
- ReAct:最小推理—行动循环