拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Langfuse 数据关联时间窗口设计:Trace / Observation / Score 关联查询的 lookback 保证与 ClickHouse 实现解析

Langfuse 数据关联时间窗口设计Trace / Observation / Score 关联查询的 lookback 保证与 ClickHouse 实现解析【免费下载链接】langfuse Open source AI engineering platform: LLM evals, observability, metrics, prompt management, playground, datasets. Integrates with OpenTelemetry, LangChain, OpenAI SDK, LiteLLM, and more. YC W23项目地址: https://gitcode.com/GitHub_Trending/la/langfuse导读本指南围绕 Langfuse 仓库中 packages/shared/src/server/repositories/README.md 记录的数据关联时间保证展开剖析 Trace、Observation、Score 三类核心遥测数据在跨表关联查询时为何需要时间回看窗口lookback interval以及这些窗口背后的统计依据与 ClickHouse SQL 落地方式。读完本文你将掌握 Langfuse 在 ClickHouse 上做多表关联时的三个核心时间常量INTERVAL 2 DAY与两个INTERVAL 1 HOUR、它们在traces.ts/observations.ts/scores.ts三个仓储实现中的真实调用位置以及这套以大概率时序关系换取查询性能的设计思路可直接复用到自建可观测性平台的数据建模中。一、背景为什么关联数据需要时间窗口保证Langfuse 的 Trace追踪、Observation观测即 Span / Generation / Event与 Score评分三类数据在 ClickHouse 中分表存储但它们之间存在紧密的父子与引用关系一个 Trace 下挂多个 ObservationScore 则依附于 Trace 或 Observation。当用户按时间范围查询某个 Trace 下有哪些 Observation或某个 Observation 属于哪个 Trace时仓储层必须跨表 JOIN。问题在于这三类数据并非在同一时刻写入。由于 SDK 上报时机、异步事件队列、eval 作业后置落库等原因Observation 的start_time、Trace 的timestamp、Score 的timestamp之间存在系统性的时间偏移。如果 JOIN 时只用精确相等或过窄的时间条件会漏掉大量本应关联成功的数据。因此 Langfuse 在仓储层引入了一组时间回看窗口基于对生产数据的统计分析为不同关联方向预先放宽时间边界在不牺牲查询性能的前提下保证绝大多数关联能被正确建立。二、三类关联方向与统计依据原文档基于 Langfuse 内部两个性能优化 issueLFE-2745「Improve generations table query performance」与 LFE-2409「Table queries」的调研给出了三个方向的时序结论关联方向核心场景统计结论采用的窗口由 Observation 找 Trace在 generations 等表格中反查所属 Trace96% 的 observation 开始时间出现在 trace 时间戳之后 2 分钟内2 天2880 分钟回看由 Trace 找 Observation在 trace 详情中展开其下所有观测97% 的 trace 时间戳出现在 observation 开始时间之前 2 分钟内1 小时截止由 Score 时间戳找 Trace / Observation按评分筛选或反查被评对象Score 大概率发生在 trace / observation 之后1 小时截止关键解读为什么窗口差异如此悬殊由 Observation 找 Trace 使用 2 天窗口Observation 的start_time晚于 Trace 的timestamp是主流96% 在 2 分钟内但存在非常大的长尾very large long-tail——少数 Observation 可能由于延迟上报、离线批量回填等原因与其 Trace 的时间差远超 2 分钟。为了不让这些数据在关联查询中失踪Langfuse 最终将回看窗口放宽到 2 天2880 分钟。由 Trace 找 Observation 使用 1 小时窗口Observation 极大概率发生在 Trace 之后即向前回溯 1 小时即可覆盖绝大多数 Observation 的写入因此维持 1 小时截止避免过度放宽导致查询扫描范围失控。Score 关联同样维持 1 小时Score 的timestamp大概率晚于其依附的 Trace / Observation因此同样采用 1 小时回看。三、源码落地constants.ts 中的三个核心常量这些窗口不是散落在各查询中的魔法数字而是集中在仓储层的常量文件中统一声明见 packages/shared/src/server/repositories/constants.ts// Rule of thumb: If you join observations from left, use observations to trace and vice versa // t.timestamp observation.start_time - 2 days export const OBSERVATIONS_TO_TRACE_INTERVAL INTERVAL 2 DAY; // observation.start_time t.timestamp - 1 hour export const TRACE_TO_OBSERVATIONS_INTERVAL INTERVAL 1 HOUR; // observation.start_time s.timestamp - 1 hour // t.timestamp s.timestamp - 1 hour export const SCORE_TO_TRACE_OBSERVATIONS_INTERVAL INTERVAL 1 HOUR;三点值得注意命名即方向OBSERVATIONS_TO_TRACE_INTERVAL表示从 Observation 侧出发关联到 Trace时使用的窗口TRACE_TO_OBSERVATIONS_INTERVAL反之。命名与查询中 JOIN 的左表方向一一对应。字符串直接内嵌进 ClickHouse SQL常量值采用 ClickHouse 的INTERVAL n DAY/HOUR语法可直接拼入 WHERE 子句如start_time {startTime: DateTime64(3)} - INTERVAL 2 DAY。注释中的经验法则Rule of thumbIf you join observations from left, use observations to trace and vice versa——若 SQL 中 observations 在 JOIN 左侧即以 observation 为驱动表就使用OBSERVATIONS_TO_TRACE_INTERVAL反之亦然。这条法则保证了时间窗口与关联方向的匹配。四、实际应用一traces.ts 中的双向窗口4.1 由 Observation 聚合反查 Trace存在性检查在 packages/shared/src/server/repositories/traces.ts 的 trace 存在性查询中先构建一个observations_aggCTE 对 Observation 按trace_id, project_id分组聚合随后与traces表 JOIN。聚合时对 Observation 的start_time使用 2 天回看FROM observations o WHERE o.project_id {projectId: String} AND o.start_time {timestamp: DateTime64(3)} - INTERVAL 2 DAY -- OBSERVATIONS_TO_TRACE_INTERVAL GROUP BY trace_id, project_id4.2 由 Trace 反查 Observation时间下界同一文件中对traces表本身施加 1 小时回看保证能捞到Trace 之后 1 小时内开始的 ObservationFROM traces t WHERE ${tracesFilterRes.query} AND t.project_id {projectId: String} AND t.timestamp {timestamp: DateTime64(3)} - INTERVAL 1 HOUR -- TRACE_TO_OBSERVATIONS_INTERVAL AND t.timestamp {timestamp: DateTime64(3)} INTERVAL 2 DAY -- 上界兜底这里还能看到一个细节查询同时使用了下界- INTERVAL 1 HOUR与上界 INTERVAL 2 DAY把时间窗口夹成一个带偏移的滑动区间。此外代码注释明确指出该查询刻意跳过 ClickHouse 的FINAL修饰符We skip FINAL here只要某条 trace 在某一时刻满足条件即视为命中即使其后被更新为非匹配状态由于更新通常是追加式的这种取舍在性能收益面前被认为可接受。五、实际应用二observations.ts 中的窗口packages/shared/src/server/repositories/observations.ts 是窗口使用最密集的仓储文件仅对OBSERVATIONS_TO_TRACE_INTERVAL/TRACE_TO_OBSERVATIONS_INTERVAL的引用就多达 8 处如第 86、188、304、504、858、1202、1880-1881 行。最典型的是checkObservationExists函数/** * Notes: * • Filters with two days lookback window subject to startTime * • Used for validating observation references before eval job creation */ export const checkObservationExists async ( projectId: string, id: string, startTime: Date | undefined, ): Promiseboolean { const query SELECT id, project_id FROM observations o WHERE project_id {projectId: String} AND id {id: String} ${startTime ? AND start_time {startTime: DateTime64(3)} - ${OBSERVATIONS_TO_TRACE_INTERVAL} : } ORDER BY event_ts DESC LIMIT 1 BY id, project_id ; // ... };它有两个值得写进实践笔记的要点用途在 eval 作业创建前校验被引用的 observation 是否真实存在。因为 eval 的结果Score会在 observation 之后很久才落库校验时必须用 2 天回看窗口对准 observation 的start_time否则刚写入的 observation 可能校验失败。写法startTime参数非必填未传入时直接跳过时间过滤传入时由convertDateToClickhouseDateTime转为 ClickHouse 的DateTime64(3)参数并与窗口常量做减法。这形成了参数化时间 常量窗口的通用模式在 traces.ts 与 scores.ts 中反复出现。六、实际应用三scores.ts 中的评分关联窗口packages/shared/src/server/repositories/scores.ts 中SCORE_TO_TRACE_OBSERVATIONS_INTERVAL1 小时被用于按 Score 时间戳反查其依附对象例如AND s.timestamp {traceTimestamp: DateTime64(3)} - INTERVAL 1 HOUR -- SCORE_TO_TRACE_OBSERVATIONS_INTERVAL函数注释也直接点明语义When provided, addsAND s.timestamp minTimestamp - SCORE_TO_TRACE_OBSERVATIONS_INTERVAL。含义是当用户以某个最小时间戳筛选评分时查询会向前放宽 1 小时从而覆盖评分虽晚于被评对象、但时间偏移在 1 小时内的绝大多数场景如第 530、658、2269 行。由于评分通常是对已完成 trace/observation 的事后反馈其时间戳天然晚于被评对象向前放宽 1 小时即可捕获长尾中的绝大多数无需像 Observation→Trace 方向那样放宽到 2 天。七、测试与回归保障这些时间窗口不仅是文档约定也被测试显式固化。在 packages/shared/src/server/services/traces-ui-table-service.test.ts 中仓储模块被 mock 时即断言了两个关键常量的取值vi.mock(../repositories, () ({ OBSERVATIONS_TO_TRACE_INTERVAL: INTERVAL 2 DAY, SCORE_TO_TRACE_OBSERVATIONS_INTERVAL: INTERVAL 1 HOUR, // ... }));这意味着任何对窗口值的调整例如将 2 天缩短为 1 天都会触发测试快照/断言变更从而在 CI 中暴露对关联行为的意外影响。若你希望修改窗口策略应同步更新 constants.ts、上述测试 mock 以及仓储层注释三者保持口径一致。八、设计启示与使用注意事项综合文档与源码这套时间窗口设计可以提炼出三条可迁移的工程经验用统计分布指导窗口取值而非拍脑袋2 天与 1 小时的差异来自真实数据分布96%/97% 落在 2 分钟内与长尾观察。设计跨表关联的时间边界时应先用数据分布确定主流偏移区间再针对长尾单独评估是否值得扩大扫描范围。窗口与 JOIN 方向强绑定OBSERVATIONS_TO_TRACE_INTERVAL与TRACE_TO_OBSERVATIONS_INTERVAL方向相反、取值不同2 天 vs 1 小时使用时必须遵循左表驱动的经验法则方向用错会直接导致漏数据或查询退化。把窗口提升为命名常量并写注释Langfuse 将三个窗口收敛为三个常量并附带方向语义注释与经验法则使所有调用点traces.ts、observations.ts、scores.ts 共十余处可读、可审计、可单点调整。这是同类 ClickHouse 项目中值得借鉴的组织方式。最后提醒以上窗口属于查询层的宽松时间边界服务于 UI 表格、eval 校验与评分筛选等场景并不改变数据本身的写入语义在自定义查询或二次开发中沿用这些常量时请结合 observations.ts、traces.ts 与 scores.ts 的实际调用上下文确认方向避免误用。【免费下载链接】langfuse Open source AI engineering platform: LLM evals, observability, metrics, prompt management, playground, datasets. Integrates with OpenTelemetry, LangChain, OpenAI SDK, LiteLLM, and more. YC W23项目地址: https://gitcode.com/GitHub_Trending/la/langfuse创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门