> ## Documentation Index
> Fetch the complete documentation index at: https://agent-compass.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# 执行、调度与清理

共享 runtime 负责单个请求内的任务执行，`Orchestrator` 负责多个命名请求之间的调度。修改生命周期时，先判断行为属于一次尝试、一个任务、一个请求，还是整次编排。

主要实现位于以下文件：

```text theme={"system"}
src/agentcompass/runtime/runner.py
src/agentcompass/runtime/orchestration.py
src/agentcompass/runtime/limits.py
src/agentcompass/runtime/base.py
```

## 准备请求

`UnifiedEvaluationRuntime.prepare` 在任务工作协程启动前完成请求级工作：

1. 预留新输出目录并初始化进度报告；
2. 写入初始 `run_info.json`；
3. 执行尚未完成的依赖与组件兼容性预检；
4. 调用 `Benchmark.load_tasks`，校验任务 ID，再调用 `Benchmark.select_tasks`；
5. 将可复用的正常明细文件放入新运行目录；
6. 加载这些明细，并从待处理队列移除对应的任务 ID。

每个请求只加载和选择一次任务。`Planner.plan` 与 `Benchmark.prepare_task` 则属于尝试作用域，会在之后按需再次执行。

## 执行一次尝试

对于每个选中的任务，`_run_attempts` 会读取 `RunRequest.execution.attempts`，解析已注册 strategy，再由 `AttemptScheduler` 决定仍需执行哪些 attempt 索引。一次 attempt 的逻辑顺序如下：

```mermaid theme={"system"}
sequenceDiagram
  participant R as runtime
  participant P as Planner
  participant E as 任务 Environment
  participant B as Benchmark
  participant H as Harness
  participant V as 评测器
  participant F as 全新评测 Environment
  participant A as 分析器
  participant S as RunStore

  R->>P: 构建 ExecutionPlan
  R->>S: 按任务和尝试记录计划
  R->>E: 在基线策略下 open
  R->>B: prepare_task
  opt 由 Harness 驱动
    R->>H: start_session
  end
  R->>E: 必要时切换到运行策略
  alt 由 Harness 驱动
    R->>H: run_task
    H-->>R: 原始 RunResult
    R->>H: close_session
  else HarnessFreeBenchmark
    R->>B: run_task
    B-->>R: 原始 RunResult
  end
  R->>B: collect_artifacts
  alt 评测模式 = reuse
    R->>E: 切换到评测策略
    R->>V: 使用任务 Environment 评测
    R->>E: 恢复基线策略并 close
  else 评测模式 = none
    R->>E: close
    R->>V: 使用 env=None 评测
  else 评测模式 = fresh
    R->>E: close
    R->>F: open 并切换到评测策略
    R->>V: 使用全新会话评测
    R->>F: 恢复基线策略并 close
  end
  R->>A: 启用时分析已评测结果
  R->>R: 加入内存 attempts 映射
```

runtime 在打开 Environment 前构建并记录 `ExecutionPlan`。下一次语义尝试会重新构建计划，同一次尝试内的 runtime 重试则复用该计划。

`Benchmark.prepare_task` 接收原始 `TaskSpec`、已打开的任务 `EnvironmentSession`、完整请求与 `plan.benchmark_plan`，并返回 `PreparedTask`。普通 Harness 路径随后依次调整输入、启动 Harness 会话、切换运行网络策略、执行任务，并在嵌套的 `finally` 中关闭 Harness 会话。

对于 `HarnessFreeBenchmark`，请求使用占位 Harness ID `none`。runtime 不构造 Harness，而是在相同的网络策略边界下调用 `HarnessFreeBenchmark.run_task`。两条路径此时都只返回原始 `RunResult`，不会在推理阶段评分。

如果已经得到 `RunResult`，runtime 会在任务 Environment 仍存活且处于运行策略时调用 `Benchmark.collect_artifacts`。这是释放任务 Environment 前，最后一个可以读取 agent 所创建文件的共享生命周期方法；它可以附加或规范化产物，但不负责聚合请求指标。

`ExecutionPlan.evaluation_environment_mode` 决定 `Benchmark.evaluate` 收到的 Environment：

