Chapter4 智能体经典范式构建

1. 文档阅读

1.1 ReAct

ReAct的核心思想是模仿人类解决问题的方式,将推理与行动显示地结合起来,形成一个“思考-行动-观察”的循环。

  • Thought(思考)
  • Action(行动)
  • Observation(观察)

ReAct的主要特点:

  1. 高可解释性
  2. 动态规划与纠错能力
  3. 工具协同能力

ReAct的固有局限性

  1. 对LLM自身能力的强依赖
  2. 执行效率问题
  3. 提示词的脆弱性
  4. 可能陷入局部最优

1.2 Plan and Solve

Plan-And-Solve将任务处理明确分为两个阶段:先规划(Plan),后执行(Slove)。

  1. 规划阶段 (Planning Phase): 首先,智能体会接收用户的完整问题。它的第一个任务不是直接去解决问题或调用工具,而是将问题分解,并制定出一个清晰、分步骤的行动计划。这个计划本身就是一次大语言模型的调用产物。
  2. 执行阶段 (Solving Phase): 在获得完整的计划后,智能体进入执行阶段。它会严格按照计划中的步骤,逐一执行。每一步的执行都可能是一次独立的 LLM 调用,或者是对上一步结果的加工处理,直到计划中的所有步骤都完成,最终得出答案。

Plan-and-Solve 尤其适用于那些结构性强、可以被清晰分解的复杂任务,例如:

  • 多步数学应用题:需要先列出计算步骤,再逐一求解。
  • 需要整合多个信息源的报告撰写:需要先规划好报告结构(引言、数据来源A、数据来源B、总结),再逐一填充内容。
  • 代码生成任务:需要先构思好函数、类和模块的结构,再逐一实现。

1.3 Reflection

Reflection 机制的核心思想是为智能体引入一种事后的自我矫正循环。执行→反思→优化。

当你的任务优先选择核心原因
充满不确定性,需要与外部API或网页交互ReAct它能根据实时反馈动态调整路径
逻辑路径清晰,侧重内部推理和步骤分解Plan-and-Slove它能提供稳定、结构化的执行流程
对最终的结果的质量的可靠性有极致的要求Reflection它通过迭代优化,能将“合格”的答案提升至“优秀”

2. 代码学习

1.1 ReAct

  1. 工具的定义

    1. 名称 (Name): 一个简洁、唯一的标识符,供智能体在 Action 中调用,例如 Search
    2. 描述 (Description): 一段清晰的自然语言描述,说明这个工具的用途。这是整个机制中最关键的部分,因为大语言模型会依赖这段描述来判断何时使用哪个工具。
    3. 执行逻辑 (Execution Logic): 真正执行任务的函数或方法。
  2. 提示词设计

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

