
企业微信

飞书
选择您喜欢的方式加入群聊

扫码添加咨询专家
在 AI 数据分析场景中,数据安全是头等大事。企业数据中往往包含敏感信息:用户手机号、身份证号、银行卡号、地址等。如何在保证 AI 推理准确性的同时,保护这些敏感数据不被泄露?
AskTable 的 SDI(Secure Data Inference)技术,通过 字段级脱敏 + Faker 生成 + Vault 映射 的方案,实现了高性能、高安全的数据脱敏。
本文将深入剖析这套方案的设计与实现。
场景:用户问"手机号为 138xxxx1234 的用户购买了什么?"
传统方案:
# 直接将敏感数据传递给 LLM prompt = f"数据:{dataframe.to_string()}\n问题:{question}"
风险:
完全脱敏:
# 将敏感字段替换为 *** df["phone"] = "***"
问题:
加密:
# 加密敏感数据 df["phone"] = encrypt(df["phone"])
问题:
class IdentifiableType(StrEnum): """敏感字段类型""" NONE = "none" # 非敏感 PHONE = "phone" # 手机号 ID_CARD = "id_card" # 身份证号 EMAIL = "email" # 邮箱 ADDRESS = "address" # 地址 NAME = "name" # 姓名 BANK_CARD = "bank_card" # 银行卡号
元数据标记:
field = { "name": "phone", "type": "VARCHAR", "identifiable_type": "phone", # 标记为敏感字段 }
class SecureDataFrame: """安全数据框架:支持脱敏的 DataFrame""" def __init__(self, df: pd.DataFrame, vault: Vault): self.df = df self.vault = vault # 映射关系管理器 def to_str(self, anonymize: bool = True) -> str: """转换为字符串,支持脱敏""" if not anonymize: return self.df.to_string() # 脱敏处理 anonymized_df = self.df.copy() for col in self.df.columns: if self.vault.is_sensitive(col): anonymized_df[col] = anonymized_df[col].apply( lambda x: self.vault.anonymize(col, x) ) return anonymized_df.to_string()
class Vault: """映射关系管理器:原值 ↔ 假值""" def __init__(self): self._forward_map: dict[str, dict[str, str]] = {} # 原值 -> 假值 self._reverse_map: dict[str, dict[str, str]] = {} # 假值 -> 原值 self._faker = Faker("zh_CN") def anonymize(self, field: str, value: str) -> str: """脱敏:原值 -> 假值""" if value in self._forward_map.get(field, {}): return self._forward_map[field][value] # 生成假值 fake_value = self._generate_fake_value(field, value) # 保存映射关系 if field not in self._forward_map: self._forward_map[field] = {} self._reverse_map[field] = {} self._forward_map[field][value] = fake_value self._reverse_map[field][fake_value] = value return fake_value def deanonymize(self, field: str, fake_value: str) -> str: """还原:假值 -> 原值""" return self._reverse_map.get(field, {}).get(fake_value, fake_value) def _generate_fake_value(self, field: str, value: str) -> str: """根据字段类型生成假值""" field_type = self._get_field_type(field) if field_type == "phone": return self._faker.phone_number() elif field_type == "email": return self._faker.email() elif field_type == "name": return self._faker.name() elif field_type == "address": return self._faker.address() elif field_type == "id_card": return self._faker.ssn() else: return "***"
async def query_with_sdi(question: str, datasource: DataSourceAdmin) -> QueryResult: """带 SDI 的查询流程""" # 1. 生成 SQL sql = await generate_sql(question, datasource) # 2. 执行 SQL,获取原始数据 raw_df = await datasource.execute_sql(sql) # 3. 创建 Vault vault = Vault() for field in datasource.get_sensitive_fields(): vault.register_field(field.name, field.identifiable_type) # 4. 创建 SecureDataFrame secure_df = SecureDataFrame(raw_df, vault) # 5. 脱敏后传递给 LLM(用于生成解释) anonymized_data = secure_df.to_str(anonymize=True) explanation = await generate_explanation(question, anonymized_data) # 6. 返回原始数据给用户(不脱敏) return QueryResult( sql=sql, dataframe=raw_df, # 原始数据 explanation=explanation, # 基于脱敏数据生成的解释 )
from faker import Faker faker = Faker("zh_CN") # 生成假手机号 fake_phone = faker.phone_number() # 输出:138-1234-5678 # 生成假姓名 fake_name = faker.name() # 输出:张伟 # 生成假地址 fake_address = faker.address() # 输出:北京市朝阳区建国路88号 # 生成假邮箱 fake_email = faker.email() # 输出:zhangwei@example.com
关键点:
Faker("zh_CN") 生成中文数据# 示例:脱敏手机号 vault = Vault() vault.register_field("phone", "phone") # 原值 -> 假值 fake1 = vault.anonymize("phone", "138-1234-5678") # 输出:156-7890-1234 # 相同原值 -> 相同假值(保持一致性) fake2 = vault.anonymize("phone", "138-1234-5678") # 输出:156-7890-1234(与 fake1 相同) # 假值 -> 原值 original = vault.deanonymize("phone", "156-7890-1234") # 输出:138-1234-5678
关键点:
| 方案 | 性能 | 准确性 | 可逆性 | 部署成本 |
|---|---|---|---|---|
| Faker | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| 本地小模型 | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐ |
| 加密 | ⭐⭐⭐⭐⭐ | ⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| 完全脱敏 | ⭐⭐⭐⭐⭐ | ⭐ | ⭐ | ⭐⭐⭐⭐⭐ |
性能:
# Faker 生成速度 import time start = time.time() for _ in range(10000): faker.phone_number() print(f"耗时:{time.time() - start:.2f}s") # 输出:耗时:0.15s(10000 次生成)
准确性:
可逆性:
部署成本:
问题:
结论:对于脱敏场景,Faker 是更优选择。
# 原始数据 df = pd.DataFrame({ "name": ["张三", "李四"], "phone": ["138-1234-5678", "139-8765-4321"], "amount": [1000, 2000], }) # 创建 Vault vault = Vault() vault.register_field("phone", "phone") # 脱敏 secure_df = SecureDataFrame(df, vault) anonymized_str = secure_df.to_str(anonymize=True) # 输出(脱敏后) """ name phone amount 0 张三 156-7890-1234 1000 1 李四 157-1234-5678 2000 """ # 传递给 LLM explanation = await generate_explanation(question, anonymized_str) # 返回给用户(原始数据) return df # 包含真实手机号
# 原始数据 df = pd.DataFrame({ "name": ["张三", "李四"], "phone": ["138-1234-5678", "139-8765-4321"], "email": ["zhangsan@example.com", "lisi@example.com"], "address": ["北京市朝阳区", "上海市浦东新区"], }) # 创建 Vault vault = Vault() vault.register_field("name", "name") vault.register_field("phone", "phone") vault.register_field("email", "email") vault.register_field("address", "address") # 脱敏 secure_df = SecureDataFrame(df, vault) anonymized_str = secure_df.to_str(anonymize=True) # 输出(脱敏后) """ name phone email address 0 王伟 156-7890-1234 wangwei@example.com 广州市天河区 1 刘洋 157-1234-5678 liuyang@example.com 深圳市南山区 """
class Vault: def __init__(self): self._cache: dict[str, str] = {} # 缓存映射关系 def anonymize(self, field: str, value: str) -> str: cache_key = f"{field}:{value}" if cache_key in self._cache: return self._cache[cache_key] fake_value = self._generate_fake_value(field, value) self._cache[cache_key] = fake_value return fake_value
效果:
def anonymize_batch(self, field: str, values: list[str]) -> list[str]: """批量脱敏""" return [self.anonymize(field, v) for v in values]
效果:
# 每个会话使用独立的 Vault session_vaults: dict[str, Vault] = {} def get_vault(session_id: str) -> Vault: if session_id not in session_vaults: session_vaults[session_id] = Vault() return session_vaults[session_id]
效果:
class EncryptedVault(Vault): def __init__(self, encryption_key: str): super().__init__() self.cipher = Fernet(encryption_key) def save_to_disk(self, path: str): """加密保存映射表""" data = json.dumps(self._forward_map) encrypted_data = self.cipher.encrypt(data.encode()) with open(path, "wb") as f: f.write(encrypted_data)
效果:
AskTable 的 SDI 技术,通过 Faker + Vault + SecureDataFrame 的组合,实现了:
✅ 高安全性:敏感数据不暴露给 LLM ✅ 高性能:Faker 生成速度快(< 1ms) ✅ 高准确性:不影响 AI 推理质量 ✅ 低成本:无需 GPU,部署简单
相关阅读:
技术交流: