框架选型不重要?扒开LangChain、LangGraph的“外衣”,Agent开发的真相在这里
每隔几天,后台咨询就会有人问:“做 Agent 开发,LangChain、LangGraph 和 LlamaIndex 怎么选?”
我写这篇文章是想说明一件事:其实这个问题本身的重要性被严重高估了。
不是说框架不重要,而是说,当你真正深入 Agent 开发之后,你会意识到框架能帮你的远比你想象的少,而你需要自己解决的远比你想象的多。
先回答你:起步该选什么
既然你来了,我不能光讲道理不给建议。先说结论:
挑你所在语言生态里 GitHub Star 最多的那个。
这个建议听起来很草率,但它背后有一个非常现实的理由:你现在写代码大概率在用 AI 辅助——Cursor、Copilot、Claude,随便哪个。这些工具的代码补全质量跟训练数据量直接挂钩。LangChain 的代码在 GitHub 上被复制了无数遍,AI 模型对它的 api 熟得不能再熟;换成一个 500 Star 的新框架,补全准确率可能直接腰斩。
这在 2025 年是一个实打实的生产力差异。别小看它。
至于具体推荐:
Python → LangChain(通用)、LlamaIndex(偏 RAG 场景)
TypeScript → Vercel AI SDK、LangChain.js
C# / Java → Semantic Kernel / Spring AI
想拖拽不想写代码 → Dify、Coze
选完就动手,不要陷入“再多看两个框架”的循环。因为接下来我要告诉你,这些框架之间的差异远没有它们的相似之处大。
扒开框架的外衣:里面到底是什么
要理解为什么框架选型没那么重要,你得先知道一个 Agent 到底是怎么跑的。
目前绝大多数 Agent——不管它被哪个框架包装——内核都是 react 模式。这个模式的流程非常简单:
while True:
response = llm.chat(messages) # 把历史对话发给大模型
if response.has_tool_call: # 大模型说“我要调个工具”
result = execute_tool(response.tool_call)
messages.append(result) # 把工具结果塞回对话
else:
return response.text # 大模型说“我想好了”,结束对,就这么点东西。一个 while 循环,一次 LLM 调用,一次工具分发。所谓的“AI Agent”,骨架就是这个。
那框架在这上面做了什么呢?主要是三件事:
第一,统一 LLM 的调用接口。 OpenAI、Anthropic、Google、各种开源模型的 API 格式略有差异,框架帮你封装成一个统一的 chat() 或 invoke()。这确实有用——但说实话,现在各家 API 都在向 OpenAI 兼容格式靠拢,这一层封装的必要性在持续下降。
第二,预置一堆工具集成。 搜索引擎、数据库查询、文件读写、各种 SaaS API 的 wrapper,框架帮你写好了。省事,但这也不是什么技术壁垒——就是一些 HTTP 请求加上格式转换的胶水代码。
第三,提供 Agent 运行的脚手架。 循环控制、输出解析、memory 管理、callback 机制……框架把 ReAct 循环包装成了一个看起来更“工程化”的东西。
这三件事加起来,构成了 Agent 框架的全部价值。你可以看出来,这一层非常薄。
为什么薄?因为底下的一切都已经标准化了。LLM API 是标准的(OpenAI 兼容格式),Tool Call 的描述方式是标准的(JSON Schema),向量检索的接口是标准的,连工具的接入协议(MCP)都在快速走向标准化。当地基全是标准件,在上面盖的这层框架也只能是一层薄薄的抹灰。
为什么你迟早会觉得框架“碍手碍脚”
上面说的是框架“薄”。接下来说另一个问题:框架“硬”。
ReAct 的核心循环虽然简单,但在真实场景中让 Agent 跑得好,优化手段却是千变万化的。而这些优化,几乎每一个都要求你突破框架的预设边界。
上下文管理是第一个爆点。
假设你在做一个浏览器操控 Agent。每一步,Agent 都要“看”一张截屏(Base64 编码后几百 KB),拿到页面的 dom 结构(可能几万 token),再结合之前的操作历史来决定下一步。跑三五步,context window 就快满了。
或者你在做一个代码生成 Agent。每次执行完代码,stdout 和 stderr 的输出要反馈给 LLM,可能有几千行。如果执行过程中还产生了新文件,文件内容也可能需要传回去。几轮下来,token 量暴涨。
标准的 ReAct 做法是把所有历史信息原封不动地塞进 messages 列表。框架也是这么实现的——最多给你一个 ConversationBufferMemory(全存)和一个 ConversationSummaryMemory(用 LLM 总结压缩)。
但你真正需要的是什么?是针对你具体场景的精细化策略。比如:
GUI Agent 可能需要保留最近两步的完整截屏,但更早的步骤只保留一句“在搜索框中输入了关键词”的摘要
Coding Agent 可能需要始终保留当前工作文件的完整内容,但丢弃已经成功执行的中间步骤的 stdout
研究型 Agent 可能需要维护一个持续更新的“发现摘要”,而不是保留每次搜索的原始结果
这些策略框架帮不了你,因为它们无法预知你的场景。你必须自己写。
你会想让 Agent “先想再做”。
刚开始用 Agent 时,你会为它能自主调用工具而兴奋。但很快你会发现,面对稍微复杂一点的任务,Agent 经常像个没头苍蝇一样乱撞——重复调用同一个工具、选了一条低效的路径、或者在关键分支上做了一个不可逆的错误决策。
这时候你会开始思考:能不能让 Agent 在动手之前先规划一下?在做完一步之后回头审视一下效果?在发现方向不对时主动调整?
这就是学术界讨论很多的 planning、thinking、self-reflection 机制。它们的共同特点是:需要在 ReAct 循环的特定位置插入额外的 LLM 调用,而这些调用的 prompt、时机、条件判断都需要根据你的场景精心设计。
比如你可能想实现这样的逻辑:
任务开始时,先让 LLM 把大任务拆解成 3-5 个子步骤
每完成一个子步骤,让 LLM 评估“目前的进展是否符合预期”
如果连续两步的 reflection 都显示“偏离计划”,则触发重新规划
如果某个工具连续调用失败超过 2 次,换一种策略而不是继续重试
这些逻辑你用框架的 AgentExecutor 怎么塞进去?答案是非常别扭,甚至根本塞不进去。你最终要么 fork 框架的代码,要么绕过它自己写循环。
错误处理才是真正的工程量。
在 demo 里,Agent 总是顺利地执行每一步。在生产环境中,出错才是常态。
LLM 可能会返回一个格式不合法的 JSON——比如多了一个逗号或者少了一个引号。它可能会“发明”一个不存在的工具名。它可能在应该停止的时候不停止,进入无限循环。工具可能因为外部服务故障而超时。网络可能会抖动导致 API 调用失败。
一个生产级的 Agent 需要在每一个环节都有细致的错误处理:
LLM 输出解析失败?把错误信息反馈给 LLM,让它重新生成
工具调用超时?记录日志,尝试用替代方案完成同样的目标
Agent 在两个工具之间来回跳?检测到循环后强制跳出并尝试不同策略
整个流程已经跑了 20 步还没结束?设置上限,优雅地告知用户并提供中间结果
框架能给你的?max_iterations=15 和 handle_parsing_errors=True。聊胜于无。
当你把这些定制化工作都做完——自定义了 memory 策略、加入了 planning 和 reflection、写了完善的错误处理——你回头看看框架,会发现一个颇为讽刺的事实:
整个框架里,你真正还在用的,就是那个调 LLM API 的函数。
其他所有东西——Agent 循环的控制、上下文的组装、输出的解析、工具的调度——全都被你自己的代码替换掉了。框架变成了一个昂贵的 fetch() wrapper。
MCP 正在让框架更“没有存在感”
说到这里,不得不提一个正在快速改变游戏规则的东西:MCP(Model Context Protocol)。
以前,框架的一个重要卖点是“我预置了 200+ 工具集成”。你要接一个 Google Search,LangChain 有现成的 wrapper;你要查一个数据库,LlamaIndex 有 connector。这些集成虽然不难写,但省时间。
MCP 的出现改变了这个局面。它定义了一个通用协议,让工具的提供方(比如 GitHub、Slack、数据库)直接暴露标准化的接口。任何 Agent——不管用什么框架,甚至不用框架——都可以直接接入。
工具侧也在主动拥抱这个趋势。越来越多的服务直接提供 MCP Server,你几行配置就能接进去,完全不需要框架帮你写 wrapper。
与此同时,LLM 本身的 function calling 能力也越来越强,返回的结构越来越规范。以前你可能需要框架帮你做复杂的输出解析(正则匹配、retry、格式修正),现在模型直接返回结构化的 tool_call 对象,拿来就能用。
这两个趋势叠加的结果是:框架能提供差异化价值的空间正在肉眼可见地缩小。
真正值得你投入时间的事情
与其纠结框架选型,不如把时间花在这些真正决定 Agent 质量的事情上:
Prompt Engineering 是被低估的核心技能。 Agent 的 system prompt 和 tool description 的写法直接决定了 LLM 的决策质量。一个精心设计的 tool description,可以让 LLM 在 90% 的情况下正确选择工具;一个随手写的,可能只有 60%。这个差距不是换框架能弥补的。
Evaluation 是最容易被忽视的环节。 Agent 的行为具有不确定性,同样的输入可能产生不同的执行路径和结果。你需要一套 evaluation 体系来衡量 Agent 在什么条件下表现好、什么条件下会翻车。没有 eval 的 Agent 开发就是在盲人摸象。
上下文工程(Context Engineering)正在取代 Prompt Engineering 成为新的关键词。它关注的是一个更大的问题:在 Agent 的每一步决策中,如何精准地组装出最有利于 LLM 做出正确判断的上下文?哪些信息该放进去,哪些该丢掉,以什么格式组织——这些决策比你选哪个框架重要一百倍。
用户体验设计不可忽略。 Agent 不是对每个任务都能完美完成的。如何让用户理解 Agent 在做什么、如何设置合理的预期、如何在 Agent 失败时优雅地降级——这些产品层面的思考往往比技术实现更难。
分阶段的选型策略
如果你非要一个清晰的行动方案,这是我的建议:
入门期(第 1-2 周):拿框架快速上手。
选最流行的框架,跑通第一个 demo。目标不是做出好产品,而是理解 Agent 的基本工作原理。用框架的好处是它帮你屏蔽了底层细节,让你专注于理解“ReAct 循环”这个核心概念。
进阶期(第 2-4 周):脱离框架理解本质。
自己用纯 API 调用手写一个最小的 Agent。不用任何框架,就用 openai 或 anthropic 的官方 SDK,50 行代码写一个能调工具的 ReAct 循环。这个练习会让你彻底明白框架帮你做了什么、没做什么。
生产期:用框架的方式要利于拆除。
如果你继续用框架,把它当作一个 LLM 调用的便利层来用,不要在它的 Agent 抽象上构建核心逻辑。你的 Agent 循环、memory 管理、错误处理都应该是你自己的代码。这样当框架的某个设计碍事了,你可以随时替换掉它,成本很低。
如果你选择不用框架,直接用官方 SDK + 自己封装的薄层,也完全可行。代码量不会比用框架多太多,但可控性高出几个量级。
最后
框架选型是一个“入口问题”——刚入门时你会觉得它很重要,深入之后你会意识到它只是一个起点。
Agent 开发的真正挑战在于:理解 LLM 的能力边界,设计合理的任务分解策略,构建鲁棒的执行和容错机制,以及在不确定性中找到产品价值。
这些事情,没有任何框架能替你想清楚。
所以,随便选一个,开始动手吧。你在框架选型上每多纠结一天,就少了一天在真正重要的事情上积累经验的时间。Agent 的灵魂不在框架里,在你对问题的理解里。
本文内容仅供个人学习、研究或参考使用,不构成任何形式的决策建议、专业指导或法律依据。未经授权,禁止任何单位或个人以商业售卖、虚假宣传、侵权传播等非学习研究目的使用本文内容。如需分享或转载,请保留原文来源信息,不得篡改、删减内容或侵犯相关权益。感谢您的理解与支持!