Agent 执行从构建有向图开始而非启动循环

第一次看到 Microsoft Agent Framework 时,很多人以为它只是把各种 Agent 能力整理成一套完整 API,于是看到 AIAgent 就觉得可以直接上手。但实际运行时,它在维护一个状态驱动的图结构,这直接决定了执行路径和中断恢复能力。

主流框架如 LangGraph 在初始化阶段就把开发者定义的任务转化为一张有向图。开发者先声明节点(node)和边(edge),节点代表具体动作,比如调用 LLM、执行工具或条件判断,边则定义状态转移规则。LangGraph 的 StateGraph 类在 compile 阶段会把这些声明编译成可执行的图对象,而不是立即进入 while True 循环。

这个构建过程远比想象中复杂。框架会根据边的条件函数动态决定下一跳节点,条件函数接收当前状态作为输入,返回布尔值或下一个节点名称。举例来说,一个客服 Agent 的图可能包含「意图识别」「查询知识库」「调用外部 API」「生成回复」四个节点,边上附加的路由函数会根据意图识别节点的输出把状态推向不同分支。

相比之下,AutoGen 更倾向于把多个 Agent 组织成群组,通过聊天消息驱动执行,但底层依然可以映射为隐式的图结构。CrewAI 则强调角色分工,每个 Crew 成员对应图中的一个专用节点,任务流转通过顺序或层次化流程定义。

开发者在实际项目中经常忽略这个构建阶段,直接把业务逻辑塞进单个节点,导致图结构扁平化,失去了框架提供的中断和可视化能力。正确的做法是在初始化时就明确划分职责边界,把每个可独立验证的步骤拆成节点,并为每条边编写清晰的路由逻辑。这样做之后,调试时可以通过框架内置的 draw 方法直接看到执行路径,而不再是黑盒。

这个图驱动的设计让 Agent 不再是线性脚本,而是真正具备分支和循环能力的计算图。理解这一点是后续所有机制的基础,如果跳过这一步,后面的状态管理和工具调用都会显得莫名其妙。

状态管理通过检查点实现中断与恢复

LangGraph 把状态持久化做到了检查点(checkpoint)层面。每次节点执行完毕,框架会把当前完整状态序列化后存入后端存储,支持内存、SQLite、PostgreSQL 等多种实现。检查点不仅记录变量值,还包含图的当前节点位置、已执行历史和元数据。

开发者可以通过 config 中的 configurable 字段传入 thread_id 来标识不同会话,从而实现多用户并发时的状态隔离。当任务因为网络中断、Token 超限或人为暂停时,已经保存的检查点允许从断点精确恢复,而不需要从头重新跑整个流程。这对长时任务特别关键,比如一个需要连续调用十几个外部 API 的数据分析 Agent,中间任何一步失败都能从检查点重启。

检查点的实现代价是序列化开销。复杂的状态对象如果包含大模型返回的原始响应、长上下文或二进制数据,序列化时间和存储体积都会显著上升。LangGraph 提供了 state reducer 机制,允许开发者自定义如何合并新旧状态,减少不必要的数据复制。

AutoGen 的状态管理相对轻量,主要依赖消息历史列表。每个 Agent 维护自己的对话历史,框架不强制持久化到数据库,开发者需要自行实现。CrewAI 则在 Task 和 Crew 层面提供了一定状态追踪,但检查点能力不如 LangGraph 完整。

实际开发中,推荐在关键节点后主动调用 checkpointer 的 put 方法,确保状态及时落地。同时要设计合理的状态 schema,只保留必要字段,避免把整个 LLM 响应对象都塞进去。很多团队在生产环境落地时因为状态过大导致内存爆炸,才意识到检查点机制既是功能也是约束。

工具调用被抽象为节点间的消息传递

在这些框架里,工具调用不再是简单的函数调用,而是被建模为图中的特殊节点。工具节点接收上游节点发来的消息,执行具体函数,把结果以消息形式发往下游节点。

LangGraph 使用 ToolNode 类封装工具,工具的 schema 通过 Pydantic 或 JSON schema 描述。调用发生时,框架先把 LLM 生成的结构化参数解析出来,传给工具函数,执行结果再包装成 AIMessageToolCall 类型的消息,重新注入图的状态中。这种消息传递机制让工具调用可以被中断、记录和重放,与传统同步函数调用差异明显。

消息队列在这里起到解耦作用。节点之间不直接调用,而是把输出写入共享状态,由图引擎统一调度下一节点。这带来两个好处:一是调用链路可观测,二是支持异步和并行执行。但也引入了序列化成本,每一次工具调用结果都需要经过消息格式转换。

