☰
从零构建Agent:先能力后框架,吃透工具调用与记忆管理
2026/10/17 4:00:14 网站建设 项目流程

如果你最近也动过“自己从零构建一个 Agent”的念头,我猜你大概率经历过下面某个场景:打开一个开源项目,看到 README 里写着环境搭建、配置文件、API Key、跑通 Demo,结果除了复制粘贴,什么都没留下;或者买了一门教你用某个框架做 Agent 的课,跟着敲完一遍,换个需求照样不知道从哪下手。

这不是你学得慢,而是学习顺序不对。太多人一开始就钻进框架里,从“安装框架”开始学构建 Agent。但真正扎实的路径,是从“能力”开始,而不是从“工具”开始。这篇文章我想分享一条我自己验证过、也带过不少新手走过的学习主线:先建立最小闭环,再逐步加深,最后再碰框架和工程化。哪怕你只有基础的编程能力,只要愿意每周投入几小时,按这个顺序走下来,会比盲目追新框架的人先读懂 Agent 的本质。

1. 先别急着碰框架:先搞懂 Agent 的最小闭环在哪

很多人对 Agent 有个误解,觉得 Agent 是一套很高级的系统。但实际上,一个最小可用的 Agent 闭环,不依赖任何框架,甚至不用写太多代码。它本质上就是一个循环:给你一个大语言模型的接口,给它若干工具的调用说明,让它在“思考”和“执行”之间反复横跳,直到完成你的目标。

我做一个粗糙但清晰的描述:

  • 用户提出目标(比如“帮我查一下这几个城市的天气,然后按温度排序”)
  • 模型判断需要调用哪个工具(查天气的接口),并生成参数(城市列表)
  • 你的程序执行这个工具,拿到真实数据
  • 数据回填给模型,模型基于结果向用户输出最终答复
  • 如果任务还没完成,模型会继续下一轮工具调用

这个循环里没有框架的戏份。框架只是把这类循环工程化、可视化、可部署,但它替代不了“你理解这个循环”这件事。如果你一上来就钻框架,很容易变成“照着搭积木”,遇到一个框架没覆盖到的场景就直接卡死。

1.1 四个地基能力,按顺序自检

在构建 Agent 之前,我认为有四个地基能力必须过关。它们不是可选项,是后面所有内容的承重墙。

第一是“稳定拿到模型输出”。你得会调用模型接口,处理超时、限流、内容截断、异常返回。很多新手调通一次接口就觉得自己会了,实际上换个模型、换个网络环境、加个并发,就暴露出问题。

第二是“结构化输出”。Agent 是一个程序系统,它不能容忍模型每次回复的格式都不一样。你要会约束模型输出 JSON、输出固定字段,也会处理“模型这次不听话,输出了一堆废话和 JSON 混在一起”的情况。这项能力直接决定你能不能把模型的输出变成程序逻辑的输入。

第三是“提示词工程的基础意识”。不是让你去背各种花哨的 few-shot 模板,而是要理解:角色设定影响什么、步骤描述影响什么、给不给示例影响什么。这部分不需要学得玄乎,但至少要能写清楚一段“让模型按照某流程处理输入”的指令。

第四是“上下文窗口意识”。模型输入会占 token,token 就是成本。你需要大概知道一段对话经过几轮之后会撑满窗口,应该怎么截断、压缩或者省略。很多人后面积累到多轮 Agent 时出了怪问题,回头查才发现是“上下文被塞满了导致模型开始胡说”。

1.2 四道自测题,过关再继续

光说“你要会”是空的,我给四道具体的自测题,你可以现在就试试。

第一题:找一篇文章,用模型接口抽取“人物、时间、地点”三个字段,要求输出 JSON,字段名固定为people、time、place,值必须是数组或字符串。这道题考察你能否稳定拿到结构化输出。

第二题:把同样的文章换成英文提问,要求 JSON 里的字段名仍然保持英文people、time、place,不论你用中文、英文、还是夹杂式的 prompt,字段名都不能漂移。这道题考察你的提示词是否具备跨场景稳定性。

第三题:故意给模型一段超长文本,要求它输出全文摘要。然后你去处理“输出被截断”的情况,看看是有 retry 机制、还是能感知到截断并请求继续。这道题考察你对输出长度的管控能力。

