
文章来源|MS08067 AI安全应用知识星球
作者:小玉玉

在 AI 驱动的安全测试领域,PentAGI 是一个不容忽视的名字。作为一款用 Go 语言构建的全栈 AI 渗透测试平台,它通过多智能体编排、三层记忆系统、10+ LLM 提供商插件式集成和 Docker 沙箱执行等前沿设计,构建了一套从目标侦察到漏洞利用再到报告生成的全自动安全测试流水线。本文将深入拆解其技术架构,剖析工程实现的关键细节。
PentAGI(Penetration testing Artificial General Intelligence)的核心理念是用多智能体系统取代单一大模型对话。它不是把 LLM 当成一个智能聊天框,而是构建了一个由 15 个专业 Agent 角色组成的协作网络,每个角色各司其职,通过工具调用和消息传递协同完成渗透测试任务。
系统的执行模型从顶层到底层分为四层:
层级 | 职责 | 生命周期 |
|---|---|---|
Flow(流程) | 一个完整的渗透测试会话或目标 | active / completed / failed |
Task(任务) | 一次分析步骤(如端口扫描) | pending / running / done / failed |
SubTask(子任务) | 一个具体的可执行单元 | queued / running / completed / failed |
Agent Chain(代理链) | Agent 协作执行子任务 | 动态编排,结束后回收 |
这种分层设计的价值在于:将一次渗透测试分解为可追踪、可审计、可恢复的执行单元,避免了大模型单次对话上下文溢出和难以复现的问题。
在 Go 后端中,这些类型通过 GraphQL Schema 精确定义。下面是从 schema.graphqls(位于 backend/pkg/graph/)中摘录的 Flow、Task、SubTask 类型定义。注意 Subtask 绑定了一个 taskId 外键,而 Task 又通过 flowId 指向所属的 Flow——这种双向关联使得系统可以高效地执行「给定 Flow,查询所有 Task 及其 SubTask」的树形遍历操作:
# 文件位置: backend/pkg/graph/schema.graphqls
# GraphQL 类型定义 —— 核心执行模型的骨架
# ==================== 核心实体 ====================
type Flow {
id: ID! # 全局唯一标识
title: String! # 用户为本次渗透测试起的名称
status: StatusType! # 生命周期: active / completed / failed
terminals: [Terminal!] # 沙箱终端会话列表
provider: Provider! # 绑定的 LLM 提供商
createdAt: Time! # 创建时间
updatedAt: Time! # 最近一次更新时间
}
type Task {
id: ID! # 全局唯一标识
title: String! # 任务名称,如"端口扫描"
status: StatusType! # pending / running / done / failed
input: String! # 用户的输入描述(从 Flow 传递过来)
result: String! # 执行结果摘要(Agent 链完成后填充)
flowId: ID! # 所属 Flow 的外键
subtasks: [Subtask!] # 该任务下的所有子任务
createdAt: Time!
updatedAt: Time!
}
type Subtask {
id: ID! # 全局唯一标识
status: StatusType! # queued / running / completed / failed
title: String! # 子任务标题
description: String! # 详细描述,包含执行目标和注意事项
result: String! # 执行结果,由 Reporter Agent 填充
taskId: ID! # 所属 Task 的外键
createdAt: Time!
updatedAt: Time!
}
值得注意的是,Subtask 的 description 字段并非简单的标题补充——Generator 和 Refiner Agent 在生成子任务列表时,会将执行策略、目标细节、注意事项都写入该字段,作为后续 Pentester Agent 的执行「剧本」。这样设计的好处是:决策与执行分离——规划阶段生成的剧本完整保存在 Subtask 中,执行阶段无需反复调用 LLM 重新理解目标,大大减少了 token 消耗。
在 Web UI 中,任务管理界面以树形结构清晰展示了 Flow → Task → SubTask 的层级关系。下方截图展示了一个渗透测试 Flow 的内部视图:每个 Task 标题下方关联着多个 SubTask,右侧面板展示了当前任务的详细信息和执行进度。这种可视化的任务拆解方式让测试负责人可以一目了然地掌握全局执行状态:

图1:任务管理界面——Flow 内 Task 与 SubTask 的树形展示
flowchart TB
subgraph Flow层
F[Flow 渗透测试会话]
end
subgraph Task层
T1[Task 信息收集]
T2[Task 漏洞识别]
T3[Task 利用验证]
T4[Task 报告生成]
end
subgraph SubTask层
ST1[SubTask 端口扫描]
ST2[SubTask 服务识别]
ST3[SubTask CVE匹配]
ST4[SubTask 漏洞利用]
end
subgraph Agent链
A1[Primary Agent<br/>主协调者]
A2[Researcher<br/>研究员]
A3[Pentester<br/>渗透者]
A4[Reporter<br/>报告员]
end
F --> T1
F --> T2
F --> T3
F --> T4
T1 --> ST1
T1 --> ST2
T2 --> ST3
T3 --> ST4
ST1 --> A1 --> A2
ST2 --> A1 --> A3
ST3 --> A1 --> A3
ST4 --> A1 --> A4

