从一个开源项目看 AI 应用的日志应该怎么设计
以 Langfuse 的 tracing 思路为参照,拆解 AI 应用需要记录的调用、链路、错误和用户反馈,以及哪些数据不该进入日志。
先说结论。
AI 应用的日志不能只记录“请求成功”和“请求失败”。真正需要被观察的是一次业务任务:谁发起、经过哪些检索与工具、调用了什么模型、在哪一步变慢、输出是否被采用。
这篇文章以开源项目 Langfuse 的 tracing 思路为参照,拆出一套不绑定具体平台的日志字段。读完后,你可以先给现有 Demo 补一条完整 trace,再决定是否引入可观测性平台。
核验边界:这里看的是日志设计,不是产品评测
本文在 2026-07-15 核验了 Langfuse GitHub 仓库 和官方文档。仓库 README 将它描述为面向 LLM 应用的开发、监控、评估与调试平台,并提供 tracing、evaluation、dataset 和 prompt management 等入口。
当天 GitHub 页面显示仓库主要语言为 TypeScript,默认分支有更新,Releases提供版本入口;GitHub API 对仓库许可证返回 NOASSERTION,仓库说明则区分核心代码与企业目录。许可证边界需要在自托管或二次开发前阅读当前 LICENSE。stars、版本和提交频率都会变化,本文不把它们当作质量结论。
选择 Langfuse 作为参照,不等于日志必须交给 Langfuse。它值得学习的是 trace、observation、session、user feedback 这些对象如何连接,而不是某个界面长什么样。
固定项目卡:先判断它值不值得进入试用
| 字段 | 核验结果 |
| --- | --- |
| 仓库 | langfuse/langfuse |
| 一句话用途 | 把 LLM 调用、链路、评估、数据集和 Prompt 版本放进同一套工程观测对象 |
| 技术栈 | 仓库以 TypeScript 为主,自托管文档提供 Docker Compose 路径 |
| 适合场景 | 已有 LLM 应用,需要追踪 RAG、Agent、成本或线上反馈 |
| 上手成本 | 中:SDK 接入不难,真正成本在字段脱敏、采样和业务 trace 设计 |
| 维护信号 | 2026-07-15 页面显示默认分支有更新,仓库提供 releases、文档与部署入口;动态版本不作为质量评分 |
| 许可边界 | 核心代码采用 MIT;ee/、web/src/ee/、worker/src/ee/ 受单独条款约束 |
| 不适合谁 | 只有单次脚本、尚未定义业务动作,或不能把敏感数据发送到观测系统的团队 |
| 是否值得试用 | 值得,但先接一条真实链路,不要先迁移全部日志 |
这张卡的重点是把“项目活跃”与“适合当前系统”分开。release 频繁只能说明维护节奏,不能替团队决定日志保留期限和数据边界。
日志的第一层不是模型调用,而是业务任务
很多 Demo 把一条模型请求当作日志的最外层。这在单轮聊天里还能工作,一旦加入检索、工具调用和重试,就无法回答“用户这次任务到底经历了什么”。
更稳的做法是给每次业务任务一个 trace_id。例如“根据售后工单生成处理建议”是一个 trace,里面可以有文档检索、客户信息读取、模型生成、规则校验四个 span。模型调用只是其中一步。
判断标准很直接:如果一次失败发生后,开发者必须在三张表里用时间戳猜测同一请求,最外层对象就设计错了。trace 至少要能关联用户、会话、业务动作、开始时间、结束状态和环境。
调用日志要回答输入、配置、输出和资源消耗
模型调用日志的目标不是复现聊天窗口,而是解释一次调用为何得到这个结果。建议至少记录模型供应方、模型标识、参数版本、Prompt 模板版本、输入摘要、输出摘要、延迟、token 或计费信息、重试次数和最终状态。
这里的“摘要”很重要。生产日志默认不应保存完整客户资料、密钥或敏感正文。可以记录文档 ID、字符数、哈希、脱敏片段和分类标签,让问题可定位,同时降低数据泄露面。
如果应用支持多模型路由,还要记录“为什么走到这个模型”。否则成本突然上升时,只能看到贵模型被调用,却不知道是业务规则、降级逻辑还是用户配置导致。
RAG 日志必须把检索过程单独拆开
RAG 回答错误时,模型往往不是唯一原因。检索可能没有命中,切片可能缺少上下文,权限过滤也可能把正确文档排除。
一次检索 span 建议记录查询文本的脱敏摘要、查询改写版本、索引或知识库版本、过滤条件、候选数量、最终片段 ID、排序分数和引用来源。不要只存“找到了 5 条”。数量不能说明相关性,也不能证明权限正确。
具体场景是:同一个问题昨天能答,今天不能答。若日志包含知识库版本、过滤条件和片段 ID,就能判断是文档更新、权限变化还是重排变化;若只有最终回答,只能重新猜一遍。
工具调用日志要记录动作边界和审批结果
Agent 或自动化流程里,工具调用比文本生成更危险。读取库存和修改库存不是同一种事件,发送邮件和生成邮件草稿也不能共用一个“tool_success”。
每次工具调用至少记录工具名、动作名、参数摘要、权限主体、幂等键、审批状态、执行结果和外部系统返回标识。对写操作,还要记录谁批准、能否回滚以及回滚结果。
判断是否够用,可以问一个问题:误发邮件后,日志能否在一分钟内说明是谁触发、模型建议了什么、谁确认、外部系统是否接受。如果不能,所谓“Agent 日志”仍只是模型调试日志。
错误日志要保留失败阶段,而不是只有异常堆栈
异常堆栈适合开发者定位代码,却不一定能说明业务失败。AI 应用常见失败还包括输出格式不合法、低置信度、内容被策略拦截、工具超时、引用缺失和人工驳回。
建议建立稳定的失败分类:input_invalid、retrieval_empty、model_timeout、output_schema_error、policy_blocked、tool_denied、human_rejected。分类名可以按项目调整,但要避免把所有问题塞进 unknown_error。
错误事件还应关联输入版本、Prompt 版本和重试策略。否则修复后无法做回归比较,也不知道一次成功是不是靠三次昂贵重试换来的。
用户反馈要和当时的输出版本绑定
点赞和点踩不是完整反馈。用户可能不满的是引用过期、格式不好、遗漏关键条款,或者根本不该由 AI 回答。
反馈日志至少包括输出 ID、反馈类型、可选原因、修正内容、反馈人角色和发生时间。若用户编辑了 AI 草稿,保存结构化差异通常比只保存“已采用”更有价值。
一个可执行的判断是:团队能否从反馈里形成下周要修的具体队列。只有好评率,不能告诉工程师该改检索、Prompt、数据还是交互。
常见失败:记录太多和记录太少同样危险
记录太少,问题无法复现;记录太多,则可能把客户文档、个人信息和密钥长期复制到日志系统。另一个常见错误是把日志当数据库,依赖日志恢复业务状态。
日志应服务于观察和审计,业务事实仍由业务数据库管理。敏感字段需要分级、脱敏、访问控制、保留周期和删除机制。开发环境能看的内容,不应自动进入生产环境。
还要注意采样。高流量场景不一定需要永久保存每个完整 trace,但错误、人工驳回和高风险动作通常值得完整保留。采样比例应由问题定位需求和合规边界决定,不能照搬工具默认值。
一份可以直接落表的字段清单
开始时不必建设完整平台,先把下面字段贯通:
- 任务层:trace_id、业务动作、用户或租户、环境、开始与结束状态;
- 步骤层:span_id、父步骤、步骤类型、开始时间、耗时、重试次数;
- 模型层:供应方、模型标识、模板版本、输入输出摘要、资源消耗;
- 检索层:知识库版本、过滤条件、候选片段、最终引用;
- 工具层:动作、权限主体、审批状态、幂等键、外部返回 ID;
- 失败层:失败分类、错误代码、降级路径、人工接管结果;
- 反馈层:输出版本、反馈原因、修正内容、是否被采用。
验收时用三类样本走一遍:正常请求、检索为空、工具被拒绝。三类都能从一个 trace 还原,日志骨架才算成立。
我的最小试用步骤:先追一条链,再接平台
我更建议先选一个真实动作,例如“生成客服回复草稿”,手工定义 trace 和 span,再接入 Langfuse、OpenTelemetry 或现有日志平台。工具只负责采集和展示,字段边界仍要由业务决定。
相邻方案要按已有基础选:团队已经统一 OpenTelemetry 时,先扩展 trace attributes 和事件,不必为 LLM 单独再建一套链路;主要需求是应用日志与告警时,现有日志平台可能足够;需要把 Prompt、数据集、评估和反馈与调用轨迹关联时,Langfuse 才体现额外价值。替代方案不是功能完全相同,而是避免两套观测系统互相对不上。
投入前给自己一个停止条件:接入一条链后,若它仍不能比现有日志更快定位一次真实失败,或敏感字段治理成本超过当前排障收益,就先不扩到全量。值得试用和值得全面迁移是两次不同决策。
下一步可以拿最近一个失败请求做演练:不看代码,只看日志,尝试回答输入版本、检索结果、模型配置、工具动作和人工反馈。答不出的地方,就是下一轮应该补的字段。
// comments
0 threads登录 后可留言、回复。
- 还没有留言,来做第一个。