ReAct架构的 AI Agent很简单?看看复杂业务场景下ReAct 的痛点和解决思路

ReAct架构的 AI Agent很简单?看看复杂业务场景下ReAct 的痛点和解决思路

一、ReAct Agent在复杂场景下的痛点

1.1 什么是ReAct Agent架构

ReAct Agent是基于ReAct(Reasoning + Acting)框架的智能代理,核心是结合推理、行动、反思的循环。
ReAct Agent的架构组成,可能包括:推理模块(LLM生成Thought和Action)、行动模块(调用工具执行Action)、观察模块(获取工具结果作为Observation)、状态管理(维护上下文)、终止条件(何时停止循环)。
通过显式的推理步骤指导行动,并通过观察反馈更新推理,形成一个闭环。这与传统的LLM直接生成答案不同,传统方法可能缺乏外部信息的获取和多步骤推理的能力。

1.2 ​​ReAct 框架机制​​

  • 循环推理流程​​:
    ReAct Agent 遵循 ​​Thought → Action → Observation​​ 的迭代循环:
    具体执行流程如下:

大家看上面的流程以及网上的示例,大部分都是提一个问题,调一下网页搜索或者查询天气工具,把最终答案交给模型去总结得到答案。可能会认为ReAct Agent也不过如此,简单。然而真是企业落地的场景中,往往没有这么简单,以下是真实企业使用大模型实现数据+业务规则分析落地场景时遇到的困局。

1.2 复杂场景下的痛点

在使用AI Agent进行数据分析的场景时,LLM本身在精确数学计算和多步逻辑推理上的固有缺陷,导致Agent在数据解读过程中常出现错误,严重制约了其可靠性。所以很容易想到会使用ReAct + Tools的方式进行数学分析和工具调用。然后这种方案在真是落地时会面临:

痛点一:工具调用场景有限

真实场景下需要计算的数据集往往不是简单的加减乘除,可能需要结合具体的业务规则把多份数据集进行分组聚合同环比,开窗聚合对比等。当前的Agent工具集无法覆盖多样化的数学计算需求。这反映出一个常见的设计局限:开发者往往针对特定功能硬编码工具,如简单的计算器或固定的数据库查询工具 。然而,数据分析的复杂性远超简单的加减乘除。它可能需要矩阵运算、线性代数、复杂的统计检验、或大规模数据集的聚合与分组操作 。当任务超出预设工具的功能范畴时,Agent便无能为力。这种单一、静态的工具集无法满足数据分析中动态、多变的计算要求。

痛点二:工具调用鲁棒性差

即使工具可用,其调用成功率也面临挑战。LLM生成工具调用所需的参数时,可能因格式错误、类型不匹配或参数位置颠倒而导致调用失败 。由于缺乏错误自查和自动修正机制,一旦出现问题,整个流程就会中断或产生错误结果。这种脆弱的调用过程,使得Agent无法在无人干预的情况下可靠地完成任务,尤其是在金融、合规等对准确性要求极高的领域 。

痛点三:长提示词下的决策困境

相比如网上输入一个问题查询报告或者查询天气的Demo, 真实场景中在包含大量业务规则和分析需求的复杂提示词中(往往是几千字以上),LLM难以准确判断何时需要调用工具,以及调用哪个工具 。这类似于让一个初学者在阅读一本厚厚的业务手册后,立即准确地找到并执行正确的操作。当提示词篇幅过长,信息密度过大时,LLM的上下文理解能力会受到挑战,其自主决策能力下降,导致其要么选择不调用工具而直接“臆造”答案,要么调用了不合适的工具,从而产生错误 。

二、究其原因

