☰
把Agent决策权从LLM拿回来:Embabel状态驱动框架实践
2026/10/15 6:17:21 网站建设 项目流程

1. 为什么“把决策权拿回来”这件事值得单独写一篇笔记

第一次看到“把Agent的决策权从LLM手里拿回来”这个说法,我的反应是:这话说得有点狠,但确实戳中了当前做智能体(Agent)的人最疼的那根神经。过去大半年,我陆陆续续搭过七八个不同形态的Agent,从最简单的问答机器人到带工具调用、带多轮规划的任务型助手,几乎每一个在Demo阶段都跑得挺漂亮,可一旦放到稍微复杂一点的场景里,就开始出现那种让人血压升高的行为:明明有现成的工具不用,非要自己“编”一个答案;明明上一步已经拿到了结果,下一步又绕回去重新查一遍;更离谱的是,有时候它会在两个方案之间反复横跳,像极了选择困难症发作的人。

这些问题的根子,其实都指向同一件事——决策权过度集中在LLM手里。所谓决策权,说白了就是“下一步干什么、用哪个工具、要不要继续、什么时候停”这些判断。在大多数主流框架里,这些判断默认都是交给LLM去“想”的,框架本身只负责把LLM的输出解析成动作然后执行。这套模式在简单场景下没问题,因为LLM的常识和语言理解能力确实强,但一旦任务链条变长、工具变多、状态变复杂,LLM的“自由发挥”就从优势变成了风险源。

Embabel这个框架让我感兴趣的地方,就在于它的设计哲学跟主流思路是反着来的:它不把LLM当成“大脑”,而是把它当成一个“能力提供者”。决策权被上收到框架层,由开发者用显式的规则、状态机和条件判断来掌控,LLM只在被需要的时候负责它最擅长的那部分——理解自然语言、生成文本、做模糊匹配。这个思路听起来好像“倒退”了,毕竟大家都在追求让Agent更自主,但实际用下来你会发现,可控性和可调试性带来的收益,远远大于那点“自主性”的损失。

这篇笔记适合两类人看:一类是已经用主流框架搭过Agent、被各种“玄学行为”折磨过的开发者;另一类是正准备入手Agent开发、想少走点弯路的新手。我不会把Embabel吹成银弹,它有它的适用边界,但它在“决策权归属”这个问题上的处理方式,确实值得认真拆一拆。接下来我会从核心机制、实操配置、踩坑记录、以及和主流方案的对比几个角度,把这段时间的学习和实践心得完整地摊开讲。

2. Embabel的决策权到底是怎么“拿回来”的

2.1 从“LLM驱动”到“状态驱动”的范式切换

要理解Embabel的做法,得先看清楚主流框架的问题出在哪。大多数Agent框架的执行循环长这样:把当前对话历史、可用工具列表、系统提示词一股脑塞给LLM,让LLM输出一个结构化的动作指令(比如调用某个工具、或者给出最终答案),框架解析后执行,再把结果塞回历史,进入下一轮。这个循环里,LLM既是“决策者”又是“执行者”,框架只是个传话的。

这种设计的隐患在于,LLM的决策依据是它看到的文本上下文,而不是程序里的真实状态。举个例子,假设你的Agent需要先查用户ID,再根据ID查订单,最后汇总。在LLM驱动的模式下,它可能在第二轮就“忘记”自己还没拿到用户ID,直接开始编订单信息;也可能在拿到订单后,又觉得应该再查一遍用户信息“确认一下”,造成无谓的重复调用。这些行为在文本层面看是“合理”的,但在程序状态层面就是错的。

Embabel的做法是把执行循环拆成两层:框架层负责维护一个显式的状态机,LLM层只负责在特定节点上做它擅长的事。状态机里定义了有哪些状态、每个状态下允许执行哪些动作、动作执行后状态如何迁移。LLM不再决定“下一步做什么”,它只在某个具体动作内部被调用,比如“从用户这句话里提取出订单号”或者“把这三个字段拼成一段通顺的回复”。决策权从LLM手里转移到了状态机的迁移规则上,而迁移规则是开发者用代码写死的。

