AI 上生产以后怎么监控FDE 的日志、Tracing 和 Observability专栏《AI FDE 实战从 Demo 到生产》第 16 篇 / 共 18 篇本篇目标把“AI 今天不好用”拆成有证据、能定位、可行动的问题同时避免让监控系统成为新的数据泄露入口。本篇产物结构化事件规范、链路实验、告警与运行手册。所有企业、事故和阈值均为教学示例。星河设备的售后助手上线试点一周后客服发来一句话“今天 AI 特别慢昨天还好好的。”开发者打开服务器看见 CPU 不高、内存充足、HTTP 大多返回成功于是猜测可能是模型服务慢。另一位同事建议换一个模型第三位同事建议加服务器。三个建议都有可能有效也都有可能完全无关。如果这次请求的四十秒有三十五秒花在 CRM 连接池排队上换模型并不能解决问题如果浏览器已经取消请求服务器却继续生成并重复调用工具加服务器还可能扩大费用。可观测性的价值就是让团队减少这种猜测。它不是买一个漂亮的仪表盘而是让系统在运行过程中留下足以回答问题的证据谁发起了哪类任务经过哪些步骤哪一步等待或失败最终对用户造成了什么影响工程师可以采取什么动作。一、先确定你要回答的五个问题如果不知道要回答什么日志很容易越记越多。每个模块都打印输入、输出和异常堆栈最后既昂贵又不安全真正出问题时仍然只能搜索“error”。所以第一步不是接 SDK而是从售后助手的业务路径列出问题。第一用户是否能够完成任务。保修政策问答是否返回适用的依据订单查询是否找到用户有权查看的订单工单草稿是否成功保存。第二等待发生在哪。模型生成、检索、身份服务、工具执行和队列排队应该能够区分。第三失败影响多大。一次偶发超时和所有请求失败需要不同的响应等级。第四代价是否可控。同一个成功任务的 token、重试次数和外部接口调用次数有没有增加变化是否与某次版本发布相关。第五我们是否仍守住边界。权限拒绝、注入尝试、确认失败和审计写入异常都可能需要调查但不能通过“让模型继续执行”来提高表面的成功率。把这五个问题写出来遥测字段就有了取舍依据。某个字段如果既不能帮助定位也不服务明确的审计或评估目标就不应该因为“以后可能有用”而长期保存。对企业 AI 来说少记录一份原始合同往往比以后再建立复杂清理系统更可靠。二、日志、链路、指标和评测分别回答什么图 2前三项主要描述运行状态评测描述任务结果。需要关联它们但不要混成一个“AI 健康分”。日志适合表达离散事件例如一次授权拒绝、一份索引切换成功、一条工具执行超时。结构化日志的关键是字段稳定不是把一句人类语言塞进 JSON。error_type应当是可枚举的类别route应当是规范化路由release_id应当能定位实际部署版本。链路适合描述一次任务的因果和时间结构。一个 trace 关联整次请求多个 span 描述检索、模型调用和工具操作。如果工具进行了两次尝试应当留下两个可辨认的尝试而不是只保留最终成功的耗时。OpenTelemetry 将 span 与上下文传播作为追踪的核心概念随文实验借用这一结构但没有实现完整协议或采集系统。来源OpenTelemetry Traces指标适合回答总体趋势每分钟多少请求、失败比例多大、延迟分布怎样、队列是否接近上限。它们通常是聚合后的时间序列不能代替每一条任务的细节。一个小时的平均延迟正常不代表没有一小部分用户每次都等很久一个服务成功率很高也不代表失败没有集中在最重要的客户流程。评测则回答“做对没有”。模型在两秒内返回了一段错误政策运行指标可能一片正常业务结果却不合格。第 14 篇的固定用例与人工抽样需要和线上版本关联这样才能判断某次优化是否以引用质量下降换来了更短延迟。三、一次任务怎样拆成可以解释的链路图 3父子关系应反映真实调用。并行检索不是依次发生异步任务也不必被强行画成一条同步直线。以“查看 SO-1042 的状态并说明保修材料”为例请求入口先完成认证和授权再分别执行订单查询与知识检索最后调用模型整理回答。这里至少有入口、授权、检索、订单工具和模型几个逻辑步骤。每个步骤都应有明确的名称、开始结束时间和结果类别。不要把整个应用只包一个 span。那样只能知道请求慢却不知道慢在哪里。也不必给每一行函数都创建 span否则主要路径会被大量无关细节淹没。优先覆盖跨进程、跨网络、外部依赖、重试以及关键业务决策的边界通常就能解释大多数故障。模型调用还应区分准备上下文、等待首段输出、完整生成和后处理。对流式界面来说首段输出时间影响用户感受总完成时间影响系统占用。界面已经出现几个字不代表后端任务完成更不代表后续工具已经执行成功。指标名称必须说明它测量的是哪一段。如果任务转为后台执行原始 HTTP 请求结束并不代表业务完成。可以通过任务标识和 span link 建立联系同时分别记录提交成功、执行成功与最终业务结果。否则“接口返回已接收”很容易被统计成“工单创建成功”让监控与实际使用脱节。四、trace_id 可以帮助关联不能证明身份浏览器、代理和下游服务都可能传递追踪头。W3C Trace Context 定义了traceparent和tracestate的格式及传播语义也讨论了隐私与安全问题。一个格式正确的 trace_id 只说明它看起来像追踪标识并不证明发送者是谁。来源W3C Trace Context因此不能从追踪头读取租户或用户身份不能因为请求带着内部风格的 trace_id 就跳过认证也不能直接信任外部输入的采样标志。攻击者可以构造许多标识诱导系统产生额外遥测甚至把外部字符串塞进日志。如果入口允许接收第三方追踪上下文需要明确验证规则、来源策略和传播边界。随文实验采用简单策略默认忽略外部提供的上下文重新创建本地 trace只有调用者明确标记为可信内部连接时才接受严格校验的零零版本格式。这个布尔参数只是实验接口生产系统不能让浏览器自己把它设置为真必须由网关、服务身份或连接策略决定。还要注意上下文的生命周期。异步服务中如果使用全局变量保存当前请求很容易把两个并发用户的事件串到一起。Python 实验使用ContextVar并在finally中恢复原上下文。跨线程、后台队列和进程传播仍需显式处理不能因为单线程测试通过就认为所有并发边界都已覆盖。五、先用字段白名单再讨论脱敏算法图 4脱敏是完整数据路径的设计。正文中的正则只用于追踪头格式验证不用于声称可以识别全部敏感信息。很多团队的第一版方案是先把原文全部记录下来再用正则替换手机号和邮箱。这个方案不能覆盖人名、地址、客户合同、订单内容、内部系统地址也很难覆盖多语言、编码、嵌套 JSON、附件和工具错误信息。更根本的问题是数据可能在替换之前就已经进入代理、队列或第三方 SDK。更稳妥的起点是字段白名单。常规运行遥测只允许规范化路由、固定操作名、有限错误类别、受限数值和版本标识。用户问题、模型答案、原始检索片段、认证头、工具完整参数默认不采集。需要内容调试时再建立单独的受控流程明确目的、访问人、保留时间和脱敏责任。即使只保存哈希也不能自动认为匿名化完成。可猜测的手机号、订单号或邮箱通过普通哈希仍可能被字典枚举稳定标识还会把多次行为关联起来。如果确实需要用户维度分析应使用经过设计的伪名化方式、限制连接能力并与企业的数据治理要求对齐。异常消息同样危险。HTTP 客户端可能把完整 URL 写进异常数据库驱动可能包含 SQL 参数工具服务可能返回原始请求。实验只记录timeout或internal这种固定类别不直接记录str(exc)。生产环境可以把详细异常送往受控调试渠道但需要再次检查 SDK 默认采集了哪些字段。采集后的处理也重要。日志平台的访问权限不应比业务系统更宽导出功能和搜索结果也应受控。内容一旦进入备份或分析仓库删除流程就会复杂很多因此保留周期应在接入前决定。采样只能减少数量不能保证恰好采到的那一条不含敏感信息。六、动手用标准库观察两次请求本篇的code/observability_lab.py不连接模型、CRM 或日志平台只使用 Python 标准库模拟一次成功请求和一次工具超时。它实现固定操作名、上下文父子关系、结构化事件、错误分类和简单聚合目的在于把边界看清楚而不是重新实现一套 OpenTelemetry。运行方式python code/observability_lab.py本次实际输出PASS: 2 requests, 8 spans, parent links, timeout classification, context reset, sensitive-field exclusion, trust boundary, cost arithmetic {requests: {answered: 1, unavailable: 1}, span_count: 8}每个请求有一个根 span 和三个子 span。成功请求的状态是answered工具抛出超时后对应工具与根请求都记录错误用户状态变成unavailable。错误消息故意含有邮箱测试确认这个内容不会进入输出事件。用户问题中也放入示例手机号与令牌字符串验证这些字段在源头就被排除。实验并没有调用真实模型所以其中的 token 数是固定 fixture不能用来计算真实服务成本。运行时间来自当前机器的本地操作也不能当作模型性能基准。它真正证明的是父子关系建立正确、上下文被清理、错误分类可用、敏感字段没有被这一段代码写入事件。核心边界在下面这个函数。完整文件还有数值范围、类型与自检逻辑defsafe_attributes(attrs):result{}forkey,valueinattrs.items():ifkeyinSTRING_VALUESandisinstance(value,str)andvalueinSTRING_VALUES[key]:result[key]valueelifkeyinNUMERIC_KEYSandtype(value)isintand0value1_000_000:result[key]valuereturnresult注意未知字段被直接舍弃而不是递归寻找“看起来敏感”的词。这样会牺牲一些临时调试便利但让常规遥测的边界更容易检查。新增字段需要更新白名单和测试也就迫使开发者说明为什么需要它它的取值范围是什么会不会包含客户内容。七、什么时候接入 OpenTelemetry当应用需要跨服务传播、批量导出、标准化资源信息和成熟采样时就不应该继续扩展这个内存实验。可以采用 OpenTelemetry 的 API 与 SDK在应用启动时配置 provider、资源属性和 exporter在重要业务边界创建 span。Python 官方文档给出了这些基础组件的用法具体依赖版本应由项目锁文件固定。来源OpenTelemetry Python instrumentation下面是可选集成片段不属于随文标准库实验也没有在本次环境执行fromopentelemetryimporttracefromopentelemetry.sdk.resourcesimportResourcefromopentelemetry.sdk.traceimportTracerProviderfromopentelemetry.sdk.trace.exportimportBatchSpanProcessor,ConsoleSpanExporter providerTracerProvider(resourceResource.create({service.name:fde-assistant-api,service.version:replace-with-release-id,}))provider.add_span_processor(BatchSpanProcessor(ConsoleSpanExporter()))trace.set_tracer_provider(provider)tracertrace.get_tracer(fde.assistant)withtracer.start_as_current_span(retrieve.policy)asspan:span.set_attribute(app.candidate_count,2)provider.shutdown()控制台导出只是让开发者理解事件结构生产通常将数据交给受控 collector再进行过滤、批处理和存储。开启自动埋点之前必须检查它会不会记录 SQL、HTTP URL、请求头或异常正文。自动化能够减少遗漏也能自动传播原本不想保存的数据。自定义业务字段最好使用自己的命名空间避免把尚未确认的字段冒充标准语义。特别是生成式 AI 相关约定会随版本演进团队应该固定所用约定和 SDK 版本记录升级差异。仪表盘依赖哪些字段也应作为接口契约维护。八、指标的维度越多代价和风险越大一个常见错误是给每个指标都加上user_id、tenant_id、order_id和完整模型输入。这样时间序列数量会快速增长查询变慢存储成本增加敏感内容也进入了长期分析系统。指标应使用稳定、有限、可以解释的标签比如路由类别、任务类别、结果状态和发布版本。Prometheus 的命名建议强调指标名、单位和标签的可理解性并提醒关注高基数标签。本文的实践是把整体趋势放进指标把具体请求细节留在受控日志或 trace 中通过必要的关联标识定位而不是把所有维度铺成指标标签。来源Prometheus 命名实践延迟指标建议看分布。平均值容易掩盖尾部问题分位数则需要足够样本和合适聚合方式。不要把多个实例各自的百分之九十五分位数再简单平均作为全局分位数也不要在样本极少时把波动解释成确定趋势。请求量、观察窗口和失败请求是否计入都应在图表说明里写清楚。流式任务至少区分首段输出、整体完成、客户端取消和服务端失败。取消不一定是系统故障可能是用户换了问题但取消以后仍继续产生费用则是资源管理问题。把这些状态压成成功或失败两个值会失去很重要的工程信息。九、成本监控要关联任务不只累计 token模型费用首先来自实际 usage价格来自当期合同或供应商计费规则两者都需要版本和时间。输入、输出、缓存、工具、批处理等计费项目可能不同不能用一个永远不变的“每千 token 单价”处理所有调用。示例实验只验证算术不提供或声称任何当前模型价格。更有用的指标是完成一个合格任务的成本。一个模型便宜但需要三次重试、两次人工修正最终可能更贵一个请求 token 增加也可能因为新增了必要证据从而减少后续返工。成本分析应与任务成功率、质量和人工时间一起看不能只按模型调用次数下结论。预算控制应该发生在执行过程中而不是月底账单出来以后。为一次任务设置最大模型调用数、工具次数和总时长为一天或一个试点设置费用上限并给出触发后的用户体验。达到预算以后可以转人工或缩小任务范围不能把不足预算的状态伪装成正常答案。还需要保留归因线索。提示词版本、检索上下文数量、重试策略和 Agent 轮数变化都可能导致成本曲线改变。如果没有发布版本关联团队很容易把一次代码变更误认为供应商突然涨价或者把用户增长误认为程序泄漏。十、SLO 先定义合格请求再定义比例假设星河设备为内部试点约定合格请求中绝大多数在指定时间内返回可用结果。这里最难的部分常常不是那个百分比而是“合格”和“可用”的定义。未登录请求、明确越权请求、用户主动取消、证据不足和系统异常应该如何分类需要工程与业务共同确认。拒绝越权是正确行为不应该为了提高成功率而被算成系统故障服务没有找到授权证据可能是合理的不足证据状态也可能暴露资料覆盖问题。建议分别记录系统可用性指标和业务任务结果不用一个数字同时承载全部含义。可用性 SLO 可以围绕系统应该完成的请求定义质量目标则通过第 14 篇的评测与抽样跟踪。错误预算用于指导发布节奏与可靠性投入不是把用户可感知的问题变成报表上的免责条款。如果核心客户连续失败即使总体比例仍在目标内也需要调查。Google SRE 对监控的讨论强调延迟、流量、错误和饱和度等信号以及可行动的告警。我们把这些思想应用到售后助手时还需要补充模型与工具的特殊状态而不是照搬某个服务的阈值。来源Google SRE 监控章节十一、告警必须指向一个可以执行的动作图 5阈值由具体服务约定。表中重点是告警的解释和动作而非某个通用数字。“模型出现一次错误”通常不值得立即打断值班人员。如果系统已经重试并在预算内成功用户没有受到明显影响可以记录趋势。相反“关键流程连续无法完成”“高优先级任务排队持续增长”“审计事件写入失败”往往需要更及时的处理。一条合格告警应包含受影响服务、时间窗口、错误预算或业务影响、最近发布版本、相关图表、代表 trace、运行手册和负责人。告警内容本身也不能携带客户原文。值班人员应该能从告警直接开始调查而不是先猜测它对应哪个项目。触发条件还要考虑持续时间和多个窗口避免短暂抖动制造大量噪声。低流量试点中两次失败可能让错误率看起来极高因此比例通常要结合请求数量解释。系统完全没有遥测也需要单独检测因为“没有错误数据”可能表示服务正常也可能表示采集器已经停了。不要让观测平台故障拖垮业务请求。遥测导出应有缓冲与丢弃策略队列有上限失败可计数同时要区分普通诊断日志与必须可靠保存的业务审计。后者可能需要事务性保存、独立存储或失败关闭策略不能简单套用“日志丢了也没关系”。十二、用一个慢请求案例练习定位图 6以下事故是虚构教学案例没有声称本次制作连接了真实生产系统。假设上午十点客服反馈订单查询明显变慢。值班人员首先确认影响只发生在需要查询订单的问题单纯政策问答仍然正常再看版本记录发现刚发布了一个“自动补全订单明细”的功能。这个范围信息比先看整台服务器 CPU 更有价值。随后比较正常与异常 trace。检索和模型耗时没有明显变化工具 span 却出现长时间等待而且一次请求多了三次串行接口调用。进一步查看连接池等待指标发现新增调用占满了可用连接其他请求也开始排队。此时“模型慢”这个假设就没有得到证据支持。临时缓解可以关闭新增明细功能恢复原来的单次查询路径并确认业务任务完成率回升。不能只把超时从十秒改成一分钟因为这样可能让队列更长。也不能立即把并发翻倍因为下游系统可能有自己的速率限制。动作必须围绕已验证的瓶颈进行。恢复以后再补长期改进减少不必要的调用设置独立工具预算明确连接池上限把依赖等待时间加入观测给新增功能增加带真实调用结构的测试。复盘记录应该解释为什么之前没有发现而不是把责任简单归结为“某位工程师忘了优化”。十三、抽样不是随机丢掉大部分证据全量保存所有 trace 通常成本较高还会增加数据暴露面完全随机保留少量数据又可能错过低频高影响失败。采样策略需要结合请求量、故障类型和数据治理目标。可以保留代表性成功样本提高异常路径的保留比例并为关键流程设置专门的观测要求。头部采样在请求开始时决定是否记录成本相对容易控制但无法提前知道最终是否失败。尾部采样等待看到结果后再决定更容易保留异常但需要缓存和处理资源也会引入自己的丢失与延迟边界。选择哪种方式应由实际诊断需求和平台能力决定。安全审计不能随意跟着普通性能 trace 一起采样。工单提交、确认失败和权限变更可能需要完整记录而常规成功问答的详细时序可以抽样。即使审计全量保存也应只保存必要事实不把模型完整上下文当作“以后追责需要”的默认材料。内容级质量抽样则有另一套约束。若需要人工评估回答应先确认样本的授权范围和脱敏方式限制评审人员访问并记录样本何时删除。运行观测、业务审计和质量评测可以共享关联标识但各自的数据访问和保留策略不必相同。时间戳一致不等于跨机器时钟完全一致排查链路时还要注意时间来源。同一进程内测量耗时应使用单调时钟避免系统校时让结束时间看起来早于开始时间事件发生的日历时间则使用带时区的统一格式方便人与系统关联。实验通过perf_counter计算本地耗时没有把本机时间差假装成分布式全局顺序。跨机器的事件可能受时钟偏移、网络延迟和批量导出影响所以不能只凭日志显示先后就断言因果。父子关系、请求标识和业务状态版本往往比两条相邻时间戳更可靠。队列任务尤其要分别记录入队等待和执行耗时否则一个只运行一秒的任务也可能让用户等待一分钟。监控系统的单位也需要统一。毫秒与秒混用会让告警差三个数量级字节与字符混用会让请求限制解释错误估算 token 与供应商返回 usage 混用则会影响费用核对。给字段写清单位、来源和是否为估计值比增加更多小数位更重要。遇到无法观测的时间段应明确留下空白或未知状态不要用零补齐图表。十四、与前面章节怎样连接第 07 篇的返回值已经包含trace_id界面可以在错误提示中显示一个便于反馈的编号。接入生产追踪后应让它对应真实入口链路或者建立明确映射不能每层随机生成一个互不关联的编号。给用户的编号不应该包含租户、订单或任何可解码的敏感内容。第 08 篇的连接器适合产生工具 span第 09—10 篇的检索适合记录候选数量和索引版本第 11—12 篇的工具与工作流适合记录状态转移和受控尝试第 14 篇的评测报告适合关联发布版本。这里提供的是集成位置随文实验没有自动修改其他篇的应用。集成时先选择一条完整路径从入口到工具再返回确认字段含义、上下文传播和错误分类都一致再扩展到其他分支。一次性自动埋满所有模块很容易得到大量不一致的数据也难以确定敏感字段从哪里进入。可观测系统本身也应通过一组已知故障来验收。十五、实战练习与运行手册第一个练习是在标准库实验中增加一次模型重试让两次尝试有可区分的属性并验证总请求只计一次。思考一次失败后成功的请求应如何展示用户结果可以成功但内部失败与额外成本仍应保留不能被最终状态覆盖。第二个练习是加入一个后台任务但不要共享全局可变上下文。为任务建立可追踪关系分别记录提交、开始、完成与取消。随后让两个不同请求交错执行检查父子关系有没有串线。这个测试比单纯查看日志里有没有 trace_id 更有意义。第三个练习是写一页运行手册先确认影响范围再检查最近变更然后查看代表链路最后列出允许执行的缓解动作与升级联系人。使用“工具超时”和“遥测断流”两个场景演练记录哪些步骤依赖个人经验再把它们转成可重复说明。FDE Thinking看见问题以后团队能不能行动可观测性最容易出现的错觉是数据很多就等于系统透明。真正有用的证据应该能够排除错误假设连接一次用户反馈与具体工程变化并支持一个经过验证的处置动作。图表越多如果没有字段契约、访问边界和负责人团队可能只是以更高的成本继续猜测。对 FDE 来说观测方案也必须服务客户交付。业务负责人需要知道哪些流程受影响值班工程师需要知道如何定位与恢复数据负责人需要知道哪些内容被保存、谁能访问。下一篇我们会进一步检查这些边界当模型读到恶意文档、用户试图跨租户查询、或者确认操作被重放时系统如何守住权限。
