ReAct Agent Loop原理解析

ReAct Agent Loop 原理解析

一、什么是 ReAct?

ReAct = Reasoning + Acting(推理 + 行动)

ReAct 是一种让 AI Agent 能够通过"思考-行动-观察"的循环来解决复杂问题的范式。它结合了推理(Reasoning)和行动(Acting),让 Agent 不仅能够执行任务,还能解释为什么要这样做。

核心思想

Reasoning (推理): 思考要做什么
    ↓
Acting (行动): 执行工具完成任务
    ↓
Observation (观察): 查看执行结果
    ↓
回到 Reasoning (继续推理)

二、Agent Loop 工作原理

Agent Loop 是 ReAct 的具体实现,是一个循环处理流程,Agent 通过不断重复以下步骤来完成用户任务。

循环流程图

┌─────────────────────────────────────────┐
│         Agent Loop (循环)              │
├─────────────────────────────────────────┤
│  1. 接收用户消息                        │
│  2. 构建上下文 (历史/记忆/技能/性格)      │
│  3. 调用 LLM 生成回复                   │
│  4. 解析 LLM 响应                      │
│     ├─ 有工具调用? → 执行工具            │
│     └─ 无工具调用? → 返回答案            │
│  5. 将工具结果反馈给 LLM                │
│  6. 回到步骤 3,继续循环 (直到完成)       │
└─────────────────────────────────────────┘

三、具体示例

假设用户问:“今天天气怎么样?”

Agent Loop 执行过程:

【迭代 1】
────────────────────────────
用户: 今天天气怎么样?

构建上下文:
  - 系统提示: 你是AI助手,名叫小C...
  - 历史对话: 无
  - 智能记忆: 无
  - 技能说明: 可用web_search工具

调用 LLM:
  LLM 思考: 我需要查询天气信息
  LLM 决定: 调用 web_search 工具
  LLM 输出: 工具调用 {"tool": "web_search", "args": {"query": "今天天气 北京"}}

工具调用:
  - 工具: web_search
  - 参数: {"query": "今天天气 北京"}

观察结果:
  - 返回: "北京今天晴,气温15-25℃,空气质量优"
────────────────────────────

【迭代 2】
────────────────────────────
将结果反馈给 LLM:
  消息: 工具执行结果: 北京今天晴,气温15-25℃,空气质量优

调用 LLM:
  LLM 思考: 已获取天气信息,可以回答用户
  LLM 决定: 不需要再调用工具,直接回复

最终回复:
  "北京今天天气晴朗,气温在15-25摄氏度之间,空气质量优,适合外出活动。"
────────────────────────────

循环结束

四、代码实现

1. 基础版 Agent Loop

class AgentLoop:
    """Agent 核心循环类"""
    
    def __init__(self, provider, tools, max_iterations=20):
        self.provider = provider  # LLM 提供商
        self.tools = tools        # 工具注册表
        self.max_iterations = max_iterations
    
    async def process_message(self, message):
        """处理用户消息"""
        # 1. 构建初始消息
        messages = [
            {"role": "system", "content": "你是AI助手..."},
            {"role": "user", "content": message}
        ]
        
        # 2. 开始循环
        iteration = 0
        final_answer = None
        
        while iteration < self.max_iterations:
            iteration += 1
            
            # 3. 调用 LLM
            response = await self.provider.chat(
                messages=messages,
                tools=self.tools.get_definitions()
            )
            
            # 4. 检查是否有工具调用
            if response.has_tool_calls:
                # 5. 执行工具
                for tool_call in response.tool_calls:
                    result = await self.tools.execute(
                        tool_call.name,
                        tool_call.arguments
                    )
                    
                    # 6. 将结果反馈给 LLM
                    messages.append({
                        "role": "tool",
                        "tool_call_id": tool_call.id,
                        "content": result
                    })
                
                # 7. 继续循环
                continue
            
            # 8. 无工具调用,得到最终答案
            final_answer = response.content
            break
        
        return final_answer

2. 增强版 Agent Loop(带流式响应和重试)

class AgentLoop:
    """增强版 Agent 循环类"""
    
    def __init__(
        self,
        provider,
        tools,
        max_iterations=25,
        max_retries=3,
        retry_delay=1.0
    ):
        self.provider = provider
        self.tools = tools
        self.max_iterations = max_iterations
        self.max_retries = max_retries
        self.retry_delay = retry_delay
    
    async def process_message(self, message, cancel_token=None):
        """处理用户消息并生成流式响应"""
        # 1. 构建上下文
        messages = [
            {"role": "system", "content": "你是AI助手..."},
            {"role": "user", "content": message}
        ]
        
        # 2. 开始循环
        iteration = 0
        total_tool_calls = 0
        final_content = ""
        
        while iteration < self.max_iterations:
            # 检查取消
            if cancel_token and cancel_token.is_cancelled:
                return
            
            iteration += 1
            
            # 3. 调用 LLM(流式)
            tool_calls_buffer = []
            
            async for chunk in self.provider.chat_stream(
                messages=messages,
                tools=self.tools.get_definitions()
            ):
                if chunk.is_content and chunk.content:
                    final_content += chunk.content
                    yield chunk.content  # 实时推送
                
                if chunk.is_tool_call and chunk.tool_call:
                    tool_calls_buffer.append(chunk.tool_call)
            
            # 4. 执行工具(带重试)
            for tool_call in tool_calls_buffer:
                # 检查取消
                if cancel_token and cancel_token.is_cancelled:
                    return
                
                total_tool_calls += 1
                
                # 重试机制
                result = None
                for attempt in range(self.max_retries):
                    try:
                        result = await self.tools.execute(
                            tool_call.name,
                            tool_call.arguments
                        )
                        logger.debug(f"Tool {tool_call.name} executed successfully")
                        break
                    except Exception as e:
                        logger.warning(f"Tool {tool_call.name} failed (attempt {attempt+1})")
                        if attempt < self.max_retries - 1:
                            await asyncio.sleep(self.retry_delay)
                
                # 5. 添加结果到消息列表
                messages.append({
                    "role": "tool",
                    "tool_call_id": tool_call.id,
                    "name": tool_call.name,
                    "content": result or "工具执行失败"
                })
            
            # 6. 判断是否继续
            if not tool_calls_buffer:
                break  # 无工具调用,结束
        
        # 7. 保存会话
        if final_content:
            self.session_manager.save(message, final_content)