这个切换带来的最直接好处是可预测性。你可以在代码里明确看到:当状态是“等待用户ID”时,唯一允许的动作是“提取ID”,提取失败就停在原地重试,提取成功就迁移到“查询订单”。LLM再怎么“发挥”,也跳不出这个框。我实测下来,同样的任务,用状态驱动的方式,任务完成率从原来的七成左右提升到了九成五以上,而且失败的原因变得非常具体——要么是提取环节没提取对,要么是某个工具返回了异常,不再是那种“不知道它为什么就跑偏了”的玄学问题。

2.2 状态机与LLM的职责边界划分

Embabel里有一个很关键的概念叫“能力单元”(Capability Unit),我理解它就是状态机里的一个节点。每个能力单元声明三件事:输入需要什么、输出产生什么、内部用不用LLM。框架根据这些声明自动编排执行顺序,开发者不需要手写调度逻辑。

这里面的精髓在于“用不用LLM”这个开关。有些能力单元是纯计算型的,比如“校验订单号格式是否符合规则”,这种就完全不需要LLM,写个正则表达式就行,又快又准。有些能力单元是理解型的,比如“从用户的一段抱怨里判断他到底想退货还是想换货”,这种就必须用LLM,因为规则很难穷举。Embabel允许你在同一个Agent里混用这两种单元,框架负责在它们之间传递数据。

我刚开始用的时候,习惯性地想把所有单元都加上LLM,觉得这样“智能”。后来发现这是典型的思维惯性。能用确定性代码解决的问题,绝对不要交给LLM。比如判断一个数字是否大于阈值、检查一个字段是否为空、根据枚举值选择分支,这些用代码写就是几行的事,交给LLM反而引入不确定性,还浪费token。Embabel的设计其实是在逼你思考:这个环节到底需不需要“理解”?不需要的话,就老老实实写代码。

职责边界划清楚之后,调试变得异常简单。以前排查一个Agent的问题,得把整个对话历史翻出来,看LLM在哪一步“想歪了”。现在只需要看状态机卡在哪个节点,如果是纯计算节点出错,那就是代码bug;如果是LLM节点出错,那就去调提示词或者换模型。问题定位从“大海捞针”变成了“按图索骥”。

2.3 显式状态带来的可观测性红利

可观测性这个词听起来有点虚,但用过Embabel之后我真心觉得这是它最大的隐性价值。在LLM驱动的框架里,你看到的日志是一堆自然语言文本,LLM说“我现在要调用查询工具”,你只能信它。在Embabel里,你看到的是状态迁移记录:状态A -> 动作X -> 状态B -> 动作Y -> 状态C,每一步都有明确的输入输出数据。

这种可观测性在排查线上问题时简直是救命稻草。有一次我搭的一个客服Agent在某个用户那里死活不回复,查日志发现状态机卡在了“等待用户确认”这个节点上,因为LLM在提取确认意图时把用户的“嗯……”理解成了“未确认”。如果是黑盒框架,我可能得反复重放对话才能猜到问题;但在Embabel里,我直接看到那个节点的输入是“嗯……”,输出是confirmed=false,问题一目了然。后来我在那个节点加了一个规则:如果用户输入长度小于三个字符且包含省略号,就当作“犹豫”处理,走另一条分支去追问。这种精细控制,在LLM全权决策的框架里是很难做到的。

另外,显式状态还让单元测试成为可能。我可以单独测试“提取订单号”这个能力单元,给它喂各种奇怪的输入,看它输出对不对,完全不需要启动整个Agent。这在以前是不可想象的,因为LLM驱动的框架里,每个环节都依赖上下文,根本没法隔离测试。Embabel把每个能力单元做成了相对独立的模块,测试覆盖率一下子就上去了。

