devival/vuln-agent-eval
GitHub: devival/vuln-agent-eval
一套用于衡量 LLM Agent 在 Solidity 智能合约漏洞发现任务上精确率、召回率与成本表现的评估基准工具。
Stars: 0 | Forks: 0
# vuln-agent-eval
**一个评估套件,用于衡量自主 LLM agent 在 Solidity 智能合约中发现已知漏洞类别的表现** —— 它基于带有真实标签的语料库进行评分,并在精确率、召回率、误报率以及每份合约的成本上,与静态分析和单次提示基线进行比较。
它旨在回答的设计问题是:*多步 agent 结构究竟在何时能真正胜过单次提示,其成本又是多少?* 该 agent 的发现阶段与单次基线完全相同 —— 唯一的区别在于增加了一个对抗性的 **verify(验证)** 阶段 —— 因此分数的任何变化都仅归因于该阶段,而非其他因素。
**核心发现(40 份合约试点):** 该验证阶段的价值是**依赖于模型的** —— 它提升了 Claude 模型的 F1 分数,但降低了原本已具备高精确率的 DeepSeek 模型的 F1 分数。该结果展示的是*模型在 agent 内部的表现*,而不是单一模型的排名。详见 [`docs/RESULTS.md`](docs/RESULTS.md)。

