AI Agent 架构设计:ReAct 与 Self-Ask 模式对比与分析

news2025/6/9 2:15:11

一、引言

(一) AI Agent 技术发展背景 🚀

AI Agent 的演进是一场从“遵循指令”到“自主决策”的深刻变革。早期,以规则引擎为核心的系统(如关键词匹配的客服机器人)只能在预设的流程上运行。然而,大语言模型的崛起为 Agent 注入了“灵魂”,使其具备了前所未有的自主规划与执行能力。像 AutoGPT 和 BabyAGI 这样的项目,已经能根据用户设定的宏大目标,自主地分解任务、调用工具、并完成一系列复杂操作,这标志着认知型 Agent 时代的到来。

在现代 Agent 设计中,任务型 Agent认知型 Agent 的界限愈发清晰。前者专注于高效执行封闭领域的具体流程(如处理退款申请),而后者则致力于解决开放域的复杂问题(如“分析市场趋势并撰写报告”)。架构设计是决定 Agent 智能上限的关键。ReActSelf-Ask 正是当前 LLM-based Agent 架构中,分别代表“行动派”和“思考派”的两种主流范式。

(二) ReAct 与 Self-Ask 模式的技术定位

ReAct (Reasoning and Acting)Self-Ask 都是驱动 LLM 进行复杂决策的核心架构,但它们的实现路径截然不同。

  • ReAct以任务执行为中心。它将 LLM 封装在一个“思考 → 行动 → 观察 → 思考…”的循环中。LLM 不直接回答最终问题,而是生成一个“想法”(Thought)和一个需要执行的“动作”(Action),通过与外部工具(如搜索引擎、API)交互来获取信息,然后根据“观察”(Observation)到的结果进行下一步推理。这种模式侧重于实时交互动态适应
  • Self-Ask以问题分解为核心。它通过递归地向自己提问来将一个复杂问题拆解成一系列更简单的子问题。每个子问题都通过外部工具(主要是搜索引擎)来回答,然后将这些中间答案逐步聚合,最终形成对原始问题的完整解答。这种模式侧重于深度推理知识溯源
特性ReActSelf-Ask
核心逻辑推理-动作循环递归问题分解
适用场景需要与环境/工具持续交互的流程化任务 (如智能客服、个人助理)需要深度知识推理和证据链的复杂问答 (如研究分析、报告生成)
交互模式多轮、动态通常为单轮、离线分析
产出特点过程透明,展示行动轨迹逻辑清晰,展示推理链条

二、ReAct 模式技术解析 ⚙️

(一) 核心技术原理

ReAct 的精髓在于将推理和行动解耦并显式化。它强迫 LLM 不仅要思考“做什么”,还要思考“为什么这么做”,并将此过程以文本形式表达出来。这极大地增强了 Agent 的可控性和可解释性。

其核心循环可以用下图表示:

Thought: 我需要...
Thought: 基于Y, 我现在可以...
用户问题
LLM 推理
Action: 调用工具X
工具执行
Observation: 获得结果Y
最终答案

这个循环的背后是精巧的提示词工程。模型被引导生成特定格式的文本,通常包含:

  • Thought: 模型的内心独白,用于分析当前情况、制定下一步计划。
  • Action: 决定调用的具体工具及输入参数。
  • Observation: 执行 Action 后从外部环境(工具)返回的结果。
  • Final Answer: 当模型认为已经收集到足够信息时,给出的最终答案。

(二) 架构组成要素

一个完备的 ReAct Agent 系统通常包含以下几个模块:

  • LLM 核心 (LLM Core):负责根据上下文和历史记录生成 ThoughtAction
  • 工具集 (Tool Suite):一组可供 Agent 调用的函数或 API,如搜索引擎、计算器、数据库查询接口等。
  • 提示模板 (Prompt Template):定义了 Agent 思考和行动的框架,是引导 LLM 按 ReAct 范式工作的关键。
  • 解析器 (Output Parser):负责解析 LLM 的输出,从中提取出 ActionAction Input,以便分发给相应的工具。
  • 执行器 (Executor):协调整个 ReAct 循环,接收用户输入,调用 LLM,解析输出,执行工具,并将结果反馈给 LLM,直到任务完成。

