
1. 为什么现在值得花时间搞懂 AI Agent过去大半年我身边不少做后端、做数据、甚至做产品的朋友都在问同一个问题AI Agent 到底是个什么东西跟我之前调个大模型 API 有啥本质区别我一般不会先讲概念而是直接反问一句你有没有遇到过那种“一次性对话搞不定、需要来回好几步、中间还得查资料调工具”的活儿如果有那你就已经站在 Agent 的门口了。先把话说透AI Agent 不是某个具体产品也不是某个模型而是一套“让模型自己决定下一步做什么”的运行机制。普通的大模型调用是你问一句它答一句边界清清楚楚而 Agent 的核心在于它拿到一个目标之后会自己拆解任务、自己选择工具、自己判断结果够不够好、不够好就再来一轮。这个“自己”不是玄学背后是一套可拆解、可实现、可调试的工程结构。我写这篇东西的出发点很实在网上讲 Agent 的文章要么停留在“未来已来”的宏大叙事要么一上来就是一堆框架名词把人劝退。但真正想动手的人需要的是从 0 到 1 搭一个能跑起来的最小 Agent然后在这个骨架上一点点加东西。所以这篇内容适合三类人完全没接触过 Agent 但想搞明白它到底怎么运转的初学者已经会调模型 API、想进一步做点“能干活”的东西的开发者以及想评估 Agent 到底能用在哪些业务场景里的产品和技术负责人。我会按“先想清楚为什么这么设计再动手实现最后踩坑排查”的顺序来讲中间会给出可以直接抄的代码结构、参数选择的计算逻辑以及我自己在实操中踩过的坑。你不需要提前懂什么框架只要会一点 Python、知道怎么调用模型接口就能跟着走完。2. 先把 AI Agent 的骨架想明白再动手2.1 Agent 和普通大模型调用的本质区别很多人第一次接触 Agent会以为它是“更聪明的模型”。这个理解方向就偏了。模型本身没变变的是模型外面套的那层循环和工具。我用一个生活化的类比来说明普通调用大模型就像你去餐厅点菜你说“来个宫保鸡丁”厨房做好端上来结束。而 Agent 更像你雇了一个助理你说“帮我安排一顿适合四个人的晚饭”助理会自己想四个人得几个菜、有没有人忌口、预算多少、要不要订位然后一步步去执行中间可能还要回来问你一句。落到工程上这个区别体现在三个地方。第一是目标驱动你给的不是一条指令而是一个目标Agent 需要把目标拆成可执行的步骤。第二是工具调用Agent 不能只靠“嘴”回答它得能真的去查数据库、调接口、读文件、算数。第三是循环与反馈做完一步要看结果结果不对就调整直到满足终止条件。这里有个关键认知Agent 的能力上限很大程度上不取决于模型多强而取决于你给它的工具和约束设计得好不好。我见过太多人一上来就换更大的模型结果效果提升有限反而是把工具描述写清楚、把终止条件收紧之后整个 Agent 的稳定性上了一个台阶。2.2 一个最小 Agent 需要哪几个核心部件把花哨的东西全剥掉一个能跑的最小 Agent 其实就四个部件我用一张表把它们和职责列清楚部件职责缺了会怎样目标与指令告诉 Agent 要达成什么、边界在哪Agent 漫无目的容易跑偏模型推理决定下一步做什么、调用哪个工具没有决策能力退化成固定流程工具集真正执行动作查询、计算、写文件等只能空谈干不了实事循环控制判断是否继续、何时终止要么死循环要么一步就停这四个部件里最容易被低估的是循环控制。新手往往把注意力全放在“怎么让模型更聪明”上结果 Agent 要么陷入无限循环烧钱要么做了一步就草草收场。我的经验是循环控制的设计优先级应该排在模型选型之前因为它直接决定了你的 Agent 是“能用”还是“能用得起”。2.3 为什么我建议从“手写循环”开始而不是直接上框架现在市面上 Agent 框架不少功能也很全但我强烈建议第一次搭 Agent 的人先手写一遍最朴素的循环哪怕只有几十行。原因很简单框架帮你封装了循环、工具调用、状态管理你用它跑通了但你不清楚里面发生了什么。一旦出问题你连从哪查都不知道。手写一遍的好处是你会亲眼看到“模型返回了一个工具调用请求我解析它执行工具把结果塞回对话历史再问模型”这个完整链路。这个过程走通一次你对 Agent 的理解就从“听说”变成了“我知道它每一步在干嘛”。之后再上框架你就能判断框架到底帮你省了什么、又在哪些地方限制了你。我自己的路径就是这样先用最原始的方式写了一个只会做加法和查天气的 Agent跑通之后才去看框架文档那时候看什么都觉得顺因为每个概念我都能对应到自己手写的那段代码上。3. 核心细节拆解工具、提示词与循环控制3.1 工具设计Agent 的手和脚怎么定义工具是 Agent 真正干活的地方也是整个系统里最需要花心思的部分。一个工具本质上就是一个函数加上一段给模型看的描述。模型根据描述来判断“这个任务该不该用这个工具”。所以工具描述写得好不好直接决定模型会不会正确使用它。我踩过的第一个坑就是工具描述写得太随意。当时我写了一个查询订单状态的工具描述就一句“查询订单”。结果模型经常在该用的时候不用在不该用的时候乱用。后来我把描述改成“根据订单号查询订单的当前状态包括已支付、已发货、已签收输入必须是订单号字符串”命中率立刻上来了。这个细节看起来小但它是 Agent 稳定性的地基。工具设计有几个实操原则。第一一个工具只做一件事不要把“查询并修改”塞进一个工具模型会分不清什么时候该调。第二参数类型要明确是字符串还是数字、必填还是可选都要在描述里说清楚否则模型传参经常出错。第三返回值要结构化最好返回 JSON 或者明确的键值对方便模型理解结果。第四工具要有失败处理网络超时、参数错误这些情况要返回清晰的错误信息让模型知道“这一步失败了可以换个方式再试”。下面是一个工具定义的示例结构用 Python 字典来描述方便你直接套tools [ { name: get_order_status, description: 根据订单号查询订单当前状态返回状态字段。输入必须是订单号字符串。, parameters: { type: object, properties: { order_id: { type: string, description: 订单号例如 A123456 } }, required: [order_id] } } ]这段结构里description是给模型看的parameters是约束模型传参的。你会发现我特意在描述里加了“输入必须是订单号字符串”这就是在给模型划边界。边界划得越清楚Agent 越不容易乱来。3.2 提示词工程怎么让模型乖乖按流程走Agent 的提示词和普通对话的提示词不一样。普通对话你只要说清楚问题就行Agent 的提示词还得告诉模型“你有哪些工具、什么时候用、用完怎么判断”。这其实是一份行为规范而不是一句提问。我一般会把系统提示词分成四块来写。第一块是角色和目标比如“你是一个订单处理助手目标是帮用户查清订单状态并给出下一步建议”。第二块是可用工具清单把每个工具的名字和用途列出来。第三块是工作流程比如“先确认用户提供了订单号没有就先问拿到订单号后调用查询工具根据返回状态给出建议”。第四块是约束比如“不要编造订单状态查不到就如实说”。这里有个很实用的技巧把“什么时候停止”写进提示词。很多人只告诉模型怎么开始不告诉它怎么结束结果模型要么一直循环要么提前收尾。我会明确写“当你已经拿到订单状态并给出建议后任务结束直接输出最终答复”。这一句话能省掉大量调试时间。还有一个细节是少样本示例。如果某个流程模型总是走不对我会在提示词里塞一两个“输入-应该怎么做”的例子。比如给一个“用户说‘帮我看看订单’”的例子展示正确的第一步是追问订单号。示例不用多一两个就能显著改善行为。3.3 循环控制终止条件与最大轮次的取舍循环控制是 Agent 的刹车和油门。没有它Agent 要么停不下来要么一步就熄火。我一般会设置两个终止条件满足任意一个就停一个是模型明确表示任务完成另一个是达到最大轮次上限。最大轮次怎么定这得看任务复杂度。我的经验值是简单查询类任务 3 到 5 轮足够多步骤任务 8 到 12 轮再复杂就得考虑拆成多个子 Agent 了。这个数字不是拍脑袋而是根据“平均每步消耗的 token 数 × 轮次 × 单价”算出来的成本上限。举个例子假设每轮平均消耗 2000 token单价按常见水平算10 轮就是 2 万 token。你心里得有个数不然一个失控的循环能把预算烧穿。除了轮次上限我还会加一个重复检测。如果模型连续两轮调用了同一个工具、传了同样的参数那大概率是卡住了这时候应该强制终止并返回“未能完成”。这个机制能挡住相当一部分死循环。max_turns 10 history [] for turn in range(max_turns): response call_model(history, tools, system_prompt) if response.is_final: break if response.tool_call: result execute_tool(response.tool_call) history.append(response.tool_call) history.append(result) else: # 达到最大轮次仍未完成 return 任务未能在限定轮次内完成这段伪代码里is_final是模型给出的完成信号tool_call是工具调用请求。注意for...else的用法循环正常走完没 break就说明超轮次了。这个结构简单但很实用。提示最大轮次不要设得太大宁可让 Agent 失败返回也不要让它无限循环。失败了你还能看到日志去优化烧钱烧到停不下来才是真的麻烦。4. 从零搭一个能跑的最小 Agent4.1 环境准备与依赖选择动手之前先把环境理清楚。我建议用 Python 3.10 以上因为类型提示和异步支持都比较完善。依赖方面最小集合其实只需要一个能调模型接口的库加上一个处理 JSON 的标准库就够了。如果你用的是某家云服务的模型接口装它对应的 SDK 即可。我不建议一上来就装一堆框架。先把依赖压到最低跑通之后再按需引入。这样你能清楚知道每个依赖是干嘛的出问题也好定位。虚拟环境一定要建我见过太多人把全局环境搞乱最后连哪个包冲突都查不出来。python -m venv agent_env source agent_env/bin/activate # Windows 用 agent_env\Scripts\activate pip install requests就这两步环境就好了。requests用来发 HTTP 请求调模型接口如果你用的 SDK 更方便换成 SDK 也行。关键是别让环境成为你动手的阻碍。4.2 定义工具函数并注册接下来把工具写出来。我以一个“计算器”和一个“查天气”的假工具为例因为这两个足够简单能让你专注在 Agent 流程本身而不是被业务逻辑分心。import json def calculator(expression: str) - str: try: result eval(expression, {__builtins__: {}}, {}) return json.dumps({result: result}) except Exception as e: return json.dumps({error: str(e)}) def get_weather(city: str) - str: # 这里用假数据演示实际应调用真实接口 fake_data {北京: 晴25度, 上海: 多云28度} return json.dumps({city: city, weather: fake_data.get(city, 未知)}) TOOL_MAP { calculator: calculator, get_weather: get_weather }注意calculator里我用了eval但把__builtins__清空了这是为了防止执行危险代码。工具函数一定要考虑安全性尤其是涉及执行、文件操作、网络请求的工具输入必须做校验。这个细节很多人会忽略等到出问题就晚了。工具写完之后要把它们的描述整理成模型能看懂的格式也就是前面 3.1 节里那个tools列表。描述和函数要一一对应名字不能写错否则模型调用了你却没有对应实现整个流程就断了。4.3 主循环实现与状态管理主循环是整个 Agent 的心脏。它的逻辑其实很朴素把对话历史发给模型看模型是要调用工具还是给最终答复如果要调工具就执行、把结果追加到历史然后继续下一轮。def run_agent(user_input, max_turns8): history [{role: user, content: user_input}] for turn in range(max_turns): response call_model(history, tools, SYSTEM_PROMPT) if response.get(final_answer): return response[final_answer] tool_call response.get(tool_call) if tool_call: tool_name tool_call[name] tool_args tool_call[arguments] if tool_name in TOOL_MAP: result TOOL_MAP[tool_name](**tool_args) else: result json.dumps({error: f未知工具 {tool_name}}) history.append({role: assistant, content: json.dumps(tool_call)}) history.append({role: tool, content: result}) return 任务未能在限定轮次内完成这段代码里history就是状态。Agent 的“记忆”全靠这个列表每一轮的模型输出和工具结果都往里塞。这里有个容易出错的地方不同模型接口对消息格式的要求不一样有的要求工具结果用特定角色标记有的要求放在特定字段里。你得对照你用的接口文档来调整别照搬。状态管理还有一个进阶话题是上下文长度控制。轮次多了之后history 会越来越长可能超出模型的上下文窗口。我的做法是保留最近若干轮加上一个任务摘要。摘要可以让模型自己生成比如“到目前为止已经查到订单状态是已发货”。这样既省 token又不丢关键信息。4.4 跑通第一个任务并观察日志代码写完先别急着上复杂任务。用一个最简单的输入测试比如“帮我算一下 23 乘以 47”。理想情况下你会看到 Agent 调用calculator工具拿到结果然后给出最终答复。跑的时候一定要打印完整日志把每一轮的模型输出、工具调用、工具结果都打出来。我第一次跑通的时候盯着日志看了半天才真正理解“模型决定调用工具”是怎么回事。日志是你调试 Agent 的唯一眼睛千万别省。如果第一次没跑通大概率是这几个原因工具描述和函数名对不上、消息格式不符合接口要求、模型没有正确理解“什么时候该停”。对照日志逐个排查基本都能解决。5. 常见问题与排查技巧实录5.1 Agent 陷入死循环怎么办死循环是新手遇到最多的坑。表现是 Agent 反复调用同一个工具或者在两三个工具之间来回横跳就是不给最终答复。原因通常有三个一是终止条件没写清楚模型不知道什么时候算完成二是工具返回的结果模型看不懂它以为没成功就重试三是任务本身超出了 Agent 的能力范围它在硬撑。排查顺序我一般是这样的。先看提示词里有没有明确的终止条件没有就补上。再看工具返回值是不是结构化的、清晰的如果返回一大段自然语言模型容易误判。最后看任务是不是太复杂如果是就拆成多个子任务或者干脆换人工处理。我还会加一个重复调用检测作为兜底记录最近几轮的“工具名参数”如果完全重复就强制终止。这个机制救过我好几次尤其是在工具接口不稳定、返回超时的时候。5.2 工具调用参数总是传错怎么修参数传错的表现是工具执行报错或者结果明显不对。根因往往在工具描述不够精确。比如一个工具需要“日期”你只写“日期”模型可能传“明天”这种自然语言而不是“2026-01-01”这种格式。修法是在参数描述里给出格式示例。把description: 日期改成description: 日期格式为 YYYY-MM-DD例如 2026-01-01。别小看这个改动它能挡掉大部分格式错误。另外参数类型也要写死是 string 就别让它传 number。如果改了描述还是错可以在工具函数里加一层参数校验和容错。比如日期格式不对就尝试解析常见格式实在解析不了就返回明确的错误提示让模型知道该怎么改。这种“工具自己兜底”的思路比单纯指望模型传对参数要可靠。5.3 成本失控与响应变慢的优化思路Agent 比普通对话贵这是事实因为它一轮轮地调模型。成本失控通常来自两个地方轮次太多或者每轮塞进去的上下文太长。优化也是从这两处下手。轮次方面把能合并的步骤合并把终止条件收紧。上下文方面及时清理 history只保留关键信息。我一般会做一个“历史压缩”超过一定轮次就把早期对话总结成一句话。实测下来这个操作能省掉相当一部分 token而且对结果影响很小。响应变慢往往是工具调用本身慢比如查数据库、调外部接口。这时候可以考虑给工具加超时超时就返回错误让模型决定下一步而不是一直等。Agent 的稳定性很大程度上取决于每个环节都有超时和兜底。下面这张表是我整理的常见问题速查方便你对照排查现象可能原因排查方向反复调用同一工具终止条件不清、结果看不懂补终止条件、结构化返回值参数格式错误工具描述不精确加格式示例、加参数校验成本异常高轮次多、上下文长收紧轮次、压缩历史响应特别慢工具接口慢、无超时加超时、异步化直接给最终答复不调工具工具描述不吸引、提示词没引导优化描述、加少样本示例注意排查 Agent 问题时永远先看日志再看提示词最后才怀疑模型。绝大多数问题都出在前两者换模型往往是最后手段而且经常解决不了根本问题。6. 从最小 Agent 到实用 Agent 的扩展方向6.1 多工具协作与任务拆解最小 Agent 跑通之后下一步就是让它能处理更复杂的任务。核心思路是任务拆解把一个大目标拆成若干子任务每个子任务可能对应不同的工具组合。比如“帮我安排一次出差”可以拆成查航班、查酒店、算预算、生成行程单几个子任务。实现上你可以让模型先输出一个任务计划然后按计划逐步执行。也可以引入“规划 Agent”和“执行 Agent”的分工前者负责拆解后者负责执行。这种多 Agent 协作的模式在复杂场景下很有用但也会带来新的复杂度建议在单 Agent 稳定之后再尝试。我个人的经验是不要为了多 Agent 而多 Agent。很多任务用单 Agent 加清晰的提示词就能搞定硬拆成多个反而增加调试难度。判断标准很简单如果单 Agent 的提示词已经长到难以维护或者不同子任务需要完全不同的工具集那才考虑拆分。6.2 记忆与知识库的接入Agent 的“记忆”分短期和长期。短期记忆就是对话历史前面已经讲过。长期记忆则是把重要信息存下来下次还能用。最简单的做法是把关键结论写进一个文件或数据库需要时再读出来。知识库接入是另一个常见需求。当 Agent 需要回答领域问题时光靠模型自身知识不够得去查资料。这时候可以接一个检索工具让 Agent 先检索、再基于检索结果回答。这个模式就是常说的检索增强能显著提升回答的准确性。接入知识库时要注意检索结果的质量。如果检索出来的内容不相关模型会被带偏。所以检索工具的描述要写清楚“什么时候用”返回结果也要做筛选和排序别一股脑全塞给模型。6.3 评估与迭代怎么判断 Agent 变好了Agent 做出来只是开始怎么判断它好不好、有没有变好是个更实际的问题。我的做法是建一个测试集把常见的输入和期望的输出列出来每次改动之后跑一遍看通过率。测试集不用很大十几二十条就够但要覆盖典型场景和边界情况。比如正常查询、参数缺失、工具报错、超范围请求这些都要有。跑测试的时候记录每条的耗时和 token 消耗这样你不仅知道对不对还知道贵不贵。迭代的时候一次只改一个地方改完跑测试对比。如果同时改提示词又改工具出了问题你都不知道是哪个引起的。这个习惯看起来笨但能帮你省下大量返工时间。Agent 的优化是个细活急不得。最后分享一个我自己的体会Agent 这东西看十篇文章不如自己动手跑通一个。哪怕它只会做加法和查天气你跑通之后对它的理解会比看再多资料都扎实。真正的门槛不在概念而在那些只有动手才会遇到的细节里。