第四题:连续对话 20 轮,每轮都把历史消息带进请求里,打印每一轮的 token 消耗。之后观察第 15 轮开始模型的回答质量是不是在下降。这道题不是考记忆,是考你对“上下文占用”的体感。

四道题做完,打开一个本地项目,把这几段代码整理成几个可复用函数。这一步的价值是:构建 Agent 后面要用的通信骨架,你已经亲手搭过了。很多人跳过了这一步,后面学什么都很虚。

2. 工具调用:从“会聊天”到“会干活”的那条线

Agent 和普通聊天机器人的本质分界线,是“工具调用”。聊天机器人只会“说”,Agent 会“干”。而“干”这件事,靠的是模型能把你的意图映射到某个真实可执行的函数上。

工具调用机制(业界常叫 Function Calling)说白了就是:你给模型一份工具菜单,每个工具描述了“这个工具叫什么、接收什么参数、参数是什么格式、大概能做什么事”,然后模型看了用户的问题,自己决定调用哪个工具、填什么参数。你的程序拿到这个结果,去执行真实函数,再把执行结果交给模型。

这个过程像什么?像你在餐厅里不直接进厨房炒菜,而是告诉服务员你要吃什么,服务员把菜单传到后厨,后厨再把菜端给你。模型就是那个服务员,你的代码才是后厨。工具调用机制的伟大之处在于,你不需要把“用户意图判别”的逻辑全部硬编码成 if-else,模型把意图理解和参数提取这部分工作替你做了。

2.1 先手写一个极简工具调用循环

我不建议你第一次接触工具调用就去框架里找封装好的 Tool 类,先自己写一个最小版本,整个过程不超过 50 行代码。下面是一个简化到极致的伪代码思路:

def run_agent_turn(user_query, tools): messages = [{"role": "user", "content": user_query}] response = llm.chat_with_tools(messages, tools=tools) if response.has_tool_call: tool_name = response.tool_call.name tool_args = response.tool_call.arguments result = execute_tool(tool_name, tool_args) # 你的程序执行真实函数 messages.append(response.message) messages.append({"role": "tool", "content": result}) final_answer = llm.chat(messages) # 把结果回填给模型 return final_answer.content return response.content

这段逻辑里最关键的一行是execute_tool。模型不执行任何东西,它只负责“决定调哪个工具”和“填参数”,真正干活的是你本地的 Python 函数。所以 Agent 的能力边界,取决于你给它接了多少真实工具、每个工具描述得够不够清楚。

我见过不少新手在第一步就想着“我做一个能自己写代码、自己执行代码的 Agent”,结果卡在“让模型生成代码”和“沙箱执行代码”的安全问题上。这个方向不是不能做,但它不是打基础的路径。你应该先从“接一个天气接口”“接一个计算器函数”“查一下本地数据库”这种确定性强、边界清晰的小工具做起。

2.2 工具 Schema 设计的三个实操经验

工具调用能不能用好,一半取决于模型,一半取决于你把工具描述写得好不好。我踩过几次坑之后,总结出三条很实用的经验。

第一,工具名和参数名要语义化。不要写func1、arg1,要写get_weather_by_city、city_name。模型不是靠记忆理解参数,是靠命名理解参数。命名越接近自然语言,模型填参数就越准。

第二,必填参数越少越好。如果某个参数不是必填,就别放进required列表里,让模型能省则省。必填参数一多,模型在不确定时就会开始编造值。比如“查天气”这个工具,city必填就够了,“日期”做成可选,没给日期就默认今天,这样成功率会明显上升。

第三,工具返回结果要尽量简洁、结构化。模型每次都要把工具结果重新读一遍,你给它返回一大段夹杂着日志的 HTML,它很容易提取错关键信息。更好的做法是让工具返回{"city": "北京", "temperature": 18}这样干净的 JSON,再交给模型总结。

我最早做工具调用时,最常犯的错是工具执行失败后不知道怎么办。比如网络请求超时了,你总不能让模型干等着。后来我的习惯是:所有工具执行失败时,统一返回一个结构化的错误信息,比如{"error": "timeout", "message": "..."},然后在提示词里告诉模型“如果看到 error 字段,就如实告诉用户,不要编造结果”。这个细节虽然不是核心机制,但能救命的程度,远远超过你的想象。