1.2 Plan and Slove

  1. 提示词设计

    PLANNER_PROMPT_TEMPLATE = """
    你是一个顶级的AI规划专家。你的任务是将用户提出的复杂问题分解成一个由多个简单步骤组成的行动计划。
    请确保计划中的每个步骤都是一个独立的、可执行的子任务,并且严格按照逻辑顺序排列。
    你的输出必须是一个Python列表,其中每个元素都是一个描述子任务的字符串。
    
    问题: {question}
    
    请严格按照以下格式输出你的计划,```python与```作为前后缀是必要的:
    ```python
    ["步骤1", "步骤2", "步骤3", ...]
    

    “”"

    
    
  2. Planner类和Executor类

1.3 Reflection

  1. 提示词设计
    1. 初始执行提示词 (Execution Prompt)
    2. 反思提示词 (Reflection Prompt)
    3. 优化提示词 (Refinement Prompt)

3.习题(1-4)

  1. 本章介绍了三种经典的智能体范式:ReActPlan-and-SolveReflection。请分析:

    • 这三种范式在"思考"与"行动"的组织方式上有什么本质区别?

      answer:

      智能体思考行动
      ReAct这是智能体的“内心独白”。它会分析当前情况、分解任务、制定下一步计划,或者反思上一步的结果。这是智能体决定采取的具体动作,通常是调用一个外部工具,例如 Search['华为最新款手机']
      Plan-and-Slove规划:将问题分解,并制定出一个清晰、分步骤的行动计划它会严格按照计划中的步骤,逐一执行
      Reflection先使用上述两种方法完成一个“初稿”,然后,智能体进入反思阶段。它会调用一个独立的、或者带有特殊提示词的大语言模型实例,来扮演一个“评审员”的角色。智能体将“初稿”和“反馈”作为新的上下文,再次调用大语言模型,要求它根据反馈内容对初稿进行修正,生成一个更完善的“修订稿”。
    • 如果要设计一个"智能家居控制助手"(需要控制灯光、空调、窗帘等多个设备,并根据用户习惯自动调节),你会选择哪种范式作为基础架构?为什么?

      answer:

      选择:ReAct

      因为ReAct 是“走一步,看一步”,能够根据用户的指示自动调节,而且有工具协同能力,可以控制多个设备。

    • 是否可以将这三种范式进行组合使用?若可以,请尝试设计一个混合范式的智能体架构,并说明其适用场景。

      answer:

      可以

      1. 规划阶段:Plan-and-Slove
      2. 执行阶段:ReAct
      3. 反思阶段:Reflection

      适用于工程搭建。

  2. 在4.2节的 ReAct 实现中,我们使用了正则表达式来解析大语言模型的输出(如 ThoughtAction)。请思考:

    • 当前的解析方法存在哪些潜在的脆弱性?在什么情况下可能会失败?

      answer: 需要模型输出的结果匹配上正则表达式,当模型输入跟正则表达式有偏差的话就可能解析失败。

    • 除了正则表达式,还有哪些更鲁棒的输出解析方案?

      answer: JSON格式

    • 尝试修改本章的代码,使用一种更可靠的输出格式,并对比两种方案的优缺点

      answer:

      from llm_client import HelloAgentsLLM
      from tools import ToolExecutor, search
      import json
      
      # ReAct 提示词模板
      # REACT_PROMPT_TEMPLATE = """
      # 请注意,你是一个有能力调用外部工具的智能助手。
      
      # 可用工具如下:
      # {tools}
      
      # 请严格按照以下JSON格式进行回应:
      
      # Thought: 你的思考过程,用于分析问题、拆解任务和规划下一步行动。
      # Action: 你决定采取的行动,必须是以下格式之一:
      # - `{{tool_name}}[{{tool_input}}]`:调用一个可用工具。
      # - `Finish[最终答案]`:当你认为已经获得最终答案时。
      # - 当你收集到足够的信息,能够回答用户的最终问题时,你必须在`Action:`字段后使用 `finish(answer="...")` 来输出最终答案。
      
      # 现在,请开始解决以下问题:
      # Question: {question}
      # History: {history}
      # """
      
      REACT_PROMPT_TEMPLATE = """
      请注意,你是一个有能力调用外部工具的智能助手
      
      可用工具如下:
      {tools}
      
      请严格按照一下JSON格式进行回应,不要添加任何额外的文本:
      {{
          "thought":"你的思考过程,用于分析问题,拆解任务和规划下一步行动",
          "action":"你决定采取的行动"
          "action_input":"行动的输入参数"
      }}
      
      行动规则:
      - 如果你决定调用一个可用工具,`action`字段必须是工具名称,`action_input`字段是该工具的输入参数。
      - 如果你认为已经获得最终答案,`action`字段必须是`Finish`,`action_input`字段是最终答案。
      - 确保JSON格式正确且不包含任何多余文本。
      
      现在,请开始解决以下问题:
      Question: {question}
      History: {history}
      """
      
      class ReActAgent:
          def __init__(self, llm_client: HelloAgentsLLM, tool_executor: ToolExecutor, max_steps: int = 10):
              self.llm_client = llm_client
              self.tool_executor = tool_executor
              self.max_steps = max_steps
              self.history = []
      
          def run(self, question: str):
              self.history = [] # 每次运行重置历史记录
              current_step = 0
      
              while current_step < self.max_steps:
                  current_step += 1
                  print(f"\n--- 第 {current_step} 步 ---")
      
                  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)
      
                  messages = [{"role": "user", "content": prompt}]
                  response_text = self.llm_client.think(messages=messages)
                  if not response_text:
                      print("错误:LLM未能返回有效响应。"); break
      
                  thought, action, action_input = self._parse_output(response_text)
                  if thought: print(f"🤔 思考: {thought}")
                  if not action: print("警告:未能解析出有效的Action,流程终止。"); break
                  
                  if action == "Finish":
                      final_answer = action_input
                      print(f"🎉 最终答案: {final_answer}")
                      return final_answer
                  
                  tool_name = action
                  tool_input = action_input
                  if not tool_name or not tool_input:
                      self.history.append("Observation: 无效的Action格式,请检查。"); continue
      
                  print(f"🎬 行动: {tool_name}[{tool_input}]")
                  tool_function = self.tool_executor.getTool(tool_name)
                  observation = tool_function(tool_input) if tool_function else f"错误:未找到名为 '{tool_name}' 的工具。"
                  
                  print(f"👀 观察: {observation}")
                  self.history.append(f"Action: {action}")
                  self.history.append(f"Observation: {observation}")
      
              print("已达到最大步数,流程终止。")
              return None
      
          def _parse_output(self, text: str):
              try:
                  data = json.loads(text.strip())
                  thought = data.get("thought", "").strip()
                  action = data.get("action", "").strip()
                  action_input = data.get("action_input", "").strip()
                  return thought, action, action_input
              except json.JSONDecodeError:
                  print("错误:LLM响应不是有效的JSON格式。")
                  return None, None, None
      
      if __name__ == '__main__':
          llm = HelloAgentsLLM()
          tool_executor = ToolExecutor()
          search_desc = "一个网页搜索引擎。当你需要回答关于时事、事实以及在你的知识库中找不到的信息时,应使用此工具。"
          tool_executor.registerTool("Search", search_desc, search)
          agent = ReActAgent(llm_client=llm, tool_executor=tool_executor)
          question = "华为最新的手机是哪一款?它的主要卖点是什么?"
          agent.run(question)
      
      
  3. 工具调用是现代智能体的核心能力之一。基于4.2.2节的 ToolExecutor 设计,请完成以下扩展实践:

    提示:这是一道动手实践题,建议实际编写代码

    • ReAct 智能体添加一个"计算器"工具,使其能够处理复杂的数学计算问题(如"计算 (123 + 456) × 789/ 12 = ? 的结果")

      answer:

      计算机函数:

      def calculate(expression: str) -> str:
          """
          计算器,用来计算数学表达式的结果
          """
          try:
              # 使用 eval 进行简单的数学计算
              result = eval(expression, {"__builtins__": None}, {})
              return str(result)
          except Exception as e:
              return f"计算错误: {e}"
      

      注册计算器:

      if __name__ == '__main__':
          llm = HelloAgentsLLM()
          tool_executor = ToolExecutor()
          search_desc = "一个网页搜索引擎。当你需要回答关于时事、事实以及在你的知识库中找不到的信息时,应使用此工具。"
          tool_executor.registerTool("Search", search_desc, search)
          calculate_description = "一个数学计算工具。用于计算数学表达式的结果。"
          tool_executor.registerTool("calculate", calculate_description, calculate)
          agent = ReActAgent(llm_client=llm, tool_executor=tool_executor)
          question = "计算 (123 + 456) × 789/ 12 = ? 的结果,要求调用工具"
          agent.run(question)
      
      

      在这里插入图片描述

    • 设计并实现一个"工具选择失败"的处理机制:当智能体多次调用错误的工具或提供错误的参数时,系统应该如何引导它纠正?

      answer:

      思路:

      在ReAct中添加一个计数器,记录工具选择失败的次数,设置阈值如果失败的次数超过了阈值,那么在history中注入纠正的提示。

    • 思考:如果可调用工具的数量增加到50个甚至100个,当前的工具描述方式是否还能有效工作?在可调用工具数量随业务需求显著增加时,从工程角度如何优化工具的组织和检索机制?

      answer:不能有效工作,可以按照工具的类型进行分类,根据解决的问题来检索工具。

  4. Plan-and-Solve 范式将任务分解为"规划"和"执行"两个阶段。请深入分析:

    • 在4.3节的实现中,规划阶段生成的计划是"静态"的(一次性生成,不可修改)。如果在执行过程中发现某个步骤无法完成或结果不符合预期,应该如何设计一个"动态重规划"机制?

      answer

      更改代码如下:

      # --- 3. 执行器 (Executor) 定义 ---
      EXECUTOR_PROMPT_TEMPLATE = """
      你是一位顶级的AI执行专家。你的任务是严格按照给定的计划,一步步地解决问题。
      你将收到原始问题、完整的计划、以及到目前为止已经完成的步骤和结果。
      请你专注于解决“当前步骤”,并仅输出该步骤的最终答案,不要输出任何额外的解释或对话。
      
      # 原始问题:
      {question}
      
      # 完整计划:
      {plan}
      
      # 历史步骤与结果:
      {history}
      
      # 当前步骤:
      {current_step}
      
      请仅输出针对“当前步骤”的回答:
      """
      
      # 新增:验证提示模板,用于检查步骤结果是否有效
      VALIDATION_PROMPT_TEMPLATE = """
      你是一位AI验证专家。请检查以下步骤的结果是否有效、合理且符合问题上下文。
      如果结果为空、包含错误、或明显不符合预期,请回复 "INVALID";否则回复 "VALID"。
      
      # 原始问题:
      {question}
      
      # 当前步骤:
      {current_step}
      
      # 步骤结果:
      {result}
      
      回复: VALID 或 INVALID
      """
      
      class Executor:
          def __init__(self, llm_client: HelloAgentsLLM, max_replans: int = 2):
              self.llm_client = llm_client
              self.max_replans = max_replans  # 新增:最大重规划次数
      
          def execute(self, question: str, plan: list[str]) -> str:
              history = ""
              final_answer = ""
              replan_count = 0  # 新增:重规划计数器
              
              print("\n--- 正在执行计划 ---")
              i = 0  # 使用索引以便重规划时调整
              while i < len(plan):
                  step = plan[i]
                  print(f"\n-> 正在执行步骤 {i+1}/{len(plan)}: {step}")
                  prompt = EXECUTOR_PROMPT_TEMPLATE.format(
                      question=question, plan=plan, history=history if history else "无", current_step=step
                  )
                  messages = [{"role": "user", "content": prompt}]
                  
                  response_text = self.llm_client.think(messages=messages) or ""
                  
                  # 新增:验证结果
                  is_valid = self._validate_result(question, step, response_text)
                  if not is_valid and replan_count < self.max_replans:
                      print(f"❌ 步骤 {i+1} 结果无效,触发重规划...")
                      replan_count += 1
                      # 重规划剩余步骤
                      remaining_plan = self._replan(question, plan[:i+1], history, step)
                      if remaining_plan:
                          plan = plan[:i] + remaining_plan  # 更新计划
                          print(f"✅ 重规划完成,新计划: {remaining_plan}")
                          continue  # 重新执行当前步骤
                      else:
                          print("❌ 重规划失败,跳过此步骤。")
                          i += 1
                          continue
                  
                  history += f"步骤 {i+1}: {step}\n结果: {response_text}\n\n"
                  final_answer = response_text
                  print(f"✅ 步骤 {i+1} 已完成,结果: {final_answer}")
                  i += 1
                  
              return final_answer
      
          def _validate_result(self, question: str, step: str, result: str) -> bool:
              """验证步骤结果是否有效"""
              if not result or "错误" in result.lower() or len(result.strip()) < 5:  # 简单检查,可扩展
                  return False
              prompt = VALIDATION_PROMPT_TEMPLATE.format(question=question, current_step=step, result=result)
              messages = [{"role": "user", "content": prompt}]
              validation_response = self.llm_client.think(messages=messages) or ""
              return "VALID" in validation_response.upper()
      
          def _replan(self, question: str, completed_plan: list[str], history: str, failed_step: str) -> list[str]:
              """动态重规划剩余步骤"""
              replan_prompt = f"""
              原始问题: {question}
              已完成步骤: {completed_plan}
              历史结果: {history}
              失败步骤: {failed_step} (结果无效,需要重新规划)
      
              请重新生成剩余步骤的计划,作为 Python 列表:
              ```python
              ["新步骤1", "新步骤2", ...]
              ```
              """
              messages = [{"role": "user", "content": replan_prompt}]
              response_text = self.llm_client.thm(messages=messages) or ""  # 假设 think 方法
              try:
                  plan_str = response_text.split("``[python")[1].split("](http://_vscodecontentref_/4)``")[0].strip()
                  new_plan = ast.literal_eval(plan_str)
                  return new_plan if isinstance(new_plan, list) else []
              except:
                  return []
      
    • 对比 Plan-and-SolveReAct:在处理"预订一次从北京到上海的商务旅行(包括机票、酒店、租车)"这样的任务时,哪种范式更合适?为什么?

      answer:Plan-and-Slove更合适

    • 尝试设计一个"分层规划"系统:先生成高层次的抽象计划,然后针对每个高层步骤再生成详细的子计划。这种设计有什么优势?

      answer:逻辑清晰

  5. Reflection 机制通过"执行-反思-优化"循环来提升输出质量。请思考:

    • 在4.4节的代码生成案例中,不同阶段使用的是同一个模型。如果使用两个不同的模型(例如,用一个更强大的模型来做反思,用一个更快的模型来做执行),会带来什么影响?

      answer:可能,会提升问题解决的正确性和效率

    • Reflection 机制的终止条件是"反馈中包含无需改进"或"达到最大迭代次数"。这种设计是否合理?能否设计一个更智能的终止条件?

      answer:不是特别合理。

    • 假设你要搭建一个"学术论文写作助手",它能够生成初稿并不断优化论文内容。请设计一个多维度的Reflection机制,从段落逻辑性、方法创新性、语言表达、引用规范等多个角度进行反思和改进。

      answer

  6. 提示词工程是影响智能体最终效果的关键技术。本章展示了多个精心设计的提示词模板。请分析:

    • 对比4.2.3节的 ReAct 提示词和4.3.2节的 Plan-and-Solve 提示词,它们显然存在结构设计上的明显不同,这些差异是如何服务于各自范式的核心逻辑的?
    • 在4.4.3节的 Reflection 提示词中,我们使用了"你是一位极其严格的代码评审专家"这样的角色设定。尝试修改这个角色设定(如改为"你是一位注重代码可读性的开源项目维护者"),观察输出结果的变化,并总结角色设定对智能体行为的影响。
    • 在提示词中加入 few-shot 示例往往能显著提升模型对特定格式的遵循能力。请为本章的某个智能体尝试添加 few-shot 示例,并对比其效果。
  7. 某电商初创公司现在希望使用"客服智能体"来代替真人客服实现降本增效,它需要具备以下功能:

    a. 理解用户的退款申请理由

    b. 查询用户的订单信息和物流状态

    c. 根据公司政策智能地判断是否应该批准退款

    d. 生成一封得体的回复邮件并发送至用户邮箱

    e. 如果判断决策存在一定争议(自我置信度低于阈值),能够进行自我反思并给出更审慎的建议

    此时作为该产品的负责人:

    • 你会选择本章的哪种范式(或哪些范式的组合)作为系统的核心架构?
    • 这个系统需要哪些工具?请列出至少3个工具及其功能描述。
    • 如何设计提示词来确保智能体的决策既符合公司利益,又能保持对用户的友好态度?
    • 这个产品上线后可能面临哪些风险和挑战?如何通过技术手段来降低这些风险?
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