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 需要真实调用exec、sqlmap、nmap、nuclei等安全工具而这些工具天然存在两个致命问题长任务阻塞一次扫描可能持续数分钟甚至数十分钟如果 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_idworker 继续后台运行第一段是创建与后台执行ExecutionService创建一条 execution 记录由独立 worker 执行真实的 MCP/工具调用第二段是有界等待Agent 只等待tool_wait_timeout_seconds配置的有限时间。若工具在等待窗口内完成直接返回工具结果若未完成则返回execution_idworker 继续在后台运行Agent 可以继续推理、改用其他工具或在后续轮次中调用wait_tool_execution继续观察。这一模型解决了 MCP server、exec、sqlmap、nmap、nuclei等长任务阻塞当前 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_idworker 继续后台执行0 表示等到完成」从实现语义可以推断默认推荐不要设为 0否则长任务会退化为同步阻塞等待。三、工具状态语义八种状态的完整含义execution 的生命周期由一组明确的状态机描述状态含义queuedexecution 已创建等待 worker 或并发槽位runningworker 正在执行background_running前端展示状态本轮 Agent 已停止等待但后台仍在跑completed本次 tool call 本身已完成failed工具真实失败cancelled用户、Agent 或会话清理主动取消hard_timeout超过硬超时被系统终止orphaned重启/异常后发现 DB 中仍是 running但运行时已无对应 worker其中hard_timeout与orphaned两个状态在 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_output、partial_output_max_byteswait_tool_execution等待指定 execution 一段时间execution_id必填、timeout_seconds、include_partial_output、partial_output_max_bytescancel_tool_execution主动取消指定 executionexecution_id必填、reason可选写入终止说明4.1 运行中输出预览partial outputget_tool_execution与wait_tool_execution支持返回运行中输出预览include_partial_output是否返回 partial output默认true。partial_output_max_bytes本次返回的尾部预览上限默认4096最大65536。从 execution_control_tools.go 的常量定义可以看到底层默认值defaultExecutionWaitTimeout 60 * time.Second、maxExecutionWaitTimeout 10 * time.Minute、defaultPartialPreviewBytes 4096、maxPartialPreviewBytes 64 * 1024。也就是说wait_tool_execution单次等待默认 60 秒、上限 10 分钟partial 预览默认取尾部 4KB、上限 64KB。重要概念区分partial output 是「已产生输出的有界预览」不等同于最终result。最终result仍只在工具结束时写入 canonical execution 记录不支持流式输出的工具不会返回 partial 字段。从formatExecutionForModelexecution_control_tools.go的实现可以看出partial_output仅在exec.PartialOutput非空时出现且取尾部partial_output_max_bytes字节tailStringBytes并携带partial_output_bytes、partial_output_truncated、partial_output_updated_at等元信息。4.2 典型流程1. 调用 exec/sqlmap/nmap 等长任务 2. 超过 tool_wait_timeout_seconds 后拿到 execution_id 3. Agent 可继续推理、改用其他工具或调用 wait_tool_execution 4. 仍未完成时可继续等待或调用 cancel_tool_executioncancel_tool_execution的查找顺序execution_control_tools.go是先尝试内部工具 execution再尝试外部 MCP executionserver.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 时默认取 2external_mcp_max_concurrent_total为 0 时默认取 16。熔断配置的连续失败阈值与冷却时间则用于「单 server 连续失败 N 次后在冷却期内快速失败」避免反复重试把已经处于异常状态的远端 server 彻底打坏。相关异步行为与测试覆盖可参考 external_manager_async_test.go。七、输出兜底全文落盘 上下文预览系统使用multi_agent.eino_middleware.reduction_max_length_for_trunc作为统一工具结果上限。当前示例配置为 50000 bytesmulti_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 resultDB/监控存储保存同一份 canonical resultget_tool_execution/wait_tool_execution读取同一份 canonical resultEinoexecute/ 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 trace8.1 新执行结果的写入路径工具完成 - NormalizeToolResultForStorage - 写入内存 execution - 写入 DB - 返回给 Agent因此正常情况下DB 中保存的就是 Agent 拿到的结果同一份 canonical result不存在第二份原始大输出。8.2 恢复路径的二次裁剪续跑恢复时系统使用LastAgentTraceInput中的model-facing trace也就是实际送入 ChatModel 的消息快照而不是原始事件流累计。恢复入口还会对历史 tool 内容再次应用上限防止以下情况撑爆上下文升级前 DB 已经存过原始大输出。手工迁移或导入的数据绕过了当前写入路径。配置从更大阈值改成 50000。未来某条旁路写入漏掉 canonicalize。这一「恢复时二次裁剪」设计非常关键即使历史数据因各种原因绕过了规范化路径恢复续跑时也会被兜底机制再次收拢保证无论数据来源如何送入模型的上下文都不会超过预算。九、关键配置建议与参数说明长任务场景推荐配置可直接放入配置文件参考 config.example.yamlagent: 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_id0 表示等到完成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_iterations0可覆盖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_dirbounded result 中包含可read_file的绝对路径。小结CyberStrikeAI 的工具执行治理本质上回答了两个工程问题「长任务如何不拖死 Agent」与「大输出如何不撑爆上下文」。前者靠两段式执行模型 execution_id有界等待 三个控制工具 会话级取消后者靠NormalizeToolResultForStorage统一兜底 persisted-output全文落盘 恢复时 model-facing trace 二次裁剪。再叠加外部 MCP 的按 server/全局双层并发限制与熔断最终让 Agent 对sqlmap、nmap、nuclei这类真实安全工具保持标准工具语义的同时系统始终处于可控、可观测、可恢复的状态。如需进一步了解相关模块可继续阅读docs/zh-CN/tool-execution-governance.md本文原始依据英文版见 docs/en-US/tool-execution-governance.mdinternal/mcp/execution_control_tools.go三个控制工具的实现internal/mcp/tool_result_guard.goNormalizeToolResultForStorage输出兜底internal/mcp/execution_service.goexecution 状态机与hard_timeout/orphanedinternal/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),仅供参考