当前位置: 首页 > news >正文

Hello-agents TASK03 第四章节 智能体经典范式构建

文章目录

  • 智能体经典范式构建
      • 配置 API 密钥
      • 封装基础 LLM 调用函数
    • ReAct
      • ReAct 的工作流程
      • 工具的定义与实现
      • ReAct 智能体的编码实现

智能体经典范式构建

  • ReAct (Reasoning and Acting): 一种将“思考”和“行动”紧密结合的范式,让智能体边想边做,动态调整。
  • Plan-and-Solve: 一种“三思而后行”的范式,智能体首先生成一个完整的行动计划,然后严格执行。
  • Reflection: 一种赋予智能体“反思”能力的范式,通过自我批判和修正来优化结果。
pip install openai python-dotenv

配置 API 密钥

为了让我们的代码更通用,我们将模型服务的相关信息(模型ID、API密钥、服务地址)统一配置在环境变量中。

在你的项目根目录下,创建一个名为 .env 的文件。
在该文件中,添加以下内容。你可以根据自己的需要,将其指向 OpenAI 官方服务,或任何兼容 OpenAI 接口的本地/第三方服务。
如果实在不知道如何获取,可以参考Datawhale另一本教程的1.2 API设置

# .env file
LLM_API_KEY="YOUR-API-KEY"
LLM_MODEL_ID="YOUR-MODEL"
LLM_BASE_URL="YOUR-URL"

封装基础 LLM 调用函数

为了让代码结构更清晰、更易于复用,我们来定义一个专属的LLM客户端类。这个类将封装所有与模型服务交互的细节,让我们的主逻辑可以更专注于智能体的构建。

import os
from openai import OpenAI
from dotenv import load_dotenv
from typing import List, Dict# 加载 .env 文件中的环境变量
load_dotenv()class HelloAgentsLLM:"""为本书 "Hello Agents" 定制的LLM客户端。它用于调用任何兼容OpenAI接口的服务,并默认使用流式响应。"""def __init__(self, model: str = None, apiKey: str = None, baseUrl: str = None, timeout: int = None):"""初始化客户端。优先使用传入参数,如果未提供,则从环境变量加载。"""self.model = model or os.getenv("LLM_MODEL_ID")apiKey = apiKey or os.getenv("LLM_API_KEY")baseUrl = baseUrl or os.getenv("LLM_BASE_URL")timeout = timeout or int(os.getenv("LLM_TIMEOUT", 60))if not all([self.model, apiKey, baseUrl]):raise ValueError("模型ID、API密钥和服务地址必须被提供或在.env文件中定义。")self.client = OpenAI(api_key=apiKey, base_url=baseUrl, timeout=timeout)def think(self, messages: List[Dict[str, str]], temperature: float = 0) -> str:"""调用大语言模型进行思考,并返回其响应。"""print(f"🧠 正在调用 {self.model} 模型...")try:response = self.client.chat.completions.create(model=self.model,messages=messages,temperature=temperature,stream=True,)# 处理流式响应print("✅ 大语言模型响应成功:")collected_content = []for chunk in response:content = chunk.choices[0].delta.content or ""print(content, end="", flush=True)collected_content.append(content)print()  # 在流式输出结束后换行return "".join(collected_content)except Exception as e:print(f"❌ 调用LLM API时发生错误: {e}")return None# --- 客户端使用示例 ---
if __name__ == '__main__':try:llmClient = HelloAgentsLLM()exampleMessages = [{"role": "system", "content": "You are a helpful assistant that writes Python code."},{"role": "user", "content": "写一个快速排序算法"}]print("--- 调用LLM ---")responseText = llmClient.think(exampleMessages)if responseText:print("\n\n--- 完整模型响应 ---")print(responseText)except ValueError as e:print(e)

ReAct

在准备好LLM客户端之后,我们将构建第一个,也是最经典的一个智能体范式ReAct(Reson+Act)
ReAct其核心思想是模仿人类解决问题的方式,将推理 (Reasoning) 与行动 (Acting) 显式地结合起来,形成一个“思考-行动-观察”的循环。

ReAct 的工作流程

