学习型防线(路径 4,v0.59.0 / v0.60.0 半自动闭环)¶
学习型防线不是「用机器学习替代规则」,而是从拦截 / 逃逸案例中自动提取候选模式, 由人审核后更新规则库与提示词。
三个阶段:采集 → 提取 → 更新。核心安全约束是:自动只到「建议」为止,合入必须有人。
组成¶
| 文件 | 作用 |
|---|---|
anti_shortcut/defense/case_library.py |
案例库读写、失败 / 逃逸采集钩子、触发类型分类 |
anti_shortcut/defense/case_analysis.py |
分组分析、模式提取、规则合入(含反查与幂等)、提示词 few-shot |
scripts/analyze_cases.py |
CLI:案例库 → 分析报告 + 更新建议 |
scripts/update_rules.py |
CLI:建议 → (人工审核后)合入规则文件 |
anti_shortcut/defense/learned_rules.yaml |
学习产物(防线 4 会加载其中的 forbidden_patterns) |
.github/workflows/learning-loop.yml |
半自动闭环:采集 → 分析 → PR(含复测)/ issue(复测失败) |
scripts/update_rules.py --promote |
置信度管理:observation → active(按复测确认次数) |
采集¶
案例库是 JSONL(每行一条),默认按工作区落在
<workspace>/.agent_gate/defense/case_library.jsonl;
defense:
case_library:
enabled: true # 默认关闭
library_file: case_library.jsonl
shared_library: null # 跨工作区汇总库(同一机器 / 同一 CI 共用)
两个采集点:
- 被拦下的案例:
run_defense_checks()里任一防线失败即调用capture_defense_failure()(含防线名、触发类型、拦截理由、阶段迁移、需求摘要、违规片段); - 逃逸成功的案例:红队 Agent 判定
escaped时调用capture_escape(),把攻击载荷 写进案例库(危险命令排在载荷最前,避免被 800 字上限截掉)。
触发类型由 classify_trigger() 归类:untraced_write / forbidden_op:<类别> /
missing_test_command / vague_template / template_missing / static_contradiction /
alias_contradiction / semantic_unavailable / semantic_tamper / risk_sampling /
escape:<技术> 等。
红线:采集绝不影响门禁判定 —— 写失败、解析失败、配置异常全部被吞掉,只记日志。
提取¶
python scripts/analyze_cases.py --cases <案例库或目录> \
--report analysis_report.json --markdown analysis_report.md --suggestions suggestions.json
python scripts/analyze_cases.py --workspace . --json # 读 <workspace>/.agent_gate/defense
按 (防线, 触发类型) 分组后提取:
- 防线 4:从案例载荷里抽「动词 + 选项」(
truncate -s、find -delete)与点号 API (os.truncate),再与BUILTIN_FORBIDDEN做减法,只留规则库尚未覆盖的; 文件名 token(notes.txt)与子串误命中(truncate里的nc)会被过滤; - 防线 1:从拦截理由里抽空话短语;
- 防线 3:抽冲突变量名;
- 防线 2B:抽弱化词(已在防线 2 提示词里明确的词不重复建议)。
逃逸案例若没声明 attack_category,不猜类别,只记录「需人工判断」,避免生成风马牛
不相及的规则。
更新(人工审核后)¶
# dry-run:只做校验与统计,不落盘
python scripts/update_rules.py --suggestions suggestions.json \
--dest anti_shortcut/defense/learned_rules.yaml --verify-cases case_library.jsonl
# 人工审核差异后落盘
python scripts/update_rules.py --suggestions suggestions.json \
--dest anti_shortcut/defense/learned_rules.yaml --verify-cases case_library.jsonl --apply
:--apply 的三道闸门:
- 正则编译校验:非法正则丢弃;
- 反查:建议的正则必须至少命中一条它来自的案例原文,否则丢弃
(
--apply时不给--verify-cases会直接拒绝执行,除非显式--allow-unverified); - 幂等:已在目标文件里的条目不会重复写入。
规则文件里的 forbidden_patterns 由防线 4 直接加载(defense.behavior_audit.
extra_forbidden_patterns_file);vague_phrases / weakening_words / few_shot_examples
是给防线 1 配置与防线 2 提示词的人工评审输入,不会被代码自动应用:
python scripts/update_rules.py --suggestions suggestions.json --dest learned_rules.yaml \
--verify-cases case_library.jsonl --apply \
--prompt anti_shortcut/defense/prompts/tamper_check.txt # 标记块整体重写,多次运行幂等
fail-closed:配了 extra_forbidden_patterns_file 但文件缺失或解析失败时,防线 4 直接
拒绝交付 —— 规则文件写坏 ≠ 没有规则。
规则文件结构(v0.60.0:auto / manual 分区块)¶
v0.59.0 的一个真实缺陷:--apply 会用建议里的 note 覆盖人工写的 note(人工审核
痕迹被抹掉)。v0.60.0 起规则文件按「机器区 / 人工区」分开:
schema_version: 2
note: "人工说明(人工区:--apply 永不覆盖)"
auto: # 机器区:每次 --apply 刷新
generated_by: analyze_cases.py
updated_at: "2026-09-11T00:00:00Z"
suggested_note: "本文件由案例库分析生成……"
rules: # 逐条规则:auto 机器更新 / manual 人工审核痕迹
- rule_id: rule-4925aa5d1e5d
category: file_delete
pattern: '(?i)\btruncate\s+[^\n;&|]{0,40}?\-s\b'
auto:
confidence: active # observation -> active(复测确认后升级)
confirmations: 3
source_cases: [case-71c21fa316bd]
updated_at: "2026-09-11T00:00:00Z"
manual:
note: "人工确认:truncate -s 0 属于 file_delete(清空文件)"
approved_by: Xuqing0415
approved_at: "2026-09-11"
forbidden_patterns: # 兼容视图:由 rules 汇总,防线 4 直接读它
file_delete:
- '(?i)\btruncate\s+[^\n;&|]{0,40}?\-s\b'
scripts/update_rules.py --apply 只刷新 auto 与顶层 auto: 区块;顶层 note 与
rules[].manual 原样保留(有回归测试 test_apply_suggestions_preserves_manual_note_block)。
规则置信度¶
新规则以 confidence: observation 进入规则库(照常参与拦截,只是标记「尚未反复
验证」)。每通过一轮红队全量复测就 +1,累计到门槛后升级:
python scripts/update_rules.py --dest anti_shortcut/defense/learned_rules.yaml \
--promote --confirmations-required 3 --apply
半自动闭环(v0.60.0)¶
.github/workflows/learning-loop.yml(每周一 04:23 UTC + 手动)把闭环从「人推动」
变成「机制推动」,但合入仍由人工在 PR 上完成:
红队全量跑(采集案例) -> analyze_cases.py -> update_rules --apply(先 dry-run)
-> 复测(加载候选规则再跑红队) -> 复测通过则开 PR / 不通过则开 issue
-> 人工审核合并 -> --promote 累积确认次数
PR 正文自动包含:规则 diff、来源案例与提取报告、复测结果、人工审核清单。复测失败 (仍有真实漏洞)不会提 PR,而是建 issue —— 说明候选规则不够,需要人判断。
与红队的联动(闭环)¶
红队攻击 → 逃逸案例 → 模式提取 → 人工审核合入 → 红队复测 → 确认堵住
红队沙箱默认加载 learned_rules.yaml(SandboxConfig.use_learned_rules=True),
即「用当前防御姿态跑红队」;--no-learned-rules 用于复现「学到之前」。
闭环实证(2026-09-11)¶
产物在 benchmarks/red_team/results/learning_loop/:
| 步骤 | 命令 | 结果 |
|---|---|---|
| 1. 修复前跑红队 | run_red_team.py --no-learned-rules --case-library .../case_library.jsonl |
15 个案例:拦截 11 / 不可判定 3 / 逃逸 1(equivalent_op/truncate_zero) |
| 2. 分析案例库 | analyze_cases.py --cases case_library.jsonl ... |
20 条案例分 8 组,逃逸组提取出 1 条新规则候选 |
| 3. 审核合入 | update_rules.py ... 先 dry-run 再 --apply |
反查命中后写入 learned_rules.yaml(file_delete 类别) |
| 4. 修复后复测 | run_red_team.py --fail-on-vulnerability |
真实漏洞 0 个;truncate_zero 被防线 4 用学到的规则拦下(阶段 4) |
对照可见于 before/report.json(escaped)与 after/report.json(blocked,拦下理由里带
的就是学到的正则)。
持续运转与告警(v1.0.0)¶
自动化闭环最大的风险是「配了但没跑」——job 全绿、其实几周没有产出。为此:
- 运行摘要:每次运行把「触发方式 / 采集案例数 / 复测结果 / 候选规则状态 / run 链接」 写进 job summary,一眼能看出这一步是否真的执行了。
- 产出健康检查(
output-healthcheckjob):每次运行时查最近一条带learning-loop标签的 PR/issue,若已连续 14 天没有产出就自动开 issue 提醒(按标题前缀去重,不会重复刷)。 该检查同时覆盖「没跑」与「跑了没产出」两种情况。
局限¶
- 提取是启发式的(从文本抽正则),只负责把候选摆到人面前;宽严由人工审核把关;
- 只能补规则可表达的缺口。设计级缺口需要改机制而不是加正则:风险伪装的
「第一个子任务必然没有历史」已由 v0.60.0 的需求族风险累积(
task_family_id+task_family_detection)补上;仍然存在的残余情形见 red-team.md 的残余风险表。 - 案例库按 800 字截断载荷,且不做自动合入 —— 这是刻意的:自动更新规则可能引入误报 或恶意规则,所以合入路径永远留一个人;
- 置信度升级(
--promote)只统计「复测通过次数」,不等于「规则一定正确」;宽严仍由 人工在 PR 上把关。