
1. 先搞清楚“从零构建 Agent”到底在构建什么很多人一上来就问“学 Agent 该先看哪个框架”这个问题本身就问反了。我见过太多人花两周把 LangChain 的文档翻了一遍能跑通几个 Demo但一旦要自己设计一个能稳定处理多轮任务的东西立刻卡住——因为他根本不清楚 Agent 和普通 LLM 应用的区别在哪里。先把概念钉死。Agent 的本质是“让模型在一个循环里自主决定下一步做什么”。普通调用大模型是“你问一句它答一句”一问一答就结束了。Agent 不一样你给它一个目标它自己判断现在该调用哪个工具、拿到结果后判断够不够、不够就继续调、够了就输出。这个“判断—行动—观察—再判断”的循环才是 Agent 的骨架。所以从零构建 Agent你真正要学的是四件事模型调用怎么把请求发给模型、怎么拿到结构化输出、怎么处理流式和错误。工具调用怎么让模型知道有哪些工具可用、怎么解析它想调哪个、怎么把结果喂回去。会话与记忆多轮对话怎么存、上下文超长了怎么压缩、会话断了怎么恢复。编排与容错循环什么时候停、工具报错了怎么办、怎么防止它无限绕圈。这四件事对应了热搜词里反复出现的“模型调用、工具调用、会话恢复、上下文压缩”。注意这四个词不是并列的知识点而是有依赖顺序的。你得先能稳定调模型才谈得上让它调工具先能调工具才谈得上多轮会话先有多轮会话才谈得上上下文压缩和恢复。顺序错了学起来就是一团乱麻。我个人的建议是不要从框架开始学从裸调 API 开始学。框架LangChain、Dify、CrewAI 这些是帮你省事的但它们把太多细节藏起来了。你如果不知道底下发生了什么出了问题连日志都看不懂。先用最原始的方式手写一个能跑的最小循环哪怕只有五十行代码你对 Agent 的理解会比跑通十个框架 Demo 都深。下面这张表是我总结的学习顺序和每阶段的判断标准你可以对照着看自己走到哪一步了阶段核心任务过关标准第一阶段裸调模型 API能拿到稳定结构化输出能处理超时和报错第二阶段手写工具调用循环模型能正确选工具结果能回传循环能终止第三阶段加入会话与记忆多轮对话不丢上下文会话能存能取第四阶段上下文压缩与恢复长对话不爆 token中断后能接着跑第五阶段编排、容错与安全能处理工具失败、死循环、越权调用这张表不是让你按部就班走完才做项目而是告诉你每个阶段该关注什么。很多人卡在第二阶段就急着上框架结果框架一报错就懵了。2. 模型调用Agent 的地基也是最容易被轻视的一环2.1 为什么“会调 API”和“调得稳”是两回事调模型 API 看起来是最简单的事——发个请求拿个回复。但 Agent 场景下模型调用有几个普通聊天场景不会遇到的坑。第一个坑是结构化输出。Agent 需要模型返回的不是一段自然语言而是“我要调用哪个工具、参数是什么”这种机器能解析的东西。如果你让模型自由发挥它可能这次返回 JSON下次返回一段带解释的文字你的解析代码直接崩。解决办法是用模型提供的结构化输出能力比如 function calling 或者 JSON mode而不是靠 prompt 里写“请返回 JSON”然后自己正则去抠。第二个坑是错误处理。热搜词里有个很典型的报错“调用 pb 模型错误请切换模型或重试。400”。这种 400 错误通常是请求格式不对或者参数超限。Agent 循环里如果不对这类错误做处理一次失败整个流程就断了。我的做法是给模型调用包一层重试逻辑区分“可重试错误”超时、限流和“不可重试错误”参数错误、鉴权失败可重试的退避重试不可重试的直接抛出并记录。第三个坑是流式与非流式的选择。Agent 内部循环建议用非流式因为你需要拿到完整结果才能解析工具调用但最终给用户的输出可以用流式体验更好。这个区分很多人一开始没意识到导致解析逻辑和展示逻辑搅在一起。2.2 手写一个最小模型调用封装不要小看这一步。我给你一个我实际用的最小封装思路不依赖任何框架import time import json def call_model(messages, toolsNone, max_retries3): for attempt in range(max_retries): try: response client.chat.completions.create( modelyour-model, messagesmessages, toolstools, temperature0 ) return response.choices[0].message except RateLimitError: time.sleep(2 ** attempt) except BadRequestError as e: # 参数错误重试没用直接抛 raise e raise Exception(模型调用重试次数耗尽)这段代码的关键点在于temperature 设成 0。Agent 场景下你需要的是确定性不是创造力。同一个输入最好每次都能选出同样的工具否则调试起来你会疯掉。另外tools参数是可选的没有工具时就是普通对话有工具时模型会返回工具调用请求。提示不同厂商的模型对 function calling 的支持程度差别很大。有的模型返回的工具调用格式规范有的需要你在 prompt 里额外引导。选模型时一定要先测它的工具调用能力别等搭完整个 Agent 才发现模型不听话。2.3 模型选型的现实考量热搜词里出现了“workbuddy 调用 deepseek-coder-v2:16b 错误”“为什么 chatgpt 调用 deepseek-v4-flash 模型可以进行代码调整无法实现其他任务执行”这类问题。这些本质上都是模型能力与任务不匹配。我的经验是Agent 的主循环模型和专用任务模型要分开选。主循环需要一个指令遵循能力强、工具调用稳定的模型不一定是最贵的但一定要“听话”。而某些特定任务比如代码生成、分类判断可以用更小更专的模型。热搜词里提到的“agent 中的分类模型”就是这个思路——用一个便宜的小模型做意图分类把复杂任务路由给大模型。具体怎么选我一般按这个顺序试先拿一个中等规模的通用模型跑主循环如果工具调用经常出错换更强的如果成本太高试试能不能把部分判断逻辑拆给小模型。这个调优过程没有标准答案得拿你自己的任务去测。3. 工具调用让 Agent 真正“能做事”的关键一跃3.1 工具调用的完整链路拆解工具调用不是“模型说调就调”这么简单它是一条完整的链路你在请求里把可用工具的描述名称、功能、参数 schema传给模型。模型判断需要调工具返回一个工具调用请求包含工具名和参数。你的代码解析这个请求找到对应的函数执行。把执行结果作为一条新消息追加到对话里再次调用模型。模型看到结果决定是继续调工具还是给出最终回答。这条链路里第 3 步是最容易出问题的。模型给的参数可能缺字段、类型不对、甚至编造一个不存在的工具名。你必须做参数校验不能直接拿去执行。def execute_tool(tool_call, available_tools): name tool_call.function.name if name not in available_tools: return {error: f未知工具: {name}} try: args json.loads(tool_call.function.arguments) except json.JSONDecodeError: return {error: 参数不是合法 JSON} # 校验必填字段 required available_tools[name][required] missing [f for f in required if f not in args] if missing: return {error: f缺少必填参数: {missing}} return available_tools[name][func](**args)注意这里返回错误时我是把错误信息作为工具结果喂回给模型的而不是直接抛异常。这样模型有机会自己纠正——比如它发现参数错了会重新调一次。这比直接崩掉友好得多。3.2 工具描述怎么写模型才用得对工具调用失败的一大半原因不是模型笨是工具描述写得烂。我见过有人把工具描述写成“查询数据”模型根本不知道查什么数据、参数怎么填。好的工具描述要包含三要素做什么、什么时候用、参数含义。举个例子{ name: search_orders, description: 根据用户手机号查询订单列表。当用户询问订单状态、物流信息时使用此工具。, parameters: { phone: { type: string, description: 用户手机号11位数字 }, status: { type: string, enum: [pending, shipped, completed], description: 订单状态筛选不传则返回全部 } } }对比一下“查询订单”这种描述差别一目了然。描述里写清楚“什么时候用”能大幅减少模型该调不调、不该调乱调的情况。3.3 工具数量多了怎么办当你有几十个工具时全塞进请求里会撑爆上下文模型也容易选错。这时候要做工具分组或者动态筛选。常见做法是先用一个分类步骤判断用户意图属于哪个领域只把该领域的工具传给模型。热搜词里的“agent 中的分类模型”说的就是这个。我实测下来单个请求里工具数量控制在 10 个以内模型的选择准确率明显更高。超过 20 个错误率会陡增。所以工具多了不是堆上去就行得设计路由层。4. 会话恢复与上下文压缩Agent 能不能“记住事”的分水岭4.1 会话状态到底该存什么很多人以为会话就是存个消息列表其实不够。一个完整的 Agent 会话状态至少包含消息历史用户说了什么、模型回了什么、工具调用了什么。中间状态当前循环走到哪一步了、还有哪些待办。外部引用如果工具返回的是大数据比如一个文件 ID消息里只存引用不存全量内容。为什么要存中间状态因为 Agent 可能跑很久中途进程重启或者用户刷新页面你得能从断点接着跑。热搜词里的“会话恢复”就是这个需求。如果只存消息历史恢复后你不知道上次循环停在哪可能重复执行已经做过的操作。我的做法是给每个会话一个状态对象消息历史只是其中一部分。状态对象定期持久化恢复时整体加载。4.2 上下文压缩的几种实用策略上下文压缩是 Agent 绕不开的问题。对话轮次一多token 就爆了。热搜词里“上下文压缩”被单独列出来说明这是大家的共同痛点。我试过几种策略各有适用场景策略一滑动窗口。只保留最近 N 轮对话更早的丢掉。简单粗暴但会丢失早期的重要信息。适合任务短、上下文关联弱的场景。策略二摘要压缩。把早期对话让模型总结成一段摘要用摘要替代原始消息。这样能保留关键信息又省 token。缺点是摘要本身要调一次模型有额外成本而且摘要可能丢细节。策略三分层记忆。把信息分成“当前任务上下文”和“长期记忆”两层。当前任务相关的放上下文里任务完成后归档到长期记忆需要时再检索出来。这个最复杂但效果最好适合长期运行的 Agent。def compress_context(messages, max_tokens4000): if count_tokens(messages) max_tokens: return messages # 保留最近几轮 recent messages[-6:] # 更早的做摘要 older messages[:-6] summary call_model([ {role: system, content: 总结以下对话的关键信息保留事实和结论去掉寒暄。}, {role: user, content: str(older)} ]) return [{role: system, content: f历史摘要{summary}}] recent注意压缩时机很关键。不要等爆了才压要在接近上限时就压。我一般设在模型上下文窗口的 70% 左右触发压缩留出余量给工具返回的大结果。4.3 会话恢复的坑状态不一致会话恢复最容易出的问题是状态不一致。比如你恢复了消息历史但工具调用产生的副作用比如已经发出去的请求没法回滚重新跑就会重复执行。我的经验是把有副作用的操作做成幂等的。比如发消息带一个唯一 ID重复发同样的 ID 就忽略。这样即使恢复后重跑也不会造成重复副作用。这个设计在构建任何会调用外部系统的 Agent 时都必须考虑。5. 编排、容错与安全让 Agent 从“能跑”到“敢用”5.1 循环终止条件的设计Agent 的主循环必须有明确的终止条件否则它可能无限绕圈。常见的终止条件有模型返回了最终回答没有工具调用请求。达到最大循环次数比如 10 次。连续 N 次工具调用返回相同错误。总耗时超过阈值。我一般同时设最大循环次数和最大耗时两个兜底。最大循环次数设 10 到 15 比较合适太少了复杂任务跑不完太多了浪费。耗时阈值看任务类型交互式的设 60 秒后台批处理的可以放宽。5.2 工具失败的降级处理工具调用失败是常态不是异常。网络抖动、第三方接口限流、参数边界情况都会导致失败。Agent 要能优雅降级。我的处理原则是单次工具失败不终止整个流程把错误信息喂回模型让它决策。模型可能换个参数重试可能换个工具也可能直接告诉用户“这个功能暂时不可用”。这比直接崩掉体验好得多。但如果同一个工具连续失败三次就该终止了说明不是偶发问题。这时候给用户一个明确的错误提示而不是让 Agent 继续无意义地重试。5.3 Agent 安全热搜词里被低估的话题热搜词里出现了“agent 安全”这个词值得单独说。Agent 能调工具、能执行操作意味着它一旦被诱导可能做出危险动作。常见风险包括提示注入用户在输入里藏指令让 Agent 执行非预期操作。越权调用Agent 调用了它不该调用的工具。数据泄露工具返回的敏感数据被写进了日志或传给了不该传的地方。防护手段有几个层次。第一层是工具权限控制每个会话能调哪些工具是白名单不是模型说了算。第二层是参数校验危险操作比如删除、转账要有额外确认。第三层是输出过滤敏感信息不进日志。我个人的做法是任何有副作用的工具执行前都要过一遍权限检查检查逻辑独立于模型模型绕不过去。这是底线。6. 框架怎么选什么时候该用什么时候不该用6.1 先手写再上框架我前面一直强调先手写最小循环原因是框架解决的是工程效率问题不是理解问题。你手写过一遍知道工具调用是怎么回事、上下文怎么管再用框架就是如虎添翼没手写过直接用框架出了问题只能干瞪眼。热搜词里“agent 框架如 langchain、dify、crewai 等哪个好”这个问题我的回答是看你的场景。LangChain 生态全、组件多但抽象层厚调试麻烦Dify 偏低代码适合快速搭原型但深度定制受限CrewAI 主打多 Agent 协作适合角色分工明确的场景。没有哪个绝对好只有哪个适合你当前阶段。6.2 框架选型的判断清单我选框架时看这几点判断维度关注点抽象程度是否暴露底层细节出问题能否定位工具生态常用工具是否有现成集成状态管理会话和记忆是否内置支持可观测性有没有日志、追踪、调试工具社区活跃度出问题能不能找到答案抽象程度是我最看重的。有些框架把模型调用包得严严实实你想改个参数都找不到入口这种用起来很痛苦。宁可选抽象薄一点、但可控性强的。6.3 多 Agent 编排别为了炫技而上热搜词里“agent 编排”“agent 框架与编排”出现多次。多 Agent 协作听起来很酷但我要泼盆冷水大部分场景单 Agent 就够了。多 Agent 带来的通信开销、状态同步复杂度、调试难度往往超过它带来的收益。什么时候真的需要多 Agent当任务能清晰拆分成几个独立角色且角色间交互有明确协议时。比如一个负责检索、一个负责写作、一个负责审核这种分工明确的场景适合。如果只是任务复杂先把单 Agent 的工具和上下文管好别急着拆。7. 一条可落地的学习路线与常见误区7.1 我建议的学习顺序把前面几节串起来一条完整的学习路线是这样的第一周裸调模型 API搞定结构化输出和错误处理。目标能稳定拿到 JSON 格式的模型回复。第二周手写工具调用循环实现“模型选工具—执行—回传—再判断”的完整链路。目标一个能查天气、能算数的玩具 Agent。第三周加入会话持久化和上下文压缩。目标多轮对话不丢状态长对话不爆 token。第四周加容错和安全控制。目标工具失败能降级危险操作有拦截。之后根据项目需要选框架或者继续手写扩展。这个节奏不是死的但顺序别乱。跳过前两步直接上框架后面会还债。7.2 几个高频误区误区一以为 Agent 就是“大模型加工具”。工具只是表象核心是那个自主循环和状态管理。没有循环和状态工具调用只是一次性的函数调用。误区二过早追求多 Agent。前面说过了单 Agent 没玩明白就上多 Agent纯属给自己找麻烦。误区三忽视上下文管理。很多人 Demo 跑得飞起一上真实场景就崩因为真实对话轮次多、工具返回大上下文管理没做好。误区四不做错误处理。Demo 里一切顺利生产环境各种报错。热搜词里那些“调用错误”“rpc error”都是真实会遇到的提前设计好错误处理比事后救火强。7.3 面试和进阶方向热搜词里“agent 面试题”“agent 八股文”说明这块已经成了考察点。面试常问的无非是工具调用怎么实现、上下文超了怎么办、怎么防止死循环、多 Agent 怎么通信。你把前面几节的内容吃透这些问题都能答。进阶方向我建议往两个方向走一是可观测性怎么追踪一个 Agent 的完整决策链路出问题能快速定位二是评测怎么量化一个 Agent 好不好用不能只靠感觉。这两个方向目前都还比较缺成熟方案是能做出差异化的地方。最后分享一个我踩过的坑早期我做 Agent 时把所有逻辑塞在一个大函数里模型调用、工具执行、状态更新混在一起调试时根本不知道哪一步出的问题。后来拆成独立的模块每步都有日志效率提升非常明显。Agent 的代码结构一定要清晰因为它的执行路径是不确定的结构乱了就没法排查。这个教训希望对你有用。