当AIAgent的执行轨迹成为应用数据时

译自:When AI agent traces become application data[1]
作者:Manveer Chawla
假设一个测试失败了,开发人员将其交给编码 Agent。它会深入相关文件,运行测试套件,修改两个文件,然后运行针对性的验证。任务视图展示了它触及的文件、运行的命令、这些命令返回的结果以及最终生成的 diff。
在接受补丁之前,开发人员会审查该活动。队友以后可能会重新打开同一个运行记录,以查看代码为何发生改变。构建该 Agent 的团队可以比较数千次运行,以观察模型或提示词(prompt)的更新是提高了测试成功率,还是仅仅增加了更多工具调用和成本。
该记录需要一个存储的地方。开发人员和审查人员希望在需要时能获得运行记录的持久版本。工程团队则希望跨运行聚合相同的执行数据,因为 Agent 的行为是不确定的,并且会随时间发生变化。
“对于许多 Agent 产品而言,该记录最终成为了具有遥测特征工作负载的应用数据。”
对于许多 Agent 产品而言,该记录最终成为了具有遥测特征工作负载的应用数据。正是这种组合改变了存储决策。
当轨迹成为产品数据
并非所有的 Agent 轨迹都算作应用数据。可以采样、过期或丢弃的内部诊断轨迹仍然属于遥测。这种边界可以存在于同一条轨迹中,其中原始诊断字段保持内部属性,而重建用户任务所需的字段则转变为产品状态。
一旦你的产品必须检索、显示或保留持久的执行记录,这个边界就会发生移动。例如,开发人员可能需要查看 Agent 检查了哪些文件,或者审查人员可能需要检查运行了哪些命令以及测试是否通过。
这并不需要暴露模型私有的思维链。产品可以呈现可观测执行的投影,显示模型调用、工具调用、文件读取、命令结果、错误、时序和状态转换。该记录让用户能够验证结果并决定他们希望信任它的程度。在某些工作流程中,它也成为审计记录,这改变了其访问和保留要求。
“内部诊断字段并不会仅仅因为它们来自同一次运行,就自动属于产品视图。”
一旦该执行记录成为产品状态,它就必须遵循你的应用程序访问模型。代码、提示词、检索到的文档、工具参数和命令输出可能包含租户或用户数据,因此应用程序在存储和检索它们时必须强制执行相同的授权边界。内部诊断字段并不会仅仅因为它们来自同一次运行,就自动属于产品视图。
为什么一次 Agent 运行会产生如此多的数据
拉取请求(Pull request)的数量随提交审查的补丁数量而扩展,而问题(issue)的数量随开发任务的数量而扩展。两者都只是在工作流边界上计算工作单位。
Agent 轨迹的扩展方式不同,具体取决于每个任务内的执行图。修复失败测试的一个请求可以在 Agent 提出补丁之前触发多次模型调用、文件读取、搜索、命令执行、测试重试和分支。这些步骤中的每一个都可以产生自己的 span 或事件。
OpenTelemetry GenAI 语义约定[2](仍在开发中)为模型推理、工具执行和检索定义了单独的 span 类型。捕获的内容取决于 span 类型和内容捕获策略。推理 span 可能带有模型标识符、Token 使用量以及可选的输入和输出消息,而工具执行 span 可能带有可选的参数和结果。
span 计数跟踪了 Agent 在每个工作单位内实际执行的操作。如果你添加了一个新工具、重试策略或分支,即使已完成的任务数量没有变化,数据量也会增加。
“如果你添加了一个新工具、重试策略或分支,即使已完成的任务数量没有变化,数据量也会增加。”
这种相同的扇出(fan-out)现象也出现在编码 Agent 之外。在一项案例研究中,Laminar 报告称 每天产生超过 500,000 个浏览器事件[3]。一个浏览器 Agent 会话可以运行超过 30 分钟,并生成数十万个 DOM diff 事件。Laminar 利用这些事件重构出类似视频的回放,展示了 Agent 的所见所闻。
无论 Agent 是在编写代码[4] 还是浏览网页,相同的数据都服务于两种不同的目的。一个人加载一条轨迹来理解单次运行,而工程团队则扫描许多轨迹来寻找模式。点查询(point retrieval)和群组分析(cohort analysis)的结合赋予了这些数据独特的形态。
为什么 Agent 轨迹数据的行为类似于遥测
Agent 轨迹中的大多数记录都是写入一次而不是更新的。模型调用或工具结果描述了一个已经发生的事件。评分、注释和运行状态可能会在稍后发生变化,但团队可以将这些可变字段分开存储,或将更改记录为新事件。
这些记录还带有高基数维度,例如模型版本、提示词模板、工具名称、会话 ID、用户 ID 和结果。它们的价值只有在上下文中才会显现。单独来看,一个孤立的工具调用 span 说明不了什么,但完整的轨迹可以解释失败的运行,而群组分析则可以揭示回归问题。
因此,相同的数据集服务于几个不同的读者:
| 消费者 | 读取模式 | 示例 |
| 产品界面 | 点查询 | 为开发人员或审查人员加载一次编码 Agent 的运行 |
| 评估流水线 | 群组扫描 | 比较不同 Agent 版本间的测试成功率、延迟和成本 |
| 平台团队 | 时间窗口聚合 | 按模型、工具或部署对错误和延迟进行分组 |
主应用数据库可以在适度规模下满足所有三种模式,但当宽范围扫描和高基数聚合开始与关键路径上的产品读写产生竞争时,情况就会发生变化。
超出主数据库的承载能力
将轨迹移出主数据库并没有一个通用的事件计数阈值。这一决策取决于工作负载。
第一个信号是争用,即摄取或保留工作开始消耗足够的 I/O 和 CPU,从而影响事务操作。接下来是分析摩擦,即评估和调试查询需要扫描很长的时间范围或连接巨大的轨迹表,并停止满足团队的延迟目标。最终,团队被迫进行强制采样,丢弃轨迹以保护应用数据库,尽管产品或审计流程需要完整的记录。
Langfuse 在扩展其开源的 LLM 可观测性[5]、评估和提示词管理平台时,记录了争用和分析摩擦。它在重负载下经历了 Postgres IOPS 耗尽,提示词 API 延迟达到七秒。Langfuse 将其追踪数据从 Postgres 迁移到了 ClickHouse[6],同时保持了事务性和延迟敏感路径的隔离。
存储只是决策的一半
数据库迁移解决了一类问题,但原始数据模型却产生了另一个问题。Langfuse 最初将其分析架构中独立的轨迹表、观察表和评分表引入。更新需要去重和跨表分析,增加了连接成本。
“存储只是决策的一半。分析存储引擎解决了工作负载问题,而数据模型减少了跨表工作。”
后来,Langfuse 将这些记录折叠成一个宽的、主要是不可变的观察表[7],每行对应一次模型调用、工具执行或 Agent 步骤。大型数据集的初始表加载时间从秒级变为毫秒级,大项目的仪表盘加载时间在更长的时间范围内至少提高了 10 倍。
Langfuse 需要这两个变化。分析存储引擎解决了工作负载问题,而数据模型减少了跨表工作。
如何选择存储模式
从你的产品必须支持的读取方式开始,然后选择满足这些需求的最简单架构。
在适度的数据量下,将轨迹保存在主数据库中可以避免额外的操作边界。随着分析争用的增加,应用程序可以将轨迹事件发送到专门的分析存储,同时将可变业务记录保留在其事务数据库中。
有些应用程序需要两个系统共享数据。支持 Postgres 的产品可以将用户、权限和工作流状态保留在 Postgres 中,同时将 Agent 事件发送到 ClickHouse 进行分析查询。如果相关的应用数据 已经存在于 Postgres 中[8],则可以使用变更数据捕获(CDC)将其复制到分析路径中。
从“谁在读取轨迹”开始
谁依赖该记录以及他们如何查询它,比轨迹看起来像日志还是拉取请求更重要。
如果你的产品需要重建单次运行的持久记录,且工程团队需要跨数千次运行比较行为,那么该轨迹就已经变成了具有遥测式存储工作负载的应用数据。
在选择存储方案之前,先规划好点查询和跨运行扫描。如果两者都是产品要求,那么从保留的第一条轨迹开始就应为此进行设计。
引用链接
[1] When AI agent traces become application data: https://thenewstack.io/agent-traces-application-data/[2] OpenTelemetry GenAI 语义约定: https://github.com/open-telemetry/semantic-conventions-genai[3] 每天产生超过 500,000 个浏览器事件: https://clickhouse.com/blog/how-laminar-reimagined-observability-for-ai-browser-agents[4] Agent 是在编写代码: https://thenewstack.io/ai-agents-software-engineering/[5] LLM 可观测性: https://thenewstack.io/agentic-ai-observability-auditing/[6] 将其追踪数据从 Postgres 迁移到了 ClickHouse: https://langfuse.com/blog/2024-12-langfuse-v3-infrastructure-evolution[7] 将这些记录折叠成一个宽的、主要是不可变的观察表: https://langfuse.com/blog/2026-03-10-simplify-langfuse-for-scale[8] 已经存在于 Postgres 中: https://thenewstack.io/why-ai-workloads-are-fueling-a-move-back-to-postgres/