3. 上手Embabel时最容易踩的三个配置坑

3.1 能力单元的输入输出声明不匹配

Embabel的能力单元靠声明式的输入输出做编排,框架会自动把上游单元的输出匹配到下游单元的输入。这个机制很优雅,但前提是你的声明得写对。我踩的第一个坑就是类型声明太宽泛。比如我定义了一个单元输出Map<String, Object>,另一个单元输入也写Map<String, Object>,框架倒是能匹配上,但运行时经常出现字段对不上的情况——上游输出的key叫orderId,下游期望的key叫order_id,框架不管这个,它只看类型,结果下游拿到一个空值,然后整个链条就断了。

正确的做法是为每个能力单元定义明确的DTO(数据传输对象),哪怕字段少也要定义。Embabel支持用类或者记录(record)来声明输入输出,框架会基于字段名做精确匹配。我后来把所有单元都改成了强类型声明,字段名统一用驼峰,问题就再也没出现过。这个坑的教训是:声明式编排的便利性是有代价的,代价就是你得把契约写清楚,不能偷懒用泛型糊弄。

还有一个相关的坑是可选字段的处理。有些单元的输出里有些字段是条件性的,比如“如果用户提供了邮箱就输出邮箱,否则不输出”。Embabel默认要求输入字段必须存在,如果上游没输出这个字段,下游就会报错。解决办法是在输入声明里把字段标记为可选,或者在状态机里加一个“补全默认值”的单元。我倾向于后者,因为显式补全比隐式可选更容易追踪。

3.2 LLM节点的提示词与状态上下文脱节

Embabel把LLM调用限制在单个能力单元内部,这本来是好事,但很容易犯一个错误:在写提示词的时候忘了把必要的状态上下文传进去。因为LLM不再能看到全局对话历史,它只能看到当前单元被喂进去的那点数据。如果你需要它根据之前的某个状态做判断,就必须手动把那个状态作为输入传进来。

我搭的第一个Embabel Agent是个会议安排助手,其中一个单元负责“根据用户说的时间判断是上午还是下午”。我一开始只把用户当前这句话传给了LLM,结果用户说“那就下午三点吧”,LLM因为不知道“那”指的是哪一天,直接返回了“下午”。后来我在这个单元的输入里加上了“已确认的日期”字段,提示词里也明确写了“当前已确认日期是X,用户说‘那就下午三点’,请输出完整的时间表达”,问题才解决。

这个坑的本质是:LLM的“视野”被框架刻意收窄了,你得负责给它补上必要的视野。好处是你能精确控制它看到什么,坏处是你得时刻记得它看不到什么。我的经验是,在每个LLM单元的提示词模板里,显式列出所有输入字段,并且在提示词开头用一句话概括“你现在的任务是基于以下已知信息做判断”,这样能大幅减少遗漏。

3.3 状态迁移条件写得太“聪明”

Embabel允许在状态迁移上加条件表达式,比如“如果提取到的订单号非空且格式正确,则迁移到查询状态,否则停留在提取状态”。这个机制很强大,但我见过不少人(包括我自己)一开始会把它写得太复杂,恨不得把所有业务逻辑都塞进迁移条件里。

我踩的坑是写了一个多条件嵌套的迁移表达式,结果某个边界情况下条件判断顺序出了问题,导致状态机进入了一个死循环。排查了半天才发现,是因为条件里用了一个短路求值,前面的条件为真时后面的条件根本没执行,而后面那个条件恰恰是防止循环的关键。迁移条件应该尽量简单、正交,复杂的判断逻辑应该下沉到能力单元内部去。能力单元内部你可以随便写代码、加日志、做单元测试,但迁移条件最好只做“是/否”的简单判断。

