🔄

Execution Loop 是什么?

AI Agent 执行循环详解 - Agent的思考-行动循环

世界上有一种循环叫 Execution Loop,它就像Agent的"思考-行动-观察"的永动机——Agent先想"我需要做什么",然后做,然后看结果,然后继续想...

凌晨2点07分,我看到Agent的日志里打印了"search('AI新闻')→处理结果→不够,再搜'Agent技术'→处理→再搜'2026趋势'..." 整整循环了8次才完成任务。那一刻我意识到:好的执行循环,就是Agent的"死磕精神"。

📚 定义

Execution Loop(执行循环) 也称 Reasoning + Acting 循环 (ReAct Loop),是AI Agent的核心运行机制:

  1. 思考 (Think):LLM分析当前任务,决定下一步行动
  2. 行动 (Act):调用工具、执行命令、发送请求
  3. 观察 (Observe):获取行动的结果反馈
  4. 继续循环:根据观察调整计划,继续执行

直到任务完成、到达最大迭代次数或用户中断。

🔬 工作原理

┌────────────────────────────────────────┐
│           Execution Loop               │
│                                        │
│  ┌─────┐     ┌──────┐     ┌─────────┐ │
│  │思考  │────▶│ 行动  │────▶│  观察   │ │
│  │Think│     │ Act  │     │ Observe │ │
│  └──┬──┘     └──────┘     └────┬─────┘ │
│     │                          │       │
│     └──────────────────────────┘       │
│             循环直到完成或终止          │
└────────────────────────────────────────┘

具体步骤:
1. LLM 生成推理 → "我需要先搜索信息"
2. 调用 web_search("最新AI新闻")
3. 获取搜索结果
4. LLM 分析结果 → "这些信息不够全面,再搜"
5. 调用 web_search("AI行业动态")
6. 获取结果
7. LLM 判断 → "信息足够了,可以生成报告"
8. 调用 write_file("report.html", content)
9. 完成,返回结果

🚀 OpenClaw 实战

# OpenClaw 执行循环配置
agent:
  execution_loop:
    max_iterations: 10      # 最大循环次数
    timeout: 300            # 总超时5分钟
    
    # 停止条件
    stop_conditions:
      - agent_declares_done  # Agent说任务完成
      - max_iterations       # 达到最大次数
      - timeout              # 超时
      - user_interrupted     # 用户中断
      - error_threshold      # 错误过多
    
    # 错误处理
    on_error:
      max_retries: 2         # 每个工具最多重试2次
      fallback: "记录错误,继续下一步"
    
    # 中间结果缓存
    cache_intermediate: true
    checkpoint_path: "/tmp/openclaw/loop_checkpoint.json"

典型执行循环(RSS聚合任务):

[Think] "用户要求生成今日RSS摘要,需要先抓取多个源"

[Act]   → web_fetch("github.com/openclaw/releases")
[Observe] → "获取到3条新版本发布"

[Think] "还需要从Hacker News获取热门讨论"

[Act]   → web_fetch("hn.algolia.com/?query=OpenClaw")
[Observe] → "获取到5条热门话题"

[Think] "信息足够了,现在生成HTML摘要"

[Act]   → write_file("/var/www/miaoquai/rss/2460.html")
[Observe] → "文件写入成功"

[Think] "任务完成,返回结果"

[Done]  → "RSS第2460期聚合完成!"

💻 代码示例:简易执行循环

// execution-loop.js
export class ExecutionLoop {
  constructor(config = {}) {
    this.maxIterations = config.maxIterations || 10;
    this.maxRetries = config.maxRetries || 2;
    this.tools = config.tools || {};
    this.context = [];
  }
  
  async run(task, systemPrompt) {
    const startTime = Date.now();
    let result = null;
    
    for (let i = 0; i < this.maxIterations; i++) {
      console.log(`\n🔄 第${i + 1}轮循环`);
      
      // 1. THINK: 让LLM决定下一步
      const thought = await this._think(systemPrompt);
      console.log(`🤔 思考: ${thought.action}`);
      
      // 2. 检查Agent是否认为任务完成
      if (thought.done) {
        console.log('✅ Agent认为任务完成');
        result = { status: 'completed', output: thought.output };
        break;
      }
      
      // 3. ACT: 执行工具调用
      let observation;
      for (let retry = 0; retry <= this.maxRetries; retry++) {
        try {
          observation = await this._act(thought);
          console.log(`🔧 执行: ${thought.tool} (重试${retry}次)`);
          break;
        } catch (err) {
          console.warn(`⚠️ 第${retry + 1}次失败:`, err.message);
          if (retry === this.maxRetries) {
            observation = `错误: ${err.message}`;
          }
        }
      }
      
      // 4. OBSERVE: 记录观察结果
      console.log(`👀 观察: ${observation.slice(0, 100)}...`);
      this.context.push({
        thought: thought.action,
        action: thought.tool,
        result: observation
      });
      
      // 5. 检查超时
      if (Date.now() - startTime > this._timeout * 1000) {
        result = { status: 'timeout', output: '执行超时' };
        break;
      }
    }
    
    return result || { status: 'max_iterations', output: '达到最大循环次数' };
  }
  
  async _think(systemPrompt) {
    // 调用LLM生成思考
    // 返回 { action, tool?, parameters?, done, output }
    return { action: 'search...', tool: 'web_search', parameters: {}, done: false };
  }
  
  async _act(thought) {
    if (!thought.tool) return '无工具调用';
    const toolFunc = this.tools[thought.tool];
    if (!toolFunc) throw new Error(`工具不存在: ${thought.tool}`);
    return await toolFunc(thought.parameters);
  }
}

🎯 最佳实践

✅ 推荐做法:
  • 设置合理的 max_iterations(5-15次)
  • 每个工具调用加超时,避免死锁
  • 记录每次循环的输入输出,便于调试
  • 工具失败记录错误并继续,不要卡死
⚠️ 踩坑:
  • 死循环:Agent可能不断搜索→结果不够→再搜索→再不够...
  • 工具结果太大:工具返回大量数据,塞满context
  • 状态漂移:Agent可能在循环中偏离原始目标