AI 产品埋点设计:要记录模型前后的关键事件
一个让我栽过跟头的数据盲区
去年我负责一个 AI 写作助手的数据看板。上线第一个月,产品同学拿着报表来找我:「生成按钮的点击率在涨,但周留存不动,是不是推荐算法有问题?」
我翻了两周的事件日志才发现,我们只埋了「点击生成」和「页面浏览」两个事件。模型到底生成了什么、用户有没有采纳、改了多少、重试了几次——全是空白。点击率上涨的真实原因是有一批用户在反复重试,因为输出质量根本不能用。
这件事让我意识到,传统产品的埋点思路——点击、浏览、转化——到了 AI 产品里远远不够。AI 产品的核心环节是「模型处理」,这个过程对用户可见但对外部分析不可见。如果不在模型前后各埋一组关键事件,你的数据看板就只看到了用户按了按钮,看不到按钮后面发生了什么。
这篇文章把我踩过的坑和后来沉淀下来的方法论整理出来,覆盖输入质量、模型输出、人工编辑、采纳、失败和成本六个事件域,附带可直接复用的代码和检查清单。
传统埋点为什么不够用
传统 SaaS 产品的埋点模型是事件驱动的:用户触发一个动作,客户端记录一个事件名加属性。这套模型假设系统的行为是确定性的——同一个按钮点击产生同一个结果。
AI 产品打破了这个假设。同样的输入,不同模型版本、不同参数、甚至不同时间调用,都可能产出完全不同的结果。用户点击「生成」只是一个意图表达,真正的价值交付发生在模型输出之后——用户看了结果、决定要不要用、改了多少。
PostHog 在 2026 年的 AI 可观测性指南中把这个问题拆成了六个构建模块:追踪(Tracing)、成本与 Token 统计、错误捕获、评估(Evals)、用户反馈闭环和 Prompt 管理^[1]^。这六个模块中,只有第一个和传统 APM 有交集,其余五个都是 AI 产品特有的分析需求。
| 维度 | 传统产品埋点 | AI 产品埋点 |
|---|---|---|
| 核心假设 | 同一输入同一输出 | 同一输入可能产生不同输出 |
| 关键事件 | 点击、浏览、表单提交 | 输入质量、模型输出、采纳/编辑、失败、成本 |
| 质量判断 | 转化率、跳出率 | 输出评分、编辑距离、采纳率 |
| 成本归因 | 服务器资源按时间 | Token 消耗按功能/用户/模型 |
| 失败模式 | HTTP 错误码、超时 | 内容策略拒绝、格式解析失败、无限循环、幻觉 |
| 隐私风险 | 用户行为数据 | 用户行为 + 输入输出内容(含敏感信息) |
Future AGI 在 2026 年的 LLM 产品分析指南中进一步提出了四个事件流的融合框架:用户行为信号、结果信号、质量信号和队列信号^[2]^。这四个事件流需要通过共享的 join key(比如 trace_id)关联起来,才能回答「某个用户的某次 AI 交互到底成不成功」这种看似简单的问题。
六个事件域:从输入到成本的完整链路
我后来把 AI 产品的埋点拆成六个事件域。每个域对应一组事件,解决一类分析问题。跳过任何一个域,你的数据分析就会出现盲区。
域一:输入质量事件
用户喂给模型的内容质量直接决定输出质量。如果你的分析只看输出好坏而不看输入好坏,等于把一半的归因让给了运气。
输入质量事件需要记录的不只是输入文本的长度。更有价值的维度是输入的完整度和结构化程度。
// ❌ 坏做法:只记一个长度,无法区分「写了一半」和「精心构造」
posthog.capture('ai_generate_clicked', {
input_length: prompt.length,
})
// ✅ 好做法:记录输入的多个质量维度
posthog.capture('ai_input_submitted', {
input_length: prompt.length,
// 输入中是否包含具体指令(而非空泛描述)
has_specific_instruction: /请|帮我|生成|修改|翻译|总结/.test(prompt),
// 是否附带了参考材料(如上传文件、选中上下文)
has_context_attachment: attachments.length > 0,
// 上下文来源类型:手动输入、模板填充、从文档选中、历史记录
context_source: determineContextSource(inputMeta),
// 输入语言(多语言产品必备)
input_language: detectLanguage(prompt),
// 历史重试次数——区分首次使用和反复重试
retry_count: currentRetryCount,
})差异在于,坏做法只给了你一个数字,你无法判断「输入长度为 15」是用户写了一个词就点了生成,还是系统自动填充了模板。好做法把输入的完整度、来源和重试情况拆开记录,事后做归因分析时可以明确回答「输入质量是否影响了输出质量」。
域二:模型输出事件
模型输出事件的核心不是「模型返回了什么内容」——那是追踪(Tracing)层面的事。产品分析层面需要关注的是输出的状态和特征。
// ❌ 坏做法:只记录「成功/失败」,丢失了关键的状态信息
posthog.capture('ai_generation_complete', {
status: response.ok ? 'success' : 'error',
})
// ✅ 好做法:记录输出的多维状态
posthog.capture('ai_output_received', {
// 模型版本——同一个功能可能同时跑 A/B 两个模型
model_id: response.model,
// 实际返回模型(提供商可能路由到不同版本)
response_model: response.actualModel,
// 完成原因:正常结束、触发长度限制、工具调用、内容过滤
finish_reason: response.finishReason,
// 输出 token 数
output_tokens: response.usage.outputTokens,
// 输入 token 数
input_tokens: response.usage.inputTokens,
// 首字延迟(用户感知速度的关键指标)
time_to_first_token_ms: ttft,
// 端到端延迟
total_latency_ms: totalLatency,
// 输出结构类型:纯文本、Markdown、JSON、代码块
output_format: classifyOutputFormat(response.content),
// 是否触发了内容安全策略
content_policy_triggered: response.finishReason === 'content_filter',
})OpenTelemetry GenAI 语义约定从 v1.37 开始正式定义了 gen_ai.client.inference.operation.details 事件,把系统指令、输入消息和输出消息作为结构化属性记录^[3]^。它的核心设计理念是把内容捕获做成 opt-in 的——默认不记录原文,需要时通过环境变量 OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true 开启。这个设计很好地平衡了调试需求和隐私保护。
域三:用户行为反馈事件
用户拿到输出后的行为才是判断 AI 功能价值的最可靠信号。这些行为分两类:显式反馈(点赞、点踩、评分)和隐式反馈(编辑、复制、重试、放弃)。
// ❌ 坏做法:只有显式反馈,而大部分用户不会主动点赞/点踩
posthog.capture('ai_feedback', {
type: 'thumbs_up', // 收集到的反馈量极少,无法代表整体
})
// ✅ 好做法:显式 + 隐式双通道
// 显式反馈
posthog.capture('ai_explicit_feedback', {
feedback_type: 'thumbs_up' | 'thumbs_down' | 'rating',
rating_value: rating, // 1-5 分
feedback_text: optionalComment,
trace_id: currentTraceId,
})
// 隐式反馈——这才是大多数用户真实态度的体现
posthog.capture('ai_implicit_feedback', {
// 用户是否在输出基础上做了编辑
edited: outputText !== finalText,
// 编辑距离——衡量改动幅度
edit_distance: levenshteinDistance(outputText, finalText),
// 编辑比例——改了几个字 vs 全部重写
edit_ratio: levenshteinDistance(outputText, finalText) / outputText.length,
// 用户是否直接复制使用
copied_to_clipboard: wasCopied,
// 从输出到下次操作的时间——太短可能意味着直接放弃
time_to_next_action_ms: timeToNextAction,
// 用户是否在同一会话内重试
retried_in_session: hasRetry,
})LangChain 团队在他们关于 LLM 可观测性的工程博客中提到一个观点:先做追踪,再定义什么是「好」^[4]^。套用到反馈事件上就是——先确保你捕获了所有隐式行为信号,再去设计显式反馈的交互。隐式信号不依赖用户主动配合,数据量大且行为真实性更高。
域四:失败事件
AI 产品的失败模式比传统产品复杂得多。不只是 HTTP 500 和超时,还包括内容策略拒绝、结构化输出解析失败、Agent 循环不终止、Token 溢出等多种形态。
PostHog 的 AI 可观测性文档列举了几类 AI 特有的失败模式^[1]^:工具调用失败(Agent 调用的工具返回错误或畸形数据)、非终止循环(模型反复调用自身或子 Agent)、结构化输出解析失败(文本正常但 JSON 格式不合法)、Token 溢出(Prompt 超出上下文窗口但静默截断)以及内容策略违规(提供商拦截了响应)。
// ❌ 坏做法:所有失败都归为一个笼统的 error 事件
posthog.capture('ai_error', {
error_message: error.message,
})
// ✅ 好做法:按失败类型分类,附带诊断信息
posthog.capture('ai_failure', {
// 失败分类——这是后续做失败归因分析的核心维度
failure_type: classifyFailure(error),
// 具体错误码或类别
error_code: error.code,
// 失败发生在哪个阶段:输入处理、模型调用、输出解析、工具调用
failure_stage: 'model_call' | 'output_parse' | 'tool_call' | 'input_process',
// 模型版本——某些失败可能只在特定模型上出现
model_id: requestModel,
// 是否可重试
is_retryable: isRetryableError(error),
// 重试次数
retry_count: attemptCount,
// 用户是否触发了降级方案(如切换到更小的模型)
fallback_triggered: usedFallback,
// trace_id 用于关联追踪数据
trace_id: currentTraceId,
})| 失败类型 | 传统产品 | AI 产品 |
|---|---|---|
| 网络错误 | HTTP 超时、DNS 解析失败 | 同左 + 模型提供商限流 |
| 解析错误 | JSON 格式错误 | 结构化输出不合法、工具调用参数缺失 |
| 业务逻辑错误 | 权限不足、数据不存在 | 内容策略拒绝、幻觉输出、循环不终止 |
| 资源错误 | 内存溢出、磁盘满 | Token 溢出、上下文窗口超限 |
| 静默失败 | 极少 | 常见——输出被截断但不报错 |
域五:采纳与结果事件
采纳事件是连接「AI 输出」和「用户价值」的桥梁。用户用了 AI 的输出,不代表 AI 帮用户完成了任务——但用户没用,几乎一定说明某些地方出了问题。
采纳的定义因产品而异。写作工具里可能是「用户把输出插入文档」,设计工具里可能是「用户把生成的布局应用到画布」,代码工具里可能是「用户接受补全并继续编码」。关键是找到你的产品里「用户真正使用了输出」的那个动作,把它作为采纳事件。
// ✅ 采纳事件——记录的是「用户真正使用了输出」
posthog.capture('ai_output_adopted', {
// 采纳方式:直接插入、合并后插入、替换已有内容
adoption_type: 'direct_insert' | 'merged_insert' | 'replace',
// 从输出到采纳的时间——太长可能意味着用户犹豫过
time_to_adoption_ms: timeToAdoption,
// 采纳时输出被修改的比例
modification_ratio: modificationRatio,
// 采纳发生在第几次重试之后
adoption_after_retry: retryCount,
// 关联的 trace_id
trace_id: currentTraceId,
})域六:成本归因事件
Token 消耗和 API 调用成本需要能按功能、用户、模型聚合。否则你无法判断某个 AI 功能到底值不值得继续投入。
这一点在商业上特别重要。我见过不止一个团队在某个功能上用了最贵的模型,跑了半年才发现每个用户的月均成本比订阅费还高。如果成本事件和功能事件、用户事件绑在一起,这种问题上线第一周就能发现。
// ✅ 成本事件——每次模型调用都附带成本信息
posthog.capture('ai_cost_recorded', {
// 功能模块——成本要能聚合到功能级别
feature: 'document_summarizer',
// 模型
model_id: 'gpt-4o',
// Token 消耗
input_tokens: inputTokens,
output_tokens: outputTokens,
// 估算成本(单位:美分)
cost_cents: calculateCost(inputTokens, outputTokens, 'gpt-4o'),
// 用户 ID——用于计算单用户成本
user_id: userId,
// 是否为重试产生的额外成本
is_retry_cost: retryCount > 0,
// trace_id
trace_id: currentTraceId,
})从事件到流程:一次完整的 AI 交互
把六个事件域串起来,一次完整的 AI 功能交互会经过这些阶段:
这个流程覆盖了从输入到采纳的完整链路,每个关键节点都有对应的事件记录。实际操作中不一定需要全部自己实现——OpenTelemetry GenAI 语义约定已经标准化了追踪层的事件格式^[3]^,你要做的是在追踪层之上叠加产品行为事件。
三种接入模式的选择
把事件数据送进分析系统有三种常见模式,各有适用场景:
| 模式 | 实现方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| SDK 包装 | 用包装过的客户端替代原始 SDK | 自动记录输入输出和元数据 | 需要改代码,多步骤 Agent 需要手动补充 span | 中小规模、需要细粒度控制 |
| 代理层 | 把 base_url 指向代理端点 | 零代码改动,只需改 URL | 只能看到单次 API 调用,无法追踪 Agent 链路 | 快速接入、单一 LLM 调用 |
| 框架集成 | 利用 LangChain 等框架的一行集成 | 自动获取 span 级别的可见性 | 和框架绑定,换框架要重新接入 | 已使用编排框架的团队 |
三种模式不互斥。我见过比较成熟的做法是用 SDK 包装做基础追踪,同时在框架层面(如果用了 LangChain 之类的编排工具)做 Agent 级别的 span 关联。
// ❌ 坏做法:手动在每个 API 调用处写埋点,容易遗漏且不统一
async function summarize(text: string) {
const response = await openai.chat.completions.create({
model: 'gpt-4o',
messages: [{ role: 'user', content: text }],
})
// 每次都要记得写这些……
posthog.capture('ai_call', { tokens: response.usage.total_tokens })
posthog.capture('ai_cost', { cost: calculateCost(response.usage) })
return response.choices[0].message.content
}
// ✅ 好做法:用 SDK 包装自动处理,业务代码保持干净
import { wrapOpenAI } from 'your-otel-wrapper'
const instrumentedOpenAI = wrapOpenAI(openai, {
posthog,
// 自动追踪每次调用,记录 token、延迟、模型、完成原因
autoTrace: true,
// 成本计算规则
costTable: COST_TABLE,
// 不记录原文内容(隐私保护)
captureContent: false,
})
async function summarize(text: string) {
const response = await instrumentedOpenAI.chat.completions.create({
model: 'gpt-4o',
messages: [{ role: 'user', content: text }],
})
// 追踪、成本、延迟——全自动
return response.choices[0].message.content
}隐私保护的实操边界
AI 产品的埋点有一个传统产品不太需要面对的问题:输入和输出内容可能包含敏感信息。用户的病历、合同、代码、私人对话——这些都会经过模型,也都可能被你不小心记录到分析系统里。
我的原则是:分析系统记录行为和质量,不记录原文。
// ❌ 坏做法:把用户输入原文存到分析事件里
posthog.capture('ai_input', {
prompt_text: userPrompt, // 可能包含姓名、电话、地址等 PII
})
// ✅ 好做法:记录结构化元数据,不存原文
posthog.capture('ai_input', {
input_length: userPrompt.length,
// 内容类别(用于分析哪类输入最多)
content_category: classifyContent(userPrompt),
// 输入文本的哈希(用于去重和统计唯一输入数)
input_hash: await sha256(userPrompt),
// 是否包含可能的敏感信息
contains_pii_hint: piiDetector.quickScan(userPrompt),
})PostHog 的 AI 工程文档明确建议在发送数据到分析平台之前做 PII 脱敏^[5]^。他们的遥测感知 AI Agent 方案在所有事件进入上下文层之前就跑一遍正则脱敏——邮件地址替换为 [redacted_email],电话号码替换为 [redacted_phone]。这个模式可以直接复用。
OTel GenAI 语义约定把内容捕获设计为三级 opt-in:不记录(默认)、记录到 span 属性、记录到外部存储并在 span 中只放引用 URL^[3]^。生产环境推荐第三种——原始内容留在你自己的存储里,分析系统只看到引用。
| 隐私级别 | 记录内容 | 适用场景 | 风险 |
|---|---|---|---|
| 零内容 | 只记元数据(长度、类型、哈希) | 所有 AI 产品的默认配置 | 最低,无法回溯具体输出 |
| 脱敏后内容 | 记录经 PII 脱敏后的内容 | 需要分析输出质量但内容涉及用户隐私 | 中等,脱敏规则可能有遗漏 |
| 完整内容 | 记录原文到受控存储 | 内部调试、合规审查 | 最高,需严格访问控制 |
| 外部引用 | 原文存独立存储,分析系统只存 URL | 生产环境推荐方案 | 低,需保证引用存储的安全性 |
落地检查清单
我把六个事件域的落地步骤整理成了一份检查清单,按阶段分组。每次设计新的 AI 功能埋点时,我会过一遍这个清单。
阶段一:基础追踪(上线前必须完成)
- 每次模型调用都记录 trace_id,且 trace_id 能贯穿该次交互的所有事件
- 记录输入长度、输入来源类型、上下文类型
- 记录模型标识(请求模型 + 实际响应模型)
- 记录完成原因(正常结束 / 长度截断 / 内容过滤 / 工具调用)
- 记录输入输出 token 数
- 记录首字延迟和端到端延迟
- 所有失败事件按类型分类(网络 / 解析 / 内容策略 / Token 溢出 / 循环)
- 失败事件记录是否可重试和当前重试次数
阶段二:行为反馈(上线后第一周补齐)
- 记录用户是否编辑了输出,以及编辑比例
- 记录用户是否采纳了输出,以及采纳方式
- 记录显式反馈(点赞 / 点踩 / 评分),如果有对应 UI
- 记录重试行为:重试次数、重试间隔、重试前最后输出的状态
阶段三:成本与归因(上线后第一个月完成)
- 每次模型调用记录估算成本,能按功能聚合
- 成本事件和用户 ID 关联,能计算单用户成本
- 重试产生的额外成本能被标记和区分
- 成本数据能和采纳数据关联,计算「每次有效采纳的平均成本」
阶段四:隐私与合规(贯穿全程)
- 分析事件中不包含用户输入的原文
- 如果需要记录内容,走外部存储 + 引用的方案
- 事件属性中的 PII 经过正则脱敏
- 内容捕获有明确的 opt-in 机制,默认关闭
写在最后
回到开头的故事。那个月之后,我花了三天时间把输入质量、模型输出、编辑行为、采纳和失败五个域的事件补齐。第二周的数据分析会上,新的看板清楚地显示:点击率在涨,但重试率也在涨;70% 的生成结果被用户完全丢弃没有采纳;重试三次以上的用户留存率只有 8%。
这些洞察在只有「点击」和「浏览」两个事件的时候完全看不到。
AI 产品的数据分析没有现成的方法论可以照搬。OpenTelemetry GenAI 语义约定在追踪层做了标准化,但产品行为层的埋点设计仍然需要每个团队根据自己的场景来做。六个事件域是一个起点——不一定要一次全做完,但至少要意识到这些维度的存在。等到需要回答「为什么留存不涨」的时候再补,就晚了。
参考资料
- [1] AI observability: what it is, how it works, and which tools to use — PostHog Blog, 2026
- [2] What is LLM Product Analytics? 2026 Guide — Future AGI, 2026
- [3] How OpenTelemetry Traces LLM Calls, Agent Reasoning, and MCP — Greptime, 2026
- [4] Why LLM observability and monitoring need evaluations — LangChain
- [5] AI engineering with PostHog — Docs — PostHog Documentation
- [6] Telemetry-Aware AI Agents: Building LLM Features That Understand Product Behavior Using PostHog — Medium, 2026
- [7] Observability of LLM Applications: Exploration and Practice from the Perspective of Trace — Alibaba Cloud, 2024