Skip to content

3.6 数据结构图谱

学习目标

完成本节后,你将能够:

  1. 用一张分层图解释 Langfuse 的产品对象如何落到契约和存储。
  2. 说清 trace、observation、score、dataset、experiment、eval job 的业务关系。
  3. 区分 Postgres、ClickHouse、Redis/BullMQ、S3/blob 中分别保存什么。
  4. 理解 v4 为什么把 observation/event row 作为事实底座,而不是继续让 trace row 承载所有事实。

3.6.1 先给结论:先分层,再看表

Langfuse 的数据结构不能直接从数据库表开始读。正确顺序是:

这张图是本节最重要的读法。比如 trace_id 在产品层表示“一次请求或工作流的相关性边界”,在 ClickHouse v4 里只是每个 observation row 上的关联字段,在 UI 查询里又可以被 uniq(trace_id) 聚合成 trace 计数。

如果直接看表,很容易误解成“Langfuse 就是 trace CRUD”。实际更准确的抽象是:

text
运行事实:observation/event row
相关性边界:trace_id、session_id
评价事实:score
实验事实:dataset run item 连接 dataset item 与 trace/observation
配置事实:prompt、score config、eval template、job configuration
执行状态:queue job、job execution、raw event pointer
查询投影:events_core、analytics_events_core、field sets

3.6.2 业务实体关系图:对象先按语义分组

下面这张图不追求把所有字段画完,而是帮助你建立“谁连接谁”的脑图。

读这张图时抓住三条主线:

主线最小闭环含义
观测闭环Session -> Trace -> Observation tree用户请求被拆成一棵操作树。trace 负责把节点归组,observation 负责表达每个实际操作。
评价闭环JobConfiguration -> JobExecution -> Scoreevaluator 的配置和执行状态在 Postgres,评价结果最终统一落成 score。
实验闭环DatasetItem -> DatasetRunItem -> Trace/Observation -> Scoredataset 是输入材料,experiment/run 是执行快照,run item 把样本、运行结果和评分连起来。

3.6.3 物理存储图:同一业务对象会跨多个存储

Langfuse 的存储不是按“对象名”一一对应拆的,而是按状态性质拆的。

这张图解释了一个常见疑问:为什么一个功能会同时碰 Postgres、ClickHouse、Redis 和 S3?

以 observation eval 为例:

状态存在哪里为什么
evaluator 配置Postgres job_configurationseval_templates需要管理 UI、事务更新、权限和版本关系。
命中的 observation 事实ClickHouse observationsevents_*这是高吞吐运行事实,需要按时间、project、type、metadata 查询。
待执行任务Redis/BullMQevaluator 执行要异步、可重试、可限流。
observation 快照S3/blob执行 evaluator 时不希望每个 job 再查一遍 ClickHouse,也避免队列 payload 过大。
最终评价结果ClickHouse scores + Postgres config 关联score 是分析事实,config 是事务型约束。

3.6.4 Observation tree:一条请求怎么拆成事实行

agent/workflow 的一次请求通常不是线性的。它可能同时调用模型、检索、工具、子 agent。Langfuse 用 observation tree 表达这件事。

落到 observation/event row 时,核心不是“更新一行 trace”,而是写多行事实:

row关键字段表达的事实
O1trace_id=T1span_id=O1parent_span_id=''type=SPAN这次应用调用的根操作。
O2trace_id=T1span_id=O2parent_span_id=O1type=GENERATIONmodel/cost/usage主 agent 的一次模型调用。
O4trace_id=T1span_id=O4parent_span_id=O3type=TOOLretrieval 下的一次工具调用。
O9trace_id=T1span_id=O9parent_span_id=O1type=GENERATIONinput/output最终回答生成。

因此 trace 页面不是从一行 trace 读出所有子对象,而是:

text
WHERE project_id = 当前项目
  AND trace_id = T1
读取同一组 observation/event row
按 parent_span_id 组装成树

这就是“trace 是相关性边界,observation 是事实节点”的具体含义。trace 仍然很重要,但它不必承担每个子步骤的输入、输出、成本、错误和评分。

3.6.5 经典模型到 v4:事实底座为什么变化

经典模型和 v4 observation-centric 模型的差异可以画成这样:

变化的关键不是“trace 消失”,而是 trace 的职责变了:

经典模型里 trace 常承担v4 更推荐的表达
trace row 保存请求级 input/output根 observation 保存整体输入输出;子 observation 保存自己的输入输出。
trace row 保存 user/session/tags/metadata,然后查询时 join 或补上下文SDK/ingestion 把 trace context 传播到每个 observation row。
trace list 是默认入口,再点进去看操作observation table 是默认入口,trace 通过 trace_id 聚合出来。
evaluator 常评价整条 traceevaluator 可以直接命中某个 observation/event。

这个取舍符合 ClickHouse 的使用方式:排序键和查询过滤要围绕高频查询模式设计,重复 join 的上下文更适合在写入或预处理时铺到分析宽表里。Langfuse 的 events_full / events_core 正是把“写入时多带上下文”换成“查询时少 join、少扫大字段”。

3.6.6 ClickHouse 里的三种形态:legacy、full、core

当前 OSS repo 里会同时看到 legacy 表和 v4 events 表。它们不是互相矛盾,而是迁移期并存。

events_full 可以理解成“把一行 observation 需要的上下文都铺平”:

字段组例子为什么放在 event row 上
关联字段project_idtrace_idspan_idparent_span_id过滤租户、还原 trace 树、定位 observation。
trace contexttrace_nameuser_idsession_idtagsreleaseversion列表和聚合常按这些字段过滤,避免每次 join trace。
observation factnametypestart_timeend_timelevelstatus_message每个操作本身的事实。
model/usage/costprovided_model_nameusage_detailscost_detailstotal_costLLM observability 的核心分析维度。
I/O 和 metadatainputoutputmetadata_namesmetadata_values详情页、全文搜索、eval 需要。
experiment contextexperiment_idexperiment_item_idexperiment_item_expected_output让实验结果可以和 observation 查询组合。

events_core 则是给常见查询准备的轻量形状。列表页、filter options、count 和聚合不应该默认读取完整 input/output/metadata。需要完整内容时再从 events_full 按 key 取。

3.6.7 Experiment / Eval 的数据加工链路

实验和评价最容易读乱,因为它横跨“离线材料、线上运行、异步 evaluator、score”。可以按下面这张图理解:

这里有两个容易混淆的点:

容易混淆的点正确理解
Dataset 是不是 eval 的结果?不是。Dataset 是材料,DatasetRun/Experiment 是一次执行快照,Score 才是评价结果。
event target 是普通日志事件吗?不是。这里的 event 是 observation-scoped evaluator,实际评价的是某个 observation/event row。

3.6.8 读源码时的“实体翻译表”

下面这张表可以当成源码阅读的索引。

产品对象业务含义主要契约/实现权威存储或投影
Project租户内的项目边界Project Prisma model、project-scoped tRPC/API contextPostgres projects
API key外部写入和读取的认证凭证ApiKey Prisma model、Public API middlewarePostgres api_keys
Trace一次请求或工作流的相关性边界trace domain、trace_id、trace UIlegacy ClickHouse traces;v4 中是 event row 上的 context
Observation/Event一个实际操作节点observation domain、span_idparent_span_id、events query builderlegacy observations;v4 events_full / events_core
Score统一评价结果score domain、score config、eval completionClickHouse scores,配置在 Postgres
Dataset测试材料集合Prisma Dataset、dataset API/UIPostgres datasets
DatasetItem一个测试样本Prisma DatasetItem、版本字段、source linksPostgres dataset_items
Experiment / DatasetRun一次离线运行快照Prisma DatasetRuns、experiment UI/servicePostgres dataset_runs
DatasetRunItem样本与运行结果的连接Prisma DatasetRunItems、trace/observation linksPostgres dataset_run_items,v4 event row 可铺 experiment context
EvalTemplateevaluator 的执行定义Prisma EvalTemplatePostgres eval_templates
JobConfigurationevaluator 何时运行、命中什么对象Prisma JobConfiguration、targetObject/filter/samplingPostgres job_configurations
JobExecution一次 evaluator 执行状态Prisma JobExecution、queue processorPostgres job_executions
Raw ingestion eventSDK/OTel 原始 payloadprocessEventBatch、queue payload pointerS3/blob
Queue job异步执行指令packages/shared/src/server/queues.tsRedis/BullMQ

读代码时把“产品对象、契约、存储”三列同时打开,才不会把 ClickHouse 的宽表字段误认为产品模型本身。

3.6.9 本节对应源码

主题源码入口
产品数据模型../langfuse-docs/content/docs/observability/data-model.mdx../langfuse-docs/content/docs/v4.mdx
Score / Experiment 产品语义../langfuse-docs/content/docs/evaluation/scores/data-model.mdx../langfuse-docs/content/docs/evaluation/experiments/data-model.mdx
Postgres 实体packages/shared/prisma/schema.prisma
legacy ClickHouse 表packages/shared/clickhouse/migrations/unclustered/0001_traces.up.sql0002_observations.up.sql0003_scores.up.sql
v4 events 表packages/shared/clickhouse/scripts/dev-tables.sh
v4 查询packages/shared/src/server/queries/clickhouse-sql/event-query-builder.ts
ingestion 请求侧packages/shared/src/server/ingestion/processEventBatch.ts
ingestion workerworker/src/queues/ingestionQueue.tsworker/src/services/IngestionService/index.ts
eval target 和执行packages/shared/src/features/evals/types.tsworker/src/features/evaluation/observationEval/**

下一节

draw.io 架构图