Agent框架选型第一性问题:下一步由谁决定

发布时间:2026/10/27 8:35:35
Agent框架选型第一性问题:下一步由谁决定

Agent 框架的选型讨论,这两年几乎成了技术社区里最热闹也最容易跑偏的话题之一。打开任何一个技术群,都有人在问 LangChain、LangGraph、AgentScope、CrewAI、Dify 到底该选哪个,回答往往是一堆功能对比表,列完工具调用、记忆管理、多智能体协作这些维度,然后给一个"看场景"的万能结论。但真正上手做过几个 Agent 项目之后你会发现,框架之间的差异,很多时候并不体现在功能清单上,而是体现在一个更底层的问题上:下一步由谁决定。

这个问题听起来抽象,但它直接决定了你写出来的代码长什么样、调试的时候有多痛苦、上线之后能不能控住成本。我前后用 LangChain、LangGraph、AgentScope 以及几个轻量自研方案做过不同类型的 Agent,踩过的坑从"循环停不下来"到"状态在节点之间莫名其妙丢失"都有。这篇文章不打算给你一张选型对照表就完事,而是想把"下一步由谁决定"这个判断维度拆开,讲清楚它为什么是选框架的第一性问题,以及不同答案对应什么样的框架、什么样的代码结构、什么样的坑。

如果你正在纠结选哪个框架,或者已经选了一个但写着写着觉得别扭,那大概率是没想清楚这个决定权归属的问题。下面我按自己的理解,把这个判断链路完整走一遍。

1. 为什么"下一步由谁决定"比功能对比更重要

大多数人选框架的习惯是打开官方文档,看特性列表:支持多少种工具、有没有内置记忆、能不能做多智能体、社区活跃度如何。这套方法在选 Web 框架、ORM 框架的时候基本够用,因为那些框架的职责边界很清晰——处理请求、映射数据,流程是确定的。但 Agent 框架不一样,它处理的是一个不确定步数、不确定路径的执行过程,核心矛盾在于"谁来推进流程"。

1.1 功能清单会骗人,控制流不会

我举个具体的例子。假设你要做一个"根据用户问题自动查资料并汇总"的 Agent。用 LangChain 的 AgentExecutor 写,大概是给一个 LLM 加一堆 tool,然后让它自己决定调哪个、调几次,直到它认为可以给出最终答案。用 LangGraph 写,你会先画一张图:一个节点负责判断意图,一个节点负责检索,一个节点负责生成,边决定从哪走到哪。这两个实现的功能清单看起来一模一样——都能查资料、都能汇总、都支持多轮。但它们的控制流完全不同:前者是 LLM 在运行时动态决定下一步,后者是你在编码时就把可能的路径定义好了。

这个差异带来的后果是全方位的。调试的时候,LangChain 那种方式你只能看日志猜 LLM 为什么调了那个工具;LangGraph 那种方式你可以精确知道卡在哪个节点。控成本的时候,前者你没法预估会调几次 LLM,后者你能算出最坏情况下的调用次数。上线之后出问题,前者你只能加 prompt 约束,后者你能直接改图结构。

所以功能清单是"能做什么",控制流是"怎么做到",后者才决定了你日常开发的体验和系统的可控性。

1.2 决定权归属决定了调试难度

我印象很深的一次踩坑,是做一个客服场景的 Agent,用早期 LangChain 的 ReAct 模式。测试的时候发现它偶尔会陷入"查订单→发现信息不全→再查订单→还是不全"的死循环,跑十几次才出一次,但一出就烧掉大量 token。排查的时候非常痛苦,因为循环是 LLM 在运行时决定的,你没法在代码里找到"这里会循环"的那一行。最后只能靠加 prompt 里的"如果连续两次查询结果相同就停止"来缓解,属于治标不治本。

后来同样的需求换成 LangGraph 重写,我把"查询订单"和"判断信息是否完整"拆成两个节点,在边上加了条件:信息完整才走向生成,不完整且重试次数小于 2 才回到查询。这个循环上限是写死在图里的,LLM 再"想"循环也循环不起来。调试的时候直接看是哪个条件判断出了问题,几分钟定位。

