Agent 把用户地址发给了模型,数据脱敏该在哪一层做
上线前的最后一眼,我在日志里看到用户完整的收货地址、手机号,还有一段自述病史。这些内容来自用户主动输入,被原样拼进了提示词,发给了模型服务商,又原样落进了日志系统。没人做错什么,每一步都按设计运行,只是设计里没有脱敏这一层。
这件事在纯文本应用里也有,Agent 让它更严重。因为 Agent 会把上下文反复送进模型,一次输入可能被发送十几轮,还会被写进摘要、写进工具参数、写进 trace。
先分清两种数据
一类是模型必须看见的,比如用户问「我这个订单能不能改地址」,模型要知道订单存在、是什么状态,但不需要知道收货人是谁、电话多少。这类数据做最小化,能给订单号就不给地址。
另一类是模型不该看见的,比如身份证号、银行卡号、完整手机号、病史。这类做替换,进模型之前换成占位符,工具需要真实值时再从受限存储里取。
判断标准可以简单一点,这条信息模型拿去推理,会改变它的决策吗。不会的,就不给。
脱敏放在哪一层
放在入口最省事,用户输入进来先脱敏,后面整条链路都是干净的。缺点是工具可能需要原始值,比如查订单要用手机号后四位,脱敏后就查不了。
放在出口更灵活,模型调用前才脱敏,工具调用给原始值。缺点是链路中间态里还有原始数据,容易漏。写日志、写缓存、写队列,每个出口都得处理一遍。
务实的做法是双保险。入口做基础脱敏,标记出敏感字段,让它们走单独的通道。出口再按用途决定给谁原文。两道都做,漏一处的后果可控。
识别靠什么
正则够用一大半。手机号、身份证、银行卡、邮箱都有固定格式,正则加校验位能挡住绝大部分。地址和姓名难一些,靠字段名和上下文,输入框的 label 往往就写着收货人、联系电话。
模型也能用来识别,让小模型判断一段文本里有没有个人信息。代价是延迟和成本,还有误判。适合处理自由文本字段,比如用户留言、工单描述。别用它处理结构化字段,正则比它准。
占位符要可逆
替换成什么很关键。直接删掉,模型就不知道那里有个值,可能答非所问。换成统一占位符,比如手机号换成手机号占位符,模型知道这里有个手机号但看不到内容,工具层再按占位符替换回真实值。
映射表要跟着请求走,不能全局共享,否则并发请求会串数据。生命周期跟请求一致,请求结束就销毁。
日志里的那条线
日志是最容易漏的出口。诊断需要看完整输入,法务要求不能留个人信息,这两件事冲突。解法是分层,明细日志脱敏后保留,原始数据加密后单独存,密钥和访问权限独立管控,需要时凭审批解密。
留存期也要写进制度。诊断明细留两到四周足够定位问题,聚合指标可以留更久但不含原文。
第三方调用要重新看一遍
数据发出去之后就不在自己手里了。模型服务商的数据留存政策、是否用于训练、日志保留多久,这些都得在选型时确认。有些服务商提供不留存选项,成本可能高一点,但涉及个人信息的业务值得开。
工具侧同理。第三方 API 收到的参数、我们发出的回调内容,都算数据出境。清单列出来,一个一个确认。
结合系列
会话隔离篇讲的是别让 A 用户看到 B 用户的数据,这篇讲的是别让模型和日志看到不该看的数据,两个维度互补。工具设计篇的参数最小化在这里变成数据最小化,能不给就不给。可观测性要有 trace,trace 里存的就是这些数据,脱敏和可观测性得一起设计。安全护栏篇管的是模型能做什么,这篇管的是模型能看见什么。