online branch: main

2026-08-29

RSS llms.txt GitHub

一次 AI 回答背后,到底发生了什么?

$
AI 应用可观测性与评估:一次 AI 回答背后,到底发生了什么?

观测平台不会自动理解业务。它看到的只是事件、时间戳和字段。要让一条运行记录可以排查、筛选和比较,应用必须先把真实执行过程翻译成一套稳定的数据关系。

继续看业务问答 Agent。用户问“昨天哪个广告计划表现异常”,系统识别问题、查数据、算指标、调用模型,然后返回答案。我们需要同时描述四件事:这次任务整体是什么,内部有哪些步骤,哪一步属于模型调用,以及它与前后几轮对话是什么关系。

在 Langfuse 中,这四层通常对应 Trace、Observation、Generation 和 Session。其他工具可能叫 Run、Span、LLM Call 或 Thread,判断边界的方法并不会因此改变。

这里要先标明 v4 的数据模型变化:Trace 仍表示一次端到端执行,但在存储和查询上,它是共享同一 trace_id 的一组 Observations,不再是拥有独立 input/output 的记录。任务整体的输入与输出应写在根 Observation 上;界面再通过根节点呈现一条 Trace。后文沿用“Trace”描述业务执行边界,不代表重新引入旧版 Trace 实体。

Trace 先划定一次任务

Trace 是一次有起点和终点的端到端执行。它回答:“为了交付这个结果,系统整体经历了什么?”

对聊天产品,一轮用户提问通常是一条 Trace。对 Agent,一次独立运行也可以是一条 Trace。判断依据是它能否独立解释一个结果,并且可以独立成功、失败或重试。

“一轮消息等于一条 Trace”只是常用方案,不是规定。前台请求立即回复“任务已创建”,后台再生成一份长报告,这两个过程有各自的生命周期,分成两条 Trace 往往更清楚。批量处理一百个客户时,也可以为每个客户建立独立 Trace,避免一个巨大链路掩盖局部失败。

所以,接入前最好让团队把定义写成一句话:

在业务问答 Agent 中,一条 Trace 代表用户发起的一次独立分析请求,从收到问题开始,到返回答案或明确失败结束。

这句话会影响请求量、成功率、延迟和成本的统计口径。如果有人把 Trace 当用户问题,有人把它当模型调用,同一张 Dashboard 里的数字就无法解释。

Observation 把过程拆开

只看根节点的整体输入、输出和总耗时,我们仍然不知道中间发生了什么。Observation 是 Trace 内部可以单独观察的工作单元。工具无关地看,它可能是一段 Span、一个离散 Event,或带有明确业务语义的步骤。

这次请求可以被记录为:

Trace:分析异常广告计划
├─ Observation:解析用户问题
├─ Observation:读取客户指标口径
├─ Observation:查询投放数据
├─ Observation:计算异常程度
└─ Observation:生成最终回答

拆开后,总耗时 12 秒不再只是一个数字。若“查询投放数据”用了 9.8 秒,性能问题有了落点;若它传入了错误账户 ID,答案错误也不该先怪模型。

Observation 不应照着函数数量机械创建。字符串格式化、局部变量转换这类细节通常没有独立诊断价值。模型调用、检索、外部工具、可失败的业务决策和重试值得记录,因为排查时需要单独查看它们。

父子关系比步骤数量更重要。Agent 先决定调用工具,工具内部再查询数据库,记录应反映这层包含关系。并行检索多个来源时,几个子步骤可以共享父节点并在时间上重叠,不必为了界面好看改成一条假装串行的路线。

Langfuse 把这些步骤统称为 Observation,并提供 spaneventgenerationagenttoolchainretrieverevaluatorembeddingguardrail 等类型。基础接入不用一次用全。先保证名称稳定、层级正确,模型、检索和工具能被分辨出来。

Generation 为什么值得单独看

模型调用也是 Observation,但它比普通步骤多出一组 AI 特有信息:模型名称、Prompt 或消息、生成参数、输入与输出 Token、首 Token 时间、完整耗时和费用。Langfuse 把这种 Observation 叫 generation

假设“生成最终回答”用了 6 秒。如果它只是普通 Span,我们不知道是模型排队太久、输入上下文突然膨胀,还是输出文本过长。作为 Generation 记录以后,几类问题可以分开判断:输入 Token 暴涨通常指向上下文拼装;首 Token 很慢可能与模型服务或网络有关;首 Token 正常而总时长较高,可能是输出过长。

Generation 的边界跟随一次真实模型请求。Agent 先用模型判断意图,再决定工具,最后组织答案,就应该有三条 Generation。流式回答虽然分很多片段返回,通常仍是一条 Generation,因为底层只发起了一次模型请求。

调用成功也不代表回答正确。status=success 只说明模型服务完成了请求。Generation 留下的是“模型如何被调用”的证据,质量判断要靠反馈、业务规则或评估。

Session 把多轮任务放在一起

用户拿到第一轮答案后继续问:“异常最早从几点开始?”随后又说:“整理成日报。”系统处理了三次独立请求,因此有三条 Trace;从用户目标看,它们属于同一次复盘任务。

Session 用来做这一层分组。Trace 解释一次请求内部发生了什么,Session 解释哪些请求属于同一段连续交互。它不是更大的 Trace,也不会改变每条 Trace 的内部结构。

观测平台很难仅凭时间或用户身份推断 Session。同一个用户可以同时处理多个客户,时间相近的请求未必有关。可靠做法是复用业务系统已有的 conversation_idthread_idtask_id,并在后续请求中持续传播。

Langfuse 使用 sessionId。带有同一 sessionId 的 Observations 及其所属 Traces 会被聚合到一个 Session 中。该值需为少于 200 个字符的 US-ASCII 字符串,超出限制会被丢弃。工程上还应做到短、稳定、可回查,不把手机号、邮箱或客户名称直接放进 ID。

什么时候结束 Session,可以回到业务目标判断。“查看概览、追问异常、生成日报”仍在推进同一件事;用户切换客户并开始新的分析,应创建新 Session。把某个用户的全部历史请求塞进一个 Session,会让回放、成本和质量统计混在一起。

三种 ID 不要混用

现在可以把几个最容易混淆的 ID 放回各自位置:

userId:谁发起任务
└─ sessionId:这次连续对话或业务任务
   ├─ traceId:第一轮请求的执行链
   ├─ traceId:第二轮请求的执行链
   └─ traceId:生成日报请求的执行链
      └─ observationId:其中一个模型或工具步骤

traceId 连接同一次请求里的步骤;sessionId 归组多条 Trace;userId 标识使用者。一个用户可以有很多 Session,一个 Session 可以包含很多 Trace,一条 Trace 又包含多个 Observation。拿 userId 代替 sessionId,会把用户的所有任务混在一起;拿 sessionId 串跨服务的同一次调用,则会把分布式追踪和会话分组混为一谈。

上下文字段让记录可以被筛选

结构正确以后,还要知道这条记录发生在什么条件下。常见上下文包括用户、环境、业务标签、自定义元数据和版本。它们看起来都是附加字段,职责却不同。

userId 使用稳定的内部 ID 或脱敏标识。Langfuse 允许使用邮箱等唯一值,但“允许”不等于“应该上传”。是否采集明文身份,要服从公司的数据政策。

environment 区分 productionstagingdevelopment 等运行环境,防止测试流量污染生产指标。Langfuse 未配置时使用 default;当前格式限制为最多 40 个字符,只允许小写字母、数字、连字符和下划线,且不能以 langfuse 开头。

tags 适合少量、可枚举的分类,例如 campaign-reviewragweb。Langfuse 的标签最长 200 个字符,创建后不能在 UI 中修改。客户 ID、计划 ID 这类高基数字段不适合做标签。

metadata 保存平台没有预设、但排查或分析需要的结构化事实。例如:

metadata = {
    "tenant_id": "tenant_42",
    "scenario": "campaign_review",
    "data_source": "ads_api"
}

Langfuse 支持按 metadata 查询。传播型 metadata 的字符串值最长 200 个字符,超长值会被丢弃。不要把整份业务对象复制进 metadata,也不要让同一含义同时出现 customer_idclientIdtenant 三种命名。

版本要分层。Langfuse 的 release 表示整个应用发布,常用语义版本或 Git Commit;Observation 的 version 表示某个具体组件或同名步骤的版本。团队也可以在 metadata 中保留自定义 agent_version,但它只是业务字段,不能与前两者混称。

把完整模型放回案例

一次可分析的业务问答可以表示为:

User:employee_731
Session:campaign-review-8f31
├─ Trace:分析异常广告计划
│  ├─ Retriever:读取指标口径
│  ├─ Tool:查询投放数据
│  ├─ Span:计算异常程度
│  └─ Generation:组织答案
└─ Trace:生成复盘日报
   ├─ Tool:读取上一轮分析结果
   └─ Generation:生成日报

Environment:production
Tags:campaign-review、web
Release:2026.08.18-a13c9f
Metadata:tenant_id、data_source

这套结构已经能回答不少实际问题:某个答案用了哪份数据,慢在哪一步,哪个版本产生,属于哪次会话,是否集中发生在特定租户。它仍然不能自行判断答案是否符合业务事实,但已经为排查和后续评价留下了可回看的过程。

参考资料


系列目录:AI 应用可观测性与评估

上一篇:AI 明明答错了,系统为什么还显示一切正常?

下一篇:第一次给 AI 接入监控,应该从哪里开始?