| 模式      | 任务 Environment | `evaluate()` 收到的 `env`  | 网络与清理行为                          |
| ------- | -------------- | ----------------------- | -------------------------------- |
| `reuse` | 保持到评测结束        | 任务 `EnvironmentSession` | 切换到评测策略、评测、恢复基线策略，再关闭或保留         |
| `none`  | 除非保留，否则评测前关闭   | `None`                  | 在任务 Environment 关闭或保留后，由驱动进程评测   |
| `fresh` | 除非保留，否则评测前关闭   | 新打开的评测会话                | 按计划打开会话、切换到评测策略、评测、恢复基线策略，再关闭或保留 |

`Benchmark.evaluate` 返回每次尝试的权威评分。评测完成后，`analyze_task` 按数据集、数据要求、配置和分析器系列选择符合条件的实现；分析失败会单独记录，不会覆盖 Benchmark 得分或原始失败信息。

这里的“保留”指 `ExecutionSpec.keep_environment=True`。它只跳过 Environment 关闭操作，不会改变传给评测器的会话。

## 区分语义尝试与 runtime 重试

两层循环解决不同问题：

| 机制         | 配置归属                                               | 计划行为                | 结果行为                                  |
| ---------- | -------------------------------------------------- | ------------------- | ------------------------------------- |
| 语义 attempt | `ExecutionSpec.attempts.k` 与已注册 strategy           | 再次调用 `Planner.plan` | 在稳定的 attempt 索引下 checkpoint 一份已评测结果   |
| runtime 重试 | `ExecutionSpec.max_retries` 与 `retry_pattern_list` | 复用当前尝试的计划           | 将失败尝试写入 `retry_details/`，只有最终尝试进入任务明细 |

重试预算在每次语义尝试开始时重置，并由该次尝试的全部可重试阶段共享。`retry_pattern_list=None` 时，任意非空错误都可匹配；列表只重试至少匹配一个正则表达式的错误，空列表不匹配任何错误。

失败位置决定重试作用域：

* 准备、Environment、推理、产物或一般尝试失败时，使用新的任务 Environment 与同一计划重跑整个尝试；
* 推理返回的错误符合重试条件时，也会重跑整个尝试；
* `reuse` 评测失败会重跑整个尝试，因为评测器共享任务 Environment 的状态；
* `none` 评测失败只使用同一个已准备任务与运行结果重试 `Benchmark.evaluate`；
* `fresh` 评测失败会重新打开评测 Environment，并使用同一个已准备任务与运行结果重试评测。

`RunStore.save_retry_detail` 将被丢弃尝试的诊断数据、重试序号、阶段、作用域、匹配模式和脱敏计划写到 `details/` 之外，因此重试不会改变可计数的任务集合。重试耗尽后，runtime 会在结果中保留异常堆栈或返回的错误。

每个已评测结果都会按稳定的 attempt 索引写入 checkpoint。`avg` strategy 会完成所有已配置 attempts；`pass` strategy 可以在主观测第一次得到有效成功后停止。调度前会恢复已有的兼容 checkpoints，因此中断后的 `k>1` 任务只执行缺失 attempts。任务进入终态后，runtime 调用 `RunStore.save_partial_result`，随后删除内部 attempt checkpoints。

## 编排请求并施加限制

以上阶段都属于单个请求；跨请求调度由 `Orchestrator` 负责。`Orchestrator._run_orchestration` 启动三类协作任务：

* `_prepare_in_order` 按声明顺序准备各请求；
* `task_concurrency` 个 `_worker` 通过同一个全局池执行任务；
* `_finalize_ready` 在请求满足收尾条件后启动聚合。

准备阶段采用流水线。只有当更早且未终止的请求都已完成准备，并且它们的待处理任务总数小于工作协程数时，后续请求才开始准备。`_select_state` 按声明顺序选择第一个已准备且仍有待处理任务的请求：

```text theme={"system"}
从最早的请求分发待处理任务
  -> 其待处理队列为空后，从下一个请求分发
  -> 已在运行的任务可以与后续请求的任务重叠
```

这是有序优先级，不是完整请求之间的屏障。改成轮询或提前交错会改变用户可见的调度语义。请求自身没有待处理或活动任务，且更早的请求都已清空待处理队列后，才会开始收尾；收尾可以与其他活动任务重叠。为兼容直接调用 runtime，`UnifiedEvaluationRuntime.execute` 仍保留有界的 `TaskExecutor` 路径；公开的单请求辅助函数会先包装成 `Orchestration`，因此使用中央工作协程池。

AgentCompass 在不同职责层独立限制并发和外部服务压力：

