很多新手和转行者来找我聊 AI Agent,第一句话基本都是同一个:LangChain 和 Dify 到底哪个好?CrewAI 是不是更牛一点?我通常都不直接回答,因为这个问题本身就问早了。你连 Agent 的底层循环都没亲手写过,选框架就像还没学会走路就研究该买哪辆跑车。这篇内容我想跟你聊聊:AI Agent 入门到底先学什么、怎么学、学到什么程度才算有底子,以及为什么我强烈不建议你一上来就啃框架。
先说清楚这篇文章适合谁。如果你是非科班转行者,或者刚接触大模型应用开发不久,正被全网铺天盖地的“智能体”“编排”“多Agent协作”搞晕,那这篇就是你该看的。我会尽量避开玄乎的名词,用做应用的角度去拆解 Agent 的骨架,最后还会带一段不依赖任何框架的代码走一遍完整流程。读完你会发现,框架只是工具,而你已经有了判断工具、驾驭工具的能力。
1. 先把概念搞懂:AI Agent 不是“会聊天的机器人”
1.1 什么是 AI Agent,它和聊天机器人差在哪
很多人把 AI Agent 理解成“能对话的机器人”,这是最要命的误区。ChatGPT 那种对话框产品,你问一句它答一句,本质是“单轮问答”的增强版,背后就是一个大模型直接生成文本。而 AI Agent 的核心在于“自主性”和“行动力”。它能够在没有逐步人工指令的情况下,自己规划任务、调用工具、观察结果、调整策略,直至完成一个相对复杂的目标。
我举个通俗的例子。你让一个普通聊天机器人查一下“北京明天适合跑步吗”,它会搜完资料直接告诉你一个答案,甚至可能瞎编一个。但一个 Agent 会这样干:先判断这个问题需要天气数据和空气质量数据,然后调用天气接口拿到温度、降水概率,再调用空气质量接口拿到 PM2.5 数据,最后结合这些数据判断“适合”或“不适合”,并给出依据。整个过程里,它会自己决定调用哪个工具、按什么顺序调用、如何整合信息。
再打一个更生活的比方:聊天机器人是柜台后的客服,你问一句他答一句;AI Agent 是你的私人助理,你交代一个目标,他会自己安排怎么干,干完再跟你汇报。这个“目标-拆解-执行-反馈”的闭环,才是 Agent 的灵魂。
1.2 Agent 的核心循环:感知、决策、行动、反馈
现在我带你拆开这个灵魂。标准的 Agent 工作循环大致有四步:
- 感知(Perception):接收用户请求和当前环境信息。这个环境可能是你输入的文本、传感器数据、系统状态,甚至是上一个工具返回的结果。
- 决策(Decision):大模型根据指令、上下文记忆和可用工具列表,决定下一步做什么。是直接回答,还是需要调用某个工具,或者需要查询某个记忆片段。
- 行动(Action):执行决策结果,比如调用天气 API、执行 SQL 查询、操作浏览器,或者只是生成一段文本。
- 反馈(Feedback):把行动结果交给模型再次感知,判断任务是否已完成。如果没完成,就进入下一轮“决策-行动”循环,直到达到终止条件。
这个循环看起来简单,但里面藏着 Agent 所有复杂性的源头。为什么很多 Agent 会“跑偏”?因为每一步都可能出错:感知阶段可能漏掉关键信息,决策阶段模型可能不理解你的工具描述,行动阶段 API 报错了,反馈阶段模型没识别出结果不合法。你只有自己实现一遍这个循环,才会对这些问题有切肤之痛,也才能真正理解框架里那些抽象概念到底在解决什么。
1.3 一个典型 Agent 的组成部分
如果你想在纸上画一个典型 Agent 的架构图,至少要有这五个模块:
- 核心模型(LLM / 规划器):Agent 的“大脑”,负责语义理解、任务规划、决策判断。没有固定选择,GPT 系、Claude、开源模型都可以。
- 工具集(Tools):Agent 的“手脚”,是模型可以用来触达外部世界的接口集合,包括 API 调用、代码执行器、数据库查询器、浏览器操作器等。关键点在于每个工具都需要给模型提供清晰的“说明书”。
- 记忆系统(Memory):Agent 的“笔记”,分为短期记忆(当前会话上下文)和长期记忆(跨会话存取的历史信息或者向量数据库)。
- 环境接口(Environment):Agent 与外部系统的交互层,比如请求封装、API key 管理、错误处理。
- 编排器(Orchestrator):调度上述模块的执行顺序,维护主循环状态。
理解这些模块是入门的第一道分水岭,因为后面所有学习路径的展开都围绕这五个模块。你去学 LangChain 也好,去读某个开源 Agent 源码也好,最终都会发现它们的本质就是在管理这五样东西。
1.4 主流架构与“心智模型”:先建框架再选框架
我见过太多新手一上来就背概念:ReAct 架构、Plan-and-Execute 架构、Multi-Agent 架构。背完之后依然一脸蒙圈。其实这些架构没有一个是高深莫测的,它们只是“Agent 主循环”的变种。
ReAct 架构是最经典的思路,名字来自“Reasoning + Acting”。它的核心是人类在解决问题时的自然模式:先思考一下怎么做,然后行动一步,观察结果,再思考下一步。每一步行动前的思考文本就是 Agent 的“推理轨迹”。目前大多数单一 Agent 产品都基于这个循环。
Plan-and-Execute 则是“先定计划再干活”:模型先拆解出一份多步计划,然后按计划逐步执行,每一步也可以检查、调整计划。这种架构适合执行路径比较长、目标比较明确的任务,比如“帮我做一份行业分析报告”。
Multi-Agent 架构则是把多个专注不同职能的 Agent 组织在一起,比如有的负责调研、有的负责写作、有的负责质检。这种架构最花哨,但对新手也最容易被带偏,因为核心难点不在框架,而在如何设计“角色分工”和“通信协议”。
我建议你在入门阶段心里装一个最朴素的单个 Agent 主循环就够,其他架构都只是在合适的场景下给这个循环加花活。把地基打牢,才能知道花活什么是好的、什么是过度设计。
2. 新手最容易踩的坑:一上来就啃框架
2.1 为什么框架是“先射箭后画靶”
框架不是不能学,而是它应该出现在你“已经踩过一些坑、能判断它帮你解决的是什么问题”之后,而不是出现在最开始。事实是,几乎每个新手学 LangChain 都会经历同样的痛苦:文档全是概念,代码全是抽象,跑通了 demo 却不知道自己改了什么,报错了也完全不知道去哪查。
为什么?因为框架本质上是对大量工程经验的封装,它的每一个接口、每一个抽象类,背后都是一类真实场景中反复出现过的问题。你不了解这些问题,那么框架对你来说就只是一个布满魔法按钮的黑盒子。你按下去它能响,但响的原理你不知道,组合键怎么按更不知道,于是你被困在最浅层。
把这个类比放大了说:框架像一间精装修的房子,水电网都铺好了。你直接住进去很舒服,但如果某天漏水了,你连水管在哪都不知道,还得砸开墙找。自己先跑一个极简版本,就等于你先在空地上搭了个棚子,通水通电都摸过一遍,这时候再进精装房,每一处设计你都能看得懂为什么。
2.2 框架们的真实分工
很多新手分不清 LangChain、Dify、CrewAI 的区别,以为它们都叫“Agent 框架”,选哪个都行。实际它们的目标对象和场景重心完全不同:
- LangChain:给开发者用的低级别工具链,提供大量可组合的组件,比如模型封装、Prompt 模板、链接(Chain)、记忆接口、工具调用抽象。适合有一定编程基础、想要深度定制自己 Agent 逻辑的人。灵活度最高,学习成本也最高。
- Dify:给产品团队和运营团队用的应用开发平台,偏重“可视化编排”,你可以在界面上拖拽配置工作流、接入知识库、发布 API。适合需要快速上线工具型应用、不太想深入底层代码调试的团队。
- CrewAI:专注于多智能体协作编排,设计理念就是“角色-任务-流程”,适合研究多 Agent 分工协作场景。属于在特定架构方向上的深度封装。
你以为你在选框架,其实是在选“你愿意在哪个抽象层次上工作”。选错的结果就是:你不擅长代码却在 LangChain 里挣扎,或者你想深入做定制却在 Dify 的边界上反复碰壁。
提示:入门阶段不要把时间花在这些框架的对比和选择上。先问自己:抛开框架,我一个人能不能用 200 行 Python 写出一个能跑通的单 Agent?
2.3 什么时候才算“需要框架”了
我个人判断“可以用框架”的信号有三个:
- 你已经手写过一遍 Agent 主循环,知道每个关键环节可能出现什么问题。
- 你的代码里开始出现大量重复样板,比如工具注册、Prompt 拼接、日志管理、外部 API 错误重试,写起来烦且容易出错。
- 你开始需要快速尝试多种模型、多种记忆方案、多种工具组合,手动切换的成本高到无法接受。
这个时候框架对你来说就不再是黑盒子了。你会带着“这个功能我自己写大概要多久”的判断力去读框架文档,效率完全是天壤之别。你也更能理解框架作者为什么做某些设计决策——你可能不同意,但至少你懂他在优化什么问题。
3. 转行者先学什么:一条不绕弯的路线图
3.1 Python 和 JSON 基础:不需要精通,但要会用
AI Agent 开发目前最成熟的语言生态是 Python,这不光是生态问题,更是 AI 领域几乎所有工具都优先提供 Python SDK。转行者不需要成为 Python 高手,但下面这些能力必须过关:
- 基础语法:变量、循环、条件、函数定义。
- 数据结构:字典(dict)和列表(list)的操作要非常熟练,因为和模型交互的数据基本都是这两个结构。
- JSON 读写:
json.dumps、json.loads、结构嵌套、键值访问。 - 异步请求和异常处理:能看懂
async/await基础调用,能处理try/except。
有一个练习方法非常有效:你去找一个公开的 API(比如天气、汇率、图书搜索),用 Python 请求它,把返回结果解析成 JSON,提取需要字段,再封装成函数。这一步能同时锻炼请求封装、JSON 解析和函数设计三个能力,而这三样就是 Agent 工具开发的核心基本功。
3.2 Prompt 工程:Agent 的上限一半在这里
很多人有一个误解,觉得 Prompt 就是“哄模型”的话术。其实在 Agent 里,Prompt 是系统指令(System Prompt),它决定模型的行为边界、决策方式和输出格式,是 Agent 里最值得反复打磨的部分。
我建议初学者把精力重点放在三块:
第一,角色与目标设定要清晰。你希望模型“严谨的研究助理”还是“简洁的执行者”?同一个底层模型,设置不同角色和语气,输出风格可能差别很大。
第二,上下文和约束条件要显式写清楚。比如“你只能使用我提供的工具”“如果信息不足,直接说实话不要猜”。Agent 最常见的失控原因之一就是约束没写死。
第三,输出格式要求要明确。很多 Agent 主循环依赖模型输出结构化的 JSON(工具调用的指令就是这个格式),Prompt 里必须说明:“请输出 JSON,字段包括 action 和 action_input,不要有其他内容。”这一步做不好,后面解析必然翻车。
你在网上海量模板里找不到通吃的 Prompt,真正好用的 Prompt 是在你自己的任务场景里反复试错、迭代出来的。这也是我鼓励新手先裸写一个 Agent 的原因:你会在调试中亲眼看到一句 Prompt 的改变如何影响整个任务成败。
3.3 LLM API 调用:从 Chat Completion 到 Stream
Prompt 是大脑的输入,API 调用就是大脑的神经通路。我不会在博文里贴某个具体平台的 SDK 代码,因为模型厂商更新太快,但核心逻辑是通用的:
- 请求参数:
model指定模型,messages传入对话列表,temperature控制随机性,tools传入工具定义(后文详述),response_format可以要求 JSON 模式。 - 返回结构:通常包含
content(正文)和tool_calls(工具调用指令)两类字段,后者是 Agent 行动的关键触发点。 - 流式输出:
stream=True时你会收到一个生成器,需要逐步拼接并实时消费。Agent 产品里常用,因为体验更好。
还有计费问题也要提前心知肚明:你每轮对话传出的消息列表是累计的,Token 越滚越多,费用也会非线性增长。我见过很多新手把整个历史全塞进上下文,结果第三轮就超了模型窗口,第四轮账单开始让人心疼。这就引出了记忆管理的需求,后面会细说。
3.4 工具调用(Function Calling):Agent 长出“手脚”
在传统 LLM API 里,模型只输出文本,它没法直接操作外部世界。OpenAI 最早推出 Function Calling 机制之后,这个局面彻底改变:你可以给模型提供一组“工具描述”,模型在回答中生成一个结构化的“调用指令”,然后你的代码负责执行这个指令,再把结果回传。
这个机制是 Agent 的核心引擎,也是我强烈建议新手优先掌握的技能。所谓“工具描述”,本质上就是把函数说明书翻译成模型能读懂的 JSON Schema。比如你有一个get_weather(location: str, date: str)函数,你需要告诉模型:
- 这个工具是干什么的;
- 参数有哪些,各是什么类型;
- 哪些参数必填,哪些可选;
- 工具调用后返回什么结构。
模型会基于描述和当前对话决定“要不要调用、传什么参数”,但真正执行函数的是你的代码,这一点要搞清楚:模型不会偷跑你的代码,它只是生成指令,指令的执行权始终握在应用手里。
自建 Agent 时你要注意别把工具描述写得太模糊或太啰嗦。太模糊,模型不知道该在什么时候调用;太啰嗦,Token 占用大,还干扰模型判断。我的经验是“一句话说明用途 + 参数说明 + 一个示例用法”是最佳配方。
3.5 记忆与 RAG:让 Agent 有上下文
Agent 一旦多轮对话,记忆问题立刻浮现。模型上下文窗口有上限,你不可能永远往里面塞全部历史。这时候你需要分层记忆策略:
短期记忆(会话内):维护一个消息队列,把早期对话“压缩总结”或“裁剪”,只保留最近几轮完整内容。最简单的方式是设定一个窗口长度,超了就丢弃,或者调用一次模型生成摘要。
长期记忆(跨会话):把重要信息存入外部存储,比如让 Agent 在关键节点“主动记忆”用户偏好。简单实现可以用 SQLite 或 JSON 文件,进阶方案是向量数据库 + RAG。
RAG(检索增强生成)是当前让 Agent“懂知识”的主流方案:你先把文档切块、向量化存入数据库,用户提问时把问题向量化,检索相似度最高的几个片段,连同原问题一起喂给模型。这个思路比让模型“背诵”文档靠谱得多,省钱且更新及时,因为知识是实时检索的。
注意:入门阶段不要一上来就搞复杂的向量数据库和 RAG 管线。先从最简单的“会话历史窗口 + 手动摘要”开始,跑顺了再加检索,否则问题排查范围会瞬间膨胀。
3.6 一点后端基础:Agent 不是单机程序
很多人忽略了这个维度。Agent 多数情况下不是本地脚本,它要作为服务提供给前端、自动化流程或别的 Agent 调用,所以你至少需要一点后端基本功:
- 会用 FastAPI 提供一个 HTTP 接口,接收请求、调用 Agent、返回结果;
- 了解基础请求验证、超时设置、异常返回;
- 知道怎么给 Agent 挂一个简单前端或者接入剪贴板/自动化工具。
你不用成为一个架构师,但“Agent 要能被调用”这个思路必须建立。很多 Demo 级项目跑通之后,卡在“怎么给团队其他人用”上,本质上就是缺了这堂课。
4. 实操:手写一个最小可用的 Agent(不用任何框架)
这一节我想带你亲手走一遍完整流程。目标是构建一个能查天气、同时设定提醒的助手。我会刻意不依赖任何 Agent 框架,只用 SDK 的底层 API,让你看清主循环的肌肉线条。
4.1 需求定义:做一个能查天气、设提醒的助手
首先明确行为和边界:
- 用户提问,Agent 判断是否需要调用工具。
- 如果是天气问题,调用
get_weather工具,拿回数据后整理成回答。 - 如果是提醒问题,调用
set_reminder工具,存储提醒信息。 - 如果两个问题混在一起,Agent 应该依次调用多个工具。
- 如果用户的请求 Agent 无法用现有工具完成,它应该直接说明,而不是瞎编。
这个边界很典型,覆盖了“单工具、多工具、拒绝回答”三种 Agent 核心行为。我强烈建议你第一次写 Agent 时也是这样一个 3 工具左右的最小集合,不要贪多。
4.2 定义工具(让 LLM 知道你有什么能力)
工具定义的本质是给模型看的参数文档。以天气查询为例,完整定义如下(伪代码风格,具体实现看模型 SDK):
{ "type": "function", "function": { "name": "get_weather", "description": "查询某个城市某一天的天气情况,返回气温、降水概率和天气状况。", "parameters": { "type": "object", "properties": { "location": { "type": "string", "description": "城市名,例如:北京、上海" }, "date": { "type": "string", "description": "日期,格式为 YYYY-MM-DD,默认今天" } }, "required": ["location"] } } }再看提醒工具:
{ "type": "function", "function": { "name": "set_reminder", "description": "设定一个提醒事项,需要提供时间和提醒内容。", "parameters": { "type": "object", "properties": { "time": { "type": "string", "description": "提醒时间,格式为 YYYY-MM-DD HH:MM" }, "content": { "type": "string", "description": "提醒的具体内容" } }, "required": ["time", "content"] } } }这里有两个写描述的心得。第一个,描述里要包含“什么时候用”而不只是“干什么”,比如提醒工具写明“用户提到提醒、闹钟、到时叫我时调用”,模型就不会在无关请求里误触。第二个,参数描述里写明格式和示例,模型返回参数时更容易符合预期,能明显减少解析阶段的类型错误。
4.3 Agent 主循环:解析意图、执行工具、生成回答
接下来是主循环。整个流程我在本地用 Python 写过,代码结构大致如下:
def run_agent(user_message, history): messages = history + [{"role":"user","content":user_message}] while True: # 1. 带着消息和工具定义请求模型 response = model_call( messages=messages, tools=[weather_tool_def, reminder_tool_def] ) assistant_msg = response["message"] # 2. 判断模型是否要求调用工具 if not assistant_msg.get("tool_calls"): # 没有工具调用,说明 Agent 认为可以收尾了 messages.append(assistant_msg) return assistant_msg["content"], messages # 3. 有工具调用,先把模型回复追加进历史 messages.append(assistant_msg) # 4. 逐个执行工具,并把结果回传给模型 for tool_call in assistant_msg["tool_calls"]: tool_name = tool_call["function"]["name"] tool_args = json.loads(tool_call["function"]["arguments"]) if tool_name == "get_weather": result = get_weather(**tool_args) elif tool_name == "set_reminder": result = set_reminder(**tool_args) else: result = {"error": f"unknown tool {tool_name}"} # 工具结果按标准格式追加入 messages messages.append({ "role": "tool", "tool_call_id": tool_call["id"], "content": json.dumps(result, ensure_ascii=False) }) # 5. 带着工具结果再次请求模型,继续循环这段代码只有二十来行,但只要你完整跑通一次,你会对 Agent 有质的理解。此处有几个细节值得叮咛:
tool_call_id必须和模型返回的 id 严格对应,否则历史消息会报错,这是新手最容易踩的坑之一。- 工具结果一律要把 Python 对象转成 JSON 字符串再回传,保证模型侧拿到的是清晰文本。
- 如果一次返回多个 tool_calls,必须循环全部执行完,再把所有结果一次性回传。
4.4 完整代码与运行效果
我把真实可用的 Python 实现缩略版完整贴出来,你去掉注释后大约 100 行出头,没有任何框架依赖,仅仅使用模型 SDK 和标准库:
import json from datetime import datetime def get_weather(location, date=None): # 实际项目里这里调用真实天气 API,比如和风天气、OpenWeatherMap # 这里用模拟数据演示主流程 return { "location": location, "date": date or str(datetime.now().date()), "temp_high": 18, "temp_low": 6, "pop": 20, "condition": "多云" } def set_reminder(time, content): # 实际项目里这里写入数据库或定时任务队列 # 演示就直接打印日志 return {"time": time, "content": content, "status": "scheduled"} def run_agent(user_message, history): # history 需包含 role/content,可自己维护一个 list messages = history + [{"role":"user","content":user_message}] tool_defs = [ { "type": "function", "function": { "name": "get_weather", "description": "查询某个城市某一天的天气情况,返回气温、降水概率和天气状况。", "parameters": { "type": "object", "properties": { "location": {"type": "string", "description": "城市名,例如北京上海"}, "date": {"type": "string", "description": "日期,格式 YYYY-MM-DD,默认今天"} }, "required": ["location"] } } }, { "type": "function", "function": { "name": "set_reminder", "description": "设定一个提醒事项,需提供时间和内容。", "parameters": { "type": "object", "properties": { "time": {"type": "string", "description": "提醒时间,格式 YYYY-MM-DD HH:MM"}, "content": {"type": "string", "description": "提醒具体内容"} }, "required": ["time", "content"] } } } ] max_rounds = 5 # 防御死循环 for _ in range(max_rounds): response = model_call(messages=messages, tools=tool_defs) assistant_msg = response["message"] messages.append(assistant_msg) tool_calls = assistant_msg.get("tool_calls") if not tool_calls: return assistant_msg["content"], messages for tc in tool_calls: fn = tc["function"]["name"] args = json.loads(tc["function"]["arguments"]) if fn == "get_weather": result = get_weather(**args) elif fn == "set_reminder": result = set_reminder(**args) else: result = {"error": f"unknown tool: {fn}"} messages.append({ "role": "tool", "tool_call_id": tc["id"], "content": json.dumps(result, ensure_ascii=False) }) return "执行轮次过多,已自动停止。", messages这段代码跑起来后,你分别输入几个测试请求,会看到完全不同的分支路径:
- 输入“北京今天天气怎么样”,Agent 只调用
get_weather一次就结束。 - 输入“明天早上提醒我买牛奶,顺便看看上海天气”,Agent 会依次调用
set_reminder和get_weather,顺序还可能因模型策略不同而变化。 - 输入“帮我写一首诗”,Agent 没有可用的创作工具,它会直接给出文本回答,不调用任何工具。
这就是 Agent 主循环的全部魔法:你在跟模型进行带有“工具执行反馈”的多轮对话,而模型在其中扮演决策者的角色。
4.5 这段代码背后的三个设计教训
第一,循环终结条件必须写下限。真实项目里模型可能陷入反复调用同一工具的死循环(比如一直查天气不知道收尾),所以我设置max_rounds做保险丝,并在超限时直接终止回复。很多新手把简洁的 while True 当真做,上线后被奇奇怪怪的循环打爆账单。
第二,工具函数必须健壮。工具是 Agent 暴露给模型触发的任意函数,所以模型传参可能千奇百怪。你必须在工具入口做参数校验、异常捕获,否则一个报错就会中断整个循环。实际生产中我坚持一个原则:工具永远是“可控的、幂等的、有日志的”。
第三,结果是回传文本,不是内存句柄。模型看不到你的 Python 变量,它只通过字符串感知工具结果。所有工具返回都该是结构清晰、字段明确的 JSON,而不是打印一堆调试日志。这决定了模型下一轮判断的质量。
5. 常见问题与排查技巧实录
这部分我积累了很长时间,因为 Agent 开发的问题排查和普通后端调试逻辑完全不一样,你面对的是一个概率型输出的大脑,传统“读堆栈找原因”在这里经常失灵。
5.1 问题:LLM 不按照 JSON 格式返回
这是新手最常遇到的第一道坎。你要求模型输出{"action": "get_weather", "args": {...}},它给你裹一层 Markdown 代码块或者附带解释文字,导致json.loads直接炸掉。
我试过三种有效办法。最优先的是利用模型接口自带的 JSON 输出模式(如response_format={"type": "json_object"}),多数情况下能彻底解决。其次是在 Prompt 里给出一个极简示例而不是抽象描述,比如明确写“参考格式:{"action":"get_weather","args":{"location":"北京"}}”,效果立竿见影。最后是写一个容错解析函数,用正则提取第一个{和最后一个}之间的内容再尝试解析,这是兜底方案。
别轻视这个问题,现实里模型输出的格式错误率在 2%-10% 波动,一次失败就可能导致整个 Agent 任务中断。调试友好的 Agent 项目都会把“解析失败重试一次 + 把错误原样回传给模型修正”作为内置策略。
5.2 问题:工具调用结果出问题,Agent 还在瞎编
典型场景:get_weather接口因为网络原因返回了空数据,但 Agent 回答时却“脑补”了一个晴天出来。这个问题的根源是模型不知道接口失败,而你传回的内容缺少明确的“失败信号”。
我的做法有两个。第一,工具侧强制规范化返回结构:成功时返回{"status":"success","data":...},失败时返回{"status":"error","message":"无法获取数据:xxx"},让结果对模型更可读。第二,在系统 Prompt 里显式声明一条铁律:“如果工具返回 error 或关键字段缺失,你必须告诉用户获取失败,不得推测或编造数据。”
你甚至可以做一层“结果守卫”,在工具返回后先代码判断是否合法,不合法时直接构造一条错误信息回传模型,而不是把原始脏数据喂给它。这套机制虽然简单,却能把 Agent 的胡说八道率压低一个量级。
5.3 问题:上下文越拖越长,Token 爆炸
Agent 每执行一轮工具调用,消息列表就会多出几条消息,多轮任务轻松破万 token。很多人做到一半才发现窗口超限,要么报错,要么费用失控。
我通常采用三层策略。第一层是“窗口滑动”:只保留最近 N 轮对话和工具结果,更早历史单独做摘要。第二层是“结果压缩”:工具返回数据如果很长,比如搜索结果列表,先让模型或脚本压缩成摘要再回传,而不是原样全量传。第三层是“按需截断”:系统指令里告诉模型“只保留完成任务必要的信息”,工具侧也尽量只返回重要字段。
费用上我还会在开发阶段把模型切换到便宜的轻量版本,等逻辑稳定了再换更强模型。很多团队在这块毫不节制,等账单来的时候才意识到“一个查询任务烧掉了几百轮 Token”。
5.4 问题:调试困难,看不到 Agent 在哪一步出错
Agent 的每一步都是自然语言和结构化数据交错,普通 print 打出来的日志毫无可读性。我的排查标配是“一个持久化的事件追踪器”:把每次用户消息、模型回复、工具调用、工具结果、耗时、Token 数都记录成结构化的日志,存成 JSON 文件或数据库。
只有记录到下面这些字段,你才能回答“Agent 为什么在这里犯了错”:
- 时间戳和会话 id;
- 当前请求消息列表(至少记录数量和累计 token);
- 模型返回的完整对象;
- 实际执行了哪个工具、传入的参数;
- 工具执行结果和状态;
- 这一轮的耗时。
这样一旦复现问题,你可以像回放录像一样把 Agent 每一步决策轨迹拉出来看。很多云平台也提供 tracing 功能,但自己写一个小记录器是理解 Agent 运行机制最好的练习之一,成本也不高。
5.5 常用排查工具与心法
聊几个我个人常用的组合拳。第一个是“最小复现”——把出问题的输入固定住,反复跑同一个 case,观察模型输出的稳定性。模型是概率性的,一次出错可能是偶然,三次出错那就一定是 Prompt 或工具描述的问题。
第二个是“逐步冻结”——先移除所有工具,只测纯对话是否正常;再加第一个工具,跑通;再加第二个工具。这样排查范围被收得很小。新手最容易犯的错就是一次加了五个工具,出问题后完全不知道是哪个工具的描述误导了模型。
第三个心法比较玄但很实用:不要在模型侧找答案,先在输入侧找答案。模型表现莫名其妙,90% 是因为工具描述混乱、系统指令前后矛盾或历史消息格式有问题。把输入整理干净,输出自然稳定,这比更换模型参数有效得多。
6. 框架学习建议:什么时候学、怎么学
6.1 先裸写一个再学框架,才是正路
如果你已经老老实实跟着第四部分的代码跑通了一个 Agent,此刻你再去看 LangChain 的 Agent 模块,你会发现一切豁然开朗:那个AgentExecutor不就是你手动写的主循环吗?那个Tool类不就是工具定义的 Python 封装吗?Memory模块不就是你处理历史消息的那堆逻辑吗?
这个“恍然大悟”的时刻极其宝贵。它意味着框架在你眼里不再是魔法,而是一组顺手的好工具。我记得自己第一次从裸写切换到框架时,原来 100 行代码缩到了 30 行,且异常处理和日志管理更标准了。但这不是因为框架聪明,而是因为它把我踩过的坑都提前填上了。
6.2 LangChain、Dify、CrewAI 选型参考
当你有了判断能力后,选型就不再纠结。我通常给这个建议:
如果你是一个开发者,且希望深度控制 Agent 的行为,优先学 LangChain。尽管它的接口更新频繁、学习曲线陡峭,但它依然是社区生态最丰富、扩展点最完整的框架。入口先看 LCEL(LangChain Expression Language)和 Tool/Agent 模块,别一上来碰太抽象的高级功能。
如果你是产品经理、运营或想快速验证应用,优先用 Dify。它帮你省去的开发时间非常可观,尤其在知识库接入和 API 发布这块做得相当顺。代价是定制空间受限,遇到平台的边界能力时你可能需要绕路。
如果你对多 Agent 协作感兴趣,可以在前两者之后看 CrewAI。它的角色编排风格很直观,帮助你快速构建小团队式的多 Agent 协作,但真正生产级别仍然需要大量工程补充。
我说句实在话:框架选型不是一锤子买卖。项目越来越复杂后,你很可能混合使用:用 Dify 做应用入口和知识库管理,用 LangChain 做底层 Pipeline,用 CrewAI 做特定场景的多角色流程。它们不是敌人,是互补工具。
6.3 学习框架的正确姿势
最后给你几条从实际经验里沉淀的学习建议:
第一条,先读官方文档的“核心概念”部分,不要一头扎进 API 参考。花半小时理解它帮你抽象了哪几层,比盲刷十个教程有用得多。
第二条,改代码优于抄代码。任何 Demo 你必须改一下:换一个工具、改一段 Prompt、增加一个记忆策略,然后观察变化。框架提供的模板只是为了证明“可能性”,你真正的功夫在于改造它适应自己的场景。
第三条,不要追新版本。Agent 框架迭代速度很疯狂,你学的最新接口可能三个月后就变了。学那些稳定的底层概念(模型抽象、工具抽象、记忆抽象、编排模式),而不是背某个具体版本的类名。
第四条,给自己定一个小小的实战目标。比如做一个“能查论文并自动总结的 Agent”或者“能管理日程的 Agent”。在目标任务驱动下,框架学习会自然进行,你不会感到自己在“学习一个库”,而是在“解决问题”。
写完这些,我依然坚持文章开头那个观点。AI Agent 的门槛不在框架,而在你是否理解“感知-决策-行动-反馈”这个闭环本身。新手真正要先学的,也是最值得学习的东西,是那个几十行代码的最小循环。等你亲手踩过解析失败的坑、见过模型瞎编的离谱答案、体会到 Token 爆炸的心痛,那时候再点开 LangChain 的文档,世界会突然变得非常清楚。
我个人在实际中的体会是:AI Agent 领域现在的知识更新快得吓人,但 Agent 的骨架、工具调用、记忆管理的底层思路,跟五年前研究“机器人控制”“任务规划”时的逻辑仍然一脉相承。框架会过时,模型会升级,但你对问题本质的理解不会贬值。把基础打好,剩下的都是时间问题。