在ReAct诞生之前,主流的方法可以分为两类:
一类是“纯思考”型,如思维链 (Chain-of-Thought),它能引导模型进行复杂的逻辑推理,但无法与外部世界交互,容易产生事实幻觉;
另一类是“纯行动”型,模型直接输出要执行的动作,但缺乏规划和纠错能力。

ReAct的巧妙之处在于,它认识到思考与行动是相辅相成的。思考指导行动,而行动的结果又反过来修正思考。为此,ReAct范式通过一种特殊的提示工程来引导模型,使其每一步的输出都遵循一个固定的轨迹:

  • Thought (思考): 这是智能体的“内心独白”。它会分析当前情况、分解任务、制定下一步计划,或者反思上一步的结果。
  • Action (行动): 这是智能体决定采取的具体动作,通常是调用一个外部工具,例如 Search['华为最新款手机']
  • Observation (观察): 这是执行Action后从外部工具返回的结果,例如搜索结果的摘要或API的返回值。

智能体将不断重复这个 Thought -> Action -> Observation 的循环,将新的观察结果追加到历史记录中,形成一个不断增长的上下文,直到它在Thought中认为已经找到了最终答案,然后输出结果。这个过程形成了一个强大的协同效应:推理使得行动更具目的性,而行动则为推理提供了事实依据。

我们可以将这个过程形式化地表示出来,如图所示。具体来说,在每个时间步t,智能体的策略(即大模型Π)会根据初始问题q和之间所有步骤的“行动-观察”历史轨迹((a1.o1),......,(at−1,ot−1))((a_1.o_1),......,(a_{t-1},o_{t-1}))((a1.o1),......,(at1,ot1))
在这里插入图片描述

随后,环境中的工具T会执行行动ata_tat,并返回一个新的观察结果oto_tot
ot=T(at)o_t=T(a_t)ot=T(at)
这个循环不断进行,将新的(at,ot)(a_t,o_t)(at,ot)对追加到历史中,直到模型在思考thtth_ttht中判断任务已完成。
在这里插入图片描述
这种机制特别适用于以下场景:

  • 需要外部知识的任务:如查询实时信息(天气、新闻、股价)、搜索专业领域的知识等。
  • 需要精确计算的任务:将数学问题交给计算器工具,避免LLM的计算错误。
  • 需要与API交互的任务:如操作数据库、调用某个服务的API来完成特定功能。
    因此我们将构建一个具备使用外部工具能力的ReAct智能体,来回答一个大语言模型仅凭自身知识库无法直接回答的问题。例如:“华为最新的手机是哪一款?它的主要卖点是什么?” 这个问题需要智能体理解自己需要上网搜索,调用工具搜索结果并总结答案。

工具的定义与实现

如果说大语言模型是智能体的大脑,那么工具 (Tools) 就是其与外部世界交互的“手和脚”。为了让ReAct范式能够真正解决我们设定的问题,智能体需要具备调用外部工具的能力。

针对本节设定的目标——回答关于“华为最新手机”的问题,我们需要为智能体提供一个网页搜索工具。在这里我们选用 SerpApi,它通过API提供结构化的Google搜索结果,能直接返回“答案摘要框”或精确的知识图谱信息,

首先,需要安装该库:

pip install google-search-results

同时,你需要前往 SerpApi官网 注册一个免费账户,获取你的API密钥,并将其添加到我们项目根目录下的 .env 文件中:

# .env file
# ... (保留之前的LLM配置)
SERPAPI_API_KEY="YOUR_SERPAPI_API_KEY"

接下来,我们通过代码来定义和管理这个工具。我们将分步进行:首先实现工具的核心功能,然后构建一个通用的工具管理器。

(1)实现搜索工具的核心逻辑

一个良好定义的工具应包含以下三个核心要素:

  1. 名称 (Name): 一个简洁、唯一的标识符,供智能体在 Action 中调用,例如 Search。
  2. 描述 (Description): 一段清晰的自然语言描述,说明这个工具的用途。这是整个机制中最关键的部分,因为大语言模型会依赖这段描述来判断何时使用哪个工具。
  3. 执行逻辑 (Execution Logic): 真正执行任务的函数或方法。
    我们的第一个工具是 search 函数,它的作用是接收一个查询字符串,然后返回搜索结果。