(三) 工程实践与代码案例

原有的 LangChain 示例非常经典,但为了更深入地理解其工作原理,我们来构建一个不依赖高级抽象、能清晰展示每一步循环的 Python 案例。

场景:一个简单的研究助手,需要回答“LangChain 的作者是谁?他现在的公司是什么?” 这个问题需要两次搜索。

import os
import re
from openai import OpenAI

# 假设已配置 OpenAI API Key
# os.environ["OPENAI_API_KEY"] = "YOUR_API_KEY"
client = OpenAI()

# --- 1. 定义工具 ---
# 这是一个模拟的搜索工具
def search_tool(query: str) -> str:
    """一个模拟的搜索引擎"""
    print(f"  [Tool] 正在搜索: {query}")
    # 针对本案例的模拟数据库
    mock_db = {
        "who is the author of LangChain": "The main author of LangChain is Harrison Chase.",
        "Harrison Chase's current company": "Harrison Chase founded a company called LangChain AI.",
    }
    return mock_db.get(query, "No information found.")

tools = {
    "search": search_tool
}

# --- 2. 定义 ReAct 提示模板 ---
react_prompt_template = """
Answer the following questions as best you can. You have access to the following tools:

search: A search engine. Use this to find information about people, events, or concepts.

Use the following format:

Question: the input question you must answer
Thought: you should always think about what to do
Action: the action to take, should be one of [search]
Action Input: the input to the action
Observation: the result of the action
... (this Thought/Action/Action Input/Observation can repeat N times)
Thought: I now know the final answer
Final Answer: the final answer to the original input question

Begin!

Question: {question}
Thought:
"""

# --- 3. 构建 ReAct 执行循环 ---
def run_react_agent(question: str):
    prompt = react_prompt_template.format(question=question)
    
    max_turns = 5
    for i in range(max_turns):
        print(f"--- Turn {i+1} ---")
        
        # 调用 LLM
        response = client.chat.completions.create(
            model="gpt-4o",
            messages=[{"role": "user", "content": prompt}],
            temperature=0,
            stop=["Observation:"] # 让模型在生成 Action 后停止
        )
        
        generated_text = response.choices[0].message.content
        print(f"[LLM Output]\n{generated_text}")
        prompt += generated_text

        # 解析 Action
        action_match = re.search(r"Action: (.*)\nAction Input: (.*)", generated_text)
        
        if action_match:
            action = action_match.group(1).strip()
            action_input = action_match.group(2).strip()
            
            # 执行工具
            if action in tools:
                tool_output = tools[action](action_input)
                observation = f"Observation: {tool_output}\n"
                print(f"[Observation]\n{observation}")
                prompt += observation
            else:
                prompt += "Observation: Invalid tool name.\n"
        else:
            # 如果没有 Action,说明可能已经得出最终答案
            final_answer_match = re.search(r"Final Answer: (.*)", generated_text, re.DOTALL)
            if final_answer_match:
                final_answer = final_answer_match.group(1).strip()
                print(f"\n✅ Final Answer Found: {final_answer}")
                return final_answer
            else:
                # 无法解析,可能是逻辑卡住了
                print("\n❌ Agent stopped: Cannot parse action or find final answer.")
                return None

    print("\n❌ Agent stopped: Reached max turns.")
    return None

# --- 4. 执行任务 ---
question = "Who is the author of LangChain and what is his current company?"
run_react_agent(question)

代码运行输出:

--- Turn 1 ---
[LLM Output]
I need to find out who the author of LangChain is first.
Action: search
Action Input: who is the author of LangChain
  [Tool] 正在搜索: who is the author of LangChain
[Observation]
Observation: The main author of LangChain is Harrison Chase.

--- Turn 2 ---
[LLM Output]
Thought: Now that I know the author is Harrison Chase, I need to find his current company.
Action: search
Action Input: Harrison Chase's current company
  [Tool] 正在搜索: Harrison Chase's current company