图2:四层执行模型架构
架构的高度抽象让我们看到了 PentAGI 的整体轮廓,但要真正理解这个平台为何能高效运作,还需要深入其工程底层,逐一拆解支撑这套执行模型的七个关键技术面。接下来,我们从技术栈选型开始,逐步深入到多 Agent 系统、工具编排、记忆体系等核心模块。
PentAGI 的技术选型体现了对性能和工程化的极致追求:
层级 | 技术方案 | 选型理由 |
|---|---|---|
后端语言 | Go 1.24 | 高并发、低资源占用、单二进制部署,适合多 Agent 并行 |
前端框架 | React 19 + TypeScript + Vite + SWC | 现代 SPA,类型安全,开发体验好 |
LLM 框架 | langchaingo(vxcontrol 分支) | Go 生态的 LangChain 替代,支持流式推理和嵌入 |
API 协议 | GraphQL(gqlgen)+ REST(Gin)+ WebSocket | GraphQL 订阅实现实时 Agent 状态推送 |
数据库 | PostgreSQL + pgvector | 关系数据 + 向量语义搜索一体 |
知识图谱 | Neo4j + Graphiti | 实体关系追踪,跨会话上下文理解 |
容器引擎 | Docker SDK | 隔离执行所有渗透操作 |
可观测性 | Langfuse + OpenTelemetry + Grafana | LLM 调用追踪 + 系统指标全链路监控 |
登录 PentAGI 后,首先映入眼帘的是如下主界面。左侧为功能导航栏,中央为 Flow 列表面板,每个 Flow 代表一次完整的渗透测试会话。界面的核心交互区域采用了标准的「列表 + 详情」布局,用户可以在侧边栏快速切换不同 Flow,通过右上角的搜索和筛选快速定位目标测试任务:

图3:PentAGI Web 主界面——Flow 列表与导航
有了这些技术作为底座,PentAGI 得以构建其最核心的竞争力——多智能体协作系统。不同于市面上将单个 LLM 包装成一个「万能 Agent」的做法,PentAGI 选择了一条更工程化的路径:将渗透测试中不同阶段的能力拆解为 15 个专业化角色,让它们像人类红队一样分工协作。
PentAGI 最核心的工程创新在于其专业化多 Agent 架构。系统预定义了 15 个 Agent 角色,通过 cast 消息传递抽象进行通信,每个角色拥有专属的工具权限和系统提示词:
flowchart LR
subgraph 协调层
PA[Primary Agent<br/>主引导者]
AS[Assistant<br/>交互助手]
end
subgraph 规划层
GEN[Generator<br/>任务生成器]
REF[Refiner<br/>任务优化器]
ADV[Adviser<br/>顾问]
PLA[Planner<br/>规划器]
end
subgraph 执行层
PEN[Pentester<br/>渗透执行者]
COD[Coder<br/>代码编写者]
SEA[Searcher<br/>信息搜索者]
ENR[Enricher<br/>上下文丰富者]
INS[Installer<br/>工具安装者]
end
subgraph 记忆与反思层
MEM[Memorist<br/>记忆管理者]
REFL[Reflector<br/>反思校验者]
SUM[Summarizer<br/>摘要生成者]
TC[Tool Call Fixer<br/>工具调用修复者]
end
subgraph 输出层
REP[Reporter<br/>报告生成者]
end
PA --> GEN
GEN --> REF
REF --> PEN & COD & SEA
SEA --> ENR
PA --> AS
PEN --> MEM
COD --> MEM
MEM --> PA
PEN & COD --> REFL
REFL --> TC
REFL --> SUM
PA --> REP
ADV --> PA
PLA --> GEN
style PA fill:#1a73e8,stroke:#0d47a1,color:#fff
style REP fill:#388e3c,stroke:#1b5e20,color:#fff
style REFL fill:#e65100,stroke:#bf360c,color:#fff

图4:15 个 Agent 角色的协作拓扑
在实际使用中,Agent 的执行过程会以流式消息的形式实时展示在 Web 界面中。下方截图展示了一个 Flow 中 Agent 的推理过程和工具调用记录——左侧是用户输入的测试目标,右侧区域实时流式输出 Agent 的思考过程和执行结果。用户可以看到每个 Agent 的「思考链」(Chain of Thought)、调用的工具名称、以及工具返回的结果,实现了完整的可解释性:

图5:Agent 执行过程实时展示——思考链与工具调用记录
各角色的职责分工清晰明确:
这种专业化分工的设计哲学是:让每个 Agent 只做自己最擅长的事,避免单个 Agent 上下文膨胀和能力泛化带来的质量问题。
在代码层面,所有 Agent 类型以枚举形式定义在 GraphQL Schema 中。下面的代码来自 schema.graphqls,它同时也是 gqlgen 代码生成器的输入——Go 后端的 AgentType 常量和前端的 TypeScript 类型都是从这个 Schema 自动生成的,保证了前后端类型的一致性:
# 文件位置: backend/pkg/graph/schema.graphqls
# Agent 角色枚举 —— 系统所有预定义角色的精确清单
enum AgentType {
primary_agent # 主协调者: 负责任务分解与 Agent 调度
reporter # 报告生成者: 结构化输出测试报告
generator # 任务生成者: 从用户输入生成子任务计划
refiner # 任务优化者: 优化子任务覆盖面和质量
reflector # 反思校验者: 验证执行结果,发现异常
enricher # 上下文丰富者: 提炼和丰富搜索结果
adviser # 导师顾问: 监控执行并提供策略建议
coder # 代码编写者: 编写自定义攻击代码
memorist # 记忆管理者: 管理向量存储和知识库
searcher # 信息搜索者: 多引擎情报收集
installer # 工具安装者: 在沙箱中安装额外工具
pentester # 渗透执行者: 执行扫描/利用/横向移动
summarizer # 摘要生成者: 压缩长对话链控制上下文
tool_call_fixer # 工具调用修复者: 修正失败的 LLM 工具调用
assistant # 交互助手: 处理用户的实时问答
}
而在 Go 后端中,这个枚举会被编译为对应的常量,并挂载在 AllAgentType 切片上供注册和校验使用。代码位于 graph/model/models_gen.go(自动生成文件),以字符串常量形式保存,确保 Agent 类型在序列化和反序列化时不会出错:
// 文件位置: backend/pkg/graph/model/models_gen.go
// AgentType 字符串常量 —— 由 gqlgen 从 GraphQL Schema 自动生成
type AgentType string
const (
AgentTypePrimaryAgent AgentType = "primary_agent"
AgentTypeReporter AgentType = "reporter"
AgentTypeGenerator AgentType = "generator"
AgentTypeRefiner AgentType = "refiner"
AgentTypeReflector AgentType = "reflector"
AgentTypeEnricher AgentType = "enricher"
AgentTypeAdviser AgentType = "adviser"
AgentTypeCoder AgentType = "coder"
AgentTypeMemorist AgentType = "memorist"
AgentTypeSearcher AgentType = "searcher"
AgentTypeInstaller AgentType = "installer"
AgentTypePentester AgentType = "pentester"
AgentTypeSummarizer AgentType = "summarizer"
AgentTypeToolCallFixer AgentType = "tool_call_fixer"
AgentTypeAssistant AgentType = "assistant"
)
// AllAgentType 用于枚举校验和遍历
var AllAgentType = []AgentType{
AgentTypePrimaryAgent,
AgentTypeReporter,
// ... 省略其他常量
}
然而,Agent 再强大也无法凭空执行操作。它们需要一套「双手」来触碰目标系统、收集情报、执行命令——这正是工具调用体系的价值所在。接下来我们看看 PentAGI 如何通过 40+ 个注册工具为每个 Agent 赋予行动能力。
Agent 本身不直接执行任何操作,而是通过工具调用(Tool Calling / Function Calling)来触发系统能力。PentAGI 维护了一个统一的工具注册表,将所有工具按类型划分:
工具类别 | 工具列表 | 用途 |
|---|---|---|
屏障工具 | done, ask | 标记子任务完成 / 向用户提问 |
Agent 委托 | coder, pentester, memorist, search | 将子任务委托给其他 Agent |
结果存储 | code_result, hack_result, search_result | 将执行结果持久化 |
环境工具 | terminal, file, browser | 执行系统命令、文件操作、浏览器操作 |
网络搜索 | Google, DuckDuckGo, Tavily, Traversaal, Perplexity, Searxng, Sploitus | 多源情报收集 |
向量搜索 | search_in_memory, search_guide, search_answer, search_code | 从记忆库和知识库中检索 |
向量存储 | store_guide, store_answer, store_code | 将新知识写入记忆库 |
每个工具都由 FlowToolsExecutor 根据当前 Agent 角色进行权限包装,确保不同角色的 Agent 只能调用其权限范围内的工具。
在代码实现层面,工具注册表位于 pkg/tools/registry.go。每个工具通过 FunctionDefinition 结构来定义,包含工具名称、用途描述和参数 Schema。参数 Schema 由 Go 结构体通过 jsonschema.Reflector 自动反射生成,LMM 在调用工具时直接获得结构化的 JSON 参数契约,而非自由文本。下面是注册表的核心片段,展示了从屏障工具 done 到搜索引擎 sploitus 的完整定义:
// 文件位置: backend/pkg/tools/registry.go
// 工具注册表 —— 所有工具以 FunctionDefinition 格式统一注册
// 每个注册条目包含: 名称、用途描述、JSON Schema 参数
// registryDefinitions 存储所有已注册的工具定义
// LLM 通过 function calling 机制调用这些工具,参数由反射自动生成 Schema
var registryDefinitions = map[string]llms.FunctionDefinition{
// 终端执行工具 —— 在 Docker 沙箱中执行任意命令
TerminalToolName: {
Name: TerminalToolName, // 常量值: "terminal"
Description: "Calls a terminal command in blocking mode. " +
"Use timeout=0 or a negative value to apply the configured server default timeout. " +
"Explicit positive values are accepted up to 10800 seconds (3 hours); " +
"values outside this range are replaced by the server default. " +
"Only one command can be executed at a time",
Parameters: reflector.Reflect(&TerminalAction{}),
// ^ 自动反射 TerminalAction 结构体为 JSON Schema
},
// 文件操作工具 —— 读写容器内的文件
FileToolName: {
Name: FileToolName, // 常量值: "file"
Description: "Modifies or reads local files",
Parameters: reflector.Reflect(&FileAction{}),
},
// 浏览器工具 —— 在隔离的 scraper 容器中打开网页
BrowserToolName: {
Name: BrowserToolName, // 常量值: "browser"
Description: "Opens a browser to look for additional information from the web site",
Parameters: reflector.Reflect(&Browser{}),
},
// Google 搜索引擎 —— 快速查询,返回短内容
GoogleToolName: {
Name: GoogleToolName, // 常量值: "google"
Description: "Search in the google search engine, it's a fast query and the shortest content " +
"to check some information or collect public links by short query",
Parameters: reflector.Reflect(&SearchAction{}),
},
// Sploitus 漏洞利用聚合器 —— 搜索公开的 PoC 和漏洞利用代码
SploitusToolName: {
Name: SploitusToolName, // 常量值: "sploitus"
Description: "Search the Sploitus exploit aggregator (https://sploitus.com) " +
"for public exploits, proof-of-concept code, and offensive security tools. " +
"Sploitus indexes ExploitDB, Packet Storm, GitHub Security Advisories, " +
"and many other sources. Use this tool to find exploit code and PoCs " +
"for specific software, services, CVEs, or vulnerability classes.",
Parameters: reflector.Reflect(&SploitusAction{}),
},
// done 屏障工具 —— 标记子任务完成,终止当前 Agent 链
FinalyToolName: {
Name: FinalyToolName, // 常量值: "done"
Description: "If you need to finish the task with success or failure, use this tool",
Parameters: reflector.Reflect(&Done{}),
},
// ... 其他工具的注册在此省略
}
// 工具类型映射 —— 将每个工具名称归类,用于权限控制
var toolsTypeMapping = map[string]ToolType{
FinalyToolName: BarrierToolType, // 屏障类
AskUserToolName: BarrierToolType, // 屏障类
CoderToolName: AgentToolType, // Agent 委托类
PentesterToolName: AgentToolType, // Agent 委托类
TerminalToolName: EnvironmentToolType, // 环境操作类
FileToolName: EnvironmentToolType, // 环境操作类
BrowserToolName: SearchNetworkToolType, // 网络搜索类
GoogleToolName: SearchNetworkToolType, // 网络搜索类
// ... 其他工具的映射
}
除了工具的注册,PentAGI 还维护了另外两个重要的辅助列表:allowedSummarizingToolsResult 和 allowedStoringInMemoryTools。前者指定哪些工具的执行结果需要被摘要压缩(如终端输出和浏览器页面,因为它们的输出可能非常大),后者指定哪些工具的结果需要自动存入长期记忆向量数据库(终端命令、文件操作、搜索和 Agent 执行的结果),从而实现自动化的知识沉淀。
// 文件位置: backend/pkg/tools/registry.go
// 允许摘要压缩结果的工具 —— 这些工具的输出可能非常大,需要压缩
var allowedSummarizingToolsResult = []string{
TerminalToolName, // 终端命令输出可能需要摘要
BrowserToolName, // 浏览器页面内容可能需要摘要
}
// 允许自动存入记忆的工具 —— 这些工具的执行结果对长期学习有价值
var allowedStoringInMemoryTools = []string{
TerminalToolName, // 终端的命令和输出
FileToolName, // 文件读写内容
SearchToolName, // 搜索请求和结果
GoogleToolName, // Google 搜索结果
TavilyToolName, // Tavily 搜索结果
SploitusToolName, // Sploitus 漏洞搜索结果
PentesterToolName, // 渗透测试结果
CoderToolName, // 代码执行结果
AdviceToolName, // 顾问建议
// ... 其他工具
}
这种设计将工具的「执行」与「知识管理」解耦——Agent 只需专注于使用工具完成任务,系统在后台自动完成结果压缩和知识入库,Agent 无需关心这些横切关注点。
工具调用让 Agent 能够「动手」,但一个更深层的问题随之而来:渗透测试往往持续数小时甚至数天,Agent 如何在长时间的任务执行中保持对目标环境的持续认知?如何在多个会话之间复用之前学到的经验?这正是 PentAGI 与其 AI 记忆系统需要解决的下一个关键问题。
渗透测试通常跨天、跨多轮对话。PentAGI 设计了完整的记忆体系来解决 AI 的「失忆」问题:
flowchart TB
subgraph 长期记忆
VS[(向量存储<br/>pgvector 嵌入)]
KB[知识库<br/>领域专业知识]
TK[工具知识<br/>使用模式与经验]
end
subgraph 工作记忆
CTX[当前上下文<br/>任务状态]
GOAL[活跃目标<br/>当前目标]
STATE[系统状态<br/>资源状态]
end
subgraph 情景记忆
PAST[历史操作<br/>命令记录]
RES[操作结果<br/>执行产出]
PATT[成功模式<br/>最佳实践]
end
CTX -->|查询| VS
VS -->|检索| CTX
GOAL -->|咨询| KB
KB -->|指导| GOAL
STATE -->|记录| PAST
PAST -->|学习| PATT
PATT -->|存储| VS
TK -->|通知| STATE
RES -->|更新| TK
VS -->|增强| KB
KB -->|索引| VS
style VS fill:#e1bee7,stroke:#7b1fa2,color:#000
style CTX fill:#bbdefb,stroke:#1565c0,color:#000
style PAST fill:#c8e6c9,stroke:#2e7d32,color:#000

