业务接 AI 前,先别急着调模型,先做输入脱敏层
很多业务系统接 AI,第一步就错了。
不是模型选错,也不是 Prompt 写得不好,而是把原始数据直接丢给模型。
客户聊天记录,直接丢。
工单详情,直接丢。
合同内容,直接丢。
数据库日志,直接丢。
用户手机号、邮箱、订单号,也顺手带进去。
然后团队还会说一句:
“我们只是内部测试。”
这句话最危险。
因为 AI 接入业务后,真正的风险往往不是它答错,而是它看到了不该看的东西。
Google I/O 2026 之后,Google 把 AI 和 Agent 能力继续往搜索、开发工具和生产应用方向推进;开发者更新里也强调,从 prompt 到 production-ready application 的工具链正在变得更完整。
AI 越接近生产流程,输入层就越不能裸奔。
尤其是企业场景。
客服系统里有客户姓名、手机号、地址。
CRM 里有销售记录、报价、合同状态。
日志系统里可能有 token、cookie、内部接口。
财务系统里有订单金额、发票、付款信息。
代码仓库里有配置、密钥、内部域名。
这些东西不应该原样进入模型。
所以业务接 AI 前,第一层应该不是 Prompt,而是 Input Sanitizer,也就是输入脱敏层。
它要做三件事:
识别敏感信息。
替换敏感信息。
记录脱敏映射,但不要暴露给模型。
一个简单结构可以这样设计:
```python
from dataclasses import dataclass
from enum import Enum
from typing import Dict, List
import re
import uuid
class SensitiveType(str, Enum):
PHONE = "phone"
EMAIL = "email"
ID_CARD = "id_card"
TOKEN = "token"
ORDER_ID = "order_id"
API_KEY = "api_key"
@dataclass
class SensitiveMatch:
sensitive_type: SensitiveType
raw_value: str
placeholder: str
```
然后定义一些基础规则:
```python
PATTERNS = {
SensitiveType.PHONE: r"1[3-9]\d{9}",
SensitiveType.EMAIL: r"[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+",
SensitiveType.API_KEY: r"(sk|api|key)_[a-zA-Z0-9]{16,}",
SensitiveType.TOKEN: r"Bearer\s+[a-zA-Z0-9._-]+",
}
```
扫描文本:
```python
def scan_sensitive(text: str) -> List[SensitiveMatch]:
matches = []
for sensitive_type, pattern in PATTERNS.items():
for item in re.findall(pattern, text):
placeholder = f"[{sensitive_type.value}_{uuid.uuid4().hex[:8]}]"
matches.append(
SensitiveMatch(
sensitive_type=sensitive_type,
raw_value=item,
placeholder=placeholder
)
)
return matches
```
替换敏感信息:
```python
def sanitize_input(text: str) -> Dict:
matches = scan_sensitive(text)
sanitized = text
mapping = {}
for match in matches:
sanitized = sanitized.replace(match.raw_value, match.placeholder)
mapping[match.placeholder] = {
"type": match.sensitive_type.value,
"raw_value": match.raw_value
}
return {
"sanitized_text": sanitized,
"mapping": mapping
}
```
比如输入:
```text
客户手机号 13812345678,邮箱 test@example.com,反馈订单 ORDER_20260521001 无法支付。
```
脱敏后应该变成:
```text
客户手机号 [phone_xxxxxxxx],邮箱 [email_xxxxxxxx],反馈订单 ORDER_20260521001 无法支付。
```
这时模型仍然能理解问题:
这是一个客户反馈。
涉及手机号和邮箱。
问题是订单无法支付。
但模型不需要看到真实手机号和邮箱。
如果涉及订单号,也可以继续做占位:
```python
ORDER_PATTERN = r"ORDER_\d+"
def sanitize_order_id(text: str):
return re.sub(ORDER_PATTERN, "[order_id]", text)
```
输入脱敏层还要配合风险等级。
不是所有内容都只是替换一下就能送给模型。
可以分三层:
低风险:公开资料、普通文案、通用问题。
中风险:客户问题摘要、内部流程、非敏感业务数据。
高风险:身份证、银行卡、合同原文、密钥、生产日志、未脱敏客户数据。
高风险内容应该直接拦截,而不是脱敏后继续丢给模型。
```python
BLOCKED_TYPES = {
SensitiveType.API_KEY,
SensitiveType.TOKEN,
SensitiveType.ID_CARD,
}
def should_block(matches: List[SensitiveMatch]) -> bool:
return any(match.sensitive_type in BLOCKED_TYPES for match in matches)
```
完整处理:
```python
def prepare_ai_input(text: str) -> Dict:
matches = scan_sensitive(text)
if should_block(matches):
return {
"ok": False,
"reason": "blocked_sensitive_data",
"message": "输入包含高风险敏感信息,已阻止发送给 AI。"
}
result = sanitize_input(text)
return {
"ok": True,
"sanitized_text": result["sanitized_text"],
"mapping_count": len(result["mapping"])
}
```
注意,mapping 不应该传给模型。
mapping 只应该留在业务系统内部,用于必要的结果回填。
但这里还要补一句:真实工程里不能只靠正则。
正则适合识别手机号、邮箱、token、订单号这类格式稳定的字段,但很多敏感信息没有固定格式。
比如:
“客户住在台北信义区某某路附近”。
“这位客户是我们重点续约对象”。
“这段日志来自生产支付接口”。
“这个合同条款还没有对外公开”。
“这是内部报价,不要外传”。
这些内容不是简单正则能识别的。
所以更成熟的做法是三层组合:
第一层,规则识别。
用于手机号、邮箱、token、API key、身份证号、订单号。
第二层,模型分类。
让模型只判断“这段文本是否包含敏感业务信息”,而不是直接处理全文任务。
第三层,人工标注。
把误判和漏判样本沉淀成规则,让脱敏层不断迭代。
可以加一个分类结果:
```python
@dataclass
class RiskClassification:
risk_level: str
reason: str
suggested_action: str
```
例如:
```python
def classify_risk_with_model(text: str) -> RiskClassification:
# 这里实际工程中应调用内部合规模型或受控分类服务
# 返回 low / medium / high
return RiskClassification(
risk_level="medium",
reason="文本包含客户投诉内容,但未发现账号密码或密钥。",
suggested_action="sanitize_then_process"
)
```
这样系统就不是“只靠正则硬扫”,而是规则、分类、人工反馈一起工作。
这就是很多 Demo 和真实业务系统的区别。
Demo 只关心模型能不能回答。
业务系统要关心模型看到了什么。
前期比较不同模型对脱敏文本的理解能力,可以用 gpt57.com 跑同一批 sanitized input,看哪个模型在信息缺失的情况下仍然能准确分类、总结、生成回复草稿。但上线时,是否脱敏、是否拦截、是否记录审计,必须由自己的系统控制。
AI 接业务前,先别急着调模型。
先保证模型看不到不该看的东西。
这比 Prompt 更基础。
