ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

基于双模型驱动的漏洞扫描Agent开发实战:DeepSeek与laya协作

基于双模型驱动的漏洞扫描Agent开发实战:DeepSeek与laya协作

1. 为什么我要自己造一个漏洞扫描 Agent

做安全审计这行的朋友应该都有体会:传统漏洞扫描器用起来越来越像在开盲盒。你配好目标、选好策略、点下扫描,然后等上几十分钟甚至几个小时,最后拿到一份动辄几百页的报告,里面塞满了误报、低危噪音和一堆"疑似存在"的模糊结论。真正需要立刻处理的高危漏洞,反而被淹没在信息洪流里。

我最初的想法很简单:能不能让大模型来当这个"筛子"和"分析师",把扫描器吐出来的原始结果做二次研判,自动判断哪些是真问题、哪些是误报、优先级怎么排、修复建议怎么写。市面上已经有不少把 LLM 接进安全流程的尝试,但大多停留在"把报告丢给模型让它总结"这种浅层用法,实际用起来问题很多——模型会幻觉、会漏判、会对上下文理解不到位。

于是我决定自己动手,用 workbuddy 搭一个真正能跑起来的漏洞扫描 Agent。核心思路是双模型驱动:DeepSeek 负责深度推理和漏洞研判,laya 负责结构化解析和任务编排,两者分工协作,再配合一套可插拔的扫描工具链,形成一个从"发现"到"研判"到"报告"的完整闭环。这套东西跑通之后,我把它用在了自己的几个测试靶场和内部资产上,效果比我预期的要好不少。

这篇文章我会把整个开发过程拆开讲清楚:为什么选这两个模型、Agent 的架构怎么设计、扫描工具怎么接、误报怎么压、实际跑起来踩了哪些坑。如果你也在做 AI Agent 相关的安全工具,或者单纯想看看一个能落地的 Agent 是怎么从零搭起来的,应该能拿到一些能直接抄的东西。

2. 双模型分工:DeepSeek 和 laya 各自该干什么

2.1 为什么不用单模型硬扛

一开始我也想过,直接用一个能力强的模型把所有活都干了不就行了?实测下来不行。漏洞扫描这个场景对模型的要求其实是分裂的:

  • 结构化解析:扫描器输出的 XML、JSON、文本报告格式五花八门,需要模型稳定地把非结构化内容转成规整的数据结构。这个任务要求确定性高、格式稳定、不能自由发挥。
  • 漏洞研判:判断一个 SQL 注入告警是真漏洞还是误报,需要结合请求响应、参数上下文、业务逻辑做推理。这个任务要求推理深度够、能处理长上下文、有一定的安全领域知识。

这两类任务用同一个模型,要么解析不够稳,要么研判不够深。所以我把它们拆开,让每个模型干自己最擅长的事。

2.2 DeepSeek 在 Agent 里的角色定位

DeepSeek 我主要用它做研判层。具体来说,扫描器吐出来的每一条原始告警,都会经过 DeepSeek 做一轮分析,输出结构化的研判结果:

{ "vuln_id": "scan-2024-001", "original_alert": "SQL Injection in /api/user?id=1", "verdict": "true_positive", "confidence": 0.87, "severity": "high", "reasoning": "参数 id 直接拼接进 SQL 查询,payload 返回了数据库报错信息,且响应时间存在明显延迟差异,符合布尔盲注特征", "remediation": "使用参数化查询,对 id 做类型强校验", "false_positive_reason": null }

选 DeepSeek 的核心原因是它在长上下文推理和中文安全语境上的表现比较稳。漏洞报告里经常夹杂大量中文描述、业务注释、自定义规则名,DeepSeek 对这些内容的理解明显比一些纯英文优化的模型要顺。另外它的推理链输出比较克制,不会像某些模型那样过度发散,这对需要稳定输出的研判任务很关键。

提示:研判层一定要让模型输出结构化 JSON,而不是自然语言。自然语言结论没法直接进下游流程,还得再解析一遍,纯属给自己找麻烦。

2.3 laya 负责的结构化解析与编排

laya 在这个 Agent 里承担的是解析层和编排层的工作。扫描器原始输出往往是一大坨文本或者嵌套很深的 XML,直接丢给研判模型会浪费大量 token 在无关内容上。laya 先把这些内容做清洗、抽取、归一化,把每条告警拆成标准字段,再决定哪些需要送进研判、哪些可以直接归档。

laya 的另一个作用是任务编排。一个完整的扫描流程包含很多步骤:目标发现、端口探测、指纹识别、漏洞扫描、结果聚合、研判、报告生成。这些步骤之间有依赖关系,有的可以并行,有的必须串行。laya 负责维护这个流程状态,决定下一步该调哪个工具、该不该触发研判、研判结果怎么回写。

我实测下来,把解析和编排交给 laya 之后,整个 Agent 的 token 消耗降了大概四成,因为送进 DeepSeek 的都是已经清洗过的关键信息,没有冗余噪音。

2.4 两个模型之间的数据流设计

两个模型不是各干各的,中间的数据流设计很关键。我的做法是:

  1. laya 解析扫描结果,产出标准化的告警列表
  2. 对每条告警做预筛:明显是信息级的直接归档,可疑的送研判
  3. DeepSeek 对可疑告警做研判,输出结构化结论
  4. laya 接收研判结果,做去重、聚合、优先级排序
  5. 最终生成报告,同时把研判结论回写到知识库,供后续同类告警参考

这个流程里有个细节值得说:预筛规则很重要。如果所有告警都送 DeepSeek,成本会爆炸,而且很多低危告警根本不需要模型介入。我设的预筛规则是——CVSS 评分低于 4.0 且没有明确利用路径的,直接归档;涉及注入、反序列化、权限绕过这类高危类型的,无条件送研判。

3. Agent 的整体架构与 workbuddy 的落地方式

3.1 分层架构:从工具层到决策层

整个 Agent 我分成了四层,每层职责清晰,方便单独替换和调试:

层级职责主要组件
工具层执行具体扫描任务端口扫描、指纹识别、POC 引擎、爬虫
解析层结果清洗与归一化laya 解析器、字段映射、去重
研判层漏洞真伪与优先级判断DeepSeek 推理、置信度打分
编排层流程控制与状态管理workbuddy 任务调度、状态机

这样分层的好处是,任何一层出问题都能快速定位。比如研判结果不准,我只需要调研判层的 prompt 和预筛规则,不用动工具层。工具层想换扫描引擎,也不影响上面的逻辑。

3.2 workbuddy 在项目里的实际作用

workbuddy 在这个项目里主要承担任务编排和 Agent 运行时的角色。它帮我解决了几个很实际的问题:

  • 任务状态管理:一个扫描任务可能跑几十分钟,中间涉及几十次工具调用和模型调用,workbuddy 帮我维护整个任务的状态,断了能续、错了能重试。
  • 工具注册与调用:我把扫描工具、解析器、模型接口都注册成 workbuddy 的 skill,Agent 在运行时按需调用,不用硬编码调用逻辑。
  • 上下文管理:Agent 跑久了上下文会膨胀,workbuddy 的上下文管理机制帮我做了裁剪和摘要,避免 token 无限增长。

具体配置上,我把每个能力都封装成一个 skill,比如port_scan、fingerprint_detect、vuln_scan、result_parse、vuln_judge、report_gen。Agent 的主循环根据当前状态决定调哪个 skill,skill 执行完把结果回写状态,循环继续。

3.3 状态机设计:让扫描流程可控可回溯

扫描流程我用状态机来管,核心状态有这么几个:

INIT -> RECON -> SCAN -> PARSE -> JUDGE -> REPORT -> DONE | | | v v v ERROR <- ERROR <- ERROR

每个状态都有明确的进入条件、执行动作、退出条件。比如SCAN状态只有在RECON完成且发现了存活目标时才能进入;JUDGE状态只有在PARSE产出了告警列表后才能进入。

状态机的好处是可回溯。任何一步出问题,我能清楚知道卡在哪个状态、上一步的输出是什么、该从哪里重试。实测中这个设计帮我省了大量排查时间,尤其是扫描任务跑到一半失败的时候,不用从头再来。

