6.2 可观测性 (Observability) 与调试


6.2 可观测性 (Observability) 与调试

线上图黑盒怎么看

本节在全册位置:可观测性消费第四章检查点。LangSmith 能自动追踪每次运行的节点、耗时、状态快照;也可以自己把检查点历史读出来做分析。没有观测,长图就是黑盒。我们的经验:上线前先确认能回放任意一次运行。

观测:轨迹与耗时

用 LangSmith 时,每次 invoke 自动上报 spans,能看到每个节点输入输出与耗时,定位慢节点。也可 get_state_history 读检查点栈,离线复盘一次运行。关键指标:单节点延迟、循环轮数、工具调用次数。类比金融:观测像风控大屏,每笔流转可见。指标要落到告警,否则只是好看的图。

# 读检查点历史做离线分析(无需 LangSmith) from langgraph.checkpoint.memory import MemorySaver cfg = {"configurable": {"thread_id": "t1"}} # g 已 compile(checkpointer=saver) for snap in g.get_state_history(cfg): print(snap.values, "ts=", snap.ts) # 可统计每步耗时、状态增长,定位异常步 # 这套数据足够做基础可观测性,不必先接商业平台
# 商业平台胜在聚合与协作,自读历史胜在零依赖

案例:慢节点排查

背景:某图整体 8 秒,用户嫌慢,但没人知道时间花在哪。

操作:读历史发现检索节点占 6 秒,其余均 <1 秒。

结果:只优化检索(加缓存/并行),整体降到 2 秒,改动集中。

解读:观测先定位瓶颈再优化,避免盲改把时间花在无关节点上。

变式:把耗时指标接入告警,节点超阈值自动通知,把可观测性变成运维闭环。

06-02-fig01

工程清单

  • 可观测性消费第四章检查点,LangSmith 自动追踪节点与耗时,也能自读 get_state_history 离线复盘。

  • 关键指标:单节点延迟、循环轮数、工具调用次数,指标要落到告警才形成运维闭环。

  • 上线前先确认能回放任意一次运行,否则长图就是黑盒,出问题只能靠猜。

常见误区

只记最终输出不记轨迹,长图出问题时没有任何可观测线索,等于黑盒上线;先定位瓶颈再优化,比盲改把时间花在无关节点强得多。

自建轻量观测

不接商业平台也能做基础可观测性:从检查点历史算出每节点耗时与状态增长,定位异常步。

# 从 get_state_history 算节点耗时 snaps = list(g.get_state_history(cfg)) deltas = [] for prev, cur in zip(snaps[1:], snaps[:-1]): dt = float(cur.ts) - float(prev.ts) if (cur.ts and prev.ts) else 0 changed = set(cur.values) ^ set(prev.values) deltas.append((cur.next if hasattr(cur, "next") else "?", round(dt, 3), len(changed))) # deltas: (走到哪, 耗时秒, 状态变化量) for d in deltas: print(d) # 耗时异常大的节点,就是优化候选
指标 含义 告警阈值
单节点延迟 该步耗时 >1s
循环轮数 反思次数 >5
工具调用数 外呼次数 >10

⚠️ 常见坑:只看整体耗时就动刀,结果改了不相关的节点;先按历史拆出每步耗时,把优化集中在最慢的那一个。

💡 关键直觉:可观测性的价值不在「好看」,而在把长图从黑盒变成可回放的时间线;回放能力先于一切优化。

补充:自建观测的代价是要自己写聚合逻辑;当团队扩大到多图并行,还是建议接 LangSmith,把这部分人力省下来做业务,自读历史更适合验证期与小团队。

观测的三层:日志、轨迹、指标

可观测性不是单一动作,拆成三层各司其职,缺哪层都会在特定故障面前失明:

回答的问题 典型实现
日志 发生了什么 节点内 print 或写日志通道
轨迹 以什么顺序发生 检查点历史、LangSmith trace
指标 发生得有多快/多频繁 耗时、轮数、调用次数聚合

定位问题时的顺序是「指标发现异常、轨迹定位环节、日志看细节」:指标告诉你哪个接口变慢,轨迹告诉你慢在哪个节点,日志告诉你那个节点里发生了什么。三层都齐了,排障才不用猜。只在某一层里打转,是观测投入最常见的问题。

节点内埋点的标准模式

埋点不应该是事后补丁,建图时就按固定模式在每个节点入口出口打点,数据才连贯:

import time, json def traced(name): """给节点包一层计时与入出记录""" def deco(fn): def wrapper(s): t0 = time.time() try: out = fn(s) emit(name, "ok", time.time() - t0) return out except Exception as e: emit(name, "err", time.time() - t0, str(e)) raise return wrapper return deco @traced("retrieve") def retrieve(s): ...

埋点输出进日志通道(而不是 print),用流式导出,线上关闭也方便。三个要记的字段:节点名、耗时、结果状态。有了这套统一埋点,任何图都能回答「每个节点跑了多久、成没成」,这是后续性能优化的数据底座。

LangSmith 里的几个关键概念

接 LangSmith 之后,先弄清四个词,用它才不会一头雾水:

概念 含义 用途
Run 一次图调用 每次 invoke/stream 一个 Run
Span Run 内的一个节点 定位耗时归属
Trace Run + Spans 的树 看整体调用链
Dataset 输入输出样本集 回归测试

LangSmith 的价值不只是看曲线,它的 Dataset 能配合评价做回归:把线上的失败样本收进数据集,改完图跑一遍,看是否修复且没引入新问题。把这套「采集-分析-回归」闭环用起来,可观测性就从「看板」升级成「质量体系」。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U