
1. 从能聊到能干活Agent 到底比裸调 LLM 多了什么很多人第一次接触 AI Agent脑子里浮现的画面是一个会自己思考、自己动手的智能体。但真上手写代码的时候往往第一步就卡住了我直接调大模型 API 不也能回答问题吗为什么还要套一层 Agent这个问题的答案藏在对话和任务这两个词的区别里。裸调 LLM本质是一次无状态的文本映射——你给它一段 prompt它给你一段 completion结束。它没有记忆、没有工具、没有循环更没有做错了重来一次的能力。而 Agent 的核心价值是把 LLM 从一个问答机器升级成一个任务执行器它能拆解目标、调用外部工具、观察结果、根据反馈调整下一步动作直到任务完成或者主动放弃。我习惯用一个类比来解释LLM 是一个知识渊博但从没出过门的顾问你问他什么他都能答但你让他帮我把这周的销售数据整理成报表发给我他做不到因为他没有手也没有脚。Agent 就是给这个顾问配上了手脚、记事本和一套工作流程——他能打开数据库、能写文件、能发邮件做完一步看一眼结果不对就换个方法再来。从工程视角看一个完整的 Agent 至少包含七个要素这七个要素缺一个都会导致系统看起来能跑实际不能用LLM 内核负责推理、规划、决策是整个系统的大脑。选型时不能只看 benchmark 分数要看它在结构化输出和工具调用格式遵循上的稳定性。指令/系统提示定义 Agent 的角色、边界、输出规范。这是最容易被低估的一环后面会专门讲。工具集Agent 能调用的外部能力比如搜索、代码执行、数据库查询、文件读写。记忆短期记忆当前对话上下文和长期记忆跨会话的知识沉淀。规划模块把大目标拆成可执行的小步骤常见的有 ReAct、Plan-and-Execute 等范式。执行循环驱动思考—行动—观察不断迭代的引擎。状态管理记录当前进度、已尝试的方案、失败原因避免重复踩坑。这七个要素里LLM 内核和工具集是硬件指令、记忆、规划、循环、状态是软件。很多教程只讲怎么调 API、怎么定义 tool却完全不讲循环怎么退出、状态怎么存、失败了怎么办——结果就是 Demo 跑得飞起一上真实任务就死循环或者胡言乱语。提示如果你现在手上的Agent只是prompt 里让模型输出 JSON然后解析 JSON 调函数那它严格来说只是一个 function calling 的封装还不算 Agent。真正的分水岭在于是否存在自主的迭代循环。理解了这七个要素接下来的问题就变成了在真实工程里这七个要素分别对应哪些必须做的决策我把它归纳成七个决策点每一个决策点做错都会在后期以诡异 bug的形式反噬你。2. 七个决策点每一个都决定你的 Agent 是玩具还是产品2.1 决策点一LLM 选型——别被榜单分数骗了选模型这件事新手最容易犯的错是看排行榜选最强的。但 Agent 场景下模型的能力维度跟聊天场景完全不同。聊天看重的是语言流畅度和知识广度Agent 看重的是三件事指令遵循的严格程度、工具调用格式的稳定性、长上下文里的信息保持能力。我实测下来的经验是一个在通用榜单上排中游、但专门做过 function calling 微调的模型在 Agent 任务里的表现往往比顶级通用模型更稳。原因很简单——Agent 的每一步输出都要被程序解析格式错一个括号整个循环就崩了。通用模型在自由发挥上很强但这种自由恰恰是工程上的灾难。具体怎么选我一般按这个顺序筛先确认工具调用协议是原生支持 function calling还是靠 prompt 约束输出 JSON前者稳定性高一个量级。测结构化输出成功率拿 50 条真实任务跑一遍统计格式解析失败率。超过 5% 就要慎重。测长上下文衰减把工具返回的大段结果塞进上下文看模型在第 10 轮之后还能不能记住最初的目标。最后才看推理能力复杂规划确实需要强推理但如果前三项不过关推理再强也白搭。还有一个常被忽略的点成本和延迟。Agent 一次任务可能要跑十几轮循环每轮都是一次完整的 LLM 调用。如果你选了一个又贵又慢的模型单次任务成本可能是聊天场景的几十倍。所以生产环境里我通常采用分层策略——用便宜快的小模型做意图识别和简单工具选择用强模型做复杂规划和最终决策。2.2 决策点二循环机制——什么时候停比什么时候走更重要Agent 的执行循环是整个系统的心脏但绝大多数教程只讲怎么让循环转起来不讲怎么让循环停下来。而后者才是工程上的真正难点。一个典型的 Agent 循环长这样把当前状态和可用工具喂给 LLMLLM 输出下一步动作程序执行动作拿到观察结果把结果追加到状态里再喂给 LLM……如此往复。问题在于这个循环天然倾向于永不停止——模型总觉得还能再试一次或者陷入调用工具 A 失败换个参数再调 A又失败的死循环。我在项目里总结了几条退出条件必须同时设置缺一不可退出条件触发逻辑建议阈值任务完成模型输出明确的 finish 信号由模型判断最大轮次循环次数硬上限10-15 轮重复检测连续 N 次动作高度相似连续 3 次超时总耗时上限60-120 秒成本上限累计 token 消耗按业务定这里有个反直觉的经验最大轮次不要设太大。我一开始设了 30 轮想着给模型足够空间结果发现超过 10 轮之后模型基本都在原地打转偶尔还会因为上下文太长而忘记最初目标。后来改成 12 轮配合重复检测任务成功率反而上升了。重复检测的实现也有讲究。不能简单比较字符串是否相同因为模型每次措辞都不一样。我的做法是提取动作的语义指纹——工具名 关键参数忽略自然语言描述部分。如果连续三次调用的工具和核心参数都一样就强制中断并把你已经重复尝试了同样的操作作为提示注入下一轮逼模型换思路。2.3 决策点三工具设计——工具不是越多越好而是越笨越好新手做 Agent总想把所有能想到的能力都做成工具塞进去觉得工具越多 Agent 越强。这是个巨大的误区。工具数量一多模型的选择困难症就犯了调用错误率直线上升。我的原则是工具要笨要单一职责要参数极简。一个工具只做一件事参数不超过三个每个参数都有明确的类型和取值范围。比如搜索和读取网页应该是两个工具而不是一个搜索并读取的复合工具——因为复合工具让模型失去了中间决策的机会。工具描述description的写法更是门手艺。模型选不选这个工具几乎完全取决于描述。我踩过的坑是描述写得太官方比如该工具用于执行数据库查询操作模型根本不知道什么时候该用。后来改成场景化描述——当用户询问订单状态、物流信息、退款进度时使用此工具输入订单号即可调用准确率立刻上来了。还有一个细节工具返回结果要控制长度。有些工具比如网页抓取返回的内容动辄几千字直接塞进上下文会迅速撑爆窗口还会稀释模型的注意力。我的做法是在工具层做预处理——截断、摘要、或者只返回结构化字段。宁可让工具多做一点脏活也别让模型去处理原始数据。2.4 决策点四记忆架构——短期靠上下文长期靠检索记忆这块很多人一上来就想搞向量数据库 RAG觉得这才是高级玩法。但实际上Agent 的记忆要分两层看而且短期记忆的重要性远高于长期记忆。短期记忆就是当前任务的上下文它决定了 Agent 能不能记住自己刚才干了什么。这里最大的敌人是上下文窗口限制。当工具返回结果、历史动作、系统提示全部堆在一起很容易就超了。我的处理策略是滑动窗口 关键信息固化保留最近 N 轮完整对话更早的内容压缩成摘要同时把任务目标已确认的事实已失败的方案这三类信息单独拎出来永远放在上下文最前面不参与压缩。长期记忆才是向量检索的用武之地。但要注意长期记忆不是把所有历史都存进去而是存可复用的经验。比如用户偏好用表格展示数据这个 API 在高峰期会超时需要重试这类跨任务的知识。存的时候要带元数据时间、场景、置信度检索的时候才能精准命中。注意长期记忆最大的风险是污染。如果 Agent 把一次错误尝试的结论存进了长期记忆下次任务就会带着错误前提开始。所以写入长期记忆前一定要有验证机制——要么任务成功后才写要么人工确认后写。2.5 决策点五状态管理——让 Agent 知道自己走到哪了状态管理是最不性感、但最影响稳定性的决策点。所谓状态就是 Agent 在执行任务过程中的进度快照当前目标是什么、已经完成了哪些子任务、正在做什么、遇到了什么障碍。没有状态管理的 Agent就像一个失忆的人每轮循环都从头开始理解任务效率极低还容易跑偏。我见过最典型的失败案例是Agent 在第 3 轮已经查到了用户 ID第 5 轮又去重新查了一遍因为它忘了自己查过。我的做法是维护一个显式的状态对象结构大概是这样state { goal: 原始任务目标, subtasks: [ {name: 查询用户信息, status: done, result: ...}, {name: 生成报表, status: in_progress, result: None}, ], facts: {user_id: 12345, date_range: 2024-01}, failed_attempts: [用 API A 查询失败返回 403], current_step: 5, }每轮循环开始前把这个状态序列化成简洁文本注入 prompt。这样模型永远知道全局进度不会重复劳动也能从失败记录里学到这条路走不通。状态管理的另一个作用是支持断点续跑。Agent 任务可能因为超时或异常中断如果状态持久化了下次可以从断点继续而不是从头再来。这在长任务场景下能省大量成本。2.6 决策点六错误处理——Agent 的健壮性全在这里Agent 跟传统程序最大的区别是它的每一步输出都是概率性的所以错误是常态而非异常。工具会失败、模型会输出非法格式、网络会抖动、API 会限流。一个没有错误处理设计的 Agent在 Demo 里能跑在生产里必崩。我把错误分成三类分别对应不同的处理策略可重试错误网络超时、限流、临时性故障。策略是带退避的重试重试 2-3 次仍失败再上报。可修正错误模型输出了非法 JSON、调用了不存在的工具、参数类型错误。策略是把错误信息作为观察结果喂回模型让它自己修正。这类错误恰恰是 Agent 自主性的体现。致命错误权限不足、资源不存在、任务本身不可完成。策略是立即终止返回清晰的失败原因不要浪费轮次。这里有个关键设计错误信息要对模型友好。程序抛出的原始异常比如 Python 的 traceback对模型毫无意义要转换成自然语言描述比如调用查询工具失败原因是订单号格式不正确请检查后重试。模型看到这种信息才知道怎么改。我踩过最深的坑是早期版本里工具报错直接抛异常终止了整个循环导致 Agent 遇到一点点小问题就死了。后来改成错误即观察让模型看到错误并尝试自救任务成功率提升了将近一倍。2.7 决策点七可观测性——看不见的 Agent 等于不可维护最后一个决策点也是最容易被跳过的一个可观测性。Agent 的执行过程是一个黑盒如果不做日志和追踪出了问题你根本不知道是哪一步错了。我的最低要求是每一轮循环都要记录完整的输入输出——喂给模型的 prompt、模型的原始输出、解析后的动作、工具返回的结果、耗时、token 消耗。这些日志在调试时的价值无可估量。更进一步我会给每个任务生成一个 trace ID把整个执行链路串起来可视化成一个时间线。这样一眼就能看出Agent 在哪一步卡住了、哪次工具调用最慢、哪轮 token 消耗异常。有了这个优化才有方向。可观测性还有一个隐性价值它是评估 Agent 质量的数据来源。你可以从日志里统计任务成功率、平均轮次、常见失败模式这些指标是迭代优化的基础。没有数据优化就是拍脑袋。3. 把七个决策点串起来一个最小可用的 Agent 骨架讲了这么多决策点可能有人觉得信息量太大不知道从哪下手。我的建议是先用最小骨架跑通再逐个决策点优化。下面这个骨架是我在多个项目里反复打磨出来的去掉了一切非必要的东西但保留了所有关键结构。class Agent: def __init__(self, llm, tools, max_steps12): self.llm llm self.tools {t.name: t for t in tools} self.max_steps max_steps def run(self, goal): state {goal: goal, facts: {}, history: [], failed: []} for step in range(self.max_steps): # 1. 构造 prompt系统指令 状态 历史 prompt self.build_prompt(state) # 2. 调用 LLM response self.llm.chat(prompt, toolsself.tools) # 3. 解析动作 action self.parse(response) # 4. 判断是否结束 if action.type finish: return action.answer # 5. 执行工具 try: result self.tools[action.name].run(**action.args) state[history].append((action, result)) except Exception as e: state[failed].append(str(e)) result f工具执行失败{e} # 6. 重复检测 if self.is_repeating(state): state[failed].append(检测到重复动作请换一种方法) return 任务未在限定轮次内完成这个骨架看起来简单但每个方法里都藏着前面讲的决策点。build_prompt负责状态注入和上下文压缩parse负责容错解析is_repeating负责循环退出异常处理负责错误即观察。你可以把它当成一个 checklist逐个方法去填充细节。我特别想强调parse这个环节。模型输出的格式永远不会 100% 稳定所以解析器必须足够宽容能处理 markdown 代码块包裹的 JSON、能处理多余的解释性文字、能在解析失败时给出明确的错误反馈让模型重试。我见过太多项目因为解析器太严格导致明明模型答对了却被判为失败。4. 那些文档里不会写的实战教训4.1 系统提示词不是越长越好而是越结构化越好我早期写系统提示恨不得把所有规则都堆上去结果模型反而抓不住重点。后来发现提示词的效果跟长度不成正比跟结构清晰度成正比。我的模板固定成四段角色定义、能力边界、输出格式、行为准则。每段用明确的分隔符隔开关键约束用编号列出。这样模型能快速定位到我该做什么我不能做什么我该怎么输出。还有一个技巧把最重要的约束放在最前面和最后面。模型对 prompt 首尾的注意力最强中间部分容易被忽略。所以必须调用工具而非直接回答这种硬约束我会在开头和结尾各强调一次。4.2 工具调用的参数校验要在程序层做不能指望模型模型生成参数时经常会出现类型错误、缺字段、超范围的情况。如果你直接把这些参数传给工具函数轻则报错重则造成数据损坏。我的做法是在工具入口做一层严格的 schema 校验不合法就返回明确的错误信息给模型让它重新生成。这层校验看似多余实则是保护系统的最后一道防线。4.3 别让 Agent 处理它不擅长的任务Agent 不是万能的。对于需要精确计算、严格逻辑、大量确定性规则的任务用传统代码比用 Agent 靠谱得多。我的一般原则是能用确定性代码解决的绝不交给模型。Agent 的价值在于处理模糊的、需要判断的、步骤不固定的任务。把这两者混在一起只会让系统又慢又不稳。4.4 测试用例要覆盖失败路径而不只是成功路径大部分人测 Agent只测任务能不能完成。但真正决定产品质量的是它在遇到异常时的表现。我的测试集里至少一半是故意设计的坑工具返回空结果、模型输出非法格式、任务本身无解、中途网络中断。只有这些场景都处理得当Agent 才算真正可用。5. 关于 Agent 学习路线的一点个人看法经常有人问我学 Agent 该从哪开始。我的建议是别一上来就啃框架。现在各种 Agent 框架层出不穷但它们的底层逻辑都是前面讲的七个要素和七个决策点。你先把裸的循环写一遍把工具调用、状态管理、错误处理都手写一遍踩够了坑再去看框架你会发现框架帮你解决的正是你踩过的那些问题理解会深刻得多。至于学习顺序我的路线是先搞懂 LLM 的 API 调用和 function calling 机制然后手写一个最简单的 ReAct 循环接着加上工具和状态管理最后再考虑记忆、多 Agent 协作这些进阶话题。每一步都要有能跑起来的代码光看文章不动手永远学不会。Agent 这个领域变化很快但底层的工程原则是相对稳定的。把七个要素和七个决策点吃透无论上层框架怎么变你都能快速上手。我在实际项目里最大的体会是Agent 的难点从来不在让它动起来而在让它稳定地、可控地、可维护地动起来。前者是 Demo后者才是工程。