CyberStrikeAI 工具执行治理全指南:长任务超时、execution_id 续跑、输出兜底与外部 MCP 隔离
2026/9/17 16:26:26 网站建设 项目流程

CyberStrikeAI 工具执行治理全指南:长任务超时、execution_id 续跑、输出兜底与外部 MCP 隔离

【免费下载链接】CyberStrikeAIThe system of action for AI-native cybersecurity—where intent becomes governed execution, evidence becomes operational memory, and every operation improves the next.项目地址: https://gitcode.com/GitHub_Trending/cy/CyberStrikeAI

本指南基于 docs/zh-CN/tool-execution-governance.md 展开,系统讲解 CyberStrikeAI 如何治理长时间工具调用、MCP 阻塞、超大输出、取消与恢复上下文。核心目标是让 Agent 保持标准工具语义(一次调用、一次返回),同时避免工具卡死、上下文爆炸、数据库膨胀以及恢复时重新注入历史大输出。读完本文,你将掌握execution_id有界等待续跑模型、get/wait/cancel_tool_execution三个控制工具的用法、<persisted-output>落盘兜底机制,以及外部 MCP 的并发限制与熔断配置。

一、治理背景与设计目标

CyberStrikeAI 是面向 AI 原生网络安全的行动系统(README),其 Agent 需要真实调用execsqlmapnmapnuclei等安全工具,而这些工具天然存在两个致命问题:

  • 长任务阻塞:一次扫描可能持续数分钟甚至数十分钟,如果 Agent 的 runner 同步等待,整轮推理会被绑死。
  • 大输出爆炸nmap的完整扫描结果、sqlmap的 dump 输出动辄数百 KB,直接塞进模型上下文会撑爆预算,写入 DB 会造成数据库膨胀,恢复续跑时还会反复注入历史大输出。

围绕这两个问题,系统确立了六条设计目标:

目标含义
Agent 不被工具绑死工具调用可以很慢,但当前 runner 只等待有限时间
长任务可继续观察超时返回execution_id,后续可用wait_tool_execution多轮等待
用户和 Agent 都能取消当前会话结束或用户停止任务时,取消仍在运行的工具
数据库与 Agent 视图一致DB 保存的是 Agent 实际拿到的兜底后结果,不再另存一份原始大输出
恢复不会撑爆上下文续跑使用 model-facing trace;历史异常大 tool trace 恢复时再次裁剪
外部 MCP 有隔离保护按 server 限并发、按全局限并发,对连续失败的 server 熔断

这套治理能力并非文档空谈,而是落在internal/mcp/下的真实实现:执行状态机在 execution_service.go,三个控制工具在 execution_control_tools.go,输出兜底在 tool_result_guard.go,对应的测试用例分布在 execution_service_test.go、external_manager_async_test.go 中。

二、执行模型:两段式执行与有界等待

普通工具调用对 Eino/Agent 仍然表现为一次标准 tool call,但底层执行被拆分为两段:

Agent 调用工具 -> ExecutionService 创建 execution -> worker 执行真实 MCP/工具调用 -> Agent bounded wait -> 完成:返回工具结果 -> 未完成:返回 execution_id,worker 继续后台运行

第一段是创建与后台执行ExecutionService创建一条 execution 记录,由独立 worker 执行真实的 MCP/工具调用;第二段是有界等待:Agent 只等待tool_wait_timeout_seconds配置的有限时间。若工具在等待窗口内完成,直接返回工具结果;若未完成,则返回execution_id,worker 继续在后台运行,Agent 可以继续推理、改用其他工具,或在后续轮次中调用wait_tool_execution继续观察。

这一模型解决了 MCP server、execsqlmapnmapnuclei等长任务阻塞当前 runner 的问题。关键点在于tool_wait_timeout_seconds的适用范围:

  • 适用于内部 MCP 工具、外部 MCP 工具,以及 Eino filesystem 的流式execute
  • Eino 的非流式 filesystem 工具(ls/read_file/write_file/edit_file/glob/grep)会写入 execution 监控记录,但不会作为后台 worker 做软等待续跑——它们本身足够快,无需进入可恢复的后台执行模型。

从源码看,tool_wait_timeout_seconds在 config.go 中的定义注释为「工具本轮等待秒数;到时返回 execution_id,worker 继续后台执行;0 表示等到完成」,从实现语义可以推断:默认推荐不要设为 0,否则长任务会退化为同步阻塞等待。

三、工具状态语义:八种状态的完整含义

execution 的生命周期由一组明确的状态机描述:

