Blade Agent 可观测性建议 v2
在 v1(指标为主干、trace 与 profiler 做下钻)的基础上,按两类真实部署环境分档给出组件清单与落地顺序: 盒子环境(DGX Spark 128G,需本机部署大模型,剩余资源紧张)与 服务器环境(典型 8c 32G,模型走外部 API,资源相对宽裕)。
长期监控以 Prometheus 指标为主干;trace 定位阶段、profiler 定位函数。Jaeger/Tempo 只是下钻工具。
组件清单按环境分档:盒子环境只保留零常驻依赖的埋点与日志;服务器环境部署完整分析栈。
用 OpenTelemetry 统一埋点,metrics 与 trace 同源导出。环境差异只体现在「后端收集器部署与否」,不体现在业务代码里。
1. 两类环境的约束差异
| 维度 | 盒子环境 | 服务器环境 |
|---|---|---|
| 硬件 | DGX Spark,128G 统一内存,但需本机部署大模型(推理引擎 + 权重常驻),留给业务与监控的内存、磁盘都很紧张 | 典型 8c 32G,模型走外部 API(LLM 网关),资源主要留给业务本身 |
| 网络 | 离线断网交付,不能依赖任何外部服务 | 内网或受控外网,可访问内部监控服务 |
| 运维 | 无人值守,出问题靠事后导出数据分析 | 团队可登录、可实时看 dashboard |
| 监控目标 | 留证据:出问题后能拿到足够数据定位 | 看趋势 + 告警 + 主动下钻 |
2. 分档组件清单
盒子环境(随产品出厂,零常驻分析组件)
| 组件 | 说明 |
|---|---|
/metrics endpoint |
FastAPI 进程内暴露 Prometheus 格式指标,开销可忽略。不部署 Prometheus server,指标由巡检脚本定期抓取快照落盘(如每分钟一次 curl 追加到滚动文件) |
| 结构化日志(JSON lines) | 每条日志带 trace_id / session_id / chat_run_id,本地滚动保留(如 7 天),不部署 Loki |
| 诊断 span 文件(默认低采样) | span 埋点始终存在;盒子环境不依赖常驻 OTel Collector/Tempo,而是写入本地滚动 diagnostic JSONL span 文件。慢请求(超阈值)和报错请求强制采样,正常请求 1%~5% |
| 诊断包导出命令 | 一条命令打包最近 N 天的指标快照 + 日志 + diagnostic JSONL span 文件,拷回内部环境后导入 Tempo/Grafana 离线分析 |
| py-spy(按需) | 镜像里预装二进制但不常驻。现场出现慢时手动 py-spy dump / record 采样,替代常驻 profiler |
服务器环境(8c 32G,部署完整分析栈)
| 组件 | 预估常驻内存 | 说明 |
|---|---|---|
| Prometheus + Grafana | ~1–2G | 主监控:chat p95/p99、错误率、并发、token/s、stream slowdown ratio,配告警规则 |
| Tempo(或 Jaeger) | ~0.5–1G | 接收 OTel trace,Grafana 内直接从指标面板跳转 trace |
| Loki | ~0.5–1G | 收结构化日志,靠 trace_id 与 trace 互跳 |
| Pyroscope(第二阶段默认部署) | ~0.5G | 用于长期 continuous profiling,回答「慢 span 内部具体哪个函数/调用栈耗时」。第一批先完成指标与 trace,第二阶段默认接入;py-spy 仅作为现场临时抓样工具 |
| Langfuse(已有) | 已部署 | LLM 语义层(prompt / completion / usage / agent loop)。与 OTel 通过 trace_id 打通,语义内容只在 Langfuse 存一份,避免与 Tempo 重复存储 |
合计常驻约 3–5G 内存,在 32G 机器上可控;全部走 docker compose 一键部署,与业务容器同机或独立监控机均可。
3. 埋点只写一套:OTel 同源导出
当前 feat/distributed-tracing 分支已在做 OTel span 埋点。建议在此基础上:
- 阶段耗时、p95、错误率优先从 span 派生(OTel metrics API 或 span metrics);运行态 gauge/counter 与流式吞吐指标使用 OTel metrics 显式记录
- 业务代码不感知环境:环境差异全部收敛在 exporter 配置——盒子导出到本地文件,服务器导出到 Tempo/Prometheus
- trace 边界维持架构阶段级(见下),不给每个函数打 span
chat.run
session.load_context
prompt.build
llm.stream
llm.first_token
projection.apply_event
projection.serialize_patch
socket.emit_patch
history.append
tool.run
4. Stream Telemetry:两个环境都要,且优先落地
「模型 100 token/s、用户可见 50 token/s」这类问题在盒子上尤其重要——本机推理引擎与业务进程抢资源,流式变慢的概率更高。这组指标属于轻量 counter/histogram,盒子上开销可忽略,两档环境都默认开启:
upstream_ttft_ms # 上游首 token 延迟
upstream_tokens_per_sec # 模型侧输出速度
backend_first_patch_ms # 后端首个 patch 发出时间
socket_emit_lag_ms # emit 排队延迟
frontend_visible_tokens_per_sec # 用户实际可见速度
stream_slowdown_ratio = upstream_tokens_per_sec / frontend_visible_tokens_per_sec
前端指标(receive / apply / render 时间差)通过现有 Socket.IO 或 REST 回传到后端汇总,不引入任何外部 RUM 服务(离线约束)。
5. 落地顺序
- 完成 OTel 架构边界 span(当前分支工作),并从 span 同源导出阶段耗时指标;运行态和流式吞吐用 OTel metrics 显式记录;exporter 按环境切换(diagnostic JSONL span 文件 / Tempo+Prometheus)
- Stream Telemetry 指标:后端各阶段计数 + 前端回传,两档环境默认开启
- 服务器环境部署分析栈:Prometheus + Grafana + Tempo + Loki,一套 compose;dashboard 覆盖 chat 延迟、错误率、token/s、slowdown ratio,配告警
- 盒子环境补齐留证能力:指标快照落盘、diagnostic JSONL span 文件导出、诊断包一键打包命令、py-spy 预装
- Langfuse 与 trace_id 打通,语义数据单份存储;慢请求 / 错误请求强制采样,正常请求低采样
- Pyroscope 第二阶段默认接入:服务器环境完成基础 dashboard 和 trace 后,引入 continuous profiling,补齐函数级长期定位能力
6. 最终监控结果会是什么样子
落地后不是只得到一堆原始 trace,而是形成「长期面板 → 告警 → 慢请求下钻 → 函数级定位 → 诊断包导出」的闭环。
服务器环境:Grafana 首页
| 面板 | 能看到什么 | 用于判断什么 |
|---|---|---|
| 系统健康总览 | 在线 session 数、running chat 数、错误率、chat p50/p95/p99、CPU/内存、事件循环阻塞时间 | 系统是否健康,是否有整体卡顿或资源压力 |
| LLM 与流式体验 | TTFT、上游 token/s、用户可见 token/s、stream slowdown ratio、首个 patch 延迟、socket emit 延迟 | 模型慢、后端慢、socket 慢、前端渲染慢分别在哪里发生 |
| Agent 阶段耗时 | context build、prompt build、llm.stream、tool.run、projection.serialize、history.append 的耗时分布 | 慢请求主要集中在哪个架构阶段 |
| 工具与沙盒 | 各工具调用次数、失败率、p95 耗时、sandbox command 耗时、文件读写耗时 | 是否是工具、沙盒、文件系统拖慢了 agent |
| 容量与趋势 | 近 1h/24h/7d 的请求量、慢请求比例、token 吞吐、错误率、资源曲线 | 是否需要扩容、限流、优化热点路径或调整模型配置 |
一次慢请求的下钻路径
Grafana 告警 / 慢请求列表
→ 点击 trace_id 打开 Tempo/Jaeger
→ 看到 chat.run 下各阶段 span 耗时
→ 定位到 projection.serialize_patch 或 socket.emit_patch 慢
→ 跳到 Pyroscope 查看同一时间窗口的 CPU/Wall time flamegraph
→ 确认具体函数:例如 model_dump、json.dumps、markdown render、history flush
→ 回到 Loki 用 trace_id 查看对应错误/上下文日志
典型告警
chat_p95_latency连续 10 分钟超过阈值,提示整体响应变慢。stream_slowdown_ratio高于阈值,提示用户可见 token/s 明显低于模型输出 token/s。llm_error_rate或tool_error_rate升高,提示外部模型、工具或沙盒异常。event_loop_block_ms升高,提示 Python 进程存在同步阻塞或 CPU 热点。socket_emit_lag_ms升高,提示实时通道可能积压。
盒子环境:现场能导出什么
盒子环境没有常驻分析栈,最终形态是一个诊断包。现场或客户复现问题后,执行一条命令导出最近 N 天数据,带回内部服务器离线分析。
blade-agent-diagnostics-2026-07-09.tar.gz
metrics/
metrics-2026-07-09.prom
traces/
spans-2026-07-09.jsonl
logs/
server.jsonl
agent.jsonl
profiles/
py-spy-2026-07-09.svg # 只有按需采样时存在
manifest.json # 机器信息、版本、时间范围、采样率、脱敏状态
7. 最终结论
埋点层(两档相同):OTel span + 同源指标 + Stream Telemetry + 结构化日志
盒子环境:只落盘留证(指标快照 / diagnostic JSONL spans / 日志),py-spy 按需,一键导出诊断包
服务器环境:Prometheus/Grafana 主监控 + Tempo 下钻 + Loki 日志 + Pyroscope 持续 profiling + Langfuse 语义层