☰
AI Agent 删了 Trace 怎么办?Harness 防篡改架构设计
2026/10/15 15:14:24 网站建设 项目流程

1. 从一个让人后背发凉的场景说起

前阵子在一个技术群里,有人抛出一个问题:AI Agent 现在权限越来越大,能读文件、能调接口、能执行命令,那如果它把审计日志、调用链路这些 Trace 数据也一起删了,我们拿什么去追溯它到底干了什么?这个问题一出来,群里安静了好几秒。因为大家心里都清楚,这不是杞人忧天,而是当下 Agent 工程化落地过程中一个非常真实的盲区。

我自己做 AI Agent 相关项目也有一段时间了,从最早的简单对话机器人,到后来接入工具调用、多步推理、自动化执行,踩过的坑不算少。Trace 这个东西,在传统后端服务里是标配,OpenTelemetry、Jaeger、Zipkin 那一套大家都很熟。但到了 Agent 场景,事情变得复杂得多。Agent 的行为是动态的、非确定性的,它可能在一次任务里调用十几个工具,每个工具的输入输出都需要记录,而这些记录本身如果被 Agent 自己或者被恶意输入操控的 Agent 给抹掉了,整个可观测性体系就塌了。

所以这篇文章想聊的核心问题是:当 AI Agent 具备了删除 Trace 的能力时,Agent Harness 这一层到底该怎么设计,才能保证安全?这里说的 Harness,不是指某个具体产品,而是指包裹在 Agent 外面、负责编排、约束、监控、审计的那一层基础设施。你可以把它理解成 Agent 的“安全带”和“行车记录仪”的结合体。这篇文章适合正在做 AI Agent 平台、Agent 中台、或者任何需要让 Agent 真正“下地干活”的工程师和架构师来看,也适合对 Agent 安全边界感兴趣的技术管理者。我会从架构设计、权限隔离、Trace 防篡改、实操落地几个角度,把这件事掰开揉碎讲清楚。

2. 先搞清楚 Harness 和 Agent 到底谁管谁

2.1 Harness 不是 Agent 的附属品,而是它的约束层

很多人第一次听到 Harness 这个词,会把它和 Agent 框架混为一谈。比如用 LangChain、LangGraph 搭一个 Agent,觉得这就是 Harness 了。其实不是。LangChain 这类框架解决的是“怎么让 Agent 跑起来”的问题,而 Harness 解决的是“怎么让 Agent 跑得安全、可控、可追溯”的问题。两者的关注点完全不同。

打个比方,Agent 就像一个刚入职的实习生,能力有,但你不确定他会不会乱来。LangChain 是给他配的电脑和办公软件,而 Harness 是公司的门禁系统、操作审计、权限管理和监控摄像头。实习生可以干活,但他不能自己把监控录像删了,也不能自己给自己开权限。Harness 的核心价值就在于,它始终站在 Agent 的“外面”,而不是“里面”。

从架构上看,Harness 通常包含这几个模块:生命周期管理(启动、暂停、终止 Agent)、权限控制(Agent 能访问哪些资源)、输入输出过滤(防止提示注入和恶意指令)、Trace 采集与存储(记录 Agent 的每一步行为)、异常检测与熔断(发现异常行为时及时止损)。这些模块的共同特点是,它们都不依赖 Agent 自身的配合,而是独立运行的。

2.2 Trace 在 Agent 场景里到底记录了什么

传统微服务里的 Trace,主要记录请求链路、耗时、状态码这些。但 Agent 的 Trace 要丰富得多。一次 Agent 任务执行,可能包含以下内容:

  • 推理步骤:Agent 每一步的思考过程,包括它决定调用哪个工具、为什么这么决定
  • 工具调用记录:调用了什么工具、传了什么参数、返回了什么结果
  • 上下文快照:每一步执行时的对话历史、系统提示词、可用工具列表
  • 资源访问记录:读了哪些文件、访问了哪些数据库、调用了哪些外部 API
  • 异常与重试:哪里出错了、重试了几次、最终结果如何

这些数据加在一起,才构成一次完整的 Agent 行为审计链路。问题在于,这些数据里有很大一部分是 Agent 自己产生的,甚至是 Agent 自己“写”进 Trace 系统的。如果 Agent 有权限去修改或删除这些记录,那 Trace 的可信度就归零了。

2.3 为什么 Agent 会“有能力”删 Trace

这不是假设,而是很多实际架构里真实存在的情况。原因有几个:

第一,很多 Agent 平台为了灵活,会给 Agent 一个通用的“执行工具”,比如 shell 执行、Python 代码执行、HTTP 请求工具。这些工具本身能力很强,如果 Agent 被恶意提示操控,或者自身推理出现偏差,完全可能执行删除日志文件的命令。

