☰
拆解Agent核心组件:手把手从零搭建最小可用智能体
2026/10/20 5:20:40 网站建设 项目流程

这几年大模型火得一塌糊涂,连我身边写Java、写前端的哥们都开始问我:Agent到底是个啥?为啥大家天天说Agent能自己干活,我却只觉得它像个高级聊天机器人?其实我刚接触Agent的时候也懵,市面上教程要么太深,一上来就甩一堆框架源码,要么太浅,讲一堆概念却落不了地。这篇内容就是给刚入门的你准备的,我尽量不用黑话,把Agent系统里最核心的几个零件拆开给你看:规划、记忆、工具、行动,再带你把一个最小可用的Agent从零搭起来。不管你是还在学校、刚转行,还是已经在写业务代码但没碰过大模型,这份解析都能帮你快速补上Agent这一课。

我自己带过好几个实习生,发现大家卡住的地方其实高度一致:不知道Agent内部的组件到底是怎么协作的,也不知道写代码时该从哪里下手。所以这篇我不讲花哨的架构,就讲最实在的组件拆解和落地经验。你把它当一份收藏夹里的清单也好,当一份排雷手册也行,照着走一遍,Agent就不再是个黑盒了。

1. 先搞清楚:Agent和“聊天机器人”差在哪

1.1 大模型是“大脑”,不是“完整的人”

很多人以为大模型就是Agent,其实不是。你可以把大模型想象成一个知识渊博但完全不会动手的顾问——你问他问题,他能给出漂亮的回答,但如果你让他“帮我把这份文件整理成表格再发邮件”,他做不到,因为他没有手、没有脚、没有工具,甚至连“下一步该做什么”都不会自己规划。

Agent要做的事,就是在这个“大脑”外面包一层“身体”:给它规划能力,让它把大目标拆成小步骤;给它记忆能力,让它记得你之前说过什么;给它工具能力,让它能查天气、调接口、执行代码、操作数据库;最后给它行动能力,让决策真正落地。这四个零件加上大模型这个大脑,才构成一个完整的Agent。

我在给团队内部做分享时常用一个类比:大模型像刚入职的实习生,聪明、知识广,但你交代任务时得告诉他要做什么、用什么工具、按什么顺序做、结果往哪放。Agent系统其实就是一套“实习生管理流程”,把任务的拆解、执行、反馈串起来。

1.2 为什么小白要先学核心组件

现在很多Agent框架把组件封装得很严实,你写几行配置就能跑起来,但一旦出问题,你根本不知道是哪一环坏了。我见过不少同学把报错信息贴给我,结果十有八九是工具调用参数不对,或者记忆把上下文撑爆了。这种东西如果你不懂组件原理,就只能瞎试。

所以我的建议是:哪怕你最后要用那些成熟的框架,也先用最朴素的代码把核心组件手写一遍。这样一来你会很清楚,用户输入进来之后,大模型到底是怎么被“指挥”去做事的。核心组件之间的关系也不复杂,大致是这样一个闭环:接收用户输入,先更新短期记忆,然后让大模型做规划,模型决定调用哪个工具,系统执行工具并拿回结果,再把结果作为观察写回记忆,最后模型生成回复。循环往复,直到任务完成或达到最大步数。

接下来的每一节我都会围绕这个闭环展开,你不必一次全记住,可以先收藏,读到哪一步卡住了再回来看。

2. Agent核心组件逐一拆解:规划、记忆、工具、行动、反思

2.1 规划(Planning):把大任务拆成小步骤

规划是Agent最核心的能力,也是它区别于普通聊天机器人的分水岭。聊天机器人收到问题后直接生成回答,Agent则先想“要完成这个目标,我需要做哪几步”。比如用户说“帮我查下明天去上海的机票,顺便看看那边的天气”,Agent不能直接回答,它得先规划出步骤:第一步调用机票查询工具,第二步调用天气查询工具,第三步把两边结果汇总。

规划能力通常靠Prompt引导实现,最常见的是ReAct模式,也就是让模型交替输出Thought(思考)、Action(行动)、Observation(观察)。简单理解就是:模型先说“我要查机票”,接着调用机票工具,拿到结果后说“票查到了,现在要查天气”,然后继续。这种模式在小任务上非常稳定,也是我建议小白第一个去实现的。

