OpenClaw Agent Loop 机制源码深度分析(三)

生命周期

完整生命周期

启动
接收请求
加载配置
构建提示词
加载历史
开始对话
处理响应
有工具调用?
继续对话
完成
发生错误
重试
失败
Idle
Initializing
Preparing
BuildingPrompt
LoadingHistory
CallingLLM
Processing
HasTools
|是|
执行工具
|否|
需要压缩? 压缩上下文 返回结果
Error

状态转换

typescript 复制代码
// 状态枚举
enum AgentLoopState {
  IDLE = "idle",
  INITIALIZING = "initializing",
  PREPARING = "preparing",
  BUILDING_PROMPT = "building_prompt",
  LOADING_HISTORY = "loading_history",
  CALLING_LLM = "calling_llm",
  PROCESSING = "processing",
  EXECUTING = "executing",
  COMPACTING = "compacting",
  RETURNING = "returning",
  ERROR = "error",
}

// 状态机
class AgentLoopStateMachine {
  private currentState: AgentLoopState = AgentLoopState.IDLE;
  
  transition(event: AgentLoopEvent): void {
    switch (this.currentState) {
      case AgentLoopState.IDLE:
        if (event === "REQUEST") {
          this.currentState = AgentLoopState.INITIALIZING;
        }
        break;
      // ... 其他转换
    }
  }
}

Mermaid 流程图

完整消息处理流程

  1. 完成阶段 4. 执行循环 3. 构建阶段 2. 准备阶段 1. 接收阶段 是

    接收消息
    解析参数
    验证会话
    加载配置
    解析模型
    检查认证
    准备工作区
    构建系统提示词
    加载引导文件
    合并 Skills
    准备工具定义
    发送用户消息
    调用 LLM
    有工具?
    执行工具
    生成回复
    压缩上下文
    保存会话
    返回结果

错误恢复流程

上下文溢出


认证失败


速率限制


其他
发生错误
错误类型?
压缩
成功?
重试
返回错误
切换认证
成功?
等待
超时?
记录日志

上下文管理流程





新消息
添加到历史
计算令牌数
超过阈值?
直接使用
估算压缩
可压缩?
执行压缩
丢弃旧历史
LLM 调用


源码关键代码解读

1. 主运行入口

typescript 复制代码
// run.ts

export async function runEmbeddedPiAgent(
  params: RunEmbeddedPiAgentParams,
): Promise<EmbeddedPiRunResult> {
  const started = Date.now();
  
  // 1. 解析执行通道(支持优先级队列)
  const sessionLane = resolveSessionLane(params.sessionKey || params.sessionId);
  const globalLane = resolveGlobalLane(params.lane);
  
  return enqueueSession(() =>
    enqueueGlobal(async () => {
      try {
        // 2. 准备工作区
        const workspace = resolveRunWorkspaceDir({
          workspaceDir: params.workspaceDir,
          sessionKey: params.sessionKey,
          agentId: params.agentId,
        });
        
        // 3. 解析模型配置
        const { model, authStorage } = await resolveModel({
          provider: params.provider,
          modelId: params.model,
          agentDir: params.agentDir,
        });
        
        // 4. 执行
        const result = await runEmbeddedAttempt({
          ...params,
          model,
          authStorage,
          workspaceDir: workspace.workspaceDir,
        });
        
        // 5. 记录指标
        logRunMetrics({
          runId: params.runId,
          duration: Date.now() - started,
          result,
        });
        
        return result;
      } catch (error) {
        return handleRunError(error);
      }
    })
  );
}

2. 执行尝试循环

typescript 复制代码
// run/attempt.ts

export async function runEmbeddedAttempt(params): Promise<EmbeddedRunAttemptResult> {
  const { sessionManager, tools } = params;
  
  // 注册运行状态
  const handle = registerRun({
    sessionId: params.sessionId,
    sessionManager,
  });
  
  try {
    // 发送用户消息
    await sessionManager.appendUserMessage(params.message);
    
    // 主循环
    let iterations = 0;
    const maxIterations = params.maxIterations ?? 20;
    
    while (iterations < maxIterations) {
      iterations++;
      
      // 调用 LLM
      const response = await sessionManager.complete({
        model: params.modelId,
        tools,
        thinking: params.thinkLevel,
        
        onChunk: (chunk) => {
          // 流式处理
          handleStreamingChunk(chunk);
        },
      });
      
      // 检查工具调用
      if (response.tool_calls?.length > 0) {
        // 执行所有工具调用
        for (const toolCall of response.tool_calls) {
          const result = await executeTool({
            name: toolCall.name,
            arguments: toolCall.arguments,
          });
          
          // 添加工具结果
          await sessionManager.appendToolResult({
            callId: toolCall.id,
            name: toolCall.name,
            content: result,
          });
        }
        
        continue;  // 继续循环
      }
      
      // 完成
      return {
        success: true,
        reply: response.content,
        usage: response.usage,
      };
    }
    
    // 超出最大迭代次数
    return {
      success: false,
      error: "Max iterations exceeded",
    };
  } finally {
    unregisterRun(params.sessionId);
  }
}

