2026 年 AI 代理技术栈:LangGraph vs 自定义 vs DIY

我每周都会将代理部署到生产环境,框架选型问题在每次项目启动会上都会反复出现。老实说,“正确”的技术栈取决于分支复杂度、状态量以及你能承受多大的影响范围。我在过去一年中用三种方式构建了同一个代理,权衡利弊比框架营销宣传的要尖锐得多。以下是我实际的决策方式。

我经常使用的三个技术栈

当我开始一个新代理时,会从三个类别中选择。其他方案都是这些的变体:

  1. DIY 循环,即围绕 LLM 调用、工具执行和消息数组的普通 while 循环。大约 150 行 Python 或 TypeScript 代码。
  2. 框架,目前几乎总是 LangGraph,偶尔在特定场景下使用 Pydantic AI 或 OpenAI Agents SDK。
  3. 自定义编排,即在消息队列或 Temporal、Inngest、AWS Step Functions 等持久执行器之上编写自己的状态机。

决策取决于三个问题:步骤数量、并发量,以及凌晨 3 点崩溃时会发生什么。其他一切(可观测性、评估、人工介入)都可以附加到这三种方案中的任何一种。

何时 DIY 循环是正确答案

对于少于约五个工具且具有线性“思考-行动-观察”循环的代理,普通循环是最佳选择。我将此用于我部署的约 40% 的代理,包括 BizFlowAI ContentStudio 中的大部分子代理。框架会增加抽象成本,只有在真正需要分支时才值得。

以下是整个代码的样子(不含日志):

def run_agent(task: str, tools: dict, max_steps: int = 12):
    messages = [
        {"role": "system", "content": SYSTEM_PROMPT},
        {"role": "user", "content": task},
    ]
    for step in range(max_steps):
        resp = client.messages.create(
            model="claude-sonnet-4-5",
            messages=messages,
            tools=list(tools.values()),
            max_tokens=4096,
        )
        messages.append({"role": "assistant", "content": resp.content})

        if resp.stop_reason == "end_turn":
            return resp.content[-1].text

        tool_results = []
        for block in resp.content:
            if block.type == "tool_use":
                try:
                    result = tools[block.name]<a href="**block.input">"fn"</a>
                    tool_results.append({
                        "type": "tool_result", "tool_use_id": block.id,
                        "content": str(result),
                    })
                except Exception as e:
                    tool_results.append({
                        "type": "tool_result", "tool_use_id": block.id,
                        "content": f"ERROR: {e}",
                        "is_error": True,
                    })
        messages.append({"role": "user", "content": tool_results})

    raise RuntimeError("Agent exceeded max_steps")

进入全屏模式 退出全屏模式

就是这样。优点是实实在在的:

  • 可在 IDE 中调试。 堆栈跟踪指向你的代码,而不是框架内部的三层抽象。
  • 令牌使用透明。 你可以看到并控制每一条发送的消息。上个季度我在一个项目上将 LLM 支出减少了 40%,大部分是通过在这个循环中修剪消息数组实现的。
  • 你掌控重试和错误面。 工具错误会以 is_error: true 的形式返回给模型,使其能够恢复;如果模型在损坏的工具上循环,你可以捕获它,因为是你编写了计数器。

DIY 的局限性:跨长时任务的并行工具执行、多代理交接,或任何需要检查点以在进程重启后存活的场景。此时你要么需要构建框架,要么需要使用框架。

何时 LangGraph 值得其复杂性

2026 年我反复使用的框架是 LangGraph,主要是因为它将代理视为具有类型化状态的节点图,这种模型实际上与复杂代理的行为方式相符。当工作流具有真实分支(三个或更多有意义的路径)、需要跨数小时或数天的持久执行,或有多个代理相互通信时,我会使用它。

价值体现在三个具体功能上:

  1. 类型化状态作为一等对象。 你为状态定义 TypedDict 或 Pydantic 模型,每个节点返回部分更新。这比听起来更有价值,因为它迫使你思考状态转换,而不是传递消息数组。
  2. 检查点。 使用 SqliteSaverPostgresSaver,如果进程死亡,图可以从最后一个节点恢复。对于长时运行的研究代理或人工介入工作流,这是不可或缺的。
  3. 中断以供人工审核。 你可以在节点暂停图,等待外部输入(Slack 批准、表单提交),然后恢复而无需重新运行之前的节点。

我遇到的问题:LangGraph 对 LLM 调用的抽象会隐藏令牌核算,除非你显式连接回调。文档更新很快,六个月前有效的某些模式现在已被弃用。如果使用它,请锁定版本并编写能快照实际发送的 API 调用的集成测试。

根据我已部署的代理的粗略经验法则:

代理类型 节点数 最佳选择
单一用途工具调用器 1-3 DIY 循环
研究 + 撰写 + 审核 4-8 LangGraph
具有交接的多代理 8+ LangGraph 或自定义
长时运行工作流(数小时以上) 任意 自定义编排
扇出至 50+ 并行任务 任意 自定义编排

何时值得编写自定义编排

自定义编排意味着将代理视为分布式系统:持久执行器(Temporal、Inngest、Step Functions 或基于 SQS 的自制方案)驱动工作流,每个 LLM 调用或工具执行都是该系统中的一个任务。当我需要承受机器故障、单个“代理回合”可能需要一小时,或需要扇出到数百个并行分支时,我会使用这种方式。

我为 SLA 合规构建的 serverless AWS + Zendesk 集成就是自定义编排。不是因为它是一个“代理”,而是因为同样的推理适用:EventBridge 驱动工作流,Lambda 函数是任务,DynamoDB 保存状态。如果 Lambda 死亡,EventBridge 会重试。如果工具失败,状态机知道从哪里恢复。这种模式一旦你接受 LLM 调用只是另一个幂等任务,就很干净地映射到代理式工作流上。

自定义编排比框架多提供的好处:

  • 真正的持久性。 LangGraph 的检查点器适用于数小时。Temporal 或 Step Functions 适用于数月。
  • 大规模扇出。 当我需要一个代理并行审核 500 个文档并聚合结果时,我需要一个真正的队列和工作池,而不是试图管理并发的图库。
  • 爆炸半径控制。 每个任务在隔离中运行。有毒的工具响应不会破坏整个运行,因为状态在步骤之间被物化。

成本是真实的。你要编写更多代码,拥有基础设施,并且必须构建代理操作的可视化。对于运行时间不到一小时且并行分支少于 10 个的任何事情,这都是过度设计。超过这个范围,框架开始感觉像玩具。

我实际的选择,逐个决策

以下是我在启动新代理项目时脑中的流程图:

  1. 工作流是否线性且少于五个工具? DIY 循环。一周内交付。添加结构化日志、令牌计数和重试包装器。
  2. 是否有真实分支,或需要暂停以等待人工输入? LangGraph,使用 PostgresSaver 进行检查点,并使用 OpenTelemetry 回调进行可观测性。
  3. 单次运行是否持续数小时,或扇出到数百个并行分支,或需要承受基础设施故障? 在 Temporal 或 Step Functions 上的自定义编排。将每个 LLM 调用视为一个活动。
  4. 三个或更多不同代理相互通信的多代理? 如果运行时间短则用 LangGraph,如果运行时间长则用自定义。永远不要用 DIY,因为代理之间的消息路由是 DIY 循环变成难以维护的意大利面条的地方。

我比框架营销建议的更频繁地倾向于 DIY。野外的大多数代理不需要图。它们需要一个好的循环、仔细的提示工程、紧凑的工具模式和诚实的评估。框架在工作流图不再适合一个屏幕时变得有用。

直到部署后才告诉你的事情

一些让我付出真实时间的教训:

工具模式比模型选择更重要。 我见过同一个任务仅通过重写工具描述和参数名称就从 60% 成功率提升到 95%。在切换模型或添加框架之前,花一天时间优化你的工具模式。模型只能像你的工具允许它那样聪明。

消息数组修剪是令牌节省所在。 在一个 20 步代理中,如果保留所有工具结果,消息数组的令牌数量会二次增长。我保留最后 3-5 个工具结果的原文,并总结其余内容。在一个内容代理上,这将成本降低了 40%,且没有可衡量的质量损失。

错误字符串就是提示。 当工具失败时,错误消息会返回到模型的上下文中,成为下一个提示的一部分。"Database connection failed" 是一个无用的提示。"Database query returned no rows for user_id=42. Try a different user_id or check if the user exists." 让模型能够恢复。

没有幂等性的检查点是一个谎言。 如果你的图从第 7 步恢复,而第 7 步调用了支付 API,你就向客户重复收费了。每个改变状态的工具都需要一个幂等性键。这对任何框架都适用。

评估不是可选的。 我在每次部署前都会将我构建的每个代理针对一组固定的 20-50 个测试用例运行。不是花哨的 LLM-as-judge 东西,只是确定性检查:是否调用了正确的工具,最终答案是否包含必需字段,是否在令牌预算内。这是代理工作中单一 ROI 最高的工程实践。

如果我明天开始一个新代理,我会怎么做

从 DIY 循环开始。首先搞定工具、系统提示和评估集。从第一天起就对令牌使用和步骤计数进行仪表化。部署它,观察它在真实输入上失败,然后修复它。

只有当工作流图有真实分支或需要持久的暂停和恢复时,才使用 LangGraph。只有当单次运行超过一小时或扇出超出单个进程应管理的范围时,才使用自定义编排。不要跳过 DIY 阶段:它会教你你的代理实际在做什么,这种知识会让后来的框架选择变得显而易见。

框架辩论大多是噪音。那些成功部署并保持运行的代理是具有紧凑工具模式、诚实评估以及让你能在 10 分钟内调试生产故障的可观测性的代理。其他一切都是脚手架。

如果你现在正在构建代理并在这些技术栈之间犹豫,我很乐意查看工作流图并告诉你我会选择什么。可以通过 lazar-milicevic.com/#contact 联系我,或阅读我在 博客 上撰写的关于代理评估、上下文工程和生产环境中削减令牌支出的更多内容。