2906 字
约 9 分钟
19
S21-X12 · Agent 框架选型:LangGraph、CrewAI 与 ADK 的分野

选框架最常见的错误不是选错,而是流程还没想清楚就选了。三家都能跑起 demo,差别全在第三天:加人工审批、断点续跑、拆可观测步骤、让 Java 后端复用同一套工具时,摩擦面完全不同。不谈"谁更强",只拆分工边界与判据。


1. 框架替你解决的三类问题

框架的价值必须能写成"不用它我要自己写什么",否则就是负债。

问题域 框架提供什么 自写的真实成本 是否值得买
编排与状态 节点/边、循环、条件分支、状态合并、检查点 状态机 + 序列化 + 恢复,典型量级几百到两千行 值得,尤其检查点
工具与模型适配 schema 生成、多供应商切换、结构化输出、重试 每家 SDK 一层适配器,接口变动跟着改 部分值得,抽象层会漏字段
可观测与部署 轨迹、token 计量、托管运行时、流式 OTel 手埋点 + 自建 trace 表 值得,但别指望开箱即全

反过来:如果流程是"读一条消息 → 调一次模型 → 写一个字段",这三类问题都不成立,自己写两百行更省。框架的成本不在学 API,在于它拿走了控制流的所有权。先问:三个月内会加几个分支、几个人工审批点? 都是 0,就别上框架。


2. 三种定位:图、角色、生命周期

中心对象不同,决定了思考的单位。

维度 LangGraph CrewAI Google ADK
中心对象 图(节点 + 状态) 角色 + 任务 Agent 生命周期 + 工具
控制流来源 显式画出 Process(顺序/层级) runner 驱动 + 回调
状态单位 TypedDict / Pydantic state Task 输出向下传 Session / State
循环表达 边回跳到前节点 靠重试或层级委派 组合器(LoopAgent 类)
人工介入 interrupt + 检查点 需自己包一层 工具确认回调

2.1 LangGraph:图是唯一真相

状态显式、边显式,"为什么走到这里"永远可回答。

class OrderState(TypedDict):
    order_id: str
    retries: int
    approved: bool

g = StateGraph(OrderState)
g.add_node("plan", plan)
g.add_node("execute", execute)
g.add_node("human", wait_for_approval)
g.add_conditional_edges("plan", route, {"execute": "execute", "human": "human"})
g.add_edge("execute", END)
app = g.compile(checkpointer=checkpointer)   # 恢复点从这里来

代价是你得先把流程想清楚;流程还在变时,画图会变成改图。

2.2 CrewAI:角色与任务,控制流是副产品

researcher = Agent(role="条款检索", goal="找到规范原文条款", backstory="熟悉内部编号体系")
auditor = Agent(role="合规核对", goal="把条款转成检查项", backstory="做过落地检查")

t1 = Task(description="检索 ORDER_SPEC 第 3 章", expected_output="原文 + 出处", agent=researcher)
t2 = Task(description="输出检查清单 JSON", expected_output="JSON 数组", agent=auditor, context=[t1])

Crew(agents=[researcher, auditor], tasks=[t1, t2], process=Process.sequential).kickoff()

写起来最快,语义贴近"分工"。但需要"根据中间结果跳回第三步"时表达就绕:只能把循环塞进 Task 描述让模型自己决定重做与否——确定性交给了概率。

2.3 ADK:生命周期与工具体系是重点

order_agent = LlmAgent(
    name="order_agent",
    model=MODEL_ID,
    instruction="只处理订单状态类问题;其他意图转交 supervisor,不要自行猜测。",
    tools=[FunctionTool(query_order)],   # docstring 即工具描述
)

强项是工具与回调:调用前后都能挂钩子,适合审计、脱敏、限流;弱项是图表达力,复杂分支要靠组合器拼。


3. 状态与控制的表达力对照

对着自己的流程逐行打勾,勾不满的就是将来要手写补的部分。

控制结构 LangGraph CrewAI ADK
条件分支 原生 conditional edge 需在 Task 内描述 靠 flow/组合器
循环与重试上限 原生(回跳 + 计数入 state) 弱,通常靠 max_iter 组合器 + 计数
并行扇出 原生(多边 + 合并节点) 部分 原生 ParallelAgent
人工介入(停数小时) 检查点 + interrupt 需外部队列拼 回调 + 长任务
状态可读性 高,state 就是 dict 中,输出藏在 Task 里 中,session 对象

三者都能循环,但只有 LangGraph 把循环画在同一张图上——评审时不用在脑子里重建控制流。


4. 可观测与持久化的内建程度

POC 最易被忽略、上线后最痛的一块。

能力 LangGraph CrewAI ADK
检查点 内建,可换后端 无内建持久化 Session service
断点续跑 原生(同 thread_id) 需自己存任务输出 需自己接
轨迹 与 LangSmith 生态紧耦合 事件监听 + 第三方 Cloud Trace / OTel 路线
单独重跑某一步 支持(从检查点 fork) 不支持 部分
config = {"configurable": {"thread_id": "order-8821"}}
app.invoke({"order_id": "8821"}, config)   # 跑到人工节点暂停
app.invoke({"approved": True}, config)     # 从断点继续,不重跑前面节点

没有检查点时,"等审批四小时"意味着四小时后整链重跑,前面所有工具副作用重来。这才是"隐式重试导致重复副作用"的根源——不是模型的问题,是架构的问题。


5. 多模型适配的抽象层次

层次 表现 风险
只换 model 字符串(LiteLLM 类网关) 三家都常见 供应商特有字段被丢弃
厂商 SDK 直连 ADK 对 Gemini 路线最顺 跨供应商要另加适配层
自建网关 统一计费/限流/审计 多一层运维,流式与工具调用要透传

换模型时先验的不是回答质量,而是工具调用是否还稳定:描述与 schema 相同,命中率也可能差一截。


6. 和既有后端的集成边界

现实里 agent 很少孤立,典型是 Java 主站 + Python 侧车。

集成方式 适用 代价
Spring AI 直接做 流程简单、团队只有 Java 图编排弱于 LangGraph
Python 侧车暴露 HTTP 职责清晰 要处理超时、幂等、鉴权
通过 MCP 共享工具层 工具被多客户端复用 需维护协议与版本
消息队列异步 长任务、可容忍延迟 需补偿与状态查询接口

MCP 是最值得投入的一层:工具定义一次,Python agent、Java 服务、IDE 客户端共用同一份 schema。

params = StdioServerParameters(command="python", args=["-m", "order_mcp"],
                               env={"LOG_LEVEL": "warn"})
async with stdio_client(params) as (read, write):
    async with ClientSession(read, write) as session:
        await session.initialize()
        tools = await session.list_tools()

Spring AI 侧对应 MCP client 自动配置,把同一批工具注册成 ToolCallback。边界划在这里,换框架时工具层不用重写。


7. 选型判据清单

按顺序回答,前面的答案会否决后面的选项。

判据 问题 指向
流程确定性 分支和顺序现在能画出来吗 能 → 图型;不能 → 先原型再谈框架
人工介入 有停几小时等审批的环节吗 有 → 必须有检查点,排除无持久化方案
断点续跑 失败能否接受从头再来 不能 → 同上
团队语言栈 主力 Java 还是 Python Java → Spring AI / MCP 桥;Python → 三者皆可
可观测要求 需要逐步复盘吗 需要 → 查检查点与 OTel 完整度
迭代节奏 三个月内会大改吗 会 → 选约束少的,别选图

把这张表做成打分卡:每项按能力给 0–3 分,低于阈值直接出局,别用"感觉更顺"做决策。


8. 迁移成本:耦合长在哪

迁移成本不看行数,看编排逻辑有多少长在框架 API 上。

耦合点 迁移难度 缓解办法
状态定义 低 纯数据类,别继承框架基类
节点函数签名 中 内部写成纯函数,外头套薄壳
路由函数 中 只读 state,不读框架上下文
检查点格式 高 只存业务状态,不存框架内部对象
工具注册方式 中 走 MCP 或自建注册表,别用装饰器直绑
def execute_refund(state: dict) -> dict:
    return refund_service.apply(state["order_id"], state["amount"])   # 纯业务

def node_execute_refund(state: OrderState) -> dict:
    return execute_refund(dict(state))     # 只有这一层知道框架

坚持这层薄壳,换框架时改壳不改业务。


9. 排错:四个高频坑

现象 根因 处理
简单流程也"模型自己选下一步",结果飘 为"agent"把确定性流程做成自主决策 能写 if 的地方不给模型;只把真需判断的环节交出去
DB 业务状态与框架 state 不一致 两套状态各写各的,没有单一写入点 业务状态以 DB 为准,state 只存游标与临时结果
一次失败后下游收到两笔退款 隐式重试 + 工具不幂等 工具带幂等键;重试次数显式配置,不留默认值
半年后框架停更,升级卡死 选了不活跃项目且深度耦合 看提交频率与发版节奏;耦合点按第 8 节收敛

框架活跃度是选型指标,不是八卦:查最近几个月的 release 与 breaking change 频率,比 star 数有用。


10. 边界:这些情况本就别用框架

场景 更合适的做法 理由
单轮抽取/分类 SDK + 结构化输出 框架只多一层间接
流程完全确定、无模型判断 普通工作流引擎 不需要概率决策
只要工具复用 MCP server + 简单 client 工具层与编排层可分开
强审计、每步留证 自建编排 + 显式状态表 框架 trace 未必满足审计
极致延迟敏感 单次调用 + 缓存 抽象有开销

一句话:框架买的是"状态 + 恢复 + 轨迹",不是智能。 这三样都不需要,它就是纯成本。


11. 自测

  1. 你的流程里"下一步做什么"由代码决定还是由模型决定,分界线画在哪?
  2. 没有检查点时,一次要等人工审批四小时的流程会发生什么?
  3. LangGraph 的 conditional edge 与 CrewAI 的 Task 描述,在可评审性上差在哪?
  4. 团队主力是 Java,走 MCP 共享工具层能省掉哪部分重复工作?
  5. 隐式重试在什么条件下会产生重复副作用,你打算怎么堵?
  6. 判断一个框架是否活跃,你会看哪三个客观信号?
S21-X12 · Agent 框架选型:LangGraph、CrewAI 与 ADK 的分野
https://www.clxhxhhr.top/posts/5906/
作者
clxstart
发布于
2024-12-31
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。