联系我们
首页 › 技术博客 › Attune 设计哲学:为什么我们不做又一个云端 AI SaaS

Attune 设计哲学:为什么我们不做又一个云端 AI SaaS

✍️ Attune 核心团队 📅 ⏱️ 12 分钟阅读 📊 中级 工程

TL;DR

  • 我们调研了 20+ 款 “AI 知识库” SaaS,发现一个共同问题:所谓”端到端加密”在数据出域那一刻就破功了
  • Attune 的解法是结构性私有性:原始文件留在本地 SQLite + AES-256-GCM 字段加密,embedding 也本地生成,只有用户主动 Chat 时才出网,且必经 12 类 PII 脱敏
  • 代价:Rust 二进制 ~30 MB,安装包 ~150-200 MB(含 Ollama runtime + whisper.cpp + tesseract),工程量是云端 SaaS 的 3 倍
  • 回报:用户拿审计日志(CSV 导出)就能向法务证明”什么时候出了多少 token,每个 token 都被脱敏过”

为什么我们抗拒云端范式

过去 3 年我们仔细看过的”私有 AI 知识库”产品名单包括 Mem.ai / Notion AI / Glean / Perplexity Spaces 等等。它们的架构惊人地一致:

浏览器/桌面 → 上传文档到 cloud → 服务端 chunk → 服务端 embed → 服务端向量库
用户 query → 服务端检索 → 拼上下文喂云端 LLM → 流式返回

“端到端加密”通常意味着传输层 TLS + 静态 AES。但这两层加密在服务端处理那一刻必然解密 — 否则没法 chunk、没法 embed、没法检索。换句话说:

任何能”理解”你内容的服务,必然在某一刻持有过你内容的明文。

这不是技术能解决的,是物理学。所以”我们不会偷看你的数据”只能是承诺,不是结构性保证。律师 / 医生 / 政府机关不能基于承诺签合规文件。

Attune 的三层数据保护

第一层:本地 vault (AES-256-GCM 字段加密)

所有原始内容(文件正文 / 批注 / 对话历史)存在本地 SQLite。两个关键字段走 AES-256-GCM 字段级加密:

// attune-core/src/store/items.rs
pub fn create_item(
    tx: &Transaction,
    title: &str,
    content: &str,         // 明文
    cipher: &Aes256Gcm,
) -> Result<i64> {
    let nonce = Nonce::from_slice(&thread_rng().gen::<[u8; 12]>());
    let ciphertext = cipher
        .encrypt(nonce, content.as_bytes())
        .map_err(|_| StoreError::EncryptFailed)?;

    tx.execute(
        "INSERT INTO items (title, content_ciphertext, content_nonce) \
         VALUES (?1, ?2, ?3)",
        params![title, ciphertext, nonce.as_slice()],
    )?;
    Ok(tx.last_insert_rowid())
}

Master key 由 Argon2id(password, device_secret, m=64MB, t=3, p=4) 派生:设备丢失 + 数据库被读取 + 密码未泄露 → 解密失败。

Argon2id 参数选择我们踩过两次坑:

  1. m=128MB / t=4:Tier T1 设备(8GB RAM)解锁要 8 秒,用户体验差
  2. m=16MB / t=2:能在 2 秒内解锁,但 GPU 暴力破解强度不够

最终落地 m=64MB / t=3 / p=4:~3 秒解锁、GPU 暴力破解需要每个 hash 64MB 内存(4090 也只能并行 ~300 个),是一个工程上能用、安全上够强的折中。

第二层:本地索引

索引实现量级备注
全文搜索tantivy 0.22 + tantivy-jieba100K 文档 ~50ms p95BM25
向量搜索usearch HNSW + F16 量化100K × 1024d ~120MB跟 LLM 共存
embedding 生成Ollama bge-m3 (本地)32 chunks/批 ~8s详见 bge-m3 选型

embedding 也在本地 — 这是关键。如果 embedding 走 OpenAI text-embedding-3-large API,原文 chunk 仍然出域了,前面的字段加密就白做了。

第三层:脱敏代理(出网时)

只有当用户主动点 Chat 按钮时才出网。出网前经过 12 类格式化 PII 自动脱敏:

用户 prompt:
  "分析这份合同的风险点"
实际发到云 LLM 的内容:
  "分析这份合同的风险点。当事人:[PERSON_1] 与 [ORG_1],
   签订日期 [DATE_1],保证金 [MONEY_1]..."
云 LLM 返回:
  "合同风险...涉及当事人 [PERSON_1] 应注意..."
解码后展示给用户:
  "合同风险...涉及当事人 张三 应注意..."

还原映射 {[PERSON_1]: "张三"} 只在内存里,永不持久化、永不出网。云 LLM 看不到原文,也无法逆推映射。

工程复杂度的真实代价

云端 SaaSAttune
后端 LoC~2K Python (FastAPI + Pinecone)~30K Rust (axum + tantivy + usearch + AES-GCM)
二进制大小N/A (服务端)30 MB Rust + 150 MB 安装包(捆绑 Ollama / whisper.cpp / tesseract)
加密开销TLS onlyTLS + AES-GCM 字段加密 (~5% CPU 开销)
跨平台编译不需要x86_64 Linux/Windows + aarch64 (K3 一体机)
部署运维我们运维用户运维(自动更新 + 健康检查)

工程量大约是云端 SaaS 的 3 倍。但这是值得的 — 因为客户拿到的不是 “trust us”,是可证明的私有性。

与 Attune Enterprise 的关系

Attune 是个人的私有 AI;Attune Enterprise 是律所 B2B 团队的自托管 SaaS。两者数据完全隔离 — Attune 的 vault 不与任何外部产品同步,包括 Attune Enterprise。同一律所同时用两者,需要用户主动 export / import 完成迁移。

这种”工程上独立 + 战略上配套”是经过深思熟虑的:私有 AI 必须是真私有,不能因为商业方便就拉一根隐藏的同步线。如果用户”装了 Attune 就被自动联到 Attune Enterprise 后台”,那”私有 AI” 就成了笑话。

后续阅读

相关阅读

· 15 分钟

12 类格式化 PII 自动脱敏:从正则到 ONNX NER 的分层防御

L0 正则 + 校验码(ISO 7064 / Luhn)+ L1 用户字典 + L3 LLM 语义脱敏。三层防御 + 可逆 [KIND_N] 占位符 + 出网审计日志,让"原文不出网"从口号变成可验证的事实。本文给出完整的 Rust 正则 + 校验码代码,以及为什么我们没有选 ML NER 而是用了 80/15/5 的成本感知分层。

阅读全文 →
· 18 分钟

F-Pro 四阶段跨域污染防御:从 0.60 → 0.80 的完整复盘

法律赛道 MRR 0.60 → 0.80(+33%)。F-Pro 不是一个算法,是 4 个阶段叠加的防御:domain 字段 + chunk 前缀 + 跨域惩罚 + 关键词意图识别。零 LLM 调用,10ms 内出结果。本文复盘了 5 轮调参 sweep,包括为什么 penalty=0.4 而不是 0.2 或 0.6,以及一个意外发现:Rust 赛道 MRR 顺带从 0.87 → 1.00。

阅读全文 →