图6:三层记忆系统架构
配合 Chain Summarization(链摘要系统),系统能在上下文窗口接近极限时,自动对历史对话进行智能摘要压缩,保留关键信息的同时控制 token 消耗。
有了记忆系统保障长周期认知的连续性,PentAGI 还需要解决另一个在实际运行中更棘手的问题:长时间自主执行时,Agent 会不会跑偏?会不会陷入死循环?会不会因为一次错误的工具调用就破坏整个测试流程?为了应对这些风险,PentAGI 构建了一套从硬限制到智能规划的四层监督体系。
PentAGI 设计了四层 Agent 监督机制,确保长时间自动测试的稳定性和结果质量:
flowchart TB
subgraph 第一层 工具调用硬限制
HARD[硬限制<br/>MAX_GENERAL=100<br/>MAX_LIMITED=20]
end
subgraph 第二层 Reflector 反思校验
REFLECT[Reflector<br/>3次失败自动介入<br/>引导正确工具调用]
end
subgraph 第三层 Execution Monitor 执行监控
MON[模式检测<br/>相同工具≥5次提醒<br/>进度分析<br/>替代策略推荐]
end
subgraph 第四层 Intelligent Task Planning 智能规划
PLAN[Planner<br/>自动分解3-7步骤<br/>上下文感知<br/>范围管控]
end
AGENT[Agent 执行] --> HARD
HARD -->|接近上限| REFLECT
REFLECT -->|异常模式| MON
MON -->|复杂任务| PLAN
PLAN -->|规划指导| AGENT
style HARD fill:#ffcdd2,stroke:#c62828,color:#000
style REFLECT fill:#fff3e0,stroke:#e65100,color:#000
style MON fill:#fff9c4,stroke:#f9a825,color:#000
style PLAN fill:#e8f5e9,stroke:#2e7d32,color:#000

