English | 简体中文
在保密草稿里埋入金丝雀,对大模型端点做单侧训练吸收审计
你把未发表的草稿喂给了云端大模型,厂商承诺「不用于训练」,却拿不出任何证据。 DraftCanary 在草稿里埋入独一无二的高熵金丝雀,并向 DeepSeek / Qwen / GLM / Kimi 周期性发起提取探测——把「我的草稿有没有被吸收进训练集」从口头承诺, 变成你自己持有、可追加、可复核的证据链。
单进程 Python CLI · 无服务、无守护进程 · 账本与报告全本地
单侧检测是科学边界:命中(hit)即证明该端点吸收过你的金丝雀; 沉默(quiet)只说明该次探测预算下未观测到提取信号,不构成「未吸收」或「安全」的证明。 DraftCanary 永远不出具清白证书——所有判定、报告与措辞都遵循这条边界。
把整份草稿交给云端助手迭代,已经是科研与长文写作的常规工作流。问题是:你没有任何办法 验证「不用于训练」的承诺——条款可以承诺,但可验证的凭证在结构上就不可能由厂商单方面提供, 自我审计没有动机。现有的三个替代方案各有结构性缺陷:
| 方案 | 给你什么 | 结构性缺陷 |
|---|---|---|
| 厂商条款承诺 / 退出开关 | 法律预期 | 拿不出任何凭证,无法被检验 |
| 敏感稿只用本地模型 | 物理消除外泄路径 | 放弃前沿能力——艰难推理恰恰发生在云端 |
| 脱敏网关 / 占位符替换 | 拦截标识符与密钥 | 剥离的正是需要助手帮忙的推导、代码与结果 |
| DraftCanary 金丝雀审计 | 可伪造检验的逐厂商信号 | 单侧:命中即吸收,沉默不等于清白 |
机制是经典的提取审计(canary / extraction probing)思路,但面向的是真实工作流: 金丝雀埋在你本来就要发出去的草稿里,探测在你本来就要用的端点上跑, 证据链由你持有——护城河在持续累积的账本数据,不在 CLI 本身。
git clone https://github.com/SuperMarioYL/draftcanary.git
cd draftcanary
uv tool install . # 或 pipx install . / pip install -e .第一步:零 API key 的确定性命中演示(约 60 秒)
draftcanary demo生成 12 枚高熵金丝雀埋入示例科研草稿 → 启动本地 mock「已污染」端点 → 自动运行 logprob 提取探测(孪生对照)→ 终端打出命中报告。全程离线。
第二步:埋入自己的草稿
draftcanary embed examples/research_draft.md # 或你的草稿
# 产出 examples/research_draft.canary.md + ledger.jsonl之后只把金丝雀版本(而非原始草稿)发给云端助手——这是人工步骤,工具不接管流量。
第三步(离线走查):对本地污染端点复现完整闭环
draftcanary probe --provider mock
draftcanary report第四步:对真实端点探测(需要该厂商的 API key)
export DEEPSEEK_API_KEY=sk-…
draftcanary probe --provider deepseek --max-usd 0.5
draftcanary report --format markdown --output audit-report.md| 端点 | 模型 | API key 环境变量 | 获取地址 |
|---|---|---|---|
| DeepSeek | deepseek-flash |
DEEPSEEK_API_KEY |
https://platform.deepseek.com |
| Qwen(百炼兼容模式) | qwen-plus |
DASHSCOPE_API_KEY |
https://bailian.console.aliyun.com |
| GLM(智谱) | glm-5 |
ZHIPU_API_KEY |
https://open.bigmodel.cn |
| Kimi(月之暗面) | kimi-k3 |
MOONSHOT_API_KEY |
https://platform.moonshot.cn |
自定义 OpenAI 兼容端点不受限:draftcanary probe --provider deepseek --base-url https://你的网关/v1。
draftcanary demo 的完整记录(真实终端录制,可由 vhs docs/demo.tape 重新渲染):
终端里的内容:12 枚金丝雀全部命中(分数 0.951 · 孪生对照 0.000 · 分离度 +0.951), 随后是 per-provider 审计时间线与单枚金丝雀的证据面板——嵌入句原文高亮、logprob 路径、与孪生对照的分离度。mock 端点模拟「草稿已被吸收进训练集」的最坏情形, 仅为机制演示,不构成对任何真实厂商的证据。
单进程 Python CLI;无服务、无数据库、无守护进程,全部状态在本地 JSONL 账本:
flowchart TB
draft["原始草稿"] -->|"draftcanary embed 埋入金丝雀"| canaried["金丝雀版本 + 账本记录"]
canaried -->|"人工步骤:发给云端助手"| cloud["DeepSeek / Qwen / GLM / Kimi(OpenAI 兼容端点)"]
ledger[("ledger.jsonl 追加账本")]
canaried --> ledger
cloud -->|"draftcanary probe 提取探测"| verdict["hit / quiet 判定"]
verdict --> ledger
ledger -->|"draftcanary report 渲染"| report["markdown / json / rich 审计报告"]
| 模块 | 职责 |
|---|---|
draftcanary/cli.py |
Typer 入口:embed / probe / report / demo |
draftcanary/canary.py |
高熵可读金丝雀生成 + 草稿嵌入 + 孪生派生 |
draftcanary/probe.py |
探测核心:logprob / 续写双方法 + 孪生对照比较 + 预算墙 |
draftcanary/providers.py |
端点注册表:四厂商 + mock(全 OpenAI 兼容) |
draftcanary/ledger.py |
JSONL 追加账本(Canary | ProbeRecord) |
draftcanary/report.py |
markdown / json / rich 三形态报告 + 证据高亮 |
draftcanary/mockserver.py |
本地污染 / 静默 mock 端点(demo 与测试夹具) |
experiments/ |
m1 本地污染实验与 m3 端点校准脚本,复用探测核心 |
孪生对照是判定的核心:每枚已提交金丝雀都有一枚从未提交的孪生串(同熵、同形状、 由金丝雀确定性派生),与金丝雀在同一端点、同一调度、逐字节相同的提示词下并排探测。 判定只看两者分数相对该端点噪声底的分离度——任何绝对分数都不构成结论。
方法按端点能力路由(四家真实 API 中仅两家的 chat completions 接受 logprobs 参数):
| 端点 | 方法 | 原因 |
|---|---|---|
| DeepSeek / Kimi | logprob |
请求 schema 支持 logprobs / top_logprobs |
| Qwen 兼容模式 / GLM | 续写 continuation |
schema 不含 logprobs 参数,固定回退 |
| mock / 本地 checkpoint | logprob |
不受真实端点 schema 限制(demo 与实验用) |
已实现(v0.1.0):
embed:12 枚(可调)80-bit 金丝雀,自然语句嵌入,原始草稿逐字保留并记录 sha256 指纹probe:四厂商 + mock 适配、孪生对照、--max-usd预算墙(全次运行共享)、方法自动路由report:per-provider 命中时间线、单条证据、下次探测预算建议(markdown / json / rich)demo:零 key、全离线、确定性命中- 账本:append-only JSONL,记录类型与计划的数据模型逐字段一致
- 实验脚本:
experiments/validate_local_poison.py(LoRA 污染开源小模型制造 ground truth, 产出「暴露次数 × 探测预算 → 检出率」曲线)、experiments/calibrate_endpoints.py(正对照 = 已知公开文本、负对照 = 全新金丝雀,校准各端点噪声底)
明确的边界(v0.1 不做,其中前两条是设计选择而非待办):
- 永远不出具「未吸收」证明或合规证书——单侧检测是科学边界,quiet 不是清白
- 不拦截、不代理与助手的对话——只产出金丝雀草稿文件与探测记录
- 吸收不等于可提取:单枚草稿的金丝雀在有限预算下可能检不出来。检出率随暴露次数与 探测预算上升——这正是 m1 实验脚本要量化的曲线,请在解读 quiet 时记住这一点
- 对
mock端点的所有记录都是演示夹具,与任何真实厂商无关 - 仅 DeepSeek / Qwen / GLM / Kimi 一等预设(自定义
--base-url不受限); 仅对话补全端点;仅 CLI;单机单用户
常用选项(draftcanary <命令> --help 查看全部):
| 选项 | 作用于 | 默认 | 说明 |
|---|---|---|---|
--max-usd |
probe |
0.5 |
本次运行的总预算墙(USD),每轮探测前检查 |
--attempts |
probe |
1 |
每枚金丝雀的探测轮数(每轮含孪生对照;首轮贪心,其后采样) |
--noise-floor |
probe |
0.5 |
hit 判定所需的最小分离度;校准值见 experiments/calibrate_endpoints.py |
--provider |
probe |
必填 | deepseek / qwen / glm / kimi / mock |
--base-url / --model / --api-key |
probe |
注册表值 | 覆盖端点地址、模型 id、key 来源 |
--count |
embed |
12 |
埋入的金丝雀数量 |
--ledger |
全部 | ./ledger.jsonl |
账本路径 |
--format / --output |
report |
terminal |
terminal / markdown / json,后两者可写文件 |
预算与定价:--max-usd 按 draftcanary/providers.py 内置单价估算,默认值是构建时的
保守偏高参考值(USD / 1M tokens,预算墙宁可提前停、不可漏),需要精确对账时请按
各厂商当前价目表更新。无定价端点(mock、本地)不启用 USD 计价。
API key 通过各厂商的环境变量注入(见快速开始表格),--api-key 可临时覆盖;
key 永远只存在于本机进程内,账本与报告不记录 key。
使用注意:提取探测是自动化请求——请遵守各厂商 API 条款与速率限制,预算墙与
--attempts 是控制用量的第一道闸。账本(ledger.jsonl)与金丝雀版本草稿
(*.canary.md)包含你的金丝雀 token,不要提交到公开仓库(.gitignore 已默认排除)。
- m1 探测核心:logprob / 续写双方法 + 孪生对照 + 预算墙
- m2 审计 CLI:
embed/probe/report/demo、四厂商适配、JSONL 账本 - m1 实验结果表:在开源 Qwen 小模型上运行
experiments/validate_local_poison.py, 提交「暴露次数 × 预算 → 检出率」的 detection-budget 曲线 - m3 端点校准与结果表:预算封顶运行
experiments/calibrate_endpoints.py, 校准四端点噪声底与静默基线,结果表随账本周期性更新 - PyPI 发布(release workflow 已就绪,trusted publishing 待一次性配置)
- 更多端点一等预设(
--base-url现已可用;欢迎 issue 表态)
MIT。审计工具按「现状」提供:它产出的是可复核的证据记录, 不是法律意见,也不是任何形式的合规认证。
MIT © 2026 SuperMarioYL
