File size: 3,980 Bytes
11735de
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9e4396a
11735de
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
---
license: apache-2.0
base_model: Qwen/Qwen3.5-4B
tags:
  - text-classification
  - qwen
  - lora
  - llama-cpp
  - gguf
  - safety
  - claude-code
language:
  - en
  - zh
pipeline_tag: text-classification
---

# claude-code-local-classifier (Qwen3.5-4B LoRA)

微调 Qwen3.5-4B 做 Claude Code 的本地安全分类器(prompt injection 哨兵)。数据不出域、阈值概率可调、本地 GPU/CPU 推理。298 条 holdout:deny_recall 0.866 / false_block 0.005 / auc 0.945。

## 底座与训练

- **思路来源**:基于 **Jev**(TypeSafe AI 的快速推理模型)——不生成文本、只做结构化决策,返回 `true/false` 二分类概率。本模型沿此思路:纯分类、不生成理由。
- **底座**:`Qwen/Qwen3.5-4B`(多模态版,训练时用 `get_text_config()` 剥视觉编码器,只取语言部分)
- **LoRA**:r=32,7 目标模块(q/k/v/o + mlp gate/up/down),adapter ~181M
- **方法**:RLCD 分类——模型学 `yes`(deny)/`no`(allow) 单 token 概率,不生成 reason
- **训练数据**:合成 RLCD case + 真实 Claude Code 会话 holdout(298 条)。**原始会话数据含隐私,不在本仓库发布**,仅提供 6 条合成样本演示格式(见 GitHub 仓库 `datasets/samples.jsonl`)
- **prompt 格式**:chatml 三段(policy_summary + `<transcript>`+action + 裁决指令),训练/推理/验收同一真源 `prompt_assembly.py`

## 推理用法

模型不读生成文本(Qwen3.5 thinking 循环会输出 ` IMD`),而是走 llama-server `/completion` 读 yes/no logprob 概率:

```bash
# 1. 起 llama-server(GGUF 版)
llama-server -m qwen35-clf-v2-q4_k_m.gguf --host 127.0.0.1 --port 11943 \
  -c 16384 --parallel 1 -ngl 999 -ctk q8_0 -ctv q8_0

# 2. /completion 读概率(prompt 由 prompt_assembly.build_prompt 生成)
curl 127.0.0.1:11943/completion -H 'Content-Type: application/json' -d '{
  "prompt": "<|im_start|>system\n{policy_summary}<|im_end|>\n<|im_start|>user\n<transcript>\n{\"user\":\"...\"}\n{\"Bash\":{...}}\n</transcript>\n\n{裁决指令}<|im_end|>\n<|im_start|>assistant\n",
  "n_predict": 1, "n_probs": 60, "temperature": 0, "cache_prompt": true
}'
# 响应 completion_probabilities[0].top_logprobs 取 yes/no logprob
# p_yes = σ((lp_yes - lp_no) / T),T=1.0448(output/adapter/temperature.json)
# block 判定:p_yes >= 0.5 => deny
```

完整示例脚本见 GitHub 仓库 `eval/verify_local.py`、`eval/eval_holdout_full.py`。

## 指标(298 holdout)

| 指标 | 本地 v2 (RTX 4050 GPU) | 服务器纯 CPU | transformers 训练环境 |
|---|---|---|---|
| deny_recall@0.5 | 0.866 | 0.866 | 0.884 |
| false_block@0.5 | 0.005 | 0.005 | 0.005 |
| auc | 0.945 | — | 0.979 |
| sep | 0.799 | 0.726 | 0.819 |
| latency | 1.8s/条 | p50 33ms | — |

## 文件

- `adapter/` — LoRA adapter(~181M),挂到 `Qwen/Qwen3.5-4B` 底座
- `qwen35-clf-v2-q4_k_m.gguf` — GGUF Q4_K_M(~2.6G),llama-server 直接跑
- `temperature.json` — 校准温度 T=1.0448

## 关键坑(复刻必读)

- convert GGUF **不要带 `--no-nextn`**(会系统性压缩 logits,deny p_yes 0.85→0.47)。本 GGUF 是 v2,不带。
- 训练用 `policy_summary.txt`(15K 精简摘要)作 system,**不要用请求体的完整 rules**(139K,与训练不同构)。
- thinking 循环绕过方式 = 读 `/completion` 的 yes/no logprob,不读生成文本。

详见 GitHub 仓库 `docs/pitfalls.md`。

## 免责

- 本模型是 prompt injection 辅助哨兵,**不替代完整安全审查**。
- 原始训练/评测数据含真实会话,不在本仓库发布;仅提供合成样本演示格式。
- policy_summary 是通用 AI 安全规则摘要;完整 rules 不随模型发布。

## 相关

- GitHub 仓库(训练管线 + 部署 + eval + router 整合):_链接待填_
- 技术博客(动机 + 过程 + 踩坑复盘):_链接待填_
- HuggingFace 本仓库:https://huggingface.co/nathaniel-min/claude-code-local-classifier