我要提问
ARTICLE DETAIL

资讯详情

前沿编程新知与开发实战干货的深度解读。

智能体Agent实战:从原理到代码,打造会做事的AI

智能体Agent实战:从原理到代码,打造会做事的AI 智能体Agent到底是个啥东西我直接说结论智能体不是一个神秘的技术黑盒而是“大模型 记忆 工具 自主决策循环”的组合体。它跟普通的聊天机器人最大的区别就三个字——会做事。你问ChatGPT“明天深圳天气怎么样”它给你一段文字你问一个Agent同样的问题它自己去查天气接口、比对数据、组织语言、甚至帮你把加衣提醒推到手机里。这一来一回的差异就是Agent存在的意义。这篇文章不堆概念我会用一套完整的实操视角来拆解智能体它跟普通AI程序的分界线在哪里、核心组件是什么、一个最小可用的Agent怎么搭起来、跑起来会踩哪些坑。适合刚开始接触AI开发、想搞清楚“Agent到底能不能落地”的人也适合已经在做LLM应用、但总觉得“差点火候”的开发者。1. 智能体和聊天机器人的本质区别在哪里很多教程喜欢说“Agent LLM 记忆 工具”这句话没错但太粗了。真正决定一个东西是不是智能体的不是它用了多大的模型而是它有没有“行动”的能力。1.1 从“说话”到“做事”的跨越普通聊天机器人的工作流程是输入 → 模型生成 → 输出文字。它是一个封闭回路模型既看不到外界也改变不了外界。你让它“帮我定个明天早上的闹钟”它只能给你一段“好的我记住了”的假话因为它根本没有能力碰你手机里的时钟App。开发者想要欺骗这种局限最常见的做法是把功能硬编码进对话里比如识别到“闹钟”关键词就弹个确认按钮。这就是一种伪智能——所有的“能力”都是预先写好的规则模型只是在做关键词匹配。Agent不一样。它把“生成文字”拆成了“思考”和“执行”两个环节。模型先分析你的意图把它转成一个结构化的行动指令比如“调用系统闹钟App设定早上7:00响铃”。然后Agent框架接管真正去执行这个指令再把执行结果喂回给模型让模型决定下一步怎么做。这个“行动—观察—再决策”的循环才是智能体的核心也是它和聊天机器人之间那堵看不见的墙。1.2 自主性才是灵魂刚才说的闹钟例子其实还只是“工具调用”很多框架不调用工具也能做。真正让Agent从“工具”升级为“智能体”的是自主性。我用一个生活里的场景来说。你去餐厅吃饭跟服务员说“来一份招牌套餐”服务员只需要把菜单递给后厨——这是工具调用型Agent。但如果你说“帮我安排一顿适合父母50岁寿宴的晚餐”服务员要自己定预算、选菜系、考虑长辈口味、订座位、算时间——这是自主决策型Agent。落到技术层面自主性体现在Agent能自己拆解目标。它不会等你把每一步都告诉它而是把一个大任务分解成若干子任务然后为每个子任务选择合适的工具、合适的执行顺序甚至在执行失败时自己换一套方案重试。这种能力来自大模型本身具备的规划能力再加上Agent框架提供的“反思”和“修正”机制。一句话总结聊天机器人是“张嘴就来”Agent是“想好了再干干完回头看”。2. 智能体的核心组件拆解一个可用的Agent最少需要五个部分。少一个它就退化成聊天机器人或者一个普通脚本。我按重要程度逐个拆。2.1 大脑大模型的角色不只是“聊天”Agent里的大模型承担的是决策中枢的工作而不是一个话痨。它要做的事情包括理解用户请求、分析当前状态、选择合适的工具、生成行动参数、判断行动结果、决定是否继续。这个角色对模型的推理能力要求明显高于纯聊天场景——你在选型时不能只看模型在排行榜上的分数还要看它在“多轮工具调用”场景下的稳定性。我拿一个实际案例来对比。某次我用一个轻量模型搭Agent让它处理“帮我把这个表格里的重复数据清理掉”这个任务。它第一步对了识别出需要打开表格工具但在第二步就乱了——它输出了一个完全不存在的函数名把“清理重复数据”错误地翻译成了“排序”。换成推理能力更强的模型同一份数据、同一个框架它就能正确调用“数据去重”并返回处理后的结果。这就是为什么Agent项目里模型选型几乎决定了整个项目的上限。2.2 记忆短期记忆和长期记忆是两个存储Agent的记忆机制分两层。短期记忆相当于工作台。Agent在执行任务的过程中需要不断把中间状态放进去当前目标是什么、已经做了哪一步、下一步打算做什么、工具返回了什么结果。大多数Agent框架用“聊天历史”承载短期记忆但这有个隐患模型有上下文窗口上限窗口满了之后最早的信息会被挤掉导致Agent“忘记”任务最初的约束条件。实操里我见过最典型的一个场景用户让Agent“总结这份报告控制在300字以内”Agent执行到一半上下文溢出等它回来写总结时早把“300字以内”忘了输出了2000字的全文摘要。长期记忆则负责跨会话的持久化。比如一个Agent助手记住了用户的偏好“我喜欢早上九点开会”、历史任务的结论、甚至用户的说话风格。长期记忆的实现方式通常是向量数据库加Embedding先把文本转成向量存进去需要时再做相似度检索。这一层是智能体能不能“越用越懂你”的关键也是最容易被新手忽略的部分——很多人搭的Agent只能处理单次对话换个会话就失忆了。2.3 工具智能体的手和脚工具是Agent连接外部世界的接口。工具可以是函数、API、数据库查询、浏览器操作脚本、命令行指令甚至可以是另一个Agent。每一条工具定义通常包含三部分工具名称、参数描述、执行逻辑。Agent框架会把这些描述塞进系统提示词里让模型知道“你有什么工具可以用、分别怎么用”。这里有个关键细节工具描述的质量直接影响调用准确率。我见过太多人随手写工具描述比如“更新用户信息”结果模型经常在需要更新用户地址的时候调用了别的工具。把描述改成“根据用户ID更新用户地址address字段只能用于修改收货地址”之后调用准确率肉眼可见地提升。你完全可以把它理解为给工具写“上岗说明书”说明书越精确新员工模型犯错的概率就越低。2.4 规划器把大目标拆成小步骤规划器负责把一个大目标拆成有序的子步骤。有的Agent用ReAct模式Reasoning and Acting边推理边行动每一步都基于上一步的结果生成下一步的思考有的用Plan-and-Execute模式先一次性生成完整计划再逐条执行还有的更复杂带任务队列和优先级调度。实际场景里ReAct更适合任务步骤不明确、需要探索的场景Plan-and-Execute更适合步骤固定、可预测的流水线任务。这里要提一个实施细节规划器不一定要用额外的代码模块实现。很多成熟的Agent框架直接靠大模型的提示词完成规划模型在每一轮先输出“Thought思考、Action行动、Observation观察结果”循环往复直到任务完成。这种模式简单直接但缺点也很明显——一旦模型陷入错误循环它可能在一件错误的事情上反复尝试烧掉大量Token。所以现在越来越多的方案会在外部加一层“硬校验”比如规定最大步数、超时自动终止、每个工具调用的参数做Schema校验。外置校验和价值判断对稳定性很重要别把安全托付给模型自觉。2.5 反馈循环把“做完了”变成“做好了”闭环的最后一个环节是反馈循环。Agent每次调用工具后会拿到一个结果它不急着交答案而是先“看一眼”结果是否符合预期。如果工具返回的数据格式不对、结果为空、或者明显不合理Agent会重新调整参数再试一次或者换一个工具。这个“观察—反思—调整”的过程是Agent质量比普通脚本高一个档次的原因。有一些比较讲究的落地项目还会给Agent加一层“自我批评”让模型对每一步的执行结果打分低分就重跑。这种方式很耗Token但对那些“一步错、步步错”的复合任务很有帮助。你可以在自己的项目里做成可配置项让用户按需开关而不是一刀切强制开启。3. 从零搭建一个最小可用的智能体理论说完了直接上手。我尽量用最少的依赖做出一个能跑起来的Agent你不需要任何高级的框架一个支持Function Calling的模型API就够了。我们要做的事情是搭一个能查天气、能算简单数学的Agent让它具备“自主决策该调用哪个工具”的能力。3.1 环境准备选型逻辑我这里选Python环境因为生态最成熟。核心依赖只有一个openai官方SDK因为它的Function Calling机制是最典型的Agent工具调用范式其他平台也都兼容类似的能力。你本地需要配置一个环境变量存放API Key不要硬编码进代码里。pip install openai export OPENAI_API_KEY你的密钥有些人对“Function Calling”这个名称不理解它其实就是你在API请求里描述一批可用函数模型根据用户的话决定调不调用其中一个并且把调用参数以结构化JSON返回给你。真正的“调用动作”由你的代码执行模型只负责决策。明白这层之后Agent就不神秘了——它就是“模型决策 程序执行”的组合。3.2 定义工具给Agent准备“能力”我们给Agent定义两个工具get_weather和calculator。注意工具描述怎么写直接影响模型的调用准确度。tools [ { type: function, function: { name: get_weather, description: 根据城市名查询当前天气情况支持中文城市名如深圳、北京、上海, parameters: { type: object, properties: { city: { type: string, description: 城市名例如深圳 } }, required: [city] } } }, { type: function, function: { name: calculator, description: 执行四则运算表达式支持加、减、乘、除、括号如12 * 5 8, parameters: { type: object, properties: { expression: { type: string, description: 数学表达式例如(12 3) * 4 } }, required: [expression] } } } ]这段代码会告诉大模型“你有两个工具一个查天气一个算数学”。但注意一个细节模型并不知道“get_weather这个函数应该去哪个网站查数据”它只负责“根据用户意图返回city参数”。真正联网查询的代码是你要写的模型不参与。这种职责分离是Agent落地时最需要注意的边界。3.3 执行循环Agent的心脏工具定义好之后核心的执行循环如下。这段逻辑是所有Agent框架的雏形理解了它你就能看懂任何复杂的Agent框架。from openai import OpenAI import json client OpenAI() def run_agent(user_message): messages [{role: user, content: user_message}] # 第一步把用户问题 工具定义 一起发给模型 response client.chat.completions.create( modelgpt-4o-mini, messagesmessages, toolstools, tool_choiceauto ) # 第二步模型决定调用哪个工具 response_message response.choices[0].message messages.append(response_message) # 把模型的“决策”加入对话历史 # 第三步如果模型决定调用工具就执行 if response_message.tool_calls: for tool_call in response_message.tool_calls: func_name tool_call.function.name args json.loads(tool_call.function.arguments) if func_name get_weather: # 这里是模拟查询实际应接天气API result f{args[city]}今天晴28度微风 elif func_name calculator: result eval(args[expression]) # 把工具执行结果返回给模型 messages.append({ role: tool, tool_call_id: tool_call.id, content: str(result) }) # 第四步模型拿到工具结果后生成最终回答 final_response client.chat.completions.create( modelgpt-4o-mini, messagesmessages, toolstools ) return final_response.choices[0].message.content # 如果没有调用工具直接返回模型回答 return response_message.content # 测试 print(run_agent(深圳天气怎么样)) print(run_agent(12 * 5 8等于多少)) print(run_agent(先算一下(12 3) * 4然后告诉我深圳天气))这个循环就是我前面说的“感知—决策—行动—观察”的具体实现。第一轮请求相当于“感知 决策”模型的输出里带着工具调用指令代码执行工具是“行动”执行结果回传给模型是“观察”模型基于观察结果组织最终回答。一个多步骤的Agent任务无非就是把第四步的结果再次喂给模型让它继续决策下一轮工具调用直到模型认为任务完成、不再返回工具调用为止。3.4 让Agent跑多轮任务上面的例子只跑了一轮工具调用就收尾了。真实的Agent任务往往需要多轮先查一个数据再基于数据做计算最后再生成报告。把上面的循环改造一下让“判断是否还需要调用工具”变成一个while循环就是最简版的多轮Agent。def run_agent_multi(user_message, max_steps5): messages [{role: user, content: user_message}] for step in range(max_steps): response client.chat.completions.create( modelgpt-4o-mini, messagesmessages, toolstools, tool_choiceauto ) response_message response.choices[0].message messages.append(response_message) if not response_message.tool_calls: return response_message.content for tool_call in response_message.tool_calls: func_name tool_call.function.name args json.loads(tool_call.function.arguments) if func_name get_weather: result f{args[city]}今天晴28度微风 elif func_name calculator: result eval(args[expression]) messages.append({ role: tool, tool_call_id: tool_call.id, content: str(result) }) return 已达到最大轮次仍未完成请调整任务描述或增加上限这个max_steps限制非常重要。我第一次写Agent时没加这个限制结果模型在“计算一个复杂表达式”时陷入了死循环它反复调用计算器、看到结果、觉得不对、再换个方式调用直到API账单飙升我才发现。加一个硬上限既保护钱包也避免框架挂死。3.5 小型项目可直接配置一份Agent调度源码示例多轮循环本身也可拆成独立配置。如果你不想每次都在代码里写死“工具名称—函数名”的映射可以做成一个按名称注册和分发的表格式调度结构如下方示例所示# mini_agent.py # 用名称注册表管理工具调用方便后续新增工具 TOOL_REGISTRY {} def register(func): 把函数注册到工具表函数docstring作为工具描述 TOOL_REGISTRY[func.__name__] func return func register def get_weather(city: str) - str: 根据城市名查询当前天气情况支持中文城市名 # 实际场景替换为真实天气API调用 return f{city}今天晴28度微风 register def calculator(expression: str) - str: 执行四则运算表达式支持加、减、乘、除、括号 return str(eval(expression)) def run(user_message: str, max_steps: int 5) - str: tools [ { type: function, function: { name: name, description: fn.__doc__, parameters: {...} } } for name, fn in TOOL_REGISTRY.items() ] # 循环逻辑略和上文run_agent_multi一致这种写法在工具少的时候看不出优势等工具数量上到十几个你会发现“注册表模式”比一长串if-else好维护得多。新增工具只用写一个函数加注册装饰器不用改动主循环代码。4. 智能体能做什么用场景说话理解了原理再看应用场景你的视角会完全不一样。因为Agent的能力边界不是由“它能做什么”决定的而是由“你给它接了什么工具”决定的。我给几个不同类型、已经可以落地的场景。4.1 办公⽣产力把重复劳动扔给Agent最典型的应用是邮件和会议日程处理。一个Agent可以接入你的日历API、邮件API、通讯录API。你只需要对它说一句“帮我看看明天下午三点有没有空如果有的话安排一个跟某同事的半小时沟通会并把会议纪要模板准备好”它会自己查日历、判断空闲时间、发起会议邀请、创建文档模板。这一步跨了三个系统传统自动化脚本要做到这个程度你需要写几百行胶水代码而且每个系统接口一升级就要重新维护。Agent把“接口调用逻辑”藏在工具层之后你用自然语言就能指挥它完成跨系统操作。数据整理也是Agent的强项。之前我帮一个团队做销售周报的自动化以前的流程是人手动从CRM里导出数据、用Excel做透视表、再写总结。改成Agent后流程变成了Agent定时触发→查询数据库→调用数据分析工具→生成图表→调用大模型把数据写成周报文案→推送消息到工作群。整套流程需要人的环节只剩下“审核”。4.2 代码辅助从补全代码到改Bug代码补全工具已经普及了但Agent级别的代码助手做的是另外一件事理解整个仓库结构、定位问题、修改代码、运行测试、根据测试结果决定是否继续修改。给你一个具体的例子。我让一个Agent处理“这个项目的前端控制台报错提示某个接口返回了404帮我排查”。Agent先调用代码搜索工具找到调用这个接口的代码位置再调用日志查询工具看后端有没有对应路由最后用调试工具在测试环境请求一遍接口拿到404的具体原因——路由路径写错了大小写不一致。整个过程它自己定位、自己验证、自己给出修改建议。人只负责最后点一下“确认修改”。这类Agent落地速度最快的是独立开发者和中小团队因为代码库规模小、结构相对清晰Agent的上下文窗口Cover得住。4.3 个人助理让Agent真正“懂你”个人助理类Agent是目前门槛最高的场景原因在于它需要长期记忆而且不能犯低级错误。一个合格的助理Agent至少要会根据日程提醒你提前出发要接地图API和航班动态、一个月后主动问你是否需要续费某个会员要接支付记录、洞察到你说“最近好累”时自动把本周的晚间会议推迟要接日历和情绪判断。这些功能单拆出来都不复杂难的是状态管理Agent要随时追踪“用户身处什么阶段、需要什么信息、上次聊过什么”。这也是为什么很多产品团队先从“垂直场景的单功能Agent”做起比如只做一个日程管家、只做一个账单助理而不是一上来就做全能管家。4.4 行业落地客服、运维、销售头部企业级Agent落地最成熟的三个方向智能客服、自动化运维、销售助手。智能客服Agent已经不是早期的关键词回复了。现在的方案是Agent理解用户问题→搜索知识库→调用订单系统查询用户订单状态→判断是否需要人工介入。整套流程里它可以同时处理好几万并发遇到解决不了的问题再一键转人工这个模式在零售和金融行业已经大规模验证过。运维Agent现在的做法是接到告警→查监控系统→分析日志→判断根因→执行预案操作比如重启某服务、扩容某集群。能不能真正自动执行操作取决于企业的风险承受能力但“辅助排查根因”这一步已经能用Agent跑得很稳。5. 常见问题与排查技巧实录Agent项目跟传统软件开发最大的不同是失败路径极多而且很多失败是概率性的不是必现的。同一个输入有时成功有时失败这种不确定性让排查变得很头疼。我把我踩过的坑集中列一下都是实战里反复出现的问题。5.1 模型不爱调用工具怎么办现象Agent能正常对话但用户说了“查一下天气”模型却直接回答“我查不了我只是一个语言模型”。原因分析要么是工具描述写得不清晰模型不认识这个工具要么是模型对工具的“信任度”不够或者上下文里类似场景没出现过。还有一个常见原因你在系统提示词里没明确告诉模型“你可以使用工具”。排查手段把工具描述写得更具体给出典型触发场景比如“当用户询问天气、气温、降雨等信息时必须使用get_weather工具并传入城市名”。在系统提示词里加一句“你有充足的工具来回答用户问题当用户请求涉及工具能力时请直接调用”。测试时把tool_choice从auto改成指定工具名比如tool_choice{type: function, function: {name: get_weather}}强制模型走一次工具调用排查是不是工具定义本身有问题。5.2 Agent产生幻觉自己编造工具结果现象Agent明明没调用工具却在回答里写“根据天气API查询结果深圳今天31度”。这是最危险的一种错误因为回答看起来非常可信。原因分析模型在训练语料里见过类似的数据当上下文没有明确约束时它会“脑补”一个答案。这本质上是模型对“无法获得信息”这件事的容忍度太低它宁可编一个也不愿意承认自己不知道。排查手段在系统提示词里强调“只有工具调用返回的数据才能作为事实依据未经工具验证的信息必须声明为推测”。在Agent框架里加一条校验规则如果模型回答中包含数据性陈述但本轮循环中没有任何工具调用记录则判定为可疑回答要求重写。对于高正确率要求的场景如金融、医疗可以采用“拒绝回答”策略当工具结果无法获取时要求Agent明确回复“暂时无法获取该数据”而不是猜测。5.3 多轮任务中Agent“迷失方向”现象一个三步任务Agent做完第一步之后忘记最初的目标是什么开始自由发挥。原因分析这是上下文窗口被中间工具结果填充导致的注意力稀释尤其是工具返回的长文本会把关键约束挤出有效注意力范围。排查手段把“原始目标”固定在系统提示词的前部并重复强调。比如“本轮任务起点用户在请求一份季度销售报告。你必须在生成最终回答前确保已经完成数据分析步骤”。工具返回结果做摘要而非原样塞入。如果工具返回了5000行数据不要让模型硬读先用程序做统计摘要再把摘要喂给模型。在Agent里引入“目标检查点”每执行完一个子任务用一条程序日志记录“当前进度”下一次循环把这条进度注入上下文帮助Agent重新锚定目标。5.4 工具参数格式反复出错现象需要传入JSON格式的参数模型总是传错要么字段名不对要么类型不对。原因分析参数Schema描述不够严格或者模型对类型定义理解不足尤其在参数是嵌套结构时更容易出错。比如给“address”字段传了一个对象而不是字符串。排查手段参数的description写清楚“类型是什么、格式是什么、示例值是什么”。比如“date: string格式YYYY-MM-DD示例2025-06-01”。在Schema里用enum定义可选值范围模型只能从枚举里选基本上不会错。在框架层加JSON Schema校验校验不过就让模型重新生成一次参数。这比直接报错要友好因为模型看到“格式错误请修正为XX格式”往往能自己改对。5.5 成本失控Agent烧Token比预期凶现象一个简单问题Agent内部调了好几次模型账单数字吓人。原因分析每次工具调用的结果都会塞回对话历史多轮之后每发一次请求Token消耗都是上一次的累加。另一个隐性成本是工具描述本身——每轮请求都要重新携带全部工具定义。排查手段给Agent设定最大工具调用轮数比如3轮超过就强制终止。对工具返回结果做截断只保留前几百个字符既节省Token又避免无效信息分散注意力。工具数量多的项目把工具做分组按需加载。比如用户聊天气时只加载天气相关工具不加载查数据库的工具。这个优化能让Token消耗直接减半。6. 智能体项目的基本设计要点最后把思路拉高一点讲几个我在搭建Agent时反复用到的设计原则。这些原则不是通用的但凡是能稳定上线的Agent基本都会遵守其中至少三条。6.1 “最小权限”原则也适用于Agent给Agent接工具时不要图方便把所有API都接上。比如一个只处理“读取”任务的Agent就不要给它“删除”或“写入”的权限。一旦模型被注入恶意提示或者陷入错误循环最小权限能避免发生不可逆的破坏。我在做运维Agent时第一步就先做权限隔离只允许Agent执行只读操作所有变更类操作必须经过人工审批。6.2 可观测性设计Agent的“黑盒”要留观察窗Agent决策过程对用户来说是个黑盒但对开发者不能是。设计Agent时每轮模型响应都应当记录模型思考摘要、调用的工具、参数、返回结果、耗时、Token消耗。这些日志在测试阶段是唯一能帮你快速定位问题的线索。我自己的习惯是在开发阶段强制打印每一步的完整JSON上线后再改为只记录关键字段。没有日志的Agent就是盲人摸象问题复现不了也就永远修不好。6.3 把工具做薄把描述做厚传统开发里函数职责越单一越好这个原则在Agent的“工具”设计里同样成立但有一个额外维度描述信息的质量比实现逻辑更关键。一个工具哪怕实现逻辑很简单只要描述清晰、参数定义精确模型的调用准确率就会高很多。反过来一个工具实现逻辑很复杂但是描述含糊模型就不知道该什么时候用它再强的能力也白搭。每一次工具定义都值得像写接口文档一样对待。6.4 兜底策略比聪明逻辑更可靠Agent的失败模式太多了你不可能预测所有场景。与其写一堆复杂的分支逻辑来应对特殊状况不如做好三件事最大轮次限制、异常重试机制、人工接管接口。关键操作一律要求人工确认这不是对Agent能力的不信任而是工程上的负责任。7. 再分享两个实操里的小技巧这个章节算是我个人实战的附带收获不一定写得进正式文档但确实好用。第一个技巧是关于“冻结工具描述版本”。Agent跑在线上生产环境工具描述稍微改动一个字模型的行为就可能漂移。我遇到过一次小版本更新只是把工具描述里的“城市名”改成了“城市中文名”结果模型的调用准确率突然下降因为新描述跟模型在预训练时见过的样例格式不匹配了。从那之后我养成了一个习惯工具描述改动后必须跑一遍固定测试集对比准确率再决定是否上线。第二个技巧是把“观察”环节做实。默认的Agent循环里模型把工具返回的结果直接拿过来用不做质量判断。但实际上工具返回结果可能为空、可能格式异常、可能数据明显不合理。在有臻求的项目里我会在代码里加一层经验判断程序也就是简单的规则校验比如“结果是否为空”“是否超时”“JSON是否能解析”。解析失败的结果先不进对话历史先要求模型重新生成工具参数重试一次。这一步防住了很多因为工具自身偶发故障导致的连锁错误。Agent这东西说到底不是“造一个无所不能的仙灵”而是把“人的意图”翻译成“一步步可执行的行动”再把每一步行动的结果反馈给模型继续决策。它不会取代复杂系统设计但确实把人与程序之间的交互方式往前推进了一大截。如果你手边正好有某个经常重复的任务或者某个跨系统的协作流程不妨像上面那样先写一个最简Agent试着跑起来。先把“感知—决策—行动”的闭环跑通再往里加记忆、加规划、加反思你的智能体会一步步长出你想要的样子。
返回列表