后来我给自己定了个规矩:迁移条件里最多用两个布尔表达式的与或组合,超过这个复杂度就拆成独立的能力单元。比如“订单号有效且用户已确认”这种条件,我会拆成“校验订单号”和“检查确认状态”两个单元,各自输出一个布尔值,迁移条件只做and运算。这样每个环节都可测试、可观测,出问题也好定位。

4. 一个完整Agent的搭建过程与关键决策点

4.1 场景选择:为什么挑“报销单信息提取”来练手

为了把Embabel的机制吃透,我选了一个自己工作中真实存在的小痛点来练手:从一段杂乱的报销说明里提取结构化信息。平时同事发来的报销消息经常是这样的:“上周三打车去客户那边花了86,回来地铁4块,中午请客户吃饭320,发票都贴好了”。我需要从中提取出日期、交通费、餐费、总金额这几个字段。

选这个场景有几个考虑。第一,它有明确的输入输出,方便定义能力单元。第二,它既有需要LLM的理解环节(从自然语言里识别费用类型和金额),也有纯计算的环节(汇总金额、校验格式),正好能体现混合编排的价值。第三,它足够简单,不会让我在业务逻辑上分心,可以专注在框架本身。第四,它容易构造测试用例,我可以编各种奇怪的表述来验证鲁棒性。

如果你也想上手Embabel,我建议也找一个类似的小场景,不要一上来就搞多轮对话、多工具调用的复杂Agent。先用一个单轮、单目标的任务把状态机、能力单元、LLM节点这几个核心概念跑通,建立起对框架的直觉,再去扩展复杂度。

4.2 能力单元的拆分粒度怎么定

拆分能力单元是搭建过程中最需要拿捏的部分。拆得太粗,一个单元里塞太多逻辑,就失去了状态驱动的意义;拆得太细,单元数量爆炸,编排复杂度反而上升。我一开始拆得特别细,把“识别金额”和“识别费用类型”都拆成了独立单元,结果发现LLM被调用了两次,每次都要重新理解同一句话,既慢又容易不一致。

后来我调整了策略:按“是否需要独立的LLM理解”来拆分。如果两个判断可以共用同一次LLM调用,就合并成一个单元。比如“识别费用类型和金额”就合并了,因为LLM读一遍句子就能同时输出这两个信息。而“汇总金额”是纯计算,跟LLM无关,就单独拆出来。最终的单元划分是这样的:

单元名称输入输出是否用LLM
文本预处理原始文本清洗后文本否
费用项提取清洗后文本费用项列表是
格式校验费用项列表校验结果否
金额汇总费用项列表总金额否
结果组装费用项列表、总金额结构化报销单否

这个粒度我觉得比较舒服:LLM只在“费用项提取”这一个单元里被调用,其他都是确定性代码。整个链条清晰、可测、可调。

4.3 提示词工程在受限上下文下的写法

因为LLM在Embabel里只能看到当前单元的输入,提示词的写法跟常规的Agent提示词很不一样。常规做法是写一大段系统提示词,把角色、任务、约束、示例全塞进去,然后靠对话历史来提供上下文。在Embabel里,每个LLM单元的提示词应该是自包含的,它不需要知道整个Agent在干什么,只需要知道自己这个单元的任务。

我给“费用项提取”单元写的提示词大概是这样的:先说明“你是一个费用信息提取器”,然后给出输入文本的占位符,接着用几个例子展示期望的输出格式(JSON数组,每个元素包含类型、金额、备注),最后强调“只输出JSON,不要解释”。这个提示词很短,但很聚焦。因为不需要考虑多轮对话,也不需要处理历史,提示词的维护成本大大降低。

有一个细节值得注意:输出格式的约束要尽可能严格。Embabel需要解析LLM的输出并传给下游单元,如果LLM输出的JSON格式不对,整个链条就断了。我在提示词里不仅给了格式示例,还加了“如果某个字段无法确定,用null填充,不要省略字段”这样的规则。实测下来,加了这条规则之后,解析失败率从大概一成降到了几乎为零。

5. 实测中那些“反直觉”的发现