这就是决定权归属带来的差异:决定权在 LLM 手里,你只能祈祷它理性;决定权在代码手里,你能精确控制边界。前者适合探索性、开放性的任务,后者适合有明确流程、需要可控性的任务。

1.3 一个判断清单:你的任务属于哪一类

在选框架之前,我建议先回答下面几个问题,答案会直接指向你该选哪类框架:

  • 这个任务的步骤数是否可预估?如果最坏情况下的步骤数你能大致算出来,说明流程相对确定,适合代码主导。
  • 中间状态需不需要持久化?比如用户中途关掉页面,下次回来要接着走,那状态管理就是刚需。
  • 出错之后能不能重试某一步?如果某一步失败需要单独重跑而不是从头来,那节点化的框架更合适。
  • 是否需要人工介入某个环节?比如生成内容后要人工审核再发,这种"人在回路"的场景对控制流要求很高。
  • 任务是不是高度开放,连你自己都说不清有几种走法?如果是,那 LLM 主导反而更灵活。

把这几个问题答完,你基本就能判断自己的任务落在"LLM 主导"还是"代码主导"这一光谱的哪一端了。下面我分别展开这两端对应的框架特征。

2. LLM 主导型:LangChain 式 Agent 的适用边界与代价

LangChain 是很多人接触 Agent 的第一个框架,它的 AgentExecutor 加上 ReAct、OpenAI Functions 这些 agent type,本质上都是让 LLM 在每一步决定调用哪个工具、什么时候停止。这种模式我称之为"LLM 主导型",因为控制流的核心决策权交给了模型。

2.1 它到底解决了什么问题

LLM 主导型框架最大的价值在于处理你无法预先枚举路径的任务。比如一个通用的研究助手,用户可能问"帮我对比一下三个云厂商的定价",也可能问"帮我找找最近关于某个技术的论文",还可能问"帮我把这段代码优化一下"。这些任务的执行路径差异极大,你很难用一张固定的图覆盖所有情况。这时候让 LLM 根据工具描述自己决定调什么,反而是最省事的做法。

LangChain 在这方面的抽象做得比较到位:你只需要把工具用@tool装饰器定义好,写清楚描述,剩下的交给 AgentExecutor。工具描述写得好,LLM 基本能选对。我做过一个内部工具助手,挂了十几个工具,从查数据库到发消息到调内部 API,用 ReAct 模式跑起来,大部分情况下 LLM 都能选对工具,开发效率确实高。

2.2 代价一:不可预测的调用次数

但省事的代价是不可控。LLM 主导意味着你无法在编码阶段确定它会调几次工具、调几次 LLM。我遇到过最夸张的一次,一个本该两步完成的任务,LLM 因为工具返回格式不理想,反复重试了七八次,token 消耗是预期的四倍。这种问题在测试环境不容易发现,因为测试用例往往比较"标准",一到真实用户手里,各种奇怪的输入就冒出来了。

缓解办法有几个:一是给工具返回做严格的格式约束,减少 LLM"看不懂"的情况;二是设置max_iterations之类的硬上限,防止无限循环;三是在 prompt 里明确写"如果工具返回错误,最多重试一次"。但这些都只是缓解,根治不了,因为决定权本来就不在你手里。

2.3 代价二:状态管理的黑盒感

LangChain 的 AgentExecutor 内部维护了一个 scratchpad,记录中间步骤。这个设计对简单场景够用,但一旦你想在中间步骤之间做点事情——比如某一步的结果要存数据库、要触发一个副作用——就会发现很难插手。scratchpad 是个黑盒,你只能通过回调(callback)去观察,但观察和干预是两回事。

我做过一个需要"每查一次数据就记录一次审计日志"的需求,用 AgentExecutor 实现得很别扭,最后是靠 callback handler 硬塞进去的,代码可读性很差。这种需求在代码主导型框架里就是加一个节点的事,但在 LLM 主导型框架里要绕一大圈。

2.4 什么时候该果断放弃 LLM 主导

