用户一次操作可能产生散落在 prompts、traces、向量库、工具日志等十余处数据,需设计完整的删除管道才能真正满足合规与信任要求。
删除按钮容易做,真正的删除难得多。
这个差距在 AI Agent 中比普通应用更重要,因为一次用户操作就能将数据分散到提示词、调用链路、记忆存储、向量索引、工具日志、临时文件、模型网关、重试队列和埋点事件中。如果你的产品只删除了可见的聊天记录,用户可能已从 UI 中消失,但他们的数据仍残留在五个后端系统中。
对于 AI 应用开发者来说,这不只是合规任务,而是信任功能。用户会更快原谅回答慢,但无法原谅一个声称"已删除"但保留了足够上下文可在之后重建对话的系统。
本指南展示如何设计一个 AI Agent 数据删除管道,真正移除用户数据、证明发生了什么,同时避免在执行过程中破坏生产工作流。
传统删除通常从已知记录开始:用户、项目、文件、消息或数据库中的一行。AI Agent 创造了一个更混乱的形态。
一次 Agent 执行可能包括:
其中一些记录对用户可见,许多则不是。
这就是为什么"删除聊天"不够。Agent 删除需要一张地图,列出用户数据可能落到的每个位置,加上一个工作流,根据每个位置的风险和法律保留规则删除、涂黑或立墓碑。
危险模式是这样的:
用户点击删除。
应用从侧边栏移除对话。
后端保留调用链路、嵌入、提示词和工具日志用于调试。
恢复指针、支持导出、分析查询或向量搜索仍能揭示旧内容。
从产品团队角度看,项目没了。从用户角度看,删除承诺已兑现。从系统角度看,只是隐藏了。
AI 使这更糟,因为删除的内容可能间接重现:
真正的删除意味着移除数据路径,而不只是 UI 路径。
在编写删除代码之前,列出每个存储层。将这个清单保存在代码库中,而不是某人的脑子里。
一个有用的清单表如下:
| 存储层 | 内容类型 | 数据示例 | 删除动作 |
|---|---|---|---|
| 主数据库 | 消息、文件、元数据 | 对话、用户、项目 | 硬删除或墓碑 |
| 向量存储 | 嵌入 | conversation chunks | 按 lineage_id 删除 |
| 对象存储 | 上传文件、工件 | PDF、截图 | 删除对象 |
| 工具调用日志 | 结构化事件 | API 调用、参数 | 移除内容,保留元数据 |
| 记忆存储 | 事实、摘要 | 用户偏好、总结 | 重新计算或删除 |
| 提示词缓存 | 渲染后的提示词 | 系统提示词 + context | 使缓存失效 |
| 消息队列 | 待处理任务 | 计划任务、重试 | 取消并清除 |
| 分析事件 | 埋点数据 | 页面浏览、点击 | 匿名化或聚合 |
关键列是"删除动作"。不是每条记录都用相同方式处理。
例如,账单可能需要保留"发生了 12 次模型调用"这样的非内容记录。但不应保留原始提示词。可观测性可以保留延迟、token 计数、模型名称和错误码,同时丢弃消息文本和工具载荷。
当系统找不到相关记录时,删除就会失败。修复方法简单但常被跳过:给每个用户拥有的数据对象一个血缘 ID。
血缘 ID 将根对象连接到所有派生对象。
type DataLineage = {
tenantId: string;
userId: string;
subjectType: "conversation" | "file" | "agent_run" | "memory";
subjectId: string;
lineageId: string;
};
每个派生记录都应携带该血缘 ID:
type AgentTraceEvent = {
traceId: string;
lineageId: string;
tenantId: string;
runId: string;
step: "retrieve" | "model_call" | "tool_call" | "approval";
payloadRef?: string;
redactionState: "raw" | "redacted" | "deleted";
createdAt: string;
};
不要只依赖指向可见聊天的外键。AI 系统经常在主应用数据库外创建记录。向量存储、对象存储桶和模型网关可能不知道你的对话 schema。但它们可以知道血缘 ID。
一个常见错误是把删除当作全有或全无。这会产生两种糟糕结果:
相反,将记录拆分为内容和元数据。
内容包括提示词、回复、检索到的片段、上传的文本、截图、工具参数和记忆事实。
元数据包括时间戳、角色 ID、token 计数、模型名称、状态码、成本总计、审批状态、删除收据 ID 和策略决策。
当删除请求到达时,内容应被移除或不可逆地涂黑。如果需要安全、账单、法律或运营原因,最小化元数据可以保留。
删除安全的示例事件:
{
"event_id": "evt_93",
"lineage_id": "lin_abc",
"tenant_id": "tenant_7",
"run_id": "run_42",
"event_type": "model_call",
"content_state": "deleted",
"model": "primary-chat-model",
"input_tokens": 1840,
"output_tokens": 420,
"created_at": "2026-08-17T03:32:00Z",
"deleted_at": "2026-08-17T03:41:00Z",
"deletion_receipt_id": "del_771"
}
注意缺失的内容:没有提示词、没有回复、没有检索片段、没有工具结果。
删除请求应创建一个持久化的删除作业。不要试图在一个 HTTP 请求内删除所有内容。
使用如下工作流:
一个简单的收据 schema:
create table deletion_receipts (
id text primary key,
tenant_id text not null,
requested_by text not null,
subject_type text not null,
subject_id text not null,
lineage_id text not null,
status text not null,
targets jsonb not null,
created_at timestamptz not null default now(),
completed_at timestamptz
);
每个目标应跟踪自己的状态:
{
"target": "vector_store",
"action": "delete_by_lineage_id",
"status": "completed",
"records_matched": 18,
"records_remaining": 0
}
这为开发者和支持团队提供了一种安全的方式来回答"用户删除时发生了什么?"而不再次暴露已删除的数据。
AI Agent 通常是长时间运行的。删除请求可能在 Agent 仍在使用即将被删除的上下文时到达。
删除开始时:
如果跳过这一步,工作器可能在删除作业完成后重新创建已删除的数据。
一个简单的运行时检查:
async function assertLineageIsActive(lineageId: string) {
const deletion = await db.deletionReceipts.findActive(lineageId);
if (deletion) {
throw new Error(`Lineage ${lineageId} is under deletion`);
}
}
在检索、模型调用、记忆写入和工具执行前调用此函数。
不要通过运行相似性搜索用户文本来删除嵌入。那样做慢、不完整且有风险。
每个向量片段应包含元数据:
{
"chunk_id": "chunk_22",
"tenant_id": "tenant_7",
"source_type": "conversation",
"source_id": "conv_99",
"lineage_id": "lin_abc",
"created_by_run_id": "run_42"
}
然后删除是确定性的:
await vectorStore.delete({
tenantId,
filter: { lineage_id: lineageId }
});
删除后,对该 lineage ID 运行元数据查找。结果应该为零。不要问模型数据是否消失。问存储系统。
Agent 记忆很棘手,因为它通常存储摘要而不是精确的源文本。
如果记忆摘要来自十个对话,其中一个被删除,你可能不知道哪句话来自哪个源,除非你跟踪了溯源。
type MemoryFact = {
factId: string;
tenantId: string;
userId: string;
text: string;
sourceLineageIds: string[];
state: "active" | "stale" | "deleted";
};
如果 sourceLineageIds 包含已删除数据和活跃数据,不要保持旧句子不变。从活跃源重建它或删除它。
这就是许多 AI 系统泄露已删除数据的地方:原始聊天没了,但"用户偏好季度营收图表"这条记忆还在,因为它被复制到了个人资料摘要中。
你的删除管道可以控制你的系统,但可能无法删除模型提供商内部的每个临时副本。
这意味着需要在删除请求发生前建立清晰的数据路由规则:
不要承诺超出架构所能提供的。如果提供商为滥用监控日志保留了固定窗口,请在内部策略和用户条款中说明。
当删除承诺精确时,信任就会提升。
删除应该像支付或认证一样被测试。
创建一个带有已知标记文本的合成用户:
DELETE_TEST_MARKER_7f3a9b
运行一个正常的 Agent 工作流:
然后触发删除,并断言标记从每个内容层消失。
一个粗糙但有效的测试:
const surfaces = await collectDebugSurfaces({ tenantId, marker });
for (const surface of surfaces) {
if (surface.content?.includes(marker)) {
throw new Error(`Deletion marker found in ${surface.name}`);
}
}
这个测试会捕获以后会变成严重问题的无聊泄露。
用户不需要看到你的向量存储目标列表。他们需要一个真实的状态。
对于开发者工具,管理删除收据可以显示目标类别而不暴露已删除内容。
从最高风险的存储层开始。
这个顺序在构建完整删除系统的同时提升信任。
什么是 AI Agent 数据删除管道?
AI Agent 数据删除管道是一个后端工作流,跨提示词、调用链路、嵌入、记忆、缓存、文件、工具日志、队列和埋点删除或涂黑用户数据。它比删除可见的聊天记录更完整。
只删除对话够吗?
通常不够。对话可能创建了派生记录,如向量片段、记忆摘要、调用链路载荷、工具结果、提示词缓存和工件。这些需要单独的删除或涂黑步骤。
AI 日志应该硬删除吗?
内容重的日志通常应该快速删除、涂黑或过期。当需要账单、滥用预防、安全或法律原因时,可以保留最小的运营元数据。将内容与元数据分离,这样你不会意外保留原始提示词。
如何安全删除嵌入?
为每个向量片段存储源元数据,如 tenant_id、source_id 和 lineage_id。按元数据过滤器删除,然后验证该 lineage ID 没有剩余片段。不要依赖相似性搜索来删除。
删除期间 Agent 正在运行会发生什么?
删除工作流应该取消排队的工作、撤销活跃工作器租约、阻止新工具调用,并阻止该 lineage ID 的记忆写入。否则,Agent 可能在删除作业完成后重新创建数据。
如果使用第三方模型 API,能承诺永久删除吗?
只有当提供商合同和保留设置支持该承诺时。许多团队应该使用更精确的措辞:从活跃产品系统中删除,在适用的有限提供商或安全保留范围内。
应该添加的第一个删除测试是什么?
创建一个带有唯一标记的合成提示词,让 Agent 完成一个正常工作流,删除它,然后在每个内容层搜索该标记。如果标记出现在任何存储用户内容的地方,管道就不完整。
AI 删除不是设置页功能,而是数据架构功能。
如果 Agent 可以读取、转换、记忆、检索和操作用户数据,那么删除必须遵循相同的路径。目标很简单:当用户要求移除他们的数据时,你的系统应该知道它去了哪里,阻止它被重用,删除可以删除的,涂黑必须保留的,并产生一个你可以信任的收据。