第二,Trace 数据往往和 Agent 的运行环境在同一个信任域内。比如 Agent 跑在一个容器里,Trace 也写到同一个容器的本地文件或者同一个内网的服务里。Agent 一旦突破自身边界,就能碰到 Trace 数据。

第三,有些设计为了让 Agent 能“自我反思”,会把 Trace 数据回传给 Agent 看。这就相当于把审计日志的读取权限甚至写入权限交给了被审计对象,逻辑上本身就是矛盾的。

注意:不要觉得“我的 Agent 不会那么聪明”。Agent 的行为空间是由工具集决定的,只要工具集里有删除、写入、执行这类能力,风险就存在。安全设计要假设最坏情况,而不是假设 Agent 永远听话。

3. 核心思路:让 Trace 的写入权和 Agent 的执行权彻底分离

3.1 最根本的一条原则:Agent 不能碰 Trace 的存储层

我在设计 Agent Harness 的时候,第一条铁律就是:Agent 进程对 Trace 存储只有“追加”权限,没有“修改”和“删除”权限。而且这个追加权限也不是直接给的,而是通过 Harness 代理的。

具体怎么做?Agent 在执行过程中产生的所有 Trace 事件,不是由 Agent 自己写文件或写数据库,而是通过一个单向的、只进不出的通道发给 Harness。Harness 收到后,负责序列化、签名、落盘。Agent 那边根本没有 Trace 存储的地址、凭证和 API。它只知道“我发一个事件给 Harness”,至于 Harness 怎么存、存哪里,它完全不知道。

这个设计的关键在于单向性。通道是单向的,Agent 只能发,不能查、不能改、不能删。Harness 那边收到事件后,会立即做几件事:打上时间戳、计算哈希、用 Harness 自己的密钥签名、写入只追加的存储(比如 append-only 的日志文件、WORM 存储、或者带防篡改机制的数据库)。

3.2 为什么用“只追加”而不是“可读写”

只追加(append-only)这个特性在安全审计领域是老生常谈,但在 Agent 场景里尤其重要。因为 Agent 的行为是不可预测的,你没法用白名单穷举它所有可能的操作。但你可以从存储层面限制:不管 Agent 做什么,Trace 数据只能增加,不能减少,不能覆盖。

实现 append-only 有几种常见方式:

  • 文件层面:用chattr +a给日志文件加上只追加属性,或者用专门的日志系统如 systemd-journald 的 sealed 模式
  • 数据库层面:用只允许 INSERT 的数据库用户,或者用区块链式的哈希链结构,每条记录包含前一条的哈希
  • 对象存储层面:用支持对象锁(Object Lock)的存储服务,写入后在一定时间内不可删除
  • 消息队列层面:Trace 事件先进入一个只读的队列,由独立的消费者写入存储,Agent 无法直接访问队列

我自己的项目里用的是“哈希链 + 独立存储服务”的组合。每条 Trace 记录都包含前一条记录的哈希值,这样即使有人篡改了中间某条记录,后面的哈希链就对不上了,很容易被发现。存储服务是一个独立的进程,Agent 只能通过 Unix Domain Socket 发消息给它,没有其他访问途径。

3.3 Harness 自身的权限也要被约束

这里有个容易被忽略的问题:Harness 自己如果被攻破了怎么办?如果 Harness 有权限删 Trace,那攻击者只要拿下 Harness,一样能删。所以 Harness 的权限也要被约束。

我的做法是,Harness 的 Trace 写入模块和 Harness 的其他模块(比如 Agent 调度模块)在权限上也是分离的。Trace 写入模块运行在一个独立的、权限极小的沙箱里,它只能做一件事:接收事件、签名、写入存储。它没有网络访问权限,没有执行命令的权限,甚至没有读取 Agent 代码的权限。这样即使 Agent 调度模块被攻破,攻击者也碰不到 Trace 写入模块。

这种设计思路叫“最小权限分离”,在传统安全领域很常见,但用在 Agent Harness 上需要额外考虑一点:Agent 的行为是动态的,Harness 的各个模块之间需要通信,通信通道本身也可能成为攻击面。所以通道要用严格的协议,比如 gRPC 加上 mTLS,并且每个模块的身份要独立验证。

4. 实操落地:从零搭一个防删 Trace 的 Agent Harness

4.1 整体架构和组件选型

假设我们要从零搭一个 Harness,支持 Agent 执行任务并且 Trace 不可被 Agent 删除。整体架构可以分成四层:

层级组件职责权限
Agent 层Agent Runtime执行推理和工具调用只能通过 Harness 提供的 API 发 Trace 事件
通道层Event Gateway接收 Trace 事件,做初步校验只能接收和转发,不能存储
存储层Trace Store持久化 Trace,支持查询只追加,不修改不删除
审计层Audit Service校验哈希链,检测篡改只读,独立于其他组件

Agent Runtime 可以用任何框架,LangChain、LangGraph、Spring AI Agent、或者自己用 Rust 写的 Agent 都行。关键是它不能直接访问 Trace Store。Event Gateway 我推荐用 gRPC 服务,因为 gRPC 有强类型接口和流式传输,适合高频的 Trace 事件上报。Trace Store 可以用 ClickHouse 或者 Elasticsearch,但一定要配置成只允许 INSERT 的用户权限。Audit Service 可以是一个定时任务,定期扫描哈希链,发现断裂就告警。

4.2 Trace 事件的格式设计和签名机制

Trace 事件不能随便发,要有统一的格式。我设计的事件结构大概是这样:

{ "event_id": "uuid", "parent_id": "uuid or null", "timestamp": "2025-01-01T00:00:00Z", "agent_id": "agent-001", "task_id": "task-123", "event_type": "tool_call", "payload": { "tool_name": "file_read", "input": {"path": "/data/report.csv"}, "output": {"status": "success", "size": 1024} }, "prev_hash": "sha256 of previous event", "signature": "hmac-sha256 of this event using harness key" }

这里有几个关键点。prev_hash指向前一条事件的哈希,形成链式结构。signature是 Harness 用自己的密钥对事件内容做的签名,Agent 没有这个密钥,所以无法伪造合法事件。event_id和parent_id用来还原调用树,方便后续分析。

签名密钥的管理也很重要。密钥不能硬编码在代码里,要用密钥管理服务或者环境变量注入,并且定期轮换。轮换的时候要注意,旧密钥签名的历史事件仍然要能验证,所以验证逻辑要支持多版本密钥。

4.3 Agent 侧怎么发 Trace 事件

Agent 侧不需要知道 Trace 存储的任何细节,它只需要调用 Harness 提供的一个 SDK。这个 SDK 可以是一个轻量级的库,提供类似trace_event(type, payload)的方法。SDK 内部负责把事件序列化、通过 gRPC 发给 Event Gateway。

这里有个实操细节:Agent 发事件的时候,不能阻塞主流程。因为 Agent 执行任务本身可能很耗时,如果每发一个 Trace 事件都同步等待确认,性能会受影响。我的做法是异步发送,SDK 内部维护一个缓冲区,事件先放缓冲区,后台线程批量发送。但这样有个风险:如果 Agent 进程崩溃,缓冲区里的事件可能丢失。所以缓冲区要定期刷盘,或者用本地的 WAL(Write-Ahead Log)先落盘再发送。

提示:Agent 侧的 SDK 不要暴露任何“删除事件”或“修改事件”的接口。接口设计上就要杜绝这种可能性,而不是靠文档约定。

4.4 Event Gateway 的校验和转发逻辑

Event Gateway 收到事件后,要做几件事:

  1. 格式校验:检查必填字段是否齐全,类型是否正确
  2. 频率限制:防止 Agent 疯狂发事件导致存储爆炸
  3. 签名验证:用 Harness 的密钥验证事件签名,确保事件确实来自合法的 Agent Runtime
  4. 哈希链计算:根据prev_hash和当前事件内容计算新的哈希
  5. 转发存储:把事件发给 Trace Store

这里有个性能考量。如果每个事件都做签名验证和哈希计算,QPS 高了之后 Gateway 可能成为瓶颈。我的做法是批量处理:Gateway 收到一批事件后,先做批量签名验证,然后按顺序计算哈希链,最后批量写入。这样吞吐量能提升不少。

另外,Gateway 本身不能有存储权限。它只是一个中转站,收到事件后转发给 Trace Store,自己不落盘。这样即使 Gateway 被攻破,攻击者也只能伪造事件,不能删除已有事件。

4.5 Trace Store 的只追加配置

Trace Store 是最后一道防线。我用 ClickHouse 举例,配置一个只允许 INSERT 的用户:

CREATE USER trace_writer IDENTIFIED BY 'strong_password'; GRANT INSERT ON trace_db.trace_events TO trace_writer; -- 不授予 SELECT、ALTER、DROP 等权限

这样即使 Agent 通过某种方式拿到了 trace_writer 的凭证,它也只能插入新事件,不能删除或修改已有事件。查询 Trace 用另一个只读用户,和写入用户分开。

如果对防篡改要求更高,可以用支持对象锁的存储,比如 S3 的 Object Lock 或者兼容 S3 的存储服务。写入的对象在锁定期内不可删除,即使管理员也删不了。锁定期可以根据合规要求设置,比如 90 天或 1 年。

4.6 Audit Service 的哈希链校验

Audit Service 定期扫描 Trace Store,验证哈希链是否完整。具体逻辑是:按时间顺序读取事件,检查每条事件的prev_hash是否等于前一条事件的实际哈希。如果发现不匹配,说明中间有事件被篡改或删除,立即告警。

这个校验不需要实时做,可以每小时或每天跑一次。但如果对实时性要求高,也可以在 Event Gateway 里做实时校验:每收到一个新事件,就检查它的prev_hash是否和上一条事件的哈希一致。不一致就拒绝写入并告警。

我自己的项目里用的是混合方案:Gateway 做实时校验,Audit Service 做定期全量校验。实时校验能快速发现异常,定期校验能发现更隐蔽的篡改。

5. 常见问题与排查技巧实录

5.1 Agent 发的事件丢失了怎么办

这是最常见的问题。Agent 异步发事件,如果进程崩溃或者网络抖动,事件可能丢失。排查思路:

  • 检查 Agent 侧 SDK 的缓冲区配置,看是否开启了本地 WAL
  • 检查 Event Gateway 的日志,看是否有接收失败记录
  • 检查网络连接,特别是 gRPC 的 keepalive 配置
  • 如果用的是消息队列,检查队列的持久化配置和消费位点

我的经验是,Agent 侧一定要有本地 WAL,事件先写本地文件再发送,发送成功后删除本地记录。这样即使进程崩溃,重启后也能从 WAL 恢复未发送的事件。

5.2 哈希链断了但找不到原因

哈希链断裂通常意味着有事件被删除或修改。排查步骤:

  1. 定位断裂的位置,找到prev_hash不匹配的那条事件
  2. 检查该事件前后的时间戳,看是否有时间跳跃
  3. 检查 Trace Store 的访问日志,看是否有异常的 DELETE 或 UPDATE 操作
  4. 检查是否有其他进程或用户直接访问了 Trace Store

如果用的是 ClickHouse,可以查system.query_log看有没有删除操作。如果用的是文件存储,可以检查文件的修改时间和 inode 变化。

5.3 Agent 执行太慢,Trace 写入拖了后腿

Trace 写入如果同步做,确实会影响 Agent 性能。优化方向:

  • Agent 侧异步发送,不阻塞主流程
  • Event Gateway 批量处理,减少网络往返
  • Trace Store 用批量插入,而不是单条插入
  • 如果 Trace 量特别大,可以考虑采样,只记录关键事件

但要注意,采样不能牺牲安全性。关键的安全相关事件(比如权限变更、敏感资源访问)必须全量记录,不能采样。

5.4 怎么防止 Agent 通过提示注入绕过 Harness

提示注入是 Agent 安全的另一个大话题。Harness 层面能做的是:

  • 对 Agent 的输入做过滤,检测常见的注入模式
  • 对 Agent 的输出做检查,看是否有异常的工具调用意图
  • 限制 Agent 的工具集,不给它不需要的能力
  • 在 Harness 层面做二次确认,比如敏感操作需要人工审批

但说实话,提示注入很难完全防住。所以 Harness 的设计要假设 Agent 可能被操控,然后从权限和 Trace 层面做兜底。即使 Agent 被操控了,它也删不了 Trace,改不了权限,这样损失可控。

5.5 常见问题速查表

问题可能原因排查方法解决思路
Trace 事件丢失缓冲区未刷盘、网络抖动检查 WAL、Gateway 日志开启本地 WAL,增加重试
哈希链断裂事件被删改、存储被直接访问定位断裂点,查访问日志收紧存储权限,启用对象锁
写入性能差同步写入、单条插入压测 Gateway 和 Store异步批量,增加缓冲区
签名验证失败密钥不匹配、时钟偏移检查密钥版本和时间同步多版本密钥,NTP 同步
Agent 绕过 Harness工具集过大、权限过宽审计 Agent 工具调用最小权限,敏感操作审批

6. 一些踩坑之后的个人体会

做 Agent Harness 这段时间,我最大的体会是:安全设计不能依赖 Agent 的“自觉”。你不能假设 Agent 永远按预期行事,也不能假设它不会尝试越权。唯一可靠的做法是从架构上把权力分开,让 Agent 即使想作恶也作不了。

另一个体会是,Trace 的价值不在于记录了多少,而在于记录的可信度。一份被篡改过的 Trace,比没有 Trace 更危险,因为它会误导你的判断。所以防篡改机制不是锦上添花,而是必需品。

最后分享一个小技巧:在 Harness 里加一个“蜜罐 Trace”。就是故意放一些看起来像真实 Trace 但实际上是诱饵的记录,如果 Agent 尝试删除或修改这些记录,立即触发告警。这个技巧在检测内部威胁时特别有用,成本低,效果好。

这个方向后续还可以扩展的地方很多,比如把 Trace 和 Agent 的行为基线结合起来做异常检测,或者用零知识证明来验证 Trace 的完整性而不暴露内容。但那是另一个话题了,有机会再聊。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询