使用免费 LLM API 时,粘贴到输入框的客户数据、数据库名等信息可能已外传;文章提供了 Python 脚本实现发送前数据过滤与回归测试。
下午 4 点 47 分。你把免费模型端点接入内部问答机器人。第一次测试,成功。第二次测试,也成功。然后你往上一翻,看见之前粘贴的问题摘要——客户邮箱、VPN 客户端 ID、一条提及生产库名的支持备注。所有这些刚刚就这样出了你的网络。账单上写着 ¥0。代价不是。
免费 token 是一场交易,不是赠礼。把第三方模型当成本地 grep 用,你就在签署一份看不见的数据处理协议。
Disclosure:本文是 MonkeyCode 产品推广的一部分。
MonkeyCode 不断出现在我的评测列表里,因为它同时提供两个诱人的优惠:一个是标着 10M token 额度的免费模型套餐,另一个是面向小团队的免费服务器选项。我喜欢这个组合。我对它的怀疑程度和喜欢程度一样高。免费服务器意味着编排器运行在你控制的地方,但它调用的模型依然是远程 API。那才是你需要守住的边界。
这篇教程是一道三层隔离闸门。在你把任何内容粘贴进 MonkeyCode 或其他模型代理之前,跑一遍它。最终产物是一个轻量 Python 脚本加一个可以拿来就用的回归测试。
发送任何东西之前,先分类。不是用电子表格,而是用规则。
如果提示词里包含中间列的值,就脱敏。如果包含"永不发送"的值,就阻断。硬性停止。不要让"采样不算"这种想法覆盖这条规则。
这是闸门。它从标准输入读取 JSON payload,扫描提示词,发现风险内容时以非零退出码退出。
#!/usr/bin/env python3
'''quarantine.py - local preflight gate for remote model prompts.'''
import json
import re
import sys
DENY_PATTERNS = {
'aws_access_key': r'AKIA[0-9A-Z]{16}',
'github_token': r'gh[pousr]_[A-Za-z0-9]{36,255}',
'private_key': r'-----BEGIN [A-Z ]*PRIVATE KEY-----',
'email': r'[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}',
'vpn_client_id': r'(?i)\bvpn.{0,20}(?:user|login|id)\s*[:=]\s*\S+',
'connection_string': r'(?i)(?:postgres|mysql)://[^\s]+',
}
def scan(text):
hits = {}
for name, pattern in DENY_PATTERNS.items():
count = len(re.findall(pattern, text))
if count:
hits[name] = count
return hits
def main():
payload = json.load(sys.stdin)
text = payload.get('prompt', '')
hits = scan(text)
if hits:
print('BLOCKED:', json.dumps(hits), file=sys.stderr)
return 1
print(json.dumps(payload))
return 0
if __name__ == '__main__':
sys.exit(main())
把它接入你的请求链路:
python quarantine.py < prompt.json \
&& curl -s https://your-model-endpoint/v1/chat \
-H "Authorization: Bearer $MONKEYCODE_TOKEN" \
--data @prompt.json
把 payload 挡在你的 shell 历史记录之外。从文件读取 JSON,不要从命令行传入。
模型不是数据泄露的唯一场所。你的网关日志是第二个。只记录状态和延迟:
curl -s -o /dev/null -w "%{http_code} %{time_total}\n" \
--data @prompt.json https://your-model-endpoint/chat
永远不要记录请求体。永远不要记录完整响应。永远不要记录 Authorization header。检查你的反向代理是否有默认访问日志会回显 JSON body——nginx 只记路径,不记 body,但你的编排器可能会。
临时脚本只是祈祷,测试才是边界。下面是一个最小的 pytest 套件。
import quarantine
def test_blocks_aws_key():
hits = quarantine.scan('Set env to AKIAIOSFODNN7EXAMPLE before running.')
assert hits['aws_access_key'] == 1
def test_blocks_email():
hits = quarantine.scan('Contact alice@example.com')
assert hits['email'] == 1
def test_allows_clean_query():
assert quarantine.scan('How do I rotate a TLS certificate?') == {}
def test_blocks_connection_string():
hits = quarantine.scan('USE postgres://admin:secret@db.internal:5432/prod')
assert hits['connection_string'] == 1
在每次触碰代理的 push 时在 CI 里运行它:
test-quarantine:
image: python:3.12-slim
script:
- python -m pytest test_quarantine.py -q
在项目清单里锁定解释器和 pytest 版本;fixture 只对你实际测试过的版本负责。
正向 fixture 证明闸门会触发。负向 fixture 证明它不会误拦正常操作。这种不对称才是关键:你需要的是拒绝的证据,而不是一条从不触发警报的绿色流水线。
这个闸门是 regex 栅栏,不是防火墙。它catch的是意外粘贴,不是精巧的窃取。如果你的数据真正敏感,你需要带真实上下文的分类机制,加上和模型提供商的数据处理协议。两者都不是免费的。
以下情况跳过这个方案:
也不要以为 10M token 优惠会一直维持 10M。免费套餐会变。锁定模型端点,阅读变更日志,每次项目更新时重新跑一遍闸门。
你迟早会把一个免费模型接入真实服务器。问题不在于模型有多聪明——而是在你粘贴之前 CI 闸门会拦下什么样的提示词,以及当它没拦住时谁会被通知。
如果你想在一个真正的免费套餐上试探这个边界,MonkeyCode 是一个合理的沙盒。用一个临时命名空间,先跑测试套件,把免费服务器当作一个恰好你付了 ¥0 的不可信远程来对待。