图7:四层监督防护体系
要理解这些机制如何在代码层面协同工作,最直接的方式是看核心执行函数 performAgentChain。这个函数位于 pkg/providers/performer.go,是整个多 Agent 系统的「引擎」——它控制着 Agent 执行的迭代循环、工具调用分发、Reflector 介入和链摘要压缩。下面是其核心逻辑的注释版:
// 文件位置: backend/pkg/providers/performer.go
// Agent 链执行引擎 —— 控制 Agent 与 LLM 之间的迭代对话循环
// 每次迭代: Agent 发送消息 → LLM 响应(文本+工具调用) → 执行工具 → 处理结果
// performAgentChain 是 Agent 链的执行主循环
// 参数说明:
// chainID - 消息链的唯一标识,用于持久化和日志关联
// taskID/subtaskID - 当前任务/子任务,用于隔离执行上下文
// chain - 当前对话消息链(累积的 LLM 对话历史)
// executor - 工具执行器,根据 Agent 角色包装了权限信息
// summarizer - 链摘要器,在上下文接近 token 限制时自动压缩
func (fp *flowProvider) performAgentChain(
ctx context.Context,
optAgentType pconfig.ProviderOptionsType, // Agent 类型(pentester/coder/...)
chainID int64,
taskID, subtaskID *int64,
chain []llms.MessageContent, // 累积的对话链
executor tools.ContextToolsExecutor, // 工具执行器
summarizer csum.Summarizer, // 链摘要器
) error {
var (
wantToStop bool
// 构建执行监控器,用于检测重复工具调用和进度停滞
monitor = fp.buildMonitor()
// 重复调用检测器,追踪连续相同的工具调用
detector = &repeatingDetector{}
)
// 根据 Agent 类型决定最大工具调用次数
// 通用 Agent (Assistant/Pentester/Coder) 上限更高
// 受限 Agent (Searcher/Enricher/Memorist) 上限更低
var maxCallsLimit int
switch optAgentType {
case pconfig.OptionsTypeAssistant, pconfig.OptionsTypePrimaryAgent,
pconfig.OptionsTypePentester, pconfig.OptionsTypeCoder, pconfig.OptionsTypeInstaller:
// 通用 Agent: 默认 100 次工具调用
maxCallsLimit = maxGeneralAgentChainIterations // 常量: 100
default:
// 受限 Agent: 默认 20 次工具调用
maxCallsLimit = maxLimitedAgentChainIterations // 常量: 20
}
// ========== 主迭代循环 ==========
// 每次迭代: 调用 LLM → 处理响应 → 执行工具 → 追加结果到消息链
for iteration := 0; ; iteration++ {
// 硬限制: 超过最大迭代次数则报错
if iteration >= maxCallsLimit {
return errors.New("agent chain exceeded maximum iterations")
}
var result *callResult
// 当迭代次数接近上限时,自动触发 Reflector 引导 Agent 优雅终止
if iteration >= maxCallsLimit-maxAgentShutdownIterations {
// Reflector 告知 Agent 无法继续,要求其调用 done 工具
result = &callResult{
content: fmt.Sprintf(
"I can't continue this multi-turn chain because " +
"I'm too close to the AI agent iteration limit (%d).",
maxCallsLimit,
),
}
} else {
// 正常调用: 向 LLM 发送当前对话链,获取响应
result, err = fp.callWithRetries(ctx, optAgentType, chainID,
taskID, subtaskID, chain, executor, executionContext)
if err != nil {
return err
}
}
// 如果 LLM 没有返回任何工具调用(纯文本响应)
iflen(result.funcCalls) == 0 {
if optAgentType == pconfig.OptionsTypeAssistant {
// Assistant 的纯文本响应直接返回给用户
return fp.processAssistantResult(...)
} else {
// 其他 Agent: 通过 Reflector 分析 LLM 为什么没有做决策
// Reflector 会尝试引导 Agent 正确使用工具
result, err = fp.performReflector(...)
if err != nil {
return err
}
}
}
// 将 LLM 的响应(文本+工具调用)追加到对话链
msg := llms.MessageContent{Role: llms.ChatMessageTypeAI}
// 保留推理内容(如 Anthropic 的 extended thinking)
if result.content != "" || !result.thinking.IsEmpty() {
msg.Parts = append(msg.Parts,
llms.TextPartWithReasoning(result.content, result.thinking))
}
for _, toolCall := range result.funcCalls {
msg.Parts = append(msg.Parts, toolCall)
}
chain = append(chain, msg)
// ====== 遍历执行所有工具调用 ======
for idx, toolCall := range result.funcCalls {
funcName := toolCall.FunctionCall.Name
// 执行单个工具调用,monitor 和 detector 在其中生效
response, err := fp.execToolCall(
ctx, optAgentType, chainID, idx, result,
monitor, detector, executor, taskID, subtaskID, chain,
)
if toolTypeMapping[funcName] != tools.AgentToolType {
// 非 Agent 委托类工具的调用记录写入 Graphiti 知识图谱
fp.storeToolExecutionToGraphiti(...)
}
// 将工具执行结果作为 Tool 角色的消息追加到对话链
chain = append(chain, llms.MessageContent{
Role: llms.ChatMessageTypeTool,
Parts: []llms.ContentPart{
llms.ToolCallResponse{
ToolCallID: toolCall.ID,
Name: funcName,
Content: response,
},
},
})
// 如果 Agent 调用了屏障工具(done / ask),标记终止
if executor.IsBarrierFunction(funcName) {
wantToStop = true
}
}
if wantToStop {
returnnil// 正常结束 Agent 链
}
// 链摘要: 当消息链总长度接近 token 限制时,
// 自动对历史消息进行智能压缩,保留关键信息
if summarizer != nil {
chain, err = summarizer.SummarizeChain(ctx,
summarizerHandler, chain, fp.tcIDTemplate)
}
}
}
这个执行循环的设计体现了几个关键工程决策:
for 循环而不是递归调用,避免了大模型长时间运行时的栈溢出风险done 工具)→ Reflector 引导终止(接近上限)→ 硬限制报错(超过上限),确保系统即使在极端情况下也不会无限制运行这些机制对小参数模型(< 32B)尤其关键——测试表明在 Qwen3.5-27B-FP8 上启用执行监控和任务规划后,结果质量提升 2 倍,同时显著减少了死循环和冗余劳动。
说了这么多 Agent 的编排和治理,一个自然的追问是:这些 Agent 背后「大脑」到底从何而来?不同的大模型提供商各有千秋,有的擅长推理、有的响应更快、有的适合本地离线部署。PentAGI 的解决之道是打造一套插件式提供商架构,让用户可以根据场景自由选择和组合 LLM 后端。
PentAGI 支持 10 个 LLM 提供商的插件式接入,所有提供商统一实现 Provider 接口。这个接口定义在 pkg/providers/provider/provider.go 中,是整个系统 LLM 集成的核心契约。注意所有方法都接受 context.Context 用于超时和取消控制,而 CallWithTools 则是多 Agent 系统中调用频率最高的方法——因为每次 Agent 迭代都会通过它向 LLM 发送当前的对话链和可用工具列表:
// 文件位置: backend/pkg/providers/provider/provider.go
// Provider 接口 —— 所有 LLM 提供商必须实现此接口
// 这是 PentAGI 插件式 LLM 集成架构的核心抽象
type Provider interface {
// 基础信息
Type() ProviderType // 返回提供商类型: openai / anthropic / gemini / ...
Name() ProviderName // 返回实例名称,支持同一类型多个实例
Model(opt pconfig.ProviderOptionsType) string// 返回当前使用的模型名称
// 带提供商前缀的模型名称,用于 LLM API 调用和 Langfuse 日志记录
ModelWithPrefix(opt pconfig.ProviderOptionsType) string
// 从 LLM 返回的 usage 元数据中提取 token 消耗信息
GetUsage(info map[string]any) pconfig.CallUsage
// 核心调用方法
Call(ctx context.Context, opt pconfig.ProviderOptionsType,
prompt string) (string, error)
// ^ 简单文本调用:将 prompt 发送给 LLM,返回纯文本响应
CallEx(ctx context.Context, opt pconfig.ProviderOptionsType,
chain []llms.MessageContent,
streamCb streaming.Callback,
) (*llms.ContentResponse, error)
// ^ 多轮对话调用:发送完整的消息链,支持流式回调
CallWithTools(ctx context.Context, opt pconfig.ProviderOptionsType,
chain []llms.MessageContent,
tools []llms.Tool,
streamCb streaming.Callback,
) (*llms.ContentResponse, error)
// ^ 带工具调用的对话 —— 这是多 Agent 系统的核心调用路径
// Agent 向 LLM 发送对话历史和可用工具列表,
// LLM 返回文本响应 和/或 工具调用请求
// 配置访问
GetRawConfig() []byte // 原始配置(YAML 格式)
GetProviderConfig() *pconfig.ProviderConfig // 解析后的配置
// 定价信息和模型列表
GetPriceInfo(opt pconfig.ProviderOptionsType) *pconfig.PriceInfo
GetModels() pconfig.ModelsConfig
// 工具调用 ID 模板,每个提供商可能有不同的命名格式
GetToolCallIDTemplate(ctx context.Context, prompter templates.Prompter) (string, error)
}
提供商 | 接入方式 | 典型模型 |
|---|---|---|
OpenAI | REST API | GPT-4o / o3-mini |
Anthropic | REST API | Claude 4 Sonnet / Opus |
Google AI | REST API | Gemini 2.5 Pro |
AWS Bedrock | AWS SDK | Claude / Llama 部署 |
Ollama | 本地 HTTP | Llama 3 / Qwen 3.5 |
DeepSeek | REST API | DeepSeek-V3 / R1 |
GLM(智谱) | REST API | GLM-4 |
Kimi(月之暗面) | REST API | Moonshot |
Qwen(阿里云) | REST API | Qwen-Max |
Custom | OpenAI 兼容 | 任意兼容端点 |
此外还支持 OpenRouter、DeepInfra 等聚合器,以及国产模型本地部署(参见 vLLM + Qwen3.5-27B-FP8 部署指南)。
至此,Agent 能够思考(LLM)、能够行动(工具)、能够记忆(三层记忆)、能够自我纠偏(监督体系)。但还有一个关键问题悬而未决:当整个系统全速运转时,你怎么知道每一步发生了什么? 如果测试出了问题,如何回溯定位?这正是可观测性体系的职责所在。
PentAGI 构建了完整的两层可观测性:
每个工具调用和 Agent 执行都通过观测包装器(toolObservationWrapper、agentObservationWrapper、generationObservationWrapper)自动埋点,做到可追溯、可审计、可复盘。
七项核心技术拆解完毕,PentAGI 的工程全貌已经清晰。但技术从来不是目的——这些设计最终要回答的是:它在真实的安全测试场景中,到底解决了哪些传统方法无法解决的问题?下面我们从行业痛点出发,重新审视 PentAGI 的差异化价值。
行业痛点 | 传统做法的局限 | PentAGI 的差异化 |
|---|---|---|
门槛高 | 每种工具需记忆复杂 CLI 参数,串联多工具更需经验 | 自然语言驱动 + 多 Agent 编排,用户只需描述目标 |
质量不稳 | 同一目标不同测试员覆盖面和结论差异大 | 15 个专业化 Agent + 四层监督体系,标准化执行路径 |
失忆问题 | 长周期测试中对话上下文溢出,跨天测试需从头开始 | 三层记忆系统 + 链摘要,实现跨会话知识持续积累 |
难闭环 | 扫描即走,利用验证、整改推动断裂 | Flow/Task/SubTask 全生命周期管理 + 报告自动生成 |
难审计 | AI 执行过程黑盒,结论无法复核 | Langfuse + OTel 全链路追踪,每一步可回放可审计 |
terminal 工具在隔离的 Docker 沙箱中执行命令,通过 search 工具收集情报,通过 coder 工具编写自定义脚本——所有操作统一走工具调用契约,而非让 LLM 自由生成 Shell 命令。Searcher + Enricher 完成,漏洞利用由 Pentester 执行,知识沉淀由 Memorist 管理,最终由 Reporter 生成结构化报告。vxcontrol/kali-linux 镜像,内置 20+ 安全工具(nmap、metasploit、sqlmap 等),并支持自动根据任务类型选择不同的容器镜像。以上四大差异化能力——零门槛、标准化、安全隔离、全生命周期——共同回答了「PentAGI 为什么值得关注」。但理论终归要在实践中接受检验。接下来我们看看社区的真实评价、行业对比,以及它在技术先进性之外面临的客观争议。
PentAGI 由 vxcontrol 团队开发并于 GitHub 开源,是 Go 语言生态中最受关注的 AI 安全测试项目之一。项目在 GitHub 上拥有可观的 Star 数量,社区通过 Discord 和 Telegram 活跃交流。
技术定位:
对比业界的几类 AI 安全产品:
类型 | 代表项目 | 与 PentAGI 的差异 |
|---|---|---|
通用 Agent 框架 | OpenHands / AutoGPT | 缺乏安全领域专业工具和知识库,路径长、边界模糊 |
学术原型 | 多数 arXiv 论文项目 | 缺少 Web UI、持久化存储、企业治理能力 |
商业产品 | Pentera / XM Cyber | 价格高、闭源、不易定制;PentAGI 开源可控 |
脚本式自动化 | 传统 Bash/Python 脚本 | 无 AI 决策能力,无法适应动态场景 |
PentAGI 的核心竞争优势在于:用工程化手段解决了 AI 渗透测试「从实验室到生产」的所有关键问题——多 Agent 专业化分工保证质量,三层记忆解决长周期失忆,Docker 沙箱确保安全边界,Langfuse + OTel 实现全链路可观测。
待优化方向:
综合来看,PentAGI 已在技术完备性和工程化程度上走在了同类开源项目的前列,但「能做」和「做得好」之间仍有距离。从这些已知的争议和短板出发,我们不难看到 AI 渗透测试平台接下来的演进方向。
PentAGI 的架构设计为 AI 渗透测试平台的演进提供了有益参考,未来方向包括:
PentAGI 是 Go 语言 + AI 安全测试领域的一次深度实践。它没有停留在「大模型 + 命令行包装」的浅层阶段,而是从工程架构层面系统性地解决了多 Agent 编排、工具调用治理、长周期记忆和全链路可观测等核心问题。对于致力于将 AI 真正落地到渗透测试场景的团队来说,PentAGI 的架构思路和实现细节都值得仔细拆解和学习。
本文分享自 Ms08067安全实验室 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!