Agent 工具并发执行器 + 内建循环检测。
职责
- 管理 tool call batch 的生命周期:spawn → collect → finalize
- 工具执行崩溃隔离:单个工具 raise 不影响同批其他工具
- 四个 Plugin Pipeline 嵌入点:
{:before_tool, name, args}— 工具执行前(可阻止或替换参数){:on_tool_error, name, call_id, error, attempt}— 工具执行失败、retry 前{:after_tool, name, call_id, result}— 单个工具执行后{:after_tool_batch, results}— 整批工具全部完成后
内建循环检测(吸收旧 LoopDetector Plugin)
三条检测路径,在 collect_result/4 中自动运行:
- 文件编辑频率 — 追踪 write_file/edit_file 对同一文件的编辑次数,
超过阈值先
intervene(注入提示词),再次超限则abort - 重复调用模式 — 滑动窗口检测连续相同工具调用
- 连续失败 — 连续 N 次工具调用失败时
intervene
执行流
execute_batch(tool_calls, state)
└─ for each tc:
run_before_tool_pipeline → {:block_tool} | {:replace_args} | {:continue}
spawn_tool_task(state, tc)
└─ (并发执行中...)
└─ 工具执行失败 → on_tool_error pipeline → retry / 放弃
└─ collect_result(ref, tc, result, state)
→ run_after_tool_pipeline
→ 内建循环检测
→ 检查是否全批完成
→ 全批完成 → run_after_tool_batch_pipeline → finalize_batch返回约定
execute_batch/2返回{:next_state, :executing_tools, state}或{:next_turn, state}(全部被 block/无效时直接跳过)collect_result/4返回State.t()|{:next_turn, State.t()}|{:abort, State.t()}|{:intervene, String.t(), State.t()}
Summary
Functions
返回本轮可用的工具列表(已过滤 disabled_tools)。
取消所有进行中的工具任务(用于 abort)。
按 abort 模式取消进行中的工具任务(v0.2 RFC B6)。
收集单个工具任务的完成结果。
批量启动所有 tool call 的并发执行。
执行单个工具,崩溃隔离。
从工具列表中按名称查找工具模块。
获取循环检测配置,从 State.config 中读取。
获取工具 retry 配置,从 State.config 中读取。
Types
Functions
@spec active_tools(CMDC.Agent.State.t()) :: [module()]
返回本轮可用的工具列表(已过滤 disabled_tools)。
@spec cancel_all(CMDC.Agent.State.t()) :: CMDC.Agent.State.t()
取消所有进行中的工具任务(用于 abort)。
@spec cancel_for_abort(CMDC.Agent.State.t(), :all | :killable | :none, term()) :: {CMDC.Agent.State.t(), non_neg_integer()}
按 abort 模式取消进行中的工具任务(v0.2 RFC B6)。
Mode
:all— brutal_kill 所有任务(含 immune):killable— 仅杀非interrupt_immune_tools工具,与 Steering 行为一致:none— 不杀任何任务,让其自然完成(state.pending_tool_tasks 不变)
每杀一个任务广播 {:tool_killed, %{name, call_id, reason}} 事件。
返回 {updated_state, killed_count}。
@spec collect_result(reference(), map(), term(), CMDC.Agent.State.t()) :: CMDC.Agent.State.t() | {:next_turn, CMDC.Agent.State.t()} | {:abort, CMDC.Agent.State.t()} | {:intervene, String.t(), CMDC.Agent.State.t()}
收集单个工具任务的完成结果。
自动执行:
- after_tool Pipeline
- Steering 检查(killable in-flight tool 立刻 brutal_kill;详见
steering-design.md) - 内建循环检测(文件编辑频率 + 调用模式 + 连续失败)
- 批次完成检查 → finalize_batch
返回:
State.t()— 还有任务未完成{:next_turn, State.t()}— 整批完成(或 steering 已注入),进入下一 turn{:abort, State.t()}— Pipeline/循环检测中止{:intervene, String.t(), State.t()}— 循环检测注入提示词
@spec execute_batch([map()], CMDC.Agent.State.t()) :: {:next_state, :executing_tools, CMDC.Agent.State.t()} | {:next_turn, CMDC.Agent.State.t()} | {:abort, CMDC.Agent.State.t()}
批量启动所有 tool call 的并发执行。
返回:
{:next_state, :executing_tools, state}— 有任务在运行{:next_turn, state}— 没有可执行的工具(全部无效/被阻止),直接进入下一轮{:abort, state}— Pipeline 中止
@spec execute_tool(module() | nil, map(), map()) :: tool_result()
执行单个工具,崩溃隔离。
工具 raise 时捕获异常并返回 {:error, message}。
从工具列表中按名称查找工具模块。
@spec loop_config(CMDC.Agent.State.t()) :: %{ max_edits_per_file: pos_integer(), min_repeat_pattern: pos_integer(), max_consecutive_failures: pos_integer() }
获取循环检测配置,从 State.config 中读取。
支持的配置键:
:max_edits_per_file— 单文件最大编辑次数(默认 20):min_repeat_pattern— 触发重复模式检测的最小连续次数(默认 3):max_consecutive_failures— 触发连续失败警告的次数(默认 4)
@spec tool_retry_config(CMDC.Agent.State.t()) :: %{ max_retries: non_neg_integer(), delay_ms: non_neg_integer() }
获取工具 retry 配置,从 State.config 中读取。
:tool_max_retries— 工具执行失败后最大重试次数(默认 0,0 = 不重试):tool_retry_delay_ms— 每次重试前的等待时间(默认 500ms)