[Observation]
Observation: Harrison Chase founded a company called LangChain AI.

--- Turn 3 ---
[LLM Output]
Thought: I have found the author of LangChain and his current company. I can now provide the final answer.
Final Answer: The author of LangChain is Harrison Chase, and his current company is LangChain AI.

✅ Final Answer Found: The author of LangChain is Harrison Chase, and his current company is LangChain AI.

三、Self-Ask 模式技术解析 🧠

(一) 核心技术原理

Self-Ask 的核心思想是化繁为简。它模拟了人类解决复杂问题时的思维过程:先将大问题分解为一系列可以被直接回答的小问题,然后逐个击破。

其工作流程可以描绘成一个依赖图:

执行搜索
执行搜索
执行搜索
原始问题: 气候变化对农业的综合影响?
子问题1: 什么是气候变化的主要表现?
答案1: 气温上升, 极端天气频发...
子问题2: 气温上升如何影响农作物生长?
答案2: 影响光合作用, 增加病虫害...
子问题3: 极端天气如何影响农业生产?
答案3: 洪水和干旱导致减产...
答案合成
最终答案
SQ1,SQ2,SQ3

与 ReAct 不同,Self-Ask 的提示中通常包含 Are follow up questions needed here: 这样的关键词,引导模型判断当前信息是否足以回答,如果不足,则生成一个 Follow up: 问题,并通过 Intermediate answer: 记录中间结果。

(二) 架构组成要素

一个标准的 Self-Ask Agent 架构包括:

  • **分解器 **:通常是 LLM,负责接收原始问题和历史对话,生成下一个需要被回答的子问题。
  • **搜索引擎 **:这是 Self-Ask 的关键外部工具,用于回答分解器生成的具体子问题。
  • **合成器 **:也是 LLM,负责整合所有子问题的答案,形成一个连贯、全面的最终答案。在实践中,分解器和合成器可以是同一个 LLM。

(三) 工程实践与代码案例

场景:回答与 ReAct 案例中相同的问题:“LangChain 的作者是谁?他现在的公司是什么?”

import os
from openai import OpenAI

# 假设已配置 OpenAI API Key
client = OpenAI()

# --- 1. 定义工具 (与 ReAct 案例相同) ---
def search_tool(query: str) -> str:
    """一个模拟的搜索引擎"""
    print(f"  [Tool] 正在搜索: {query}")
    mock_db = {
        "who is the author of LangChain": "Harrison Chase is the main author of LangChain.",
        "Harrison Chase's current company": "Harrison Chase is the founder of LangChain AI.",
    }
    return mock_db.get(query, "No information found.")

# --- 2. 定义 Self-Ask 提示模板 ---
self_ask_prompt_template = """
Question: Who is the author of LangChain and what is his current company?
Are follow up questions needed here: Yes.
Follow up: Who is the author of LangChain?
Intermediate answer: Harrison Chase is the main author of LangChain.
Are follow up questions needed here: Yes.
Follow up: What is Harrison Chase's current company?
Intermediate answer: Harrison Chase is the founder of LangChain AI.
Are follow up questions needed here: No.
So the final answer is: The author of LangChain is Harrison Chase, and his current company is LangChain AI.

Question: {question}
"""

