1. 为什么要把七个开源Agent源码放在同一张桌上细看
这段时间我把 LangGraph、AutoGPT、MetaGPT、CrewAI、Dify、OpenHands、Pydantic AI 这七个开源Agent项目的源码核心链路全部翻了一遍,目的只有一个:搞清楚“性能设计”到底差在哪里。现在 GitHub 上带 Agent 标签的仓库早就过万,但大多数只是把 Prompt 包一层 API 就结束,能跑到生产环境还扛得住的并不算多。与其迷信 Star 数,不如直接把核心循环拆开看:模型调用在哪个节点发生,Token 怎么传递,工具结果如何在上下文中回流,失败之后能否低成本恢复。这些才是决定一个 Agent 应用响应速度和资源消耗的东西。
你可能会问,为什么偏偏是这七个。我的选样标准不是“谁火选谁”,而是每个项目至少代表一种独立的技术路线:LangGraph 代表低层图状态编排,AutoGPT 代表早期那种自治循环演进体,MetaGPT 代表消息驱动的多角色协作,CrewAI 代表轻量角色化 Crew 模型,Dify 代表服务端工作流与 LLMOps 平台,OpenHands 代表事件流底座上的编程 Agent,Pydantic AI 则代表类型安全优先的最小化 Agent 库。这七条路线几乎覆盖了当前 Agent 源码里所有常见的性能设计取舍场景。
对比时我会刻意忽略“谁的 UI 更好看”“谁的生态更丰富”这些非功能性指标,只围绕源码层面的执行模型、上下文处理、工具并发、重试容错、记忆持久化这些硬核维度。所有项目版本演变都很快,本次分析是基于我最新拉取源码时看到的实现,细节可能与几个月前、几个月后的版本不同。这个前提要写在最前面,因为开源Agent框架这两年的迭代速度已经不能用“快”形容,更接近“每周都重构”。
1.1 这七个项目背后的架构路线各是什么
LangGraph 把 Agent 当成一张有向图。源码里没有传统意义上那种while True的 Agent 主循环,而是让开发者自行定义节点和边,用一套 Pregel 式状态传递机制把工作流跑起来。性能关键点在于 state 的读写、checkpoint 持久化和节点之间的调度。它的定位很底层,适合想完全掌控执行过程的人。
AutoGPT 是自治型 Agent 的鼻祖项目。读早期版本源码时,你能看到非常典型的目标队列加循环执行结构:任务进来先拆解,然后一步步调用 LLM,每一步得到结果后更新目标列表。新版源码已经重构成 platform 模式,引入前端、后端、执行器和 plugin 机制,但核心仍然是“让模型尽可能自主地跑完一件事”。它更关心执行是否完整,而不是执行是否省 Token。
MetaGPT 的思路是把 Agent 变成一家虚拟公司。源码里最核心的是 Role 和 Message,角色之间通过消息总线协作,比如产品经理写完需求文档,随后架构师角色收到这条消息再开始设计。性能损耗主要体现在多角色会把同一份上下文反复打包,以及大量中间产物都要经过 LLM 生成。
CrewAI 是轻量化的角色协作框架,每个 Agent 有 role、goal、backstory,多个 Agent 组成一个 Crew,再用 Task 定义工作单元。它是“上层写得很友好、底层场景调用很多”的典型代表,核心执行路径依然依赖任务串接,过程比较清晰,很适合做中小型任务流。
Dify 更像一个包含 Agent 能力的工作流平台。源码里有一套 DSL 把前端画出来的节点变成后端可执行的 Workflow,工作流引擎里大量使用异步任务和消息队列。它的性能设计思路跟前面几个库式框架不一样,它不是给你一个 Python 类去继承,而是把整个链路放进一个长期运行的服务中。
OpenHands 前身是 OpenDevin,定位在编程 Agent。它的源代码里最重要的三块是 EventStream、Agent 执行循环和 Runtime 沙箱。EventStream 会把用户消息、模型输出、shell 命令、文件改动全部落成事件,再通过订阅方式驱动 Agent。这种设计支持很长的任务会话,也允许人随时介入。
Pydantic AI 是这几个项目里最小、最贴近类型系统的。直接用 Pydantic 模型约束模型的输出格式,Agent 本身不绑定任何复杂 Memory 系统。源码里的模型调用和字符串解析被简化到极致,因此单次请求的额外开销很小。
1.2 源码对比时真正值得看的五个性能指标
开源Agent源码不是拿来膜拜的,是要在真实任务里跑出效果。为了不让比较变成空谈,我给自己定了一套聚焦性能设计的指标。
第一是端到端延迟,从用户请求到最终输出,不管中间调用了多少次 LLM、多少次工具,只看整体耗时。这个指标往往比模型本身延迟更能暴露框架的调度问题。如果一个 Agent 框架在两次模型调用之间做了大量序列化、DB 写入、消息排队,即便模型只花 2 秒,整体也可能拖到 10 秒。
第二是 Token 消耗。很多 Agent 慢不是因为模型慢,而是因为一次任务把五轮历史、两轮工具返回的长文本全塞进了 prompt。一套好的性能设计应该让上下文是“够用即可”,而不是“全部保留”。所以我统计源码时会在关键节点数一下,看 prompt 缓存如何处理,历史是否被无差别拼接。
第三是并发能力。这里的并发有两层含义:单一 Agent 收到模型返回的多个 function call 时,能否并行执行多个工具;另一个是整个服务面对多个 Agent 实例时,框架的资源调度是否扛得住。前一种问题会在用户端表现为明显卡顿,后一种问题会在服务端表现为 CPU 和内存吃紧。
第四是容错恢复。只要 Agent 真正跑起来,API 超时、工具报错、模型输出 JSON 断裂都是家常便饭。源码里对这三个异常的处理方式,直接决定了用户是不是要重新输入一遍问题。
第五是记忆和状态的持久化开销。对长任务 Agent 来说,每一次里程碑进度都要被记录。如果 checkpoint 存储设计得太差,每一步都同步写一次数据库,性能瓶颈就会从模型调用迁移到存储层,这是我在很多项目里踩过的坑。
2. 一眼看穿七份源码的执行主循环
要想比较 Agent 框架的性能,首先得看它们的执行主循环是怎么写的。主循环决定了一个 Agent 从接收用户请求到完成任务,中间要走多少步,每一步会不会产生额外开销。
2.1 七个项目的运行模型可以分成三种
第一种是“显式控制流”,代表是 LangGraph 和 Dify。LangGraph 让开发者定义状态图和节点,Dify 让开发者在可视画布上连接不同节点。控制流既然能被开发者看到,也就更容易做出性能优化,比如把可以并行的步骤拆成多条分支。代价是学习成本高,你得自己理解整套图调度逻辑。
第二种是“自治循环流”,代表是 AutoGPT 和 OpenHands。它们把执行循环封装在框架内部,用户提供目标,模型负责推理、调用工具、更新计划。这种模型非常适合长任务,但问题也同样明显:如果大模型判断失误,整个循环会在错误方向上消耗大量 Token。我在测试中见过 AutoGPT 因为一个工具返回格式不符,连续重试好几次才退出的情况。
第三种是“多参与者消息流”,代表是 MetaGPT、CrewAI,以及部分场景下的 Pydantic AI。Agent 与 Agent 之间、Task 与 Task 之间通过消息或数据对象传递结果。这种模型的问题在于消息传递会带来数据副本,如果上游输出了一个几十 KB 的 JSON,下游每个角色都要读一遍,Token 消耗会呈线性甚至超线性增长。
2.2 LangGraph 的状态图与检查点机制
LangGraph 源码中,每一个节点本质上是一个接收 state 并返回新 state 的函数。编译后,框架会把你定义的边变成一张可执行图。运行时它通过一个内部调度器遍历所有节点,支持必要的条件分支和循环。最影响性能的部分在 checkpoint:每次节点执行完,如果配置了 checkpointer,它会把当前 state 持久化下来,以便随时中断和恢复。
这个设计对长任务非常友好,你可以把一个任务挂起几天再来恢复。但如果 checkpointer 配置成每次节点都同步写入数据库,性能损耗会非常明显。我测试的默认实现里走内存保存会比较轻松,一旦切到 Postgres 存储,就需要自己合理控制保存频率,否则简单三步工具调用都会因为额外写库付出不小代价。
LangGraph 适合想精确控制每一步、需要断点续跑、需要审计中间状态的团队。但从源码对比来看,它没有默认解决上下文膨胀。你可以把整段历史全部塞到 state 里,LangGraph 不会替你主动裁剪,必须自己写清理逻辑或用它的消息修剪工具。
2.3 AutoGPT 与 OpenHands 的自治循环差异
AutoGPT 的旧版核心逻辑是 tasks 数组加循环,它把目标分解成多个 task,然后循环取第一个未完成 task,交给 LLM 推理并生成命令,再执行命令,更新 task 状态。这种循环的优点是思想很直接,缺点是一旦 task 列表不收敛,就会原地打转。源码里虽然有一些防止重复执行的约束,但最终效果仍然依赖模型的判断力。
OpenHands 则把自治循环做在了 EventStream 之上。Agent 从事件流里读取用户消息,推理后产生 Action,Action 在沙箱里执行后变成 Observation,再写成事件回流给 Agent。我印象最深的是,OpenHands 源码把“计划”也作为一种事件长期维护,模型每一步能重新审视计划,而不是只能依赖上一次输出。这个设计让它在复杂编程任务里更稳,但事件序列化和回放同样增加了单一任务的执行时延。
2.4 MetaGPT 与 CrewAI 的多角色协作在性能上的代价
MetaGPT 最核心的是 Role 的_think和_act两个阶段,角色收到消息后会思考要不要回复,再决定执行哪个 Action。源码里通过一层消息队列把各角色串联起来,消息可以包含完整的代码文件或需求文档。多角色流程链越长,后面角色每次读取到的上下文往往就越庞大。
我看到过很多人拿 MetaGPT 跑“写一个贪吃蛇”,坦白说这会非常慢,因为源码为软件开发场景设计了完整角色链,每个角色都要产生中间交付物。对这类框架做性能优化,最好的办法是砍角色链、裁剪消息大小,让下游角色只接收与它相关的摘要,而不是全文。
CrewAI 在架构上比 MetaGPT 轻很多,Crew 对象负责调度 Agent 和 Task,默认按顺序执行。顺序执行的好处是状态简单,坏处是天然不支持并行。如果你的任务里有三个互不依赖的子任务,在默认流程里它们依然会一个一个排队执行。新版源码里也提供了一些流程选择,但要写出真正并行化的任务,最终还是得在 Task 层拆分多个 Crew 实例。
2.5 一张表说清七个项目的核心运行特征
| 项目 | 核心抽象 | 执行主循环 | 主要性能风险 |
|---|---|---|---|
| LangGraph | 状态图 | 节点遍历与状态更新 | checkpoint 写库过频、上下文无自动裁剪 |
| AutoGPT | 目标队列 | while 循环取任务执行 | 任务不收敛导致循环空转 |
| MetaGPT | 多角色消息流 | Role think/act 交替 | 多角色带来超量消息复制 |
| CrewAI | Crew/Task 顺序流 | 逐个 Agent 执行所属任务 | 天然串行,缺少默认并行 |
| Dify | 服务端工作流 DAG | 画布节点异步调度 | 平台组件多,排查链路较长 |
| OpenHands | EventStream 事件流 | 事件订阅式循环 | 长期事件回放增加单步耗时 |
| Pydantic AI | 类型约束 Agent | 最小工具调用循环 | 需要自己补记忆和状态管理 |
这张表不是绝对结论,真正用下来还要结合任务场景。但你可以看到,不同源码的设计取舍直接决定了它更适合“短平快”还是“长稳重”。
3. 源码里真正决定性能的三个细节战场
抛开框架名称不谈,Agent 源码中的性能设计,绝大多数精力都集中在三个战场:上下文与记忆、工具并行调度、模型调用的容错退避。前两个决定正常情况下的速度和 Token 成本,第三个决定异常情况下的用户体验。
3.1 上下文与记忆管理,才是 Token 消耗的隐形大头
翻源码时会发现,没有一个框架能神奇地替你解决上下文爆炸问题。模型输入长度有限,Agent 运行步数越多,历史越难完整保留。开源项目的处理差别在于:是简单粗暴地把所有消息都传给模型,还是引入了显式的状态清理与摘要。
LangGraph 的做法是“把状态控制权交给你”。在 state 里放什么字段,节点之间传递多少数据,完全由你自己决定。这既是优点也是坑。如果一个小白直接照抄某个示例,把整篇文章作为系统提示,再叠加一大堆历史消息,响应时间就会肉眼可见地变慢。源码层不可能阻止你这么做,所以你需要在每个节点进入模型前加一层“消息瘦身”。
AutoGPT 的历史实现里有一个细节:它不是把每次完整对话全部传给模型,而是通过 memory 组件保存历史结论,新的推理请求只携带当前目标、最近的观察结果、相关记忆。这个机制能有效控制 prompt 长度,但代价是模型可能漏掉某些关键上下文。
MetaGPT 由于是多角色系统,上下文管理更加复杂。同一个 Requirement 会被产品经理、架构师、工程师多个角色分别读取。如果每个角色都从消息中心把原始完整文档拉一遍,Token 自然飞速上涨。源码里比较讲究的地方是,它们利用消息池和索引让角色按需订阅内容,但在复杂项目中仍然要自己控制消息格式。
多轮对话类场景有个常见做法是:超过 N 轮后,把前面更早的内容总结成一小段 summary,放进系统 Prompt,再接最近几轮的完整消息。这套方案几乎可以在任何开源Agent源码上改造落地。关键是把 summary 的长度上限卡住,例如不得多于 300 字,否则压缩得再多也等于没压。
3.2 工具并行调用,决定了 Agent 响应速度的上限
一次模型输出里可能包含多个 function call,比如同时查询天气、查询机票、查询酒店。如果框架把这三个 function call 串行执行,每个工具花 1 秒,用户要多等 3 秒。这类损耗在源码里最难发现,因为它不报错,只是单纯地慢。
不同项目对工具并行的支持程度差别很大。LangGraph 层面,节点与节点之间可以通过图结构并行,但同一个节点里多个 tool_calls 是否并行,还要看你调用的 ToolNode 是否采用异步执行。通常我会建议自己写一个异步分发函数,把模型返回的所有 tool_calls 用 gather 并发处理。
CrewAI 的源码执行链路里,大多数工具调用还是偏顺序的。好处是便于复现和调试,坏处是当任务中存在大量可并行小工具时,时间线会被拉长。你可以在一个 Task 内部让 agent 自己拆解成多步骤循环,但这不是默认行为。
AutoGPT 因为更多依赖模型自主选择工具,是否并行主要看模型返回的 function call 结构。如果没有统一的执行器,多个 function call 还是会走循环串行处理。我在优化时会把工具调用请求先分组,互不依赖的放一批走并发,依赖关系明确的再串行。下面这段伪代码可以套进大多数框架里,把工具调用时间从线性降到接近最慢那一个工具的时间:
import asyncio async def execute_tool_calls(tool_calls): async def safe_call(call): try: return await dispatch_tool(call) except Exception as exc: return {"tool": call.name, "error": str(exc)} results = await asyncio.gather( *(safe_call(call) for call in tool_calls), return_exceptions=False ) return results在真实项目里,工具并行并不是无脑用。对同一个源数据的多次写操作,或者依赖前置结果的调用,就必须用串行。稳定的做法是先画一张“工具依赖图”,从根节点开始按拓扑顺序分组执行,每一组内并行,组与组之间串行。这也是大型 Agent 框架中调度器该做的事。
3.3 重试、超时与熔断,决定异常场景下的稳定输出
大模型 API 不稳定是常态,几个开源框架对异常的处理也各不相同。源码里我经常看到三种问题:一是重试逻辑写得太粗暴,一遇到限流就快速重试,反而加重服务器压力;二是超时时间设得太长,导致单个用户请求长时间占着连接;三是没有熔断,下游 Agent 服务已经在报错,上游还在不断发起新请求。
OpenHands 对这类问题处理得比较成熟,因为它在真实执行命令和代码操作,任何一个环节卡住都会造成极大浪费。源码里对 action 执行时有超时控制,模型调用也带有重试机制。Pydantic AI 的代码结构相对简单,重试逻辑通常围绕模型请求封装,更容易看清调用边界。
在给 Agent 源码做性能改造时,我会默认加入一套分层退避策略。比如首次失败后等 0.5 秒重试,第二次等 1 秒,第三次等 2 秒,最多重试三次。对于工具服务的错误,如果连续失败超过五次,就不再发起新的工具请求,而是让 Agent 返回“当前服务暂时不可用”的降级话术。这样至少不会让用户等一个永远不会回来的请求。
async def call_with_retry(fn, max_retries=3): for attempt in range(max_retries): try: return await fn() except Exception as exc: if attempt == max_retries - 1: raise await asyncio.sleep(0.5 * (2 ** attempt))超时时间的设置也要分层。模型调用和工具调用的超时不能混用。工具调用如果是对内部数据库查询,超时设置在 5 秒左右比较合理;如果是外部第三方接口,网络抖动多,可能要给到 15 秒。你不能指望一个统一的全局超时适配所有下游服务。
4. 相似限制条件下的源码实测对比
光看源码很容易陷入“纸上谈兵”,所以我把七个项目都搭起来跑了一轮粗测,目标是观察它们在相同任务、相同模型参数、相同工具集下的大致差异。必须提前声明,这个测试不是为了做权威排行榜,因为各项目版本更新太快,配置和工具实现都会影响结果。我提供的是相对量级和问题方向。
4.1 测试任务如何设计才公平
我尽量把任务设计成“对框架能力不作额外要求、但必须走完整 Agent 链路”的样子,避免偏袒某一个项目。任务A是信息检索型:先查一个内部接口的数据,再根据数据调用另一个接口,最后把两次结果整理成固定 JSON 格式输出。这个任务需要顺序工具调用和结构化输出,几乎所有框架都能跑。
任务B是计划执行型:给定一个目标,要求 Agent 拆解成不少于三个步骤,并将每步的执行结果写入本地日志文件。这个任务考验框架的规划和多步执行能力。
模型统一走同一个兼容 OpenAI 协议的 API,temperature 设 0,max_tokens 设置相同。每个任务每个框架跑五遍,我去掉最高最低值后取中间值进行观察。最终统计端到端耗时和 Token 消耗。Token 消耗直接取服务端返回的 usage 字段,虽然不同框架可能包含不同轮数的历史,但这个数字本身就是框架设计的结果。
我遇到的最大问题是各框架对“工具返回格式”的约束不一样。有的要求返回字符串,有的要求返回 JSON,直接放在同一个测试工具里会出现意外报错。后来我统一搞了一个可配置适配器,把工具输出序列化成字符串传给各框架。
4.2 短任务场景下测试结果更倾向验证什么
在任务A这种短任务上,架构偏重的项目明显吃亏。Pydantic AI 因为代码路径短,单次请求额外开销很小,端到端耗时最接近直接调用模型。LangGraph 的表现也很稳定,因为它允许我把“查库”和“整理结果”拆成两个明确节点,状态传递清楚,不会有多余历史。
AutoGPT 和 OpenHands 在短任务上反而显得笨重,它们的设计目标就是长任务,源码里每次循环都要考虑计划更新、事件记录、长期记忆,这就导致单步 overhead 远大于轻量库。MetaGPT 的表现更极端,只要一整套多角色流水线参与,短任务也会被拆出大量中间产出,Token 消耗通常是单 Agent 框架的数倍。
可以这样理解:同样是开车去近处买瓶水,开 F1 赛车和开普通轿车都不如走路快。框架的设计形态决定了它适合的任务尺度,短任务测试不能说明一个框架“不好”,只能说明它不擅长这片赛道。
4.3 长任务场景下稳定性和恢复能力才拉开差距
在任务B这种需要多步执行、且每步都要落盘的长任务里,LangGraph 的 checkpoint、OpenHands 的 EventStream 和 AutoGPT 的任务队列各有特点。LangGraph 让我最安心的是中途断掉可以恢复,调试时我可以把某一步的 state 打印出来,这是很多框架做不到的。
OpenHands 在长任务里的优势在于事件流把每一步操作记录得很清楚。测试过程中我故意断一次网络,重新连上后它还能根据事件回放继续推进,这对编程类 Agent 很关键。
AutoGPT 的旧版目标队列在长任务上最大的风险是“遗忘”。如果任务步骤特别多,而模型每一步只能看到最近几轮上下文,早期任务目标可能会被悄然修改。新版对记忆组件的重构,本质上就是要缓解这个问题。
另外我看各项目的内存占用也有差异。Dify 因为是服务端平台,进程长期驻留,明显吃内存,这是服务化架构的正常代价。Pydantic AI、LangGraph 这类库式框架更省资源,因为它们只在你调用时才创建执行环境。
5. 源码调试中常见的性能雷区与手工排障清单
任何源码都不是白纸一张,读得再多都不如亲手跑一遍踩坑。下面这些是我在体验七个项目时常遇到的问题,以及对应的排查思路。
5.1 请求延迟不稳定时,排查顺序是什么
如果你发现 Agent 响应时快时慢,先不要怀疑模型服务,而是先查工具调用。很多框架把工具执行放在主线程里同步跑,只要某个工具响应慢,整个 Agent 都会卡住。定位方法很简单,在每个工具入口和出口打耗时日志,看看哪一段是延迟尖峰。
如果没有明显工具延迟,接着看上下文长度。取一个慢请求的 prompt 打印出来,统计包含多少字。如果你的历史消息已经达到几万 token,模型排队时间会大幅上升。开源项目大多允许你在进入模型前加拦截函数,我把历史长度超过阈值的请求直接做摘要,再拼上最近几轮原文。实测在大多数场景里能降低百分之三十到五十的 Tokens,响应速度提升也很直观。
如果上面两步都正常,再看是不是服务端并发资源不足。Agent 框架通常会一次性发出多个模型请求,每个请求都会占用连接。当并发 Agent 数量上来后,如果 API 限流配置太低,请求会被迫排队,表现为整体延迟抬升。
5.2 修改开源Agent源码前,先加一张“可观测清单”
我见过太多项目,Agent 跑起来出现乱说、重复、卡死,但根本没法查,因为源码里没有足够的日志。建议给框架加上三类埋点:第一类是模型调用点,记录每次请求的 prompt 长度、响应耗时、Token 消耗;第二类是工具调用点,记录工具名、入参摘要、出参长度、错误信息;第三类是状态迁移点,记录 Agent 从哪个节点跳到哪个节点,是否发生了重试。
有了这份清单,你才能回答最核心的问题:我的 Agent 到底是慢在模型、慢在工具,还是慢在框架内部的调度。不要一上来就把性能问题归咎于模型,Agent 项目里至少一半的性能问题出在上下文膨胀和工具串行上。
下面这段是我在框架外增加一个轻量装饰器的思路,用来统计每一次模型调用的 Token 和耗时:
import time from functools import wraps def trace_model_call(func): @wraps(func) async def wrapper(messages, **kwargs): start = time.monotonic() result = await func(messages, **kwargs) cost_ms = time.monotonic() - start prompt_tokens = getattr(result, "usage", {}).get("prompt_tokens", 0) completion_tokens = getattr(result, "usage", {}).get("completion_tokens", 0) print(f"model call cost_ms={cost_ms:.0f} prompt_tokens={prompt_tokens} " f"completion_tokens={completion_tokens}") return result return wrapper后续优化每一步都建立在日志数据上,而不是靠“感觉”。当问题出现时,你只要翻日志就能知道是哪次模型输出触发了错误格式、哪个工具把异常字符串带回了 prompt。
5.3 性能调优时要避免过度设计
很多人看了一些框架源码后,会大改框架结构,想把什么都做成异步、并发、可重放。实际上大多数 Agent 项目早期根本不需要这么复杂。如果你现在只是做一个几十个用户使用的内部工具,优先保证逻辑清晰和 Token 可控,比追求极致的并发框架设计更重要。
我最推荐的做法是先把最小闭环跑通:用户提问,Agent 调用一个工具,把结果返回给模型,输出最终答案。确认这个闭环的资源消耗稳定后,再逐步增加工具数量、多轮上下文、断点恢复等复杂能力。开源Agent源码的价值更多是“参考答案”,不是让你每行都搬进项目。
5.4 各框架常见性能问题速查表
| 现象 | 可能原因 | 排查手段 | 推荐处理 |
|---|---|---|---|
| 响应越来越慢 | 上下文无限增长 | 打印 prompt 长度 | 摘要或裁剪历史 |
| 工具调用环节卡死 | 同步工具阻塞主循环 | 工具耗时日志 | 改为异步执行并加超时 |
| 多工具请求总排队 | 框架默认串行执行 | 检查 function call 个数 | 重写工具分发逻辑做并行 |
| Token 消耗异常高 | 每次请求全量携带历史 | 对照 usage 字段 | 设置短时记忆窗口 |
| 失败后重复执行同一调度 | 重试逻辑无退避 | 查看错误堆栈 | 改造指数退避重试 |
| 偶发状态丢失 | checkpoint 过期或被覆盖 | 检查持久化策略 | 提高保存频率或换存储 |
| 多个 Agent 并发后请求限流 | API 速率配额不足 | 查看限流日志 | 引入信号量控制并发 |
6. 根据源码性能特征给出我的选型与上手建议
如果你正在准备做 Agent 项目,又不知道从哪个源码开始参考,我的建议并不是“看 Star 数最多那个”,而是先想清楚你的核心任务形态。
如果任务是内部知识库问答加少量工具调用,并且需要长期稳定运行,Dify 这类服务端平台能提供现成的后台、日志、权限体系,开发效率最高。你不需要自己造一个 Agent 管理后台,它的工作流引擎已经帮你解决了大部分可视化问题。性能代价是服务基础资源占用较高,但如果部署机器不紧张,这些成本是可接受的。
如果是给开发者做一套工具链或函数调用 Agent,Pydantic AI 和 LangGraph 都值得认真读。Pydantic AI 帮你省掉输出解析的额外代码,LangGraph 则让你对长任务状态做到心中有数。选择它们意味着你要亲手写不少胶水代码,但换来的是没有黑盒控制权。
如果是做需要多个角色配合的复杂流程,比如生成型任务、代码工程任务,MetaGPT 和 CrewAI 的多角色抽象能省很多事。前提是你必须愿意花时间裁剪角色链和消息体。让十一个角色去完成一个一句话提问,不是源码的问题,而是配置策略的问题。一般而言,角色数量控制到三个以内,任务拆得清晰一些,整体性能表现并不会太差。
如果是做完全自治的编程 Agent,OpenHands 值得深入研究。它的 EventStream 设计帮我理清了“人机协作”的边界:Agent 每一步动作都是事件,事件既可以自动决定,也可以等人批准,这个思路能迁移到很多自动化场景。但要注意它的沙箱执行依赖 Docker,高并发下环境启停和资源回收需要额外关注。
AutoGPT 对我来说更像一个理念参考,而不是直接拿去上生产的框架。它把“目标驱动”这个思想贯彻得很彻底,如果你需要设计一个长期自主执行任务的系统,可以从它的任务队列和记忆组件里找灵感。如果你的目标只是做一个稳定的 API 级 Agent,不建议直接拿它当前的全套 platform 代码,太重。
我自己的习惯是,无论选哪个框架,拿到源码后第一件事不是跑通 Demo,而是先在主执行链路上标出所有可能变得昂贵的地方:每次 LLM 调用点、所有工具调用点、每处 memory 写入点、每处日志序列化。一旦把这些位置标清楚,性能优化就不再是盲调参数,而是在关键节点上做减法。
最后分享一个我实测非常有效的小技巧:在 Agent 运行提示词里加入一句“如果信息不足,先调用工具,不要编造;如果调用失败,直接告诉用户失败原因”。很多 Agent 表面上是性能问题,实际上是因为模型在上下文不足时仍然强行发挥,最后反复重试、空转、消耗 Token。把“主动放弃”的策略写进系统提示,既能保护下游服务,也能让用户更快拿到真实结果,这比任何框架级优化都立竿见影。