Agent 工程体系 · 第 78/98 篇。内容以 2026 年 9 月可验证的公开规范和稳定接口为基线;框架版本敏感能力会明确标注,不把实验行为写成通用保证。
Agent 数据隐私:最小采集、脱敏、保留、跨境、导出和删除
Agent 的隐私问题,不只是“模型会不会泄露聊天内容”。一个可调用工具、读取知识库、保存记忆、生成审计日志的 Agent,实际上会让同一份数据经过多个系统:
flowchart LR
U[用户或业务主体] --> G[接入网关]
G --> P[隐私策略与数据分类]
P --> M[会话编排器]
M --> D1[模型服务]
M --> D2[工具服务]
M --> D3[记忆存储]
M --> D4[RAG与向量库]
M --> L[审计日志]
D1 --> O[输出过滤]
D2 --> O
D3 --> O
D4 --> O
O --> U
X[导出/删除请求] --> I[身份核验]
I --> T[数据资产索引]
T --> Q[删除任务队列]
Q --> D3
Q --> D4
Q --> L
Q --> B[备份与副本]
Q --> E[删除证据]
隐私保护的目标不是让系统“完全不接触数据”,而是让每一次接触都满足四个条件:
- 有明确目的;
- 只处理完成该目的所需的数据;
- 数据只在必要的范围和时间内存在;
- 能够解释数据去了哪里,并能够导出或删除。
这也是为什么 Agent 隐私必须覆盖采集、推理、工具调用、记忆、检索、日志、备份和供应商处理,而不能只配置一个“不要训练用户数据”的模型选项。OWASP 将个人信息、财务信息、健康记录、商业机密、凭据和法律文书等都列为可能导致敏感信息披露的对象,并指出系统提示词中的限制可能被提示注入绕过。(genai.owasp.org)
一、先确定隐私边界:什么是“数据”,谁是“主体”
1. 数据不是只有用户输入
在 Agent 系统中,至少存在以下数据类型:
| 数据类型 | 示例 | 常见存储位置 |
|---|---|---|
| 原始输入 | 用户问题、上传文档、语音转写 | API 网关、对象存储 |
| 推理上下文 | system prompt、历史消息、检索片段 | 编排器、模型供应商 |
| 工具数据 | CRM 记录、订单、邮件、数据库结果 | 工具服务、缓存 |
| 记忆 | 用户偏好、长期事实、摘要 | 记忆库、向量库 |
| 派生数据 | embedding、分类标签、风险评分 | 向量库、特征库 |
| 输出数据 | 回复、生成的文件、工具执行结果 | 会话库、文件存储 |
| 运维数据 | IP、设备标识、trace、错误堆栈 | 日志平台、监控系统 |
| 审计数据 | 身份、指令、工具、决策、审批记录 | 审计库、不可变存储 |
| 备份数据 | 数据库快照、日志归档、磁带副本 | 备份系统、灾备区域 |
**派生数据仍然是隐私资产。**例如,删除了原始文本,却保留了可以区分用户兴趣、疾病、财务状况的 embedding,不能简单地说“原文已经删除,所以隐私风险消失”。
向量和 embedding 的生成、保存、检索过程都可能引入泄露、跨租户访问和 embedding inversion 风险。OWASP 明确要求向量存储具备细粒度权限控制和逻辑隔离,并建议记录检索活动。(genai.owasp.org)
2. 数据主体不一定等于登录用户
一次请求可能涉及多个主体:
员工 A 请求总结客户 B 的邮件
客户 B 是邮件内容中的数据主体
员工 A 是操作发起者
企业 C 是业务控制者或数据管理方
模型供应商 D 是外部处理方
因此,删除请求不能只用 user_id 过滤。至少还要考虑:
- 谁发起了请求;
- 请求针对哪个主体;
- 该主体是否拥有数据控制权;
- 数据是否包含第三方信息;
- 是否存在保留义务、争议、审计或安全调查;
- 是否已经进入模型训练、统计聚合或不可逆匿名化流程。
“用户删除自己的账户”与“删除所有与该用户有关的数据”不是同一个操作。前者是账户生命周期事件,后者是数据主体范围判定。
二、最小采集:不是少存字段,而是让数据集合服从目的
1. 最小采集的形式化定义
设:
- 是业务目的,例如“回答订单状态”;
- 是系统可采集的数据集合;
- 是完成目的 所需的最小数据集合;
- 是使用数据集合 的隐私风险;
- 是数据对任务质量的贡献。
理想的数据选择可以写成:
约束条件是:
其中:
- 是实际允许进入流程的数据;
- 是业务可接受的最低效果;
- 表示组织对隐私风险的敏感程度。
直觉是:不能为了追求“模型可能更聪明”而无限扩大数据范围。只要删除某字段不会使业务目的失败,就应当不采集,或者在进入模型前移除。
2. 推导一个完整例子
目标:客服 Agent 回答“我的订单什么时候发货”。
原始请求可能是:
{
"user_id": "u-1001",
"name": "张三",
"phone": "13800138000",
"address": "浙江省杭州市……",
"order_id": "o-9001",
"order_items": ["机械键盘"],
"message": "我的订单什么时候发货?"
}
逐步判断字段:
order_id用于查询订单状态,属于必要字段;message用于确认用户意图,属于必要字段;user_id可能用于授权检查,但不一定需要交给模型;name对“发货时间”没有贡献;phone对查询订单状态没有贡献;address对查询订单状态没有贡献;order_items只有在回答商品相关问题时才需要。
因此,模型实际接收的数据可以是:
{
"request_context": {
"authorized_order_id": "o-9001"
},
"message": "我的订单什么时候发货?"
}
而数据库查询可以由受控工具完成:
SELECT order_id, shipping_status, estimated_ship_at
FROM orders
WHERE order_id = :order_id
AND owner_user_id = :authenticated_user_id;
模型看到的是工具返回的最小结果:
{
"order_id": "o-9001",
"shipping_status": "已出库",
"estimated_ship_at": "2026-09-03"
}
这里有一个重要边界:**最小采集不等于让模型直接查询数据库。**身份验证和数据授权应在工具服务中执行,而不是让模型自行决定用户是否有权限。OWASP 将过度代理能力归因于过多功能、过多权限和过高自治,并建议在下游系统实施完整授权,而不是依赖 LLM 判断是否允许操作。(genai.owasp.org)
3. 采集前应建立“字段—目的—去向”表
一条数据进入系统前,应至少能回答:
| 字段 | 目的 | 是否进模型 | 是否进记忆 | 是否进日志 | 保留期限 |
|---|---|---|---|---|---|
order_id |
查询订单 | 脱敏后 | 否 | 允许记录引用 | 30 天 |
phone |
身份核验 | 否 | 否 | 仅记录哈希 | 认证会话期间 |
message |
理解问题 | 是 | 仅用户同意后 | 默认摘要 | 会话结束后 7 天 |
shipping_status |
生成回复 | 是 | 否 | 允许记录枚举值 | 30 天 |
trace_id |
故障定位 | 否 | 否 | 是 | 90 天 |
如果一个字段没有明确的目的、接收方和保留期限,它就不应默认进入全链路。
三、脱敏:改变数据的可识别性,不是简单替换几个字符
1. 脱敏的四种结果
脱敏是对原始数据进行变换,使接收方无法获得不必要的敏感信息。常见方式不同,安全性质也不同。
删除或截断
13800138000 -> 138****8000
浙江省杭州市西湖区某路 -> 浙江省杭州市
适合展示和日志,但不适合需要精确匹配的业务。
掩码
掩码保留部分结构:
身份证号:3301**********1234
订单号:o-9***
掩码不是匿名化。若其他字段能够缩小候选范围,掩码后的信息仍然可能识别个人。
令牌化
把敏感值替换为随机令牌:
张三 -> <PERSON_1>
13800138000 -> <PHONE_1>
映射关系保存在独立的令牌保险库中:
token original_value purpose expires_at
---------- ------------------- ------------ -------------------
<PHONE_1> 13800138000 order_query 2026-09-01T10:30:00Z
模型只处理令牌。只有确实需要发送短信的工具,才在受控边界内解析令牌。
哈希或加密摘要
phone_hash = HMAC-SHA256(tenant_secret, phone)
带密钥的 HMAC 适合做精确匹配或去重。普通 SHA-256 对低熵字段并不安全,因为攻击者可以枚举电话号码、邮箱和身份证号进行字典攻击。
**加密不是脱敏。**加密数据只要密钥仍然可用,通常仍可恢复原文;它解决的是机密性,不自动解决最小化、用途限制或删除问题。
2. 脱敏必须在正确的边界发生
错误流程:
用户输入
-> 原文写入日志
-> 原文写入会话库
-> 原文发送给模型
-> 最后才在前端掩码
前端掩码只能改变用户看到的内容,不能阻止后端、模型供应商、日志系统或备份系统获得原文。
更合理的流程是:
接收请求
-> 识别字段和敏感类型
-> 授权与目的检查
-> 令牌化/删除
-> 记录最小审计摘要
-> 调用模型或工具
-> 输出检查
-> 按不同保留策略写入不同存储
3. 一个可运行的 Python 令牌化示例
下面示例使用内存字典演示机制。生产环境应将令牌保险库替换为独立服务,并使用 KMS 管理加密密钥、限制解令牌权限和记录访问审计。
import re
import uuid
from dataclasses import dataclass
from datetime import datetime, timedelta, timezone
PHONE_RE = re.compile(r"(?<!\d)1[3-9]\d{9}(?!\d)")
@dataclass
class TokenRecord:
value: str
purpose: str
expires_at: datetime
class TokenVault:
def __init__(self):
self._records = {}
def put(self, value: str, purpose: str, ttl_seconds: int = 1800) -> str:
token = f"<PHONE_{uuid.uuid4().hex[:10]}>"
self._records[token] = TokenRecord(
value=value,
purpose=purpose,
expires_at=datetime.now(timezone.utc)
+ timedelta(seconds=ttl_seconds)
)
return token
def resolve(self, token: str, purpose: str) -> str:
record = self._records[token]
if record.purpose != purpose:
raise PermissionError("token purpose mismatch")
if record.expires_at <= datetime.now(timezone.utc):
raise PermissionError("token expired")
return record.value
def sanitize_message(message: str, vault: TokenVault) -> str:
def replace(match):
return vault.put(match.group(0), purpose="order_query")
return PHONE_RE.sub(replace, message)
vault = TokenVault()
raw = "请查询 13800138000 的订单状态"
safe = sanitize_message(raw, vault)
print(safe)
预期输出类似:
请查询 <PHONE_7f3a12c9ab> 的订单状态
这个实现演示了三个关键点:
- 模型输入中不出现原始电话号码;
- 令牌绑定用途
order_query; - 令牌有过期时间。
但它仍然有边界:
- 正则无法覆盖所有姓名、地址、证件和自由文本实体;
- 令牌本身可能成为关联标识,不能无限期保存;
- 如果模型输出令牌,输出层必须决定是否允许还原;
- 如果日志记录了令牌保险库内容,脱敏仍然失败;
- 如果同一个令牌跨不同租户复用,可能形成跨租户关联。
四、记忆、RAG 和 embedding:脱敏不能停在聊天输入
Agent 记忆通常分为三类:
- 短期记忆:当前会话上下文;
- 长期记忆:用户偏好、稳定事实、历史摘要;
- 外部知识:企业文档、数据库记录、工单、邮件和文件。
三者的隐私策略不同。
1. 不是所有对话都应写入长期记忆
例如:
用户:我这周在看胃病,帮我找附近医院。
如果系统把它自动总结为:
用户长期偏好:患有胃病,关注消化科医院。
就发生了语义升级:一次临时问题被转换成长期健康画像。
长期记忆写入至少应经过:
候选事实
-> 判断是否稳定
-> 判断是否必要
-> 判断是否敏感
-> 检查用户同意和用途
-> 写入结构化记忆
比自由文本更容易治理的记忆结构是:
{
"subject_id": "u-1001",
"memory_type": "response_preference",
"value": "prefer_concise_answers",
"source_event_id": "evt-20260901-001",
"purpose": "personalize_response",
"consent_id": "consent-42",
"expires_at": "2026-12-01T00:00:00Z"
}
这里保存的是偏好,而不是整段原始对话;保存了来源事件,因此后续可以追溯和删除。
2. embedding 不能当作匿名副本
下面这种设计存在明显问题:
原始文档 -> embedding -> 向量库
原始文档删除
删除原文后,embedding 仍可能:
- 被相似度检索返回;
- 通过元数据暴露文档范围;
- 在多租户过滤错误时泄露给其他用户;
- 被尝试反演出部分源信息;
- 在备份和索引副本中继续存在。
正确的删除对象应是一个数据资产集合:
subject_id
-> 原始消息
-> 摘要记忆
-> 文档分块
-> embedding
-> 检索缓存
-> 生成文件
-> 训练/评测数据副本
-> 审计引用
-> 备份索引
向量库的检索条件必须同时包含租户、主体和授权域:
SELECT chunk_id, content
FROM document_chunks
WHERE tenant_id = :tenant_id
AND subject_scope @> :authorized_scopes
AND deleted_at IS NULL
ORDER BY embedding <=> :query_embedding
LIMIT 5;
实际数据库语法会随 PostgreSQL 扩展或向量数据库产品而变化;这里重要的不是具体操作符,而是权限过滤必须参与检索条件,而不是检索后再过滤。否则未授权数据可能已经进入模型上下文。
五、保留:每条数据都要有终止条件
1. 保留期不是“数据库永不过期”
保留期是数据从产生到应当删除、匿名化或转入受控归档的时间范围。它必须绑定具体目的:
会话原文:用于提供当前服务,保留 7 天
故障日志:用于排查服务问题,保留 30 天
安全审计:用于追踪高风险操作,保留 180 天
发票记录:依业务和适用规则确定
备份副本:随备份轮换周期清除
“为了以后可能有用”不是充分的保留目的。
一个保留策略可以表达为:
data_class: conversation_content
purpose: provide_service
retention:
active: 7d
archive: 0d
backup: backup_cycle
deletion:
mode: hard_delete
propagate_to:
- primary_db
- cache
- vector_store
- object_store
- analytics
- backup
2. 用状态机处理保留和删除
stateDiagram-v2
[*] --> ACTIVE: 写入
ACTIVE --> EXPIRED: 到达保留期
ACTIVE --> DELETE_REQUESTED: 收到删除请求
DELETE_REQUESTED --> DELETING: 身份核验通过
DELETE_REQUESTED --> REJECTED: 无权/范围不明
DELETING --> DELETED: 全部目标完成
DELETING --> PARTIAL_FAILURE: 某副本失败
PARTIAL_FAILURE --> DELETING: 重试或人工处理
ACTIVE --> LEGAL_HOLD: 合法保留条件成立
LEGAL_HOLD --> ACTIVE: 保留条件解除
LEGAL_HOLD --> DELETING: 解除后执行删除
关键是不要把 DELETE FROM conversations 当成删除完成。删除完成应满足:
其中 是受影响的存储系统集合。只要主库已删、缓存未删,或者原文已删、向量未删,整体状态就不是 DELETED,而是 PARTIAL_FAILURE。
3. 软删除和硬删除的区别
软删除通常是设置:
deleted_at = CURRENT_TIMESTAMP
优点是可恢复、事务成本低;缺点是:
- 查询错误可能重新返回数据;
- 搜索索引仍可能命中;
- 备份中仍保留原文;
- 数据库管理员仍可读取。
硬删除是物理移除记录及相关对象。它更接近真正删除,但需要处理:
- 级联引用;
- 异步索引;
- 缓存;
- 对象存储版本;
- 消息队列中的待处理任务;
- 备份和灾备副本。
生产系统通常使用“软删除隔离 + 异步硬删除”的组合,但必须定义最终完成条件,而不是把软删除状态永久保留。
六、跨境:首先是数据流路由问题,其次才是加密问题
跨境处理是指数据从一个司法辖区、区域或受约束的数据域传输到另一个区域,或者被位于另一地区的供应商、人员、模型服务和运维系统访问。
以下情况都可能属于跨域风险,需要在组织规则和适用法律下判定:
- 国内服务调用境外模型 API;
- 境外供应商在境外保存请求和响应;
- 境外客服或工程师访问日志;
- 备份复制到另一地区;
- 监控平台收集原始请求;
- 模型供应商进行跨地区故障排查;
- 通过第三方 SaaS 处理包含个人信息的附件。
TLS 保护传输,不等于没有跨境。
数据库加密,不等于没有跨境。
脱敏,也不等于天然不受跨境规则约束。
跨境网关应在调用前做数据域判断:
ALLOWED_REGIONS = {
"cn-mainland": {"cn-mainland"},
"global-public": {"cn-mainland", "us", "eu"},
}
def can_route(data_class: str, source_region: str, target_region: str) -> bool:
return target_region in ALLOWED_REGIONS[data_class]
实际系统中不能只用静态集合,还应检查:
- 数据分类;
- 主体所在区域;
- 业务租户策略;
- 供应商处理位置;
- 是否允许供应商保留;
- 是否允许用于训练或改进服务;
- 是否支持导出和删除;
- 备份区域;
- 运维和技术支持访问区域;
- 合同和组织审批状态。
更稳妥的架构是:
原始敏感数据
-> 区域内工具服务处理
-> 返回最小化、令牌化结果
-> 区域内或获准区域的模型推理
例如,订单 Agent 不需要把姓名、手机号和完整地址发送给模型。区域内授权服务先完成身份校验和订单查询,模型只接收:
{
"shipping_status": "已出库",
"estimated_ship_at": "2026-09-03"
}
跨境策略是组织合规要求的工程实现,不应被误写成某个框架的保证。NIST AI RMF 的定位是帮助组织管理 AI 对个人、组织和社会的风险,并明确其为自愿使用的框架;其生成式 AI Profile 用于识别生成式 AI 的特有风险和管理措施。它提供风险管理方法,不替代具体司法辖区的法律判断。(nist.gov)
七、导出:导出的不是一张聊天表,而是主体可理解的数据包
数据导出是把与某个主体有关、且在导出范围内的数据,以可读、可处理的形式交付给有权请求者。
一个 Agent 的导出包至少可以包含:
manifest.json
conversations/
memories/
documents/
generated_files/
consents/
tool_actions/
audit_references/
但审计日志不能无条件原样导出。日志可能包含:
- 其他用户的信息;
- 内部系统提示词;
- 访问令牌;
- 安全规则;
- 第三方商业机密;
- 不应向请求者暴露的检测结果。
因此导出过程通常分为:
- 身份核验;
- 确认主体和范围;
- 查询数据资产索引;
- 去除无关第三方数据;
- 脱敏内部字段;
- 固定一致性快照;
- 生成可读和机器可读格式;
- 记录导出事件;
- 设置下载链接过期时间;
- 删除导出临时文件。
导出的时间点也很重要。若导出任务运行期间仍有并发写入,结果可能不一致。常见做法是使用数据库快照或事件序号:
snapshot_event_id = 98120
读取所有 event_id <= 98120 的数据
导出完成后,再提示 98121 之后产生的数据未包含在本次快照中
不能把“当前查询返回的几张表”冒充完整导出。记忆、向量、对象存储和生成文件往往不在同一个数据库中,必须依靠数据资产索引关联它们。
八、删除:可追溯删除需要知道“删了什么、删到哪里、还剩什么”
1. 删除请求的生命周期
sequenceDiagram
participant S as 主体
participant API as 隐私 API
participant IAM as 身份服务
participant IDX as 数据资产索引
participant Q as 删除队列
participant DB as 主库/记忆库
participant V as 向量库
participant OBJ as 对象存储
participant BK as 备份系统
participant AUD as 审计系统
S->>API: 提交删除请求
API->>IAM: 核验身份和权限
IAM-->>API: 核验通过
API->>IDX: 查询主体关联资产
IDX-->>API: 返回资产清单和版本
API->>Q: 创建删除任务
Q->>DB: 删除消息、记忆、索引
Q->>V: 删除文档块和 embedding
Q->>OBJ: 删除文件和对象版本
Q->>BK: 标记备份删除/等待轮换
Q->>AUD: 写入最小删除证据
Q-->>API: 返回任务状态
API-->>S: 异步任务编号
删除接口不应同步等待所有系统完成,因为向量索引、对象存储和备份通常是异步系统。接口返回的应是:
{
"request_id": "del-20260901-001",
"status": "accepted",
"scope": "subject:u-1001"
}
之后通过任务状态查询:
{
"request_id": "del-20260901-001",
"status": "partial_failure",
"completed": [
"conversation_db",
"memory_db",
"vector_store"
],
"pending": [
"backup_2026_08_31"
],
"last_error": "backup provider accepted tombstone; physical expiry follows rotation policy"
}
2. 删除与备份
备份有三种处理方式:
方式一:立即改写备份
安全性最好,但对不可变备份、磁带和对象锁定存储通常不可行。
方式二:加密擦除
每个租户或数据域使用独立数据密钥。删除时销毁密钥,使备份中的密文不可恢复。
这要求:
- 密钥确实只用于该数据范围;
- 没有明文副本;
- 密钥备份也被纳入删除策略;
- 密钥销毁过程有独立审计证据。
方式三:备份轮换后过期
保留备份中的数据直到备份自然过期,同时在在线系统设置删除标记,恢复时不得把已删除数据重新导入生产。
这不是“已经立刻物理删除”,而是“在线副本立即隔离,备份副本按轮换周期完成清除”。产品文档和用户响应必须如实区分这两种状态。
3. 删除证据不能重新制造隐私泄露
审计系统需要证明删除发生过,但不应保存被删除的原文。可保存:
{
"request_id": "del-20260901-001",
"subject_ref": "hmac:u-1001",
"asset_type": "conversation",
"asset_count": 42,
"deletion_started_at": "2026-09-01T03:00:00Z",
"deletion_completed_at": "2026-09-01T03:00:08Z",
"result": "completed",
"executor": "privacy-worker-v3",
"evidence_hash": "..."
}
subject_ref 可以使用带密钥的 HMAC,而不是直接写入用户 ID。证据哈希用于证明记录未被篡改,但哈希本身不应被误解为原数据已经不可识别。
九、一个最小数据模型
以下 SQL 展示了数据资产索引、保留期、删除状态和来源关联。示例使用 PostgreSQL 风格语法:
CREATE TABLE privacy_assets (
asset_id UUID PRIMARY KEY,
tenant_id TEXT NOT NULL,
subject_ref TEXT NOT NULL,
asset_type TEXT NOT NULL,
storage_system TEXT NOT NULL,
storage_key TEXT NOT NULL,
purpose TEXT NOT NULL,
classification TEXT NOT NULL,
region TEXT NOT NULL,
consent_id TEXT,
created_at TIMESTAMPTZ NOT NULL,
retain_until TIMESTAMPTZ,
deleted_at TIMESTAMPTZ,
deletion_status TEXT NOT NULL DEFAULT 'active',
source_event_id TEXT,
metadata JSONB NOT NULL DEFAULT '{}'
);
CREATE INDEX privacy_assets_subject_idx
ON privacy_assets (tenant_id, subject_ref, deletion_status);
CREATE INDEX privacy_assets_expiry_idx
ON privacy_assets (retain_until)
WHERE deletion_status = 'active';
删除任务应以资产为单位执行,而不是只删除业务主表:
UPDATE privacy_assets
SET deletion_status = 'deleting'
WHERE tenant_id = :tenant_id
AND subject_ref = :subject_ref
AND deletion_status = 'active';
任务处理完成后:
UPDATE privacy_assets
SET deletion_status = 'deleted',
deleted_at = CURRENT_TIMESTAMP
WHERE asset_id = :asset_id
AND deletion_status = 'deleting';
如果底层系统失败,不要直接写成 deleted:
UPDATE privacy_assets
SET deletion_status = 'failed',
metadata = metadata || jsonb_build_object(
'last_error', :error,
'retryable', :retryable
)
WHERE asset_id = :asset_id;
一个常见错误是先删除业务数据,再删除资产索引。这样一旦索引删除失败,系统将失去“还有哪些副本”的清单。更安全的顺序是:
建立资产索引
-> 执行删除
-> 验证底层删除
-> 更新资产状态
-> 生成删除证据
资产索引本身也包含主体引用,因此它需要独立的保留和访问控制。
十、并发、重试和故障路径
1. 删除与新写入并发
假设用户在 t1 发起删除请求,删除任务在 t2 开始;但另一个会话在 t2 到 t3 之间继续写入一条新的长期记忆。如果删除任务只扫描一次,就会遗漏这条记忆。
解决方法之一是引入主体删除屏障:
ACTIVE
-> DELETE_REQUESTED
-> 禁止新增长期记忆和新建导出副本
-> 扫描并删除现有资产
-> 验证没有新写入
-> DELETED
或者使用事件序号:
delete_start_event = 98120
delete_barrier_event = 98135
删除任务必须处理:
event_id <= 98120:请求前已有数据
98120 < event_id <= 98135:删除期间产生的数据
event_id > 98135:删除完成后才允许产生,或拒绝写入
2. 重试必须幂等
删除任务可能因为网络错误被执行多次,因此底层删除接口应满足:
删除已不存在的对象 -> 成功
重复设置 deleted_at -> 成功
重复写入删除证据 -> 使用 request_id 去重
不要使用“删除成功后再发送一次不可重放的破坏性命令”而没有幂等键。
3. 删除失败的诊断顺序
当系统报告“已删除,但检索仍能召回”时,按以下路径排查:
- 主库是否仍有原始记录;
- 会话缓存是否仍有旧响应;
- 文档分块是否仍存在;
- embedding 是否仍在向量库;
- 检索过滤是否遗漏
deleted_at; - 搜索索引是否尚未刷新;
- 是否存在异步队列中的旧任务;
- 是否从备份恢复过旧数据;
- 模型是否只是生成了相似内容,而不是检索到了原文;
- 日志或评测集是否仍包含原始片段。
“模型还记得”是一个含糊诊断。必须先确认内容来源是:
当前上下文
还是 RAG 结果
还是长期记忆
还是缓存
还是日志注入
还是模型自身的训练知识
应用层可以删除前四类在线资产,但不能把供应商模型的通用参数记忆、训练数据处理和删除能力假设为应用方自动可控。是否进入训练、能否删除或排除,取决于供应商条款、产品配置和具体合同,不能仅凭 API 调用结果推断。
十一、审计记录如何支持隐私,而不是扩大泄露面
隐私治理需要审计,但审计日志本身是高价值数据。Agent 审计至少要记录:
谁:
subject、actor、service identity、tenant
做了什么:
指令、工具调用、数据库操作、审批结果
对什么做:
数据资产引用、资源 ID、权限域
为什么做:
purpose、policy version、consent reference
结果如何:
success、failure、denied、partial
何时何地:
timestamp、region、trace_id
是否可验证:
event hash、链式序号、签名或不可变存储引用
但不应默认记录完整 prompt、完整工具响应和完整模型输出。更小的审计记录可以是:
{
"event_type": "tool_call",
"actor_ref": "user-hmac:...",
"tool": "order.read_status",
"resource_ref": "order-hmac:...",
"purpose": "provide_service",
"policy_version": "privacy-policy-2026-09",
"input_digest": "hmac:...",
"result": "allowed",
"region": "cn-mainland",
"trace_id": "tr-001"
}
这里的 input_digest 用于关联和完整性验证,而不是让审计人员恢复输入。需要重现故障时,应使用有权限的短期调试捕获,并经过审批、脱敏和自动过期,而不是永久保存所有原文。
不可抵赖记录也不等于必须公开原文。不可抵赖关注的是:
- 谁产生了事件;
- 事件是否被篡改;
- 事件顺序是否可信;
- 系统是否能证明某次授权或删除确实发生。
它不要求审计日志包含最多的数据。
十二、常见误解和反例
误解一:模型供应商承诺“不训练”,所以可以发送原文
“不用于训练”只覆盖某一类用途,不能自动解决:
- 供应商日志保留;
- 人工支持访问;
- 区域位置;
- 备份副本;
- 请求内容泄露;
- 工具服务和应用日志中的副本。
误解二:把邮箱替换成 <EMAIL> 就完成了隐私保护
如果系统仍然保存:
用户 ID + 时间 + 完整地址 + 唯一订单 + 特殊病史
攻击者可能通过组合字段重新识别主体。脱敏应按场景评估重识别风险,而不是只统计替换了多少个正则匹配。
误解三:只删除会话表就完成删除
Agent 的原文可能已被:
摘要任务
embedding 任务
缓存
分析平台
导出文件
备份快照
复制。删除必须基于资产索引和数据血缘。
误解四:把个人数据导出后打包下载
如果压缩包没有短期有效期、访问控制和导出审计,导出动作本身会制造新的泄露副本。导出文件应具有自己的 asset_id 和保留期限。
误解五:让模型决定是否可以读取数据
模型可以提出工具调用,但不应成为最终授权者。恶意文档、邮件或网页中的间接提示注入,可能诱导 Agent 读取并外传本不应访问的数据。过度代理的根因正包括过多权限和过高自治。(genai.owasp.org)
十三、生产验收应验证行为,而不是只检查配置
至少应设计以下测试。
最小采集测试
输入包含姓名、电话、地址和订单号,断言:
模型请求中没有姓名、电话、地址
工具请求中只有经过授权的订单号
日志中没有原始手机号
跨域路由测试
将数据分类设为 restricted-cn,模拟目标模型区域为 us,断言:
请求在路由层被拒绝
没有产生供应商请求
拒绝事件进入审计日志
RAG 隔离测试
租户 A 写入文档,租户 B 使用相同关键词检索,断言:
召回结果为空
模型上下文不包含租户 A 的 chunk
向量数据库审计记录显示过滤条件包含租户 B
删除传播测试
创建同一主体的数据副本:
主库、缓存、记忆库、向量库、对象存储、日志引用、备份索引
发起删除后逐项验证。任何一个系统仍能返回原文,都不能标记为 completed。
并发测试
删除进行期间持续写入记忆,断言:
新的长期记忆写入被拒绝,或被纳入同一删除批次
删除完成后不存在可检索的新增资产
恢复测试
从删除前备份恢复到隔离环境,执行删除墓碑重放:
restore -> load deletion tombstones -> delete matching assets -> verify
如果恢复流程会把已删除数据重新送入生产,说明删除策略只覆盖了在线系统,没有覆盖灾备生命周期。
十四、边界:什么必须由组织策略和法律确定
工程系统可以保证:
- 某字段没有发送给模型;
- 某个租户无法检索另一个租户的向量;
- 删除任务覆盖了资产索引中的存储系统;
- 删除失败会进入重试和告警;
- 导出包不包含内部凭据;
- 跨区域调用在网关被阻断。
工程系统不能单独决定:
- 某类数据在某司法辖区是否可以跨境;
- 某个保留期限是否满足法定义务;
- 某种处理是否需要同意;
- 某次安全调查是否构成合法保留;
- 供应商是否可以将请求用于训练;
- 模型参数中的信息是否具备可执行删除能力。
因此,Agent 隐私基线需要把政策配置和技术控制绑定起来:
法律/合同判断
-> 数据分类与目的
-> 路由策略
-> 模型和工具权限
-> 保留期限
-> 导出范围
-> 删除目标
-> 审计证据
如果只做加密,不做数据资产索引,无法解释数据在哪里;如果只做脱敏,不做权限控制,令牌和派生数据仍可能泄露;如果只做删除接口,不处理并发、缓存、向量和备份,删除就只是一个数据库操作。
Agent 数据隐私的核心不是“把所有数据保护起来”,而是把数据的必要性、可见性、流向、生命周期和可撤销性设计成系统状态。最小采集减少进入系统的数据,脱敏减少单个组件能看到的数据,保留策略限制数据存在的时间,跨境控制限制数据流向,导出保证主体能够理解系统保存了什么,删除则把这些约束落实到每个副本和每条数据血缘上。
系列导航与关联阅读
- 系列入口:Agent 工程完整路线:从运行循环、记忆与协议到安全、评测和生产交付
- 上一篇:Agent Secret 与网络安全:凭证代理、SSRF、DNS、重定向和出口
- 下一篇:Agent 内容安全:输入输出分类、政策、误判、升级和申诉
- 延伸:Agent 记忆隐私与删除:同意、保留期、可追溯删除和备份
- 延伸:Agent 审计与合规:身份、指令、工具、数据、决策和不可抵赖记录
官方资料
本文依据 Agent、模型、协议与框架官方资料重新梳理;正文、示例与生产清单由 WR BLOG 编写。

评论
0 条讨论