Hvoegeli/agentforge
GitHub: Hvoegeli/agentforge
一个基于多智能体协作的 LLM 应用持续红队评估平台,自动化发现、验证和回归追踪临床聊天机器人的安全漏洞。
Stars: 0 | Forks: 0
# AgentForge
**状态:** MVP (2026-05-12) — 平台构建完成,201 个测试通过,6 项已植入的发现,针对部署的目标实时演练了全部 9 个攻击类别(仪表板上显示 9/9)。最终版本:2026-05-15。
**目标系统(“受害者”):** [OpenEMR Clinical Co-Pilot](https://github.com/Hvoegeli/openemr)(第 1-2 周构建),运行在 Hetzner 上并通过 Cloudflare quick tunnel 暴露。AgentForge 通过 HTTP 访问目标——与外部攻击者的方式完全相同——并且从不与其共享进程、数据库或 CI 流水线。
- **已部署的目标 (Co-Pilot):** `https://hansen-rat-ages-rim.trycloudflare.com/` — *Cloudflare quick tunnel;子域会在 `cloudflared` 重启时轮换,因此每次提交检查点时会附带当前的 URL。* 回归测试套件固定的基线 SHA:`74aa5be4` (openemr `master`);当前的在线部署运行的是 `1055abd71`(渗透测试修复版本——参见 [`THREAT_MODEL.md`](THREAT_MODEL.md) 第 *Fix status* 节)。
- **已部署的 AgentForge 仪表板(本平台自身的 URL,与 Co-Pilot 分离):** `https://asin-vessels-differ-drunk.trycloudflare.com/` — 渲染后的可观测性仪表板,由 Hetzner 服务器上独立的 Cloudflare quick tunnel 提供服务(`systemd`:`agentforge-dashboard-http.service` 在 `:8090` 端口提供 `/opt/agentforge-dashboard/` 服务,`agentforge-dashboard-tunnel.service` 运行 `cloudflared`)。`RESILIENCE.md` 在 `/RESILIENCE.md` 路径下提供服务。*Quick-tunnel 子域会在 `cloudflared` 重启时轮换——每次提交检查点时会附带当前的 URL;在服务器上运行 `journalctl -u agentforge-dashboard-tunnel -n 50 | grep trycloudflare` 可获取当前在线的 URL。* 提交的快照:[`dashboard.html`](dashboard.html)。配套的交接文档 [`RESILIENCE.md`](RESILIENCE.md) — 包含按类别划分的通过/失败结果以及每一项未解决的发现,作为提供给 Co-Pilot 维护者的可复现工作清单;使用 `agentforge resilience-report` 重新生成。
- **配套文档:** [`THREAT_MODEL.md`](THREAT_MODEL.md)(攻击面),[`ARCHITECTURE.md`](ARCHITECTURE.md)(多智能体设计 + 图表),[`USERS.md`](USERS.md)(谁在使用 / 为什么自动化),[`evals/success_criteria.md`](evals/success_criteria.md)(不变量),[`COST_LATENCY_REPORT.md`](COST_LATENCY_REPORT.md)(成本模型 + 100/1K/10K/100K 预测),[`presearch.md`](presearch.md)(约束 + 决策)。
## 它的作用
四个智能体协同工作,持续识别、评估和记录目标 Clinical Co-Pilot 中的漏洞,然后将每个确认的利用转化为永久的回归测试:
- **Red Team 智能体** — 生成和变异对抗性输入(单轮和多轮);每次循环都会运行一个确定性基础层(PyRIT / promptfoo / garak 语料库 + 公共数据集,以及精选的种子语料库),并且*仅在变异未遂事件时*调用开源的安全专用 LLM。
- **Judge 智能体** — 独立于攻击引擎;基于**机器可检查的不变量**(每个威胁类别的一个确定性断言,定义在 [`evals/success_criteria.md`](evals/success_criteria.md) 中)给出 pass/fail/partial/uncertain(通过/失败/部分/不确定)的裁决。对于需要语义判断的残留情况,LLM-judge 会针对带有标签的真实语料库([`evals/judge_corpus/`](evals/judge_corpus/))运行,并测量其一致率 / FP / FN 率——在该比率确立之前,其裁决仅作为*线索*呈现,绝不自动归档为发现。
- **Orchestrator 智能体** — 读取可观测性状态(覆盖率缺口、未解决的高严重性发现、近期的回归、成本消耗)并决定下一步攻击什么。掌控速率上限、无信号成本终止开关和回归重放触发器。LangGraph `StateGraph` 将整个循环串联起来。
- **Documentation 智能体** — 将确认的 `Finding` 及其 Judge 裁决转换为结构化、可复现的漏洞报告(OWASP / MITRE ATLAS / NIST 参考、临床影响、最小复现、实际表现与预期对比、修复建议、修复验证状态);自动归档 HIGH 及以下级别,将 CRITICAL 级别保留为草稿,等待人工批准。
此外还有一个**回归测试工具**(SQLite 发现数据库;每个确认的利用都会成为一个确定性的重放测试,它断言的是*不变量*,而不是输出字符串)和一个**可观测性层**(JSONL 跟踪日志 + 静态 HTML 仪表板)。
## 为什么使用开源权重 / 不使用主流 LLM
前沿的专有模型在要求生成对抗性输入时,即使在明确的授权渗透测试框架下,也往往会拒绝、弱化或产生幻觉——这使得覆盖率数据变成了虚构的。每次循环中运行的确定性基础层(PyRIT + promptfoo + garak 语料库 + 诸如 HarmBench / JailbreakBench / AdvBench 的公共数据集 + 精选的技术语料库)是可复现、可审计且成本低廉的;LLM 仅在通过 [OpenRouter](https://openrouter.ai/) 变异未遂事件时被调用(用于 Red Team 的开源安全专用模型;用于 Judge 的*不同模型家族*,从而在设计中保证 Judge 的独立性)——并在本地免费开发迭代中使用 Ollama。
| 角色 | 模型 | 原因 |
|---|---|---|
| Red Team — 攻击生成 | **WhiteRabbitNeo-2-70B** | 专为授权安全测试微调的 Llama-3 版本 |
| Red Team — 变异 / 规划 | **Qwen3-32B** / DeepSeek-V3 | 强大的智能体推理能力;结构性,不易产生拒绝 |
| Judge — 语义判断场景 | **Mistral-Large-2** / Foundation-Sec-8B | 与 Red Team *不同的家族* → 设计上保证了 Judge 的独立性 |
| Documentation | **Llama-3.1-8B-Instruct** | 模板化 Markdown 填充;成本低廉 |
## 我们映射的标准
我们将已知的分类法投入实际应用;我们不发明新的分类法。
- [**OWASP LLM 应用 Top 10 (2025)**](https://genai.owasp.org/llm-top-10/) — 威胁模型的主干
- [**MITRE ATLAS**](https://atlas.mitre.org/) — 漏洞报告的 TTP ID
- [**NIST AI 600-1** (生成式 AI 配置文件)](https://nvlpubs.nist.gov/nistpubs/ai/NIST.AI.600-1.pdf) — 治理词汇
- [**OWASP 智能体 AI / 多智能体系统威胁建模指南**](https://genai.owasp.org/resource/multi-agentic-system-threat-modeling-guide-v1-0/) — 多智能体特有的威胁
## 项目布局
```
agentforge/
├── README.md (this file)
├── ARCHITECTURE.md (multi-agent design + agent-interaction diagram)
├── THREAT_MODEL.md (full attack-surface map + Framework Mapping Chart + Fix status)
├── USERS.md (who uses AgentForge, workflows, why automation)
├── COST_LATENCY_REPORT.md (agent-side cost model + 100/1K/10K/100K projection)
├── presearch.md (planning doc — constraints, decisions, open questions)
├── dashboard.html (rendered observability dashboard — static, self-contained)
├── RESILIENCE.md (generated: per-category pass/fail + open-findings work list for the target's maintainer)
├── deploy-dashboard.sh (regenerate the dashboard + RESILIENCE.md from a live run + scp the dashboard to the box)
├── reports/ (the 6 generated vulnerability reports — all filed; the 2 CRITICALs were reviewed & signed off, so reports/drafts/ is empty. A Red-Team-discovered CRITICAL would land in reports/drafts/ until approved.)
├── evals/
│ ├── success_criteria.md (the invariant table — the Judge's spec)
│ ├── judge_corpus/ (labeled ground-truth transcripts — the Judge validation set)
│ ├── thresholds.yaml (token / cost / wall-time / hop / amplification thresholds for the DoS invariants)
│ └── results/ (committed run artifacts)
├── src/agentforge/ (Python package — uv project)
│ ├── models.py (the Pydantic contracts between agents)
│ ├── cli.py (entrypoint: run / status / replay / validate-judge / dashboard / resilience-report / seed-findings / regression-suite)
│ ├── config.py (settings + the target-host allowlist)
│ ├── llm.py (OpenRouter / Ollama model router)
│ ├── known_findings.py (the 6 seeded Co-Pilot findings — 4 day-one + 2 from the 2026-05-12 pen-test)
│ ├── target/ (Target Adapter — HTTP client, host allowlist, rate cap, redaction)
│ ├── attacks/ (deterministic floor — PyRIT/promptfoo/garak wrappers + curated corpus + the Red Team agent + LLM mutation + poisoned-doc rendering)
│ ├── invariants/ (the deterministic checkers, one per success_criteria invariant + thresholds loader)
│ ├── judge/ (the Judge agent + LLM-Judge + the corpus-validation harness)
│ ├── documentation/ (the Documentation agent + report templates)
│ ├── orchestrator/ (the Orchestrator agent + LangGraph StateGraph + the priority heuristic)
│ ├── regression/ (the Regression Curator + Suite — replay_case / replay_finding)
│ ├── storage/ (SQLite findings DB + JSONL trace writer)
│ ├── observability/ (metrics computation for the dashboard)
│ └── dashboard/ (Jinja2 render → static HTML)
├── tests/ (pytest — 201 tests)
├── .github/workflows/ (ci.yml)
├── LICENSE (MIT)
└── pyproject.toml / uv.lock / .env.example / .python-version
```
## 运行说明
需要 [`uv`](https://docs.astral.sh/uv/)。在仓库根目录下运行:
```
uv sync # install deps
uv run pytest -q # run the test suite
uv run ruff check src/ tests/ # lint
# 将 6 个已知 Co-Pilot findings(case + attempt + real deterministic verdict + vuln report)写入 findings DB
uv run agentforge seed-findings --db findings.sqlite --reports-dir reports/
# 针对已部署的目标实时运行 campaign(如果省略 --category,Orchestrator 会选择下一个 category)
COPILOT_USERNAME= COPILOT_PASSWORD= \
uv run agentforge run --category C1 \
--target-url https:// --target-sha copilot@1055abd71 \
--db findings.sqlite --reports-dir reports/
uv run agentforge status --db findings.sqlite # coverage / verdict rates / open findings / recent runs
uv run agentforge validate-judge # corpus-validate the Judge (agreement / FP / FN)
uv run agentforge replay --finding --n 10 --target-url # regression-replay one finding's case
# 在 post-fix SHA 处重放整个 regression suite — “found → reported → fixed → regression-verified” artifact:
uv run agentforge regression-suite --db findings.sqlite --target-url --target-sha copilot@1055abd71 \
--out evals/results/regression-1055abd71.json --update-status # resolves the cases that now hold; flags any regression; exits non-zero if any case fails
uv run agentforge dashboard --db findings.sqlite --out dashboard.html --resilience-md RESILIENCE.md # render the observability dashboard (+ the RESILIENCE.md work list)
uv run agentforge resilience-report --db findings.sqlite --out RESILIENCE.md # just the hand-off doc
# 一条命令:re-seed findings + 针对实时目标的 fresh C1 floor + render + 通过 scp 将 dashboard 传输到 box
COPILOT_USERNAME= COPILOT_PASSWORD= ./deploy-dashboard.sh
```
Target Adapter 仅针对列入白名单的主机(`localhost` / 服务器 IP / `*.trycloudflare.com`)进行构建——AgentForge 仅攻击这一个经过授权的 Co-Pilot 目标,绝不攻击其他目标。仅使用合成患者数据;有速率限制(≤ 20 次攻击/分钟);记录在存储前会进行脱敏处理;CRITICAL 级别的发现将保留为草稿,等待人工批准。
关于每个不变量具体检查的内容,请参见 [`evals/success_criteria.md`](evals/success_criteria.md),关于完整的攻击面映射,请参见 [`THREAT_MODEL.md`](THREAT_MODEL.md),关于多智能体设计,请参见 [`ARCHITECTURE.md`](ARCHITECTURE.md)。
## 路线图(周五之后)
1. 生产级别的 Judge 语料库(来自经过脱敏处理的真实记录的 200 多个手工裁定案例)。
2. 针对上述 HTTP-setup 发现(`raw_http_get` / `session_adoption`)的在线实时重放执行 + 一个管理员可观测性账户,以便针对已打补丁的目标使 C4/C5 跟踪富化正常工作。
3. Langfuse 跟踪集成,用于智能体级别的调试。
4. 更深入的 PyRIT / garak / promptfoo 覆盖。
5. 为 openemr fork 的 CI 提供插件,以 AgentForge 的回归测试套件作为门禁。
## 许可证
[MIT](LICENSE)
标签:AI风险治理, AI风险缓解, CISA项目, Cloudflare Tunnel, Hetzner部署, LangGraph, LLM红队评估, MITRE ATLAS, NIST AI 600-1, OpenEMR, OpenRouter, OWASP LLM Top 10, promptfoo, PyRIT, 临床辅助系统, 人工智能安全, 医疗信息系统安全, 医疗聊天机器人, 合规性, 回归测试, 多智能体系统, 大语言模型安全, 安全大模型, 实时处理, 密码管理, 对抗性测试, 持续安全评估, 智能体安全, 机密管理, 红队自动化, 网络安全, 自动化多智能体, 逆向工具, 隐私保护, 黑盒测试