## 包含内容
| 模块 | 作用 |
|---|---|
| `vae.llm` | Provider 适配层 (`Protocol`):包含 `mock`、`claude`、`deepseek` 或你自定义的模型 —— 其他部分不依赖于具体的模型选择 |
| `vae.agent` | 被测系统:`discover`(发现)候选漏洞,对抗性地 `verify`(验证)每一项,并(可选启用)`judge`(裁定)误报 |
| `vae.eval.systems` | 用于对比的系统:agent、单次基线、Slither 静态基线 |
| `vae.eval` | 真实标签语料库加载器 + 评分器(精确率 / 召回率 / F1 / 误报) |
| `vae.report` | 终端计分板(及其背后的数据) |
## 快速开始(无需 API key)
```
pip install -e '.[dev]'
make demo # full eval on the mock provider + sample corpus -> scoreboard
make test # unit-test the scorer
```
示例(使用 mock LLM;如果已安装 Slither,则 Slither 行将显示真实数据):
```
system Precision Recall F1 False positives Calls/contract
agent 100% 100% 100% 0 2.3
single_shot 50% 100% 67% 2 1.0
slither 100% 50% 67% 0 0.0
```
三个系统,三种截然不同的失败模式:单次基线过度标记(低精确率),Slither 虽精确但对逻辑错误视而不见(低召回率),而 agent 通过验证恢复了这两项指标。
## 概念与指标
- **corpus(语料库)** —— 带标签的测试集:一个包含合约的文件夹,其中的漏洞*已知*(每个 `.sol` 文件都有对应的 `.json` 答案)。只有在知道正确答案的情况下,你才能对漏洞查找器进行评分。
- **eval(评估)** —— 在语料库上运行系统,并根据这些答案进行评分。(语料库 = 考试题 + 答案;评估 = 参加考试并获得分数。)
- **single_shot(单次提示)** —— 仅询问模型一次(“发现 bug”),并将该答案作为最终结果。
- **agent** —— 同样的首次询问,然后进行第二轮 *verify(验证)*,尝试反驳每个声称的 bug,仅保留未被驳倒的项。验证步骤是唯一的区别,因此分数的差异完全归因于此。
| 指标 | 通俗含义 | 公式 |
|---|---|---|
| **Precision(精确率)** | 在标记出的问题中,真实漏洞的比例(误报更少) | TP / (TP + FP) |
| **Precision (judged)(裁定精确率)** | 由严格的 LLM judge 重新检查每次误报后的精确率,因此正确但*未打标签*的发现不会受到惩罚(可选启用 `--judge`) | (TP + confirmed) / (TP + FP) |
| **Recall(召回率)** | 在存在的真实 bug 中,被发现的比例(遗漏更少) | TP / (TP + FN) |
| **F1** | 只有在精确率和召回率都很高时才会很高的单一分数 | 2·P·R / (P + R) |
| **False positives(误报)** | 误报数量(已标记,但实际上不是 bug) | — |
| **Calls/contract(单合约调用数)** | 每份合约的平均 LLM 调用次数(速度/工作量) | single_shot = 1; agent = 1 + verifies |
| **Cost/contract(单合约成本)** | 每份合约的平均美元成本(token 数 × 价格) | — |
以百分比显示。烟雾报警器的直观理解:**precision** 意味着“当它报警时,是否真的有火灾?”,**recall** 意味着“在真实的火灾中,它捕捉到了多少?”。基础计数定义:
**TP** = 正确找到的真实 bug,**FP** = 误报,**FN** = 遗漏的真实 bug。
## 实时运行
```
cp .env.example .env # set ANTHROPIC_API_KEY and/or DEEPSEEK_API_KEY
pip install -e '.[llm]'
make matrix # the enabled models in models.toml, on the 3 samples
```
### 选择模型(你到底需要多强的模型?)
编辑 `models.toml` 并为每个模型切换 `enabled = true/false` —— 无需更改代码。计分板随后会为每个 系统×模型 组合增加一行,让你可以查看便宜的模型是否能在精确率/召回率上匹敌昂贵的模型,以及其成本是多少。默认开启的模型有:Claude Sonnet、Claude Haiku、DeepSeek V4 Flash(为了节省预算,Opus 和 DeepSeek Pro 处于关闭状态)。不使用该文件的临时替代方案:
```
python -m vae eval --models claude:claude-haiku-4-5,deepseek:deepseek-v4-flash
```
### 廉价样本 vs. 完整基准测试
`--per-class N` 会为每种漏洞类型最多保留 N 份合约(平衡且廉价);省略它即可运行完整集合。最简单的方法是通过 Makefile:
```
make eval-sample # ~2 per class across enabled models (pennies)
make eval-sample PER_CLASS=5 # a bit larger
make eval-all # all 143 SmartBugs contracts (still < ~$2 on the cheap models)
```
每次运行还会生成一份独立的 Markdown 报告(`--out`,由这些目标设置)—— 包含计分板**以及**“发现”部分,列出每个系统报告的内容、验证器驳斥的内容以及遗漏的内容。
### 为什么精确率看起来很低 —— 以及 `--judge` 修正
SmartBugs 为每份合约仅标记了一个 bug,但许多合约包含更多。在*另一个*真实 bug 上的正确发现会被计为误报,因此原始精确率是一个保守的下限。添加 `--judge`:一个严格的 LLM judge 会重新检查每次误报,如果它确认该发现是真实的但未被标记,则不再将其计入影响精确率的扣分项。计分板会增加一个 **Precision (judged)** 列,显示在严格精确率旁边 —— 这是一个诚实的范围,而不是一个被低估的单个数字。
```
python -m vae eval --models deepseek:deepseek-v4-flash --corpus data/corpus/smartbugs \
--per-class 2 --exclude-class other --judge
```
## 评分(简版)
当且仅当某个发现的 `vuln_class` 匹配真实标签,*且*其行号在带标签行号的 `--line-tol`(默认为 3)范围内时,该发现才算作 true positive;匹配过程是贪心的且一一对应的。成本是根据真实的 token 数量 × 当前模型定价计算的。详见
[`docs/architecture.md`](docs/architecture.md)。
## 语料库
`data/corpus/samples/` 包含三份手动标记的合约,用于测试匹配器(已提交,由无密钥 demo 使用)。如果需要进行真实基准测试,`scripts/ingest_smartbugs.py` 会将公开的 **SmartBugs-curated** 数据集(143 份带标签合约)转换为相同的 `.sol` + `.json` 格式 —— 加载器和评分器无需任何更改:
```
git clone --depth 1 https://github.com/smartbugs/smartbugs-curated /tmp/smartbugs-curated
make corpus-smartbugs # -> data/corpus/smartbugs/ (gitignored; regenerate locally)
make eval-sample # cheap subset across enabled models; or `make eval-all`
```
请参阅 [`data/corpus/README.md`](data/corpus/README.md)。
## 文档
- [`docs/RESULTS.md`](docs/RESULTS.md) — 来自真实运行的最新计分板(以及带标签的 mock 运行)。
- [`docs/MOTIVATION.md`](docs/MOTIVATION.md) — 为什么这项评估很重要以及所采用的方法。
- [`docs/architecture.md`](docs/architecture.md) — 深入探讨设计、Provider 适配层和评分机制。
- [`docs/ROADMAP.md`](docs/ROADMAP.md) — 从脚手架到可扩展、超越基线产出的路线图。
## 状态
支持在 mock provider(CI、无密钥)上端到端运行,并且支持 Claude 和 DeepSeek。多模型矩阵、平衡的廉价抽样、143 份合约的 SmartBugs 语料库以及可选的精确率下限 LLM judge 已连接完毕;参见 [`docs/RESULTS.md`](docs/RESULTS.md)。下一步:跨模型 judge、全量规模下的分类别能力曲线,以及一场攻击者↔防守者锦标赛([`docs/ROADMAP.md`](docs/ROADMAP.md))。
采用 MIT 许可证。
标签:DLL 劫持, 人工智能, 反取证, 大语言模型, 安全评估, 智能合约审计, 用户模式Hook绕过, 逆向工具