指标
只想知道看板怎么算:看 接入上报 的「看板怎么算」。 这份文档定完整口径:每个指标怎么定义、用哪些事件算、分子分母是什么、哪些情况排除。 chatkit 只负责发出事件(事件),上报、存储、看板、告警由接入方做。口径写在这里, 结论字段(
outcome)由 Core 直接算好,接入方可直接使用该字段,不必另写结论分类;最终指标仍取决于事件完整性、去重和归并。
两套口径,选一套
turn_summary 由 Core 按本文的口径从原始事件归并而成:结论、是否计入有效轮次、首字延迟、完成耗时都已经算好。
两套口径做看板时二选一,不要相加,否则同一轮会算两次。下面每个指标都同时给出两套算法。
统计单位:一轮
一轮 = 一次 Agent 执行,用 sessionId 与 runId 一起标识,可以由本地发送或远端触发;不要假设不同会话的后端轮次编号全局唯一。chatkit 里的「会话」指整个对话(sessionId),
下面叫「会话完成率」「会话完成耗时」的指标,统计单位都是一轮。
一轮的结论
每一轮落进下面一类且只落一类,取 turn_summary.outcome(原始事件口径取 run_settled.outcome,没有终态的取 run_abandoned):
连接断开或恢复失败本身不一定产生终态。没有收到 run_settled 或 run_abandoned 的轮次,应单独观察,不能自行补成失败或中断。completed 只表示有正文、媒体或数据片段,不代表内容正确、业务卡片成功渲染或用户任务已经解决。
「卡住」:在跑、却超过阈值(默认 60 秒,stallThresholdMs)没有被接受的新事件或状态更新,发 run_stalled;等待外部输入
(awaiting-external,比如等用户确认)不算。卡住之后又恢复并完成的,结论仍是 completed,只是 stallCount 大于 0。
通用规则
- 按
sessionId+runId归并,以最后一个结论为准。 汇总口径取revision最大的一条;原始事件口径收到run_abandoned后,若同一轮又收到run_settled,用后者更新统计,不能累计成两轮。 - 去重与排序用信封字段。 重传、离开页面通道与普通通道各发一次时按
eventId去重;判断先后用seq(同一次页面访问内递增),不用ts。 关联同一次页面访问用pageVisitId。 useChatSessions内切换会话不会产生放弃事件;单对话 hook 换sessionId或卸载才释放旧运行时。页面侧监听pagehide,并非每次切换浏览器标签页都会触发。- 有效轮次(稳定类的分母)= 结论为
completed、empty、failed、interrupted的轮次。用户主动停止(aborted)是 用户的选择,不算系统不稳定,单独看用户中止率;cancelled不算一轮。所以完成率 + 空回复率 + 失败率 + 中断率 = 100%。 - 耗时看分位数(P50 / P90 / P99),不看平均值:长尾才是体验问题。
- 耗时用载荷里的
*Ms,不用ts相减。 载荷里的耗时用单调时钟算,不受系统时间调整影响;ts是墙钟,只用来对齐与排查。 刷新后重新接上、远端发起的一轮(origin: 'remote')没有本地受理时点,耗时退回墙钟计算,可能混入服务端时间,应与本地发起的一轮分开看。 - 页面关闭时的事件要用离开页面通道发(
navigator.sendBeacon或埋点 SDK 的对应能力),否则run_abandoned会丢,中断率偏低。 - 刷新页面后重新接上的一轮(
origin: 'remote')没有send_result,不进首字延迟、首次响应的样本(汇总里也没有firstPaintMs/firstOutputMs)。 - 前台与后台分开看。 终态时会话在后台的一轮(
turn_summary.background为 true)用户并没有在等,没有首字样本,完成耗时也不代表体验,体验类指标只取前台的一轮。
性能
上述口径不包含输入框校验或异步 onSend 的耗时。若要统计“点击发送到响应”,需要业务方另记点击时刻。first_paint 在正文组件渲染后触发,不是浏览器精确绘制时间;历史展示、后台会话切回后的延迟渲染应与持续可见的对话分开分析。
run_first_output 记录运行时的首次执行信号,正文、思考、工具阶段或工作项都可能触发,不保证此时正文已经显示。latencyMs 与 run_started.queuedMs 都用单调时钟,可以直接相加。
短回复可能先落终态、界面随后才画出正文:turn_summary 在终态之后最多等 firstPaintGraceMs(默认 1000 毫秒)的首次绘制再发出;
到点没等到(低端机、主线程繁忙)就不带 firstPaintMs。等待超时的一轮多了,首字延迟会偏乐观,可调大这个值。
稳定
有效轮次:汇总口径是 turn_summary.effective 为 true 的轮次;原始事件口径见上面的「通用规则」。
发送失败:这里只统计运行时未受理或派发失败,不等于远端一定没有执行。onSend 拦截发生在运行时事件之前,不计入下面的分子或分母。算以下两种:
- 受理阶段就失败:
send_result的stage: 'accepted'且accepted: false; - 派发阶段失败:
send_result的stage: 'dispatched'、accepted: false,且reason是adapter-rejected(适配器拒收)或error(派发时抛错)。
派发阶段的 aborted、aborted-before-dispatch、cancelled-before-dispatch、runtime-disposed 表示停止、撤下排队或运行时已释放,不算发送失败。
send_blocked 是接入方自己的中间件拦下的(比如内容审核),不进分母。
工具名是协议给的原始名(mcp__server__search、webSearch…)。同一个工具在不同协议下可能有不同名称;需要合并时,在自己的上报层维护明确的名称映射,不要依赖包内未导出的工具函数。
体验
重新生成的次数:turn_summary 里 via 为 regenerate 的轮次;原始事件口径是 send_result 里 stage: 'accepted'、accepted: true,且 via 为 regenerate 的条数。
编辑后重发单独统计,条件相同,via 为 edit;需要时两者相加作为“重新尝试”的次数;仅凭点击行为不能确定用户是否不满意。
普通发送的 via 是 send,统计提问量时只算它。
辅助观测
不作为核心指标,排查时有用:
- 卡住率:有效轮次中
stallCount大于 0 的轮次 ÷ 有效轮次;卡住时的阶段看run_stalled.phase,是否已有输出看hasOutput。 - 排队等待:
turn_summary.queuedMs或run_started.queuedMs,用户在上一轮没结束时连续发送的等待。 - 首字等待超时:前台、有正文、却没有
firstPaintMs的汇总 ÷ 前台有正文的汇总,用来校准firstPaintGraceMs。 - 阶段耗时:同一轮相邻
run_phase_changed的时间差可估算中间阶段;首段需关联run_started,末段需关联run_settled。缺少边界事件的样本不应算作完整阶段。
维度与扩充属性
每个事件都带 sessionId、runId(运行相关的)和接入方扩充的 attributes。常用维度建议在 useChatSessions / useChatRuntime 的
eventAttributes 里给(入口与合并顺序见 事件):
属性名由接入方约定,chatkit 不解释。只收扁平的基础类型,不要放用户输入。
重新生成与编辑后重发沿用发送的事件,载荷里的 via 是 regenerate / edit。统计用户提问量、发送失败率时按 via 分开看,
重新生成不算新的提问。
从事件到指标
汇总口径:用 createTelemetry 接入(默认就是 summary 预设),数据侧每轮一条 turn_summary,按 sessionId + runId 取 revision 最大的一条,
上面的公式直接按字段算。开发时可以用 @volcengine/chatkit/devtools 的 createTurnMetrics 在页面里实时看到同样口径算出的结果。
原始事件口径:createTelemetry({ track, events: 'core-metrics' }) 上报 METRICS.md 用到的原始事件(不含 turn_summary)。数据侧按以下规则归并:
- 按
sessionId+runId关联运行事件,关联首字时再限定为同一个pageVisitId。 - 受理时刻只取
send_result中stage: 'accepted'且accepted: true的事件;首字延迟直接取first_paint.sinceAcceptedMs。 - 结论更新按同一轮覆盖(按
seq取最后一个),不能把run_abandoned和后续run_settled相加。 - 没有
runId的发送尝试、拦截、历史加载和渲染错误也要保留,分别按各自的指标统计。
常见问题
中断率比预期低。 页面关闭时的事件没发出去:run_abandoned 和同时发出的 turn_summary 要走离开页面通道(createTelemetry 的 beacon),
普通的异步请求在页面关闭时会被浏览器丢掉。
首字延迟的样本比轮次少。 正常:远端发起、刷新后接上的一轮没有受理时点;没有正文的一轮没有首次绘制;后台落定的一轮不进样本;
终态之后等了 firstPaintGraceMs 还没画出正文的一轮不带 firstPaintMs。
切走会话之后还在收到事件。 正常:切走的会话在后台继续接收数据,那一轮跑完照样报,用 sessionId 区分;它的 turn_summary 标着 background: true。
同一轮出现了两条 turn_summary。 先被放弃(页面隐藏、运行时释放)报了一条,之后又落了终态再报一条,revision 递增,取最大的一条。
完成率和失败率加起来超过 100%。 两套口径相加了:turn_summary 和 run_settled 只能选一套做看板。verifyTelemetry 会检查出这种情况。