为什么在复杂的数学分析场景下直接使用大模型会有问题?
究其原因,大语言模型不擅长数学计算的根本原因在于其符号-语义鸿沟。LLM的训练目标是学习语言模式和统计规律,而非进行精确的符号运算 。当被问及一个数学问题时,LLM并非像编程语言那样执行 2+2的确定性指令,而是根据其在海量训练数据中见过的相关模式,生成一个概率上最接近正确答案的文本序列。这种基于文本相似性的“猜测”机制,在面对需要高精度的算术、代数或微积分问题时,准确率会显著下降 。这就是为什么LLM在处理“9.11比9.9大”这类问题时会出错,因为它可能将数字识别为日期或版本号,而非数值进行比较,这完全是由其分词器和训练机制决定的 。

为什么ReAct能有效解决上述缺陷?
为了弥补这一内在缺陷,引入了链式思考( CoT)这一提示工程技术 。CoT并非让模型自己进行计算,而是通过在提示词中加入“让我们一步一步思考”等指令,引导模型将复杂的数学问题分解为可执行的子步骤 。例如,一个复杂的数学应用题可以被分解为“第一步,识别关键变量;第二步,列出公式;第三步,执行计算;第四步,得出最终答案”等步骤。这个过程模拟了人类的推理路径,帮助LLM将一个复杂问题转化为一系列更简单、更易于判断和执行的子任务 。更重要的是,这个过程为 工具调用提供了必要的“准备”——模型不再需要自己去计算,而是将分解后的子任务交给外部工具去完成。CoT帮助模型解决了“何时”调用和“调用什么”的决策问题,但并未解决“如何”调用以及“调用失败后”的鲁棒性问题。

然而上面真实落地时复杂场景下的痛点应该如何解决,以下是一些解决思路,并在实践中得到验证:

三、实践中的解决方案

3.1 方案总览:混合Agentic数据分析系统架构

为了系统性地解决上述痛点,本报告提出一个基于多层级智能体协作的混合Agentic数据分析系统架构。该架构将不同的Agentic模式部署在不同的层级,形成一个逻辑清晰、职责分明的智能工作流。架构的考量本质是把复杂问题进行拆解,在子问题中解决各个分点,降低整个任务的复杂度。

下图展示了该系统的总体架构:

主控层: 这是整个系统的入口。它接收用户的原始查询,并根据规划模式,将复杂查询分解为一个或多个高层次的子任务。它还充当路由器的角色,将子任务分配给最合适的专业Agent进行处理 。

规划层: 负责将主控层分解出的高层次子任务进一步细化,生成一个清晰的、分步骤的执行计划。该计划是一个自然语言序列,例如“第一步:计算总收入;第二步:计算各渠道的销售占比;第三步:找出占比最高的三个渠道” 。

执行层: 这是行动的核心。它接收规划层生成的单个子任务,并决定何时以及如何调用相应的工具或生成代码来完成任务。该层可以采用ReAct模式,在每次执行前进行思考和判断 。

反思与修正层: 这是一个关键的、贯穿于执行过程中的模块。它负责评估执行层的行为和工具调用的结果。当发现错误(如工具调用失败、结果异常)时,它会生成详细的修正意见,并反馈给执行层或规划层,促使其进行自我修正和重试,直到任务成功 。

工具与数据层: 这是一个包含各种外部能力的基础设施。它包括:

通用工具集: 如计算器、符号计算器、数据库查询工具等。

专业数据科学工具集: 封装了Python数据分析库(如Pandas, NumPy)。

数据源: 结构化数据库(SQL, NoSQL)、非结构化文档、向量数据库等 。

通过这个架构,上述的三个痛点都可以在不同的层级得到系统性的解决。

3.2 解决痛点一:构建动态与鲁棒的工具集

问题分析
用户遇到的工具集覆盖有限的问题,源于其为特定场景设计的局限性。要解决这一问题,核心策略是超越硬编码的计算函数,构建一个通用且动态的工具集。这一工具集应能将LLM的能力无缝扩展到整个强大的Python数据科学生态。

核心策略与工具集设计
我们不应试图为每一个数学运算都编写一个独立的工具。相反,我们应该创建能够处理一整类任务的、高级别的**“智能工具”**。基于Python强大的数据科学库,我们可以设计以下几类工具:

基础数学工具: 用于处理高精度、符号级的数学计算,避免浮点数误差。例如,当LLM需要处理一个代数方程或积分时,它可以调用这个工具,由SymPy库来执行精确的符号运算,而非进行近似的数值计算 。

数据处理与统计工具: 这是数据分析Agent的核心工具。它封装了Pandas库,能够接收数据框对象和操作指令,执行数据清洗、聚合、分组、透视表、描述性统计等任务。

科学计算与线性代数工具: 封装NumPy库,提供向量-向量、向量-矩阵、矩阵-矩阵等操作能力 。这直接解决了数据分析中常见的复杂计算需求,如求协方差矩阵、特征值分解等。

生成代码方式(依赖模型底座要有强大的代码生成功能):针对输入的数据和规则,使用大模型生成对应的python执行代码,并提供沙箱进行执行计算,得到最终答案,这种方式最为灵活,但是需要强依赖模型底层的代码生成能力。

基于LangChain的通用数据分析工具集实现示例
以下示例代码展示了如何基于LangChain的@tool装饰器,将这些Python库封装成Agent可调用的工具。关键在于,工具的name和description参数必须非常明确,以优化LLM的决策,让它在复杂的上下文里能够精准判断何时调用哪个工具 。
代码示例:

import pandas as pd
import numpy as np
from sympy import sympify, Symbol
from langchain.tools import tool
from typing import Union, List

# 工具描述应清晰,指导LLM何时使用
@tool
def sympy_calculator(expression: str) -> str:
    """
    一个高精度的符号数学计算器。
    当需要进行精确的数学运算、解方程、处理代数表达式或进行微积分计算时使用。
    输入应为有效的数学表达式字符串。
    """
    try:
        # 允许LLM传入变量,例如 'x + 2*y'
        symbols = {s for s in expression.split() if s.isalpha() and len(s) == 1}
        for s in symbols:
            exec(f'{s} = Symbol("{s}")')
        
        result = sympify(expression)
        return f"计算结果:{result}"
    except Exception as e:
        return f"符号计算失败,请检查表达式格式。错误信息:{e}"

@tool
def pandas_data_frame_processor(df_str: str, operation: str, **kwargs) -> str:
    """
    强大的数据处理工具,基于Pandas库。
    当需要对类表格数据(如CSV、JSON字符串表示的DataFrame)进行清洗、聚合、过滤、排序、统计分析(如均值、中位数、总和)时使用。
    需要输入DataFrame的JSON字符串表示、具体操作名称以及操作所需的参数。
    支持的操作包括: 'describe', 'groupby', 'filter', 'sort', 'sum', 'mean', 'pivot_table'等。
    """
    try:
        df = pd.read_json(df_str)
        if operation == 'describe':
            return df.describe().to_json()
        elif operation == 'sum':
            column = kwargs.get('column')
            return str(df[column].sum())
        elif operation == 'mean':
            column = kwargs.get('column')
            return str(df[column].mean())
        # 可以继续扩展其他操作...
        else:
            return f"不支持的操作: {operation}"
    except Exception as e:
        return f"Pandas数据处理失败,请检查数据和操作参数。错误信息:{e}"

# 这是一个更通用的、基于代码执行的工具,但风险较高
@tool
def python_code_executor(code: str) -> str:
    """
    这个可以在前置使用代码生成大模型进行计算代码生成,或者
    执行Python代码的工具,用于进行复杂的数据处理、图表生成或算法执行。
    只有在sympy_calculator和pandas_data_frame_processor等更具体的工具无法满足需求时才使用。
    """
    # 警告:实际部署中需在沙箱环境中执行,确保安全
    try:
        local_scope = {}
        exec(code, {}, local_scope)
        # 假设代码的输出被存储在一个变量中
        return str(local_scope.get('result', 'No result variable found.'))
    except Exception as e:
        return f"Python代码执行失败,请检查语法。错误信息:{e}"

3.3 解决痛点二:引入反射机制提升调用鲁棒性