我的经验是,只要满足下面任意一条,就该考虑换代码主导型框架:

  • 任务有明确的合规或审计要求,每一步都要留痕;
  • 步骤数需要严格控制,比如成本敏感的场景;
  • 需要人工审核介入某个环节;
  • 出错后需要从中间某步重试而不是从头来。

反过来,如果任务是探索性的、路径高度不确定、对成本不敏感,那 LLM 主导型反而更合适,别为了"可控"硬上代码主导,最后写出一堆用不上的节点。

3. 代码主导型:LangGraph 把控制流画成图之后

LangGraph 这两年被讨论得很多,它的核心思路是把 Agent 的执行过程显式地建模成一张图:节点是执行单元,边是流转条件,状态在节点之间传递。这个模型把"下一步由谁决定"的答案从 LLM 手里拿回到了代码手里。

3.1 图模型为什么让状态变得可见

LangGraph 最让我舒服的一点是状态(State)是显式定义的。你会先定义一个 TypedDict 或者 Pydantic 模型,声明这张图里流转的所有字段,然后每个节点接收状态、返回状态的更新。这意味着任何时候你都能知道"当前系统里有哪些数据",而不是像 AgentExecutor 那样只能从 scratchpad 里猜。

这个设计对调试的帮助是巨大的。我之前做一个多轮信息收集的 Agent,用户要分几次提供信息,中间还要查库校验。用 LangGraph 写的时候,状态里明确有collected_info、missing_fields、validation_result这些字段,每次节点执行完打印一下状态,问题一目了然。换成 AgentExecutor,这些信息全混在对话历史里,得靠肉眼从一长串文本里找。

3.2 条件边:把"如果"写进代码而不是 prompt

LangGraph 的条件边(conditional edge)是我用得最多的特性。它允许你根据当前状态决定下一步走哪个节点,这个判断是纯代码逻辑,不经过 LLM。比如:

def should_continue(state): if state["retry_count"] >= 3: return "give_up" if state["validation_passed"]: return "generate" return "retry"

这段逻辑是确定性的,LLM 再怎么"想"也绕不过去。这就把很多原本要靠 prompt 约束的东西变成了代码约束,可靠性完全不是一个量级。我现在的习惯是,凡是能用代码判断的流转条件,绝不交给 LLM;只有真正需要语义理解的地方才让 LLM 参与决策。

3.3 持久化与人在回路:LangGraph 的杀手锏

LangGraph 的 checkpointer 机制支持把图的状态持久化到数据库,这意味着你可以实现"暂停-恢复"和"人在回路"。比如一个内容生成流程,生成完草稿后暂停,等人工审核通过再继续。这个能力在 LLM 主导型框架里几乎没法优雅实现。

我做过一个合同审核的 Agent,流程是:解析合同→提取关键条款→LLM 初审→人工复核→生成报告。中间的人工复核环节用 LangGraph 的interrupt实现,图执行到复核节点就停下来,把当前状态存起来,等人工在界面上点了"通过"或"驳回",再带着决定恢复执行。整个流程清晰可控,每一步都有记录。这种场景如果硬用 AgentExecutor,基本是自找麻烦。

3.4 学习曲线到底陡在哪

很多人说 LangGraph 学习曲线陡,我的体会是,陡的不是 API,而是思维方式。你得先把任务拆成节点和边,这对习惯了"写个 prompt 让 LLM 自己跑"的人来说是个转变。刚开始我也觉得麻烦,明明一句话能描述的任务,非要画成图。但拆过几个任务之后发现,这个拆解过程本身就是对任务的梳理,很多原本模糊的地方在画图的时候被迫想清楚了。

真正需要适应的是状态设计。状态字段设计得好,节点之间传递顺畅;设计得不好,会出现"这个节点需要的数据上个节点没存"的尴尬。我的经验是,状态宁可设计得宽一点,把可能用到的中间结果都放进去,别为了"精简"导致后面反复改状态定义。

4. AgentScope 与多智能体:当决定权分散到多个角色

