资讯详情

从零构建智能体:ReAct模式、工具调用与工程落地实践

📅 2026/10/21 3:30:00 | 华诺云谱 👁 阅读
从零构建智能体:ReAct模式、工具调用与工程落地实践
1. 从会聊天到会干活智能体到底补齐了什么最近圈子里到处都在聊智能体好像不搞个Agent都不好意思说自己做AI。我也跟很多人一样一开始觉得智能体无非就是给大模型套一层提示词让它多调用几个接口。直到我自己把一个只会在对话框里回答问题的大模型变成一个能自己规划步骤、调用工具、完成实际任务的系统时才真正意识到智能体不是Plus版聊天机器人而是软件交互范式的变化。这篇是初识智能体系列的第一篇我会从最朴素的问题出发智能体到底解决了传统程序的什么痛点它跟你手机上那些语音助手有什么本质区别如果你想动手我会用一套不依赖任何重型框架的思路带你从零搭一个能完成查询信息执行动作的小智能体同时把我在真实项目里踩过的坑一并交代清楚。1.1 大模型时代的应用缺的不只是一张嘴传统软件的逻辑是你输入我处理你输出。哪怕用了AI常见做法也是把用户问题丢给大模型得到一段文字回复。这种模式的问题很明显模型只会说不会做。你问它帮我订个明天早上的闹钟它可能给你写一段如何设置闹钟的教程但不会真的打开你的日历应用。智能体的核心差异在于它把理解任务和执行任务连成了一条可以循环的链路。语言不再是终点而是行动的一个中间环节。模型可以生成指令、选择工具、读取结果、判断下一步直到任务完成。这就像你请了一个实习生你不会只让他复述你的需求而是期望他拆解任务、去查资料、执行操作、回来汇报。我自己在做一个内部信息查询系统的时候体会特别深。原来直接抛给大模型问题它要么给出泛泛而谈的答案要么编造一个不存在的数据。后来我把公司内部数据库、文档库、工单系统都封装成工具让模型在回答问题前先查一查再用获取到的真实数据回答效果立刻不一样了。这个查一查的动作就是智能体的雏形。1.2 把任务拆成动作智能体的最小闭环那智能体工作的最小闭环是什么我的理解是四个环节理解目标 → 拆解步骤 → 调用工具 → 评估结果。这四个环节不停循环直到任务达成或确认无法达成。举一个生活化的例子。假设你让智能体帮我安排一个明天上午的技术分享会要订会议室、发通知、准备投影。一个合格的智能体会这样工作理解目标拆出三个子任务——订会议室、发通知、准备投影设备。拆解步骤先查哪个会议室空闲调会议室系统接口再创建日历邀请调日历接口同时向参会人发消息调消息接口最后检查投影设备状态调设备管理系统。调用工具每一步都调用对应的API工具返回成功或失败。评估结果如果某一步失败比如会议室被占它会尝试换一个会议室或者返回给用户一个替代方案。这个闭环里最关键的变化是系统不再是一次性输入输出而是带着当前状态不断做判断。每一步的结果都会影响下一步的决策。这种动态性才是智能体区别于普通接口编排的地方。后续我会详细展开每个环节的机制但先记住闭环这个词它是整个智能体概念的地基。2. 智能体的四个核心部件模型、规划、工具、记忆如果只用一个句子描述智能体我会说智能体 一个能思考的模型 一套能行动的工具 一组约束怎么思考、怎么行动的规则。但真正落地时这四个部件缺一不可而且每一个都藏着不少门道。2.1 主脑模型别只盯着参数要看它的决策倾向所有智能体的智商都由底层大模型决定。你可能觉得直接选参数最大的模型就行但我的实际体验是智能体场景对模型的指令遵循能力和工具调用稳定性要求极高这两个纬度往往比综合benchmark分数更重要。什么叫指令遵循能力举个例子你告诉模型如果找不到对应城市的天气数据就回复无法获取不要编造。弱的模型可能在上下文稍微复杂时就忘掉这个约束编出一个晴26度来。强的模型即使中间经历了五六轮工具调用依然记得这条硬性规则。工具调用稳定性则体现在模型能否准确返回标准化的调用参数比如把明天早上8点正确转换成{datetime: 2025-01-15T08:00:00}。参数少一个冒号都可能导致整个流程崩溃。另外要注意决策倾向。有的模型倾向于多聊两句而不是直接动手有的模型则喜欢先斩后奏直接执行高风险操作。做智能体时你要根据应用场景挑选适合的模型性格。比如客服智能体我希望它多澄清用户需求自动化运维智能体我却希望它在明确授权范围内大胆执行。2.2 规划能力ReAct模式为什么好用规划是智能体看起来聪明的关键。目前最主流也最被我验证过的方法是ReAct模式让模型交替进行推理Reasoning和行动Acting把思考过程显式地输出出来。具体做法是每一轮循环中模型先输出一段Thought我分析现在该做什么然后输出一个Action我调用某个工具工具返回结果后再进入下一步思考。这种方式之所以好用是因为它把复杂任务分解成可追踪的小步而且每一步的推理过程都留痕方便你做错误排查。我在实际项目里看到过很多伪智能体只让模型输出一个最终答案中途如果遇到需要查询外部数据的环节就直接翻车。ReAct相当于给模型装了一个边想边做的循环遇到信息不足就停下来查资料。你可以在提示词里明确限制模型你必须先输出Thought再输出Action禁止跳过中间步骤。这样能大幅减少模型直接胡编答案的概率。2.3 工具调用把想法变成动作的那一步工具是智能体的手和脚。没有工具的智能体只是高级文案生成器。工具可以是一个API、一个数据库查询函数、一段本地脚本甚至另一个智能体。在设计工具时最容易被忽略的是工具描述的质量。模型靠什么知道该调用哪个工具靠你提供的工具名称和描述。如果工具描述写得模棱两可模型就会迷茫。举个例子你提供一个函数get_price(product_id)描述写获取价格就太简陋了。你应该写清楚当用户询问商品价格、折扣或历史价格时调用此函数。参数product_id为商品唯一标识可以从商品列表接口获取。该函数返回最近30天的价格曲线。我的经验是工具描述越具体模型的选择准确率越高。另一个要点是工具的返回格式。一定要让工具返回结构化且尽量简洁的数据。如果返回一大段无关紧要的日志模型的注意力会被干扰甚至把日志内容当成答案的一部分。2.4 记忆机制长期记忆和短时上下文怎么搭配说记忆是第四个核心部件它同样决定了智能体的上限。一个大模型对话窗口能容纳的token有限你不可能把用户三个月的聊天记录都塞进去。这时候就要区分长期记忆和短时上下文。短时上下文就是当前任务相关的对话内容和工具返回结果应当精简只保留和当前目标直接相关的信息。长期记忆则可以是用户的偏好、历史任务结论、重要的业务规则需要通过向量检索或摘要存储的方式在需要时召回。我的习惯做法是每完成一个任务让模型生成一条经验摘要存入长期记忆库新任务进来时先用用户的query做一次相似度检索把最相关的三五条记忆注入上下文。这样既省token又能让智能体记得住。记忆模块还有一个反直觉的点不是所有信息都值得记。我见过团队把整段对话原文塞进记忆库结果后面每次调用都带着一大堆历史噪声反而降低了准确率。最好的做法是记忆提取时做二次清洗让模型把可复用的结论和一次性的闲聊区分开。3. 不依赖重型框架手写一个最小可用的智能体纸上谈兵结束来点实在的。很多人入门智能体第一件事就是去学那些臃肿的Agent框架结果被概念淹没。我建议你亲手用几十行代码写一个最简循环所有神秘感都会消失。下面这个例子不依赖任何特定框架核心就是一个大模型函数 一个工具注册表 一个while循环。我从一个能查天气能设提醒的小智能体说起因为这两个工具逻辑简单又能体现工具调用的闭环。3.1 先把工具注册表搭起来工具注册表的作用是让模型知道有哪些工具可用、怎么用。我们用Python字典来模拟。def get_weather(city: str) - str: # 这里替换成真实天气API data { 北京: 晴25度微风, 上海: 多云28度东南风3级, 广州: 雷阵雨30度湿度80% } return data.get(city, 暂不支持该城市的天气查询) def add_reminder(time: str, content: str) - str: # 这里可以接日历或提醒系统 return f已帮你设置提醒{time} 提醒你 {content} TOOLS { get_weather: { description: 查询城市当前天气。当用户询问某地天气时使用。参数city为城市名称。, parameters: {city: string}, function: get_weather, }, add_reminder: { description: 新增一条提醒。当用户需要定时提醒时使用。参数time为ISO格式时间content为提醒内容。, parameters: {time: string, content: string}, function: add_reminder, } }注意工具描述里我特意写了当用户……时使用这种触发条件。模型全靠这段描述来决定调用谁千万别偷懒。3.2 用ReAct循环把思考-调用-观察串起来接下来是最核心的循环。我们用一个大模型的调用函数call_llm作为占位它接收系统提示词、工具列表和对话历史返回一段带Thought和Action的文本。你实际使用时把它替换成你熟悉的大模型API调用即可。import json def call_llm(messages, tools_schema): # 伪代码将messages和tools_schema发送给大模型 # 返回字符串例如 # Thought: 用户需要查北京天气。\nAction: get_weather {\city\: \北京\} # 实际使用时换成你的模型接口。 pass def build_system_prompt(tools_schema): prompt 你是一个智能助手。你可以使用以下工具完成用户任务。\n\n for name, info in tools_schema.items(): prompt f工具名: {name}\n prompt f描述: {info[description]}\n prompt f参数: {json.dumps(info[parameters], ensure_asciiFalse)}\n\n prompt ( 你必须按如下格式输出\n Thought: 你的分析\n Action: 工具名 {\参数\: 值}\n 当你认为任务已完成时输出\n Thought: 任务已完成\n Final: 最终回复内容\n ) return prompt def parse_action(text): lines text.strip().split(\n) action_line None for line in lines: if line.startswith(Action:): action_line line[7:].strip() break if not action_line: return None, None # 期望格式: tool_name {arg: value} try: name, arg_str action_line.split(maxsplit1) args json.loads(arg_str) return name, args except Exception: return None, None def run_agent(user_query, max_steps5): system build_system_prompt(TOOLS) messages [{role: system, content: system}, {role: user, content: user_query}] for step in range(max_steps): response call_llm(messages, TOOLS) messages.append({role: assistant, content: response}) print(f[Step {step1}] {response}) if Final: in response: final response.split(Final:, 1)[1].strip() print(\\n智能体最终回复:, final) return final tool_name, args parse_action(response) if tool_name and tool_name in TOOLS: try: result TOOLS[tool_name][function](**args) except Exception as e: result f工具执行出错: {e} # 把工具结果作为observation注入对话 messages.append({role: user, content: fObservation: {result}}) print(f - 调用 {tool_name}结果: {result}) else: # 如果模型输出格式错误给它一个修正信息 messages.append({role: user, content: Observation: 你的Action格式无法识别请重新输出。仅调用上述工具。}) print(\\n达到最大步数强制结束。)这个run_agent函数就是智能体的心脏。它做的事情很简单每一步让模型思考并决定要不要调用工具调用完把结果反馈给模型模型再决定下一步。max_steps限定了最大循环次数防止模型陷入死循环。3.3 跑起来看效果它真的在工作我们用一个模拟的call_llm来演示。假设用户说帮我查一下北京天气顺便定个明天早上8点的提醒内容是开会。如果模型足够聪明它的输出可能长这样Step 1: Thought: 用户有两个需求先查天气再设提醒我可以依次调用工具。 Action: get_weather {city: 北京} - 调用 get_weather结果: 北京晴25度微风 Step 2: Thought: 天气已经拿到接下来需要添加一个提醒。 Action: add_reminder {time: 2025-01-15T08:00:00, content: 开会} - 调用 add_reminder结果: 已帮你设置提醒2025-01-15T08:00:00 提醒你 开会 Step 3: Thought: 两个工具都调用完成我把结果汇总给用户。 Final: 北京今天晴25度微风。另外我已帮你设置明天早上8点的开会提醒。看到没有模型没有一次性吐完结果而是先调用第一个工具拿到结果后再调用第二个工具最后才总结。这就是智能体最基础的工作方式。你不需要一开始就上复杂的多智能体框架只要这个循环能转起来你就已经有一只脚迈进门了。这个迷你例子里还有很多可优化的地方比如真正的模型输出可能不严格遵守格式你需要用更强的约束提示词或者结构化输出来兜底。但先把主循环跑通再谈优化这是我一直坚持的实践路径。4. 我在实际项目里踩过的坑工具描述、上下文失控与幻觉循环理论讲完代码也给了接下来是重头戏。我做了四五个智能体Demo和一个生产系统踩过的坑一只手数不过来。这里挑三个最有代表性的每一个都差点让我怀疑人生。4.1 工具描述写得太实诚模型根本不调用第一个坑出现在我的第一个智能体项目里。当时我设计了一个订单查询智能体工具列表里有query_order我的描述写的是查询订单。输入订单号。结果模型宁可自己编造一个您的订单已发货也不调用工具。后来我反复调整提示词都不行直到我把工具描述改成当用户提供订单号并询问订单状态、物流、发货时间等问题时调用query_order。订单号通常是SO开头的字符串。如果用户没提供订单号先询问用户。该工具返回订单当前状态和物流轨迹。加了触发条件、参数格式、返回内容说明之后模型的调用准确率从不到50%直接提升到95%以上。这件事让我明白大模型不会理解工具它只是根据工具描述做模式匹配。描述里的每个关键词都可能成为匹配信号所以一定要把什么时候用写得比怎么用更详细。4.2 上下文越滚越长智能体开始忘事儿用ReAct循环有一个副作用工具调用结果和模型思考会不断累积在对话历史里。五轮以内问题不大但一旦超过十轮模型往往会表现出注意力漂移。我遇到的一个典型现象是智能体在第四步获取了某个数据到第八步需要引用这个数据时却给出一个与之前结果矛盾的答案。排查发现中间夹了太多无用的工具返回日志把关键信息冲淡了。后来我采用两条规则解决每次工具返回后立即压缩让模型用一句话总结Observation中有用的信息原始日志不进入下一轮上下文。只保留与当前目标相关的步骤如果模型已经确认某个子任务完成就把前面几轮的历史压缩成一条摘要。这两条规则执行后我的智能体在长任务里的稳定性有了明显提升。如果你的Agent框架自带记忆压缩机制建议优先打开如果没有就自己写一个summarize步骤。4.3 幻觉循环它会在错误答案上反复打转最让人崩溃的坑是智能体在某个错误的前提下反复推理形成幻觉循环。举个例子我的某个智能体需要查询库存它先调用了一个工具工具返回库存Id不存在但模型不认为这是错误反而在下一轮编造了一个备选建议又调用同一个工具传入另一个不存在的Id如此往复直到步数耗尽。这种问题的根源有两个一是模型缺乏错误敏感度二是工具返回的错误信息不够明显。我当时的修复方法是给工具返回错误时增加强标识比如返回内容以ERROR:开头并在系统提示词里明确规定一旦看到ERROR必须停止当前策略选择另一个工具或直接向用户说明情况。在解析工具结果的代码里如果检测到明确错误码直接打断当前计划插入一条plan_b_adjustment指引让模型重新规划。说实话这类问题很难百分百消除因为大模型天然会往合理的答案上靠。我在生产项目里的做法是加上一层自动校验如果最终答案里包含数字性结论就反向调用一遍原工具核对。成本高一点但能避免灾难性错误。5. 从Demo到落地还需要跨过哪些隐形门槛很多人照着教程写完Demo后会觉得智能体也不过如此。但真正让它承担业务你会发现Demo没有暴露的问题全跑出来了。我总结了三道坎跨过去才算入门。5.1 稳定性与成本两个绕不开的指标智能体看起来比普通API调用灵活但它本质上是多次模型调用的叠加。一个复杂任务可能要调用模型5到10次甚至更多。每次调用都有失败风险而且总耗时会线性增长。我做的一个调研Agent一次完整任务平均要调用8次模型有一次因为上游工具超时整个流程跑了整整三分钟。成本也一样。token消耗不再是你问一句答一句的几十个token而是连思考过程、工具调用日志、多轮Observation全都要算进去。我建议在Demo阶段就记录三个指标任务成功率、平均模型调用次数、平均单任务token消耗。如果调用次数超过10次还频繁失败先简化任务拆解粒度或者优化工具返回结果的精简度而不是盲目堆更多工具。5.2 安全与权限边界让Agent能动手但不能乱伸手智能体一旦具备调用工具的能力就相当于把你的一部分操作权限交给了模型。这是非常大的安全隐患。我见过一个智能体测试时直接把一条生产环境的测试配置给改掉了原因只是用户说了一句把配置调整一下。模型根本分不清调整是修改测试环境还是生产环境。我后来总结了一套权限管理的粗浅原则所有工具在定义时增加权限级别字段比如只读、追加、修改、删除。智能体只能调用权限级别不超过当前会话上下文允许范围的工具。涉及删除、覆盖类动作强制增加人类确认环节即使模型认为它已经确认过也不行。工具执行前记录完整参数执行后进行差异比对一旦发现异常操作就回滚。这套规则不能靠模型自觉必须在代码里硬编码。你可以把工具分成低风险工具和高风险工具只在特定场景下把高风险工具注入到模型可见的列表中。模型都看不到高风险工具自然无法调用。5.3 可观测性没有日志的智能体等于盲人开车最后一点是我自己血泪换来的。普通接口出错你能看到报错栈。智能体出错你看到的可能是十轮不相关的Thought记录根本不知道哪里跑偏了。我现在每个智能体项目都会强制带上一个运行追踪日志至少记录每步的Thought、Action、工具输入参数、工具返回结果、当前累计消耗token、此步耗时。这些日志不仅方便排错还能沉淀成测试集——把跑失败的case重新喂给模型观察它能不能在修正后走通。如果你用的是框架也一定要检查它是否暴露了中间步骤的回调接口。很多框架隐藏了Agent的思考细节只给你最终结果一旦出错就完全黑盒。我宁可多写几行代码保存日志也不要在深夜对着一个任务失败的输出干瞪眼。到这里智能体的核心概念、最小实现和落地要点都过了一遍。现在再回去看初识智能体这个标题你会发现智能体并不神秘——它就是一个让大模型具备思考—行动—观察闭环能力的工程系统。你可以沿着ReAct这个模式继续深入也可以去研究更复杂的多智能体协作、记忆网络、自动规划。但我始终觉得先把这一套最小闭环跑熟后面所有概念都会自然长在你这棵技能树上。下一次遇到复杂任务你也可以像我一样先拆出行动步骤再让它一步步去做。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