🔐 我们对你做了什么
✓
券商/客户/产品名 → 加密 token,每次请求不同
原文: 中国国际金融股份有限公司
发给 LLM: [ENT_a3f8b2_000]
下次请求: [ENT_9c2e1f_000] ← 完全不同的 token,LLM 服务商无法关联
✓
PDF 文件本身 → 不发给 LLM
只在浏览器内用 PDF.js 抽文本,LLM 看到的是纯文本。
✓
API Key → 走浏览器直连,不入我们的服务器
你填的 API key 存在浏览器 localStorage,PositionLens 服务端永远看不到。
✓
LLM 响应反解 → 浏览器内立刻完成,明文不持久化
LLM 返回 JSON 后,浏览器立刻把 [ENT_xxx] 替换回真实身份,反解表丢弃。
✓
持仓数据 → 本地 IndexedDB(模式 A) 或 AES-GCM 加密上传(模式 B)
模式 B:服务端只见密文,密钥由你的密码在浏览器派生。
⚠️ 我们主动不收集
- ❌ 不记 IP 地址
- ❌ 不记 User-Agent / 来源页
- ❌ 不用你的持仓/文档训练任何模型
- ❌ 不外发任何持仓数据(除非你点"切换到模式 B")
⚠️ 一个权衡: 金额数字不加密
发到 LLM 的文本里,金额数字保留原值(如 9,786,860.31),不加密。
为什么?
实测发现 LLM 会简化数字(例如把 29,039.0000 改成 29039),如果金额也用 token 化, LLM 输出的数字就跟原始 token 对不上,反解失败。
为什么可以接受?
LLM 看到"金额 1000 万"不知道"是谁的钱"。真正的隐私问题是"这是 XX 私募的客户",这部分已经被 token 化保护。
每请求 salt · 防关联攻击
威胁:如果用固定 token([BROKER_1_<salt>]),LLM 服务商能看到前缀 BROKER_1 一致,推断"这是同一券商的多次请求",累加推断总敞口。
解决:salt 影响整个 token([ENT_<salt>_<idx>]),每次请求都完全不同的字符串。
💡 实现细节:每次请求 crypto.getRandomValues(3 bytes) 生成 6 hex chars salt,反解表仅存在请求内存中,请求结束丢弃。
模式 A vs 模式 B
| 维度 | 模式 A · 浏览器本地 | 模式 B · E2E 加密云同步 |
|---|---|---|
| 数据存储 | IndexedDB(浏览器) | IndexedDB + 服务端密文 |
| 多端同步 | ❌ 手动导出/导入 | ✅ 自动 |
| 服务端可见 | 任何东西都不传 | 只见 AES-GCM 密文 |
| 密码丢了 | N/A(无密码) | ⚠️ 数据永久丢失 |
| 适合谁 | 单设备用户 | 多设备切换用户 |