多 Agent 安全研究工作站
把开放式漏洞研究拆成三件事:执行事实、推理状态、反证审查。
注:本文记录的是这个工作站的前一版架构。当前架构正在重新设计中。
0 · 起点
0.1 我想验证什么 · 多 Agent 的增益在哪里
我想验证的不是“多几个 agent 是否更热闹”,而是多 Agent 在开放任务里是否能带来稳定增益。增益只有一个标准:在信息不完整、实验会失败、证据必须复现的环境里,系统能不能比单一长会话更可靠地推进。
所以我选漏洞研究做测试场。它不给漂亮解释额外加分;最终结论必须落到请求、响应、日志、账号权限和复现步骤。LLM 可以提出假设,但证据链要能被另一个人重新跑出来。
0.2 为什么用漏洞研究 · 结果必须复现
漏洞研究天然适合作闭环评估:提出假设、执行实验、留下证据、判断是否成立。错的推理会断在复现环节;模糊直觉必须变成具体请求、浏览器动作或日志检查。
图 1这里的评估标准不是“像不像分析”,而是“能不能复现”。失败路径也要写回系统,避免下一轮重复走同一条路。
0.3 CM.com 起点 · 一次手工发现引出的系统问题
起点是一条手工发现的跨租户授权问题。一个免费 CM.com 账户登录后可以访问一组 admin API;同一会话不仅能读取其他客户的路由配置和 MO handler 记录,还能改动部分场景配置。
MO handler 里出现三类凭据:第三方服务的 Bearer token 和 Basic Auth、其他客户的 CM.com PRODUCTTOKEN,以及 gw.cmtelecom.com XML 网关凭据。凭据链还能继续展开。最严重的一条指向 Rijkswaterstaat RWSAlarm,即荷兰洪水警报和道路事故系统的短信发送账户;我只做了零副作用认证验证。
报告被评为 10.0 Exceptional,当天确认修复。这个结果提出了工程问题:能不能把“弱信号 → 实验 → 证据链 → 报告”的过程沉淀成工作站,而不是依赖一次性手动判断。
1 · 当前系统
1.1 执行层与推理层 · Pentagi 负责事实,图谱负责状态
现在的系统分两层。Pentagi 是执行层,负责跑工具、扫端点、枚举变体、保存 finding 和 workspace 证据。Neo4j 图谱是推理状态层,保存 Observation、Hypothesis、Goal 和 Challenge。
两层之间只留一个主动入口:Scout。Scout 下发 scope 和 brief,也把 Pentagi finding 审读后投影成 Observation。Builder 和 Breaker 可以读取执行事实,但不能派执行任务,也不能把自己的推理写成执行事实。
图 2当前系统先看这一张:Pentagi 是执行事实源,Neo4j 是推理状态源,Scout 是两者之间的边界。
1.2 三角色边界 · Scout 派单,Builder 串链,Breaker 审链
Scout、Builder、Breaker 不是三个 agent 互相聊天。它们是三种检查流程:Scout 看覆盖,Builder 串证据,Breaker 找缺口。
拆角色的原因是目标冲突。同一批执行结果,Scout 会优先找未测试区域,Builder 会优先找攻击路径,Breaker 会优先找证据缺口。系统把目标分开,避免一个上下文同时追三种奖励。
图 3三角色共享同一张图谱,但写入目的不同:覆盖、串链、反证。
1.3 状态层 · 只保留会影响下一步的节点
当前图谱是一张工作台,不是安全知识百科。它只保留报告链会用到的四类状态:Goal 是要证明的目标,Observation 是执行层产生的事实,Hypothesis 是对事实的解释,Challenge 是尚未解决的反方意见。
Observation 不能写成推测;Hypothesis 可以被串链,但不能自动变成 finding;active 的 Challenge 会阻断报告候选。这样系统才能知道一条链缺的是事实、解释,还是反证处理。
图 4图谱只保留会被稳定消费的对象:Goal、Hypothesis、Observation、Challenge。
1.4 检索入口 · 新会话从图谱拿当前工作集
新会话不继承旧长上下文。它从图谱取当前工作集:相关事实、未完成假设、未解决反证和下一步目标。
查询入口只做一件事:把图谱状态压成这轮可用的上下文包。语义召回找相近问题,关键词召回找精确线索,合并排序去重,经验加权把最近命中过的内容提前。短会话可以结束,状态不丢。
图 5图谱查询把记忆变成可检索状态:每个新会话只拿当前任务需要的上下文包。
2 · 迭代记录
2.1 Playbook · 规则库挡不住开放状态空间
最早的方案是把安全测试写成规则库。OAuth、IDOR、SSRF、Race Condition、Business Logic 这些类别都被写成 if-then 步骤:看到某种现象,就按对应步骤测试,再用 oracle 判断是否成立。
它失败的原因不是规则写少了,而是产品状态空间开放。真实目标会把权限、流程和对象关系组合出规则外的新形态;规则越写越多,仍然追不上具体产品的例外。Playbook 最后被降级成参考材料,而不是系统主脑。
2.2 长会话多角色 · 推理和执行互相污染
长会话多角色把推理和执行塞进同一段上下文。多个 session 各自扮演角色,看起来能并行覆盖;实际运行时,工具输出占满上下文,推理过程拖慢执行,失败重试又污染下一轮判断。
这一步留下的结论是:推理和执行要拆生命周期。它不是提示词纪律问题,而是上下文环境问题。后面的双脑、图谱状态和执行层平台化,都是在处理这个分离。
2.3 自研 Hub · 从 Claude Code 学运行时工程
自研 Hub 是一次运行时工程练习,不是 prompt 实验。归档时,Python Hub 还留下约 10,000 行代码:hub.py、advisor_tools.py、compact.py,以及 qwen_gateway.py、cdp_capture.py、browser_pool.py 和一个 Web UI。
我从 Claude Code 的运行时设计里学习几件事:tool call loop 里的 compact,只读工具并行,长工具结果落盘,session runtime state,MCP / HTTP / Web UI 多入口,以及 MicroCompact / Session Memory / FullCompact 分层压缩。
这段工程后来被 OpenClaw / Pentagi 替代,但它留下了边界问题:哪些能力该交给通用 agent 平台,哪些状态必须由安全研究系统自己维护。最后保留下来的不是 Hub 本身,而是对 session、compaction、subagent 和工具编排边界的判断。
2.4 双脑 · 用短上下文保护推理空间
双脑不是角色命名,而是上下文预算设计。一个长 session 同时负责思考和执行,很快会被 HTTP 响应、浏览器 DOM、扫描日志和失败重试占满。这些内容对当下执行有用,对下一轮推理通常无关。
所以当时的方案是拆用途:主脑只保留目标、假设、证据缺口和下一个实验;短 session 执行器拿一个明确任务,用干净上下文执行,跑完只交回结果、证据和失败原因。它没有让模型更聪明,只是减少不同任务争用同一段上下文。
图 6双脑表达的是一条边界:推理层保留全局视野,执行层用短上下文完成具体任务。
2.5 反证入口 · 反方意见必须成为状态
早期反证的问题不是“有没有想到反方”,而是反方意见没有稳定入口。它有时写在假设说明里,有时写在测试结果里,有时又写成图上的旧关系。一次对账暴露了问题:158 个测试节点都有结论,但 143 条“这个测试验证了哪个假设”的边没有结论;图里还残留 106 条旧的“支持 / 违反”关系。
后来的规则很简单:Builder 把链尾接到 Goal,Breaker 倒推审整条链;如果有疑虑,就写成 Challenge,并用 CHALLENGES 边挂到被质疑的 Hypothesis 或 Observation 上。反方意见不再是一段评论,而是会阻断报告候选的状态。
图 7Breaker 的疑虑进入图谱状态,而不是停在自由文本批评里。
2.6 Schema 瘦身 · 节点越多,推进越慢
图谱不是节点越多越好。早期我把很多“可能会遇到的东西”提前做成节点:漏洞分类、防御技术、能力标签、范围标签、测试模板、资产容器。看起来覆盖更全,实际是在提前规定漏洞会以哪些形态出现。
漏洞研究不是按预设模板填空。有效链条经常来自具体产品里的组合:某个权限模型、某个对象关系、某个返回字段、某个业务流程拼到一起,才变成可利用路径。节点越多,每个节点就要有创建条件、字段定义、关系类型和检索规则;agent 越容易维护结构,而不是推进证据。
所以后来大幅缩减 schema。一次 Neo4j dump 里有 2,652 个节点、36,812 条边,但很多预设节点没有进入主流程:HypothesisTemplate、TrustBoundary 的索引 readCount 是 0,Playbook 后来变成 0 数据,Header、DataField、Identity、JSFile、TLSCert、Secret 这类资产容器长期没有真实实例。删掉它们不是否定安全知识,而是让主图谱只保留会推动下一步实验的状态。
2.7 计划模式 · 约束要落到工具层
Plan Mode 也来自同一个问题:如果 agent 在计划阶段仍然能顺手改文件,“先计划再执行”就只是口头约定。有效的计划模式必须在工具层禁用写操作,让阶段边界变成系统事实。
这一步留下的原则是:约束机制的寿命必须跟被约束对象一致。临时提示词只能约束一轮话;要约束整个工作流,就要把权限、状态和阶段切换放进结构里。
2.8 演变时间线 · 每一步都来自具体失败
把这些尝试串起来看,系统不是因为想复杂才变复杂。规则库不可扩展,就退回参考;推理和执行互相污染,就拆生命周期;反证字段和测试结论漂移,就把反方动作变成图谱状态。
图 8主要变化不是“多一个角色”,而是把一个失败模式转成可执行约束。
3 · 反思
3.1 适用边界 · 多 Agent 的价值来自隔离
多 Agent 的价值不来自角色数量,而来自职责隔离。如果多个角色读同一段上下文、追同一个奖励、最后互相复述,它们只是一个更贵的单 agent。
这个项目里有效的拆分都对应具体冲突:探索要开放,验证要保守;执行要快,推理要慢;构造攻击链要往前推,反证要倒着拆。多 Agent 真正改变的是三件事:任务寿命、工具权限、状态读写边界。
3.2 真相源 · 最难的是确认系统做过什么
多 Agent 系统最容易失控的地方是状态不一致。agent 很容易开始执行任务,难的是确认它做过什么、哪些是真的、哪些只是写进了文档但运行时从来没发生。设计文档、prompt、图谱、数据库、workspace 如果同时保存结论,系统很快会冲突。
所以后半段越来越多工作是在对账:字段是否被写入,边是否被读取,Pentagi 的 flow 有没有落到 evidence,图谱状态能不能从运行时证据推导出来。这个项目后半段的重心,是多 Agent 系统里的真相维护。
3.3 下一步 · 少造轮子,把判断规则写进系统
自研 Hub、双脑、OpenClaw、Pentagi 不是一条直线升级,而是在不断分辨哪里值得自己发明,哪里应该交给平台。session 管理、compaction、工具编排、浏览器执行这些底座,能用成熟系统就不该反复造。项目自己的部分,是安全研究里的状态模型、证据边界和反证流程。
下一轮不是继续堆角色,而是把判断规则写清楚:哪些 finding 能进报告候选,哪些证据只能算 candidate,哪些跨目标经验可以迁移,哪些 Challenge 必须阻断链。到那一步,多 Agent 才更像工作站,而不是一组会说话的脚本。
引用来源
- Anthropic. Towards Understanding Sycophancy in Language Models. anthropic.com/research/towards-understanding-sycophancy-in-language-models — 支撑本文对 sycophancy / 迎合性风险的工程判断。
- Liu et al. Lost in the Middle: How Language Models Use Long Contexts. arxiv.org/abs/2307.03172 — 支撑本文对长上下文中间信息衰减的判断。
- Anthropic. How we built our multi-agent research system. anthropic.com/engineering/built-multi-agent-research-system — 与本文“子任务隔离、独立上下文、最后合并”的系统设计方向一致。