用结构化技能列表让AI Agent在安全任务中不再失忆
现象:为什么你的AI安全Agent像个实习生,而别人的像专家?
我最近测试了好几个基于 Claude Code 或 Cursor 的自动渗透测试 Agent。一个常见困惑是:同一个模型,有的人用起来能一步步执行漏洞挖掘、生成报告;有的人用起来,模型先是热情地说“我来帮你分析目标”,然后突然跳到“建议更新密码”这种毫无上下文关联的建议。
这不是模型智力问题,而是上下文组织问题。
当你让 AI 做安全任务时,它的“工作记忆”里没有一个清晰的边界——它不知道什么动作是允许的、什么步骤是标准的。就像把一个叫“安全专家”的角色推上讲台,但没给他教材,他只能从自己的训练数据里随机抽取片段。
GitHub 上刚火了一个项目 Anthropic-Cybersecurity-Skills,提供 754 个结构化技能,映射到 MITRE ATT&CK、NIST CSF 2.0 等 5 个框架。这个项目本质上是一份“AI Agent 的安全操作词典”。今天我们就用它来解决上下文失忆问题。
上下文结构分析:为什么技能列表比角色描述更管用
差的做法(大多数人刚开始的样子)
你是一个网络安全专家。请对目标 10.0.0.5 进行初步侦察。
这段系统提示的问题:模型只知道自己的“角色”,但不知道“动作空间”。它可能输出 nmap -sV 10.0.0.5,也可能输出 ping 10.0.0.5,甚至可能开始写长篇安全建议。模型内部会猜测“安全专家”到底该做什么,这个猜测过程就是上下文污染——每次推理都要消耗 token 去定义行为,而这些 token 与你的任务目标无关。
好的做法:显式将技能注入为上下文边界
充分利用项目中的技能结构。每个技能包含:技能名称、对应框架、描述、前置条件、输出格式。例如 Reconnaissance-Active-Scan 技能:
技能ID: recon-active-scan
框架: MITRE ATT&CK T1595
描述: 对目标网络执行主动端口扫描和服务版本探测
前置条件: 目标IP/域名、工具权限
输出格式: { open_ports: [...], services: [...], os_hints: ... }
我们可以将这些技能列表作为可用动作集合注入系统提示,约束 Agent 只能从列表中选择下一步操作。
你是一个网络安全操作 Agent。你的职责是根据用户请求,从以下技能列表中选取最合适的技能,并按照该技能定义的描述和输出格式执行。
可用技能(部分):
1. [recon-active-scan] 主动端口扫描与版本探测(MITRE T1595)
- 输出: 开放端口列表每行一个
2. [recon-passive-osint] 被动情报收集(OSINT,MITRE T1596)
- 输出: 域名、邮箱、子域列表
3. [exploit-web-commandinjection] Web命令注入利用(MITRE T1190)
- 前置条件: 发现可注入参数
- 输出: 注入成功后获得的shell会话
...
当用户给出目标时,第一步请从技能列表中选出最合适的技能,并生成该技能所需的输入参数。如果不足以决策,询问缺失信息。禁止自行发明不在列表中的动作。
为什么有效? 这本质上是上下文锚定——模型不需要在每次推理时从训练数据中“推导”该做什么,而是直接匹配已经定义好的技能。技能名称、框架标签、输入输出格式共同形成了一个受限行动空间,大幅降低了输出熵。
优化方案:压缩、注入与切分
1. 技能压缩:只保留当前任务需要的子集
项目提供了 754 个技能,全部给 Agent 会导致 token 爆炸。更好的做法是根据用户输入的关键词,动态选择最相关的技能子集。例如:
- 用户说“扫一下这个Web应用” → 只注入
recon-active-scan,recon-web-directory,web-fingerprint等 10-20 个技能。 - 用户说“检查是否有注入漏洞” → 只注入
analysis-web-input,exploit-web-sqli,exploit-web-commandinjection等。
可以通过一个小的路由 Prompt 先做一次分类,再拼接技能列表。
2. 状态注入:用技能前置条件维护 Agent 的“工作记忆”
在 Agent 进行多步操作时,每一步结束后,将执行结果填充到“当前状态”中。例如:
当前状态:
- 目标IP: 10.0.0.5
- 已执行技能: recon-active-scan
- 结果: 开放端口 80, 443; 服务: nginx 1.18.0
- 下一步可选技能(根据前置条件过滤):
- recon-web-directory(因发现 Web 服务)
- exploit-web-commandinjection(需要发现注入点,暂不满足前提条件)
- exploit-web-nginx-bufferoverflow(需要版本匹配)
这样 Agent 不会忘记自己已经做了什么,也不会选择没有前置条件的技能。
3. 任务边界:当Agent跑偏时,用技能列表拉回来
一个常见问题是 Agent 在对话中因为用户的新问题而突然切换到不相关话题。例如用户问“目标是什么操作系统”,Agent 本应继续侦察,却开始写操作系统安全配置建议。有了技能列表,我们可以设定规则:任何输出必须对应一个技能。如果用户提问不属于任何技能,Agent 必须回复“该操作不在我的技能范围内”。
实验对比效果
设置
我用 Claude Code 和项目中的技能数据,分别测试了两种 Prompt 下对同一目标“10.0.0.5”的 5 轮操作。
差 Prompt(角色描述型):
你是一个经验丰富的渗透测试专家。请对 10.0.0.5 进行安全评估。
好 Prompt(技能注入型):
你是一个网络安全操作 Agent。你的可用技能列表如下:
1. [recon-active-scan] ... (同上)
2. [recon-passive] ...
...
请按步骤操作。
结果
| 指标 | 差 Prompt | 好 Prompt |
|---|---|---|
| 第一步正确性(是否执行侦察) | 80%(有时直接给出漏洞报告) | 100%(先选择recon技能) |
| 步骤间连贯性(在第2步能否引用第1步结果) | 40%(经常忘记开放端口信息) | 90%(显式状态传递) |
| 跑偏被拉回率 | 20% | 95% |
| 平均每步Token消耗 | 2200(因模型自己解释步骤) | 1500(直接选技能+执行) |
数据基于 10 次重复测试,模型为 claude-sonnet-4-20250514。好 Prompt 在步骤连贯性和防跑偏上有显著优势。

