近几年被问得最多的问题就是:我想入门 AI Agent,是不是应该先学 LangChain?每次听到这句话我都想拦人——你连 Agent 的“核”还没摸到,就去啃框架,只会越学越晕。这篇就写给两类人:完全没接触过 Agent 的新手,以及想从其他岗位转来做 AI 应用的人。我会直接告诉你什么值得先学、什么真的可以晚点再碰,并给出一条我自己验证过的学习路径。
我不反对框架,甚至现在做项目我也会用框架。但“先学框架”和“先懂原理再学框架”,学习效率完全是两回事。这篇文章不是那种“30分钟搭一个Agent”的速通指南,而是一篇会劝阻你“慢下来”的路径梳理。看完你大致能回答三个问题:Agent 的本质是什么、手写一个最小 Agent 需要哪几步、以及框架到底该在什么时候进入你的学习列表。
1. 先搞清楚:Agent 到底是什么,别把框架当成 Agent 本身
1.1 一句话说清 Agent 的本质
Agent 在 AI 圈被用得太滥了。我的定义很简单:Agent 是一个能自己理解目标、拆解任务、调用工具、检查结果、并在失败后纠正自己的程序。它不是一个产品名字,也不等于“ChatGPT 套壳”,更不是某个框架的代名词。
拆开来说,核心能力就四件事:
- 理解目标:把一段模糊的用户需求,转换成可执行的任务描述。这依赖大模型的理解能力,也依赖你写 Prompt 的水平。
- 拆解任务:把一个大目标拆成多个小步骤。比如“帮我准备一份周末北京出行计划”,可能要拆成查天气、查高铁票、查景点、列行程表。
- 调用工具:Agent 不能只靠模型自身知识,它需要外部能力。查天气要调天气 API,发邮件要调邮件 API,查数据库要写 SQL。这部分就是 Function Calling 或 Tool Calling。
- 检查与纠正:执行结果可能不对,比如天气接口返回 500,或者查询结果不完整。Agent 要能识别异常,换一种方式重试。
你发现没有,这四件事里没有一件是“某个框架特有的”。框架只是把以上过程封装成组件,让你少写样板代码。但封装的代价是,你很可能看不透它内部发生了什么。等出了问题,你连日志都看不懂。
1.2 为什么新手总把 Agent 和框架混为一谈
因为市面上 90% 的 Agent 教程,标题都是“用 LangChain 做一个 PDF 问答机器人”或者“基于 Dify 搭建一个联网搜索助手”。这些教程的共同点是:用少量代码跑通一个 Demo,然后大家对 Agent 的认知就变成了“LangChain 就是 Agent”。
这个印象太牢固了。以至于我做技术分享时,只要说到 Agent,总有人问“那用哪个框架好”。我跟他们说“先别问框架”,他们一脸茫然——不学框架,那 Agent 怎么做出来的?
这其实是“工具思维”和“原理思维”的差别。普通人用电脑不需要懂操作系统,只要会点鼠标就行。但 Agent 开发不一样,因为你遇到的大多数问题都藏在原理层:
- 模型返回 JSON 格式不稳定,是因为你需要强制结构化输出,而不是换个框架。
- Agent 老是重复执行同一个工具,是因为你的循环逻辑缺少终止条件,和框架无关。
- 上下文越来越长导致费用飙升,是因为你没有做上下文压缩,这同样是设计问题。
还有一个现实原因:框架迭代太快。你今天学的 LangChain 0.3 用法,下个月可能就被 0.4 或者被新的 Agent 库取代。但大模型 API 的调用逻辑、Token 计算方式、Prompt 设计原则,两三年都不会大变。把时间花在稳定的事物上,才是新手回报率最高的策略。
1.3 学 Agent 前,必须弄懂的五个底层概念
我把这些概念称为“Agent 的地基”,顺序也是我建议的学习顺序:
| 概念 | 一句话解释 | 常见误区 |
|---|---|---|
| 模型(Model) | 大模型本身,如 GPT、Claude、国产开源模型 | 以为 Agent 是模型之外的魔法 |
| 上下文(Context) | 模型一次能看到的全部内容,含系统提示、历史、工具结果 | 以为上下文越大越好 |
| Token | 模型计量文本的单位,约等于 0.7 个汉字 | 以为只有输入才消耗 Token |
| 工具调用(Function Calling) | 模型输出“要调哪个函数、传什么参数”的结构化指令 | 以为工具调用是框架自动生成的 |
| 循环与状态(Loop & State) | Agent 的多次推理-行动-观察过程 | 以为一次请求就能完成复杂任务 |
你只要把这个表格里的概念都亲手验证过一遍,后面看任何框架都会轻松很多。接下来我就按这个顺序展开说,并给出一套从零手写最小 Agent 的完整过程。
2. 别急着装框架,先把三块地基打牢
2.1 第一块地基:会调大模型 API,看得懂 Token
这一节看上去基础得过分,但我带过的人里,有 30% 卡在“API 调用都跑不通”这个阶段。不是不会写代码,而是不懂怎么跟大模型 API 打交道。
以目前最常见的 OpenAI 兼容接口为例,一个最基本的调用是这样的:
from openai import OpenAI client = OpenAI() resp = client.chat.completions.create( model="gpt-4o", messages=[ {"role": "system", "content": "你是一个旅游助手,回答要简洁。"}, {"role": "user", "content": "推荐一下北京玩三天怎么安排?"} ] ) print(resp.choices[0].message.content)这套代码值得吃透的细节,比看上去多得多:
- messages 结构:system、user、assistant、tool 四种角色,顺序不能乱。Agent 的每一次自我对话,本质上就是往 messages 里追加消息。
- 温度参数:默认 1 或 0.7,影响随机性。做 Agent 时我通常调到 0,因为不希望模型在调用工具时自由发挥。
- Token 消耗:输入和输出都计费。每次带着全部历史记录调用,Token 会随对话轮数线性增长。有个 100 条历史记录的工具调用场景,费用可能比单次问答贵 10 倍。
实操心得:我让每个新手先做一个“Token 计算器”练习。用 tiktoken 库,把一段文本拆成 Token 并统计数量,体会一下“一个汉字大概占 1.5 个 Token”“一段 API 返回结果到底吃了多少钱”。做过这个练习的人,后面设计 Agent 时都会条件反射地控制上下文长度。
2.2 第二块地基:Prompt 工程与结构化输出
很多新手以为 Prompt 就是“把问题说清楚”。Agent 开发里的 Prompt 要复杂得多,因为你是在给一个“会自动执行工具的程序”下达指令,任何模糊都会造成执行偏差。
Agent 的 System Prompt 至少要包含以下要素:
- 角色与目标:你是谁、要完成什么任务。
- 工作流程:先做什么、再做什么、什么情况下调用哪个工具。
- 输出格式:要求 Markdown、JSON,还是表格。
- 边界规则:什么不能做、什么情况下停止。
- 兜底策略:工具出错时怎么反馈,而不是胡编。
结构化输出在 Agent 里尤其关键。因为你要让模型输出可解析的 JSON,而不是“好的,现在我来调用查天气函数”。两种方式都试过之后你就知道,自由文本的可用性有多差。
推荐的做法是使用模型的 JSON Mode 或结构化输出能力:
resp = client.chat.completions.create( model="gpt-4o", messages=[ {"role": "system", "content": "你只输出 JSON,字段为 city 和 date。"}, {"role": "user", "content": "查一下北京明天的天气"} ], response_format={"type": "json_object"} )加了这一层之后,模型的输出就从“废话加结果”变成了干净的 JSON。这一步是后续所有工具调用解析的前提。很多框架内部帮你做了这件事,但如果你自己不知道,出了问题就只会瞎按。
2.3 第三块地基:工具调用与数据流转
工具调用(Function Calling)是整个 Agent 的技术核心。很多人以为 Agent 里的“查天气”是模型自己查的,其实大模型根本不会查任何东西,它只会输出一段“我想调用 get_weather 函数,参数是 {'city': '北京'}”的结构化指令,真正去请求天气 API 的是你的程序代码。
我用一个最简单的例子说明工具调用的完整流程:
import json from openai import OpenAI client = OpenAI() tools = [{ "type": "function", "function": { "name": "get_weather", "description": "查询指定城市的当前天气", "parameters": { "type": "object", "properties": { "city": {"type": "string", "description": "城市名,例如北京"} }, "required": ["city"] } } }] # 1. 告诉模型有哪些工具可用 resp = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "北京今天适合出门吗?先查天气"}], tools=tools ) msg = resp.choices[0].message print(msg.tool_calls)模型会返回类似这样的结果:
[{"id": "call_xxx", "type": "function", "function": {"name": "get_weather", "arguments": "{\"city\":\"北京\"}"}}]注意:此刻模型并没有真的查到天气,它只是表示了查询意图。接下来要由你的代码去执行 get_weather,再把结果作为 tool 消息传回给模型:
# 2. 执行工具函数 city = json.loads(msg.tool_calls[0].function.arguments)["city"] weather = get_weather(city) # 真正的API调用发生在这一步 # 3. 把工具结果传回给模型 messages = [{"role": "user", "content": "北京今天适合出门吗?先查天气"}] messages.append(msg) messages.append({ "role": "tool", "tool_call_id": msg.tool_calls[0].id, "content": weather }) # 4. 模型读取工具结果,生成最终回答 resp2 = client.chat.completions.create( model="gpt-4o", messages=messages, tools=tools ) print(resp2.choices[0].message.content)这段代码就是 Agent 的真身。所谓“Agent 循环”,不过是把上面的 1 到 4 包在一个 while 循环里,反复执行“生成意图 → 调用工具 → 传回结果 → 再生成意图”,直到模型认为任务完成。
看起来不复杂吧?这就是为什么我一直强调先手写一遍。你亲手写完这 20 行代码,再看 LangChain 里的AgentExecutor,会发现它本质上就是个循环管理器,只是帮你处理了消息拼接、异常重试、停止条件这些后勤工作。你已经被剧透了,后面就不可能再被框架的抽象概念吓住。
3. 从零手写一个最小 Agent,比跑通任何 Demo 都有价值
3.1 最小 Agent 的设计思路
所谓最小,是指“能完成一次工具调用并返回最终答案”的完整 Agent,功能再少都没关系。我开始带团队时,给每个新人的第一个任务就是这个,不做任何框架封装,纯用 OpenAI SDK 写。设计思路是:
- 只有一个工具:
get_weather(city),返回固定字符串模拟真实天气接口。 - 循环上限设 5 次,防止 Agent 陷入死循环烧钱。
- 每次循环都打印当前步骤的意图,这样你能直观看到 Agent 在“想什么”。
再加上一个真实项目里必须有但对新手不太友好的点:异常兜底。如果工具函数抛异常,比如城市名是空的,Agent 要把异常信息传给模型,由模型决定是重新调用还是询问用户。这一步能帮你建立“容错设计”的直觉。
3.2 完整实现代码
import json from openai import OpenAI client = OpenAI() TOOLS = [{ "type": "function", "function": { "name": "get_weather", "description": "查询指定城市的当前天气。", "parameters": { "type": "object", "properties": { "city": {"type": "string", "description": "城市名,例如北京"} }, "required": ["city"] } } }] def get_weather(city: str) -> str: """真实项目里这里会去请求天气API,这里用固定值模拟""" if not city: raise ValueError("城市名为空") return f"{city},当前 25℃,多云,东南风3级" def run_agent(user_input: str) -> str: messages = [{"role": "user", "content": user_input}] for step in range(5): print(f"[Step {step + 1}] 调用模型...") resp = client.chat.completions.create( model="gpt-4o", messages=messages, tools=TOOLS, temperature=0 ) msg = resp.choices[0].message messages.append(msg) # 模型没有调用工具,说明任务已完成 if not msg.tool_calls: return msg.content for tool_call in msg.tool_calls: fn = tool_call.function print(f"[Step {step + 1}] 调用工具 {fn.name}, 参数: {fn.arguments}") try: args = json.loads(fn.arguments) result = get_weather(args.get("city", "")) except Exception as e: result = f"工具执行失败,错误信息: {type(e).__name__}: {e}" messages.append({ "role": "tool", "tool_call_id": tool_call.id, "content": result }) return "已达最大运行轮数,Agent 已停止。" if __name__ == "__main__": print(run_agent("北京今天适合出门吗?先帮我查一下天气"))运行之后,你会看到类似下面这样的输出,这是 Agent 工作过程的“X光片”:
[Step 1] 调用模型... [Step 1] 调用工具 get_weather, 参数: {"city": "北京"} [Step 2] 调用模型... 北京,当前 25℃,多云,东南风3级。今天天气较好,适合出门。这个 60 行不到的代码里,已经包含了 Agent 的所有关键部分:模型推理、工具选择、参数解析、工具执行、结果回传、循环控制、异常处理。把它跑通一次,你就拥有了对 Agent 最原始的体感。
3.3 手写版与框架版的差异对照
等你手写完,再打开框架的文档,会发现所有概念都似曾相识。我列一个对照表,方便你理解框架到底做了什么:
| 环节 | 手写版所做 | 框架版的封装方式 |
|---|---|---|
| 消息循环 | 自己写 for/while | AgentExecutor 自动管理 |
| 工具注册 | 手写 JSON Schema | @tool 装饰器自动生成 |
| 参数解析 | json.loads 手动处理 | 框架自动注入参数 |
| 历史管理 | 自己拼接 messages | 自动累加并可选剪裁 |
| 停止条件 | 检测 tool_calls 为空 | max_iterations 参数 |
| 记忆 | 需要自己实现 | 各种 Memory 类 |
这个对照表不是让你觉得框架没用,而是让你看清:框架是替你写模板代码,不是替你写业务逻辑。业务逻辑在你自己脑子里。
我见过不少转行者,简历上写着“熟悉 LangChain、Dify”,但问到“如果 Agent 在调用第三个工具后疯了,怎么办”,答不上来。因为框架的报错信息和文档里根本没有这个答案,你需要懂得底层原理才能推断。而手写过最小 Agent 的人,至少会想到“检查 messages 拼接是否重复、检查循环终止条件、检查有没有把多余的历史传给它”。这些思路,只有亲手踩过坑的人才会有。
4. 框架到底该什么时候学、怎么选
4.1 主流 Agent 框架到底解决什么问题
等你手写过一遍,就可以去学框架了。但学之前,得搞懂它存在的意义。框架解决的痛点,本质上就四个:
- 模板代码复用:不用每次手写循环、消息拼接、工具注册。
- 生态组件丰富:内置各种解析器、记忆组件、向量存储对接。
- 多模型对接:换底座模型时,改配置而不是改代码。
- 可视化编排:像 Dify 这种平台,可以拖拽节点完成流程设计。
但框架也带来新的问题:学习成本高、版本碎片化、抽象层把细节藏得太深。这就是我要反复强调“先手写一遍”的原因——当你已经理解 Agent 的全流程后,框架文档里的术语就不再是障碍,而是你已经认识的东西的“别名”。
4.2 按场景选型:LangChain / Dify / CrewAI 怎么挑
市面上常见的 Agent 相关框架和平台,我挑三个大家公认的典型做横向对比,列完后你大概能知道哪种适合你的目标:
| 维度 | LangChain | Dify | CrewAI |
|---|---|---|---|
| 定位 | 开发框架/工具链 | 低代码平台 + API | 多角色协作框架 |
| 上手门槛 | 高,需要写代码 | 低,拖拽即可 | 中,需要写少量代码 |
| 灵活性 | 很高,适合深度定制 | 中等,适合快速搭建 | 中等,侧重于编排 |
| 典型场景 | 构建复杂 RAG 应用、深度定制 Agent | 快速验证产品原型、企业内部助手 | 模拟多 Agent 协作团队 |
具体建议如下:
- 如果你在做产品验证阶段,只想快速看效果,选 Dify。它能把知识库、工作流、Agent 调试串起来,界面友好。
- 如果目标是进公司做 AI 应用开发,LangChain 还是主流选择,因为它在企业项目里的存量很大,相关岗位的 JD 也经常点名。
- 如果你对多智能体协作感兴趣,比如想做一个“产品经理 + 程序员 + 测试”的虚拟小组,可以看看 CrewAI。
不过我必须提醒一句:框架选型不是非此即彼的关系。我现在的常规项目,经常是 Dify 搭快速原型、LangChain 做复杂链路、二者通过 API 对接到生产系统。这是典型的“组合应用”,但前提是你要理解接缝在哪里——真到了这个地步,其实你已经不需要纠结选型了。
4.3 框架选型的四个判断标准
我帮大家把选型标准提炼成四句口诀,每条都对应踩过的坑:
- 看项目阶段而非流行程度。0 到 1 验证用低代码平台,1 到 100 规模化再用定制框架。一上来就是最重的架构,大概率是给自己找不痛快。
- 看团队水平而非框架热度。团队里全是新手,选 Dify 这类可视化平台,能快速产出;选 LangChain 的复杂抽象,会变成维护噩梦。
- 看生态成熟度而非Star数量。有些框架 Star 多但文档烂。实际用起来,多翻官方文档和 GitHub Issues 的活跃度,比看 Star 数靠谱得多。
- 看是否容易脱离。这是最容易被忽略的一条。用框架时,核心业务逻辑一定要写在独立模块里,别让框架的 API 渗透到每个角落。否则框架一旦升级或者要迁移,你的项目就跟着“陪葬”。
选型本质上是“在约束条件下做取舍”。约束条件没有标准答案,但你必须有清晰的选择依据。
5. 新手和转行者最容易踩的坑,一条条排给你看
5.1 四个典型问题和排查实录
我接触过的实战问题不少,挑了四个新手们几乎必遇到的,并附上排查过程。
问题一:Agent 明明调了工具,最后还是答非所问
- 排查过程:先打印出每次循环的 messages,看看 tool 消息有没有正确拼进历史。最常见的问题是,把 tool 结果塞进了 user 角色,模型没识别为工具执行结果,于是乱答。
- 解决方案:严格按
role: "tool"+tool_call_id拼接。
问题二:JSON 解析时不时报错
- 排查过程:把模型原始输出完整打印出来,发现模型在 JSON 前后加了注释或解释文字。单纯
json.loads时稍微有点杂质就挂了。 - 解决方案:改用模型的结构化输出模式,或者用健壮一点的解析库做
extract_json。
问题三:Agent 在工具调用里死循环,预算烧得飞快
- 排查过程:检查循环停止条件,发现工具结果里缺少能告诉模型“任务已完成”的信号,于是模型每次都以为“还要再查一次”。
- 解决方案:给 Agent 循环加最大轮数硬限制,同时设计工具结果时,加上类似
status: success的字段,帮助模型判断完成状态。
问题四:上下文越来越长,响应变慢且贵
- 排查过程:打印每次请求的 token 用量,发现每次循环都带着全部历史记录。一个 8 轮循环,调用 8 次模型,token 总消耗是首轮请求的 4 倍以上。
- 解决方案:加简单的历史摘要压缩,把早期对话浓缩成摘要再放入 messages,或者设置最大历史条数。这一步能省 30% 到 50% 的费用。
这些问题的共性是什么?它们没有一个和框架的选择有直接关系,全部是 Agent 基础机制层面的东西。你换成哪个框架,问题都会以类似形态出现。
5.2 给新手和转行者的学习路径建议与避坑清单
最后给一条可执行的路径,我在内部带新人时基本就是按这个顺序:
- 第一个月,只学基础调用。做 API 调用,理解 messages、Token、参数。目标:能把一个带工具调用的最小 Agent 手写出来。
- 第二个月,做三个真实小项目。比如桌面端天气助手、一个自动整理周报的小工具、一个基于本地文件的问答机器人。别贪多,关键是每个都要包含至少一次工具调用。
- 第三个月,再接触框架。把一个手写项目用框架重写一遍,比较差异。这时候你对框架的理解,会比直接从框架开始的人深入得多。
避坑清单,几乎是拿真金白银换来的:
- 不要用生产环境去练手。很多人一上手就接公司生产数据库,结果工具参数解析有问题,把客户数据查坏了。先用假数据跑通全流程。
- 不要迷信超长上下文。上下文越长,单次请求越慢越贵,且模型注意力会分散。该精简就精简。
- 不要忽视成本估算。Agent 项目上线后最大的坑是费用。写代码时就要在日志里记录每次调用的 token 数,做到心里有数。
- 不要忽略日志。Agent 的调试比一般程序难,因为每次输出有随机性。所有关键步骤——模型返回、工具选择、工具参数、工具结果——都要打日志,否则等于盲人摸象。
说实话,Agent 本身是个逻辑并不复杂的系统,它难在“看得见全局”和“能容忍不确定输出”。这两个能力只能靠亲手写代码、亲手调试来磨炼,框架帮不了你。我在实际项目里,最怕的不是候选人不熟悉某个框架,而是他只会照着官方示例跑通 Demo,却没有独立排查问题的能力。这篇文章的核心就是帮你建立这种能力——从手写开始,后面的一切都会顺畅很多。