问题分析
工具调用失败和结果错误源于LLM生成参数的不可控性及缺乏错误自查机制。简单的ReAct模式无法处理这种“行动失败”的情况,因为它只是一条线性的“思考-行动”序列。

核心策略
在工具调用前后增加**“反思循环”**。这一机制将工具调用过程分解为多个可监控、可修正的步骤,形成一个可循环的、具备自愈能力的流程 。

带反思机制的工具调用流程图:

计划调用: Execution Agent通过其思考过程,决定需要调用某个工具(例如pandas_data_frame_processor),并生成相应的参数(例如df_str和operation)。

前置校验: 在实际调用工具之前,反思模块会根据工具的JSON Schema对生成的参数进行格式和类型校验。例如,检查df_str是否为有效的JSON字符串,operation是否在支持的操作列表中。如果校验失败,反思模块会立即生成一个修正意见,如“参数df_str格式错误,请确保其为有效的JSON字符串”,并返回给Execution Agent。

工具执行: 只有当参数通过前置校验后,才会实际调用工具。执行过程中,需要捕获所有可能的异常,如TypeError、ValueError等 。

结果反思: 工具执行完毕后,反思模块会评估其输出。这包括:

异常捕获: 如果执行失败,反思模块分析异常信息。

结果有效性检查: 检查结果是否为NaN、无穷大或空值,或与预期范围严重不符。例如,计算销售额占比,结果不应超过100%。

自我修正: 如果反思模块发现错误,它会生成一个详细的、有针对性的修正提示词,并返回给Execution Agent进行重试。这个修正提示词是关键,它将复杂的错误信息转化为LLM能够理解并用于自我优化的语言 。例如,提示词可以是:“工具调用失败,错误:

TypeError: cannot convert string to float。请将参数column_a转换为数字类型后重试。”

基于LangGraph的反思循环实现示例
LangGraph提供了一种构建可循环、有状态的Agent工作流的强大方式,非常适合实现这种反思循环 。
代码示例:

from langgraph.graph import StateGraph, START, END
from typing import TypedDict, List
from langchain.tools import tool
from langchain.schema import HumanMessage, AIMessage, ToolMessage

# 1. 定义Agent的状态
class AgentState(TypedDict):
    messages: List]
    tool_input_validation: str
    reflection_result: str
    plan: str
    subtask: str

# 2. 定义工具
#使用1中定义的工具

# 3. 定义图中的节点
def agent_executor_node(state: AgentState):
    # 模拟LLM决定调用工具
    subtask = state["subtask"]
    # 简单的判断逻辑,实际应由LLM决定
    if "计算" in subtask:
        # 模拟LLM生成工具调用参数
        tool_call_args = {'a': 10, 'b': 5, 'operation': 'add'}
        # 模拟LLM返回的工具调用指令
        return {"tool_call_args": tool_call_args, "tool_name": "simple_calculator"}
    return {"response": "无需工具,直接回答。"}

def validation_node(state: AgentState):
    # 前置校验节点
    tool_call_args = state.get("tool_call_args")
    if not tool_call_args:
        return {"tool_input_validation": "pass"}
    
    # 模拟校验逻辑:检查参数类型
    if not isinstance(tool_call_args['a'], (int, float)) or \
       not isinstance(tool_call_args['b'], (int, float)):
        return {"tool_input_validation": "fail", "reflection_result": "参数类型不正确,请将参数转换为数字。"}
    return {"tool_input_validation": "pass"}

def tool_execution_node(state: AgentState):
    # 工具执行节点
    tool_call_args = state.get("tool_call_args")
    if state["tool_input_validation"] == "fail":
        return {"tool_output": None, "tool_execution_status": "failed"}

    try:
        result = simple_calculator(**tool_call_args)
        return {"tool_output": result, "tool_execution_status": "su***ess"}
    except Exception as e:
        return {"tool_output": None, "tool_execution_status": "failed", "reflection_result": f"工具执行失败,错误:{e}"}

