1. 为什么会有这个项目:AI审计员"言之无物"的尴尬
事情得从一次实际测试说起。我把自己写的一个小型Web服务扔给Claude,让它做一次安全审计。当时用的是通用对话模式,模型也确实很配合,一口气给我列了十几条"安全隐患",什么SQL注入风险、XSS风险、CSRF防护不足……乍一看头头是道,但仔细一读就发现不对劲:它说的那些问题,我的代码里根本不存在。
比如它指出某个接口存在SQL注入,理由是"用户输入未经参数化查询直接拼接到SQL语句中"。但我那个项目用的是ORM,压根没有手写SQL的地方。再往下看,它建议"使用HTTPS传输敏感数据",可作为本地开发环境,这本来就是一句正确的废话。整个审计报告充斥着"正确的废话+想象中的漏洞",真正有价值的内容几乎为零。
这个现象我后来想明白了:模型的知识库里确实装了海量安全知识,但它缺少三样东西——标准的工作流程、可落地的工具调用、以及严谨的证据链意识。它像一个刚毕业的安全专业学生,理论背得滚瓜烂熟,真到了现场却不知道第一步该干什么,看到什么都想往上套。
于是我萌生了一个想法:能不能把一套完整的安全审计方法论,包括流程、工具、判断标准、报告格式,全部封装成一个我们可以称之为"技能"的东西,让Agent在接到审计任务时,严格按照这套方法论去执行?这就是security-audit-skill项目的由来。
如果你也有类似的困惑——让大模型帮忙审代码、查依赖、找配置风险,但结果总是"大而空"——那这篇文章大概率能帮到你。我会从Skill的设计思路、文件结构、核心实现、实测效果几个方面,完整地复盘这个项目的搭建过程。
2. Skill到底是什么:给Agent的"工作方法包",不是插件
在动手之前,得先把"Skill"这个概念搞清楚。最近"skill"这个词在各种AI编程工具里出现的频率越来越高——Claude有Skills,Codex有AGENTS.md和自定义指令,OpenCode也支持类似机制,大家会搜索"skill和agent的区别"、"codex skill怎么写"、"好用的skill推荐"这类词。但很多人的理解是模糊的。
2.1 用"入职培训手册"来理解Skill
打个比方。一家公司来了个新员工(Agent),他学历很高(大模型的预训练知识),什么技术都懂一点。但真正让他独立干活之前,公司会怎么做?会给他发一份入职培训手册,里面写清楚了:你负责什么岗位、业务流程分几步、每一步用什么系统、遇到什么情况向谁汇报、交付物长什么样。
Skill就是这个"入职培训手册"。它不是一个独立的AI,不是一套需要单独部署的服务,更不是传统意义上的"插件"。插件通常是给开发环境加按钮、加界面的,而Skill是给Agent的思维和工作流预设。Agent本身还是那个Agent,但挂上不同的Skill,它面对同一类任务时就会用不同的方式工作。
所以回答"skill和agent的区别"就很简单:Agent是执行者,Skill是执行者脑子里的SOP(标准作业程序)。Agent可以同时掌握多个Skill,就像一名员工既能做安全审计,也能做代码Review,前提是他脑子里的SOP足够多、足够好。
2.2 Skill的四个组成部分
我翻了很多开源Skill项目的实现,也参考了官方文档,最终归纳出Skill的四个核心组成部分:
- 技能描述:说明这个技能是干什么的、在什么场景下触发、有哪些前置条件。通常以YAML头(frontmatter)的形式写在技能文件的顶部,Agent会先读这段描述,判断当前场景是否应该启用这个技能。
- 执行流程:把一项任务拆解成若干步骤,每一步做什么、产出什么、如何判断是否继续。这是整个技能的核心,直接决定Agent干活的质量。
- 配套脚本:当Agent需要执行一些确定性操作(比如扫描代码、查依赖库版本)时,不能靠它自己空想,必须调用真实的脚本和工具。Skill里会内置这些脚本。
- 输出模板:规定最终交付物的格式,比如审计报告应该包含哪些章节、漏洞评级怎么写、如何附上证据代码。
我做的security-audit-skill就是按照这四个维度来设计和组织的。只有全部四个维度都补齐了,Agent的执行质量才是稳定、可预期的。
3. 动手之前先拆解:安全审计流程的标准化设计
3.1 从OWASP和实际工作中提炼的审计主线
不同的人做安全审计思路不一样。有人偏重渗透测试,有人偏重代码审查,有人偏重合规检查。security-audit-skill这个项目聚焦的是"代码库与应用配置的安全审计",也就是开发侧的审计。所以我的流程主线参考了OWASP的应用安全验证标准(ASVS)和日常开发时做安全Review的常见步骤,最后收敛成四个阶段:
- 侦察与清点(Recon):先搞清楚项目是什么技术栈、有哪些模块、暴露了哪些入口点、有没有配置文件。
- 自动化扫描(Scan):用现成的工具扫密钥泄露、扫依赖漏洞、做静态代码分析,把机器能干的事儿先干完。
- 人工验证(Verify):自动扫描会有大量误报,这一步要逐条看自动扫描的结果,结合代码上下文判断真假,并确认漏洞是否真的可被利用。
- 报告输出(Report):把确认的风险按照严重程度分级,附上位置、证据、修复建议。
这四个阶段里,Agent最容易出问题的就是第1步和第3步。没有明确指令的时候,它经常跳过侦察直接开喷,也经常把扫描工具的原样输出直接当成结论,不做任何验证。Skill要做的就是把这些坑预先填上。
3.2 每条审计结论必须包含四要素
在实际写提示词和规则之前,我先定下了一条铁律:任何一条审计结论,必须包含位置、证据、原因、建议四个要素。
- 位置:哪个文件、哪一行、哪个配置项。
- 证据:代码片段、配置原文、工具输出,必须是可以回溯的。
- 原因:为什么这里是风险,攻击者能利用它做什么,参考CWE编号也行。
- 建议:具体的修复方案,最好是能直接改掉的代码或配置。
这条铁律后来被证明是整个项目里最关键的决策。没有这条约束,Agent会自然而然地滑向"泛泛而谈"。有了这条约束,它的每一条结论都必须落到具体文件和代码上,那些"想象出来的漏洞"就少了很多。
3.3 设计原则:可复用、可解释、可追溯
整个技能包还有三个设计原则,是我在写代码和文档之前就想清楚的:
可复用:技能不能只对单一项目有效,它需要能在不同类型的代码库上跑起来。所以我尽量让脚本用通用方式去识别技术栈,而不是写死某个框架。
可解释:Agent每一步为什么要做这个检查、用了什么规则,需要能被用户理解。这样审计报告出来,用户能判断哪些结论可信,哪些需要自己再确认。
可追溯:任何一条审计结论都能追溯到原始数据源。Agent必须在报告中给出文件路径和行号,脚本扫描的结果也要保留原始日志,而不是只给出一个结论性的列表。
这三点对齐之后,我才开始动手写目录结构和代码。
4. security-audit-skill的文件级实现:从SKILL.md到审计脚本
4.1 目录结构与设计逻辑
一个Skill通常是一个目录,我把它放在项目的skills/security-audit-skill/目录下。完整结构如下:
security-audit-skill/ ├── SKILL.md ├── scripts/ │ ├── scan_secrets.py │ ├── audit_dependencies.py │ └── detect_tech_stack.py ├── rules/ │ ├── secret_patterns.yaml │ ├── high_risk_config.yaml │ └── insecure_code_patterns.yaml ├── templates/ │ ├── security_report_template.md │ └── issue_item_template.md └── examples/ └── sample_report.md这个结构是经过几次迭代才稳定下来的。一开始我图省事,把所有内容全塞进一个SKILL.md里,结果SKILL.md变得又长又乱,Agent读的时候抓不住重点。后来我参考了几个人气较高的Skill项目的布局方式,把规则和模板拆到独立文件里,SKILL.md只保留流程和决策逻辑,效果好很多。
4.2 SKILL.md:技能的入口文档
SKILL.md是这个技能包的入口文件。它的开头是一段YAML frontmatter,给Agent看的技能摘要:
--- name: security-audit-skill description: 对代码库进行全面的安全审计,识别代码漏洞、密钥泄露、不安全依赖和高风险配置,并输出带有证据链的审计报告。 when_to_use: 用户要求做安全审计、代码安全检查、漏洞扫描、合规审查,或要求review代码中是否存在安全隐患时。 ---frontmatter之后是正文。正文的写法很关键,我采用的是"先总后分"的结构,先告诉Agent这个技能的工作流程是什么,再对每一步做详细说明。SKILL.md中最核心的一段是这样写的:
执行本技能时,必须严格按照以下五个步骤进行,每个步骤完成后再进入下一步。禁止跳步,禁止在不了解项目结构的情况下直接开始扫描或下结论。
侦察:先阅读项目根目录下的README、package.json、requirements.txt、go.mod等文件,确认技术栈和项目结构;列出源代码目录、配置文件目录、测试目录的位置。
清点:识别项目的入口点、外部输入来源、认证机制、数据存储方式,记录在临时工作笔记中。
自动扫描:在确认项目结构后,依次调用
scripts/detect_tech_stack.py、scripts/scan_secrets.py、scripts/audit_dependencies.py对项目进行扫描。每执行完一个脚本,先记录输出,再继续下一个。筛选验证:对自动扫描的每一条结果,打开对应的源码文件,检查上下文,判断是否为真实风险。筛选原则是:宁缺毋滥,只有能确认的才算数。无法确认的标记为"需人工复核"。
报告输出:基于
templates/security_report_template.md生成审计报告。每条发现必须包含位置、证据、原因、建议四要素,缺少任一要素的发现视为无效。
这套措辞是我反复打磨过的。重点在于"禁止跳步""每执行完一个脚本先记录输出""打开对应的源码文件检查上下文"这类指令。这些指令是让Agent从"脑补式审计"转向"证据式审计"的关键。没有这些硬性约束,它很容易跳过侦察直接给出结论。
4.3 三个核心脚本的角色分工
Skill里脚本不在多,而在每一段脚本都要能真正确地解决一个问题。目前我保留了三个脚本,它们分别负责技术栈识别、密钥扫描、依赖审计。
detect_tech_stack.py负责识别项目技术栈。这个脚本不复杂,就是读取项目根目录下的依赖清单文件,然后返回一个结构化的技术栈摘要:
#!/usr/bin/env python3 """Detect the tech stack of a project for security audit pre-check.""" import json import os import sys from collections import defaultdict def detect_tech_stack(project_root: str) -> dict: """Return a dict describing language, framework and package manager.""" result = { "project_root": project_root, "language": None, "package_managers": [], "entry_files": [], "config_files": [], } markers = { "python": ["requirements.txt", "pyproject.toml", "Pipfile"], "node": ["package.json", "pnpm-lock.yaml", "yarn.lock"], "go": ["go.mod"], "java": ["pom.xml", "build.gradle"], "rust": ["Cargo.toml"], "ruby": ["Gemfile"], } for lang, files in markers.items(): for f in files: p = os.path.join(project_root, f) if os.path.isfile(p): if lang not in result["language"]: result["language"] = lang result["package_managers"].append(f) break # detect entry files (heuristic) for entry in ["main.py", "app.py", "index.js", "main.go", "manage.py"]: p = os.path.join(project_root, entry) if os.path.isfile(p): result["entry_files"].append(entry) # read config files that often carry security-relevant settings for cfg in [".env", "config.yaml", "config.yml", "config.json", "application.yml", "settings.py"]: p = os.path.join(project_root, cfg) if os.path.isfile(p): result["config_files"].append(cfg) return result if __name__ == "__main__": root = sys.argv[1] if len(sys.argv) > 1 else "." print(json.dumps(detect_tech_stack(root), indent=2, ensure_ascii=False))scan_secrets.py是重点。它读取rules/secret_patterns.yaml里的正则规则,在项目中扫描疑似密钥的内容。最开始时我打算自己写一堆正则去匹配各种密钥格式,但后来发现这件事情没有想象中简单——AWS Access Key、GitHub Token、Stripe Key、私钥等等,每种的格式都不一样,写正则不仅要准确还要控制误报。规则文件我维护了一组典型的密钥格式,包括AWS AKIA系列、OpenSSH私钥、JWT、各类API Key前缀等,同时加了一个排除机制,避免命中测试文件或样本数据。
#!/usr/bin/env python3 """Scan for potential secrets and hardcoded credentials in source files.""" import json import os import re import sys import yaml def load_patterns(rules_path: str) -> list: with open(rules_path, "r", encoding="utf-8") as f: data = yaml.safe_load(f) patterns = [] for item in data.get("secret_patterns", []): patterns.append({ "name": item["name"], "regex": re.compile(item["pattern"]), "severity": item.get("severity", "high"), }) return patterns def should_skip(path: str) -> bool: skip_dirs = {".git", "node_modules", "venv", ".venv", "__pycache__", "dist", "build", ".next", ".idea", ".vscode"} parts = path.split(os.sep) return any(d in parts for d in skip_dirs) def scan_directory(project_root: str, patterns: list) -> list: findings = [] for dirpath, dirnames, filenames in os.walk(project_root): dirnames[:] = [d for d in dirnames if d not in {".git", "node_modules", "venv", ".venv", "__pycache__", "dist", "build", ".next", ".idea", ".vscode"}] for filename in filenames: filepath = os.path.join(dirpath, filename) if should_skip(filepath): continue try: with open(filepath, "r", encoding="utf-8", errors="ignore") as f: content = f.read() except (OSError, UnicodeDecodeError): continue lineno = 0 for line in content.splitlines(): lineno += 1 for pat in patterns: if pat["regex"].search(line): findings.append({ "file": filepath, "line": lineno, "type": pat["name"], "severity": pat["severity"], "snippet": line.strip()[:200], }) # deduplicate: if multiple patterns match same file+line+type, keep the first seen = set() unique = [] for f in findings: key = (f["file"], f["line"], f["type"]) if key not in seen: seen.add(key) unique.append(f) return unique if __name__ == "__main__": root = sys.argv[1] if len(sys.argv) > 1 else "." rules_path = os.path.join(os.path.dirname(__file__), "..", "rules", "secret_patterns.yaml") pats = load_patterns(rules_path) results = scan_directory(root, pats) print(json.dumps(results, indent=2, ensure_ascii=False))audit_dependencies.py则更简单,它不自己去连漏洞数据库(这需要网络请求且容易踩坑),而是调用项目自带的依赖检查能力。比如Node项目跑npm audit --json,Python项目跑pip-audit --format json。脚本只负责把结果转成标准格式,方便Agent读取。这个设计思路是:脚本不是越全能越好,能调用生态里成熟的工具,就不要自己重复造轮子。
5. 让Agent真正"会审计":规则文件与提示词的设计细节
有了流程和脚本,Skill只算完成了一半。剩下的一半,是怎么让Agent在执行过程中保持"审计思维"。这部分藏在规则文件、报告模板和SKILL.md的说明里。
5.1 规则文件:把审计经验翻译成可检索的规律
secret_patterns.yaml这个规则文件,我维护了一份通用密钥格式清单。它的价值在于:Agent在扫描时不再完全依赖脚本,当脚本没有命中时,它还能参考规则文件里的模式去人工检查某些可疑的硬编码值。
secret_patterns: - name: AWS Access Key ID pattern: "AKIA[0-9A-Z]{16}" severity: high - name: GitHub Personal Access Token pattern: "ghp_[A-Za-z0-9]{36}" severity: high - name: OpenAI API Key pattern: "sk-[A-Za-z0-9]{48}" severity: high - name: Google API Key pattern: "AIza[0-9A-Za-z_-]{35}" severity: high - name: Slack Token pattern: "xox[baprs]-[0-9A-Za-z-]{10,}" severity: high - name: Private Key pattern: "-----BEGIN (RSA|EC|OPENSSH|DSA) PRIVATE KEY-----" severity: high - name: JWT Token pattern: "eyJ[A-Za-z0-9_-]{10,}\\.[A-Za-z0-9_-]{10,}\\.[A-Za-z0-9_-]{10,}" severity: medium - name: Stripe Secret Key pattern: "sk_live_[0-9A-Za-z]{24,}" severity: high这里有一个很实际的教训:正则误报是免不了的。比如sk-开头的字符串,可能只是某个测试样本数据,或者某个示例代码里的假密钥。所以我给Skill定了一条规则:凡是扫描命中的结果,Agent必须打开源文件,确认该处是真实的生产密钥,而不是测试样本或文档示例,才能写进报告。这条规则把误报率降了至少一半。
insecure_code_patterns.yaml则是给Agent做人工代码审阅时用的检查清单。它不匹配具体代码,而是提供一组需要重点留意的模式:
insecure_code_patterns: - category: SQL注入 keywords: ["execute(", "executemany(", "raw_query", "db.query"] description: 当用户输入直接拼接到SQL语句时,存在SQL注入风险。应使用参数化查询或ORM。 - category: 命令注入 keywords: ["os.system(", "subprocess.Popen(", "subprocess.call(", "exec(", "eval("] description: 动态拼接shell命令时,若输入来自用户,可能导致命令注入。应使用参数列表方式调用,避免shell=True。 - category: 路径遍历 keywords: ["open(", "read_text(", "send_file("] description: 使用用户可控的文件路径时,需检查是否存在路径遍历风险,应使用安全的路径拼接并校验路径。 - category: 反序列化 keywords: ["pickle.loads(", "yaml.load(", "jsonpickle.decode("] description: 对不可信数据执行反序列化时,可能触发反序列化攻击。应使用安全的序列化格式并做输入校验。 - category: 不安全的随机数 keywords: ["random.random(", "random.randint(", "Math.random("] description: 涉及安全场景(验证码、令牌、密钥生成)时,不得使用非加密安全的随机数生成器。应使用secrets模块或crypto库。 - category: 硬编码凭据 keywords: ["password=", "passwd", "api_key"] description: 代码中出现硬编码口令或密钥时,应改为环境变量或秘密管理服务。有了这个清单,Agent在打开源码逐个文件审阅的时候就有了抓手,不会漫无目的地瞎看。虽然它不可能像专门工具那样全面,但在人工审阅这个环节,给Agent一个明确的检查点列表,比让它自由发挥要靠谱得多。
5.2 报告模板:把结论强制装进"四要素"框架里
报告模板是另一个关键文件。我用一个Markdown模板把审计发现的标准格式固定下来,Agent在写报告时只能照这个格式来填充。
templates/security_report_template.md的骨架是这样:
# 安全审计报告 - 审计目标:{项目名称/地址} - 审计时间:{时间} - 审计方式:security-audit-skill 自动扫描 + Agent代码审阅 - 技术栈:{由detect_tech_stack脚本输出} ## 审计范围 {列出本次审计覆盖的目录和文件,以及未覆盖的部分及原因} ## 发现总览 | 严重级别 | 数量 | 说明 | |---------|------|------| | 严重 | {n} | 可直接被外部利用且影响面大 | | 高危 | {n} | 存在明确可利用路径,或影响明显 | | 中危 | {n} | 需要特定条件才能利用 | | 低危 | {n} | 代码质量问题,安全影响有限 | ## 严重/高危发现详情 ### {漏洞名称}(严重/高危) - **位置**:{文件路径:行号} - **证据**: ```{language} {代码片段}- 原因:{为什么这是风险,攻击者如何利用}
- 建议:{具体修复方案}
- 参考:{CWE编号或其他参考}
中危发现详情
...
低危与改进建议
...
需人工复核的发现
{Agent无法确认,需要人工进一步验证的项目}
这个模板最重要的设计是"需人工复核的发现"这个章节。它给了Agent一条退出路径:遇到不确定的情况,不要硬着头皮下结论,而是诚实地说"这个我确认不了"。这一招解决了Agent"过度自信"的老毛病。我宁愿它多标几个"需人工复核",也不想看到它把没有问题的代码硬说成漏洞。 ### 5.3 提示词里刻意埋入的"审计思维" 除了文件和脚本之外,我在SKILL.md中还特意写了一段"审计原则",作为Agent执行整个流程时的思想纲领: > 审计原则 > > - 不要假设,要验证:任何一条判断都必须有代码或配置作为支撑。没有证据的结论不要写进报告。 > - 从攻击者视角看问题:思考"如果我是攻击者,我能利用这段代码做什么",而不是从开发者视角想"这段代码本来应该做什么"。 > - 危害和可利用性要区分:一个漏洞即使存在,如果无法被外部输入触达,其危害级别也应调低。报告中的严重级别必须结合项目的实际暴露面来定。 > - 代码审查要逐文件进行,避免只盯着脚本扫描结果不放。脚本没有扫到的文件,不代表没有风险。 这几条原则确实起了作用。后来我拿同一个项目,分别用"普通Prompt+要求审计"和"加载security-audit-skill"各测了一遍,前者的报告还是老样子——空泛、套话多、没有可执行性;后者的报告至少在形式上完全变了个样——有文件路径、有代码证据、有明确的修复建议,而且会区分"确认存在"和"需人工复核"。这一步的转变,让我确信这个项目方向是对的。 ## 6. 实测记录:它能查出什么,又会漏掉什么 ### 6.1 测试项目与执行情况 为了验证Skill的真实效果,我准备了一个专门用于测试的示例项目。它故意包含以下几类问题:一个硬编码的数据库口令、一个把用户输入直接拼到SQL里的接口、一个调用 `os.system` 执行shell命令的工具函数、一个明显过时的第三方依赖、以及一个写得还算安全的登录接口(用于测试误报率)。同时还有一个 `.env.example` 文件,里面写着 `API_KEY=your-api-key-here`——这个是用来考验Agent是否能分辨占位符和真实密钥的。 实际执行过程中,Agent先是调用了 `detect_tech_stack.py`,确认这是一个Python项目,技术栈是Flask + SQLAlchemy;然后调用了 `scan_secrets.py`,命中了 `.env.example` 和 `config.py` 里的两处硬编码口令;再用 `audit_dependencies.py` 跑了一遍 `pip-audit`,发现一个已知有CVE的依赖版本;最后开始人工审阅环节,逐文件打开源码做检查。整体执行链路是完整的,没有跳步。 ### 6.2 做得好的地方 让我比较满意的是它对"占位符密钥"的处理。`.env.example` 里那个 `your-api-key-here` 其实被 `scan_secrets.py` 的正则规则误报了——虽然没有明确前缀,但我有一条规则会匹配 `your-api-key-here` 格式的占位内容(其实这是个设计失误,但我没有刻意修掉这个误报)。在生成报告时,Agent打开 `.env.example` 文件看了一眼,发现这个值是占位符,不是真实密钥,于是没有把它写进"严重风险",而是放进了"需人工复核的发现"里。这说明"打开源文件确认上下文"这一步指令真的生效了。 它对那个硬编码数据库口令的判断也很准确。`config.py` 里有一行 `MYSQL_PASSWORD = "root123456"` 。Agent不仅指出了这是硬编码凭据,还顺着代码找到这个 `config.py` 是在生产环境启动时也会被加载的模块,所以判定为严重级别。证据链完整,修复建议也给得具体——改用环境变量,并给出了修改前后的代码对比。 ### 6.3 没做好的地方 当然也有不少失败场景。最突出的是,在人工审阅环节,Agent打开几个业务代码文件之后,出现了一定程度的"疲态"——它对前面的文件检查得很仔细,但越到后面,检查得越草率,经常跳到下一个文件时不再逐行看代码,而是直接根据文件名和函数名推测有没有风险。有一次它面对一个名为 `utils.py` 的文件,扫了一眼函数名就直接判断"此文件无重大安全风险",但实际上那个文件里就藏着命令注入的函数调用。后来我加了强制指令,要求"每个文件必须至少读取全部函数定义和关键调用点的上下文,不得仅凭函数名判断",情况稍有好转。 另一个问题是严重级别判断偶尔失准。它会把一个需要本地权限才能触发的配置缺陷标成"严重",又把一个理论上可被未授权用户直接访问的接口漏洞标成"中危"。虽然报告中给出了理由,但从安全专业视角看,级别调整的空间很大。这也说明:**Skill能把Agent的审计执行质量提升到一个稳定线,但不会让它直接变成资深安全专家**。最终的级别判断还是需要人来复核。 这些实测结果让我对Skill的定位更清晰了:它不是要取代人工审计,而是要把审计工作中最费时、最容易遗漏的机械性部分(密钥扫描、依赖检查、逐文件过代码找可疑模式)自动化、标准化,把人的精力留给真正需要判断力的地方。 ## 7. 踩坑记录与后续规划 ### 7.1 最值得记录的三个坑 第一个坑是关于正则扫描的误报。最初版本的密钥扫描脚本没有任何排除机制,结果把 `tests/` 目录和 `docs/` 目录里的示例密钥全报了出来。后来我在脚本里加上了对测试目录和文档目录的单独处理:这些目录里出现的疑似密钥默认标记为"低危"或"需人工复核",而不是直接当"高危"处理。 第二个坑是SKILL.md写得太长。第一版SKILL.md直接沿用了我写技术方案的习惯,事无巨细地列出了几十条规则。结果Agent执行时反而"记不住重点",对越靠后的规则执行得越不到位。后来我大幅精简SKILL.md,把每条规则都压缩成短句,把细节挪到规则文件和模板里。精简之后,Agent对核心流程的执行率反而更高了。写Skill和写代码一样,少即是多。 第三个坑是"脚本报错后Agent会试图脑补结果"。有一次 `pip-audit` 因为网络问题没有跑成功,Agent竟然直接跳过了依赖审计这一步,然后在报告里"根据知识判断"写上了几个依赖存在潜在风险。这种行为是我绝对不想要的——它等于绕过了工具链,回到了"脑补审计"的老路。后来我在SKILL.md里加了一条硬性规定:**任何工具执行失败时,必须在报告里明确标注"扫描失败"及失败原因,禁止用模型知识填补扫描结果**。这条规定非常重要,大家自己写Skill的时候建议直接抄过去。 ### 7.2 后续扩展方向 这个项目目前还在持续迭代。计划中的扩展方向有三个: 一是把静态分析的规则库做得更丰富。目前 `insecure_code_patterns.yaml` 只覆盖了Python和JavaScript的一些常见模式,后续打算补充Go、Java、Ruby等语言的高频风险模式,并支持按语言加载不同的规则集。 二是接入更多外部审计工具,比如gitleaks、bandit、semgrep,让脚本层的能力更全面。工具调用设计上,`audit_dependencies.py` 已经是这个思路,后续会把更多工具以同样的方式集成进来。 三是做一个"审计报告对比"功能。同一份代码,用不同配置的Skill跑两遍,对比两次报告的差异,用来帮助用户理解Skill中哪些规则或提示词起了作用。这对我来说也是优化Skill的一种手段。 回到最初的那个尴尬场景:让AI做安全审计,它只会讲一堆正确的废话。现在再回头看这个问题,我的答案其实已经很简单——不是模型不行,而是它缺少一套可执行的"工作方法"。把方法论沉淀成Skill,让Agent按标准流程调工具、看代码、找证据、写报告,它的输出质量就能出现明显的跳跃。 我在实际使用这个Skill时的最大感受是:**不要把Skill当成一个能解决所有问题的黑盒,而要把自己当成带徒弟的老师傅,把你想让Agent做的每一步都掰开揉碎写清楚**。你写得越细,Agent执行得越稳;你写得越笼统,它就越容易用漂亮的空话把你糊弄过去。这个道理,和带真人新人的时候一模一样。 如果你也在折腾Agent技能,建议先从一个高频、重复、流程相对固定的任务切入,把流程拆到最细,配上真实可用的脚本和模板,再反复用实际案例去打磨提示词。等第一个Skill跑顺了,你会发现后面做第二个、第三个就轻车熟路了。