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 机制!

相关推荐
带娃的IT创业者6 小时前
解密OpenClaw系列08-OpenClaw组件交互关系(2)
软件工程·ai编程·代码规范·ai智能体·openclaw·编程文档·组件设计
政安晨7 小时前
政安晨【人工智能项目随笔】OpenClaw网关与子节点完整配对指南——从零构建分布式AI助手网络
人工智能·ai网关·openclaw·分布式ai助手网络·openclaw分布式子节点·分布式ai节点·主节点-子节点
love530love7 小时前
【OpenClaw 本地实战 Ep.2】零代码对接:使用交互式向导快速连接本地 LM Studio 用 CUDA GPU 推理
人工智能·windows·gpu·cuda·ollama·lm studio·openclaw
脆皮瞎19 小时前
openclaw本地部署(windows)
windows·ai·openclaw
凯子坚持 c1 天前
在 openJiuwen 里把在线小工具搬回本地
人工智能·windows·stable diffusion·openteledb·openclaw
caicongyang1 天前
OpenClaw Agent Loop 机制源码深度分析(一)
ai·openclaw·大红虾
SJMP19741 天前
OpenClaw 本地 Windows 部署(WSL),对接钉钉
windows·钉钉·阿里云百炼·openclaw
liangshanbo12151 天前
OpenClaw 安装与钉钉机器人配置总结
钉钉·openclaw
带娃的IT创业者1 天前
解密OpenClaw系列04-OpenClaw设计模式应用
设计模式·软件工程·软件架构·ai agent·ai智能体开发·openclaw