背景
2025年被称为 Agent 元年,AI 的发展逐渐来到了新的临界点,Agent 凭借其独特的自主理解、感知、规划、记忆、和工具使用能力,实现在处理复杂任务时所展现出来的灵活性和效率。
现今业务场景逐渐复杂多样化,单 Agent 形式框架逐步被效果更好的 Multi Agent 所取代,Multi Agent 凭借其任务拆分、精细化分工的能力,通过分层来优化性能、提升效率
Agent vs Multi Agent
一张简单的架构图来看清 Single Agent System(SAS) 和 Multi Agent System(MAS) 之间的区别
从架构图里可以很容易的看出来,SAS 和 MAS 在处理任务的时候一个是单一 agent 循环 loop 进行执行,另一个则是将任务进行拆解来处理。这两种架构并没有完全好与不好的说法,并且随着底层 LLM 能力不断的提升,SAS 也能够解决复杂问题。但是在某些具体场景的应用下还是存在一些差异,同时彼此之间也有优势和缺点。
Single Agent System
局限性:
- SAS 在复杂问题处理上必然会遇到 Context Windows 长度限制的问题,并且过长的上下文也会影响实际效果
优势:
- 上下文较为连贯,解决问题的时候能保证完成的上下文,通常用于处理单一简单场景的问题
Multi Agent System
局限性:
-
MAS 在解决同类型问题时消耗的 Token 成本较高
-
MAS 需要解决跨 Agent 之间的 Context 共享的问题,并且确保共享信息的精度,如果关键信息丢失那么就有可能导致最终实际效果反而更差
优势:
- 通过将任务拆解,可以通过不同的 Prompt 、Rag 等信息实现团队化问题解决,适合解决多场景的复杂问题
这其实非常像「单兵作战 vs 团队协作」的问题
-
单兵作战效率最高,同时没有沟通成本,但是在实现过程中存在体力和时间的上限
-
团队协作可以能够处理更大更多场景的问题,但是需要付出更多协作、沟通的成本,有时错误的沟通最终会导致效果的降低
在具体实现过程中可以根据实际业务场景来选择,SAS 和 MAS 没有更好一说,能更好的解决实际问题那么就选择哪一种,甚者将两者混合使用也是可以的
Multi Agent 实现思路
前面提到了 Agent 的两种 SAS 和 MAS ,SAS 这类的 Single Agent System 其实理解起来和实现大家都非常的了解,在百炼上就可以快速进行创建,我们只需要提供 prompt 、rag 索引、工具等信息就可以快速创建一个 Agent
那么如果需要构建 MAS 的时候和 SAS 的差异是什么,其实最大的差异就是不同 Agent 之间的上下文共享、以及任务分配处理的问题,我下面举例一个最常见的 MAS 的实现逻辑
-
需要一个全局的 Context 上下文,用于将任务进行存储,通常将任务list、已完成任务、历史任务执行结果等需要共享的信息进行存放
-
子agent 构建对应的 prompt 形成专家 agent 来获取 全局上下文,并进行循环问题解决,解决之后将任务更新到全局上下文
-
如此往复执行,直至 todolist 中的任务被解决,则代表任务执行完成
LangGraph:面向状态与循环的编排框架
LangGraph是一个专门用于构建、管理和部署长期运行、有状态智能体的底层编排框架。专注于解决复杂、非线性的任务流。
前面提到,在实现 MAS 的时候需要做好跨 Agent 之间的上下文以及信息要素的协调,Langgraph 就很好的在底层实现了这一特性,基于图结构来管理所有 agent 执行的任务流,可以实现将 agent 之间进行快速编排,同时 图结构 也是跨 agent 之间共享,所以我们可以基于 langgraph 来快速开发适合我们自己的 MAS 系统
- 核心理念:它基于图结构来管理任务流,将智能体(Agent)和各种工具看作图中的节点(Node),将它们之间的交互看作边(Edge)。这种设计使其非常适用于需要动态决策和循环往复的复杂场景。
4.1 核心概念
-
Graph(图):这是LangGraph的核心。它允许我们构建复杂的任务流程,其中各个Agent都是图中的节点,过程信息可以实现所有Agent共享。值得注意的是,与传统有向无环图(DAG)不同,LangGraph最强大的地方在于它能够支持循环(Cycle),这正是我们自动化演练中处理重试、循环探测等场景所必需的。
-
Node(节点):图中的基本单元,通常代表一个具体的Agent、一个工具调用、或是一个逻辑分支。
-
Edge(边):连接节点之间的连接。它们可以是简单的顺序执行,也可以是并行处理,或是根据条件进行动态转换,这极大地增强了流程的灵活性。
4.2 优势
- 低成本实现记忆与上下文管理:
通过内置的 GraphState 图状态管理机制,所有 Agent 都能访问、共享、更新过程中的状态,以低成本的方式实现了多个 Agent 之间记忆共享和上下文管理的问题
- 复杂场景的良好支持
基于 Graph 可以构建条件边,针对 Agent 执行的不同状态进入不同的路由,下面是一个简单的路由边的例子
4.3 快速构建
我这里以之前构建的一个自动化演练 demo 来介绍 Langgraph 如何实现快速构建基于 React 模式的 Multi Agent 框架
- 定义图状态信息, 图状态中的所有信息都可被各 Agent 实现信息共享
前面所说的全局上下文,在 Langgraph 中只需要定义 图状态就可以了
class GraphState(TypedDict):
initial_goal: str # 最初的演练目标
plan: List[Task] # 主 Agent 生成的计划(ToDoList)
current_task_id: int # 当前正在执行的任务索引
completed_tasks: Annotated[list[Task], operator.add] # 已完成任务的结果列表
final_summary: str # 最终的演练总结报告
- 构建项目中所需要的 Agent
Planner Agent: 这个很简单,编写对应合适的 Prompt 就可以了
直接返回即可,返回之后 GraphState 中的 plan 就会自动更新
返回之后 langgraph 中的 图节点中我们定义的 plan 就会存在数据
例:GraphState:
Executor Agent:因为是具体执行的 Agent 所以实现会比 Planner 步骤要稍微多一些,我们需要做如下的调整,本质还是为 Agent 提供工具,加强对环境以及上下文内容的感知
-
Agent 上下文的传入(上一个子任务的执行情况、执行结果、过程信息的传递)
-
Executor Agent 能够使用的工具注册(工具的注册、字段的描述等信息)
-
历史交互(当前子Agent流程中的问答记录)
子Agent执行的时候可以从 Graph 图中获取到计划任务、以及之前执行的结果信息
从全局共享的 GraphStat 中获取执行任务,并进行子 Agent 的循环执行
下面这个执行流程就是子 agent 执行的实例,会调用各类我们注册的工具,并且把任务结果重新追加到 prompt 中进行重复追问,最终直至 agent 觉得当前子任务执行完成,便退出,交给下一个 agent 来消费下一个 任务
- 根据我们想要编排的流程确定对应的入口、结束点、以及条件转移边
在实际情况中,因为大模型肯定会出现幻觉的问题,所以我们可以写路由的方式来根据 agent 返回的结果来决定下一步如何走,在 langgraph中提供了边路由函数的实现,从而实现基于内容来判断是再次执行还是状态转移等...
在 Langgraph 中可以通过对 Start 和 End 节点的设置,来快速编排 Agent
构建条件边路由信息,图基于 agent 返回的内容来判断是否需要继续执行、或状态转移
- Demo 效果
主 Agent 基于任务进行拆解,然后分发子 Agent 进行执行,执行过程中能有效知晓前面任务的执行情况、执行结果,从而实现完成复杂场景的自动化演练
主Agent根据任务要求,进行攻击编排
子Agent进行任务执行
传统攻击模式的审视
前面提到了 MAS 的整体系统框架,那么现在回过头来看 MAS 架构在能力水位度量侧的应用。
首先,回顾一下传统的人工渗透测试或红队攻击模式。一支专业的攻击队更像一个高度协作的“特种部队”。它通常由不同角色的专家组成,各自负责渗透周期的不同阶段:
-
外部打点手:负责信息收集、外部资产扫描、Web漏洞挖掘等,寻找突破口。
-
内网渗透专家:一旦突破外部边界,负责内网横向移动、权限维持、内网信息收集。
-
漏洞利用工程师:负责分析和利用特定的漏洞,为团队提供“弹药”。
-
恶意基础设施专家:提供C2服务器、代理、混淆等技术支持。
这种模式是一种典型的人工驱动、高度迭代的攻击流程。这个流程的关键在于,每一步的执行之后,都必须依赖工程师的二次分析和判断,然后才能规划下一步行动。因此攻击队整个攻击的流程更像是基于团队之间的协作,不同人员负责不同的问题,因此在演练场景下更加适合 Multi Agent System 的方式去构建
外部开源框架分析
先看外部
PentestGPT:人机协同的渗透助手
PentestGPT 是自动化攻击演练领域较早出现的、基于GPT实现的自动化工具,获得了广泛关注(Star数已达8.7k)。它的核心定位是一款半自动化的渗透机器人,旨在成为渗透测试工程师的“小助手”,而非完全取代人工。
项目Star:8.7k
PentestGPT 大致的执行流程如下
人工输入测试目标 -> 任务规划Agent进行任务拆解 -> 命令生成Agent 生成命令 -> 人工执行 -> 贴出来结果 -> 解析Agent 分析理解报告 -> 在task tree 中标记任务完成
技术亮点:Task Tree
Pentest GPT 通过引入 Task Tree 的全局树状数据结构来解决大模型过程中带来的遗忘问题
-
设计理念:它将复杂的渗透测试流程梳理成一个具有层级、有顺序的树状数据结构。
-
如何实现:这个数据结构被交给大语言模型进行维护和标记。当人工输入结果后,Agent会在树中标记任务状态(如
[完成]
、[待办]
),甚至新增待办任务。
- 渗透测试计划:10.10.11.123
- [完成] 阶段一:信息收集
- [完成] 1.1: 端口扫描与服务识别 (Port Scanning and Service Discovery)
- **[发现]** 开放端口: 22(SSH), 80(HTTP), 443(HTTPS)
- **[发现]** Web服务: Apache 2.4.29 on port 80
- [待办] 1.2: Web服务目录枚举 (Web Content Discovery)
- **[新增待办]** 1.3: 对Apache 2.4.29进行已知漏洞扫描
- [待办] 阶段二:漏洞分析
记忆与上下文管理
-
短期记忆:将历史上与大模型的问答附加到下一次问题之后。
-
长期记忆:通过Python的文件I/O,将整个
Task Tree
的信息以JSON格式进行存储,并在下次启动时加载,结合特定的Prompt继续对话。 -
上下文工程:将工具执行的结果进行总结(summary),然后将这个总结追加到下一次的问答中,确保上下文的简洁有效。
个人总结:定位是简化渗透测试工程师的执行流程,而非追求全自动化演练,相当于生成工具执行的提示信息,然后人工进行执行,然后交给 AI 进行研判
CAI: 多Agent 协作的自动化渗透框架
CAI的定位是一个多角色的自动化渗透测试机器人,其设计思路基于经典的 “管理者-工作者” 设计流模式,目前已能实现自动化完成各类 CTF 题目
-
顶层管理者Agent:负责分析宏观任务、进行任务拆解,并将子任务分发给专门的执行专家Agent。
-
执行专家Agent:这些专门的Agent以 React模式 进行工作,负责执行具体的子任务。
该模式最大的亮点在于,顶层Agent将子Agent视为可调用的工具。它通过一个AgentManager
来实现任务之间的流转,就如同调用一个nmap
或dirsearch
工具一样,大大简化了顶层决策的复杂性。
该项目也通过定义不同的 Prompt 模版来实现不同的角色功能
该项目采用的架构是目前 Multi Agent 中最常见的架构,能够有效拆解复杂任务并进行执行。
-
Planner Agent:该Agent负责任务的规划和流程控制,并能够处理简单的重试逻辑或在根源性报错时清空任务。
-
记忆与上下文管理:每个Agent内部都维护一个自己的
history list
作为短期记忆。
通过构建了 AgentManger 来实现任务之间的转移,将子 agent 看作工具来进行调用
同时具备基础的重试机制,当出现根源性错误时会自动化的清空任务,并进行重新规划
‒ Planner Agent (规划)
‒ task (task1 task 2 )
‒ task1
‒ 顶层 agent 调用 redteam agent 来解决
CAI 的主要缺点在于,由于其采用的是工具调用的方式,每个独立的Agent之间没有共享上下文。这种设计虽然实现了信息隔离,但也导致它缺少了类似PentestGPT的全局树结构视角。在一个完整的渗透测试周期中,全局的、可视化的任务状态跟踪和管理是至关重要的。
总结
从上述两个经典外部开源项目来看,PentestGPT 提供了一种优秀的人机协同模式,通过全局树解决了大模型的长期记忆和上下文问题;而 CAI 则探索了一种全自动化、多Agent协作的架构,通过管理者-工作者模式实现了任务的有效分解和执行。
两者各有所长,也各有局限。我们的内部建设,可以借鉴PentestGPT的全局状态管理能力,并融合CAI的分层协作架构,打造一个既能实现高自动化,又能保证全局可视、可控的下一代自动化演练平台。
未来发展
从技术发展的角度进行考量的话,未来和漏扫描进行结合,极有可能会涌现出一批自动化边界打点、上线、探测的全流程框架,当这些框架都应用广泛了之后,当一个新的组件、漏洞披露之后,攻击者利用的时间会更加缩短,防护应急的窗口时间有可能会进一步压缩。
原有的逻辑有可能是挂着扫描器、信息收集等工具进行常态化收集,然后人工筛选上线收菜。
未来很有可能是常态化自动化探测、收集、利用,有可能人工上线的时候后门已经利用成功并且上线了,甚至都完成了基础信息收集,在高度串联下利用时间会大大压缩。
基于 Multi Agent 的全自动化演练
前面提到纯代码脚本无法在演练过程中午发应对复杂多变的环境,但是借助 Agent 带来的种种能力能够带来全自动化演练的全新可能性。
- 为什么单 Agent 无法解决
在自动化演练场景下,常常是一个长链路、复杂场景,那么就会带来上下文爆炸的问题,过长的上下文导致超出限制,最终导致演练识别,通过基于 MAS 的多 Agent 架构来讲上下文进行拆解,并定义对应的专家来解决特定的问题。
-
过多输入影响生成质量:一个Agent难以同时精通 ATT&CK 中各个阶段所有的知识和工具,同时同时输入过多插件、rag描述,会显著降低 agent 的执行结果的质量,从而导致演练整体性下降
-
长程规划困难:在执行复杂链路的过程中,由于节点众多、链路复杂、信息要素多的情况,必然会带来超出上下文限制的问题,最终导致 Agent 侧的决策失误
-
信息处理瓶颈:所有信息输入和处理都集中在一个Agent上,当演练计划过于复杂时,容易导致实际执行过程中因信息容量过大导致性能瓶颈
因此采用 Multi Agent 作为自动化演练的架构实现,通过让多个Agent之间通过协作来解决各种各样复杂的任务。这与单Agent最大的区别在于,任务执行过程中,通过不断反思、和与环境的不断交互,从而实现攻击过程的逐步执行。
核心思路:基于管理者-工作者模式来进行构建,通过将演练过程任务进行拆解成子任务,然后给到子 Agent 进行执行,并且执行过程中将光锥插件、恶意基础设施框架以 function call、mcp 服务的形式进行接入。
光锥:AI驱动下的安全水位度量
8.1 局限性
以往基于基于纯代码实现的自动化演练,随机将 ATT&CK 下各个阶段下的手法进行随机拼接组合,更像是在线上业务环境中执行的攻击手法测试集,之前是完成了自动化攻击演练从无到有,实现了真实验证线上环境的能力。
但是随着 AI 的发展,为演练带来了更多的可能性,以往纯代码实现的过程中,很难真实的发现线上业务的实际利用点,由于实际情况各种各样的都存在,基于纯代码实现就无法很好的覆盖只能利用代码、正则等手段去判断,同时针对复杂场景,需要大量代码进行场景适配,因为整体攻击手法的利用深度不足。
我们可以将各种攻击手法以 工具、MCP 服务的方式来热插拔到 Agent 上,然后Agent 作为大脑根据与环境的交互调用对应的工具、服务来进行利用。
以一个最简单的场景进行举例:AK、SK 的自动化利用
-
演练深度更深,能够更好的利用脆弱点,组合更多攻击手法链路,实现更多方式的自动化利用
-
攻击手法原子化利用,可以组合出更多演练方式和路径
核心还是在于 Agent 的引入可以和环境进行不断的交互判断,从而为演练带来更多的可能性
基于 Langgraph 的自动化攻击演练引擎
前面主要是介绍 langgraph 是如何帮助我们进行 multi agent 快速构建的,但是上文的单纯组合是远远不够的,例如内部知识的补充(rag向量库建立)、具体执行流程的细节
9.1 RAG 向量知识库
核心还是为了解决大模型不知晓内部特有的攻击场景,由于阿里内部同时还存在部分需要额外构建知识库的信息,例如 HSF、Diamond 等攻击场景,所以需要编写对应的知识信息提供给 Agent 来进行索引,帮助演练
文档加载 -> 文档切割 -> 向量化 -> 向量数据存储
问题输入 -> 向量知识库索引 -> 索引结果合并 prompt -> 输入到 Agent -> 得到回答
自己构建 rag 索引会更加的精细,同时在切割的过程中能确保同一知识块的内容不会被拆分,我们可以加载文档之后基于自定义分隔符来进行索引构建
建立索引知识库的时候指定分隔符
然后利用 Qwen text-embedding-v4 来对文本进行向量化索引,然后存储到向量数据库中即可
在引用 rag 的过程中,使用langchain来将检索后的内容,追加到 prompt 中就可以了
RAG 前后的效果还是比较明显的
9.2 Tool Calling - 光锥插件注册
如何将光锥平台上的数百个插件融入到 MAS 执行体系中
-
如何合理的注册,将海量插件全部注册会导致 prompt 过长超出限制,同时也增加选择难度
-
如何将插件执行与 function call / tool call 来进行结合
针对第一点,我前置了一个基于攻击意图选择插件的 Agent,该 Agent 主要是基于输入的攻击目标并结合 rag 索引来筛选出符合要求的插件,从而将数量级进行大幅度降低
然后将降低数量级后的插件给到执行 Agent 进行注册,然后让 Agent 基于当前目标选择合适的插件
针对第二点,采用 tool call 相结合,实现插件的动态引入执行
正常大模型的插件调用通常是将工具的描述和参数描述添加到 prompt 中然后给到大模型,然后大模型会返回调用的工具名和结果
所以在光锥插件调用上也是一样的,我们只需要 agent 返回对应的插件名和入参信息即可,然后编写一个统一的插件执行代码进行包装, 同时传入描述给大模型即可实现光锥插件的大模型集成
9.3 记忆模块
通常的 Agent 架构中主要是短期记忆和长期记忆来做区分
短期记忆
短期记忆主要是将上下文、以及历史问答的信息重新附加到 prompt 里来解决 agent 之间任务感知不足的情况,可以在 prompt 中进行内容预留,然后将问答等信息进行补充
长期记忆
长期记忆的引入在光锥场景下主要是为了防止链路的单一化
-
将历史执行的链路进行归档到数据库,然后在新一次执行的时候优先执行权重较低的链路,从而实现攻击手法的多样化
-
将以往剪枝链路进行归档存储,当后续演练可以利用该剪枝链路则进行重新导出利用
使用的时候我们只需要编写一个 sql 的 query 函数,然后将查询出来的信息填充到 prompt 里即可
其实攻击过程中不断归档的信息、长期记忆,就是不断积累的壁垒,高质量数据让攻击演练流程更加智能化