# --- 3. 构建 Self-Ask 执行类 ---
class SelfAskAgent:
    def __init__(self, llm_client, search_tool):
        self.client = llm_client
        self.search_tool = search_tool

    def run(self, question: str, max_iters=5):
        prompt = self_ask_prompt_template.format(question=question)
        
        for i in range(max_iters):
            print(f"--- Iteration {i+1} ---")
            
            prompt += "\nAre follow up questions needed here:"
            
            # 调用 LLM 判断是否需要子问题或生成最终答案
            response = self.client.chat.completions.create(
                model="gpt-4o",
                messages=[{"role": "user", "content": prompt}],
                temperature=0,
                stop=["\n"] # 在换行符处停止
            )
            choice = response.choices[0].message.content.strip()
            prompt += choice
            print(f"[LLM Decision]: {choice}")

            if "Yes" in choice:
                # 生成并执行子问题
                prompt += "\nFollow up:"
                resp_follow_up = self.client.chat.completions.create(
                    model="gpt-4o",
                    messages=[{"role": "user", "content": prompt}],
                    temperature=0,
                    stop=["\n"]
                ).choices[0].message.content.strip()
                
                prompt += resp_follow_up
                sub_question = resp_follow_up
                print(f"[Sub-question]: {sub_question}")

                # 执行搜索
                answer = self.search_tool(sub_question)
                
                prompt += f"\n {answer}"
                print(f"[Intermediate Answer]: {answer}")

            elif "No" in choice:
                # 生成最终答案
                prompt += "\nSo the final answer is:"
                resp_final = self.client.chat.completions.create(
                    model="gpt-4o",
                    messages=[{"role": "user", "content": prompt}],
                    temperature=0,
                ).choices[0].message.content.strip()

                print(f"\n✅ Final Answer: {resp_final}")
                return resp_final
            else:
                print("\n❌ Agent stopped: Unexpected decision.")
                return None
        
        print("\n❌ Agent stopped: Reached max iterations.")
        return None

# --- 4. 执行任务 ---
agent = SelfAskAgent(client, search_tool)
agent.run("Who is the author of LangChain and what is his current company?")

代码运行输出:

--- Iteration 1 ---
[LLM Decision]: Yes.
[Sub-question]: Who is the author of LangChain?
  [Tool] 正在搜索: Who is the author of LangChain?
[Intermediate Answer]: Harrison Chase is the main author of LangChain.
--- Iteration 2 ---
[LLM Decision]: Yes.
[Sub-question]: What is Harrison Chase's current company?
  [Tool] 正在搜索: What is Harrison Chase's current company?
[Intermediate Answer]: Harrison Chase is the founder of LangChain AI.
--- Iteration 3 ---
[LLM Decision]: No.

✅ Final Answer: The author of LangChain is Harrison Chase, and his current company is LangChain AI.

四、核心技术对比与选型指南

对比维度ReActSelf-Ask
决策逻辑任务导向:基于环境反馈进行状态转移,遵循决策过程 。决策粒度为细粒度动作问题导向:基于问题依赖图进行推理,状态转移由子问题驱动。决策粒度为粗粒度问题分解
任务处理能力流程化任务王者。非常适合需要与多个不同工具(API、数据库)交互的场景,如智能客服、自动化运维、个人助理。知识推理专家。在处理需要深度、广度信息检索和整合的开放域问题上表现出色,如学术研究、市场分析。
上下文管理依赖对话历史和中间Thought来维持上下文,对长序列处理能力要求高。通过Intermediate answer显式记录知识,上下文压力相对较小,但依赖于清晰的问题分解。
错误处理Observation中返回的错误信息可以被Thought捕获,进行重试或改变策略。对工具的健壮性要求高。如果一个子问题无法回答或回答错误,可能会影响整个推理链。可以通过设计更好的回溯机制来弥补。
性能与成本时间复杂度约为 O ( n ⋅ m ) O(n \cdot m) O(nm),其中 n n n 是交互轮次, m m m 是每次 LLM 推理和工具调用的平均耗时。API 调用频繁。时间复杂度约为 O ( k d ) O(k^d) O(kd),其中 k k k 是每个问题分解出的子问题数, d d d 是推理深度。LLM 调用次数可能较多。
可解释性行动轨迹透明。用户可以看到 Agent 的每一步思考和尝试,即使失败了也能理解原因。推理链条清晰。用户可以看到原始问题是如何一步步被拆解和回答的,答案的来源有据可查。

如何选择:一个实用的决策框架 🤔

