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

相关推荐
宇文仲竹1 天前
Docker 一键部署 SearXNG 私有搜索引擎 (给 openclaw 用代替 web_search)
openclaw
AC赳赳老秦2 天前
企业级合规审计体系:用 OpenClaw 落地采集全链路留痕,自动生成合规审计报告
java·python·django·beautifulsoup·php·deepseek·openclaw
AC赳赳老秦3 天前
个保法下数据处理:OpenClaw 自动过滤公开数据中的个人信息,保障采集分析合规性
java·python·sqlite·json·php·deepseek·openclaw
海兰4 天前
【插件】Logbook 插件完全指南(适配 Ubuntu 24.04)
linux·运维·人工智能·ubuntu·agent·openclaw
AC赳赳老秦4 天前
企业标签体系搭建:基于 OpenClaw 采集的多维度公开数据,构建企业画像标签库
java·运维·服务器·python·信息可视化·deepseek·openclaw
海兰5 天前
【插件】OpenClaw 上下文引擎指南
人工智能·agent·openclaw
AC赳赳老秦5 天前
多源数据关联挖掘:OpenClaw 打通工商、招投标、专利公开数据,挖掘企业业务关联关系
java·c语言·javascript·c++·python·deepseek·openclaw
七夜zippoe6 天前
OpenClaw 边缘部署实战:IoT 场景下的轻量级 Agent 集群与离线自治
agent·集群·lot·轻量级·边缘部署·openclaw
梦想的颜色7 天前
Pi‑Agent 深度硬核解析:极简主义编程智能体,OpenClaw 底层内核源码剖析
大模型应用·ai 编程·codingagent·openclaw·piagent·agent 框架源码·2026ai 工具
AC赳赳老秦9 天前
语义采集进阶实战:利用 OpenClaw AI 语义识别自动提取网页核心信息,无需手动编写选择器
java·运维·服务器·python·信息可视化·deepseek·openclaw