3. 记忆管理:为什么“记住上下文”比想象中难

工具调用解决的是“干”的问题,记忆管理解决的是“记得住”的问题。一个 Agent 如果只能处理单轮指令,那它的实用性会大打折扣。可一旦进入多轮对话,你立刻会撞上一个大麻烦:模型的上下文窗口是有限的,你不能把所有历史消息都原封不动地塞进去。

上下文窗口就像你的办公桌。桌子就那么大,你手头能放的文件就那么多。你可以在桌上堆更多纸,但堆到一定程度,就再也找不到真正需要的那张了。Agent 的记忆管理,本质上是在做“办公桌整理”:哪些纸张保留,哪些纸张压缩,哪些纸张放进档案柜(长期存储)以后要用再去翻。

3.1 短期记忆:三种简单的对话压缩策略

短期记忆指的是当前这轮会话里,模型能“看到”的信息。最笨的办法是把所有对话历史都带上,但在多轮任务里很快会撑爆窗口。实操中常用的有三种策略。

滑动窗口:只保留最近 N 轮对话。好处是简单直接,坏处是早期出现的关键信息会被丢得一干二净。比如用户在第 3 轮说过“我想要红色”,第 20 轮你问“你想要什么颜色”时,模型完全不知道。所以滑动窗口适合那些前置信息不重要的任务。

摘要压缩:每隔几轮,让模型把之前的对话浓缩成一段摘要,比如“用户想做一个博客系统,已确认使用某某数据库,当前关注登录功能”。之后每一轮,你只带上这段摘要加最近几轮对话,模型既保留了全局脉络,又不用担心窗口被塞满。

关键信息抽取:针对特定业务,只抽核心信息。比如客服场景里,抽“用户名、订单号、诉求类型、情绪倾向”就够了,无关寒暄直接丢掉。这种方式最节省 token,但需要你对业务非常了解,能列出真正值得留存的字段。

让我给一个最容易上手的组合:最近 N 轮完整对话,加上前面所有内容的摘要。N 取多少取决于你的任务复杂度,一般 5 到 10 轮比较合适。这个组合不用引入任何额外组件,纯靠提示词就能实现,是我推荐新手先跑通的第一版记忆方案。

3.2 长期记忆:什么时候才需要向量数据库和 RAG

短期记忆装不下所有东西,而人类用户希望 Agent 能长期记住自己的偏好、历史、项目背景,这就引出了长期记忆。很多人一提到长期记忆就条件反射想到向量数据库和 RAG,但我必须泼盆冷水:大部分早期项目根本不需要向量检索,简单存储完全够用。

RAG 解决的是一个更具体的问题:模型知识库之外的大量私有文档,如何按需检索。比如你有一个 Agent 需要回答公司内部规章制度问题,几千页文档不可能全塞进上下文,于是你把文档切成片段、做向量化、存进检索库,用户提问时先检索相关片段,再把片段塞给模型参考。这套逻辑的适用场景是“有大量静态文本需要按相关性查询”。

但如果你只是想让 Agent 记住“用户上次选了哪个城市、偏好什么风格”,一个简单的 JSON 文件、或者一个普通的数据表就够用了,不需要上向量库。我见过太多项目,一上来就搭了一套完整的向量检索,实际上长期记忆只有三条字段。存储选型不是越高级越好,而是越匹配越好。

如果确实要上 RAG,我提醒三个关键控点。第一是切块大小,块太大会夹带无关信息,块太小会丢失上下文连贯性,一般 300 到 500 字左右起步,按你的文档类型调。第二是检索数量,只取 top 3 到 top 5 的片段喂给模型,喂太多片段会稀释注意力。第三是引用溯源,让模型在回答里标注来源片段编号,否则出错时你连查都查不了。

3.3 记忆层的正确学习顺序

我建议的记忆层学习顺序是这样的:先不要碰任何存储组件,用“最近 N 轮 + 摘要”把记忆跑通;然后用一个最简单的键值存储(甚至是一个本地 JSON 文件)保存用户长期偏好;等你的量级到几千条、查询速度有明显瓶颈、或者你真的面临“文档知识问答”需求时,再上向量数据库。