3. 会话压缩

typescript 复制代码
// compact.ts

export async function compactEmbeddedPiSession(params): Promise<EmbeddedPiCompactResult> {
  const { sessionManager, config } = params;
  
  // 1. 检查是否需要压缩
  const needsCompaction = await sessionManager.needsCompaction();
  if (!needsCompaction) {
    return { ok: true, compacted: false };
  }
  
  // 2. 获取压缩估算
  const estimate = await sessionManager.compactionEstimate();
  
  // 3. 执行压缩
  const result = await sessionManager.compact({
    systemPrompt: buildSystemPrompt(),
    reserveTokens: resolveCompactionReserveTokensFloor(config),
    
    // 压缩策略
    strategy: "summarize",  // 总结模式
    targetTokens: estimate.targetTokens,
  });
  
  if (result.success) {
    return {
      ok: true,
      compacted: true,
      originalTokens: estimate.originalTokens,
      compactedTokens: result.tokenCount,
    };
  }
  
  return {
    ok: false,
    compacted: false,
    reason: result.error,
  };
}

4. 工具执行

typescript 复制代码
// 工具执行逻辑
async function executeTool(params) {
  const { name, arguments } = params;
  
  // 1. 查找工具定义
  const toolDef = findToolDefinition(name);
  if (!toolDef) {
    return { error: `Unknown tool: ${name}` };
  }
  
  // 2. 验证参数
  const validation = validateParameters(toolDef.schema, arguments);
  if (!validation.valid) {
    return { error: validation.error };
  }
  
  // 3. 检查策略
  const policyCheck = await checkToolPolicy({
    toolName: name,
    arguments,
  });
  if (!policyCheck.allowed) {
    return { error: policyCheck.reason };
  }
  
  // 4. 执行工具
  try {
    const result = await toolDef.handler(arguments);
    return { success: true, result };
  } catch (error) {
    return { error: error.message };
  }
}

5. 错误分类

typescript 复制代码
// 错误分类和恢复
function classifyError(error: Error): ErrorCategory {
  const message = error.message;
  
  if (message.includes("context_length_exceeded")) {
    return "CONTEXT_OVERFLOW";
  }
  
  if (message.includes("rate_limit")) {
    return "RATE_LIMIT";
  }
  
  if (message.includes("authentication")) {
    return "AUTH_ERROR";
  }
  
  if (message.includes("timeout")) {
    return "TIMEOUT";
  }
  
  return "UNKNOWN";
}

// 错误恢复策略
async function recoverFromError(
  error: Error,
  context: RunContext,
): Promise<RecoveryResult> {
  const category = classifyError(error);
  
  switch (category) {
    case "CONTEXT_OVERFLOW":
      // 尝试压缩
      const compactResult = await compactSession(context);
      if (compactResult.success) {
        return { action: "retry", reason: "compacted" };
      }
      return { action: "fail", reason: "cannot_compact" };
    
    case "RATE_LIMIT":
      // 等待后重试
      await sleep(getRetryDelay(error));
      return { action: "retry", reason: "rate_limited" };
    
    case "AUTH_ERROR":
      // 尝试切换认证
      const failoverResult = await attemptAuthFailover(context);
      if (failoverResult.success) {
        return { action: "retry", reason: "auth_failover" };
      }
      return { action: "fail", reason: "auth_failed" };
    
    default:
      return { action: "fail", reason: error.message };
  }
}

常见问题

Q1: Agent Loop 和普通对话有什么区别?

方面 Agent Loop 普通对话
工具调用 支持自动调用工具 仅文本交互
迭代 循环直到无工具调用 单轮响应
上下文 自动压缩管理 手动管理
流式 支持增量响应 等待完整响应

Q2: 如何限制循环次数?