五、关键特性

1. 循环次数限制

防止无限循环,通常限制在 20-50 次:

self.max_iterations = 25

while iteration < self.max_iterations:
    iteration += 1
    # ... 执行逻辑

2. 工具调用链

用户问题 → 工具1 → 结果1 → 工具2 → 结果2 → ... → 最终答案

3. 上下文累积

messages = [
    {"role": "system", "content": "..."},
    {"role": "user", "content": "查询天气"},
    {"role": "assistant", "content": "...", "tool_calls": [...]},
    {"role": "tool", "content": "天气结果"},
    {"role": "assistant", "content": "根据天气结果..."}  # LLM基于工具结果继续思考
]

4. 流式响应

async for chunk in self.provider.chat_stream(messages, tools):
    if chunk.is_content:
        yield chunk.content  # 实时返回给用户

5. 取消支持

if cancel_token and cancel_token.is_cancelled:
    logger.info("Agent loop cancelled")
    return

六、循环终止条件

Agent Loop 在以下情况终止:

条件说明
无工具调用LLM 决定不再需要工具,直接给出答案
达到迭代上限防止无限循环,强制停止
用户取消用户主动取消任务
发生错误工具执行失败且重试后仍失败

七、复杂示例

用户问题: “帮我创建一个Python爬虫,抓取新闻网站的标题”

【迭代 1】
LLM: 我需要先查看用户的文件结构
工具: list_dir(path=".")

【迭代 2】
LLM: 我需要创建爬虫文件
工具: write_file(path="crawler.py", content="import requests...")

【迭代 3】
LLM: 我需要测试爬虫是否工作
工具: exec(command="python crawler.py")

【迭代 4】
LLM: 爬虫运行成功,已完成任务
最终答案: "已创建 crawler.py,测试成功,可以抓取新闻标题了。"

八、不同框架的实现对比

NanoBot - 简洁同步

while iteration < max_iterations:
    response = await provider.chat(messages, tools)
    if response.has_tool_calls:
        for tool_call in response.tool_calls:
            result = await tools.execute(tool_call.name, args)
            messages.append(tool_result)
    else:
        break

CountBot - 增强异步流式

while iteration < max_iterations:
    async for chunk in provider.chat_stream(messages, tools):
        if chunk.is_content:
            yield chunk.content  # 流式推送
        if chunk.is_tool_call:
            tool_calls_buffer.append(chunk.tool_call)
    
    for tool_call in tool_calls_buffer:
        for attempt in range(max_retries):  # 重试机制
            try:
                result = await tools.execute(tool_name, args)
                break
            except Exception:
                await asyncio.sleep(retry_delay)

OpenClaw - Pi框架 + 多级重试

// 32-160次重试,带Auth Profile轮换
const maxRetries = resolveMaxRunRetryIterations(profileCount);
for (let attempt = 0; attempt < maxRetries; attempt++) {
  try {
    const result = await runEmbeddedAttempt({
      messages,
      tools,
      thinkingLevel
    });
    return result;
  } catch (error) {
    if (isFailoverError(error)) {
      await markAuthProfileFailure(profile);
      continue; // 切换到下一个Auth Profile
    }
  }
}

九、最佳实践

1. 工具设计原则

  • 单一职责: 每个工具只做一件事
  • 参数清晰: 使用清晰的参数名称和描述
  • 错误处理: 返回有意义的错误信息
  • 幂等性: 相同输入产生相同输出

2. 上下文管理

  • 历史限制: 限制历史消息数量,避免上下文溢出
  • 记忆总结: 定期总结对话,压缩上下文
  • 技能加载: 按需加载技能,减少提示词长度

3. 性能优化

  • 异步并发: 使用异步IO提高并发性能
  • 缓存机制: 缓存工具结果,避免重复调用
  • 超时控制: 设置工具执行超时,防止阻塞

4. 安全考虑

  • 输入验证: 验证工具参数,防止注入攻击
  • 权限控制: 限制工具访问范围
  • 审计日志: 记录所有工具调用

十、总结

Agent Loop 是 AI Agent 的心脏,它:

  1. 持续循环: 不断推理和行动
  2. 工具调用: 根据需要调用工具
  3. 上下文累积: 记录每一步的结果
  4. 最终决策: 判断何时给出最终答案

简单来说:

Agent Loop = 思考 → 行动 → 观察 → 再思考,直到完成任务

核心优势

  • 灵活性: 可以处理各种复杂任务
  • 可解释性: 每一步都可以追溯和解释
  • 可扩展性: 通过添加工具扩展能力
  • 自主性: 能够自主决策和执行

应用场景

  • 代码生成: 编写、测试、调试代码
  • 数据分析: 查询、分析、可视化数据
  • 任务自动化: 执行复杂的自动化任务
  • 知识检索: 搜索、总结、整合信息
  • 问题解决: 分析问题、制定方案、执行解决

ReAct Agent Loop 是现代 AI Agent 的核心架构,它让 AI 不仅能够回答问题,还能够思考、行动、解决实际问题