def reflection_node(state: AgentState):
    # 反思和修正节点
    if state["tool_execution_status"] == "failed":
        reflection_result = state["reflection_result"]
        # LLM生成修正提示词
        correction_prompt = f"反思:工具调用失败。原因:{reflection_result}。请重新思考并修正参数。"
        return {"messages": [HumanMessage(content=correction_prompt)]}
    return {"messages": [HumanMessage(content=f"工具执行成功。结果:{state['tool_output']}")]}

# 4. 构建LangGraph图
workflow = StateGraph(AgentState)
workflow.add_node("agent_executor", agent_executor_node)
workflow.add_node("validation", validation_node)
workflow.add_node("tool_execution", tool_execution_node)
workflow.add_node("reflection", reflection_node)

# 5. 定义图中的边和条件
workflow.add_edge(START, "agent_executor")
workflow.add_edge("agent_executor", "validation")
workflow.add_edge("validation", "tool_execution")

workflow.add_conditional_edges(
    "tool_execution",
    lambda state: "continue" if state["tool_execution_status"] == "su***ess" else "rethink",
    {"continue": "reflection", "rethink": "agent_executor"} # 失败时返回到agent_executor重新思考
)

workflow.add_edge("reflection", END)

# 6. 编译并运行
app = workflow.***pile()
# 模拟用户输入
initial_state = {"messages": [HumanMessage(content="计算10+5")], "subtask": "计算"}
final_state = app.invoke(initial_state)

print(final_state)

3.4 解决痛点三:规划与执行模式处理复杂业务逻辑

问题分析
长提示词中的业务规则和分析需求混合在一起,使得LLM难以一次性梳理出正确的工具调用路径 。这需要将复杂的决策过程解耦,并引入一个专门的“规划者”。

核心策略
采用**“规划与执行”(Plan-and-Execute)**模式 。这一模式将一个复杂任务的决策过程明确地分解为两个独立的阶段:规划阶段和执行阶段。

详细流程:

规划阶段
用户输入包含多步业务逻辑的复杂提示词,例如“请分析某公司的月度财务报告。首先,计算总利润和总收入。然后,分析利润率,并找出本月利润率低于20%的所有产品线。最后,生成一份报告,总结这些发现。”

Planning Agent(一个专门的Agent)接收该提示词,其唯一职责是进行任务分解。它会生成一个清晰的、分步骤的自然语言分析计划,不涉及任何具体计算或工具调用 。例如: 商品销售分析

Plan: [解析业务规则,生成查数代码,调用ReAct计算总利润和总收入, 分析利润率, 找出利润率低于20%的产品线, 总结发现进行深度洞察,生成报告]

这种任务分解过程将复杂的原始输入转化为一个结构化的、线性的执行列表。

执行阶段

Execution Agent逐一接收规划层生成的每个步骤。

由于每个步骤都是一个简短、明确的子任务(例如,“计算总利润和总收入”),Execution Agent更容易、更准确地判断何时需要调用哪个工具(例如,pandas_data_frame_processor)以及需要哪些参数。

在这一阶段,可以继续沿用第3.3节中介绍的**“反思循环”**来确保每个子任务的执行成功。每个子任务完成后,Execution Agent会向规划Agent报告执行结果,以便规划Agent决定下一步行动或在失败时进行重规划 。

并且不要在生成报告或者解读过程中进行工具调用的思考,判断是否调用工具,而是把调用工具的逻辑拆散到各个子任务中,通过单独的工具路由判断进行工具调用。

import autogen
from autogen.agentchat import AssistantAgent, UserProxyAgent
from typing import Dict, Union

