Easel 架构课程 · 参考文档

Agent Loop 解剖(速查)

调用链(每层 file:line 均为源码实证)

gateway 收 /v1/chat/completions
 → agentCommand(按 sessionKey 准入)            src/agents/agent-command.ts:199
  → runEmbeddedAgent                             run-orchestrator.ts:110
   → runPreparedEmbeddedLoop                     run-loop.ts:254  while(true) 外层
    → runEmbeddedAttempt                         run/attempt.ts:64(装配工具+提示词+历史)
     → Agent.prompt                              agent.ts:444(重入保护:二次调用抛错)
      → runAgentLoop → runLoop                   agent-loop.ts:68/141/214 ← 主循环

循环骨架(伪代码)

外层 while(true):                        # 重试/compaction/排队消息的宿主
                                         # 预算:24+8/档案,min 32,max 160 次
  while (hasMoreToolCalls):              # agentic 主循环
    ① abort 检查
    ② pending/steering 消息提交进 transcript
    ③ streamAgentResponse 调 LLM(流式)
       流中 toolcall_end → 工具提前入队执行
    ④ stopReason 判定:
       toolUse  → 执行工具 → 结果(含 isError)回填 → 继续
       error/aborted → providerFailed 终止
       stop      → 看 endTurn / terminate 钩子
    ⑤ tool results 追加回 context.messages
    ⑥ turn_end 事件 → prepareNextTurn 钩子(可换模型/思维档)
  排空 followUp 队列;无新消息 → agent_end

关键数值与语义

项值 / 语义出处
stopReason 词表stop | length | toolUse | error | abortedllm-core/src/types.ts:357
继续循环的条件还有 tool calls 未消化,或 endTurn===false 续跑agent-loop.ts:317-322
工具形态进程内函数 execute(id, args, signal);MCP 工具包装成同形态;串行/并行可配agent-loop.ts:443/498-510
工具失败不是异常:isError:true 结果回填给模型,让它自我纠错agent-loop.ts:1140-1147
重试预算24 基数 +8/档案,min 32 / max 160 次run/helpers.ts:51-54
idle 看门狗云端 120s / 自托管 300sdocs/concepts/agent-loop.md:205
compaction 触发tokens > contextWindow − reserve(16384),保留最近 20000 tokenscompaction.ts:196-199
compaction 语义不是 stopReason:在 assistant 消息后发生,成功后 continue() 续跑agent-session-prompting.ts:88
transcript 持久化JSONL→SQLite,只追加不重写;compaction 也作为 entry 落盘session-manager-entries.ts:515-530
并发隔离每会话一个 lane(并发=1)串行;跨会话并行;Agent.prompt 重入直接抛错command-queue.ts:121-126

流式链路与 Easel 集成契约

provider 流 → agent-stream-response.ts(text_delta/toolcall_end 事件)→ Agent 事件总线 → gateway onAgentEvent。另有一条旁路:OPENCLAW_RAW_STREAM=1 + OPENCLAW_RAW_STREAM_PATH 时,订阅 handler 把 text/thinking delta 和 message_end 以 JSONL best-effort 追加到共享文件(embedded-agent-subscribe.raw-stream.ts:24-52)—— 这就是 Easel tail 的 /tmp/easel-raw-stream.jsonl。每行带 runId/sessionId, 多会话交错写入时靠 runId 闩锁认领自己的行。

四条第一性设计(换你建 loop 时照抄)

  1. 工具失败是给模型的消息,不是给用户的异常——isError 回填,模型自我纠错
  2. 历史只追加不重写——compaction 也是 entry,可回放、可审计、prompt cache 稳定
  3. 同会话串行、跨会话并行——lane 并发 1 + 重入保护,杜绝交错写
  4. 系统提示词分"稳定前缀 + 可变尾部"——稳定段在前命中 provider 的 prompt cache