Skip to content

AtomCode Agent 执行引擎与回合循环分析

持续深度分析 — Agent Loop 状态机、回合生命周期、执行控制 2026-06-23


1. Agent 回合循环状态机

1.1 状态定义

从二进制中提取的完整状态枚举:

状态说明
Idle空闲状态 — 等待用户输入
CallingTool正在调用工具
WaitingApproval等待用户审批(权限请求)
SkillCommand正在执行 Skill/命令
MCPMCP 工具调用
LSPLSP 语言服务器操作
PH钩子执行阶段 (PreHook/PostHook)

1.2 回合生命周期

用户输入


┌────────────────────────────────────────────────┐
│                 run_turn()                       │
│  ┌─────────────┐   ┌──────────┐   ┌──────────┐ │
│  │  Hook:      │   │ LLM Call │   │ Tool     │ │
│  │ on_turn_    │──▶│ + Stream │──▶│ Execute  │ │
│  │ start       │   │          │   │          │ │
│  └─────────────┘   └──────────┘   └──────────┘ │
│                          │                      │
│                          ▼                      │
│                   ┌──────────────┐              │
│                   │ Hook:        │              │
│                   │ on_turn_end  │              │
│                   │ + Datalog    │              │
│                   └──────────────┘              │
│                          │                      │
│                          ▼                      │
│                   ┌──────────────┐              │
│                   │ Agent Loop:  │              │
│                   │ Idle → queue │              │
│                   │ empty →      │              │
│                   │ redraw_idle  │              │
│                   └──────────────┘              │
└────────────────────────────────────────────────┘


Idle, queue empty, redraw_idle

1.3 工具调用流程

CallingTool

    ├──▶ 权限检查
    │      ├── 已缓存 "always_allow" → 自动批准
    │      ├── 敏感路径 → 提示用户审批
    │      ├── 破坏性命令 → 提示用户确认
    │      └── --dangerously-skip-permissions → 全部自动批准

    ├──▶ 前置钩子 (PreToolUse)
    │      ├── 返回 Allow → 继续
    │      ├── 返回 Block → 拒绝
    │      └── 返回 Modify → 修改后继续

    ├──▶ 执行工具
    │      ├── bash
    │      ├── read_file / write_file / edit_file
    │      ├── web_fetch / web_search
    │      ├── MCP 工具 (mcp__<name>__<tool>)
    │      └── ...

    ├──▶ 后置钩子 (PostToolUse)
    │      └── 分析结果、触发通知

    └──▶ 返回 LLM 进行下一轮

1.4 重试逻辑

run_turn_with_retry 循环:
  ┌──────────────────┐
  │ 最大回合数限制   │── NaturalTurnLimit
  │ (LLM 生成轮次)   │
  ├──────────────────┤
  │ 最大步骤限制     │── StepLimit
  │ (工具调用数)     │
  ├──────────────────┤
  │ 首 Token 超时    │── first token timeout
  │ (断连/无响应)    │
  ├──────────────────┤
  │ 流超时           │── Stream timeout: 120s
  │ (流中断)         │
  ├──────────────────┤
  │ 错误恢复         │── "run_turn_with_retry loop
  │                   │    must exit via the inner return"
  └──────────────────┘
       
  "internal error: entered unreachable code: 
   run_turn_with_retry loop must exit via the inner return"

2. 回合结束处理

2.1 TurnCompleteContext

rust
struct TurnCompleteContext {
    model_name: String,
    provider_name: String,
    result_type: String,          // "success" | "error" | "interrupted"
    tokens_used: u64,
    truncated: bool,              // 上下文窗口截断
    edited_files: Vec<String>,    // 本次编辑的文件
    timestamp: u64,
    tool_calls: Vec<ToolCallContext>,
}

struct ToolCallContext {
    tool_name: String,
    args: serde_json::Value,
    result_type: String,
    success: bool,
    duration_ms: u64,
}

2.2 回合结束处理链

1. 记录 Datalog (turn_{N}.md)
2. 触发 TurnComplete webhook
3. 更新 Telemetry 计数器
4. 更新会话状态
5. 检查是否需继续循环
6. 状态转为 Idle
7. 队列空 → redraw_idle

3. 后台任务系统

3.1 /bg 子系统

