StepContext 是一次模型采样的“一致执行视图”。它最重要的设计不是字段多,而是模型看到的工具规格与 Runtime 随后执行调用时使用的 ToolRouter来自同一次捕获。MCP 刷新不能在两者之间插队。
七类字段组成一个请求视图
| 字段 | 来源 | 为什么属于 Step |
|---|---|---|
turn | Arc | 继承本 Turn 决策基线 |
| environments | Turn 快照 refresh_readiness | 环境可能在采样间变 Ready |
| selected capability roots | ready environments | 只绑定本 Step 可用根 |
| executor discovery | 执行环境能力扫描 | 能力文件可能刷新 |
| MCP binding/tools | MCP runtime snapshot | 连接和 catalog 会变化 |
| ToolRouter | 同一环境、MCP、dynamic tools | 执行必须匹配 advertised specs |
| loaded AGENTS.md | AgentsMdManager refresh | 项目规则可能在工具修改后变化 |
capture_step_context_with_required_mcp_servers 先从 TurnContext 的选择刷新 readiness,再刷新 AGENTS.md,解析 capability roots 和 executor discovery,构造 Step extension data。之后以可取消方式取得满足 required servers 的 MCP binding 和工具推荐,最后构建固定 MCP tools 与 ToolRouter。
async def capture_step(turn, required_servers, cancel):
envs = turn.environments.refresh_readiness()
agents_md = await agents_md_manager.refresh_and_load(envs, cancel)
roots = resolve_selected_capability_roots(envs)
discovery = await discover_executor_capabilities(roots, cancel)
step_store = ExtensionData(turn.id)
mcp, recommendations = await cancellable_gather(
mcp_runtime.binding(required_servers),
prepare_tool_recommendations(turn),
token=cancel,
)
mcp_tools, router = await build_tools(
turn, envs, mcp, step_store, recommendations
)
return StepContext(turn, envs, roots, discovery,
mcp, mcp_tools, router, agents_md)
什么时候重新捕获
第一次 run_turn 在记录上下文和输入之前捕获首个 Step。循环后续每次准备新的模型请求都捕获新的 Step,只有已经预先保存的 first/continuation context 会被直接取走。pending input 提到了特定 MCP server 时,捕获路径先解析 required servers,确保该请求需要的连接进入 binding。
重试同一次 Responses stream 不重新捕获 Step。重试继续使用原 Prompt 和 ToolRouter,保持“同一个请求尝试”的语义;工具完成、Steer、自动压缩或新一次采样才进入新的 Step 边界。
旧 Step 为什么不能原地更新
假设模型在 Step A 看见工具 server_x.read,调用返回途中 MCP refresh 把该工具换成另一 schema。若旧 Step 的 router 原地更新,Runtime 会用模型从未见过的新 schema 解释旧参数。Arc
测试 refresh_mcp_servers_uses_latest_state_for_existing_turns 明确覆盖这一点:同一个 Turn 已捕获的旧 Step 保持原配置,新捕获 Step 读取新 MCP state。它证明可变边界是 Step 之间,不是 Turn 之间,也不是调用执行中途。
评论
登录后即可评论