这样看起来慢,但实际上每一步都在为下一步铺垫。很多人的悲剧在于第一步都没做扎实,就直接跳到 RAG,结果既不会压缩上下文,也不会设计检索流程,整条链路乱成一锅粥。

4. 规划与反思:让 Agent 自己拆解任务只是第一步

工具调用让你能执行单步操作,记忆让你能记住跨轮信息,但真实世界的任务往往是多步的。比如“帮我整理这份报告并生成 PPT 大纲”就至少包含:读取报告、提取要点、设计大纲结构、输出成指定格式。你当然可以手写代码把这些步骤串起来,但 Agent 的核心魅力在于:让模型自己规划步骤。

这一步的学习重点不是“怎么把步骤写完”,而是“怎么让模型在只有目标、没有明确步骤的情况下,自己拆解并执行”。业内最常见的思路是 ReAct:交替进行“推理”和“行动”,每一步行动之后观察结果,再调整下一步行动。

4.1 ReAct 模式到底在做什么

ReAct 这个名字来自 Reasoning + Acting,翻译过来就是“边想边做”。它的关键区别是:模型不只是一次性给出答案,而是在每一轮都输出“对当前状态的分析”和“下一个行动指令”。我给你一个简化版的执行轨迹,你就明白了:

  • 用户:“帮我查一下北京今天的天气,如果下雨就提醒我带伞。”
  • 模型思考:“用户想知道北京天气,并且需要判断是否需要带伞。我需要先调用天气查询工具。”
  • 模型动作:get_weather_by_city(city="北京")
  • 工具返回:{"city": "北京", "temperature": 16, "condition": "小雨"}
  • 模型思考:“天气是小雨,所以需要提醒用户带伞。”
  • 最终输出:“北京今天 16 度,有小雨,记得带伞。”

这个轨迹里,“思考”和“动作”是交替出现的。当你把这段思考轨迹展示给用户看时,Agent 的行为就会显得很“聪明”。更重要的是,如果某一步工具返回的结果和预期不一致,模型能在下一步思考中修正计划,而不是按死板脚本继续往下走。

4.2 三种规划策略的取舍对比

并不是所有任务都适合用 ReAct。我在实际项目中一般把规划方式分成三档,按任务复杂度选择。

规划方式适用场景优点缺点
单轮工具调用用户只提一个明确操作快、省 token只解决单步任务
ReAct 循环多步操作,且步骤之间依赖强灵活、能及时纠错延迟高、token 消耗大
计划-执行(Plan-and-Execute)任务步骤多且相对独立先给出全局计划,执行更稳定计划错了后面容易跟着错

用大白话翻译:如果任务一句话就能说清,别套 ReAct,直接一步调用工具,省时间也省钱;如果任务需要三四步且每步依赖上一步的结果,ReAct 是自然选择;如果任务有七八步、你可以先让模型列个大纲给你看,再按大纲执行,那就用 Plan-and-Execute。

我个人的建议是,先死磕 ReAct,把它理解透,再去看 Plan-and-Execute。ReAct 的缺点是慢,但它给了模型最大的灵活性,是你理解 Agent 循环心智的最佳载体。

4.3 警惕“假规划”:漂亮的计划不等于能执行

新手很容易被一件事迷惑:模型能输出一份看起来非常合理的五步计划,但执行起来完全不是那么回事。我把这种情况叫做“假规划”。比如模型计划“第一步收集数据,第二步清洗数据,第三步建模”,听起来像模像样,但每一句都是空话,根本没有落到具体的工具调用上。

这个问题的根源是,模型在生成计划时,并没有真的在执行环境里验证每一步是否可行。它只是在“模仿”一份好计划的样子。解决办法有两个方向。

一个是 ReAct 模式下不要逼着模型一次性输出完整步骤,而是让它“走一步看一步”。每一步动作都必须对应一个真实存在的工具,如果模型想不到该调什么工具,它就必须停下来向你求助。这样“思考”始终和“动作”绑在一起,假规划就很难出现。

另一个是 Plan-and-Execute 模式下,把计划里每一步都写成一个可校验的中期产物。比如“清洗数据”写清楚“输出一份缺失值统计和清洗后的 CSV 文件路径”。有了可校验的中间产物,你就能发现某一步到底有没有完成。