from serpapi import SerpApiClientdef search(query:str ) -> str:"""一个基于SerpApi的实战网页搜索引擎工具,它会智能的解析搜索结果,优先返回直接答案或知识图谱信息。"""print(f"正在执行[serpaApi]网页搜索:{query}")try:api_key = os.getenv("SERPAPI_API_KEY")if not api_key:return "错误:SERPAPI_API_KEY未设置"params = {"engine":"google","q":query,"api_key":api_key,"gl":"cn",#gl 代表 "geolocation"(地理位置)"hl":"zh-cn",#语言代码     "zh-cn"代表简体中文} client = SerpApiClient(params)results = client.get_dict()#智能解析:优先搜索最直接的答案if "answer_box_list" in results:return "\n".join(results["answer_box_list"])if "answer_box" in results and "answer" in results["answer_box"]:return results["answer_box"]["answer"]if "knowledge_graph" in results and "description" in results["knowledge_graph"]:return results["knowledge_graph"]["description"]#前两项都不存在时,检查是否存在知识图谱if "organic_results" in results and results["organic_results"]:snippets = [f"[{i+1}] {res.get('title','')}\n{res.get('snippet','')}"for i,res in enumerate(results["organic_results"][:3])]return "\n\n".join(snippets)#最低优先级:organic_resultsreturn f"对不起,没有找到关于'{query}'的信息。"except Exception as e:return f"搜索的时候发生错误:{e}"

(2) 构建通用的工具执行器
当智能体需要使用多种工具时(例如,除了搜索,还可能需要计算、查询数据库等),我们需要一个统一的管理器来注册和调度这些工具。为此,我们创建一个 ToolExecutor 类。

from typing import Dict, Anyclass ToolExecutor:"""一个工具执行器,负责管理和执行工具。"""def __init__(self):self.tools: Dict[str, Dict[str, Any]] = {}def registerTool(self, name: str, description: str, func: callable):"""向工具箱中注册一个新工具。"""if name in self.tools:print(f"警告:工具 '{name}' 已存在,将被覆盖。")self.tools[name] = {"description": description, "func": func}print(f"工具 '{name}' 已注册。")def getTool(self, name: str) -> callable:"""根据名称获取一个工具的执行函数。"""return self.tools.get(name, {}).get("func")def getAvailableTools(self) -> str:"""获取所有可用工具的格式化描述字符串。"""return "\n".join([f"- {name}: {info['description']}" for name, info in self.tools.items()])

(3)测试

现在,我们将 search 工具注册到 ToolExecutor 中,并模拟一次调用,以验证整个流程是否正常工作。

# --- 工具初始化与使用示例 ---
if __name__ == '__main__':# 1. 初始化工具执行器toolExecutor = ToolExecutor()# 2. 注册我们的实战搜索工具search_description = "一个网页搜索引擎。当你需要回答关于时事、事实以及在你的知识库中找不到的信息时,应使用此工具。"toolExecutor.registerTool("Search", search_description, search)# 3. 打印可用的工具print("\n--- 可用的工具 ---")print(toolExecutor.getAvailableTools())# 4. 智能体的Action调用,这次我们问一个实时性的问题print("\n--- 执行 Action: Search['英伟达最新的GPU型号是什么'] ---")tool_name = "Search"tool_input = "英伟达最新的GPU型号是什么"tool_function = toolExecutor.getTool(tool_name)if tool_function:observation = tool_function(tool_input)print("--- 观察 (Observation) ---")print(observation)else:print(f"错误:未找到名为 '{tool_name}' 的工具。")>>>
工具 'Search' 已注册。--- 可用的工具 ---
- Search: 一个网页搜索引擎。当你需要回答关于时事、事实以及在你的知识库中找不到的信息时,应使用此工具。--- 执行 Action: Search['英伟达最新的GPU型号是什么'] ---
🔍 正在执行 [SerpApi] 网页搜索: 英伟达最新的GPU型号是什么
--- 观察 (Observation) ---
[1] GeForce RTX 50 系列显卡
GeForce RTX™ 50 系列GPU 搭载NVIDIA Blackwell 架构,为游戏玩家和创作者带来全新玩法。RTX 50 系列具备强大的AI 算力,带来升级体验和更逼真的画面。[2] 比较GeForce 系列最新一代显卡和前代显卡
比较最新一代RTX 30 系列显卡和前代的RTX 20 系列、GTX 10900 系列显卡。查看规格、功能、技术支持等内容。[3] GeForce 显卡| NVIDIA
DRIVE AGX. 强大的车载计算能力,适用于AI 驱动的智能汽车系统 · Clara AGX. 适用于创新型医疗设备和成像的AI 计算. 游戏和创作. GeForce. 探索显卡、游戏解决方案、AI ...

ReAct 智能体的编码实现

现在,我们将所有独立的组件,LLM客户端和工具执行器组装起来,构建一个完整的 ReAct 智能体。我们将通过一个 ReActAgent 类来封装其核心逻辑。为了便于理解,我们将这个类的实现过程拆分为以下几个关键部分进行讲解。

(1)系统提示词设计

提示词是整个 ReAct 机制的基石,它为大语言模型提供了行动的操作指令。我们需要精心设计一个模板,它将动态地插入可用工具、用户问题以及中间步骤的交互历史。

# ReAct 提示词模板
REACT_PROMPT_TEMPLATE = """
请注意,你是一个有能力调用外部工具的智能助手。可用工具如下:
{tools}请严格按照以下格式进行回应:Thought: 你的思考过程,用于分析问题、拆解任务和规划下一步行动。
Action: 你决定采取的行动,必须是以下格式之一:
- `{{tool_name}}[{{tool_input}}]`:调用一个可用工具。
- `Finish[最终答案]`:当你认为已经获得最终答案时。
- 当你收集到足够的信息,能够回答用户的最终问题时,你必须在Action:字段后使用 finish(answer="...") 来输出最终答案。现在,请开始解决以下问题:
Question: {question}
History: {history}
"""

这个模板定义了智能体与LLM之间交互的规范:

  • 角色定义: “你是一个有能力调用外部工具的智能助手”,设定了LLM的角色。
  • 工具清单 ({tools}): 告知LLM它有哪些可用的“手脚”。
  • 格式规约 (Thought/Action): 这是最重要的部分,它强制LLM的输出具有结构性,使我们能通过代码精确解析其意图。
  • 动态上下文 ({question}/{history}): 将用户的原始问题和不断累积的交互历史注入,让LLM基于完整的上下文进行决策。

(2) 核心循环的实现
ReActAgent的核心是一个循环,它不断地"格式化提示词->调用LLM->执行操作->整合结果".直到任务完整或达到最大步数限制。

class ReActAgent:def __init__(self, llm_client: HelloAgentsLLM, tool_executor: ToolExecutor, max_steps: int = 5):self.llm_client = llm_clientself.tool_executor = tool_executorself.max_steps = max_stepsself.history = []def run(self, question: str):"""运行ReAct智能体来回答一个问题。"""self.history = [] # 每次运行时重置历史记录current_step = 0
#while循环是主题while current_step < self.max_steps:#max_steps参数是一个重要的安全阀.防止智能体陷入无限循环耗尽资源.current_step += 1print(f"--- 第 {current_step} 步 ---")# 1. 格式化提示词tools_desc = self.tool_executor.getAvailableTools()history_str = "\n".join(self.history)prompt = REACT_PROMPT_TEMPLATE.format(tools=tools_desc,question=question,history=history_str)# 2. 调用LLM进行思考messages = [{"role": "user", "content": prompt}]response_text = self.llm_client.think(messages=messages)if not response_text:print("错误:LLM未能返回有效响应。")break# ... (后续的解析、执行、整合步骤)

run方法是智能体的入口,它的while循环构成了ReAct范式的主体,max_steps参数则是一个重要的安全阀,防止智能体陷入无限循环而耗尽资源。

(3)输出解析器的实现
LLM 返回的是纯文本,我们需要从中精确地提取出Thought和Action。这是通过几个辅助解析函数完成的,它们通常使用正则表达式来实现。

# (这些方法是 ReActAgent 类的一部分)def _parse_output(self, text: str):"""解析LLM的输出,提取Thought和Action。"""thought_match = re.search(r"Thought: (.*)", text)action_match = re.search(r"Action: (.*)", text)thought = thought_match.group(1).strip() if thought_match else Noneaction = action_match.group(1).strip() if action_match else Nonereturn thought, actiondef _parse_action(self, action_text: str):"""解析Action字符串,提取工具名称和输入。"""match = re.match(r"(\w+)\[(.*)\]", action_text)if match:return match.group(1), match.group(2)return None, None
  • _parse_output: 负责从LLM的完整响应中分离出Thought和Action两个主要部分。
  • _parse_action: 负责进一步解析Action字符串,例如从 Search[华为最新手机] 中提取出工具名 Search 和工具输入 华为最新手机。
    (4) 工具调用与执行
	# (这段逻辑在 run 方法的 while 循环内)# 3. 解析LLM的输出thought, action = self._parse_output(response_text)if thought:print(f"思考: {thought}")if not action:print("警告:未能解析出有效的Action,流程终止。")break# 4. 执行Actionif action.startswith("Finish"):# 如果是Finish指令,提取最终答案并结束final_answer = re.match(r"Finish\[(.*)\]", action).group(1)print(f"🎉 最终答案: {final_answer}")return final_answertool_name, tool_input = self._parse_action(action)if not tool_name or not tool_input:# ... 处理无效Action格式 ...continueprint(f"🎬 行动: {tool_name}[{tool_input}]")tool_function = self.tool_executor.getTool(tool_name)if not tool_function:observation = f"错误:未找到名为 '{tool_name}' 的工具。"else:observation = tool_function(tool_input) # 调用真实工具

这段代码是Action的执行中心。它首先检查是否为Finish指令,如果是,则流程结束。否则,它会通过tool_executor获取对应的工具函数并执行,得到observation。

(5)观测结果的整合

最后一步,也是形成闭环的关键,是将Action本身和工具执行后的Observation添加回历史记录中,为下一轮循环提供新的上下文。

# (这段逻辑紧随工具调用之后,在 while 循环的末尾)print(f"👀 观察: {observation}")# 将本轮的Action和Observation添加到历史记录中self.history.append(f"Action: {action}")self.history.append(f"Observation: {observation}")# 循环结束print("已达到最大步数,流程终止。")return None

通过将Observation追加到self.history,智能体在下一轮生成提示词时,就能“看到”上一步行动的结果,并据此进行新一轮的思考和规划。

(6)运行实例与分析

将以上所有部分组合起来,我们就得到了完整的 ReActAgent 类。完整的代码运行实例可以在本书配套的代码仓库 code 文件夹中找到。

下面是一次真实的运行记录:

工具 'Search' 已注册。--- 第 1 步 ---
🧠 正在调用 xxxxxx 模型...
✅ 大语言模型响应成功:
Thought: 要回答这个问题,我需要查找华为最新发布的手机型号及其主要特点。这些信息可能在我的现有知识库之外,因此需要使用搜索引擎来获取最新数据。
Action: Search[华为最新手机型号及主要卖点]
🤔 思考: 要回答这个问题,我需要查找华为最新发布的手机型号及其主要特点。这些信息可能在我的现有知识库之外,因此需要使用搜索引擎来获取最新数据。
🎬 行动: Search[华为最新手机型号及主要卖点]
🔍 正在执行 [SerpApi] 网页搜索: 华为最新手机型号及主要卖点
👀 观察: [1] 华为手机- 华为官网
智能手机 ; Mate 系列. 非凡旗舰 · HUAWEI Mate XTs. 非凡大师 ; Pura 系列. 先锋影像 · HUAWEI Pura 80 Pro+ ; Pocket 系列. 美学新篇. HUAWEI Pocket 2 ; nova 系列. 专业人像.[2] 2025年华为手机哪一款性价比高?华为手机推荐与市场分析 ...
现在华为手机最大的卖点只剩下鸿蒙HarmonyOS系统,以及饱受争议的品牌信仰。 这里推荐目前值得入手的几款华为系列手机,根据不同预算自行选择:. 华为目前最受欢迎,也是搭载 ...[3] 2025年华为新款手机哪个性价比高?10款华为新款手机推荐
选华为主要还是要推荐高端手机,Mate 70和Pura 70系列是最新发布的旗舰机型。 HUAWEI Mate 70. 优点是,拍照配置依旧顶级,全焦段覆盖,适合专业摄影,做工出色,户外抗摔 ...--- 第 2 步 ---
🧠 正在调用 xxxxxx 模型...
✅ 大语言模型响应成功:
Thought: 根据搜索结果,华为最新发布的旗舰机型包括Mate 70和Pura 80 Pro+。为了确定最新型号及其主要卖点,我将重点放在这些信息上。从提供的链接来看,Mate 70系列和Pura 80 Pro+都是近期发布的产品,但具体哪一个是“最新”还需要进一步确认。同时,我可以从这些信息中提取出它们的主要
卖点。
Action: Finish[根据最新信息,华为的最新手机可能是HUAWEI Pura 80 Pro+或HUAWEI Mate 70。其中,HUAWEI Mate 70的主要卖点包括顶级的拍照配置,全焦段覆盖,适合专业摄影,做工出色,并且具有良好的户外抗摔性能。而HUAWEI Pura 80 Pro+则强调了先锋影像技术。]
🤔 思考: 根据搜索结果,华为最新发布的旗舰机型包括Mate 70和Pura 80 Pro+。为了确定最新型号及其主要卖点,我将重点放在这些信息上。从提供的链接来看,Mate 70系列和Pura 80 Pro+都是近期发布的产品,但具体哪一个是“最新”还需要进一步确认。同时,我可以从这些信息中提取出它们的主要 
卖点。
🎉 最终答案: 根据最新信息,华为的最新手机可能是HUAWEI Pura 80 Pro+或HUAWEI Mate 70。其中,HUAWEI Mate 70的主要卖点包括顶级的拍照配置,全焦段覆盖,适合专业摄影,做工出色,并且具有良好的户外抗摔性能。而HUAWEI Pura 80 Pro+则强调了先锋影像技术。
http://www.dtcms.com/a/609129.html

相关文章:

  • C++ 二叉搜索树(BST)完全指南:从概念原理、核心操作到底层实现
  • 电子电气架构 --- 哨兵模式初入门
  • 桌面开发,在线%考试管理%系统,基于eclipse,java,swing,mysql数据库。
  • 超融合架构的核心组件与协同机制深度解析
  • 桌面开发,在线%图书管理%系统,基于eclipse,jdk,java,swing,sqlserver数据库
  • 快速学会做网站网站建设公司怎么推广
  • 无需 iTunes 备份与恢复 iPhone 的 2 种方法
  • 【Linux】Ubuntu图形界面崩溃(无法进入)的解决方法汇总
  • Lidar调试记录Ⅳ之Ubuntu22.04+ROS2+Livox_SDK2环境下编译Livox ROS Driver 2
  • 网站收录查询网摘抄一则新闻
  • 做电影网站违法么深圳网站建设10强
  • 荆州北京网站建设如何自己做网页链接
  • 网站建设开发教程视频网站如何建设目录
  • 为企业为什么做网站数据分析师是干嘛的
  • 叶县建设局网站网站快速建设视频
  • 保定 网站制作门户网站如何建设
  • 网站建设基本流程视频网站建站思路
  • 三星官网网站wordpress评论钩子
  • 关于网站开发的外文书籍怎么做网站软件
  • 好一点的网站建设电商代运营公司
  • 给人做网站的公司现在一个天猫店要多少钱
  • 微网站建设哪家好电商网站如何设计内容
  • 关于重新建设网站的请示中国贸易网登录
  • 网站开发的实训周的实训过程网店运营心得体会
  • 网站备案前置审批 成都个人网站建设怎么赚钱
  • 网站设计中超链接怎么做网站优化 合同
  • 嘉兴企业网站制作单页网站规划设计书
  • 临沂网站制作报价宿迁发布最新通告
  • 毕业生对于网站建设感受客户网站开发全流程图
  • 溧阳网站建设中心中国房地产未来走势