尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

大模型智能体从0到1:Agent循环、工具调用与简易项目实战

大模型智能体从0到1:Agent循环、工具调用与简易项目实战 大模型智能体Agent不是某个具体的产品而是一套让大模型从“能聊天”变成“能干活”的流程组合。很多人第一次接触这个概念时容易把它想复杂以为要上多深的架构、多重的框架。其实拆开来看一个简易智能体的核心就三件事给模型接上工具、让它自己决定怎么用、把结果一步步带回到对话里。这篇文章不堆概念我用一个能直接跑通的“查天气记待办”小项目把大模型智能体的简易流程完整过一遍。不管你是刚接触智能体开发还是准备在公司里做一个内部自动化小助手这套思路都够用而且每一步都能落到代码上。1. 先搞清楚一件事智能体和聊天机器人到底差在哪1.1 一个最简单的对比场景假设你问它“明天下午的会议要整理成纪要发给没参会的人顺便看看明天北京天气要不要带伞。”普通聊天机器人拿到这个问题只能基于训练数据里的旧知识“编”一个回答。它既不会去翻会议记录也不会去查实时天气更不会在纪要根据天气更新完之后主动提醒你带伞。它本质是“一句话预测下一句话”没有外部反馈也没有可执行的工具。你可以把它理解成一位记忆力很好、但手边没有任何资料和办公用品的实习生你说什么它都能接但什么都落不了地。智能体的处理方式完全不一样。它会先把任务拆开然后循环执行这么一串动作先判断“整理会议纪要”需要调用“获取会议记录”的工具于是去拿数据拿到后继续判断发现“要不要带伞”依赖实时天气于是再去调“天气查询”工具两次工具结果都拿到之后它才汇总成最终回答。整个过程里模型负责“想”外部函数负责“做”历史消息负责“记”。这就像你雇了一位真正的助理他接到任务后会自己分工、查资料、做记录最后给你一份完整结果。这也是为什么智能体被称为 Agent——它不再是被动等待输入的问答机器而是有目标、能拆解、会调用资源并主动推进任务的执行体。ChatGPT 那个对话框本身不是 Agent但把对话框外面接上工具、流程和记忆它就变成了 Agent。1.2 智能体的核心机制感知、决策、执行、反馈一个最简单的智能体运行起来就是一个“感知—决策—执行—反馈”的循环。感知阶段系统把用户输入、历史对话、系统提示词拼装成一个完整的上下文发给大模型。这个上下文决定了模型“看到”了什么也决定了它后续所有判断的边界。所以这一步往往也是调试时最先要检查的地方模型没有按要求做十有八九是上下文里给的信息不够或不对。决策阶段大模型根据当前上下文和候选工具列表决定下一步做什么。它有两种输出可能一种是直接给出最终回答说明它认为任务已经完成另一种是返回一个“工具调用请求”里面包含工具名称和按 JSON 格式填好的参数。注意模型并不是直接执行代码它只是“写了一张指令单”真正执行还在后面。执行阶段我们的程序收到这个工具调用请求后去调用真实的函数比如查天气的 HTTP 接口、写数据库的动作或者是任何内部系统 API。这一步是智能体连接真实世界的桥梁也是最容易出 bug 的地方因为模型生成的参数经常不完全符合函数签名。反馈阶段把工具执行结果以一条独立消息追加回对话历史然后带着更新后的上下文再次进入决策阶段。如果模型觉得还不够它会继续调用下一个工具如果觉得已经能回答就输出最终结果。整个过程不断循环直到模型给出最终答案或者达到我们设定的最大迭代次数。这个循环其实很像我们做饭的流程想好做红烧肉这是一个决策打开冰箱看有没有五花肉这是一个工具调用发现没有再决定出门买菜这是根据工具结果做的新决策买回来之后再次确认材料齐了才开始下锅。智能体干活的逻辑完全一样只是这个“做饭的人”是大模型而“冰箱、菜市场”被封装成一个个函数。2. 动手前的三选模型、框架和知识库怎么定2.1 模型怎么选本地部署还是调 API搭建智能体首先要面对的问题就是让哪个大模型来当“大脑”。选择无非两大类调用现成的 API 服务或者本地部署开源模型。调用 API 的好处是模型能力强、接入方便、也不需要自己维护算力适合快速验证和业务上线初期的方案。劣势主要有两点一是数据会发送到服务商一侧很多企业内部数据不允许出域二是当请求量上来之后token 费用会成为一笔必须认真核算的运营成本。本地部署则正好相反。数据全程留在自己的机器上隐私可控固定成本一次投入长期跑高频任务反而更划算。代价是效果上限通常低于商业大模型而且你得有一块像样的 GPU或者愿意接受 CPU 推理时“一个字一个字往外蹦”的慢速体验。如果还没想清楚我建议按这个标准选原型验证优先用 API 方案先跑通业务逻辑如果涉及客户隐私、财务数据、内部制度文档或者需要在内网环境长期运行再考虑本地部署。本地部署最简单省事的方式就是 Ollama直接把模型拉下来跑ollama pull qwen2.5:7b ollama serve我自己实测下来qwen2.5:7b 这类 7B 级别模型做工具调用已经够用简单的天气查询、待办记录、信息抽取都没问题。它的响应速度在消费级显卡上大概每秒十几个 token做内部小工具完全 OK。2.2 框架怎么选自研、LangChain 还是 Dify 这类平台第二个选择是我要不要用框架市面上的 LangChain、LangGraph、Dify、Coze 等工具广告铺天盖地但选型之前最好先想清楚自己到底需要多少“脚手架”。自研一个简易 Agent 循环技术含量其实不高核心代码一百行以内就能写完。好处是每一行逻辑都在你掌控之下出了问题直接看代码不需要去翻框架的抽象和源码。坏处是人类的力量是有限的一旦要做到多智能体协作、复杂状态机、人机审核节点自研的维护成本会快速上升。使用 LangChain、LangGraph 这类开发框架优势在于组件丰富、抽象层级高复杂编排可以少写很多胶水代码。但我踩过最大的坑是“框架黑盒化”报错信息经常要追到三层依赖之外改一个 prompt 可能因为缓存或 chain 的拼接方式不对而毫无效果。所以我个人建议你的第一个 Agent 不要用框架先把裸循环写通体会到“模型调用工具”这件事的本质之后再决定要不要引入框架来提升效率。低代码平台则是另一个极端。Dify、Coze 这类平台把工具节点、知识库、工作流都做成了可视化卡片业务同学拖拖拽拽就能搭出能用的助手非常适合快速验证产品和给业务方看 demo。缺点也很明显系统的逻辑被平台框住了当你想实现一些特殊控制流、或者对接内部系统的长链路认证时会很不顺手。表格对比会更直观方案灵活性上手成本适合场景自研循环最高中等需要写代码学习、可控性要求高、轻量需求开发框架中高较高概念多多智能体、复杂状态、长期项目低代码平台低最低业务自助、快速验证、知识库问答2.3 要不要上 RAG知识库不是标配但有场景就值得加RAG检索增强生成解决的是“模型不懂你们公司的私有知识”这个问题而 Agent 解决的是“模型不能操作外部工具”的问题。两者是独立的维度不一定要绑定。很多人一上来就搭向量库、搞知识库做了半个月才发现核心的工具调用链路还没跑通业务方连个 demo 都看不到。我自己的经验是先做 Agent 工具调用让模型能真正“操作”业务动作再根据需求加知识库。如果业务确实需要回答大量内部制度问题比如员工问“报销单怎么填”这时候再把制度文档切块、做 embedding、塞进检索器让 Agent 在回答前先查文档。简易场景下完全不引入复杂向量数据库也可以。文档量小的时候用关键词检索甚至直接命中也能满足需求文档量大、需要语义检索的时候再上向量库。RAG 的本质是“给模型递小抄”小抄怎么准备不是最关键的关键是小抄里的内容必须准确、时效性要够。有了准确的知识增强Agent 的回答才能从“胡编”变成“有理有据”。3. 从零搭建一个“知天气、能记账”的简易 Agent3.1 先拆解需求画一条 Agent 工作流我用一个具体的需求来搭建用户输入“帮我看看明天北京天气要是不下雨就把‘准备户外团建’记到待办本上”。我们的 Agent 需要完成以下流程接收用户输入识别出两个意图查询天气、记录待办。模型决策优先调用 get_weather 工具获取北京明天天气。程序执行 get_weather拿到天气结果。将结果追加到对话历史再次请求模型。模型根据天气结果继续决策如果不下雨则调用 add_todo 工具。程序执行 add_todo写入待办。模型汇总工具结果输出最终回答。这一步的目标不是画出一张漂亮的架构图而是明确两件事哪些动作需要真实的外部系统去执行哪些信息需要在多轮对话之间被记住。拿一张纸把这几个环节写下来远比直接写代码重要。因为后面排查问题的时候你会发现 80% 的 bug 都出在“我以为模型会先做 A 再做 B”而实际模型在第一步就给出了最终回答。3.2 配置模型接口把业务动作封装成函数这个项目的模型后端我选择本地 Ollama用它的原生 API 接口。你不需要部署额外服务只要确认 Ollama 在跑模型已经 pull 下来即可。然后写一个统一的对话函数方便后续调用import requests def chat(messages, toolsNone): payload { model: qwen2.5:7b, messages: messages, stream: False, } if tools: payload[tools] tools resp requests.post(http://localhost:11434/api/chat, jsonpayload) return resp.json()接下来是业务动作的封装。这一步很关键模型能不能正确调用工具很大程度取决于你给它的“工具说明书”写得好不好。我们定义两个工具函数一个查天气一个写待办def get_weather(city: str) - dict: fake_db {北京: 晴25°C适合户外, 上海: 小雨22°C记得带伞} return {city: city, weather: fake_db.get(city, 未知)} def add_todo(item: str) - dict: # 实际项目中这里写数据库 insert 操作 return {status: ok, item: item}为了让模型知道在什么情况下调用什么函数我们需要把这些函数转换成一个结构化 JSON Schema。描述文字要尽量写清楚因为模型是靠描述来“理解”工具能力的。里面字段写得不严谨后面一定会踩参数缺失的坑TOOLS [ { type: function, function: { name: get_weather, description: 查询某个城市第二天的天气情况, parameters: { type: object, properties: { city: { type: string, description: 城市名例如北京 } }, required: [city] } } }, { type: function, function: { name: add_todo, description: 把一条待办事项写入待办本, parameters: { type: object, properties: { item: { type: string, description: 待办内容 } }, required: [item] } } } ]这里要特别提醒一个新手常犯的错误模型不是真正理解代码它只是在工具描述里找关键词然后猜哪个函数能解决当前问题。所以描述里最好直接包含触发场景比如“用户问天气”“用户要求记录”这些词。描述越具体调用准确率越高。3.3 写一个 Agent 循环让模型学会用工具工具和模型接口都准备好了接下来就是核心部分——Agent 循环。它会不断问模型“接下来干什么”如果模型说要调工具就去执行再把结果塞回上下文直到模型说“我搞定了”。import json def execute_tool(name: str, args: dict): if name get_weather: return get_weather(**args) if name add_todo: return add_todo(**args) raise ValueError(funknown tool: {name}) def run_agent(user_input, max_iter5): messages [{role: user, content: user_input}] for step in range(max_iter): response chat(messages, toolsTOOLS) message response.get(message, {}) # 如果模型没有返回工具调用说明任务已结束直接输出最终回答 if not message.get(tool_calls): return message.get(content, ) # 把模型这条带工具调用的消息加入历史保证上下文连续 messages.append(message) # 逐个执行模型要求调用的工具 for call in message[tool_calls]: fn call.get(function, {}) name fn.get(name) args json.loads(fn.get(arguments) or {}) result execute_tool(name, args) print(f[step {step 1}] 调用了 {name}结果{result}) # 把工具结果作为一条独立消息追加回去 messages.append({ role: tool, tool_call_id: call.get(id), content: json.dumps(result, ensure_asciiFalse) }) return 已达到最大迭代次数流程未完成。这段代码就是智能体的最小骨架。你注意几个关键点第一max_iter5是一道保险防止模型在某个分支上来回调用工具停不下来。实际业务里如果任务步骤很多可以设大一点但一定不能没有上限。第二role: tool的那条消息是模型看到工具结果的唯一途径。如果不把结果追加回messages模型在下一轮决策时完全不知道刚才工具执行得怎么样就会出现“明明查到了晴天回答里却说下雨”这种精分现场。第三如果你的模型一次返回多个tool_calls循环里会逐个执行、逐个添加结果。这种并行调用在处理“查多个城市天气对比”时特别有用能省掉好几轮交互。跑一下这段代码输入我们的测试需求控制台会输出类似这样的日志[step 1] 调用了 get_weather结果{city: 北京, weather: 晴25°C适合户外} [step 2] 调用了 add_todo结果{status: ok, item: 准备户外团建}最终模型的回答可能是“北京明天晴天25°C适合户外活动。我已经帮你把‘准备户外团建’记到待办本里了。”到这里一个简易智能体已经能自己决定“什么时候查、什么时候记、什么时候结束”了。核心的“模型调用工具”机制已经被你亲手实现了。3.4 给 Agent 加记忆多轮对话不乱套上面的例子是单轮对话真实场景里用户往往会连续说好几句话“帮我看看北京天气”“对了把明天下午的会议纪要先存一下”“还有周三要下雨的话提醒我带伞”。问题来了第二次和第三次提问时模型怎么知道“你”是谁、前一轮已经做了什么最简单的方式就是让messages变量在会话内一直保留。每次用户新输入就 append 一条role: user的消息然后重新进入 Agent 循环。这样模型天然拥有“短时记忆”它能看到这个会话里所有的历史对话和工具调用记录。但无限保留历史会带来 token 膨胀。本地模型上下文窗口有限对话一长要么报错要么模型“忘”掉最开始的内容。我常用的做法是加一个窗口截断只保留最近 N 条消息系统提示词和最新一条用户输入永远保留。代码很简单def trim_messages(messages, max_len10): head [m for m in messages if m.get(role) system] tail messages[-max_len:] return head tail如果业务里需要跨会话长期记忆比如记住用户偏好“只关心北京的天气”那就需要把关键信息抽出来存进数据库或向量库。这一步属于工程化增强简易版完全可以先不做。但有一条经验你现在就值得记住多轮对话最容易出的问题是模型把上一轮的“工具执行结果”误当成这轮的用户指令。所以工具结果消息一定要固定用role: tool并在内容前面加上“工具执行结果:”这类清晰前缀让模型分清楚哪些是“别人说的话”哪些是“代码返回的数据”。4. 运行调试与效果调优踩过的坑一次说清4.1 最常见的四类故障和排查顺序我实测跑 Agent 项目遇到最多的问题可以归成四类基本覆盖了新手期 90% 的报错。故障现象排查方向模型不调用工具直接给答案检查 request 里有没有传 tools、模型是否支持 function calling、工具描述是否清晰工具调用参数缺字段或 JSON 解析失败看模型返回的 arguments 结构、工具 schema 中 required 是否设置、默认值是否缺失循环一直不结束或反复调同一个工具看工具返回结果是不是让模型误判为“还没完成”、max_iter 是否太小、工具是否抛异常回答结果和工具返回矛盾看工具结果是否成功追加到 messages、系统提示词有没有要求“以工具结果为准”排查顺序我建议固定成一条线先看日志里模型到底输出了什么再确认工具是否真的被执行最后检查工具结果是否被正确带回了上下文。很多人一上来就改 prompt结果发现是 tools 参数根本没传上去白折腾半天。日志是整个调试过程里最重要的东西。我自己的习惯是每一步运行都打印出完整的message和tool_calls原始结构同时把每次对话存成一个 JSONL 文件方便事后复盘。别嫌麻烦模型输出有时候就是“这次好下次坏”没有日志基本没法定位。4.2 为什么模型总是不调用工具这个问题几乎每个刚入门的人都会遇到。你明明把工具名写得清清楚楚模型却一本正经地凭空回答“北京明天晴”。我先说结论多半不是模型笨而是你让它“看不到”或“不想用”工具。“看不到”有两种情况。一种是本地模型本身不支持 function calling像早期的纯对话模型你就是把 tools 传上天它也会无视。解决办法是换一个明确支持工具调用的模型比如 qwen2.5、glm4 系列或者直接用大厂的函数调用 API。另一种是代码里忘了传 tools 参数或者传了但格式不对。这种问题一查日志就能看到请求体里如果没有tools: [...]这个字段那就别怪模型乱来。“不想用”则通常是工具描述写得不够有引导性。比如你把get_weather描述成“获取天气信息”模型可能觉得用户只是随便问问不值得动用工具。我会在描述里加上触发条件“当用户询问某个城市当前或未来的天气情况时必须调用此工具。”同时在系统提示词里加一句“需要实时信息或执行动作时你必须调用工具不能凭空编造结果。”这一句话能把调用率显着拉上来。还有一种情况是用户输入过于含糊。比如“帮我看看明天要带伞吗”如果工具描述里没有“带伞”相关的词模型可能不知道要去查天气。遇到这种情况要么优化描述要么在入口处先做一轮意图改写把用户输入补全成“查询北京明天是否下雨”再传给 Agent。4.3 从“能跑”到“好用”提示词、模型参数和日志三板斧第一个 Agent 跑通之后你别急着往上加花活先把三件事做好体验会立刻不一样。第一系统提示词。我给业务 Agent 写的 system prompt 通常包含四块角色定义、工作原则、工具使用规则、输出格式要求。工作原则里一定要写“所有事实必须来自工具返回结果工具没有返回的信息不得编造”。输出格式可以要求“结论先行然后附上关键信息”。这能明显减少模型自由发挥的空间。第二模型参数。稳定优先temperature调到 0 到 0.3 之间太高模型会发挥不稳定max_tokens要留够避免工具调用结果还没生成完就被截断top_p保持默认或配合 temperature 一起调低。参数不是越高越好Agent 场景里我们追求的是可复现不是文采。第三运行日志。每轮调用都记录输入、输出、耗时和 token 消耗。我之前排查一个“Agent 频繁调用无用工具”的问题就是靠日志发现模型在某轮把历史里的城市名反复提取出来每次都要查一遍天气白白浪费了好几次调用。后来在系统提示词里加了“如果城市信息没有变化不要重复查询”问题立刻消失。等你把这三板斧都用上这个简易 Agent 就已经算是“可用”状态了。后面再根据业务需要去加更多工具、接知识库、做多智能体协同都是在这个骨架上做加法。每一步都不难难的是沉住气把循环逻辑理清楚把日志看仔细。最后再分享一个小经验我自己做 Agent 项目第一版从来不用框架先用几十行循环把“模型会调工具”这件事跑通让业务方亲眼看到效果然后才回头补记忆、权限、审计这些工程细节。因为 Agent 最大的变数从来不是框架而是模型在具体提示词和工具描述下的表现这种东西只有放到真实场景里跑一遍才能暴露出来。你把上面的代码改成自己的业务函数跑完第一个 Agent 之后大概率会收到两种反馈一种是“它居然会自己去查数据了”另一种是“它这次怎么又没调用工具”。这两种反馈恰恰就是继续优化的起点。
返回列表