/bg              → 创建后台会话
/bg list         → 列出活跃后台任务
/bg <N>          → 切换到指定的后台任务
/bg drop <N>     → 终止后台任务

后台会话特性:
  - 隔离上下文(独立的会话状态)
  - 只读工具子集(read-only-ish)
  - 异步执行(不阻塞主会话)

3.2 run_in_background 标志

rust
struct BashArgs {
    command: String,
    timeout: u64?,           // 默认 60s
    run_in_background: bool, // 后台运行标志
}

后台 Bash 行为:

run_in_background=true:
  - 分离到独立的进程组
  - 立即返回
  - 输出写入日志文件(可后续 cat/read_file 读取)
  - 适用于长期运行任务(dev server, watcher, tunnel)

4. 回合统计 (TurnStat)

rust
struct TurnStat {
    input_tokens: u64,
    output_tokens: u64,
    cached_tokens: u64,
    tool_calls: u64,
    had_error: bool,
    duration_ms: u64,
}

每个回合记录到 Session 中,用于:

  • Token 消耗统计
  • 成本计算(显示于 /cost
  • Telemetry 上报
  • 上下文预算管理

5. 上下文预算管理

5.1 预算控制

/compact              → 压缩对话历史(保留最近轮次)
/context              → 显示上下文预算明细
/undo / /undo N       → 撤回回合(内存回滚)

预算报告内容:
  - system_tokens       - 系统提示词 token
  - tool_def_tokens     - 工具定义 token
  - tool_result_tokens  - 工具结果 token
  - message_tokens      - 消息历史 token
  - cached_tokens       - 缓存 token
  - messages_count      - 消息条数

5.2 上下文窗口超限处理

context_window 检测:
  - 当响应包含 `context_window` 错误时触发
  - 自动运行 /compact 压缩历史
  - 重试最后一次 LLM 调用
  - 如仍超限 → 提示用户

6. 流式控制

6.1 流式参数

rust
struct ChatRequest {
    // ...
    stream: bool,
    // ...
}

6.2 首 Token 超时

"first token timeout"
一旦建立 SSE 连接,等待第一个数据块:
  - 超时 → 断开连接
  - 重试策略:由 run_turn_with_retry 处理

6.3 流中断检测

stream_interrupted:
  - 网络中断
  - 提供商 API 中断
  - 用户取消 (Ctrl+C)

6.4 Agent 循环检测

loop_detected:
  - 检测重复工具调用模式
  - 相同工具 + 相同参数 X 次后触发
  - 强制中断并提示用户

7. Telemetry 事件与回合关联

事件触发时机关联数据
input_tokens每回合LLM 请求 token
output_tokens每回合LLM 响应 token
cached_tokens每回合缓存命中
tool_call每次工具调用工具名
tool_calls每回合总调用次数
had_error每回合错误类型
stream_interrupted流中断时
rate_limited被限流时
loop_detected检测到循环
session_duration_secs会话结束总时长
turns_completed会话结束总回合数

8. 新覆盖的盲区

#之前未覆盖的领域状态发现
1Agent 回合循环状态机6 种状态 + 完整生命周期
2run_turn_with_retry 重试逻辑4 种终止条件 + 错误恢复
3TurnCompleteContext 完整字段6+ 字段
4后台任务 (/bg) 机制隔离上下文 + 只读子集
5run_in_background Bash 行为分离进程 + 日志输出
6上下文预算控制/compact + /context + /undo
7流式控制参数首 Token 超时 + 流中断 + 循环检测
8Telemetry 与回合关联14 种关联事件

本报告由持续分析循环生成

逆向命令索引

bash
# 提取 agent 引擎相关
strings atomcode.bin.bak | grep -E "TurnStat|CountersSnapshot|NaturalTurnLimit|StepLimit|loop_detected" | sort -u
# 输出: struct TurnStat(6) CountersSnapshot(7) NaturalTurnLimit StepLimit loop_detected

# 提取 agent 源文件
strings atomcode.bin.bak | grep "^crates/atomcode-core/src/agent/" | sort -u

# 运行时查看 datalog
tail -3 ~/.atomcode/datalog/*/calls.log 2>/dev/null || echo "no datalog"

基于 VitePress 构建 · AtomCode v4.25.3 逆向工程分析文档