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

相关推荐
七夜zippoe8 小时前
OpenClaw 实战:智能文档助手——从问答到生成的完整方案
人工智能·python·机器学习·openclaw·智能文档
AC赳赳老秦9 小时前
时间开销自动统计:OpenClaw 记录工作任务时长、分析时间分配、给出优化建议
java·大数据·开发语言·python·自动化·deepseek·openclaw
AC赳赳老秦1 天前
GTD 工作法落地:用 OpenClaw 搭建收集-整理-执行-复盘全流程自动化工作流
大数据·运维·人工智能·信息可视化·自动化·deepseek·openclaw
向哆哆2 天前
【Bug已解决】openclaw encoding error / invalid byte sequence — OpenClaw 编码错误解决方案
utf-8·gbk·bug解决·openclaw·编码错误
无心水4 天前
【全域智能营销实战】10、三大引擎协同工作流:从用户消息到智能决策的完整链路
人工智能·springai·openclaw·顶尖架构师·全域智能营销·harmess·herness
Android洋芋5 天前
OpenClaw 数据采集实战入门
openclaw
Koma-forever5 天前
openclaw的安装
openclaw
AC赳赳老秦6 天前
Excel 动态仪表盘制作:用 OpenClaw 自动处理数据、生成交互式图表并实时更新仪表盘
大数据·服务器·后端·测试用例·excel·deepseek·openclaw
龙亘川6 天前
开源本地 AI 智能体网关 OpenClaw 深度实践:架构解析、全场景部署与自动化落地指南
人工智能·架构·开源·openclaw
AC赳赳老秦7 天前
传感器数据自动汇总:OpenClaw 采集多类传感器数据、清洗入库、生成趋势分析
java·人工智能·python·自动化·github·php·openclaw