状态含义
queuedexecution 已创建,等待 worker 或并发槽位
runningworker 正在执行
background_running前端展示状态:本轮 Agent 已停止等待,但后台仍在跑
completed本次 tool call 本身已完成
failed工具真实失败
cancelled用户、Agent 或会话清理主动取消
hard_timeout超过硬超时,被系统终止
orphaned重启/异常后发现 DB 中仍是 running,但运行时已无对应 worker

其中hard_timeoutorphaned两个状态在 execution_service.go 中作为常量直接定义:ToolExecutionStatusHardTimeout = "hard_timeout"ToolExecutionStatusOrphaned = "orphaned"。前者对应tool_timeout_minutes硬超时被系统终止;后者用于服务重启或异常后,对 DB 中残留 running 记录但运行时已无对应 worker 的情况进行对账(reconcile)。

一个重要的语义细节:当wait_tool_execution到达timeout_seconds时,如果目标 execution 仍在运行,这次 wait 调用本身是完成的观察动作,不是工具执行失败。返回体会说明目标仍为running,前端不应显示为红色失败。这一点在 execution_control_tools.go 的实现中得到印证:超时返回ErrExecutionWaitTimeout时,工具结果会追加一行「本次等待已到达 timeout_seconds,上述 execution 仍未完成。可继续等待、取消,或采用其他步骤」,且isError标志保持为false,而不是作为失败返回。

四、三个控制工具:get / wait / cancel

系统以普通 MCP 工具的形式向 Eino 暴露三个执行句柄操作(注册逻辑见 RegisterExecutionControlTools),让 Agent 循环保持原生语义:模型调用工具、拿到有界结果、必要时再次调用wait_tool_execution

工具用途关键参数
get_tool_execution读取 execution 当前状态execution_id(必填)、include_partial_outputpartial_output_max_bytes
wait_tool_execution等待指定 execution 一段时间execution_id(必填)、timeout_secondsinclude_partial_outputpartial_output_max_bytes
cancel_tool_execution主动取消指定 executionexecution_id(必填)、reason(可选,写入终止说明)

4.1 运行中输出预览(partial output)

get_tool_executionwait_tool_execution支持返回运行中输出预览:

  • include_partial_output:是否返回 partial output,默认true
  • partial_output_max_bytes:本次返回的尾部预览上限,默认4096,最大65536

从 execution_control_tools.go 的常量定义可以看到底层默认值:defaultExecutionWaitTimeout = 60 * time.SecondmaxExecutionWaitTimeout = 10 * time.MinutedefaultPartialPreviewBytes = 4096maxPartialPreviewBytes = 64 * 1024。也就是说,wait_tool_execution单次等待默认 60 秒、上限 10 分钟;partial 预览默认取尾部 4KB、上限 64KB。

重要概念区分:partial output 是「已产生输出的有界预览」,不等同于最终result。最终result仍只在工具结束时写入 canonical execution 记录;不支持流式输出的工具不会返回 partial 字段。从formatExecutionForModel(execution_control_tools.go)的实现可以看出:partial_output仅在exec.PartialOutput非空时出现,且取尾部partial_output_max_bytes字节(tailStringBytes),并携带partial_output_bytespartial_output_truncatedpartial_output_updated_at等元信息。

4.2 典型流程

1. 调用 exec/sqlmap/nmap 等长任务 2. 超过 tool_wait_timeout_seconds 后拿到 execution_id 3. Agent 可继续推理、改用其他工具,或调用 wait_tool_execution 4. 仍未完成时可继续等待,或调用 cancel_tool_execution

cancel_tool_execution的查找顺序(execution_control_tools.go)是先尝试内部工具 execution,再尝试外部 MCP execution:server.CancelToolExecutionWithNote(id, reason)优先,失败后调用external.CancelToolExecutionWithNote(id, reason);两者都找不到时返回错误提示「未找到进行中的 execution,或该 execution 已结束」。

五、取消与会话清理

取消逻辑遵循「会话级作用域」原则,避免误杀:

  • 用户点击「停止任务」时,会取消当前会话仍在运行的工具。
  • 会话正常结束后,会批量取消当前会话仍running的工具。
  • 「中断并继续」类流程不会做会话级批量取消,以免误杀后续需要等待的 worker。
  • 取消只针对当前 conversation 绑定的 execution,不会误杀其他会话的工具。

这套设计保证了:用户在 Web 控制台停止任务或会话自然结束时,后台的长任务(如正在跑的sqlmap)会被及时终止,释放系统资源;而「中断并继续」场景下,工具 worker 被保留,续跑时可以继续等待同一批execution_id

六、外部 MCP 隔离:限并发 + 熔断

外部 MCP 可能因为远端 server 卡住、断连或返回异常而拖慢 Agent。系统提供三层保护:

能力配置说明
单 server 并发限制external_mcp_max_concurrent_per_server同一个外部 MCP server 同时运行的工具数
全局并发限制external_mcp_max_concurrent_total所有外部 MCP 工具总并发
熔断external_mcp_circuit_failure_threshold/external_mcp_circuit_cooldown_seconds单 server 连续失败后短期快速失败,避免反复打坏 server

推荐默认值:

agent: external_mcp_max_concurrent_per_server: 2 external_mcp_max_concurrent_total: 16 external_mcp_circuit_failure_threshold: 3 external_mcp_circuit_cooldown_seconds: 60

从 config.go 的源码注释可以确认这些配置的默认语义:external_mcp_max_concurrent_per_server为 0 时默认取 2,external_mcp_max_concurrent_total为 0 时默认取 16。熔断配置的连续失败阈值与冷却时间则用于「单 server 连续失败 N 次后,在冷却期内快速失败」,避免反复重试把已经处于异常状态的远端 server 彻底打坏。相关异步行为与测试覆盖可参考 external_manager_async_test.go。

七、输出兜底:全文落盘 + 上下文预览

系统使用multi_agent.eino_middleware.reduction_max_length_for_trunc作为统一工具结果上限。当前示例配置为 50000 bytes:

multi_agent: eino_middleware: reduction_enable: true reduction_max_length_for_trunc: 50000

注意一个细节:tool_result_guard.go 中定义了代码层默认DefaultToolResultMaxBytes = 12000,而文档示例配置为 50000,两者并不冲突——12000 是未配置时的代码兜底值,50000 是生产推荐配置,配置项ReductionMaxLengthForTrunc在 config.go 中注释默认值为 12000。实际生效值以 YAML 配置为准。

7.1 兜底覆盖渠道

渠道行为
Agent 实际拿到的工具结果使用兜底后的 canonical result
DB/监控存储保存同一份 canonical result
get_tool_execution/wait_tool_execution读取同一份 canonical result
Einoexecute/ filesystem 监控记录完成记录前统一兜底
非流式execstdout/stderr源头 bounded buffer
流式execstdout/stderr推送给前端的累计输出也受上限控制
PTY 执行路径同样受上限控制
前端详情弹窗额外有 UI 展示截断保护

这一「全渠道统一兜底」策略的底层实现正是 NormalizeToolResultForStorage:当文本内容总字节数超过maxBytes时,完整文本先经tooloutput.BoundWithSpill落盘,再把Content替换为带绝对路径的<persisted-output>预览。因此Agent、DB、监控、控制工具读取到的都是同一份 canonical result,从根源上消除了「DB 存一份原始大输出、Agent 拿一份截断结果」的不一致。

7.2 触发上限后的行为

触发上限后,完整输出先写入本地 trunc 文件,Agent 侧只保留计入预算的<persisted-output>预览(含绝对路径)。因此阈值为 50000 时,上下文文本不会超过该上限。

示例(假设输出 200000 字节):

<persisted-output> Output too large (200000). Full output saved to: /path/to/tmp/reduction/conversations/<id>/trunc/<execution_id> Use read_file with offset/limit to read parts of the file. Preview (first …): … Preview (last …): … </persisted-output>

当前策略总结:「全文落盘 + 上下文预览」——超过reduction_max_length_for_trunc时,完整输出写入本地文件(默认tmp/reduction/conversations/<会话ID>/trunc/<execution_id>,可通过reduction_root_dir自定义根目录),Agent/DB/监控拿到的是带绝对路径的<persisted-output>预览;可用read_file按 offset/limit 回读全文。这既保住了上下文预算,又不丢失任何数据——模型需要完整输出时,用read_file分段读回即可。

八、DB 与恢复上下文:model-facing trace

8.1 新执行结果的写入路径

工具完成 -> NormalizeToolResultForStorage -> 写入内存 execution -> 写入 DB -> 返回给 Agent

因此正常情况下,DB 中保存的就是 Agent 拿到的结果(同一份 canonical result),不存在第二份原始大输出。

8.2 恢复路径的二次裁剪

续跑恢复时,系统使用LastAgentTraceInput中的model-facing trace,也就是实际送入 ChatModel 的消息快照,而不是原始事件流累计。恢复入口还会对历史 tool 内容再次应用上限,防止以下情况撑爆上下文:

  • 升级前 DB 已经存过原始大输出。
  • 手工迁移或导入的数据绕过了当前写入路径。
  • 配置从更大阈值改成 50000。
  • 未来某条旁路写入漏掉 canonicalize。

这一「恢复时二次裁剪」设计非常关键:即使历史数据因各种原因绕过了规范化路径,恢复续跑时也会被兜底机制再次收拢,保证无论数据来源如何,送入模型的上下文都不会超过预算。