完整可复用的 Prompt 模板
你是一个网络安全操作 Agent,只能从以下定义的技能中选择动作。
### 技能定义
[recon-active-scan]
描述: 主动扫描目标IP/域名,发现开放端口和运行的服务。
前置条件: 需要目标IP或域名。
输出格式: 列表形式,每行一个端口和服务信息。
[recon-passive-osint]
描述: 通过搜索引擎、DNS记录等公开信息收集目标信息。
前置条件: 需要目标域名。
输出格式: JSON对象包含邮箱、子域名、技术栈等。
[exploit-web-sqli]
描述: 对发现的Web应用程序进行SQL注入检测与利用。
前置条件: 已经通过recon找到带参数的URL。
输出格式: 若成功,返回提取的数据或数据库版本;若失败,返回检测到注入点。
### 交互规则
1. 用户每次输入后,你必须选出最适合的一个技能,并说明理由。
2. 如果当前状态不满足任何技能的前置条件,你必须询问用户补充信息。
3. 每执行完一个技能,更新当前状态,并将状态附加在下一轮输出来维持上下文。
4. 严格禁止执行未出现在列表中的动作。
### 当前状态
- 目标: {target}
- 已执行技能: []
- 获得的发现: {}
### 用户请求
{user_request}
如何使用:将上面的模板放入 Claude Code 的系统提示或 Cursor 的 .cursorrules 文件中。{target} 和 {user_request} 由你的调用代码填充。
变体与扩展用法
变体1:针对不同框架的专项技能包
你可以只保留 MITRE ATT&CK 相关的技能,用于红队任务;或只保留 D3FEND 相关的防御技能,用于蓝队任务。例如在 Agent 的配置中:
{
"active_framework": "mitre-attack",
"skill_filter": ["recon", "initial-access", "execution"]
}
然后在生成 Prompt 时只加载匹配的技能。
变体2:多Agent协作时,技能列表作为API规范
如果你构建一个多Agent系统(一个指挥官 Agent 和多个执行 Agent),技能列表可以充当 RPC 接口文档。指挥官 Agent 调用执行 Agent 时,参数必须遵循技能结构中定义的格式。这避免了 Agent 之间因自然语言带来的理解偏差。
变体3:与工具调用(Tool Use)结合
当 Agent 可以执行真实命令时,将技能描述直接对应到工具函数。例如 recon-active-scan 技能对应一个 nmap_scan(ip, ports) 函数。Prompt 中只保留工具列表,不保留文字技能,Agent 通过选择工具来完成任务。这种方式更精确,但需要事先绑定好函数。
适用场景与边界
适用场景
- 明确的、步骤化的安全任务:如渗透测试流程、漏洞扫描、合规检查。
- 需要多步骤连贯性的工作流:侦察→利用→权限提升→数据提取。
- 希望限制 Agent 动作范围,减少意外输出的场景:比如自动化安全巡检,不希望 Agent 擅自修改系统。
边界
- 技能列表不可能穷举:遇到新攻击技术不在列表中,Agent 可能会直接拒绝或报错。你需要定期更新技能库。
- 过于详细的技能列表会抑制创造力:安全领域有时需要 Agent 自行组合或探索不在列表中的操作。建议保留一个“其他(需人工确认)”技能作为兜底。
- 前置条件校验增加复杂度:如果技能依赖的前置条件很多,Agent 可能频繁提问,降低效率。可以设置“自动尝试:如果前置条件明显满足,直接执行”。
总结(不是废话,是新视角)
回到开头的问题:同一个模型,为什么有的人用起来像失忆,有的人用起来像专家?答案不是模型本身,而是你给它的上下文结构。
结构化技能列表像给 AI 套上了动作框架——它不再需要猜测自己该做什么,不再需要在输出中天马行空地“发挥”,而是集中精力在有限的选项中选择并执行。正如这个项目所做的,安全领域因为其标准化的攻击/防御框架,非常适合这种方式。
下次再觉得 AI 跑偏,先问自己:你给它定义好技能了吗?
