尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

从零构建Agent:工具调用、记忆管理与完整实战指南

从零构建Agent:工具调用、记忆管理与完整实战指南 大概两周前我在一个内部工具项目里被逼着做了一件事——从零开始构建一个Agent。当时我对“Agent”这个词的理解还很模糊以为它就是个“能聊天的机器人”结果真上手才发现这东西与其说像聊天机器人不如说像一套完整的自动化业务流程。它要自己判断做什么、决定什么时候调工具、把工具结果拿回来继续推理甚至还要记住前面说过的话。整个过程踩了不少坑也摸出了一些门道今天就把从零开始构建Agent的完整过程整理出来希望给同样准备入门的你一条更顺的路。这篇文章适合已经有基本编程经验、但对Agent开发还比较陌生的朋友。它会带着你从概念到实操把一个能真正干活的Agent搭起来——不是调了个接口就完事而是包括工具调用、记忆管理和完整的业务案例。1. 先搞清楚Agent到底是什么一个能自己调用工具的流程系统1.1 模型是实习生Agent是完整业务流程很多人第一次接触Agent时都会犯一个认知上的偏差觉得Agent就是大模型本身。这个理解不能算全错但会严重影响后面所有技术决策。如果你只是想让AI回答“今天天气怎么样”那直接调用模型就行根本不需要Agent。Agent的价值在于它能从一句模糊指令开始自己规划步骤、调用外部能力、消化返回结果最后完成一个需要多环节协作的任务。我用一个不太严谨但很好用的类比来理解这件事大模型是一个能力很强但毫无职场的实习生他知道很多知识但你不告诉他具体流程他不会主动去查数据库、调接口、发邮件。Agent则是把这名实习生放进一套完整业务流程里给他配好工具定好规则让他遇事自己判断、自己动手、做完向你汇报。所以Agent背后真正的工程量不在模型而在模型外面那套流程系统。1.2 一个最小Agent必须具备的四样东西从零构建Agent首先要知道它的骨架由哪些部分组成。经过拆解我认为一个最小可用的Agent至少需要四样东西。第一是模型核心也就是负责推理的那个大模型。它承担了理解用户意图、拆解任务、决定调用什么工具的职责。你可以把它理解为整个系统的大脑但这颗大脑务必选一个支持工具调用的模型原因后面详细说。第二是工具集也就是能让Agent去操作外部世界的一系列函数。查天气、写文件、调数据库、发HTTP请求都可以是Agent的工具。没有工具的Agent只是一个空谈家有了工具的Agent才是一个执行者。第三是提示词它告诉Agent“你是一个什么角色”“你有哪些工具”“遇到什么情况该怎么做”。很多新手忽略提示词的分量实际上Agent的行为边界几乎全靠它约束系统提示词写得差后续的一切都容易失控。第四是运行循环这是一个Agent和普通“调模型API”最大的区别所在。运行循环负责维持一轮又一轮的“模型思考—调用工具—返回结果—再思考”过程直到模型认为任务完成、决定给出最终回答为止。这四个零件缺一不可构建Agent的过程本质上就是在打磨这四样东西以及处理它们之间的协作关系。2. 动手前的三件套模型接口、编排框架、运行环境2.1 模型接口为什么必须选支持工具调用的版本准备工作不能从写代码开始而是先从选型开始。最容易被忽视的选型决策是模型接口。市面上大部分对话模型API都分两类一类是纯文本问答接口你发一段话它回一段话另一类是支持工具调用业内通常叫function calling的接口模型回答时不仅会给你文本还会给你一个结构化的“我想调用某个工具参数是这些”的请求。构建Agent一定要选第二类接口。原因是Agent的核心机制在于“让模型自主决定何时调用工具”如果模型只能输出文本你就得自己写解析器去拆词费劲且极不稳定如果模型原生支持工具调用它会以结构化JSON返回意图你只需要把JSON解析出来去执行对应函数就行。这一步的投资回报率极高我建议把“支持工具调用”设为模型选型的硬性门槛而不是加分项。以我这次用的模型为例它提供两种返回方式一种是正常文本内容另一种是包含tool和args字段的工具调用请求。下面这段伪代码展示了工具调用的结构化返回是什么样子# 这是支持工具调用的模型可能返回的内容 { need_tool: True, tool: get_weather, args: { city: 北京 } }收到这个返回后Agent框架要做的就是去执行get_weather(北京)然后把结果拼进下一轮对话再交给模型继续推理。这个“模型要求调用→系统执行→回填结果”的往返机制就是整个Agent的运转发动机。2.2 编排框架手写太痛苦但要理解它的定位模型选好之后紧接着面临的问题是要不要用编排框架我在一开始坚持手写了一个极简循环奔着“搞清楚原理不吃别人嚼过的馍”去但一旦任务变复杂比如要维护多轮对话历史、处理并行工具调用、加记忆、做会话管理手写代码的维护成本立刻上来了。后来我切换到一个开源编排框架从开发效率来说这个选择非常值得。这类框架的定位是把你从“处理每轮对话的胶水代码”里解放出来让你专注于工具定义和业务逻辑。我把这个框架临时叫作AgentBase它的核心编码思路很典型注册工具、配置模型、运行Agent。你只需要将工具函数注册进去AgentBase会自动处理模型返回的工具调用请求。这让我理解了框架的真正价值它不是在替你思考而是把“执行工具调用循环”这个重复劳动自动化。2.3 运行环境与密钥管理最后是运行环境这里有一个新手容易翻车的细节API密钥管理。有人会把密钥直接硬编码在代码里然后一不小心把代码传到公开平台等于把密钥公之于众。正确做法是放进环境变量文件并在代码里通过环境变量读取。这一步虽然是常识但在实际项目里总能看到各种泄漏事故。另外Python环境务必使用虚拟环境管理依赖别把Agent项目依赖和系统全局依赖混在一起。我用一个最小依赖清单来组织项目模型客户端、编排框架、环境变量读取库、其他业务需要的库。依赖越多意味着出问题的可能越大能精简就精简。3. 第一个跑通的Agent两条分支组成的工具调用循环3.1 工具调用的完整循环一次对话是怎么走完的先来看一次完整的Agent交互是什么样的这能帮你建立直觉。假设用户说“北京今天需要穿外套吗”Agent内部会经历这些步骤第一轮将系统提示词、历史对话、用户问题一起送给模型模型判断“我需要查询北京天气”返回一个结构化工具调用请求系统收到请求后执行get_weather(北京)拿到“晴12°C”把这个结果作为一条消息追加到对话里再送回模型模型看到天气数据后生成最终回答“北京今天晴12°C建议穿外套。”整个过程看起来简单但它揭示了Agent设计的一个核心原则工具执行的结果必须回到模型中让模型基于最新信息继续推理。如果工具执行完就中断流程、直接把结果返回用户那Agent就不是Agent了只是一个披着Agent外壳的普通函数调用。很多人构建第一个Agent失败就是因为把这个循环掐断了一半。3.2 从零手写一个最小工具调用循环为了让你彻底弄懂这个机制我用Python写了一个最简版本的工具调用循环。这个版本省掉了框架把核心逻辑暴露无遗import json # 系统提示词向模型声明工具的存在和使用规则 SYSTEM_PROMPT 你是一个实用助手。当用户的问题需要实时数据时 请调用工具获取信息。你目前拥有的工具 - get_weather(city: str): 查询指定城市的当前天气 调用格式形如{tool: get_weather, args: {city: 北京}} 如果不需要调用工具直接回答用户即可。 # 工具函数本体 def get_weather(city): # 实际项目中这里会请求真实天气服务此处用静态数据演示 weather_map {北京: 晴12°C, 上海: 小雨16°C} return weather_map.get(city, 暂无该城市的数据) # 最简Agent主循环 def run_agent(user_input, api_client): messages [ {role: system, content: SYSTEM_PROMPT}, {role: user, content: user_input} ] # 第一轮让模型判断是否需要工具 first api_client.chat(messages, temperature0.2) content first[message][content] # 分支一模型选择调用工具 if first[need_tool]: tool_name content[tool] args content[args] if tool_name get_weather: result get_weather(args[city]) # 将工具结果追加到对话中 messages.append({role: tool, content: result}) # 第二轮让模型基于工具结果生成回答 second api_client.chat(messages, temperature0.2) return second[message][content] # 分支二模型直接回答 return content # 使用示例 if __name__ __main__: answer run_agent(北京需要穿外套吗, model_client) print(Agent:, answer)你可以看到整个流程就是上面说的两条分支。实际工程中工具会有很多个循环也会因为“模型连续调用多个工具”而多走几轮但骨架始终是这个样子。你动手实践时建议先跑通这样一个小Demo再考虑引入框架。3.3 为什么要把执行权交给模型一开始我很排斥“把执行权交给模型”这件事总觉得不可控。直到我做完这个Demo才明白Agent的自主性恰恰是这个系统最珍贵的价值。用户不需要知道有哪些工具、参数怎么传只要用自然语言说出目标模型负责翻译成行动。这其实就是Agent产品体验的核心把背后的复杂度全部藏起来只留给用户一句“告诉它你想要的”。同时这种设计也带来了一个必须面对的代价——模型的判断可能出错。所以后续所有工程手段比如收紧提示词、限制最大迭代轮数、加人工复核环节本质上都是在给模型的自由判断套上合理的护栏。4. 给Agent装记忆三层记忆结构哪里需要哪里加4.1 无状态Agent的尴尬聊完就忘第一个Demo跑通后我很快发现了新问题Agent是一问一答式的它记不住你前面说过的话。你让它“帮我记录一下明天的会议”它可以回答“好的”但下一轮你问“我明天有什么安排”它就完全不知道了。这种无状态的设计在单轮任务里没问题一旦涉及连续对话或跨会话任务就完全不够用。这让我意识到一件事Agent跟人一样记忆不是可有可无的增强功能而是支撑连续推理的基础设施。没有记忆的Agent每轮对话都相当于一个新人从头开始很多本来可以承接上下文的任务根本做不了。4.2 三层记忆短期、中期与长期后来我查阅了一些开源Agent框架的实现思路发现它们普遍把记忆分成三层来处理。短期记忆简单说就是当前对话上下文也就是把之前的对话原封不动拼进下一轮的messages里。这是最朴素、也最有效的方式实现成本几乎为零但受限于模型上下文长度不可能无限拼下去。中期记忆针对上面那个“总结前文再继续”的需求。常见做法是滑动窗口只保留最近的若干轮对话更早的内容则丢给模型生成一段摘要把摘要作为历史信息继续保留。这样既能延续上下文又把token占用控制住。长期记忆则把重要信息存到外部存储里最常见的是向量数据库。每次对话结束后把对话内容向量化存起来新对话开始时先根据用户问题做向量相似度检索把相关的旧记录找出来塞进提示词。这相当于给Agent配了一个“长期档案柜”它不用记住所有事但需要时知道去哪里查。记忆层级实现方式优点缺点适合场景短期记忆保存最近几轮对话实现简单、效果直接受上下文长度限制普通连续对话中期记忆滑动窗口摘要压缩节省token、保留主线会丢细节长会话任务长期记忆向量数据库检索跨会话持久保存需要额外基础设施个人助理、知识库问答4.3 记忆不是越多越好上下文预算的取舍加记忆时最容易犯的错是“什么都想留”。我把用户每一句话都塞进向量库结果检索时经常把无关旧记录也翻出来不仅占用上下文还会干扰模型判断。后来我学到一个关键策略记忆生成时就要做取舍只保存对后续任务可能有用的信息而不是一切对话记录。比如“用户夸了Agent一句”这种内容不值得占用上下文预算。实践时我会给每条记忆附带一个“重要性”标签后面检索时按优先级过滤。这个细节不复杂但对Agent的行为质量改善非常明显。你可以把上下文想象成一个有限的行李箱记忆是里面的衣物出发前必须精挑细选不然什么都往里塞到关键时刻箱子就装不下真正有用的东西了。5. 完整案例用一个工单分类Agent串起全部概念5.1 业务背景与需求拆解概念讲完用一个我实际做过的完整案例把这些知识点串起来。当时团队需要一个内部小工具每天收到大量用户反馈希望让Agent先自动把工单分成几类判断紧急程度把高优先级工单升级人工处理。这个需求听起来简单但拆解之后发现它背后藏着三条要求一是分类准确不同类型之间的边界要清晰二是能调用现有内部接口把升级操作真正落地三是整个过程要可说明让人工复核时知道Agent为什么这么判断。5.2 工具设计少而明确的优先级最高做Agent设计时第一反应往往是“给Agent尽量多的能力”让它什么都能干。实际上我在这次项目中得到的经验恰恰相反工具越少Agent越可靠。工具一多模型就频繁出现选错工具的情况。设计阶段可以把候选工具列得多一点但最终落地上线时我建议只保留最少必要的一个或两个。这个工单Agent最终只保留了三个工具一个是把工单标记为“已分类”并写入分类字段一个是把高优工单升级给特定人工队列还有一个是查询分类规则供Agent在不确定时查阅。看起来第三个工具有点多余但它的价值在于Agent实在拿不准时它会主动去查规则文档而不是硬编一个分类。5.3 提示词设计把判定规则写进工具描述在这次工单分类Agent里真正决定分类质量的是工具描述怎么写。我踩过几次坑后总结出一个模式每个工具描述要写清楚“这个工具是干什么的、什么时候该调用、什么时候不该调用”。尤其“什么时候不该调用”很多人会漏掉结果模型把不该升级的工单也升级了。以升级工具为例我在原框架基础上做了这样的修正不把“紧急”的判断标准留给模型自由发挥而是直接把判定清单写进提示词明确列出哪些情况必须升级。这相当于把业务规则嵌入到Agent的推理边界里而不是寄希望于模型天生理解你们团队的工单制度。5.4 小规模评估先跑20条样本再上线很多人做完Agent第一件事是上线我强烈建议先做小规模评估。我当时的做法是从历史工单里抽了20条已分类样本先不看原有答案让Agent盲跑一遍再跟人工结果对照。这个测试跑下来准确率比预想的低一截错误集中在“紧急”和“重要”两个概念的混淆上。用户说“这个问题很严重”并不代表“需要立即升级”模型困惑的正是这一点。针对这个发现我把升级工具的调用条件从“紧急”改成“明确影响收入或用户安全问题”并给Agent增加一步“先判断影响范围再决定是否升级”。这个改动虽然没有改变代码架构却大幅提升了准确率。评估不是走过场它是Agent迭代的核心手段。6. 我从零跑通后踩过的坑五条排查链路与经验清单6.1 坑一模型频繁选错工具这个问题在给Agent添加第一个工具时最容易出现。现象是用户要求查询天气模型却调用了日历工具或者根本没有调用任何工具直接甩一句“我无法获取实时信息”。排查链路是先检查工具描述是否足够明确尤其是参数名的表述要让模型一看就懂再检查系统提示词里有没有写清楚“先判断优先级再调用工具”最后检查是不是模型版本本身不支持工具调用有些人用的虽然是一个大牌模型但选成纯文本接口返回的自然没结构。这个坑让我得出一个经验工具描述宁可啰嗦也不要简写给模型的信息不够它就会发挥想象力。6.2 坑二Agent陷入死循环运行Agent时会出现模型不断要求调用同一个工具、拿到结果后又问一样问题的情况白白浪费大量token。排查后我发现根因是提示词里缺少“终止条件”模型不知道任务什么时候算完成。解决办法是在系统提示词里显式写入“如果你已经得到了满足用户问题的答案请立即停止调用工具并直接回答”同时在代码里加上最大迭代轮数的硬限制。两者结合之后死循环基本消失。这也是我建议所有从零构建Agent的人一开始就加上的护栏。6.3 坑三工具返回结果过大导致截断有一次Agent需要调用一个内部数据分析接口函数返回了5000多字符的JSON。调整上下文后模型回答变得前言不搭后语后来发现是工具返回结果太大把有效上下文挤爆了。排查时我用token计数工具一看光是工具返回就占掉了大半预算。解决办法是给工具输出做摘要化只从完整返回值里提取关键字段再回填给模型。记住一个原则模型需要的是“关键事实”不是完整数据别把原始大结果原封不动塞进循环里。6.4 坑四温度参数没有分开调有人会用同一套参数来调Agent的所有环节结果负责调用工具判断的环节也用了过高的温度导致模型判断飘忽不定。工具判断这种环节我是用0.1到0.2之间的低温让它尽量稳定而最终生成回答的环节可以适当提高一点让语言更自然。在同一个Agent里给不同节点分配不同参数效果会比一刀切好很多。6.5 坑五测试正常上线失灵的隐形污染本地测试一切正常上线后输入同样一句话结果不同了。这个坑当时排查了很久最后发现原因是测试环境与线上环境共用了同一个对话历史存储线上出现过的几条旧消息被当成了上下文导致模型行为偏移。从那之后我养成一个习惯每次评估前都清理上下文缓存并且明确区分测试会话与生产会话。Agent的状态性既是功能也是风险你对它越克制系统就越稳。从零到能干活再到稳定可用整个过程中我学到最重要的一件事是Agent开发不是纯粹的模型调优也不是纯粹的业务编码而是两者之间一种高密度的交织。模型负责理解与推理你要负责划定边界、提供工具、定义规则和建立评估手段。技术演进很快框架换了一茬又一茬但工具调用循环、记忆分层、护栏机制这些底层思路短时间内不会变。最后再分享一个小技巧给工具起名字时宁可多写几个词也不用缩写或代号。我试过用一个叫“execute_ticket_op”的工具结果模型始终没搞懂它跟“classify_ticket”有什么区别改成“escalate_ticket_to_human_team”之后一次就调对了。模型不认识缩写它只认描述够不够清楚。这个细节不起眼却让我省下了大量调试时间。
返回列表