Agent的Trace和对话日志进数仓,模型怎么建?
本文目录
30秒版回答
核心是按粒度拆事实表,不要做成一张大宽表。我会拆成四到五张事实表:会话事实(一次会话一行)、轮次事实(用户一次提问一行)、模型调用事实(一次模型调用一行,带 Token 和成本)、工具调用事实(一次工具调用一行)、检索事实(一次检索的候选结果,一个候选一行)。它们之间通过 session_id、turn_id、span_id 关联。
维度包括用户、Agent 版本、模型、工具、提示词版本、知识库版本。
几个特殊问题:重试要标记,避免一次提问被算成多次;流式输出要以最终完成事件为准;对话原文要脱敏分级存储,分析用的表只保留脱敏文本或分类标签。
先把这段讲顺;面试官追问时,再展开下面的原理和细节。
详细讲解
面试官:「我们的 Agent 每天产生大量对话和调用日志,现在都是一坨 JSON 存在日志系统里,没法分析。如果让你把这些数据建进数仓,你会怎么设计模型?」
这道题是数据开发在 Agent 面试里最能展示专业能力的题,因为它本质上是一道数仓建模题,只是业务对象从“订单”换成了“对话”。
很多算法和应用工程师会把日志直接打平成一张大宽表,结果是:会话数、提问数、模型调用数混在一起,Token 成本算重复,一个问题重试了三次被算成三个问题。粒度不清是这类数据最常见的问题,而粒度设计恰恰是数据开发的基本功。
面试官在考什么
• 能否识别出不同粒度的业务过程:会话、轮次、模型调用、工具调用、检索。
• 事实表和维度表设计是否合理,关联关系是否清晰。
• 是否考虑流式输出、重试、迟到数据等特殊问题。
• 是否考虑敏感信息:对话内容可能包含个人信息和业务机密。
展开一:识别业务过程和粒度
建模第一步永远是梳理业务过程。一次 Agent 交互里有几个层次的“事件”,它们的数量关系是一对多的:
会话 session(用户打开一次对话)
└─ 轮次 turn(用户的一次提问 + Agent 的最终回答) 1 会话 : N 轮次
└─ 步骤 span(Agent 内部的一次动作) 1 轮次 : N 步骤
├─ 模型调用 llm_call(含 Token、成本、模型)
├─ 工具调用 tool_call(含工具名、参数、耗时、结果状态)
└─ 检索 retrieval(含检索词、过滤条件)
└─ 候选 candidate(召回的每个 Chunk 及分数) 1 检索 : N 候选
这和电商里“订单 → 子订单 → 商品明细”的层次结构非常像。如果把它们打平到一张表里,就会出现和“订单金额按商品明细重复求和”一样的问题:会话数被轮次数放大,Token 成本被检索候选数放大。
展开二:事实表设计
| 事实表 | 粒度 | 核心度量 | 主要外键 |
|---|---|---|---|
| fct_agent_session | 一次会话一行 | 轮次数、时长、是否转人工、满意度 | user、agent_version、channel |
| fct_agent_turn | 一次提问一行 | 端到端延迟、步数、总 Token、总成本、是否成功、反馈 | session、intent |
| fct_llm_call | 一次模型调用一行 | 输入/输出/缓存 Token、成本、首字延迟、总延迟 | turn、span、model、prompt_version |
| fct_tool_call | 一次工具调用一行 | 耗时、是否成功、错误类型、返回大小 | turn、span、tool |
| fct_retrieval_candidate | 一个召回候选一行 | 召回分数、重排分数、最终排名、是否被引用 | retrieval、chunk、doc_version |
-- 模型调用事实表:一次模型调用一行
CREATE TABLE dwd.fct_llm_call (
call_id STRING, -- 主键,来自 span_id
turn_id STRING, -- 所属轮次
session_id STRING,
parent_span_id STRING, -- 用于还原调用树(多 Agent 场景)
model_key STRING, -- 关联模型维度:厂商、名称、版本、单价
prompt_version STRING, -- 提示词版本
purpose STRING, -- 规划/生成SQL/总结/评估,用于成本归因
input_tokens BIGINT,
cached_tokens BIGINT, -- 命中缓存的输入 Token,单价不同
output_tokens BIGINT,
cost_amount DECIMAL(18,6),-- 按调用当时的单价计算,冗余存储
ttft_ms INT, -- 首字延迟
latency_ms INT,
finish_reason STRING, -- 正常结束/长度截断/工具调用/内容过滤
is_retry TINYINT, -- 是否为重试调用
event_time TIMESTAMP,
dt STRING -- 分区
);
这里有两个值得讲的设计:
• cost_amount 按调用当时的单价冗余存储。模型价格会调整,如果每次查询都用最新单价去乘 Token 数,历史成本就会被改写。这和订单金额要存交易时的价格、而不是实时关联商品价格是一个道理。
• purpose 字段标记这次调用的用途(规划、生成 SQL、总结、评估)。没有它,你只知道总成本,不知道钱花在哪个环节。第 14 题的成本归因就靠它。
展开三:维度设计
• dim_agent_version:Agent 名称、版本、上线时间、包含的提示词和工具版本。版本对比分析的基础。
• dim_model:模型厂商、名称、版本、上下文长度、各类 Token 单价及生效时间(用拉链表管理价格变化)。
• dim_tool:工具名称、所属系统、是否有副作用、负责人。
• dim_prompt_version:提示词版本、变更说明、变更人。
• dim_user:用户、部门、角色。注意和业务系统的用户维度做一致性关联,才能分析“哪些部门在用、用得怎么样”。
展开四:特殊问题处理
| 问题 | 现象 | 处理方式 |
|---|---|---|
| 重试 | 一次提问因超时重试三次,被算成三次提问 | turn 以用户提问为粒度;模型调用打 is_retry 标记,指标区分“提问数”和“调用数” |
| 流式输出 | 一个回答拆成很多个流式事件 | 以完成事件为准生成一行;中途断开的标记为未完成 |
| 迟到数据 | 反馈(点赞/点踩)可能几天后才到 | 反馈单独建事实表,按 turn_id 关联;会话表的满意度按累计快照更新 |
| 多 Agent 嵌套 | 子 Agent 的调用挂在主 Agent 下面 | 用 parent_span_id 还原调用树,成本可按根节点汇总 |
| 大字段 | 提示词和输出很长 | 原文存对象存储或冷表,事实表只存哈希、长度和引用 |
展开五:敏感信息分级存储
对话内容可能包含用户的个人信息、公司的业务机密、甚至密码。这部分数据不能像普通日志一样随便开放查询。我会分三级存储:
• L1 原文层:完整对话原文,加密存储,访问严格审批,保留期短,仅用于必要的问题排查。
• L2 脱敏层:自动识别并替换手机号、邮箱、身份证号等敏感信息后的文本,用于评测和质量分析。
• L3 分析层:只有结构化字段和分类标签(意图、主题、是否成功),对分析人员开放。
这个思路和我们在数仓里做用户明细数据的分级管理是一样的。第 16 题会专门讲 Agent 场景的合规。
实战经验:埋点规范的思路可以直接复用
我在某出海硬件品牌做埋点治理时,最大的教训是:数据采集阶段的规范比事后清洗重要得多。当时 1000 多个事件命名混乱、属性含义不一致,后期治理花了很大力气,最终精简到 100 多个标准化事件。
Agent 日志也一样。我会推动应用侧在埋点阶段就遵循统一规范:每个事件必须带 session_id、turn_id、span_id、parent_span_id、版本信息和时间戳,字段命名统一。业界也有在可观测性标准里为大模型调用定义的通用语义约定,可以作为字段规范的参考,比每个团队自己发明一套格式要好得多。
追问链
追问一:“对话数”这个指标,你怎么定义?
我会先反问:业务说的“对话数”到底是会话数、提问数还是模型调用数?这三个数可能差一个数量级。我会在指标平台里分别定义:会话数(用户发起的会话个数)、提问数(用户的提问轮次,不含重试)、模型调用数(含 Agent 内部的多步调用和重试)。看板上明确标注口径,避免不同团队拿着不同的数吵架。
追问二:Agent 版本迭代很快,怎么做版本间的效果对比?
关键是每条事实数据都带上 agent_version 和 prompt_version,然后在轮次事实表上按版本分组对比成功率、步数、成本、延迟、反馈。注意要控制流量分布的差异,比如新版本只在某个部门灰度,不能直接和全量的旧版本比,要在同一用户群体上对比。
追问三:数据量很大,实时还是离线?
分场景。成本监控、异常告警需要实时或准实时,可以用流式计算做分钟级聚合;效果分析、版本对比、归因统计用离线 T+1 就够了。明细数据量大的检索候选表,可以只保留一定时间的全量数据,历史数据只保留被引用的候选和聚合结果。
加分说法 vs 减分说法
| ✔ 加分说法 | ✘ 减分说法 |
|---|---|
| 按粒度拆事实表:会话/轮次/模型调用/工具/检索 | 把日志打平成一张大宽表 |
| 用 span_id 和 parent_span_id 还原调用树 | 会话数、提问数、调用数混用 |
| 成本按调用时单价冗余存储 | 用最新单价重算历史成本 |
| 重试、流式、迟到反馈分别处理 | 对话原文直接开放查询 |
| 对话原文分级存储,分析层只用脱敏数据 | 不区分重试调用 |
怎么挂到你的项目上
话术模板:“Agent 日志建模我会按粒度拆成会话、轮次、模型调用、工具调用、检索候选几张事实表,用 span_id 还原调用树。成本按调用当时的单价冗余存储,重试单独标记,避免提问数被放大。对话原文分三级存储。我之前做埋点治理的经验是:采集规范比事后清洗重要,所以我会推动应用侧统一事件字段。”
总结
Agent 日志建模本质上是一道粒度设计题。会话、轮次、调用、检索是一对多的层次关系,拆开建事实表才能避免指标放大。再加上成本冗余、重试标记、流式和迟到处理、敏感数据分级,这道题就能体现出数据开发扎实的建模功底。
下一篇聊钱:大模型账单一个月翻了三倍,成本怎么归因、怎么压下来?
你们的 AI 应用日志现在存在哪里?能回答“上周一共花了多少钱、花在哪”吗?评论区聊聊~
关注「Wood聊大数据」| 9年大数据老兵,聊技术、聊职场、聊成长