写代码时,你不需要做什么高深算法,只需要在发给大模型的Prompt里明确告诉它:“你需要一步一步来,每步输出一个JSON,包含你的意图、工具名和参数。”然后把模型输出的JSON解析出来执行。听起来简单,但这里有个关键坑:模型规划的步骤数可能非常多,如果没有任何限制,它会陷入“查了天气还要查酒店、查了酒店还要查餐厅”的无限循环。所以规划组件必须配一个最大步数参数,比如5步,超出就强制结束。

2.2 记忆(Memory):短期和长期分开管

大模型本身没有记忆,你每次调用API传过去的是完整上下文,它“看”到什么就“记得”什么。这就带来两个问题:一是上下文窗口有限,塞不下无限长的历史;二是就算塞得下,塞太多无关信息反而让模型抓不住重点。所以Agent需要一套记忆管理系统。

我的做法是把记忆分成三层。第一层是短期记忆,也就是最近几轮对话的原始内容,直接拼到Prompt里。第二层是工作记忆,指当前这个任务执行过程中产生的临时观察结果,比如工具返回的数据。第三层是长期记忆,通常放在外部存储里,比如把历史对话做摘要,或者用向量数据库存起来,等需要时再检索出来。

这里有个很实用的笨办法:如果你的项目还在起步阶段,不用一上来就上向量数据库。你可以先把历史对话用另一个大模型API压缩成几句话,放进系统提示里,效果已经能顶很多场景。比如用户昨天提过“我养了一只猫”,今天你只要在摘要里保留“用户有一只猫”,Agent就能自然说出相关的话。等数据量真的大了,再考虑向量检索。

短期记忆管理还有一个绕不开的话题:Token限制。每次调API都要把历史拼进去,历史越长,花销越大,速度越慢。我自己的经验是设置一个滑动窗口,比如只保留最近10轮对话,更早的全部进入摘要。这样既不会失忆,也不会让账单爆炸。

2.3 工具(Tools):给Agent安上“手和脚”

工具是Agent能实际改变世界的关键。没有工具的Agent只会纸上谈兵,有了工具,它才能查天气、发邮件、算数学、操作数据库。工具的本质就是一个函数,但你要让它能被大模型“看见”,光写一个函数不够,你还需要写一份让模型看得懂的使用说明书。

这份说明书包含三样东西:工具名称、功能描述、参数格式。我用一个生活例子解释:假如你让一个实习生去帮你取快递,你光说“去取快递”他肯定懵,你得告诉他“出门右转第三个快递柜,用手机号尾号1234开柜,有两个包裹”。工具描述就是这个作用,它告诉模型:“天气工具有一个参数,叫城市名,类型是字符串,传入北京就返回北京天气。”描述写得越清楚,模型选择工具的正确率越高。

实际实现时,你需要维护一个工具注册表,把所有工具函数、描述、参数Schema放在一个列表里,然后把这个列表转成模型能读的格式(通常是JSON)拼进Prompt。模型看到这些工具后,如果觉得需要查天气,就会在输出里说“我要调用weather_now,参数是北京”。你的调度器解析这段输出,找到对应函数,把参数传进去执行,再把结果返回给模型。

这里有几个让我印象深刻的小白翻车点:第一,工具函数内部忘记做异常捕获,第三方接口一超时,整个Agent直接崩掉;第二,参数类型没校验,用户说“帮我查北京和上海天气”,模型可能传一个数组,而你的函数只接收字符串;第三,工具描述写得太随意,比如只写“天气函数”,模型根本不知道什么时候该用它。这些都避开了,工具这关你就过了。

2.4 行动与执行(Action/Execution):从决定到落地

规划决定了要做什么,工具定义了能做什么,行动组件则负责真正落地:解析模型的决策、调用函数、拿回结果。这个组件看起来简单,但往往是最让程序员掉头发的地方,因为模型的输出天然不稳定。

模型说“我要调用weather工具,参数是北京”,它可能输出成纯文本,也可能输出成JSON,还可能输出成一堆解释文字里夹着一个函数名。所以你的行动组件必须做三件事:第一,从模型输出里可靠地抽取结构化指令;第二,校验参数是否合法;第三,执行函数并捕获异常。

我建议小白一开始就约定一种结构化输出格式,比如强制模型输出JSON,然后写一个解析器。解析失败了就让模型重新输出,但最多重试两三次。这里还有个细节:JSON经常因为被前后文字包裹而解析失败,解决办法是用正则把花括号里的内容先截出来,再调JSON解析函数,成功率会高不少。

执行结果拿到后,需要作为观察结果返回给模型。这一步如果漏了,模型就像石沉大海一样,不知道自己的行动有没有成功。所以行动组件一定要把“执行成功/失败”、“返回值是什么”、“错误信息是什么”完整记录下来,拼进下一轮Prompt。

2.5 反思(Reflection)与自我纠错:少踩坑的秘密武器

反思不是必需组件,但加上之后,Agent的成功率会有肉眼可见的提升。所谓反思,就是让Agent在行动失败或结果不理想时,不盲目重试,而是先分析原因,再调整策略。

我做过一个对比实验:同一个任务,Agent第一次调用工具失败后,直接重复调用十几次,全失败;加上反思之后,模型会先输出一句“我猜测是因为参数格式不对,我改成浮点数重试”,第二次就成功了。成本低,效果好,强烈推荐。

实现反思也很简单,就是在每一轮循环里多给模型一个字段:如果观察结果里包含“error”或“失败”,就要求模型先解释可能的原因,再决定下一步。注意要控制反思的深度,别让模型“反思”上瘾,一件事还没做,先道歉两轮。我的做法是如果连续失败两次就强制停止,免得产生不必要的费用。

如果你想做得更精细,还可以在Agent里加一个“评估器”,专门用来判断当前结果离最终目标还差多远。不过这属于锦上添花,先把前四件套用好,你的Agent就已经能跑得很像样了。

3. 从零搭一个最小可用Agent:实操笔记

3.1 环境准备与设计思路

理论讲了这么多,是时候动手了。我下面要搭的是一个“最小可用Agent”,它只做三件事:能查天气(工具调用)、能算数学题(工具调用)、能记住用户名字(记忆)。这三点分别对应工具、规划、记忆三个核心组件。

我不打算依赖任何重量级框架,只用Python加一个你能获取到的大模型API。这样做的好处是,你能看清每一行代码在做的事,而不是被框架的魔法掩盖。整体流程用一句话概括:把用户的输入、工具清单、历史摘要一起拼成一个Prompt,发给大模型,它返回一个JSON,你解析后执行对应函数,再把函数返回值拼回Prompt,重复几轮,直到模型说“任务完成”。

参数方面,我推荐把temperature设置为0.2左右,因为Agent执行任务时需要稳定,别太有“创造力”。最大迭代步数我设为5,防止模型钻牛角尖。

3.2 核心代码逐段解析

下面是一个极简实现,你可以照着理解,不需要真的申请API Key也能看懂逻辑。我故意把大模型调用封装成函数,方便你替换成自己的实际接口。

import json import re # 1. 定义你的工具函数:这里写具体做什么 def weather_now(city: str) -> str: # 真实项目中这里会请求天气API,我们这里模拟返回 return f"{city}目前晴,气温26度,适合出门。" def calculator(expression: str) -> str: # 安全起见,真实项目不要用eval,这里只是示例 try: result = eval(expression) return f"计算结果: {result}" except Exception as e: return f"计算失败: {e}" # 2. 工具注册表:给模型看的"使用说明书" TOOLS = [ { "name": "weather_now", "description": "查询某个城市的当前天气。用这个工具前,你需要知道用户想知道哪个城市的天气。", "parameters": { "type": "object", "properties": { "city": {"type": "string", "description": "城市名,例如:北京、上海"} }, "required": ["city"] } }, { "name": "calculator", "description": "计算数学表达式的值。当用户给出一段数学公式时使用。", "parameters": { "type": "object", "properties": { "expression": {"type": "string", "description": "需要计算的数学表达式,例如:12 * 3 + 5"} }, "required": ["expression"] } } ] # 3. 模拟调用大模型:实际项目中替换成你的模型API def call_llm(prompt: str) -> str: # 这里塞给模型的Prompt会指导它输出固定格式JSON # 实际代码你会调用你的大模型接口,并拿到返回文本 # 我们手动构造一个模型返回结果演示 if "天气" in prompt: return json.dumps({ "thought": "用户想知道北京的天气,我需要调用天气工具。", "action": "weather_now", "action_parameters": {"city": "北京"}, "final_output": None }) if "计算" in prompt: return json.dumps({ "thought": "用户想计算一个表达式,我调用计算器。", "action": "calculator", "action_parameters": {"expression": "12 * 3 + 5"}, "final_output": None }) return json.dumps({ "thought": "我已经有足够信息,可以回答了。", "action": None, "action_parameters": None, "final_output": "你好,我已经记住你叫小张。有什么我可以帮你的?" })