5.1 限制LLM反而让它的表现更稳定

这个发现是我始料未及的。在主流框架里,我们总是想方设法给LLM更多上下文、更多工具、更多自由度,觉得这样它才能“聪明”地完成任务。但在Embabel里,LLM的上下文被严格限制在单个单元内,工具调用也完全由状态机控制,它反而表现得更稳定了。

我分析原因有两个。第一,任务越聚焦,LLM的出错空间越小。当它只需要做“从这句话里提取费用项”这一件事时,它不需要分心去考虑“我下一步该干嘛”“我要不要调用工具”,全部注意力都在提取上,准确率自然高。第二,没有历史包袱。多轮对话里,LLM容易被之前的错误带偏,一步错步步错。在Embabel里,每个单元都是独立的,上一个单元的错误不会污染下一个单元的输入(除非你显式传递了错误数据)。

这个发现让我重新思考了“Agent智能”的定义。我们是不是太执着于让LLM做所有事,以至于忽略了“分工”的价值?LLM是个通才,但通才在具体任务上往往不如专才。Embabel的思路是把通才放在需要通才的地方,其他地方用专才(确定性代码),整体效果反而更好。

5.2 状态机的“笨”恰恰是它的可靠之处

刚开始用状态机的时候,我觉得它有点“笨”——为什么不能根据情况自动决定下一步?为什么非要我写死迁移规则?用了一段时间之后,我反而爱上了这种“笨”。因为它的“笨”是可预测的笨,而不是LLM那种不可预测的“聪明”。

举个例子,用户输入“我上周三打车花了86”,状态机走到“费用项提取”单元,LLM输出[{type: "交通", amount: 86}],然后迁移到“格式校验”,校验通过,迁移到“金额汇总”,输出86,结束。整个过程没有任何意外。如果用户输入“我上周三好像打车花了八十六左右”,LLM可能输出[{type: "交通", amount: 86, confidence: "low"}],这时候我可以在迁移条件里加一条:如果confidence是low,就迁移到一个“追问确认”的单元,让Agent问用户“您是说86元对吗?”。这种精细的分支控制,在LLM全权决策的框架里需要写很复杂的提示词才能实现,而且还不一定稳定。

状态机的“笨”还体现在错误处理上。如果某个单元执行失败,状态机可以明确地停在那个状态,等待重试或者人工介入。LLM驱动的框架里,失败往往表现为“LLM开始胡言乱语”,你很难判断它到底是失败了还是在尝试别的路径。明确的失败比模糊的成功更有价值,这是我用Embabel最大的体会。

5.3 性能与成本上的意外收获

除了稳定性,Embabel在性能和成本上也给了我惊喜。因为LLM只在必要的单元被调用,而且每次调用的上下文都很短,token消耗比LLM驱动的框架低了不少。我对比了同一个报销提取任务,在主流框架里平均每次要消耗约1500个token(因为要把完整对话历史和工具列表都塞进去),在Embabel里只需要约300个token(只传当前单元的输入和简短提示词)。成本降到了五分之一,响应速度也从平均3秒多降到了1秒以内。

这个差异在单次调用上不明显,但如果你的Agent每天要处理几千上万次请求,成本差距就很可观了。而且因为状态机是确定性的,很多纯计算单元根本不消耗任何API调用,进一步压低了成本。我算过一笔账,同样的任务量,Embabel方案一个月的API费用大概只有原来的两成左右。

当然,这个优势的前提是你把职责边界划清楚了。如果你在每个单元里都塞LLM调用,那成本优势就不存在了。所以省钱的本质不是框架本身,而是框架逼你做的那个“这个环节到底需不需要LLM”的思考。

6. 和主流LLM驱动框架的对比与选型建议

6.1 什么场景适合Embabel,什么场景不适合

Embabel不是万能的,它有明确的适用边界。我根据自己的实践,整理了一个简单的判断表:

场景特征适合Embabel适合LLM驱动框架
任务流程是否固定固定或半固定高度开放、无法预定义
是否需要精确控制需要不太需要
调试和可观测性要求高低
任务链条长度中长链短链
对成本敏感度高低
团队对状态机的熟悉度熟悉不熟悉

简单说,如果你的Agent任务可以用流程图大致画出来,那Embabel就很合适。比如客服工单处理、表单信息提取、多步骤审批流、数据清洗管道,这些场景的步骤是相对确定的,用状态机控制能获得最大的稳定性和可维护性。反过来,如果你的Agent需要处理完全开放的对话、需要根据用户意图动态生成全新的任务流程,那LLM驱动的框架可能更合适,因为状态机没法预定义所有可能的路径。

我个人的做法是混合使用:用Embabel处理确定性的主干流程,在需要开放式理解的节点上,把控制权临时交给一个LLM驱动的子Agent,等子Agent返回结果后再交回状态机。这样既有主干的可控性,又有局部的灵活性。

6.2 迁移成本与团队协作的考量

从LLM驱动框架迁移到Embabel,最大的成本不是技术上的,而是思维方式的转变。习惯了“把提示词写好,剩下的交给LLM”的人,一开始会很不适应“每一步都要自己定义”的方式。我团队里有个同事,第一次用Embabel的时候抱怨说“这怎么比之前还麻烦了”,因为他得把原来藏在提示词里的逻辑显式地写成代码。

但过了适应期之后,他的态度完全变了。因为显式逻辑虽然写的时候麻烦,但改的时候、调的时候、交接的时候都省心。以前改一个Agent的行为,得小心翼翼地改提示词,改完还得反复测试怕影响其他环节。现在改状态机,改哪个节点就测哪个节点,影响范围清清楚楚。团队协作的时候,每个人可以负责不同的能力单元,接口定义好了就能并行开发,不像提示词那样容易互相冲突。

迁移成本还体现在工具链上。Embabel的状态机调试、单元测试、日志追踪都需要一些配套工具,如果你的团队之前没有这方面的积累,需要花点时间搭建。但我觉得这个投入是值得的,因为这些工具一旦建好,后面所有Agent项目都能复用。

6.3 我最终的生产环境选型结论

经过这段时间的实践,我最终在生产环境里的选型策略是这样的:核心业务流程用Embabel的状态机控制,边缘的、开放式的交互用LLM驱动。具体来说,用户请求进来之后,先经过一个轻量的意图分类器(这个用LLM,因为意图种类多且表达多样),分类结果决定进入哪条状态机分支。状态机内部的主干步骤全部用能力单元编排,只在需要理解自然语言或者生成自然语言回复的节点调用LLM。

这套组合拳打下来,系统的整体稳定性比纯LLM驱动方案高了一个档次,成本也降了不少。当然,它也不是没有代价——开发初期的工作量确实比“写个提示词就完事”要大。但考虑到后期维护和线上问题排查的省心程度,我认为这个投入是划算的。

如果你正在选型,我的建议是:先想清楚你的Agent任务能不能画出流程图。能画出来,就认真考虑Embabel这类状态驱动的方案;画不出来,或者流程经常变,那就先用LLM驱动快速验证,等流程稳定了再考虑迁移。不要为了“先进”而先进,适合自己场景的才是最好的。

7. 几个我反复验证过的实操技巧

7.1 给每个能力单元加“自检”输出

Embabel的能力单元可以输出多个字段,我养成了一个习惯:每个单元除了输出业务数据,还输出一个status字段,标明这次执行是成功、部分成功还是失败。这个字段不参与业务逻辑,但会被状态机用来做迁移判断。比如“费用项提取”单元如果只提取到了部分费用项,status就是partial,状态机可以选择迁移到“追问缺失项”而不是直接汇总。

