ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Agent控制权回归:从流程编排到模型自主决策

Agent控制权回归:从流程编排到模型自主决策 最近我在重构内部一套客服Agent翻旧代码时发现一个特别扎心的现象代码量砍掉一半线上效果反而变好。原因是我把花了两周写出来的流程编排逻辑几乎全删了改成让模型自己在每一轮工具调用之后决定下一步。这个改动让我重新开始想一个问题——Agent的控制权到底应该放在哪一层过去一年多圈子里的风向非常明显Agent的控制权正在从外部框架里抽出来交回给模型本身。从Claude Code这类编码Agent带来的体验冲击到agent框架的组件化演进再到底层模型对函数调用、长上下文、多步推理的原生支持控制权转移不是某种理念口号而是被一层一层能力逼出来的结果。这篇文章我想结合自己踩过的坑和重构经验把这个变化彻底讲透。1. 控制权之争从一次让我翻车的多Agent编排说起1.1 我最初理解的“Agent”外部控制流加模型填空两年前我做第一版客服Agent时对Agent的理解非常朴素模型负责说人话逻辑控制全部交给代码。系统里有个router模块先对用户query做意图分类猜到是“订单查询”还是“退款售后”之后再派发给对应的子Agent。每个子Agent内部是一个状态机状态枚举写死在代码里INIT、FETCHING、ANALYZING、ANSWERING每个状态对应一个独立的prompt模板状态之间的迁移由代码里的if-else控制。这套设计当时看起来非常“工程化”。我有明确的流程图有每个状态的输入输出结构有异常跳转。甚至上线前我自信满满觉得这是标准的多Agent系统。但后来发生的线上问题让我明白这种外部控制流本质上是在让模型“填空”而不是让模型“做事”。举个例子用户问“我上周买的充电器坏了能不能换一个新款的”这句话同时踩了三个领域订单查询上周买的、售后政策能不能换、产品知识新款是否兼容。Router只能输出一个标签一旦选中“订单查询”后面的流程就锁死了模型永远没机会发现自己应该再看看退货政策和产品信息。用户换一种说法整条链路就直接失效。1.2 状态机的代价每加一个业务场景就要重画一张图最难受的是维护期。状态机版本跑了两个月业务方提了一堆新需求要支持“已发货但物流停滞”的催单场景要加“优惠券补发”流程要处理“多订单合并退款”。每加一个场景我都要在状态迁移表里加节点、加分支、加重试逻辑。到后面一次发布要改十几个状态代码和prompt一起动改一个地方就担心另外两个地方会不会受影响。后来我把所有状态枚举和迁移逻辑数了一下光状态定义就超过四十个加上每个状态里的prompt模板、输出解析器、重试规则核心编排代码接近两千行。而模型在里面扮演的角色是什么呢就是根据状态去生成一段话。它看不到全局也不知道自己为什么处于这个状态。控制权完全握在外部代码手里模型只是一个表达能力很强的鹦鹉。1.3 一个关键转折把“下一步”交给模型重构的那次契机很偶然。业务方要求支持一个没有明确路径的复杂任务用户描述一个模糊的故障现象需要Agent自己去判断该查订单、查商品还是查知识库甚至要连续查好几次才能得出结论。我在状态图前画了半天发现自己根本穷举不完分支。当时组里有同事提议干脆别走状态机了只给模型定义好工具把用户问题丢进去让它自己决定调用顺序。我一开始非常抗拒觉得这不就是让模型“裸奔”吗但实在被状态机折磨得不行就让它跑了一版demo。结果让我很意外模型在“充电器坏了想换新款”这个例子上自己先调订单接口确认购买时间再查售后政策判断能否换货最后查商品库找新款信息三步走完自然整合答案。整个过程没有任何外部流程参与。那次实验之后我开始认真复盘到底是我画的状态机不够精细还是在Agent架构里控制权本来就应该交给模型答案是后者但理解这一点需要回到更早的技术背景里去看。2. 框架曾经为什么必须抢控制权三个硬伤逼出来的设计控制权回归不代表以前的做法是错的。事实上早期的Agent框架把控制权从模型手里拿走是当时模型能力不够的必然结果。我们批评旧方案“僵硬”但不能无视它在当时解决了真问题。2.1 早期模型不具备可靠的函数调用能力2023年上半年做Agent最痛苦的一件事是让模型“调用工具”。当时没有标准的function calling能力你只能在prompt里写当你需要查询订单时输出JSON格式为{action: query_order, params: {order_id: xxx}}。然后靠代码去解析模型输出的自由文本正则抽取出action和params。这个方案脆弱到什么程度模型只要在JSON前后多说一句“好的我来帮你查询”正则就直接抽不到参数。模型心情好的时候输出标准JSON心情差的时候给你一段Markdown代码块。我被逼着写了各种兜底解析逻辑包括但不仅限于截取第一个{到最后一个}之间的内容、把单引号替换成双引号、递归查找json子串。外部框架之所以要介入是因为模型“伸手”这个动作本身不可靠。没有可靠的函数调用机制你自然不敢把控制权交给模型因为它连第一步工具调用都可能失败。2.2 上下文窗口太小模型看不到全局第二个硬伤是上下文窗口。当时主流模型的上下文在4K到8K左右稍微塞一点系统prompt、用户对话历史、工具返回结果就满了。一个客服Agent需要同时容纳订单结构、商品百科、售后政策、多轮对话记录这些信息量远超当时的上下文容纳能力。因为装不下所以只能分层对话历史由外部存储接管商品信息靠检索接口临时查询订单状态由代码直接访问数据库。然后由编排器决定“什么时候把哪些信息喂给模型”。这种情况下模型当然没有全局视野它永远是“切片式”地看问题。控制权自然是属于那个负责调度和喂数据的外部框架。我当时用LangChain和自研状态机本质就是在做一件事情管理碎片化的上下文。每一轮模型看到的只是我拼好的一个窗口窗口外的一切模型都不知道也不需要知道。2.3 推理链不稳定模型不具备多步自主决策的底气还有一个更根本的问题旧模型的推理链非常脆弱。让模型自己规划“先查订单、再比较商品、最后给出建议”它经常走一半就忘了目标。我遇到过模型在第二步就给出结论、完全跳过后续查询的情况也遇到过被工具返回的长文本带偏、开始复述日志的情况。这种情况下如果让模型主导控制流业务方大概率会被吓到。于是框架的职责里多了一条“强制顺序”代码规定好了先调什么、后调什么、拿到什么字段才可以进入下一步。这本质上是在替模型“补课”因为模型当时连稳定的多步规划都做不到。回头看早期Agent框架之所以那么重不是因为框架作者喜欢搞复杂设计而是底层模型的能力撑不起“自主”两个字。控制权被拿走是能力不足的合理补偿。3. 模型凭什么接得住控制权底层能力的四处变化那为什么现在控制权又能回到模型手里答案也很直接模型底层能力已经悄悄换了一批早期那些硬伤被一项项补齐了。这不是某个单一技术的突破而是四个变化同时发生才形成的结果。3.1 函数调用从“prompt教的”变成“原生的”最关键的转折点是2023年6月主流大模型API开始支持真正的function calling。模型可以在输出里原生返回结构化的tool_calls字段不需要靠正则解析自由文本。这个变化看着不大但对Agent架构来说等于地基换了。以前“模型调用工具”是模型在模仿一个格式现在“调用工具”是模型训练时就掌握的一种原生能力。就好比一个实习生以前需要你反复叮嘱“写邮件要按这个格式”现在他自己清楚什么情况下该写邮件、邮件给谁、格式怎么是标准流程。工具不是被prompt硬教出来的而是长在模型行为里的。有了这个基础让模型自己决定“下一步调用什么工具”才变得可控。3.2 上下文窗口把“切片思维”变成“全局思维”上下文从8K涨到200K、1M这个数字变化被很多人当成营销参数但对Agent架构的影响是实打实的。以前模型只能看一小段切片现在可以把整个任务背景、历史记录、工具返回结果都放进上下文里模型在做判断时能看到全局材料。我自己的体会是上下文变大之后很多以前要靠外部代码维护的逻辑可以“还给”模型。比如客服对话里“这个用户是不是已经问过三次退款”以前要写一个查询去数据库里数历史工单现在直接让模型看历史摘要它自己就能判断。当然长上下文不是没有坑模型对中间位置的记忆还是会弱一些但配合上下文压缩、prompt caching这些手段已经足够支撑自主决策了。3.3 推理能力增强模型开始能自己拆解任务还有一个容易被忽视的变化是推理链路稳定性的提升。新一代模型在多步规划上的表现比一年前的模型强非常多。给它一个复杂目标它能在内部拆解成若干子任务并且关键的是——它会在中间结果不符合预期时主动改变策略。这一点对Agent控制权来说意义很大。以前模型是“一条路走到黑”现在模型会“走着走着发现路不对就换一条”。编码Agent里这种表现最明显改了一次代码后测试仍然失败模型不会傻傻重试它会读日志、猜测原因、换一种改法。这种“从失败中自我修正”的行为是典型的自主控制也是外部流程很难写出来的东西。3.4 工具和输出标准化模型更容易被接上最后一个变化不在模型内部而在模型周围。JSON Schema、OpenAPI、MCP这类协议让工具定义的标准化程度大幅提升。模型在训练时可以接触到大量统一格式的工具描述它使用工具的行为被塑造得更加稳定。MCPModel Context Protocol出现以后工具接入成本又低了一个量级。以前给Agent接一个内部接口要写自定义的调用封装、鉴权、参数映射现在只要写一个MCP server模型就能直接使用这个工具。工具越容易接入模型就越容易在运行时按需选择。控制权回归离不开这套外部生态的成熟。4. 控制权回归的三个明显信号编码Agent、Workflow概念、Evals变化底层能力变了之后行业里的实际形态也在跟着变。我判断控制权回归不是看谁写了多少理念文章而是看三个可观察的信号概念体系、产品形态、评估方式。4.1 行业开始认真区分“工作流”和“Agent”最直观的信号是概念体系的洗牌。过去人人都把“流程编排模型调用”叫Agent但现在业内开始明确把Workflow和Agent区分开Workflow是预定义的代码路径每一步执行什么都是写死的Agent是模型动态引导自己的流程决定下一步做什么和怎么做。这个区分不是咬文嚼字而是直接指向控制权归属。如果你在产品里写死了状态机和分支逻辑那么即使挂了一个模型在上面你做的本质还是Workflow不是Agent。承认这个区分等于承认“真正的Agent必须把控制权交给模型”。现在很多框架设计都围绕这个区别来展开反而让早期的“伪Agent”无处遁形。4.2 编码类Agent用实测证明模型主导的闭环更加可靠第二个信号是编码Agent的爆发。Claude Code、OpenCode这些工具核心逻辑都很像用户下一条指令模型自己去读文件、跑测试、改代码、再验证整个过程完全由模型主导。外部代码只负责提供沙箱、工具接口和权限控制至于“先改哪个文件、再跑哪条命令、用什么方式验证”全由模型自己决定。我试用Claude Code时有几个瞬间印象非常深。一次是它改完代码跑测试挂了它没有把错误抛给我而是自己打开日志文件分析报错原因重新改了一遍代码再次验证。另一次是它连续尝试了好几种方案最后找出一条跟我在网上搜到的解法完全不同的路径。这种“遇到问题自己换思路”的能力是状态机式编排根本无法模拟的。OpenCode把同样思路开源化之后很多团队迅速跟进因为它们用很低的成本就复现了这种体验。4.3 记忆与评估体系的变化从“查库”到“注入”从“单轮打分”到“轨迹追踪”第三个信号藏在两个容易被忽略的地方记忆和评估。以前的Agent记忆系统典型做法是把对话历史向量化存进数据库需要时做相似度检索。这套方案本质上还是“外部状态管理”模型只是个读取者。现在更多团队开始把记忆“工具化”记忆不是由框架决定怎么存怎么取而是变成一个记忆类工具由模型决定何时写入、何时读取、读取哪段记忆。控制权又一次落回模型手里。Evals的变化更明显。早期评估Agent效果大家做的是“单轮打分”——给一段输入看模型输出文本好不好。现在评估一个Agent要看整条轨迹它是否选择了合理的工具、是否绕了远路、有没有多次调用同一个工具、有没有在关键节点做出正确判断。轨迹式评估意味着行业承认Agent的价值取决于“过程控制”而不是“输出文本”。而过程控制的主体恰恰是模型本身。5. 我现在怎么设计Agent把决策点还给模型的具体做法说了这么多趋势再回到实际操作。我现在设计Agent和一年前最大的区别在于四条原则只给工具不给剧本、用上下文管理代替流程编排、用护栏代替硬编码约束、接受token成本换来自主性。下面逐条说清楚。5.1 只定义工具不定义剧本我现在的习惯是先把Agent可能用到的所有工具列出来然后像写API文档一样认真写清楚每个工具的参数和适用场景。工具定义的细节非常关键。一个模糊的描述会导致模型在不能用的场景里硬调用一个准确的描述能帮模型少走很多弯路。比如查询订单工具我以前的描述是“查询用户订单”模型经常在用户还没提供订单号时就去调这个工具返回空结果。后来我把描述改成“仅当用户提供订单号或明确提到某笔订单时使用如果没有订单号先向用户询问”误调用的概率立刻降了很多。工具定义写得好模型自主决策的准确率就能大幅提升。不定义剧本意味着我不再写“第一步做什么、第二步做什么”的代码逻辑。模型拿到用户问题后自己决定先查什么、后查什么。如果中间发现查错了它允许自己换方向。这套逻辑对应到代码层面就是一个简单的agent主循环配合几个工具函数代码量从两千行降到几百行。5.2 用上下文管理代替流程编排早期框架花大力气做流程编排本质是在管理模型看不到的上下文。现在模型上下文够大我反而把设计重心转移到“如何构建一个高质量的工作上下文”上。我现在的做法是每一轮任务启动时不是把所有历史一股脑塞进去而是构建一个“工作台”包含用户的核心目标、对话摘要、当前任务涉及的实体订单号、商品ID、用户等级、相关记忆片段。这个工作台相当于给模型一份精炼的任务简报模型在这个简报基础上自主规划。Context Engineering被我放在比prompt engineering更高的优先级上。prompt写得好只是让模型“会回答”上下文组织得好才能让模型“会决策”。尤其是长上下文场景把最关键的约束和目标放在开头和结尾中间放参考资料模型的利用率会明显高很多。5.3 用护栏约束行为边界而不是约束路径把控制权给模型不代表放任不管。我现在的思路是不在“路径”层面设置限制但在“边界”层面做一个安全护栏。路径层面的限制就是写死流程比如“必须先查A再查B”这种现在基本不用了。边界层面的限制是对“不能做什么”做硬约束比如涉及用户敏感信息时必须脱敏后再回答取消订单或退款操作必须再向用户确认一次调用高危工具删除、批量发送前必须拿到显式权限。这些护栏用代码实现放在工具执行层不干预模型内部的决策逻辑。模型可以自己决定任何路径但在触碰边界时会被强制拦截或要求确认。这套设计的好处是保留了模型的灵活性同时把真正的风险控制在代码手里。5.4 一次重构前后的对比数据不会骗人拿我开头提到的客服系统重构做个直观对比。下表是我整理的核心差异维度旧方案流程编排新方案模型主导状态管理代码里维护枚举表和迁移逻辑模型运行时自行维护任务状态新增业务场景改代码、改prompt、发布加一个工具定义即可复杂分支处理每一条分支都要提前画清楚模型根据中间结果动态调整失败处理靠外部异常捕获和固定重试模型读取错误信息后自我修正代码量约2000行编排逻辑约200行工具定义和主循环知识库/接口接入每个接口都要写适配层通过MCP等标准协议直接暴露核心成本维护状态机的时间token消耗上升需要预算控制换到新方案之后客服Agent的“一次性解决率”从68%升到79%。细看数据会发现提升最大的场景正是那些无法提前穷举分支的模糊需求。数据说明把决策点还给模型至少在需要灵活性的场景里是有效的。6. 控制权回到模型手里之后的新麻烦控制权交给模型不等于高枕无忧。运行一段时间之后我碰到了几个新的问题也琢磨出了一些应对办法。这一节分享给想上车的朋友全是踩出来的经验。6.1 最真实的一次失控模型陷入工具调用循环有一次我做一个文档生成Agent它需要改一个配置文件然后反复运行验证。结果它在run_command和read_file两个工具之间反复跳运行验证、读日志、改配置、再运行验证整整循环了17次最后被max_steps掐断。我看trace的时候发现它之所以一直循环是因为工具返回的日志太长模型根本没找到真正的报错行每次都“以为这次改对了”。这个问题让我意识到给模型完整的工具结果未必是好事。大段日志塞进上下文模型不仅抓不到重点还会浪费大量token。现在我的做法是给工具配置“结果摘要”日志只保留最后若干行中间部分截断报错要单独高亮提炼多个参数返回时按重要性排序。工具结果简洁准确模型的决策质量直接往上走。6.2 可观测性必须跟上记录每次模型决策的理由模型主导流程之后你不能再用“看代码”来理解Agent为什么这么走。这时候必须把可观测性做到位。我现在每个Agent任务都会完整记录trace调用了哪些工具、每个工具输入输出摘要、模型的阶段性判断。遇到效果问题先看trace定位再决定是改工具定义还是改上下文。有一段时间我把prompt改得越来越长效果却越来越差。后来通过trace发现模型根本没用到我精心写的那段复杂prompt反而被工具返回结果带跑了。这个问题如果只看离线评测是发现不了的必须靠运行时的决策链路才能看清楚。Agent的调试逻辑跟传统程序完全不同传统程序debug看堆栈Agent debug看决策轨迹。6.3 成本问题不是回到硬编码的理由但要有预算机制模型主导流程因为存在“试错成本”token消耗确实比硬编码流程高。我见过不少团队因为账单上涨立刻把控制权收回去重新写死流程。我觉得这有点因噎废食。正确的做法是加预算机制而不是退回去。我现在常用三招控制成本给Agent设置最大迭代次数和单任务token预算超了就主动向用户说明并转人工简单决策用便宜快模型复杂判断才让大模型上工具结果默认摘要在千字以内减少重复token消耗。其实Agent的token开销大头往往不是正常的多次调用而是无效循环和垃圾信息返回。把这两类问题修掉模型主导流程的成本完全可以在可控范围内。6.4 哪些场景我现在仍然坚持用固定流程最后说说什么场景下我还是会主动放弃模型控制权选择硬编码流程。简单概括就是两类涉及强合规审计的场景和追求毫秒级确定性响应的场景。交易结算、订单取消、退款路径这类操作每一步都必须有审计记录而且不能由模型自由发挥。比如直接改数据库、批量发消息等一旦模型自主决策出错后果不是token超支而是资损事故。这类系统应该走代码流程模型最多承担“向用户解释流程结果”的角色。另外如果某个任务已经有明确且稳定的最优路径也不需要把控制权交给模型。固定流程零思考成本、零随机性比模型决策更稳定。控制在谁手里不是越“高级”越好而是取决于这个任务的本质确定性任务用代码非确定性任务才值得交给模型。现在的Agent架构选择本质上就是在这个确定性和不确定性的交界处做取舍。把控制权还给模型不是趋势使然而是模型能力到了那个位置。代码的职责回归到“提供工具、守住边界”模型的职责回归到“做决定、走路径”。我重构完这套客服系统后的体会是真正稳定的Agent不是流程最多的那个而是工具最清晰、护栏最清楚、决策最自主的那个。
返回列表