# 1. 定义 Planning Agent
planner_agent = AssistantAgent(
    name="planner",
    llm_config={"config_list": [{"model": "gpt-4o"}]},
    system_message="""
    你是一个任务规划专家。你的任务是接收一个复杂的用户请求,并将其分解成一个清晰、分步的、可执行的计划。
    每个步骤都必须是简短、原子化的,并且可以独立执行。
    例如:
    用户请求:分析销售数据,找出总销售额并计算利润率。
    {复杂业务规则}
    你的计划:
    [ 
        "分析用户需求",
        "匹配业务规则",
        "拆解子问题",
        "查询数据",
        "计算总销售额",
        "计算总成本",
        "调用ReAct计算总利润",
        "调用ReAct计算利润率",
        "数据解读和生成建议",
        "生成报告"
    ]
    你只需返回一个包含所有步骤的JSON格式数组,不要返回其他任何内容。
    """
)

# 2. 定义 Execution Agent,它负责执行具体的任务
executor_agent = AssistantAgent(
    name="executor",
    llm_config={"config_list": [{"model": "gpt-4o"}]},
    system_message="""
    你是一个任务执行专家。你的任务是严格按照规划者提供的计划中的每一步进行操作。
    你有权使用以下工具来完成任务:
    - `sympy_calculator`:用于精确数学计算
    - `pandas_data_frame_processor`:用于数据处理和统计分析
    -...
    你将接收一个单一的、原子化的任务。你需要判断是否需要调用工具,然后执行它并返回结果。
    """
)

# 3. 定义一个用户代理,它充当 orchestrator 的角色
user_proxy_agent = UserProxyAgent(
    name="user_proxy",
    system_message="一个协调者,负责将用户的请求发送给规划者,并将规划者的计划发送给执行者。",
    human_input_mode="NEVER",  # 自动执行
    is_termination_msg=lambda x: "summarize" in x.get("content", "").lower(), # 当返回总结时停止
)

# 4. 构建多Agent协作流
# 这是一个简单的循环,实际可以通过Autogen的GroupChat或更复杂的编排模式实现
async def plan_and_execute_flow(request: str) -> Dict[str, Union[str, List[str]]]:
    # 步骤1:将用户请求发送给规划者
    chat_result = await user_proxy_agent.a_initiate_chat(
        recipient=planner_agent,
        message=request,
    )
    # 获取规划者的计划(JSON格式)
    plan = chat_result.summary.strip()
    try:
        plan_steps = json.loads(plan)
    except json.JSONDecodeError:
        return {"error": "无法解析规划者的输出"}

    results =
    # 步骤2:遍历计划的每个步骤,发送给执行者
    for step in plan_steps:
        print(f"\n--- 正在执行步骤: {step} ---")
        execution_result = await user_proxy_agent.a_initiate_chat(
            recipient=executor_agent,
            message=step,
        )
        results.append(execution_result.summary)
    
    # 步骤3:将所有结果发送给一个总结Agent(或返回)
    #... 在此省略,可以直接返回结果列表
    return {"plan": plan_steps, "results": results}

# 示例:运行流程
# asyncio.run(plan_and_execute_flow("请帮我分析一下A店铺的销售情况"))

通过上述的方案,可以有效地提高在复杂业务场景下ReAct的准确性和鲁棒性,以上的解决方案,我们汇总一下传统ReAct Agent 混合Agentic架构对比:

维度 传统ReAct Agent 混合Agentic架构
核心模式 思考与行动的单一线性循环 规划、反思、多智能体协作的混合模式
处理逻辑 仅依赖LLM的上下文判断 通过多层级Agent进行任务分解与分工
鲁棒性 缺乏错误自查与修正能力,调用易失败 具备反思与修正循环,能处理异常并自愈
适应性 依赖于硬编码工具,场景受限 采用动态工具集,能应对更广泛的复杂计算
复杂任务处理 难以在长提示词中准确决策 规划层将复杂任务分解为原子化子任务,简化决策
成本 较低(单次调用) 较高(多轮次调用)
转载请说明出处内容投诉
CSS教程网 » ReAct架构的 AI Agent很简单?看看复杂业务场景下ReAct 的痛点和解决思路

发表评论

欢迎 访客 发表评论

一个令你着迷的主题!

查看演示 官网购买