在实际项目中,该如何选择?可以从以下四个角度考虑:

  1. 任务的本质是什么?

    • 是“做事”还是“回答”? 如果任务是执行一个多步骤流程(预订机票、管理日历),选择 ReAct。如果任务是回答一个复杂的、需要查证和综合信息的问题,选择 Self-Ask
  2. 交互性要求有多高?

    • 需要实时反馈吗? 如果 Agent 需要与用户或动态环境进行多轮对话和交互,ReAct 的循环机制更具优势。如果是一次性提交问题,等待最终分析报告,Self-Ask 更合适。
  3. 工具的多样性如何?

    • 需要调用多种工具吗? ReAct 的 Action 设计天然支持调用各种不同的工具(计算器、代码解释器、搜索引擎等)。Self-Ask 则强依赖于一个核心工具——搜索引擎
  4. 对过程还是结果更看重?

    • 需要追溯答案来源吗? 如果最终答案的可靠性和证据链至关重要,Self-Ask 提供的Intermediate answer提供了极佳的可追溯性。如果更关心任务是否被成功执行,ReAct 的行动日志则更为直观。

混合模式:未来的方向

ReAct 和 Self-Ask 并非完全互斥。先进的 Agent 架构已经开始融合两者的优点。例如,一个 ReAct Agent 在其 Thought 阶段,可能会发现需要回答一个复杂的子问题,此时它可以“启动”一个临时的 Self-Ask 流程来解决这个子问题,然后将结果作为 Observation 返回到 ReAct 的主循环中。这种分层、混合的架构将是未来 AI Agent 实现更高级智能的关键。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2404842.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

sql入门语句-案例

Sql入门 数据库、数据表、数据的关系介绍 数据库 用于存储和管理数据的仓库 一个库中可以包含多个数据表 数据表 数据库最重要的组成部分之一 它由纵向的列和横向的行组成(类似excel表格) 可以指定列名、数据类型、约束等 一个表中可以存储多条数据 数据 想要永久化存储…

A Survey on the Memory Mechanism of Large Language Model based Agents

目录 摘要Abstract1. LLM-Based Agent的Memory1.1 基础概念1.2 用于解释Memory的例子1.3 智能体记忆的定义1.3.1 狭义定义(肯定不用这个定义)1.3.2 广义定义 1.4 记忆协助下智能体与环境的交互过程1.4.1 记忆写入1.4.2 记忆管理1.4.3 记忆读取1.4.4 总过程 2. 如何实现智能体记…

华为OD机试 - 猴子吃桃 - 二分查找(Java 2025 B卷 200分)