3.4 工具链的接入方式与选型考量

工具链这块我没有追求大而全,而是按"够用、可替换、输出可解析"三个标准来选:

  • 端口与指纹:用轻量级的探测工具,输出 JSON,方便 laya 解析
  • 漏洞扫描:接了一个支持自定义 POC 的引擎,方便我针对特定场景写规则
  • 爬虫:用无头浏览器方案,能处理 JS 渲染的页面

选型时我特别看重输出格式的规范性。有些扫描器输出的是花花绿绿的文本报告,解析起来极其痛苦,这种我直接排除。宁可功能少一点,也要输出结构清晰,因为解析层的稳定性直接决定整个 Agent 的可靠性。

注意:工具链接入时一定要做超时和异常处理。扫描工具卡死、返回空、返回格式错误都是常态,Agent 必须能识别这些异常并做出合理反应,而不是直接崩溃。

4. 漏洞研判的核心逻辑:怎么让模型少说废话多干活

4.1 研判 Prompt 的设计要点

研判层的 prompt 是整个 Agent 里我改得最多的部分。早期版本我写得很"全面",把漏洞类型、判断标准、输出格式全塞进去,结果模型经常抓不住重点,输出一堆无关分析。后来我做了几个关键调整:

第一,明确角色和边界。prompt 开头直接告诉模型:"你是一个漏洞研判专家,你的任务是对给定的扫描告警判断真伪,只输出 JSON,不要输出任何解释性文字。"

第二,给判断依据,不给判断结论。我不告诉模型"这个应该是真漏洞",而是给它判断所需的全部证据:原始请求、响应、payload、上下文。让模型自己推。

第三,用 few-shot 示例锚定输出格式。我给两三个正例和反例,让模型照着格式输出。实测下来,有示例的情况下输出格式错误率从 15% 降到了 2% 以下。

4.2 误报压制:从规则预筛到模型复核

误报是漏洞扫描的老大难。我的压制策略是两层过滤:

第一层是规则预筛,用确定性规则干掉明显误报。比如:

  • 响应里包含"error"但状态码是 200 且没有数据库特征 → 大概率误报
  • 告警类型是"信息泄露"但泄露内容是公开信息 → 直接归档
  • 同一 URL 同一参数重复告警 → 去重

第二层是模型复核,对预筛后剩下的可疑告警做深度研判。模型会结合请求响应上下文,判断是否存在真实的利用路径。

实测数据:某次对内部测试靶场的扫描,原始告警 312 条,规则预筛后剩 87 条,模型复核后确认真实漏洞 23 条,误报率从最初的 70% 以上压到了 15% 左右。这个数字不算完美,但已经比传统扫描器好用太多了。

4.3 置信度打分与人工复核的衔接

模型输出的置信度不是拿来好看的,是要真正参与流程决策的。我设了三档:

置信度区间处理方式
≥ 0.85直接进报告,标记为高可信
0.6 ~ 0.85进报告但标记"待人工确认"
< 0.6归档,不打扰人工

这样人工只需要关注中间那档,工作量大幅下降。而且我会把人工复核的结果回写,作为后续同类告警的参考,让 Agent 越用越准。

4.4 长上下文场景下的 token 控制

漏洞研判经常需要看很长的请求响应,尤其是涉及文件上传、反序列化的场景,payload 可能几 KB。如果无脑全塞进 prompt,token 消耗很快失控。

我的做法是分层截断:

  • 请求头只保留关键字段(Content-Type、Cookie、Authorization 等)
  • 请求体超过阈值就截断,但保留首尾和特征片段
  • 响应体只保留状态码、关键 header、以及和漏洞类型相关的片段

这样处理后,单条告警的研判 token 消耗从平均 3000+ 降到了 800 左右,成本降了七成多,研判准确率基本没受影响。

5. 实测踩坑记录:那些文档里不会写的问题

5.1 模型幻觉导致的"假阳性研判"