九、关键配置建议与参数说明

长任务场景推荐配置(可直接放入配置文件,参考 config.example.yaml):

agent: max_iterations: 800 tool_timeout_minutes: 60 tool_wait_timeout_seconds: 30 external_mcp_max_concurrent_per_server: 2 external_mcp_max_concurrent_total: 16 external_mcp_circuit_failure_threshold: 3 external_mcp_circuit_cooldown_seconds: 60 shell_no_output_timeout_seconds: 1200 multi_agent: eino_middleware: reduction_enable: true reduction_max_length_for_trunc: 50000

参数说明:

参数建议说明
max_iterations300-1000Agent 单任务最大迭代轮数;太大等于放弃循环保护
tool_timeout_minutes60单次工具硬超时(分钟),超时自动终止,适合 sqlmap 等长任务;0 表示不限制(不推荐)
tool_wait_timeout_seconds30-60Agent 本轮等待上限,到时返回execution_id;0 表示等到完成
shell_no_output_timeout_seconds600-1200连续无输出时终止 shell 任务,防止静默挂死
reduction_max_length_for_trunc50000工具结果统一上限(bytes)

一个重要的调优提醒:不建议把tool_wait_timeout_seconds设置得很大。长任务应由 worker 后台跑,Agent 通过execution_id继续观察,而不是一轮等待数分钟。max_iterations与工具等待是配合关系:max_iterations限制总轮数(config.go 中MaxIterations同时作用于主代理与子代理,Markdown 中max_iterations>0可覆盖),tool_wait_timeout_seconds限制单轮等待,二者共同防止 Agent 陷入「无限轮等待长任务」的死循环。

十、测试建议:两条可复现的验证对话

10.1 长任务语义测试

在 CyberStrikeAI 会话中发起如下对话:

调用 exec 执行 sleep 120;如果超过 10 秒还没完成,不要一直等,告诉我 execution_id,然后调用 wait_tool_execution 等 5 秒;如果仍未完成,再调用 cancel_tool_execution,最后说明状态。

10.2 大输出兜底测试

调用 exec 执行:python3 - <<'PY' print("A" * 200000) PY 然后展示工具结果长度和是否包含截断提示。

10.3 预期行为

  • 初始长任务会返回execution_id,状态为running或前端展示background_running
  • wait_tool_execution等待到上限但目标未完成时,本次 wait 调用不应显示为执行失败
  • 大输出结果不会超过reduction_max_length_for_trunc
  • DB、监控详情、Agent 继续推理看到的是同一份兜底结果

这两条测试分别覆盖了治理体系的两大支柱:长任务不阻塞(可等待、可取消)与输出不爆炸(统一兜底、全局一致)。仓库内对应的行为测试可参考 execution_service_test.go 与 tool_result_guard_test.go。

十一、当前边界

  • 外部 MCP 的远端 server 内部如何采集输出不由 CyberStrikeAI 控制;CyberStrikeAI 会在结果进入本系统后统一兜底、限并发和熔断。换句话说,治理边界在本系统入口,远端 server 的异常行为由并发限制与熔断机制在入口处消化。
  • 超长工具输出会在截断前写入本地tmp/reduction/.../trunc/<id>(或reduction_root_dir),bounded result 中包含可read_file的绝对路径。

小结

CyberStrikeAI 的工具执行治理本质上回答了两个工程问题:「长任务如何不拖死 Agent」与「大输出如何不撑爆上下文」。前者靠两段式执行模型 +execution_id有界等待 + 三个控制工具 + 会话级取消;后者靠NormalizeToolResultForStorage统一兜底 +<persisted-output>全文落盘 + 恢复时 model-facing trace 二次裁剪。再叠加外部 MCP 的按 server/全局双层并发限制与熔断,最终让 Agent 对sqlmapnmapnuclei这类真实安全工具保持标准工具语义的同时,系统始终处于可控、可观测、可恢复的状态。

如需进一步了解相关模块,可继续阅读:

  • docs/zh-CN/tool-execution-governance.md(本文原始依据,英文版见 docs/en-US/tool-execution-governance.md)
  • internal/mcp/execution_control_tools.go(三个控制工具的实现)
  • internal/mcp/tool_result_guard.go(NormalizeToolResultForStorage输出兜底)
  • internal/mcp/execution_service.go(execution 状态机与hard_timeout/orphaned
  • internal/config/config.go(全部治理相关配置项定义)
  • config.example.yaml(完整示例配置)

【免费下载链接】CyberStrikeAIThe system of action for AI-native cybersecurity—where intent becomes governed execution, evidence becomes operational memory, and every operation improves the next.项目地址: https://gitcode.com/GitHub_Trending/cy/CyberStrikeAI

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询