生命周期
完整生命周期
启动
接收请求
加载配置
构建提示词
加载历史
开始对话
处理响应
有工具调用?
继续对话
完成
发生错误
重试
失败
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 流程图
完整消息处理流程
- 完成阶段 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 机制核心要点:
架构设计
- 模块化 - 分离运行、尝试、压缩等功能
- 状态管理 - 跟踪运行状态和等待者
- 错误恢复 - 多种错误类型对应不同恢复策略
- 上下文优化 - 自动压缩避免超出窗口限制
关键流程
是
否
消息
构建提示词
LLM 调用
工具?
执行工具
返回结果
压缩
最佳实践
- 合理设置超时 - 避免长时间阻塞
- 配置压缩策略 - 根据需求选择总结或截断
- 工具白名单 - 只暴露必要的工具
- 监控执行 - 使用日志追踪执行流程
掌握这些概念,就能深入理解并高效使用 OpenClaw 的 Agent Loop 机制!