上面这段代码定义了工具和“假”模型。接着看主循环,这部分就是Agent的调度核心:它把历史、工具列表、用户输入拼成一个Prompt,调用上面那个call_llm解析、执行工具、把结果再次交给模型,直到拿到最终输出。

def run_agent(user_input: str): # 记忆:这里用最简形式,先只有当前输入和历史摘要 short_memory = [user_input] with open("my_history.txt", "w", encoding="utf-8") as f: f.write("暂无历史摘要") system_prompt = build_system_prompt() history_summary = "暂无历史摘要" for step in range(5): # 拼Prompt prompt = system_prompt + "\n\n当前对话历史:\n" + "\n".join(short_memory) prompt += f"\n\n本轮思考:请计算下一步行动。\n历史摘要:{history_summary}\n用户输入:{user_input}" # 模拟模型返回结果 response = call_llm(prompt) try: parsed = json.loads(response) except Exception: # 如果模型输出不干净,我们尝试正则抽取 matched = re.search(r"\{.*\}", response, re.S) if matched: parsed = json.loads(matched.group()) else: return "模型输出格式解析失败,建议增加重试机制。" # 重要:判断是最终回答,还是要调用工具 if parsed.get("final_output"): return parsed["final_output"] action_name = parsed.get("action") action_params = parsed.get("action_parameters") or {} # 在工具注册表里找到函数并执行 for tool in TOOLS: if tool["name"] == action_name: # 把工具执行结果通知给模型 result = globals()[action_name](**action_params) short_memory.append(f"工具返回:{result}") # 这里在实际项目中,会把 result 拼进下一轮Prompt break else: return "Agent找不到对应工具,请检查工具注册表。" return "达到最大迭代步数,自动停止。"

代码里我特意把“把工具执行结果拼回下一轮Prompt”留成了注释,实际项目里你需要在循环里把结果添加到消息队列中。这样模型下一轮就能看到“北京目前晴,气温26度”这个观察,从而给出最终回答。这个小循环就是Agent工作的最小骨架,理解了它,你就理解了绝大部分Agent框架的底层逻辑。

3.3 实测结果与踩坑复盘

我用这个最小Agent跑过几个场景,第一个是“北京天气怎么样”,它走了两步:第一步模型说要调用weather_now工具,第二步模型拿到天气结果后生成最终回答。整个过程很顺。第二个场景是我告诉它“我叫小张,帮我记住”,因为我没有给它长期的持久化设计,它只能靠当前Prompt硬记,换一轮对话就忘了。这个现象很典型,会让你真切理解长期记忆组件到底解决了什么问题。

踩坑环节才是重点。我第一次写这个循环时,模型把工具调用了两次:先调用了一次天气,然后又调了一次天气,才给最终结果。原因是我的Prompt没有明确要求“只要一次调用就够”,也没有在拿到结果后及时提示模型“你已经拿到结果,请直接回答”。解决办法是在工具执行后追加一句系统提示:“工具结果已返回,如果信息足够,直接给出最终答案,不要再调用重复工具。”

另外一个坑是JSON解析失败。我发现模型有时候会输出类似“好的,我马上帮你查:{...}”这样的内容,直接json.loads必炸。后来我加了正则提取花括号的部分,再解析,就稳多了。这个技巧我一直在用,每次模型输出不干净,先用正则抢救一下,实在救不了再让模型重答。

4. 小白必看的常见问题与排查技巧

4.1 问题速查表

下面这些问题是新手做Agent时最容易遇到的,我直接整理成一张表,方便你以后排查。

