粘贴进 Duck.ai 前的敏感信息自检:密码/Key/身份证号等模式命中则拒绝——官方明确匿名≠可随意输入敏感信息。
先看清成品
- 礼貌邮件样例 ok。
- 含密码+身份证样例 hits≥2。
- 1 项 pytest 通过。
合规提醒:Duck.ai 没有官方公开聊天 REST API。本系列不教网页逆向、不自动化登录、不绕过额度。A 篇使用 DuckDuckGo 官方 Instant Answer API;B/C 为本地账本与粘贴前自检。匿名化不等于可输入身份证/密码;对话仍会发往模型提供方处理。Instant Answer 需署名且非商用须遵守官方条款。
运行说明:Instant Answer 无需 Key;A 篇 mock + 对 api.duckduckgo.com 实测可返回 Abstract。未调用任何非官方 Duck.ai 聊天端点。B/C 纯本地。2026-10-23(Asia/Shanghai)pytest 通过。
做完之后你会得到什么?
| 成果 | 说明 |
|---|---|
duckai_prompt_guard.py |
自检 |
| 样例 txt | ok/bad |
test_119c.py |
测试 |
需要准备哪些环境?
| 项目 | 说明 |
|---|---|
| Python | 3.10+ |
关键事实
| 事实 | 来源 |
|---|---|
| 对话内容仍发往模型提供方 | 官方帮助页 |
| 勿输入身份证号、密码等 | 教程页提醒 |
| 本工具纯本地正则,无上传 | 实现 |
文件结构
119-duck-ai-C-src/
├── duckai_prompt_guard.py
├── sample_ok.txt
├── sample_bad.txt
└── test_119c.py
第 1 步:自检
#!/usr/bin/env python3
"""Duck.ai 粘贴前敏感信息自检。
官方强调:匿名化不等于可随意输入身份证号/密码等;对话仍会发往模型提供方处理。
本工具只做本地正则提示,不上传任何内容。
"""
from __future__ import annotations
import argparse, json, re, sys
from pathlib import Path
PATTERNS = [
("password_like", re.compile(r"(?i)(password|passwd|密码)\s*[:=]\s*\S+")),
("api_key_like", re.compile(r"(?i)\b(sk-[A-Za-z0-9]{16,}|AKIA[0-9A-Z]{16})\b")),
("cn_id_like", re.compile(r"\b\d{17}[\dXx]\b")),
("bank_card_like", re.compile(r"\b\d{16,19}\b")),
("email_password_pair", re.compile(r"(?i)[\w.+-]+@[\w-]+\.[\w.-]+.{0,20}(password|密码)")),
]
def check(text: str) -> dict:
hits = []
for name, rx in PATTERNS:
if rx.search(text):
hits.append(name)
reasons = [f"possible {h}" for h in hits]
if len(text) > 8000:
reasons.append("prompt very long; prefer splitting non-sensitive context")
return {"ok": not reasons, "reasons": reasons, "hits": hits}
def main(argv=None) -> int:
ap = argparse.ArgumentParser(description=__doc__)
ap.add_argument("text_file")
args = ap.parse_args(argv)
r = check(Path(args.text_file).read_text())
print(json.dumps(r, ensure_ascii=False, indent=2))
return 0 if r["ok"] else 1
if __name__ == "__main__":
raise SystemExit(main())
第 2 步:测试
import json, subprocess, sys
from pathlib import Path
HERE = Path(__file__).resolve().parent
def test_guard():
ok = subprocess.run([sys.executable, str(HERE/"duckai_prompt_guard.py"), str(HERE/"sample_ok.txt")],
capture_output=True, text=True, check=True)
assert json.loads(ok.stdout)["ok"] is True
bad = subprocess.run([sys.executable, str(HERE/"duckai_prompt_guard.py"), str(HERE/"sample_bad.txt")],
capture_output=True, text=True)
assert bad.returncode == 1
assert len(json.loads(bad.stdout)["hits"]) >= 2
第 3 步:运行
. [100%]
1 passed in 0.05s
{
"ok": true,
"reasons": [],
"hits": []
}
{
"ok": false,
"reasons": [
"possible password_like",
"possible cn_id_like",
"possible bank_card_like"
],
"hits": [
"password_like",
"cn_id_like",
"bank_card_like"
]
}
怎么检查结果?
- ok → exit 0。
- bad → hits ≥ 2。
调试示例
「已经匿名了所以可以贴 Key」。 不行。上游模型提供方仍会看到明文;删掉敏感段再问。
常见问题
| 现象 | 原因 | 处理 |
|---|---|---|
| 误报银行卡 | 长数字可能命中 | 人工复核 |
| 企业机密 | 同样不要贴 | 用私有部署模型 |
| Plus/Pro | 更多模型与额度 | 见帮助页,不编造价格 |
模板规则
- 先 guard 再粘贴。
- 命中即改写。
- 日志勿保存原文敏感段。
项目资源
| 用途 | 地址 |
|---|---|
| 帮助说明 | https://duckduckgo.com/duckduckgo-help-pages/duckai/ |
| 隐私政策 | https://duckduckgo.com/privacy |
| Duck.ai | https://duck.ai |
隐私产品也救不了你主动粘贴的密码。