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

相关推荐
进击的雷神2 天前
把 Claude/GPT 接进飞书、Telegram、Discord:一条命令跑起来的 OpenClaw 部署工具
gpt·飞书·openclaw
鬼手点金3 天前
opencode-隐私优先配置
服务器·前端·javascript·bug·openclaw
AC赳赳老秦6 天前
OpenClaw 与 FineBI 联动方案:公开数据自动采集与实时业务分析看板实践
大数据·开发语言·python·php·finebi·deepseek·openclaw
dtsola7 天前
一个人怎么指挥一支 AI 队伍
人工智能·程序员·ai创业·独立开发者·openclaw·一人公司·小遥claw
七夜zippoe7 天前
Agent 自动化测试:怎么给“会自己决策“的程序写测试
ai·自动化·agent·测试·openclaw
AC赳赳老秦7 天前
采集行为合规自检:OpenClaw 自动校验 robots 协议与采集频率,规避违规采集风险
java·开发语言·c++·python·php·deepseek·openclaw
AC赳赳老秦8 天前
OpenClaw 数据引用规范自动生成:为公开数据构建可信来源标注与标准引用体系
大数据·开发语言·汇编·数据库·人工智能·deepseek·openclaw
AC赳赳老秦9 天前
数据采集全链路审计留痕:用 OpenClaw 实现合规审计与追溯
开发语言·汇编·python·php·swift·deepseek·openclaw
AC赳赳老秦12 天前
公开音频转写信息提取:OpenClaw 处理发布会与听证会文本并提取核心决策信息
大数据·开发语言·汇编·数据库·人工智能·deepseek·openclaw
AC赳赳老秦12 天前
财报附注表格精准提取:OpenClaw 从 PDF 年报附注挖掘隐藏明细,补齐财务分析维度
java·汇编·c++·python·青少年编程·deepseek·openclaw