| 限制                               | 作用域                                            | 强制位置                                                  |
| -------------------------------- | ---------------------------------------------- | ----------------------------------------------------- |
| `Orchestration.task_concurrency` | 一次编排中的活动 Benchmark 任务总数                        | `Orchestrator._worker` 数量                             |
| provider 容量                      | 当前进程内某个 Environment ID 的并发尝试数                  | 在 `_run_single_attempt` 外层获取对应 Environment ID 的进程全局许可 |
| Environment 打开 QPS               | 当前进程内某个 provider 的 `BaseEnvironment.open` 调用速率 | `BaseEnvironment.__init_subclass__` 安装的包装器            |
| 编排超时                             | 预检结束后的实际耗时                                     | `Orchestrator.execute`                                |
| 清理宽限期                            | 协作取消与线程清理窗口                                    | `Orchestrator._cancel_remaining`                      |

provider 容量或打开 QPS 为 `0` 时，会禁用相应限制。两类缓存都是进程全局的，并可跨事件循环安全使用；重跑整个尝试时会先释放再重新获取 provider 容量许可。任务并发不能代替 provider 容量，因为多个编排可能在同一进程中共享同一个 provider。

网络策略属于 `ExecutionPlan`，强制执行由 Environment provider 负责：

| 阶段 | 预期工作                                       | runtime 转换                                      |
| -- | ------------------------------------------ | ----------------------------------------------- |
| 基线 | 建立受信任的 Environment、准备 Benchmark、启动 Harness | provider 使用计划中的基线策略打开 Environment               |
| 运行 | agent 或 Model 执行与产物收集                      | 必要时切换到 `plan.run_network_policy`                |
| 评测 | Benchmark 验证                               | 将复用或全新的评测会话切换到 `plan.evaluation_network_policy` |

`BaseEnvironment.build_config` 会校验 provider 是否支持请求的策略转换。不能动态切换的 provider 必须拒绝阶段策略不同于基线策略的计划，不能静默使用更宽松的访问范围。评测结束后，runtime 会先避免恢复基线策略的操作被取消，再进入正常清理路径。

## 清理并收尾

资源按照所有权从内到外释放：

```text theme={"system"}
Harness.start_session
  -> 运行阶段 finally 中的 Harness.close_session

用于任务的 Environment.open
  -> 尝试层 finally 中的 Environment.close

用于全新评测的 Environment.open
  -> fresh 评测 finally 中的 Environment.close

请求进度与日志资源
  -> UnifiedEvaluationRuntime.close 与 RunLogRegistry.close
```

`Harness.close_session` 在 `Benchmark.collect_artifacts` 前执行，任务 Environment 在产物收集期间仍可用。驱动进程评测或全新 Environment 评测会先关闭任务 Environment，`reuse` 则保留它直到评测结束。Harness 或 Environment 释放失败会记录警告，避免覆盖原始任务失败；取消、`KeyboardInterrupt` 和 `SystemExit` 会继续抛出。

`ExecutionSpec.keep_environment=True` 只适用于显式保留或调试，不能用来掩盖清理失败。新增 provider 的 `close` 仍应具备幂等性，并正确处理部分初始化、失败和取消。具体测试要求见 [Environment 验证与对齐](/zh/developer_guide/extensions/environment/validation_and_alignment)。

`Orchestrator.execute` 在开始计算编排截止时间前完成组件预检。超时或外层 `asyncio` 取消后，会依次锁定终态、取消准备与工作协程、在 `cleanup_grace_seconds` 内等待自身创建的异步任务和已跟踪线程、持久化未终止请求的终态，最后关闭日志与进度渲染器。第一次 CLI 中断请求这条协作清理路径，第二次中断会强制退出，因此清理代码不能依赖无限等待。

取消可能发生在任意 `await`。获取会话、进程、代理、客户端、后台任务或临时端点后，应立即安装清理逻辑，并继续抛出 `asyncio.CancelledError`。

单个请求的待处理队列与活动任务清空后，`UnifiedEvaluationRuntime.finalize` 会按选中任务的顺序合并新旧记录，将指标聚合交给 `Benchmark.aggregate_metrics`，并写入摘要与可选的分析产物。返回值代表整个请求，而不是单次执行使用的 `RunResult`。

继续阅读 [结果与复用](/zh/developer_guide/architecture/results_and_reuse) 了解部分结果、聚合产物与终态结构。
