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

资讯详情

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

从Workflow到AI Agent:ReAct架构与智能体开发实战解析

从Workflow到AI Agent:ReAct架构与智能体开发实战解析 1. 从“流水线”到“智能体”一次认知的跃迁最近我花了些时间仔细研读了Anthropic和OpenAI发布的两份关于AI Agent的指南。说实话看完之后感觉像是给脑子里那套关于“自动化流程”的旧观念做了一次彻底的格式化重装。过去几年我们谈论“Workflow”工作流已经习以为常它代表着一种确定性的、线性的、由规则驱动的自动化。但“Agent”智能体这个词正在以一种截然不同的姿态闯入视野它带来的不是效率的简单叠加而是能力范式的根本性转变。这种转变就像是从操作一台精密的数控机床到聘请了一位具备独立思考和解决问题能力的资深工程师。简单来说Workflow是“怎么做”的说明书而Agent是“做什么”并决定“怎么做”的思考者。前者是你设定好“如果A则执行B”的规则链后者是你告诉它“请帮我达成目标C”它会自己去拆解任务、调用工具、评估结果并在过程中动态调整策略。这种从“流程执行”到“目标驱动”的跨越正是当前AI应用从“玩具”走向“工具”乃至“伙伴”的关键一步。无论是开发者想要构建更智能的应用还是业务人员希望利用AI真正解放生产力理解Workflow与Agent的核心区别以及如何设计一个有效的Agent都变得至关重要。2. 核心分野Workflow的确定性与Agent的自主性要理解Agent我们必须先把它和我们已经熟悉的Workflow放在一起对比。这种对比不是非此即彼而是为了清晰地划出两种不同自动化范式的边界。2.1 Workflow确定性的规则引擎Workflow或者说工作流自动化其核心逻辑是“if-then-else”。它的运行完全依赖于预设的、明确的规则和路径。我们可以把它想象成一个非常高效的“数字流水线工人”。确定性输入与输出给定相同的输入和初始状态一个Workflow每次都会产生完全相同的输出和执行路径。它的行为是可预测、可追溯的。线性或分支逻辑流程通常是顺序执行的可能包含条件分支比如“如果邮件包含‘紧急’关键词则转发给经理否则归档”但每个分支都是预先定义好的。无状态或简单状态大多数Workflow本身不维护复杂的“记忆”或“目标感”。它处理当前任务完成后就重置等待下一个触发。状态通常仅限于流程变量如当前处理的数据、步骤索引。工具调用为步骤在Workflow中调用一个外部工具如发送邮件、查询数据库只是流程中的一个固定步骤。这个步骤何时发生、以什么参数发生在流程设计时就已经决定了。一个典型的例子是Zapier或Make原Integromat上的自动化流程。你设置“当Gmail收到新邮件触发器→ 解析邮件内容 → 如果主题包含‘订单’则将发件人信息添加到Google Sheets动作”。这个流程完美、可靠但它不会思考“这封邮件是不是真的订单”“除了加到表格是否需要通知客服”它只会忠实地执行你画好的图纸。2.2 Agent目标驱动的自主系统Agent则完全不同。它的核心是“Objective-Action-Evaluation”的循环。你可以把它看作一个拥有简单“大脑”的智能体。目标导向你给Agent的是一个高层次目标或意图例如“分析本季度销售数据并撰写一份亮点报告”而不是“第一步打开Excel第二步筛选Q2数据...”。自主规划与决策Agent内部或依靠LLM会将大目标分解为子任务并规划执行这些子任务的顺序。它需要决定“先做什么后做什么”甚至“是否需要做某件事”。工具使用能力Agent的核心能力之一是“工具调用”Tool Use。但它调用工具是动态的、基于情境的。它知道自己有哪些工具可用如搜索网络、执行代码、查询数据库并在认为需要时主动选择并调用合适的工具来完成任务。这不再是流程中的一个固定步骤而是它解决问题的手段。状态与记忆Agent通常具备短期记忆当前任务上下文和长期记忆历史交互、学到的知识这使它能够进行多轮对话、参考之前的结论、保持任务的一致性。评估与迭代Agent会评估其行动的结果。例如执行一次网络搜索后它会判断得到的信息是否足够回答子问题。如果不够它可能会调整搜索关键词再次尝试或者尝试另一种方法如计算。这种“思考-行动-观察”的循环是Agent自主性的体现。一个简单的对比表格可以更直观地展示差异特性维度Workflow (工作流)Agent (智能体)驱动方式规则/事件驱动目标/意图驱动决策主体流程设计者Agent自身基于LLM推理灵活性低路径预设高动态规划核心能力条件判断、顺序执行任务分解、工具调用、结果评估状态管理简单限于流程变量复杂包含记忆、上下文、目标适用场景结构化、重复性高的明确任务非结构化、需要推理和判断的复杂任务注意在实际应用中两者并非完全割裂。一个复杂的系统可能底层由多个确定性的Workflow作为“原子能力”提供支持而上层的Agent则负责协调和调用这些Workflow来达成更灵活的目标。理解它们的区别有助于我们在设计系统时做出正确的架构选择。3. 深入Agent架构ReAct模式与思考过程可视化理解了Agent是什么接下来我们看看它具体是如何“思考”和“行动”的。目前最主流且被Anthropic和OpenAI指南都重点提及的范式是ReAct (Reasoning Acting)。3.1 ReAct模式详解不只是链式调用ReAct不是一个简单的“调用LLM然后执行动作”的链条。它是一个严格的、结构化的交互循环。其核心思想是让LLM的“思考”Reasoning过程外显化并基于思考结果来指导“行动”Acting。一个标准的ReAct循环通常包含以下步骤思考 (Thought)这是Agent的“内心独白”。LLM基于当前的目标、已有的历史记忆和上一步的观察结果分析当前形势决定下一步该做什么。思考内容会被完整记录这不仅是给LLM自己的提示也让我们开发者能够窥见其决策过程便于调试。例如“用户想了解OpenAI的最新动态。我已经知道用户之前问过AI趋势。为了获取最新信息我需要使用网络搜索工具。我应该搜索‘OpenAI 最新公告 2024’。”行动 (Action)根据思考的结论Agent格式化地提出一个具体的行动请求。这通常是一个工具调用。格式是标准的比如Action: search_web, Action Input: {query: OpenAI 最新公告 2024}。这一步的关键在于行动是从思考中推导出来的而不是盲目的。观察 (Observation)执行工具调用后环境或工具返回结果。这个结果被作为“观察”输入给Agent。例如“Observation: 根据搜索OpenAI于2024年X月Y日发布了新一代模型GPT-4.5主要提升了...”。循环Agent将最新的“思考-行动-观察”三元组加入到其工作记忆中然后重新开始“思考”步骤评估目标是否达成或者是否需要采取进一步行动。例如新的思考可能是“已经获得了OpenAI发布新模型的信息。但用户可能还想知道价格变化。我需要再次搜索‘GPT-4.5 API 定价’。”这个循环会一直持续直到Agent认为目标已达成最终思考为“我现在有了足够的信息来回答用户的问题”然后它进入最终回答 (Final Answer)阶段。3.2 为什么ReAct如此重要从“黑盒”到“白盒”在简单的LLM调用中模型内部发生了什么我们无从得知它是一个“黑盒”。ReAct模式通过强制LLM输出结构化的“思考”步骤带来了几个根本性优势可解释性与可调试性作为开发者你可以完整地看到Agent的决策链路。如果它犯了错你可以追溯到是哪一步的“思考”出了问题例如错误地判断了信息是否充足或者是哪个“工具”返回了垃圾信息。这比面对一个莫名其妙的错误答案要友好得多。提升可靠性让LLM“一步一步想”通常比让它直接生成最终答案的准确性更高。这类似于人类解决复杂问题时会把大问题拆解成小问题逐个击破。外显的思考步骤减少了LLM“跳跃性推理”可能带来的幻觉或错误。支持复杂工具使用对于需要多步工具交互的任务如“查天气然后根据天气推荐旅游地点再查找当地的酒店”ReAct提供了一个自然的框架来组织和迭代这些调用。在Anthropic的Claude和OpenAI的GPT系列中通过System Prompt和Function Calling/Tool Calling API的良好设计我们可以非常清晰地构建出遵循ReAct模式的Agent。System Prompt用来定义Agent的角色、可用工具和输出格式强制要求以Thought/Action/Observation的格式响应而API则负责将“Action”部分解析为具体的工具调用。4. 构建实战一个简易研究助手Agent的完整实现理论说得再多不如动手实现一个。下面我将以一个“研究助手Agent”为例展示如何从零开始构建一个具备ReAct能力的智能体。这个Agent的目标是根据用户提出的开放式研究问题自动进行网络搜索、信息整合并生成一份简洁的报告。我们将使用Python并假设以OpenAI的API为例其思路与Anthropic的Claude完全相通。4.1 环境准备与核心组件定义首先我们需要几个核心库openai用于调用大模型requests用于模拟网络搜索工具实际生产环境会用Serper API、Google Search API等。import openai import json import requests from typing import List, Dict, Any, Optional # 初始化OpenAI客户端请替换为你的API Key client openai.OpenAI(api_keyyour-api-key-here) # 模拟一个简单的网络搜索工具函数 def search_web(query: str) - str: 模拟网络搜索。在实际应用中这里应接入真实的搜索API。 此处我们返回一个模拟的、结构化的结果字符串。 # 这里只是一个模拟。真实情况会调用如Serper API并解析返回的摘要。 mock_results { 大语言模型 Agent 架构: 大语言模型Agent通常采用ReAct模式结合任务规划、工具调用和记忆模块..., Anthropic Agent 指南要点: Anthropic强调Agent的安全性、可预测性以及使用Constitutional AI原则进行对齐..., OpenAI Function Calling: OpenAI的Function Calling允许模型以结构化方式请求调用外部工具是构建Agent的核心能力..., 工具使用Tool Use: Tool Use是Agent的核心能力之一使模型能够执行超越文本生成的操作如计算、查询、控制..., } # 简单模拟返回与查询最相关的一条结果 for key, value in mock_results.items(): if query.lower() in key.lower(): return f网络搜索结果{key} - {value} return f网络搜索结果未找到与{query}直接相关的简明信息。接下来我们定义Agent的核心类。它的主要职责是管理对话历史记忆、可用工具列表并运行ReAct循环。class ResearchAssistantAgent: def __init__(self, model: str gpt-4-turbo-preview): self.model model self.conversation_history: List[Dict[str, str]] [] # 存储完整的交互历史 self.available_tools [ # 定义Agent可用的工具 { type: function, function: { name: search_web, description: 执行一次网络搜索获取关于某个主题的最新信息。, parameters: { type: object, properties: { query: {type: string, description: 搜索查询关键词} }, required: [query] } } } ] # System Prompt是Agent的“大脑设定”至关重要 self.system_prompt 你是一个专业的研究助手。你的任务是通过使用提供的工具来回答用户的研究性问题。 你必须严格按照以下格式进行回应 Thought: 在这里你需要详细分析当前情况。回顾对话历史和目标解释你为什么需要采取下一步行动或者为什么认为已经可以给出最终答案。 Action: 调用工具的名称。必须严格是 search_web 中的一个。如果你不需要调用工具就输出 None。 Action Input: 调用工具时需要的输入参数必须是一个合法的JSON字符串。如果不调用工具输出 null。 Observation: 工具执行后返回的结果。如果你没有调用工具这里输出 None。 在你拥有足够信息回答用户的问题后你必须在Thought中说明并跳过Action和Action Input直接在Final Answer中给出清晰、有条理、基于事实的回答。 开始吧4.2 ReAct循环引擎的实现这是Agent最核心的部分。我们将实现一个run方法它接收用户查询然后开启一个循环在每次循环中1) 将历史、系统提示和当前状态组合成消息发送给LLM2) 解析LLM的响应3) 执行Action4) 将结果作为Observation加入历史并进入下一轮。def run(self, user_query: str, max_turns: int 5) - str: 运行Agent处理用户查询。 # 初始化对话历史加入用户查询 self.conversation_history.append({role: user, content: user_query}) for turn in range(max_turns): # 1. 准备发送给LLM的消息系统指令 完整历史 messages [{role: system, content: self.system_prompt}] # 将历史中的每轮交互格式化成LLM容易理解的文本 for msg in self.conversation_history: if msg[role] user: messages.append({role: user, content: msg[content]}) elif msg[role] assistant: # 注意历史中的assistant消息应该是上一次LLM的完整输出包含Thought/Action等 messages.append({role: assistant, content: msg[content]}) elif msg[role] tool: # 工具执行结果以特定格式加入 messages.append({role: tool, content: msg[content], tool_call_id: msg.get(tool_call_id)}) # 2. 调用LLM要求其以结构化格式响应 try: response client.chat.completions.create( modelself.model, messagesmessages, toolsself.available_tools, # 关键告诉模型有哪些工具可用 tool_choiceauto, # 让模型自行决定是否及如何调用工具 temperature0.1, # 低温度保证输出的格式稳定性和可靠性 ) except Exception as e: return f调用模型API时出错{e} assistant_message response.choices[0].message response_content assistant_message.content or tool_calls assistant_message.tool_calls # 3. 解析响应 # 首先将本次LLM的完整响应加入历史便于后续循环参考 full_response response_content if tool_calls: full_response \n json.dumps([tc.function.model_dump() for tc in tool_calls], ensure_asciiFalse) self.conversation_history.append({role: assistant, content: full_response}) # 4. 检查是否有工具调用 if tool_calls: print(f\n--- Turn {turn1} ---) print(fThought: {response_content}) for tool_call in tool_calls: func_name tool_call.function.name func_args json.loads(tool_call.function.arguments) print(fAction: {func_name}) print(fAction Input: {func_args}) # 5. 执行工具调用 if func_name search_web: observation search_web(func_args[query]) else: observation f错误未知工具 {func_name} print(fObservation: {observation[:200]}...) # 打印部分观察结果 # 6. 将观察结果加入历史作为下一轮LLM的输入 self.conversation_history.append({ role: tool, content: observation, tool_call_id: tool_call.id }) # 执行了工具继续下一轮循环 continue else: # 7. 没有工具调用说明LLM认为可以给出最终答案了 print(f\n--- Final Answer ---) print(fThought: {response_content}) # 最后的Thought解释了为何结束 # 最终答案就在response_content中根据我们的promptLLM会在Thought后直接给出答案 # 在实际更复杂的prompt设计中可能需要从内容中提取出“Final Answer:”部分。 final_answer response_content.strip() # 清理可能残留的格式标记 if Final Answer: in final_answer: final_answer final_answer.split(Final Answer:)[-1].strip() return final_answer return f已达到最大循环次数{max_turns}未能完成研究。当前历史{self.conversation_history}4.3 运行示例与结果分析现在让我们运行这个Agent看看它如何处理一个研究性问题。if __name__ __main__: agent ResearchAssistantAgent() query 请帮我研究一下大语言模型Agent的主流架构是什么以及Anthropic和OpenAI在指南中分别强调了哪些重点 result agent.run(query) print(\n *50) print(最终研究报告) print(*50) print(result)预期的控制台输出会类似于--- Turn 1 --- Thought: 用户想了解大语言模型Agent的主流架构以及Anthropic和OpenAI指南的重点。这是一个需要最新信息的开放式研究问题。我应该先搜索“大语言模型 Agent 架构”来获取主流架构信息。 Action: search_web Action Input: {query: 大语言模型 Agent 架构} Observation: 网络搜索结果大语言模型 Agent 架构 - 大语言模型Agent通常采用ReAct模式结合任务规划、工具调用和记忆模块... --- Turn 2 --- Thought: 我已经获得了关于Agent主流架构ReAct模式的基础信息。接下来我需要分别查找Anthropic和OpenAI的官方指南或权威解读以了解他们强调的重点。我应该先搜索“Anthropic Agent 指南要点”。 Action: search_web Action Input: {query: Anthropic Agent 指南要点} Observation: 网络搜索结果Anthropic Agent 指南要点 - Anthropic强调Agent的安全性、可预测性以及使用Constitutional AI原则进行对齐... --- Turn 3 --- Thought: 很好我了解了Anthropic的重点。现在需要搜索OpenAI的相关指南重点。搜索“OpenAI Function Calling 指南”或类似内容。 Action: search_web Action Input: {query: OpenAI Function Calling 指南} Observation: 网络搜索结果OpenAI Function Calling - OpenAI的Function Calling允许模型以结构化方式请求调用外部工具是构建Agent的核心能力... --- Turn 4 --- Thought: 我已经收集了关于主流架构ReAct、Anthropic重点安全性、可预测性、Constitutional AI和OpenAI重点Function Calling作为核心的信息。这些信息足以综合成一份简洁的报告来回答用户的问题。我不再需要调用工具。 --- Final Answer --- Thought: 基于已获取的信息我现在可以给出最终答案。 最终研究报告 根据研究当前大语言模型Agent的主流架构普遍采用 **ReActReasoning Acting范式**。该架构强调将模型的“思考”过程外显化并基于思考结果动态调用工具Tool Use通过“思考-行动-观察”的循环来完成任务。其核心组件通常包括任务规划器、工具调用模块和记忆系统。 关于两家公司的指南重点 * **Anthropic** 在其指南中特别强调了Agent的 **安全性与可预测性**。他们主张采用“Constitutional AI”原则对Agent进行对齐确保其行为符合预设的伦理和安全准则避免产生有害或不可控的输出。他们的设计哲学倾向于构建更可靠、更易于理解的智能体。 * **OpenAI** 的指南则大力推广其 **Function Calling/Tool Calling** 能力作为构建Agent的基石。他们提供了清晰的API和最佳实践鼓励开发者利用模型的结构化输出能力来动态调用外部工具从而扩展模型的功能边界。OpenAI更侧重于提供强大、灵活的基础设施来赋能开发者生态。 两者都认同工具使用和规划是Agent的核心但Anthropic的视角更偏向于安全和可控的Agent“行为准则”而OpenAI更侧重于提供实现Agent“能力”的技术框架。通过这个简单的示例你可以清晰地看到Agent的思考-行动循环。它自动将一个大问题分解为三个搜索子任务并依次执行最后整合信息给出答案。整个过程无需人工干预每一步。5. 超越基础高级模式、挑战与设计心得构建一个能跑起来的简易Agent只是第一步。要让它在真实场景中可靠工作我们必须深入更多细节和挑战。5.1 复杂任务规划与子Agent协同对于“写一份行业报告”这样的复杂目标单个ReAct循环可能不够。这时需要引入更高级的任务规划Task Planning。这通常由一个“规划器”Planner来完成它可以是一个专门的LLM调用负责将顶级目标分解成一个任务DAG有向无环图。例如规划器可能输出1. 子任务A搜索“2024年AI Agent市场规模与预测” 负责Agent研究Agent-1 2. 子任务B搜索“头部AI Agent初创公司融资情况” 负责Agent研究Agent-2 3. 子任务C基于A和B的结果撰写报告摘要 负责Agent写作Agent 4. 子任务D检查报告中的事实准确性 负责Agent审核Agent然后一个“控制器”Controller会协调这些子Agent每个子Agent本身可能就是一个ReAct循环并行或串行执行任务并整合结果。这就是多智能体系统Multi-Agent System的雏形。在代码层面这意味著你需要一个更上层的调度模块以及定义清晰的Agent间通信协议比如通过共享内存或消息队列传递结果。5.2 核心挑战与应对策略在实际开发中你会遇到一系列棘手的问题幻觉与错误工具调用LLM可能“幻想”出一个不存在的工具参数或者误解工具的功能。对策在System Prompt中极其清晰、无歧义地描述每个工具的功能、输入输出格式。使用严格的输出解析如Pydantic模型来校验LLM的响应一旦格式不符立即让LLM重试Retry。为关键工具调用设置“确认”步骤例如“你确定要执行删除操作吗”也是一种安全策略。循环与效率低下Agent可能陷入无限循环或者进行一些无意义的搜索。对策设置最大循环次数如我们代码中的max_turns。在Prompt中明确要求Agent“在信息足够时及时停止”。实现一个“验证器”Validator步骤评估当前信息是否已满足回答条件。对于常见任务可以内置一些启发式规则来短路不必要的循环。长上下文与记忆管理随着对话和工具调用变多历史记录会迅速膨胀消耗大量Token并可能让模型遗忘早期关键信息。对策实现记忆压缩与摘要。例如每经过几轮交互就让LLM对之前的对话和观察结果做一个简要摘要然后用摘要替换掉冗长的原始历史。也可以采用向量数据库进行长期记忆存储根据当前查询动态检索相关记忆而非全部送入上下文。工具生态与安全性给Agent的工具越多能力越强但风险也越高如发送邮件、操作数据库。对策实施严格的工具权限管理。为Agent划分“安全沙箱”限制其可访问的资源。对工具调用的输入参数进行严格的清洗和验证防注入攻击。记录所有工具调用日志便于审计和回滚。5.3 个人实践中的设计心得从我自己的踩坑经验来看有几个点特别值得分享Prompt工程是地基但不要过度依赖一个清晰、结构化的System Prompt是成功的80%。但当你发现Agent行为异常时首先应该检查工具的实现逻辑和返回格式很多时候问题出在工具返回的数据LLM无法理解而不是Prompt没写好。Prompt要像给一个聪明但死板的新员工写工作手册步骤和格式要求必须毫厘不差。从“玩具”到“工具”的关键是错误处理Demo能跑通只是开始。你必须为每一个工具调用、每一次模型响应设想所有可能的失败情况网络超时、API限额、返回格式异常、内容为空……并为这些情况设计降级方案如重试、使用缓存、返回友好错误信息。一个健壮的Agent其错误处理代码量往往会超过核心逻辑。成本与延迟的权衡每次工具调用和LLM推理都需要时间和金钱。在设计时要考虑“有必要让LLM思考这一步吗”。对于一些简单的、确定性的子任务完全可以用一个普通的函数即一个微型的Workflow来解决而不是启动一次昂贵的LLM调用。混合使用Agent和确定性Workflow是平衡智能与效率的实用架构。可观测性不是奢侈品是必需品一定要把Agent的每一步“Thought”、“Action”、“Observation”都详细地日志记录下来。这不仅是调试的救命稻草也是优化Agent、理解其“思维”模式、甚至发现潜在偏见或安全风险的最重要依据。没有日志的Agent就像一个在黑箱里运行的魔法出了问题你根本无从下手。从Workflow到Agent的演进本质上是自动化从“执行预设脚本”到“基于目标动态生成并执行脚本”的升级。这要求我们开发者从“流程设计师”转变为“目标定义者”和“能力赋能者”。我们不再需要预见所有可能的分支而是需要定义清晰的边界、提供可靠的工具、并设计一个能安全高效利用这些工具的“大脑”。这条路充满挑战但正是这些挑战让构建AI应用这件事从单纯的工程变成了一门兼具艺术与科学的、令人兴奋的新手艺。
返回列表