Skip to content

3.7 业务实体关系:从 Trace 到 Experiment

学习目标

完成本节后,你将能够:

  1. 说清 Observation -> Trace -> Session 在业务中表达什么。
  2. 解释 dataset、dataset item、dataset run、dataset run item 为什么不是一张表就能说完。
  3. 区分 Score、ScoreConfig、Evaluator、EvalTemplate、JobConfiguration、JobExecution 的职责。
  4. 从“线上问题沉淀为 dataset,再跑 experiment,再产出 score”的完整链路理解 Langfuse 的数据抽象。

3.7.1 先给结论:Langfuse 有四类实体,不是一条 Trace 线

初学者很容易把 Langfuse 理解成“trace 管理系统”。这只说对了入口,没有说对抽象。更准确的分组是:

实体组解决的问题代表实体
观测实体应用运行时实际发生了什么ObservationTraceSession
评价实体结果好不好,谁给的评价,按什么 schema 评价ScoreScoreConfigEvaluatorAnnotationQueue
实验实体如何把线上样本变成可重复测试,再比较多个版本DatasetDatasetItemDatasetRunDatasetRunItem
执行实体异步任务如何调度、重试、记录状态JobConfigurationJobExecution、BullMQ job、S3 snapshot

这张图的读法是:

  • Observation 是运行事实的细粒度节点。
  • Trace 是把一组 observations 聚成一次请求或工作流的相关性边界。
  • Session 是把多个 traces 聚成一次长交互的边界。
  • Score 是所有评价结果的统一落点。
  • DatasetRun 是当前 repo 中承载 experiment 语义的对象。
  • JobConfigurationJobExecution 不等于 score,它们只是“如何产生 score”的配置和执行状态。

3.7.2 Observation:最小运行事实节点

源码中的 observation 类型来自 packages/shared/src/domain/observations.ts

类型业务含义常见字段重点
SPAN通用过程、函数、工作流节点startTimeendTimeinputoutputmetadata
EVENT一个瞬时事件或日志化操作startTimelevelstatusMessage
GENERATIONLLM 调用modelmodelParametersusageDetailscostDetailsprompt*
AGENTagent 级别的执行节点可有模型、工具、输入输出和子节点
TOOL工具/函数调用toolCallstoolCallNamesinputoutput
CHAINchain 或组合步骤输入输出、子 observation
RETRIEVER检索步骤query、返回文档、metadata
EVALUATORevaluator 自身的运行节点evaluator 执行 trace 里的内部步骤
EMBEDDINGembedding 调用model、usage、cost
GUARDRAIL安全/策略检查pass/fail、策略输出、metadata

其中 GENERATIONAGENTTOOLCHAINRETRIEVEREVALUATOREMBEDDINGGUARDRAIL 在源码里被归为 generation-like:它们可能带模型、usage、cost、prompt 或工具调用语义。

一个 observation 不是孤立行。它至少要回答五个问题:

问题关键字段说明
属于哪次请求?traceId / trace_id把节点归到同一条 trace。
在树里的父节点是谁?parentObservationId / parent_span_id还原嵌套调用关系。
它做了什么?typenamelevelstatusMessage用于阅读、筛选、错误定位。
输入输出是什么?inputoutputmetadata用于 debug、评估、沉淀 dataset。
成本和性能如何?startTimeendTimeusageDetailscostDetails用于 latency、token、cost 分析。

3.7.3 Observation 如何构成 Trace,Trace 如何构成 Session

在 LLM 应用里,不要把这三层理解成普通后端里的“HTTP 请求 -> span -> 子 span”。Langfuse 兼容 OpenTelemetry,但它要表达的核心对象是 LLM 应用的一次交互和其中的模型/工具/检索/agent 节点

更贴近业务的理解是:

text
Session:同一个用户和 AI 应用的一段连续对话或长期任务
  Trace:其中一个用户 turn 触发的一次 agent run / workflow run
    Observation:这次 agent run 里的一个 LLM 运行节点
      子 Observation:这个节点内部继续调用的模型、工具、检索、guardrail 或子 agent

这张图的重点不是“每个方块都是一个后端 span”,而是:

在 LLM 应用里的含义例子
Session一段连续对话或长期任务,用来观察跨多轮的质量用户从咨询退款政策,到提供订单信息,再到确认退款。
Trace一个用户 turn 触发的一次完整 agent/workflow run“用户问能不能退款”这一轮,包含意图识别、检索、生成回答和 guardrail。
Observationtrace 内部可单独调试、计费、评价的 LLM 节点一次 generation、一次 retriever、一次 tool 调用、一次 guardrail、一个子 agent。

落到当前 repo 的实现,要分清三层:

当前表示读源码入口
业务关系session 包含多条 trace,trace 包含 observation tree../langfuse-docs/content/docs/observability/data-model.mdx
domain 契约TraceDomain.sessionIdObservationSchema.traceIdparentObservationIdpackages/shared/src/domain/traces.tsobservations.ts
存储/查询legacy tracesobservations;v4 events_full/events_coreClickHouse migrations、dev-tables.sh、events repository

结论不是“trace 是最低单位”。 对运行事实来说,最细的可分析节点是 observation/event row;对用户体验来说,trace 是更好阅读的一次请求;对长会话来说,session 是更高一层聚合。

3.7.4 为什么 agent/workflow 更适合 Observation Tree

一条 agent 请求可能跨多个模型、工具、检索、子 agent。用一行可变 trace 记录所有细节会遇到三个问题:

问题如果只维护一行 trace用 observation tree
并发步骤多个工具或子 agent 同时更新同一行,顺序和覆盖语义复杂每个步骤是一行事实,天然可并发写入
局部评价想评价“最终回答”或“retrieval 质量”时粒度太粗evaluator 直接命中某个 observation
成本拆分总 cost 有了,但不知道哪个步骤贵每个 generation-like observation 自带 usage/cost

这也是 v4 抽象变化的收益:trace 没有消失,但更多变成 trace_id 这个相关性字段;事实本身落在 observation/event row 上。查询 trace 页面时,再按 trace_id 把这些 rows 取出来并按 parent_span_id 组装成树。

3.7.5 Dataset:不是结果表,而是可复用测试材料

Dataset 的业务含义是“可重复运行的测试材料集合”。它不是实验结果,也不是 score。

源码里对应:

实体存储关键字段业务解释
DatasetPostgres datasetsnamemetadatainputSchemaexpectedOutputSchema一组测试材料和 schema 约束。
DatasetItemPostgres dataset_itemsinputexpectedOutputmetadatasourceTraceIdsourceObservationId一个测试样本,可以从线上 trace/observation 沉淀而来。
版本字段Postgres dataset_itemsvalidFromvalidToisDeleteditem 更新不是简单覆盖,而是保留版本边界。

从 trace 添加到 dataset 的链路可以这样看:

这个链路体现了产品闭环:线上失败样本不是只停留在 trace 页面,而是被复制成 dataset item,成为后续 experiment 的固定输入。

3.7.6 Dataset Run / Experiment:一次运行怎么表达

当前 repo 中 DatasetRun 基本就是 experiment run 的实现形态。产品上说“跑一次实验”,源码里通常是:

text
Dataset
  -> DatasetRun / Experiment
    -> DatasetRunItem
      -> Trace
        -> Observation tree
      -> Score

注意 score 有两种常见层次:

层次Score 怎么挂业务含义
item/result 级 score挂到 traceIdtraceId + observationId某个 dataset item 的一次运行输出好不好。UI 可通过 dataset_run_items_rmt.trace_id 把它归到 run。
run 级 score直接挂 datasetRunId整个 experiment run 的聚合评价,例如 pass rate、平均质量、run evaluator 输出。

这就是为什么 scores 表既有 trace_id/observation_id,也有 dataset_run_id。前者评价某次执行结果,后者评价整个实验运行。

3.7.7 DatasetRunItem:实验里的连接表,也是分析投影

DatasetRunItem 是理解 experiment 的关键。它不是普通 join table,因为它承载了“一个测试样本在一次 run 中产生了哪个运行结果”。

字段含义
datasetRunId属于哪一次 experiment run。
datasetItemId这次运行用的是哪个测试样本。
traceId应用跑这个样本时产生的 trace。
observationId可选,老 SDK 或特定输出可以指向具体 observation。
error这次 item 运行是否因配置或执行失败。注意它在 ingestion event 和 ClickHouse 读模型中存在,当前 Prisma DatasetRunItems 模型没有这个字段。

当前 repo 中它有两个落点:

落点作用
Postgres dataset_run_items事务型关系和历史兼容,表达 run item、dataset item、trace、observation 的基础连接。
ClickHouse dataset_run_items_rmt列表、比较、score 聚合的高性能读模型,并且反规范化了 run/item 的部分字段,也承载 run item error。

这种设计的目的不是重复造表,而是把不同查询代价拆开:Postgres 负责关系和版本,ClickHouse 负责按 run、item、trace、score 的分析查询。

3.7.8 Score:统一评价结果,不等于 Scorer

Score 是结果,不是规则。它回答的是“某个对象在某个维度上的评价值是什么”。

源码中的 ScoreSource 有三类:

source谁产生典型场景
APISDK/API 或外部 pipeline自定义 evaluator、CI、业务系统回写用户反馈。
EVALLangfuse 内部 evaluator jobLLM-as-a-Judge、code evaluator。
ANNOTATION人工标注 UI / annotation queue领域专家打分、纠正输出。

Score 可以挂到四类对象,但约束是“只能选一个主目标”:

在源码里,applyScoreValidation 约束了这个目标关系:

目标合法字段组合用途
TracetraceId评价一次请求/工作流。
ObservationtraceId + observationId评价 trace 中某一步,例如最终 generation 或 retrieval。
SessionsessionId评价整个多轮对话。
DatasetRundatasetRunId评价一次 experiment run 的整体表现。

ScoreConfig 是 score 的 schema 约束,不是 evaluator 规则。它定义:

字段用途
name分数名,例如 correctnesstoxicity
dataTypeNUMERICCATEGORICALBOOLEANTEXT
minValue/maxValuenumeric 分数范围。
categoriescategorical/boolean 的合法标签和值。
isArchived停止新 score 使用,但保留历史。

所以要分清:

text
ScoreConfig:定义分数长什么样
Evaluator / Scorer:定义如何产生分数
Score:某次评价产生的结果

3.7.9 Scorer / Evaluator:规则在哪里,结果在哪里

用户常说 scorer,当前 repo 里更多叫 evaluator。它的实现可以拆成三层:

实体存在哪里负责什么
评分方法EvalTemplatePostgres eval_templatesLLM judge prompt、code、变量、输出定义、模型配置。
命中规则JobConfiguration / evaluation rulePostgres job_configurations对什么 target 跑、filter、mapping、sampling、delay、timeScope、状态。
执行状态JobExecutionPostgres job_executions某一次执行是否 pending/completed/error、输入 trace/observation/dataset item、输出 score id。
执行输入快照observation snapshotS3/blobobservation eval 执行时使用的稳定输入。
执行结果ScoreClickHouse scores最终评分结果,source=EVAL

新旧 evaluator target 也要分清:

legacy target新 target业务含义当前实现入口
traceevent从评价整条 trace,转向评价某个 observation/event row。evalService.createEvalJobsobservationEval/* 并存。
datasetexperiment从 dataset run item 语义,转向 experiment item/root observation 语义。DatasetRunItemUpsertscheduleExperimentObservationEvals 并存。

这里的 event 不要理解成普通日志。它在 evaluator 语义里基本等价于 observation-scoped target:按 observation 的 type、name、input/output、metadata 和 trace context 命中规则。

3.7.10 一次 Observation-level Evaluator 怎么工作

新 evaluator 路径的核心是:在 ingestion 或 experiment 运行过程中,直接拿命中的 observation 调度 evaluator。

这个设计回答了两个常见问题:

问题答案
evaluator 的规则存在哪里?EvalTemplate 存“怎么评分”,JobConfiguration 存“评谁和何时评”。两者都在 Postgres。
evaluator 的结果存在哪里?结果是 Score,最终进入 ClickHouse scoresJobExecution 只记录执行状态和输出 score id。

3.7.11 一次 Experiment 怎么跑

以 UI prompt experiment 为例,可以把源码链路压缩成下面这张图:

把这条链路翻译成实体:

阶段产生或读取的实体说明
准备材料DatasetDatasetItem固定输入和期望输出。
创建运行DatasetRun这一次实验的名字、描述、metadata。
跑每个样本DatasetRunItem把 item 和本次运行产生的 trace 连接起来。
记录执行TraceObservation/Event真正的 LLM/tool/span 运行过程。
评价结果Scoreitem/result 级 score 或 run 级 score。

所以 “dataset run 一次怎么表达” 的答案是:一行 DatasetRun 表示一次 experiment,N 行 DatasetRunItem 表示这个 run 中 N 个样本的运行结果,每个 run item 指向运行产生的 trace/observation,score 再挂到 trace/observation 或 dataset run 上。

3.7.12 存储视角:哪些是事实,哪些是配置

实体落点可以总结成:

对象主要权威存储为什么
DatasetDatasetItemDatasetRunPostgres需要事务、版本、唯一约束和管理 UI。
TraceObservation/EventClickHouse高频写入和按时间/project/type/metadata 查询。
ScoreClickHouse分析型事实,常做聚合、过滤和对比。
ScoreConfigEvalTemplateJobConfigurationPostgres配置型对象,变更频率低,需要权限和事务。
JobExecutionPostgres执行状态需要可查询、可更新、可审计。
queue payloadRedis/BullMQ短期调度、重试、延迟、并发控制。
raw event / snapshot / mediaS3/blobpayload 大或需要可重放,不适合塞进 Redis job。

ClickHouse 设计上能看到几条基础原则:

设计点对应原则在 Langfuse 中的体现
查询常按 project、时间、type、trace 过滤schema-pk-prioritize-filtersschema-pk-cardinality-orderevents_full/events_core 的排序键以 project_id、时间和 trace_id hash 组织。
重复字符串用字典编码schema-types-lowcardinalitytypeenvironmentlevel 等字段使用 LowCardinality(String)
高频写入不做单行 insertinsert-batch-sizeingestion 请求侧异步入队,worker 的 ClickhouseWriter 批量写入。
更新/删除避免频繁 mutationinsert-mutation-avoid-update使用 ReplacingMergeTree(event_ts, is_deleted) 表达更新版本和删除标记。

这些不是数据库技巧的装饰,而是会反过来影响产品抽象:当事实数据必须高吞吐查询时,把 trace context 铺到 observation/event 宽表上,比每次查询都 join 可变 trace row 更适合分析型系统。

3.7.13 从产品动作反推实体关系

只看表名很容易乱。更好的方法是从用户动作倒推:用户做了什么,系统要记住什么,最后落到哪个实体。

产品动作系统要记住什么核心实体主要存储
SDK 上报一次请求这次请求的整体输入输出、每个模型/工具/检索步骤、父子调用关系TraceObservation/EventClickHouse events_full/events_core,legacy traces/observations
多轮对话继续发生多条 trace 属于同一个长会话TraceSession、trace/event 上的 session_idPostgres trace_sessions 管 session 标记;ClickHouse facts 带 session_id
把线上样本加入 dataset固定输入、期望输出、元数据,以及来源 trace/observationDatasetDatasetItemPostgres datasets/dataset_items
跑一次 experiment本次 run 的名字、metadata,每个样本跑出来的 traceDatasetRunDatasetRunItemTraceObservation/EventPostgres 存 run 关系;ClickHouse 存 run item 投影和执行 facts
自动或人工评分谁评的、评哪个对象、分数名和值、是否符合 schemaScoreScoreConfigAnnotationQueueScore 在 ClickHouse;配置和人工队列在 Postgres
配置 evaluator/scorer用什么模板评分、命中哪些对象、如何抽样和延迟执行EvalTemplateJobConfigurationJobExecutionPostgres 配置/状态,S3 存执行输入快照,最终 Score 写 ClickHouse

这张图的重点是:dataset 不是 trace 的子表,score 也不是 dataset run item 的子表。它们通过 id 形成业务关系,分别承担“材料”“执行”“评价”的职责。

3.7.14 Experiment 有两条线:关系线和事件线

Experiment 最容易误解的地方,是把 DatasetRunItem 当成“运行结果本身”。更准确地说,它是连接对象,真正的运行过程还是 trace/observation tree。

这两条线对应不同问题:

问题看哪条线为什么
这个 experiment 跑了哪些 dataset items?关系线DatasetRun -> DatasetRunItem -> DatasetItem 最直接。
某个 item 的模型调用、工具调用、错误在哪里?事件线运行细节在 trace/observation tree。
为什么一个 run 可以做列表、比较、过滤 score?分析线dataset_run_items_rmt 反规范化 run/item 字段,events_* 铺了 experiment context。
experiment evaluator 为什么只评 root?事件线 + 分析线targetObject=experiment 仍然拿 observation 作为输入,但要求 span_id = experiment_item_root_span_id

这里的 experiment_item_root_span_id 很关键。一次实验运行会产生一棵 observation tree,但 experiment item 的输出语义通常在 root observation 上。源码里的 scheduleObservationEvals 会对 targetObject=experiment 增加 root 判断,避免同一个样本的每个 generation/tool 都触发一次 experiment evaluator。

3.7.15 Score 的归属:不要把 item score 和 run score 混在一起

Score 的目标约束是:traceIdtraceId + observationIdsessionIddatasetRunId 四种主目标里只能选一种。对 experiment 来说,这会形成三种常见写法:

写法字段表达的业务含义怎么回到 experiment
item 结果级 scoretraceId某个 dataset item 本次执行的整体输出好不好通过 dataset_run_items_rmt.trace_id 找到 run item
步骤级 scoretraceId + observationId某个生成、检索、工具步骤好不好先回到 trace,再按 observation 定位步骤
run 聚合级 scoredatasetRunId整次 experiment run 的总体表现直接挂在 DatasetRun 上

因此,DatasetRunItem 本身通常不直接“拥有 score”。它通过 traceId 把 score 归到某个样本的运行结果。列表页需要比较不同 runs 的分数时,会把 dataset_run_items_rmtscoresproject_id + trace_id 聚合起来看。

这也是为什么 scores 表既有 trace_id/observation_id/session_id,又有 dataset_run_id:Langfuse 需要同时支持“单个输出质量”和“整次实验质量”两种评价层级。

3.7.16 Scorer / Evaluator 的新语义:event 和 experiment 都是 observation 视角

当前 repo 里 evaluator target 有四个值:

targetObject语义输入对象变量映射怎么取值
tracelegacy online eval一条 trace,外加可按名字找 observationavailableTraceEvalVariables,需要说明用哪类 object/name
datasetlegacy dataset evaldataset item + traceavailableDatasetEvalVariables,可以取 dataset item 的 input/expected output
event新 observation-level eval一行 observation/eventobservationVariableMapping,直接取这行的 input/output/metadata 等
experiment新 experiment item evalexperiment item 的 root observation/event同 event,但额外能取 expected output 和 experiment item metadata

这解释了“trace -> event、dataset -> experiment”的变化:

旧理解新理解收益
eval 一条 trace,然后在 trace 里找某个 generationeval 一行 observation/event命中对象更明确,不需要在执行时重新解析整棵树。
dataset eval 以 dataset/run item 为中心experiment eval 以 root observation 为中心,并带上 dataset item context评分输入同时拥有实际 output 和 expected output。
variableMapping 要描述“从哪类对象的哪个字段取变量”variableMapping 直接描述“从当前 observation 的哪个字段取变量”配置更短,也更适合 agent/workflow 中的多节点评价。
每次评价可能需要重新查 trace 详情ingestion/experiment 阶段上传 observation snapshotevaluator 使用稳定快照,避免后续事实行更新影响本次评分。

这里不要把 event 理解成普通日志。它在 Langfuse v4 语义里更接近“统一 observation row”:span、generation、tool、retriever、agent、guardrail 都可以作为 evaluator target。

3.7.17 为什么很多关系不是数据库外键

Langfuse 的核心 facts 在 ClickHouse,配置和关系对象大量在 Postgres。两边不会像单库业务系统那样处处建外键,而是用稳定 id 形成逻辑关联。

关系为什么不是普通 FK 思维
DatasetItem.sourceTraceId/sourceObservationId 指向线上事实trace/observation 在 ClickHouse,dataset item 在 Postgres,来源是可追溯引用,不是事务级子表。
DatasetRunItem.traceId/observationId 指向运行结果run item 关系在 Postgres/ClickHouse 都可能出现,运行事实在 ClickHouse。删除和重放靠 project 边界、队列和清理逻辑处理。
Score.configId 指向 ScoreConfigscore 是分析事实,config 是 schema 配置。读分数时可按 config 展示/校验,但 score 表本身服务聚合查询。
JobExecution.jobInputTraceId/jobInputObservationId 指向被评价对象JobExecution 记录“这次 evaluator 为什么跑、跑到哪一步”,不拥有 trace/observation 的生命周期。
events_* 行带 experiment_* 字段这是为了查询和过滤快,不是因为 event 在概念上属于 dataset。它仍然是一条运行事实。

这套设计的代价是:读源码时不能只沿 Prisma relation 找关系。你要同时看三类关系:

对初学者来说,判断一个实体关系时可以问三个问题:

问题如果答案是 yes,通常意味着
这个对象需要事务、唯一约束、权限管理吗?优先在 Postgres 建模。
这个对象是高频运行事实,需要按时间、project、type、trace 查吗?优先在 ClickHouse 建模。
这个查询经常要跨多个对象做列表/聚合吗?可能需要 ClickHouse 反规范化投影。

3.7.18 三种评分入口:同样落成 Score,不同的是规则来源

ScoreSourceAPIEVALANNOTATION 三类。它们最后都是 Score,但“谁决定怎么打分”完全不同。

三条入口的差别:

入口规则在哪里状态在哪里结果在哪里适合什么
API / SDK外部业务系统自己决定,configId 可选外部系统自己管;Langfuse ingestion 只校验 score bodyClickHouse scoressource=APICI、线上 feedback、业务规则 pipeline
Evaluator / ScorerEvalTemplate + JobConfigurationPostgres job_executions,S3 snapshot,BullMQ jobClickHouse scoressource=EVALLLM-as-a-Judge、code evaluator、自动化 online/offline eval
Annotation QueueAnnotationQueue.scoreConfigIds 约束表单Postgres annotation_queue_items 的 pending/completed/lock 状态ClickHouse scoressource=ANNOTATION人工 review、ground truth、专家标注

ScoreConfig 不是 scorer。它只定义结果长什么样,例如:

ScoreConfig 类型约束什么例子
NUMERIC数值范围correctness 只能在 0 到 1 之间
CATEGORICAL标签和值映射toxicity = safe/unsafe
BOOLEANTrue/False 两类contains_citation = True/False
TEXT文本长度和字段形态reviewer comment 或 reasoning

Evaluator 使用 ScoreConfig 时,是为了保证 evaluator 输出能被稳定比较;Annotation Queue 使用 ScoreConfig 时,是为了让人工标注表单知道该展示哪些字段;API/SDK 使用 ScoreConfig 时,是为了让外部打回来的分数和团队统一 schema 对齐。

3.7.19 三条源码追踪路线

读这个 repo 时,不建议从所有表开始扫。按下面三条路线读,能把业务语义和实现对应起来。

路线 A:一次线上 trace 如何变成 observation tree

源码入口:

看什么入口
ingestion event schemapackages/shared/src/server/ingestion/types.ts
worker 如何分流 trace/observation/score/run itemworker/src/services/IngestionService/index.ts
observation domain 字段packages/shared/src/domain/observations.ts
v4 event 查询packages/shared/src/server/queries/clickhouse-sql/event-query-builder.ts

路线 B:线上样本如何沉淀成 dataset,再跑 experiment

源码入口:

看什么入口
public dataset item/run item API schemaweb/src/features/public-api/types/datasets.ts
API service 如何创建 dataset item/run itemweb/src/features/datasets/server/publicDatasetService.ts
dataset item 版本和 source 字段packages/shared/src/server/repositories/dataset-items.ts
UI prompt experiment workerworker/src/features/experiments/experimentServiceClickhouse.ts
run item ingestion event 转 ClickHouse 投影worker/src/services/IngestionService/index.ts

路线 C:evaluator 如何从规则变成 score

源码入口:

看什么入口
evaluator target 枚举和 variable mappingpackages/shared/src/features/evals/types.ts
event/experiment eval 可用过滤字段packages/shared/src/features/evals/observationForEval.ts
调度 observation evalworker/src/features/evaluation/observationEval/scheduleObservationEvals.ts
experiment root 触发 evaluatorworker/src/features/experiments/scheduleExperimentEvals.ts
evaluator 写 score eventworker/src/features/evaluation/evalScoreEvent.ts
score config 校验和落库前 inflatepackages/shared/src/server/ingestion/validateAndInflateScore.ts

这三条路线串起来,就是“观测 -> 沉淀 -> 实验 -> 评价 -> 再迭代”的源码版。

3.7.20 读源码的实体索引

想理解的实体先读再读
Observation 类型和字段packages/shared/src/domain/observations.tspackages/shared/src/server/repositories/definitions.ts、ClickHouse observations / events_*
Trace / Sessionpackages/shared/src/domain/traces.ts、Prisma TraceSessiontrace/session repositories、events query
Score 目标和数据类型packages/shared/src/domain/scores.tspackages/shared/src/utils/scores.tsvalidateAndInflateScore.ts、ClickHouse scores
ScoreConfigpackages/shared/src/domain/score-configs.tsPrisma ScoreConfig、annotation form
Dataset / DatasetItemPrisma DatasetDatasetItempackages/shared/src/server/repositories/dataset-items.tsNewDatasetItemForm.tsx
DatasetRun / ExperimentPrisma DatasetRunsDatasetRunItemsworker/src/features/experiments/experimentServiceClickhouse.ts
DatasetRunItem 分析读模型packages/shared/src/domain/dataset-run-items.tsClickHouse dataset_run_items_rmt、dataset run item repository
Evaluator / ScorerPrisma EvalTemplateJobConfigurationJobExecutionworker/src/features/evaluation/**、unstable evaluator API types
Observation-level evalpackages/shared/src/features/evals/types.tsworker/src/features/evaluation/observationEval/**

3.7.21 用一句话串起来

Langfuse 的业务数据流可以压缩成一句话:

text
线上应用产生 observations,这些 observations 通过 trace_id 组成 trace,通过 session_id 组成 session;
人或 evaluator 对 trace/observation/session/experiment run 写 score;
有价值的线上样本被沉淀成 dataset item;
dataset run 把一组 dataset items 重新跑一遍,产出新的 traces/observations 和 scores;
score analytics 再把这些结果聚合,帮助决定下一次 prompt、model 或 workflow 怎么改。

这就是当前 repo 的核心抽象:不是单纯存 trace,而是围绕“观测、评价、实验、迭代”构建一套数据基础设施。

下一节

draw.io 架构图