AgentScope 是阿里出的多智能体框架,它的定位和前面两个不太一样,更偏向"多个 Agent 协作"的场景。这时候"下一步由谁决定"这个问题变得更复杂了,因为决定权不仅涉及 LLM 和代码,还涉及多个 Agent 之间的消息传递和角色分工。

4.1 多智能体不是"多个 Agent 简单叠加"

很多人对多智能体的理解是"把一个大 Agent 拆成几个小 Agent",但实际做下来会发现,拆开之后最大的挑战是协调。谁先说话、谁听谁的、消息怎么传递、冲突怎么解决,这些都是单 Agent 场景不存在的问题。AgentScope 在这方面提供了一套消息传递机制和角色定义方式,让多个 Agent 能按一定规则交互。

我做过一个"模拟辩论"的小项目,两个 Agent 分别持正反方观点,一个裁判 Agent 做总结。用 AgentScope 写的时候,消息在 Agent 之间的流转是显式的,每个 Agent 有自己的记忆和角色设定。这个场景用单 Agent 很难做,因为单 Agent 很难"同时"持有两个对立观点。

4.2 消息传递模式决定了协作的上限

多智能体框架的核心差异在于消息传递模式。有的是广播式,一个 Agent 发言所有 Agent 都能看到;有的是定向式,消息只发给特定 Agent;还有的是层级式,有个协调者 Agent 负责分发任务。AgentScope 支持比较灵活的消息配置,你可以定义谁在什么条件下给谁发消息。

这个灵活性是双刃剑。配置得好的时候,协作流程很顺畅;配置得不好的时候,会出现 Agent 之间"互相等待"或者"消息风暴"的问题。我踩过一个坑:两个 Agent 都配置成"收到消息就回复",结果它们互相回复停不下来,token 哗哗地烧。后来加了轮次上限和终止条件才解决。这个坑的本质还是"下一步由谁决定"没想清楚——如果每个 Agent 都能无条件触发下一步,那流程就失控了。

4.3 多智能体适合什么、不适合什么

我的判断是,多智能体适合那些天然可以拆成多个角色、角色之间有明确交互的任务,比如模拟对话、多视角分析、分工协作。不适合那些本质上是线性流程、硬拆成多个 Agent 反而增加协调成本的任务。

见过不少项目,明明一个 Agent 加几个工具就能搞定,非要拆成"规划 Agent + 执行 Agent + 审核 Agent",结果三个 Agent 之间的消息传递代码比业务逻辑还多。这种为了多智能体而多智能体的做法,我是不推荐的。选框架之前先问自己:这个任务真的需要多个角色吗?如果不需要,单 Agent 加好的控制流往往更简单。

5. 一张判断表:把你的任务映射到框架

讲了这么多,落到实操上,我整理了一张判断表,把常见的任务特征和对应的框架倾向列出来。这张表不是绝对的,但能帮你快速定位。

任务特征决定权倾向推荐框架方向典型场景
路径高度不确定,步骤数无法预估LLM 主导LangChain AgentExecutor通用研究助手、开放问答
流程明确,步骤可枚举代码主导LangGraph审批流、信息收集、多步处理
需要状态持久化和断点恢复代码主导LangGraph + checkpointer长流程、人在回路
需要多个角色交互协作混合AgentScope模拟对话、多视角分析
快速验证想法,不在乎可控性LLM 主导LangChain 或 DifyDemo、原型
成本敏感,需要精确控制调用代码主导LangGraph 或自研生产环境、高频调用

用这张表的时候有个技巧:先看"步骤数能否预估"这一行,它基本能帮你排除掉一半的选项。能预估的往代码主导走,不能预估的往 LLM 主导走。然后再看有没有持久化、多角色这些附加需求,进一步缩小范围。

5.1 别忽略自研这条路的性价比

表格里我特意加了"自研"这个选项。很多人一上来就纠结选哪个框架,但其实对于流程比较固定的场景,直接用一个 LLM SDK 加自己的状态机,可能比套框架更清爽。我做过一个只有五个步骤的 Agent,用 LangGraph 写要定义状态、建图、编译,用原生 SDK 加一个 while 循环和几个 if 判断,代码量少一半,调试还更直接。