现象常见原因排查方向
Agent答非所问上下文太长,历史被截断,关键信息丢失检查Prompt里实际拼了哪些历史;给历史加摘要
工具调用老是失败工具描述太含糊,模型不知道何时用把工具描述写成“用户提到XX时调用”的句式
参数传不对工具参数Schema写得有歧义,或缺少类型约束检查参数描述,明确类型和格式,必要时给默认值
Agent死循环没有设最大步数,或模型反复调用同一工具增加max_steps;在Prompt里加“不要重复调用”
JSON解析报错模型输出前后夹带了文字用正则抽取JSON片段,失败后重试一次
API费用飙升循环步数太多,或历史每次都全量发送限制步数,缩短上下文,把历史做摘要
用户说“它根本没记忆”长期记忆没有真正落地,只是写在当前Prompt里把重要信息存到文件或数据库,并在新对话开始前加载

排查问题时我的顺序是:先看日志,确认模型每一步输出了什么;再确认工具有没有被正确调用;最后才怀疑记忆系统。很多时候“Agent笨”不是大模型笨,而是你中间的衔接代码掉了链子。

4.2 三个独家避坑技巧

第一个技巧:给Agent写日志。我要求自己所有Agent项目里,每一轮循环都要打印或落盘以下信息:当前步骤、模型原始输出、解析后的action、工具执行结果、内存占用。出问题时不靠猜,拿日志回放一遍就能定位。

第二个技巧:Prompt里明确要求“先思考,再行动”。你可以这样写:“每一步你都必须先输出来你自己的理解和计划,再决定调用哪个工具。如果信息已经足够,请直接输出最终答案。”加了这句话之后,模型乱调工具的频率会明显下降。

第三个技巧:所有工具函数都做成安全的。接收任何参数都不能直接eval用户输入,更不能把用户输入拼进Shell命令。我在内部一个业务Agent上吃过亏,后来所有工具函数加了白名单校验和超时控制,才算睡了个安稳觉。你以后接第三方API时尤其要注意,工具是给Agent使用的,也是给恶意用户试探的入口,别在小项目阶段就埋雷。

5. 进阶方向:从“能用”到“好用”

5.1 再往上走可以学什么

如果你已经能把最小Agent跑通,接下来的进阶路径我推荐四条:第一,多Agent协作。别让一个Agent干所有事,拆成“老板Agent”负责规划,“打工Agent”负责具体工具,互相传递结果。这能解决单Agent工具太多、决策混乱的问题。第二,记忆持久化。用一个向量数据库把历史对话、文档片段存起来,用户提问时先检索再回答,而不是把所有内容一股脑塞给模型。第三,工具的自动发现。让Agent根据用户需求,从一组候选API里自己找最合适的,这涉及更复杂的元信息描述,但和小白阶段的工具注册表思路一致。第四,结果评估。给Agent准备一堆测试用例,每次改进后用同一批用例跑一遍,量化正确率,而不是靠肉眼感觉“好像变聪明了”。

5.2 我的一点实践建议

带过几个新手之后,我发现最容易让人放弃的不是概念难,而是期望值太高。有人一上来就想写一个能自动写代码、自动改Bug的Agent,结果搞了三天发现连工具调用都不稳定,心态直接崩了。我自己的做法是:先把一个极其简单的业务场景跑通,哪怕就是“帮我查天气、算个税”,再逐步加组件。每加一个组件前,先问自己“不加行不行”,不行再加,这样你的系统永远是最简可用的。

还有一点想提醒你:多读几个开源项目的源码,但别只盯着一行行看。我习惯先看它们的Prompt是怎么写的,再看工具定义的结构,最后看主循环怎么组织。你会发现不管项目多大,骨干逻辑和我上面写的最小循环其实差不太多,只是多了缓存、并发、安全这些工程细节。等你绕完这一圈,再回头看你最初问的“Agent到底是个啥”,答案已经在手上了。

如果非要给一条最快的学习路径,我会说:先跑通我这个最小Agent,再把你工作中一个重复性的小任务(比如整理会议纪要、定时查询报表)交给它做。不用管效果多惊艳,只要它跑起来,你就已经摸到大模型世界的门槛了。后面的事情,都是熟能生巧。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询