AutoGen 的工具调用更接近传统多 Agent 对话,每个 Agent 可以声明可调用函数,当接收到特定消息时触发本地函数。CrewAI 则把工具绑定到具体 Agent 角色上,执行时按角色顺序传递上下文。

开发者经常犯的错误是把工具函数写得过于复杂,把大量业务逻辑塞进单个工具,导致消息体积膨胀。更好的做法是保持工具函数单一职责,只完成原子操作,复杂逻辑通过多个工具节点串联完成。同时要为每个工具准备清晰的 docstring,因为 LLM 完全依赖这些描述来决定是否调用以及如何传参。

LangGraph AutoGen CrewAI 的核心循环机制存在本质差异

LangGraph 的核心是预编译的图执行引擎。compile 之后得到的是一个可调用 runnable,每次 invoke 或 ainvoke 都会沿着图的边推进,直到到达结束节点或遇到中断条件。循环通过在边上设置条件函数实现,如果条件始终满足就会形成循环,但框架会通过 recursion_limit 参数防止无限循环。

AutoGen 采用对话驱动的循环。框架维护一个群组聊天,由 GroupChatManager 决定下一个发言的 Agent,形成「说话-回应-说话」的循环。这种机制更灵活,但执行路径难以预测,调试难度更高。开发者经常需要通过自定义 SpeakerSelectionMethod 来控制循环逻辑。

CrewAI 的执行模型是层次化流程(hierarchical process)。它支持 sequential 和 hierarchical 两种模式,前者按顺序执行,后者由经理 Agent 动态分配任务给工人 Agent。循环体现在任务完成判断上,当所有子任务都标记为完成时,整个 Crew 才结束。

在实际使用场景中,选择哪种框架取决于任务特性。需要强可控性和可解释性的长流程推荐 LangGraph,需要快速原型和自然对话的场景适合 AutoGen,而强调角色分工和任务分解的团队更喜欢 CrewAI。三者在状态流转上的差异直接影响生产部署方式:LangGraph 可以轻松对接外部工作流引擎,AutoGen 更适合聊天式交互产品,CrewAI 则在企业内部自动化流程中表现较好。

性能瓶颈集中在状态序列化和并行工具调度

状态序列化是目前最主要的性能开销。每次检查点保存都要把整个状态对象转成 JSON 或 pickle,对于包含几万 token 上下文的 Agent 来说,这个过程可能消耗几百毫秒。LangGraph 虽然提供了异步检查点,但默认实现仍是同步的。

另一个瓶颈是并行工具调度。很多开发者以为框架会自动并行执行所有工具调用,实际上 LangGraph 默认是顺序执行,只有显式使用 Send API 或并行边才能触发并发。而并发又带来新的问题:状态合并时的冲突处理,以及资源争抢。

边界条件也很容易被忽视。比如 Token 计数器没有及时更新导致上下文无限膨胀,工具超时没有设置全局 timeout,检查点存储后端在高并发下成为单点瓶颈。这些问题在小规模测试时不会暴露,一旦上线到日活跃用户过万的系统就会集中爆发。

实际测量显示,一个包含 5 个工具节点的流程,状态序列化可能占总耗时的 35% 以上。优化方向包括精简状态字段、使用更快的序列化库、采用 Redis 作为检查点后端,以及在非关键路径上关闭检查点。

开发者最大误区是假设框架会自动兜底所有异常

很多中文开发者看到框架文档里写了「robust」和「error handling」就认为生产环境可以高枕无忧。实际编码中,框架只处理它能理解的结构化错误,对于工具函数内部抛出的网络异常、数据库连接失败、权限错误等,框架只会简单地把异常信息塞进状态,然后交给下一个节点处理。

这意味着开发者必须在每个工具节点里实现完整的 try-except 逻辑,并决定是重试、降级还是把错误信息转化为用户可见的回复。LangGraph 提供了 retry 装饰器,但粒度较粗,无法针对不同错误类型设置不同策略。

另一个常见误区是认为图结构能自动解决幻觉和漂移问题。实际上如果没有设计良好的状态校验和人工介入节点,Agent 仍然会在长流程中逐渐偏离初始目标。生产环境落地时,建议在关键决策节点后增加人工审核或 LLM 自校验步骤,而不是完全依赖框架的自动循环。

对中文开发者来说,文档多为英文,示例也以 OpenAI API 为主,在对接百度千帆、阿里通义或本地部署模型时,工具调用格式转换经常出错。建议尽早搭建完整的可观测链路,把图执行轨迹、每个节点的输入输出、检查点快照全部记录下来,否则排查问题时只能靠日志猜。

理解这些机制后,开发者才能把 Agent Framework 从「看起来很厉害的 Demo」变成真正可维护的生产系统。框架提供了强大的抽象,但抽象之下仍是需要仔细调优的工程细节。

参考来源