最开始的版本,模型经常把不存在的漏洞"研判"成真的。比如一个普通的参数回显,模型会脑补出"可能存在 XSS"。这个问题在早期特别严重,因为 prompt 里没有明确要求模型"基于证据判断"。

修复方法是在 prompt 里加了一条硬约束:"如果证据不足以支撑结论,必须输出 verdict 为 uncertain,并说明缺少什么证据。"加了这条之后,模型不再强行下结论,uncertain 的比例大概在 10% 左右,这些会被归到人工复核队列。

5.2 扫描器输出格式不一致的解析难题

不同扫描工具的输出格式差异极大,有的用 XML,有的用 JSON,有的直接是文本。laya 解析层最初是按工具写死的解析器,结果每接一个新工具就要改代码,维护成本很高。

后来我改成配置驱动:把每种工具的字段映射写成配置,解析器读配置做转换。这样接新工具只需要加一份配置,不用动代码。这个改动让工具接入时间从半天缩短到半小时。

5.3 并发扫描时的状态竞争问题

当同时跑多个扫描任务时,出现了状态串扰——A 任务的研判结果写到了 B 任务里。排查后发现是状态管理没有做任务隔离,多个任务共享了同一份上下文。

修复方案是给每个任务分配独立的任务上下文空间,所有状态读写都带上任务 ID。workbuddy 的任务隔离机制在这里帮了大忙,我只需要在 skill 调用时传入正确的任务上下文就行。

5.4 研判结果回写知识库的坑

我设计了一个知识库,把研判结论存起来供后续参考。但早期回写时没做去重和版本管理,导致同一个漏洞被反复写入,知识库越来越臃肿,检索也越来越慢。

后来加了指纹去重:用漏洞类型 + 目标特征 + payload 特征生成指纹,相同指纹只保留最新结论。同时加了定期清理机制,超过一定时间且未被引用的记录自动归档。

6. 从靶场验证到真实资产:效果与边界

6.1 靶场环境下的验证结果

我在几个常见的漏洞靶场上做了验证,覆盖 SQL 注入、XSS、命令执行、文件包含、越权等类型。整体表现:

  • 检出率:对已知漏洞的检出率在 90% 以上,漏报主要出现在需要多步交互才能触发的场景
  • 误报率:经过双层过滤后,最终报告里的误报率控制在 15% 以内
  • 研判准确率:模型对真漏洞的判定准确率约 88%,对误报的识别准确率约 82%

这个数据说明 Agent 已经能承担大部分初筛工作,但还不能完全替代人工。

6.2 真实资产扫描中的意外发现

在内部资产上跑的时候,Agent 发现了一些传统扫描器漏掉的问题,主要是逻辑类漏洞——比如参数篡改导致的越权、业务流程绕过。这类漏洞传统扫描器很难覆盖,但模型结合上下文推理后能识别出来。

不过也暴露了边界:对于需要多步状态保持才能触发的漏洞(比如需要先登录、再操作、再验证的链式漏洞),当前 Agent 的处理能力有限,因为状态机还没有很好地支持跨请求的会话保持。

6.3 当前方案的适用边界

说清楚边界比吹效果更重要。这套 Agent 目前适合:

  • 大批量资产的初筛和优先级排序
  • 已知漏洞类型的快速研判和误报压制
  • 常规 Web 漏洞的自动化审计

不太适合:

  • 需要深度交互的复杂逻辑漏洞
  • 需要逆向分析的二进制漏洞
  • 对准确性要求极高的合规审计场景(仍需人工兜底)

6.4 后续可以继续打磨的方向

如果继续迭代,我会优先做这几件事:一是把会话保持做进状态机,支持链式漏洞检测;二是把研判结论做成可检索的知识库,让 Agent 能参考历史案例;三是加一个反馈闭环,人工复核的结果自动优化预筛规则和 prompt。

这套东西我从零搭到现在大概花了三周多的业余时间,中间踩的坑基本都写在上面了。如果你也想用 workbuddy 搭类似的 Agent,我的建议是先把解析层做扎实,别急着上模型。解析不稳,后面全是空中楼阁。另外研判 prompt 一定要反复调,别指望一次写好,我前后改了十几版才稳定下来。

返回列表