public class Test14 {public static void main(String[] args) {Scanner sc = new Scanner(System.in);while (sc.hasNext()) {String[] s = sc.nextLine().split(" ");int[] arr = new int[s.length-1];int count = Integer.parseInt(s[s

【设计模式-5】设计模式的总结

说明:介绍完所有的设计模式,本文做一下总结 设计模式介绍 博主写的设计模式博客如下: 【设计模式-1】UML和设计原则 【设计模式-2.1】创建型——单例模式 【设计模式-2.2】创建型——简单工厂和工厂模式 【设计模式-2.3】创建型——原型…

【无人机】无人机UAV、穿越机FPV的概念介绍,机型与工具,证书与规定

【无人机】无人机UAV、穿越机FPV的概念介绍,机型与工具,证书与规定 文章目录 1、无人机的定义、概念、技术栈1.1 无人机的概念1.2 无人机技术(飞控,动力,通信) 2、无人机机型2.1 DJI无人机 (航拍…

链表好题-多种实现

143. 重排链表 - 力扣(LeetCode) 这道题非常经典,很多大厂都作为面试题。 方法一:寻找中点翻转链表合并链表 class Solution { public:void reorderList(ListNode* head) {if (head nullptr) {return;}ListNode* mid middleNo…

oracle数据恢复—oracle数据库执行truncate命令后的怎么恢复数据?

oracle数据库误执行truncate命令导致数据丢失是一种常见情况。通常情况下,oracle数据库误操作删除数据只需要通过备份恢复数据即可。也会碰到一些特殊情况,例如数据库备份无法使用或者还原报错等。下面和大家分享一例oracle数据库误执行truncate命令导致…

OneNet + openssl + MTLL

1.OneNet 使用的教程 1.在网络上搜索onenet,注册并且登录账号。 2.产品服务-----物联网服务平台立即体验 3.在底下找到立即体验进去 4.产品开发------创建产品 5.关键是选择MQTT,其他的内容自己填写 6.这里产品以及开发完成,接下来就是添加设…

分享两个日常办公软件:uTools、PixPin

1. uTools 网址:https://u.tools/ 这是一个高效智能的在线工具平台。 特点: 专为提升用户的工作效率跟生活便利性设计。 优点: 1:由国内团队开发。 2:通过插件化的方式为用户提供多样化的功能支持。 3&#xf…

Golang基础学习

​​​​​​​​​​ 初见golang语法 go项目路径 cd $GOPATH //ls可以看到有bin,pkg,src三个文件 cd src/ mkdir GolangStudy cd GolangStudy mkdir firstGolanggo程序执行: go run hello.go//如果想分两步执行: go build hello.go ./hello导入包的方式 import "f…

Spark 之 DataFrame 开发

foreachPartition val data = spark.sparkContext.parallelize(1 to 100)// 使用 foreachPartition 批量处理分区 data.foreachPartition {partitionIterator =

嵌入式学习笔记 - freeRTOS xTaskResumeAll( )函数解析

第一部分 移除挂起等待列表中的任务 while( listLIST_IS_EMPTY( &xPendingReadyList ) pdFALSE )//循环寻找直到为空,把全部任务扫描一遍 { pxTCB ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( ( &xPendingR…

机器学习KNN算法全解析:从原理到实战

大家好!今天我们来聊聊机器学习中的"懒人算法"——KNN(K-Nearest Neighbors,K近邻)算法。这个算法就像个"墙头草",它不学习模型参数,而是直接根据邻居的"投票"来做决策&…

【QT】自定义QWidget标题栏,可拖拽(拖拽时窗体变为normal大小),可最小/大化、关闭(图文详情)

目录 0.背景 1.详细实现 思路简介 .h文件 .cpp文件 0.背景 Qt Linux;项目遇到问题,解决后特此记录 项目需要,个性化的标题栏(是个widget),在传统的三个按钮(最大化、最小化、关闭&#xf…

FPGA定点和浮点数学运算-实例对比

在创建 RTL 示例时,经常使用 VHDL 2008 附带的 VHDL 包。它提供了出色的功能,可以高效地处理定点数,当然,它们也是可综合的。该包的一些优点包括: 有符号和无符号(后缀和后缀)定点向量。轻松将定…

Linux编程:2、进程基础知识

一、进程基本概念 1、进程与程序的区别 程序:静态的可执行文件(如电脑中的vs2022安装程序)。进程:程序的动态执行过程(如启动后的vs2022实例),是操作系统分配资源的单位(如 CPU 时…

Cursor Rules 使用

前言 最近在使用 Cursor 进行编程辅助时,发现 AI 生成的代码风格和当前的代码风格大相径庭。而且有时它会输出很奇怪的代码,总是不符合预期。 遂引出本篇,介绍一下 Rules ,它就可以做一些规范约束之类的事情。 什么是 Cursor R…

服务器数据恢复—服务器raid5阵列崩溃如何恢复数据?

服务器数据恢复环境&故障: 某品牌型号为X3850服务器上有一组由14块数据盘和1块热备盘组建的raid5磁盘阵列。 服务器在正常使用过程中突然崩溃,管理员查看raid5阵列故障情况的时发现磁盘阵列中有2块硬盘掉线,但是热备盘没有启用。 服务器数…

Go语言堆内存管理

Go堆内存管理 1. Go内存模型层级结构 Golang内存管理模型与TCMalloc的设计极其相似。基本轮廓和概念也几乎相同,只是一些规则和流程存在差异。 2. Go内存管理的基本概念 Go内存管理的许多概念在TCMalloc中已经有了,含义是相同的,只是名字有…

【DAY41】简单CNN

内容来自浙大疏锦行python打卡训练营 浙大疏锦行 知识点: 数据增强卷积神经网络定义的写法batch归一化:调整一个批次的分布,常用与图像数据特征图:只有卷积操作输出的才叫特征图调度器:直接修改基础学习率 卷积操作常…