框架的价值在于处理复杂度和提供通用能力,如果你的场景复杂度不高,框架反而成了负担。判断标准很简单:如果框架提供的抽象(图、节点、状态)和你的任务结构天然契合,那就用;如果为了用框架要把任务硬塞进它的模型里,那不如自研。

5.2 混合使用是常态,别被"选一个"框住

实际项目里,我很少纯用一种模式。常见的组合是:外层用 LangGraph 控制主流程,某个节点内部用 LangChain 的 AgentExecutor 处理开放性子任务。这样既有主流程的可控性,又有子任务的灵活性。比如一个报告生成系统,主流程是"收集需求→检索资料→生成→审核",这是确定的,用图控制;但"检索资料"这一步具体查什么、查几次,是不确定的,交给 LLM 主导的 Agent 去做。

这种混合模式要求你对两套框架都熟悉,但收益是明显的:该可控的地方可控,该灵活的地方灵活。别被"选一个框架"的思维框住,框架是工具,组合使用很正常。

6. 选型之后:几个我踩过的实操坑

框架选定只是开始,真正写起来还有一堆细节坑。这部分分享几个我印象比较深的,都是文档里不太会写、但实际会遇到的。

6.1 状态字段设计过窄导致反复重构

前面提过状态设计,这里展开说。我第一个 LangGraph 项目,状态定义得很"精简",只放了当前必需的字段。结果写到第三个节点发现需要第一个节点的某个中间结果,但那个结果没存进状态,只能回头改状态定义、改前面所有节点的返回。改了两三次之后学乖了,现在状态里会把所有可能用到的中间结果都放进去,哪怕暂时用不上。

这个坑的本质是没想清楚"状态是节点之间唯一的通信渠道"。在 LangGraph 里,节点之间不能直接传参,只能通过状态。所以状态设计要站在全局视角,而不是每个节点只顾自己。

6.2 工具描述写不好,LLM 主导型直接崩

用 LLM 主导型框架的时候,工具描述的质量直接决定成败。我见过工具描述写成"查询数据"四个字的,LLM 根本不知道这个工具查什么数据、什么时候该用。好的工具描述应该包含:这个工具做什么、什么情况下用、输入参数的含义和格式、返回什么。写得越清楚,LLM 选错的概率越低。

我的习惯是,工具描述里会写一两个使用示例,比如"当用户询问订单状态时使用此工具,输入订单号,返回订单的当前状态和预计送达时间"。这种描述比干巴巴的一句话有效得多。

6.3 循环终止条件不能只靠 LLM 自觉

不管是哪种框架,只要有循环,就必须有硬性的终止条件。LLM 主导型里靠max_iterations,代码主导型里靠条件边里的计数器。我踩过的坑是,早期觉得"LLM 应该知道什么时候停",结果它不知道。现在我的原则是,任何循环都必须有一个不依赖 LLM 的终止条件,哪怕这个条件设得很宽松,也比没有强。

6.4 多智能体的消息风暴要提前防

做多智能体的时候,消息传递规则一定要提前设计好终止条件。我那个辩论项目踩的坑就是没设轮次上限,两个 Agent 互相回复停不下来。后来加了"最多辩论五轮"的硬限制才解决。多智能体场景里,每个 Agent 都可能触发其他 Agent,如果不加全局的轮次或时间限制,很容易失控。

6.5 别在框架选择上过度纠结

最后说个心态问题。我见过不少人花大量时间对比框架,迟迟不动手。其实框架之间的差异,很多时候要真正写起来才能体会到。与其纠结选哪个,不如先用一个上手,写个小 Demo,感受一下它的控制流模型和你的任务是否契合。不契合就换,切换成本没有想象中那么高,因为核心的业务逻辑和 prompt 是可复用的,换的主要是编排层。

选框架这件事,想清楚"下一步由谁决定"这个核心问题,剩下的就是动手验证。我自己的经验是,凡是流程能画出来的任务,用代码主导型框架基本不会错;凡是画不出来的,先用 LLM 主导型跑通再说。这个判断标准简单,但用下来很准。