
企业微信

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

扫码添加咨询专家
在 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,部署简单
相关阅读:
技术交流: