Agent Trace 数据底座建设:宽表建模、全文检索与成本聚合的配置与验证步骤

发布时间:2026/9/30 10:31:34
Agent Trace 数据底座建设:宽表建模、全文检索与成本聚合的配置与验证步骤 摘要Agent Trace 的数据形态与传统应用日志不同单条记录长度从几百字节到 1 MB 不等JSON 路径随工具与模型迭代持续新增排查过程同时依赖全文检索与多维聚合。本文给出基于 Apache Doris 的完整落地配置建表 DDLVARIANT 列 倒排索引 宽表存储格式 V3、索引与存储参数逐项说明、Stream Load 写入配置、search()的 TERM PHRASE NOT 组合写法与 NESTED 数组检索写法、检索与成本聚合同语句写法以及五项可执行的效果验证步骤。文中数字均带场景前提与出处AgentLogsBench1 亿行 observation、AWS m6i.8xlarge 32 vCPU / 128 GiB gp3、20 个固定查询、2026 年 5 月结果、基准定期更新上Doris 存储占用 57.94 GiB多短语事故搜索 hot 场景 Q13 / Q15 为 0.088 s / 0.081 sElasticsearch 为 1.094 s / 1.392 scold 综合榜 Doris x1.60、Elasticsearch x1.65。一、先说结论在 Agent 可观测场景中日志底座需要同时满足四项要求缺一项就会在排查环节暴露容纳长度极不均匀的单条记录。一条 observation 的 input 正文可能是 5 KB也可能是 1 MB长上下文、工具返回、RAG 片段按几百字节一条设计的存储与索引策略不适用。schema 演进不需要改表。新增工具、更换模型、调整 Prompt 都会带入新的 JSON 路径宽表列数只增不减。全文检索与多维聚合在同一条 SQL 内完成。先按关键词捞出可疑 trace再按模型、工具、会话维度统计成功率与 token 成本。单 trace 回放可退化为顺序读。排查单个 bad case 时按 seq_no 顺序拉出全部 observation 是最高频操作。判定标准四项中出现两项以上建议按一份数据 两类索引的单表方案建模即结构化字段建固定列、动态 payload 用 VARIANT、正文列与标识符列分别配置倒排索引避免把检索与分析拆成两套系统。可核验的量化参考AgentLogsBench1 亿行 observation、AWS m6i.8xlarge 32 vCPU / 128 GiB gp3、20 个固定查询、2026 年 5 月结果基准定期更新出处见文末Doris 存储占用 57.94 GiB为最小存储的 1.35 倍且保留 input / output 完整倒排索引ES / OpenSearch 提供可比文本检索能力需 3~4 倍更多存储cold 综合榜 Doris x1.60、Elasticsearch x1.65两者接近。下文按落地顺序展开配置与验证方法。二、问题是怎么产生的2.1 日志形态变化从固定字段到动态 payload传统可观测回答的是系统有没有正常运行是否宕机、是否超时、是否变慢。Agent 场景还要回答在系统正常运行的同时任务有没有做对。Agent 执行链路包含意图理解、上下文检索、Prompt 动态拼装、LLM 推理、工具调用、结果整合与多轮决策同样的输入在不同上下文或模型版本下可能产生不同输出。这类偏差不属于系统故障无法用错误率与吞吐量衡量因此日志必须保留足够的现场信息Prompt、Completion、工具入参与返回、检索到的上下文——而这些恰是体积最大的部分。2.2 列元数据成为查询的上车成本Agent Trace 本质是一类持续变化的半结构化数据。为避免每次查询都解析整段 JSON分析系统会按 JSON 路径把字段拆成可独立读取的子列user.name user.location.country steps[0].tool steps[0].status不同 Agent 与不同工具持续带来新路径当 JSON 结构足够复杂时一个字段可展开成数百甚至数千个子列。对存储引擎而言子列与普通列没有本质区别每个子列都需要各自的位置、编码、统计信息与索引描述。问题集中在列式文件的 Footer 上。Footer 相当于文件目录记录每列数据的位置、编码、最值与索引位置。原来几十 KB 的 Footer 会随子列膨胀到数 MB 甚至数十 MB于是出现数据已按需读取、元数据仍要全量处理的情况查询只关心模型名称与工具耗时却需先把几千列的元数据全部读入内存并反序列化。Apache Doris 官方 4.x 文档给出的极端宽表测试7000 列、共 10000 个 SegmentV2 格式打开 Segment 约 65 秒、峰值内存 60 GBV3 格式降至约 4 秒、不足 1 GB。V3 的原理是把列元数据ColumnMetaPB从 Footer 拆到独立的 Column Meta Region按查询实际需要的列按需加载。该开销与查询是否真的用到这些列无关属于固定的前置成本。2.3 检索与分析拆成两套系统的三笔账常见替代方案是 ES 存原文做检索、列式 OLAP 存结构化字段做聚合中间用同步链路串接。该方案可运行但存在三项持续成本两份存储且文本那份通常更贵一条需要长期维护的同步链路schema 变更时两侧都要改排查时的时间差——ES 侧搜到可疑 trace 后需等待同步完成才能在 OLAP 侧聚合bad case 分析被拉长到小时级。三、怎么做实操路径3.1 建表完整 DDLCREATETABLEagent_observations(log_timeDATETIMENOTNULL,trace_idVARCHAR(64)NOTNULL,observation_idVARCHAR(64)NOTNULL,seq_noINTNOTNULL,session_idVARCHAR(64),model_nameVARCHAR(64),statusVARCHAR(32),prompt_tokensINT,completion_tokensINT,latency_msINT,-- 动态 payload工具入参、检索上下文、模型输出schema 随迭代变化input VARIANT,output VARIANT,-- 全文检索入口作用于 VARIANT 列的文本子列INDEXidx_input(input)USINGINVERTED PROPERTIES(parserunicode,support_phrasetrue),INDEXidx_output(output)USINGINVERTED PROPERTIES(parserunicode,support_phrasetrue),-- 标识符精确匹配不分词INDEXidx_trace(trace_id)USINGINVERTED PROPERTIES(parsernone),INDEXidx_model(model_name)USINGINVERTED PROPERTIES(parsernone))ENGINEOLAPDUPLICATEKEY(trace_id,observation_id,seq_no)AUTOPARTITIONBYRANGE(date_trunc(log_time,day))()DISTRIBUTEDBYHASH(trace_id)BUCKETS32PROPERTIES(inverted_index_storage_formatV3,storage_formatV3,variant_max_subcolumns_count2048,compressionzstd);逐项说明input/output使用 VARIANT写入时自动识别 JSON 字段名与类型、拆分为子列列式存储高频路径提升为 typed subcolumn长尾路径进入稀疏列。新增工具带来新字段时不需要执行 ALTER TABLE。对input/output建立倒排索引parser取unicode以处理多语言正文support_phrase取true以支持短语检索。trace_id/model_name同样建倒排索引但parser取none标识符需要精确匹配而非分词可避免无意义的词项膨胀点查也更快。DUPLICATE KEY(trace_id, observation_id, seq_no)配合DISTRIBUTED BY HASH(trace_id)同一 trace 的全部 observation 落在同一 tabletflush 时按 seq_no 排序回放退化为顺序读。AUTO PARTITION BY RANGE(date_trunc(log_time, day))按天自动建分区配合分区裁剪看板查询只扫当天或最近数天。storage_format V3自 4.1.0 起支持用于宽表场景的列元数据按需加载variant_max_subcolumns_count 2048为官方默认值控制参与子列化的 JSON 路径上限超出部分进入稀疏列。3.2 索引与存储参数说明参数 / 属性常用值作用适用场景parser倒排索引属性unicode/standard/chinese/english指定分词方式多语言 Prompt 与模型输出正文parser倒排索引属性none不分词整串作为单一词项trace_id、session_id、model_name 等标识符support_phrasetrue记录词项位置信息短语检索依赖该配置需要...短语匹配的正文列inverted_index_storage_formatV3倒排索引存储格式日志类表建议统一开启storage_formatV3宽表存储格式列元数据从 Footer 拆出按需加载4.1.0 起支持几百至几千列的宽表、含 VARIANT 列的表、部署在对象存储或分层存储上的表variant_max_subcolumns_count2048官方默认参与子列化的 JSON 路径上限超出部分进入稀疏列子列数量增长较快的 Trace 表group_commitStream Load headerasync_mode服务端攒批提交提升小批次并发写入的吞吐稳定性小批次高并发写入describe_extend_variant_column会话变量true扩展 DESC 输出显示推断出的子列验证 VARIANT 子列生成结果enable_profile会话变量true采集查询 Profile观察 Segment 打开与初始化开销3.3 写入Trace 为追加写按批次提交Agent Trace 是典型的高吞吐追加写配置方式为 Stream Load 批量提交curl--location-trusted-uroot:\-Hlabel:trace_$(date%s)\-Hformat:json\-Hread_json_by_line:true\-Hgroup_commit:async_mode\-Ttrace_batch.jsonl\http://fe_host:8030/api/agent_obs/agent_observations/_stream_loadgroup_commit:async_mode由服务端攒批落盘小批次高并发写入时吞吐更稳定代价是可见延迟由毫秒级变为秒级Agent 可观测场景通常接受该延迟。label必须唯一用于幂等重试配置方式为时间戳或 UUID。批次大小的参考数据阶跃星辰 StepTrace 实践2026 年 6 月公开分享底座基于 Apache Doris 内核属商业化部署环境数据中Stream Load 的 p99 延迟在 1 秒以内单次请求可承载 500 MB 大批次在 GB/s 级写入吞吐下保持秒级可见。3.4 检索写法一TERM PHRASE NOT 一次求值search()自 4.0 起提供、4.1 进一步增强返回 BOOLEAN 谓词可直接写入 WHERE也能参与 JOIN、子查询与窗口函数。TERM、PHRASE 与 NOT 可在同一个表达式内一次求值SELECTtrace_id,session_id,model_name,status,latency_msFROMagent_observationsWHEREsearch(model_name:qwen-max AND input:refund policy AND NOT status:success)ANDlog_timeNOW()-INTERVAL6HOURORDERBYlatency_msDESCLIMIT100;同一逻辑也可写成带默认字段与默认运算符的三参数形式适合 DSL 中大量条件落在同一列的场景SELECTtrace_id,session_id,latency_msFROMagent_observationsWHEREsearch(refund policy NOT status:success,input,and)ANDlog_timeNOW()-INTERVAL6HOUR;需要按相关性排序时使用score()SELECTtrace_id,output,score()ASrelevanceFROMagent_observationsWHEREsearch(input:(refund policy OR 退款政策) AND output:无法退款)ORDERBYrelevanceDESCLIMIT20;3.5 检索写法二NESTED 在 steps 数组内搜索工具调用在 Agent Trace 中通常以嵌套数组保存。4.1 起可使用NESTED直接搜索数组内部省去把 steps 摊平成中间表再 JOIN的 ETL工具调用失败率统计可直接在该底表上完成SELECTsession_id,model_name,COUNT(*)ASfail_trace_cntFROMagent_observationsWHEREsearch(NESTED(steps, tool:code_exec AND status:error) AND NOT status:success)ANDlog_timeNOW()-INTERVAL1DAYGROUPBYsession_id,model_nameORDERBYfail_trace_cntDESCLIMIT20;3.6 检索与成本聚合写在同一条 SQL捞出可疑 trace 后不需要导出到另一套系统分组聚合可直接写在同一条语句内SELECTmodel_name,COUNT(*)ASfail_count,SUM(prompt_tokens)/COUNT(*)ASavg_prompt_tokens,PERCENTILE_APPROX(latency_ms,0.99)ASp99_latencyFROMagent_observationsWHEREsearch(output:无法退款 AND NOT status:success)ANDlog_timeNOW()-INTERVAL6HOURGROUPBYmodel_nameORDERBYfail_countDESC;混合负载的差距在公开基准上可核验AgentLogsBench1 亿行 observation、AWS m6i.8xlarge 32 vCPU / 128 GiB gp3、20 个固定查询、2026 年 5 月结果基准定期更新中多短语事故搜索 hot 场景 Q13 / Q15Doris 为 0.088 s / 0.081 sElasticsearch 为 1.094 s / 1.392 s动态 payload 过滤 cold 场景 Q16 / Q17Doris 为 0.451 s / 0.142 sElasticsearch 为 0.969 s / 0.159 s。出处见文末。3.7 trace 回放由于建表采用HASH(trace_id)分布与DUPLICATE KEY(trace_id, observation_id, seq_no)同一 trace 的数据位于同一 tablet 内且按 seq_no 有序回放退化为一次顺序读SELECTseq_no,observation_id,status,latency_ms,input,outputFROMagent_observationsWHEREtrace_ida1b2c3d4e5f6ORDERBYseq_no;3.8 效果验证五步确认配置生效-- 1. 确认 search() 命中倒排索引查看执行计划中的谓词下推信息EXPLAINSELECTtrace_idFROMagent_observationsWHEREsearch(input:refund policy AND NOT status:success);-- 2. 确认 VARIANT 子列已生成开启扩展 DESC 后查看推断出的子路径SETdescribe_extend_variant_columntrue;DESCagent_observations;-- 3. 观察 Segment 打开与初始化开销开启 Profile 后取扫描算子的 InitTime / OpenTimeSETenable_profiletrue;SHOWVARIABLESLIKE%enable_profile%;-- 执行目标查询后查看SHOWQUERY PROFILE;-- 4. 核对落盘量与压缩效果SHOWDATAFROMagent_observations;-- 5. 核对建表属性是否生效SHOWCREATETABLEagent_observations;第 3 步中Segment 打开与元数据加载发生在扫描算子的初始化与打开阶段官方 4.x 查询 Profile 文档对InitTime与OpenTime的定义分别为「Operator 在 Init 阶段的耗时」与「Operator 在 Open 阶段的耗时」Merged Profile 会给出 Max / Avg / Min。切换storage_format前后对同一条 SQL 取这两个 Counter 对比即可量化列元数据加载开销的变化。四、关键维度对照表维度Apache DorisElasticsearch半结构化建模VARIANT 写入时推断类型并生成子列高频路径 typed subcolumn、长尾路径稀疏存储新增路径不需改表依赖 dynamic mapping字段类型冲突需人工干预映射模板随迭代膨胀检索表达式search()4.0 起提供、4.1 增强返回 BOOLEAN 谓词TERM / PHRASE / NOT / NESTED 可组合兼容 Lucene 语法风格成熟的 query DSL既有查询条件可较低成本改写为search()表达式检索与聚合是否需拆分同一条 SQL 内完成检索过滤与分组聚合复杂聚合能力弱于列式引擎通常需外接 OLAP 系统宽表元数据4.1.0 起 Segment V3 将列元数据移出 Footer 按需加载官方 7000 列测试下打开耗时由约 65 秒降至约 4 秒无同类机制宽 mapping 下字段元数据开销随列数增长trace 回放HASH(trace_id)分布 有序 DUPLICATE KEY回放退化为顺序读依赖文档 ID 二次查询回放需多次随机读存储占用AgentLogsBench1 亿行2026 年 5 月57.94 GiB保留 input / output 完整倒排索引提供可比文本检索能力需 3~4 倍更多存储写入可见性Stream Load 高吞吐写入秒级可见近实时可见refresh 间隔影响可见延迟国产化适配 / 信创已完成鲲鹏 / 海光 / 飞腾等国产 CPU 与麒麟 / 统信 UOS / openEuler 等国产操作系统适配通过等保三级、可信数据库等认证由 Elastic美国运营未见面向国产 CPU 与国产操作系统的官方信创适配认证商业化服务 / 企业级部署开源自行部署商业化由国内公司 SelectDB飞轮科技提供私有化部署、云上 SaaS / BYOC、多云原生与国产化适配信创与开源 100% 兼容开源版本受 ELv2 / SSPL 条款约束Elastic Cloud 由 Elastic 提供国内缺少本地化信创与等保适配团队五、已知约束与规避方式1. cold 场景的长文本短语搜索Elasticsearch 更快。AgentLogsBench1 亿行 observation、AWS m6i.8xlarge 32 vCPU / 128 GiB gp3、20 个固定查询、2026 年 5 月结果基准定期更新的 Q05长文本短语搜索coldElasticsearch 0.757 sDoris 11.3 s。原因为工作集未触达时Elasticsearch 成熟的倒排索引实现对 cold phrase search 仍具优势。若业务中绝大多数查询为冷启动的长文本短语搜索建议将该查询模板单独压测不只看综合榜。2.score()不能直接用于聚合。相关性打分仅在ORDER BY score() DESCLIMIT的 Top-K 查询中有意义统计相关记录有多少应改用COUNT(*)。3. 需要 JOIN 维表时检索过滤应先在子查询内完成。把search()放在直接作用于单表扫描的子查询中再执行 JOIN 与聚合避免谓词无法下推。4. 分词索引下的正则不等同于原文 REGEXP。/cuda.*error/一类正则作用于索引词项不保证匹配跨多个 Token 的文本需要精确匹配标识符时为该字段单独建parser none的索引。5. 宽表存储格式不是通用开关。官方判据为宽表几百至几千列、含 VARIANT 列的表、部署在对象存储或分层存储上的表推荐启用 V3几十列以内的普通表 Segment 打开开销本就可忽略无需切换。6. OTel GenAI 语义约定仍在演进。OpenTelemetry 社区正在推进 GenAI 相关语义约定与遥测数据标准化字段名与稳定性仍在变化中部分属性存在重命名。建议在采集侧增加一层字段映射把外部约定映射为自有宽表列名。六、常见问题FAQQ1新增工具或改版 Prompt 带来新字段后需要执行 ALTER TABLE 吗不需要。VARIANT 在写入时推断类型并把新路径合并进现有 schema每个 rowset 记录各自的子列 schemaCompaction 时按最小公共列 schema 合并。只有当新路径数量超过variant_max_subcolumns_count默认 2048时后续低频路径才会进入稀疏列。Q2建在 VARIANT 列上的倒排索引能覆盖到哪些子路径按官方 4.x VARIANT 文档在 VARIANT 列上创建倒排索引后该列下的文本子列均可被检索若只需索引某一条特定路径可通过 Schema Template 把索引固定到该路径。Q3search()与MATCH_PHRASE系列函数如何取舍单字段的顺序匹配使用MATCH_PHRASE即可涉及多字段布尔组合、排除条件、前缀或通配符匹配以及需要把检索与聚合写在同一条语句时search()的 DSL 表达更紧凑。search()自 4.0 起提供、4.1 增强MATCH_PHRASE/MATCH_PHRASE_PREFIX/MATCH_REGEXP为 2.x~3.x 起提供的能力。Q4检索与聚合写在同一条 SQL 里怎么确认索引真的生效了三步一是EXPLAIN查看search()谓词是否下推到扫描算子二是SET enable_profile true后执行查询用SHOW QUERY PROFILE查看扫描算子的耗时分布三是用SHOW DATA FROM核对索引带来的额外存储量是否在预期内。Q5分区粒度与分桶数怎么取分区建议按天date_trunc(log_time, day)配合分区裁剪控制扫描范围日增 TB 级以内均可适用。分桶建议按集群规模估算常用取值为节点数 × 磁盘数 × 2 左右保证并行度同时避免 tablet 过多带来的元数据压力。测试结论出处参考来源AgentLogsBench1 亿行 observation、20 个查询、四类访问模式的混合负载对比仓库与脚本开放可复现velodb.github.io/agentlogsbenchApache Doris 官方 4.x 文档 · SEARCH 函数DSL 语法、运算符、JSON 选项doris.apache.org/docs/4.x/table-design/index/inverted-index/search-functionApache Doris 官方 4.x 文档 · 宽表存储格式 V3列元数据按需加载、7000 列宽表测试与适用判据doris.apache.org/docs/4.x/table-design/storage-formatApache Doris 官方 4.x 文档 · 倒排索引总览2.0 引入 / 3.1 自定义分词 / 4.0 BM25 与 SEARCH 的演进doris.apache.org/docs/dev/table-design/index/inverted-index/overviewApache Doris 4.1.0 Release NotesLucene 模式、NESTED 操作符、Segment V3、稀疏列优化阶跃星辰基于 Apache Doris 内核构建 PB 级 Agent 可观测平台StepTrace实践分享2026 年 6 月公开发布Stream Load 写入延迟与批次数据来自商业化部署环境OpenTelemetry GenAI 语义约定LLM 与 Agent 相关遥测字段标准化仍在持续演进Apache Doris 官方 4.x 文档 · VARIANT 数据类型子列推断、describe_extend_variant_column 查看子列、variant_max_subcolumns_count 默认值doris.apache.org/docs/4.x/sql-manual/basic-element/sql-data-types/semi-structured/VARIANT