typescript 复制代码
// 配置最大迭代次数
await runEmbeddedPiAgent({
  message: "...",
  maxIterations: 10,  // 最多 10 轮(LLM 调用)
});

Q3: 上下文压缩会影响质量吗?

压缩策略:

  • summarize: 总结旧消息,保留关键信息
  • truncate: 截断超长消息
  • hybrid: 结合总结和截断
typescript 复制代码
// 配置压缩策略
{
  agents: {
    defaults: {
      compaction: {
        strategy: "summarize",
        reserveTokens: 2000,
      },
    },
  },
}

Q4: 如何调试循环过程?

typescript 复制代码
// 启用调试日志
log.setLevel("debug");

// 查看详细执行流程
await runEmbeddedPiAgent({
  message: "...",
  debug: {
    logSteps: true,
    logTools: true,
    logPrompt: true,
  },
});

Q5: 工具调用失败会怎样?

可恢复
致命


工具调用失败
错误类型?
重试
返回错误结果
重试次数?
等待后重试

Q6: 如何处理长时间运行的任务?

typescript 复制代码
// 设置超时
await runEmbeddedPiAgent({
  message: "分析这个大型项目",
  timeoutMs: 300000,  // 5 分钟超时
});

// 或者使用后台执行
await sessions_spawn({
  task: "深度分析代码库",
  runTimeoutSeconds: 1800,  // 30 分钟
  cleanup: "keep",  // 保留结果
});

Q7: 流式响应如何工作?

typescript 复制代码
// 启用流式
await runEmbeddedPiAgent({
  message: "写一篇长文章",
  streaming: true,
  
  onChunk: (chunk) => {
    // 实时接收增量内容
    process.stdout.write(chunk.content);
  },
  
  onComplete: (result) => {
    // 完成后处理
    console.log("\n完成!");
  },
});

Q8: 如何自定义工具策略?

yaml 复制代码
# openclaw.yaml
tools:
  sandbox:
    tools:
      allow:
        - read
        - write
        - exec
      deny:
        - dangerous_command

总结

OpenClaw Agent Loop 机制核心要点:

架构设计

  1. 模块化 - 分离运行、尝试、压缩等功能
  2. 状态管理 - 跟踪运行状态和等待者
  3. 错误恢复 - 多种错误类型对应不同恢复策略
  4. 上下文优化 - 自动压缩避免超出窗口限制

关键流程



消息
构建提示词
LLM 调用
工具?
执行工具
返回结果
压缩

最佳实践

  1. 合理设置超时 - 避免长时间阻塞
  2. 配置压缩策略 - 根据需求选择总结或截断
  3. 工具白名单 - 只暴露必要的工具
  4. 监控执行 - 使用日志追踪执行流程

掌握这些概念,就能深入理解并高效使用 OpenClaw 的 Agent Loop 机制!

相关推荐
AC赳赳老秦1 小时前
供应链专员提效:OpenClaw自动跟踪物流信息、更新库存数据,异常自动提醒
java·大数据·服务器·数据库·人工智能·自动化·openclaw
无心水2 小时前
【Hermes:安全、权限与生产环境】40、运行 Hermes 前的生命线:安全审计清单与 11 个必须检查的配置项
人工智能·安全·mcp协议·openclaw·养龙虾·hermes·honcho
跟尚西学PowerBI10 小时前
【供应链AI实践案例】OpenClaw+PowerBI 打造 AI 智能库存预警实战
大数据·人工智能·数据分析·openclaw
AC赳赳老秦11 小时前
文案策划提效:OpenClaw批量生成活动文案、宣传海报配文,适配不同渠道调性
java·大数据·服务器·人工智能·python·deepseek·openclaw
三棱球13 小时前
Hermes 极简安装教程
openclaw
鼎道开发者联盟14 小时前
让多端同步看到 OpenClaw tool 事件:两种无需大改源码的实现方案
websocket·openclaw
第404块砖头15 小时前
WorkBuddy清理Claw历史会话指南
ai·openclaw·workbuddy
组合缺一16 小时前
OpenClaw vs SolonCode:绑定飞书与钉钉,到底谁更简单?
ai·钉钉·飞书·ai编程·数字员工·openclaw·soloncode
七夜zippoe16 小时前
OpenClaw Browser:浏览器控制入门
ai·自动化·浏览器·browser·openclaw
周易宅2 天前
2026年自主智能体系统架构演进:OpenClaw与Hermes Agent在现代软件生态中的定位、机制与应用
ai·系统架构·openclaw·hermes