我还会在规划类任务里加一个兜底机制:给模型一个“工具列表里没有合适工具时,如实说做不到”的指令。很多模型在用户需求超出能力范围时,会倾向于编造一个即将执行但实际不存在的动作。这一句话能把很多坑提前填上。

5. 上框架和工程化之前,先想清楚这几件事

当你已经亲手写过工具调用循环、做过基础记忆压缩、理解 ReAct 的运转方式之后,再去看市面上那些 Agent 编排框架,你会发现它们不再神秘。大部分框架做的事情,无非是把一个循环图可视化、抽象成节点和边,让你能以状态机的思维编排“什么时候执行什么节点、节点之间怎么流转”。

这也是我为什么一直坚持“先手写循环,再碰框架”。你亲手写过一遍,就能快速识别框架里哪些抽象是必要的,哪些是纯增加心智负担的。反过来,如果你从来没写过循环,你会把框架里的每一行都当作魔法去背,效率极低。

5.1 用状态机思维理解主流编排方式

我不打算在这里评价某个具体框架,因为这类迭代太快了,今天推荐的工具半年后可能就没人维护。但你要掌握的是它们背后的共同抽象:把 Agent 的每一次“思考 - 工具调用 - 观测结果”看成状态之间的转移。

一个常见的建模方式是:把所有流程画成图,节点是“要执行的动作”(比如调用某个大模型、执行某个工具、发给用户确认),边是“动作执行完之后往哪走”。你在自己的手写循环里用 if-else 实现的流程,在框架里就是图和边。理解了这一点,不管未来出现什么新框架,你上手都会很快,因为底层心智没有变。

5.2 可观测性:别让 Agent 变成一个黑盒

Agent 项目最容易踩的坑之一是黑盒运行。模型在一个循环里自己调用工具、自己总结、自己决定下一步,一旦结果不符合预期,你连“是哪一步出了问题”都不知道。所以我建议你在项目一开始就记录三样东西:每一步的模型输入输出、工具调用的参数和返回结果、每一步消耗的 token 数。

这些日志不是给用户看的,是给你自己排查用的。我见过一个排查案例,某开发者说“Agent 总在第三步乱调工具”,翻日志后才发现是第二步工具返回里多了一个历史遗留的旧字段,模型把这个字段理解成了新信息,才导致后面的一系列误判。如果没有日志,这个排查可能要猜上几个小时,有了日志,十分钟就定位了。

5.3 成本、延迟、失败重试的平衡

工程化最后绕不开的是成本和延迟。多步 ReAct 虽然聪明,但每一轮都要调用模型接口,调用次数一多,延迟累计起来非常可观,费用也会快速上升。我的经验是做三层控制。

第一层,控制最大迭代次数。给循环设一个硬上限,比如最多 5 次工具调用,超过就强制结束,输出“任务未完成,超出最大尝试次数”。这一条能防止模型在某些失败场景里无限循环。我见过最夸张的情况是模型反复调用一个一直报错的工具,连续调用十几次才停止,费用和体验都崩了。

第二层,调整模型选型。不是所有步骤都需要最强模型。规划和大局判断用能力强的模型,摘要、格式化、简单判断用便宜的小模型,能把平均成本降下来一截。代价是实现更复杂,建议中期再考虑。

第三层,设置兜底结果。工具失败时,返回一个结构化错误,让模型基于错误决定是重试、换方案还是放弃。这里不要放任模型无限重试,一般重试一次失败后就让模型如实向用户说明原因,并提出替代方案。

这三层控制看起来都是小事,但它们在线上环境里直接决定了你的 Agent 是“偶尔聪明的小玩具”还是“稳定可用的工具”。


我自己在实际项目中最大的体会是:构建 Agent 最难的不是某个单独环节,而是当所有环节串起来时,你能在脑海里构建一条完整的链路——输入进来,模型怎么理解,工具怎么执行,结果怎么回来,历史怎么记住,计划怎么调整。这个链路感的建立,靠的就是“先能力、后框架”的顺序。

如果你现在正处在“不知道下一步该学什么”的状态,我的建议很具体:这周把四道自测题做完,下周写一个极简工具调用循环,再下周给这个循环加一个“最近 N 轮 + 摘要”的短期记忆。三个星期之后,你再看那些满天飞的 Agent 开源项目和框架教程,眼里看到的将是骨架,而不是一堆新名词。

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

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

立即咨询