这个技巧的好处是把“执行质量”变成了显式的状态,而不是藏在LLM的输出文本里。以前在LLM驱动框架里,LLM说“我提取到了这些”,你没法判断它是不是漏了什么。现在有了status字段,漏了就是漏了,状态机会处理。我实测下来,加了自检输出之后,漏提取的情况减少了大概六成,因为状态机在partial状态下会主动追问,而不是默默接受不完整的结果。

7.2 用“影子单元”做A/B测试

Embabel的状态机支持并行分支,我利用这个特性做了一个“影子单元”的机制:在关键节点上,同时跑两个不同版本的能力单元(比如一个用旧提示词,一个用新提示词),把两个结果都记录下来,但只用其中一个的结果继续往下走。这样可以在不影响线上行为的前提下,对比两个版本的效果。

这个技巧在调优提示词的时候特别有用。以前改提示词只能靠离线测试集,但离线测试集往往覆盖不了线上的真实多样性。用影子单元跑一段时间,收集真实数据下的对比结果,再决定用哪个版本,靠谱得多。当然,这会让LLM调用量翻倍,所以只适合在关键节点上短期使用,验证完就撤掉。

7.3 状态机的“超时”与“重试”策略

状态机跑长任务的时候,难免会遇到某个单元卡住或者失败。Embabel允许给每个状态设置超时和重试策略,我踩过的坑是重试策略设得太激进,导致一个失败的LLM调用被重试了五次,每次都消耗token,最后还是失败。后来我改成了“最多重试两次,第二次重试时换一个更简单的提示词或者降级到规则匹配”,成功率反而提高了。

超时策略也很重要。LLM调用有时候会莫名其妙地慢,如果不设超时,整个状态机就卡在那里。我给所有LLM单元设了10秒超时,超时后状态机迁移到一个“降级处理”单元,用规则或者默认值先顶上,同时记录日志供后续分析。降级处理比无限等待更符合生产环境的要求,用户宁可得到一个不太完美但及时的结果,也不愿意一直等。

7.4 日志里一定要记录“决策依据”

Embabel的状态迁移日志默认会记录状态变化,但我建议额外记录每次迁移的“决策依据”,也就是为什么从这个状态迁移到了那个状态。比如“因为status=partial,所以迁移到追问状态”或者“因为校验通过,所以迁移到汇总状态”。这个信息在排查问题时非常关键,因为有时候状态迁移是对的,但依据是错的(比如status被错误地设成了partial),有了决策依据就能快速定位。

我实现这个的方式是在每个迁移条件里加一个日志输出,虽然有点啰嗦,但排查问题时省下的时间远远超过写日志的时间。特别是当Agent行为不符合预期时,你能一眼看出是哪个判断出了问题,而不是靠猜。

8. 关于“决策权”这件事的再思考

把决策权从LLM手里拿回来,这个说法容易让人误以为是要“削弱”LLM。但实际用下来,我觉得更准确的说法是给LLM划定一个更清晰的职责范围。LLM最擅长的是理解模糊的自然语言、生成流畅的文本、做需要常识的模糊判断,这些事交给它没问题。但“下一步该干嘛”“要不要继续”“用哪个工具”这些事,本质上是流程控制问题,交给确定性的代码来处理,既可靠又高效。

Embabel给我的最大启发不是某个具体的技术点,而是一种工程化的思维方式:在构建智能系统的时候,不要被“智能”这个词绑架,该用规则的地方就用规则,该用代码的地方就用代码,LLM只是工具箱里的一件工具,不是全部。这种思维方式在Agent开发越来越火的今天,反而显得有点“反潮流”,但我觉得它代表了一种更成熟、更可持续的方向。

如果你也在做Agent开发,被各种不确定的行为困扰,我建议你试试把决策权拿回来。不一定要用Embabel,任何能让你显式控制流程的方式都行。你会发现,当你不再指望LLM搞定一切的时候,它反而能把该搞定的事搞得更好。这个体会,是我这段时间最大的收获。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询