name: llm-log-analyzer description: 分析 llm-gateway 代理产生的请求/响应日志。当用户说"分析日志"、"查看 LLM 请求"、"对比 session"、"检查 token 用量"、"日志里有什么"、"帮我看看 data 目录"、"哪个请求失败了"、"找一下 session 的请求"等涉及 LLM 网关日志分析的场景时使用此 skill。即使用户只是笼统地说"看看日志"或"data 里有什么",也应触发。
LLM Log Analyzer
分析 ./data/ 下的 LLM 请求/响应日志。
日志结构
每个请求对应一个目录,命名格式 YYYY-MM-DD_HH-MM-SS-mmm_NNNN:
新格式(当前)
data/
└── 2026-05-20_03-56-28-921_0014/
├── request.json # { headers: {...}, body: {...} }
└── stream.log # SSE 流式响应原文(含 usage 数据)
旧格式(兼容)
data/
└── 2026-05-14_10-30-15-123_0003/
├── request.json # { headers?: {...}, body?: {...} } 或裸 body
├── response.json # JSON 响应(非流式)
├── stream.log # SSE 流式响应原文
└── log.txt # 终端格式的人类可读日志
request.json 格式:{ "headers": {...}, "body": {...} }。body 包含以下字段:
model:模型名称(如deepseek-v4-pro)system:system prompt 数组(Anthropic 格式),每项含text+ 可选cache_controlmessages:消息数组tools:工具定义数组(Anthropic 格式用name,OpenAI 格式用function.name)thinking:推理配置(如{"type": "enabled", "budget_tokens": 8000})output_config:输出配置(如{"effort": "high"})stream:是否流式(true)max_tokens:最大输出 token
headers 中的 x-session-id 可按 session 追踪同一 agent 的多次请求。host 头用于推断 API 路由(如 api.deepseek.com → deepseek)。
stream.log 格式:SSE 事件流,包含:
message_start:初始 usage(input_tokens、cache_read_input_tokens、cache_creation_input_tokens、output_tokens=0)content_block_start/delta/stop:thinking、text、tool_use 内容块message_delta:最终 usage(含实际output_tokens)+stop_reasonmessage_stop:流结束标记
工具自动从 stream.log 的 message_delta 事件提取 token usage 数据,无需 response.json。
分析工具
scripts/llm-log-query.mjs 提供以下子命令,用 bun run scripts/llm-log-query.mjs <command> 运行:
list — 列出请求摘要
bun run scripts/llm-log-query.mjs list [--dir ./data] [--limit 20] [--model NAME] [--session ID] [--route openai|anthropic|deepseek] [--after YYYY-MM-DD] [--before YYYY-MM-DD] [--errors]
输出表格:序号 | 请求ID | 时间 | 路由 | 模型 | Session | 消息数 | 状态 | 延迟
show — 查看单个请求详情
bun run scripts/llm-log-query.mjs show <request-id> [--dir ./data] [--body] [--messages] [--tools] [--stream]
- 默认显示摘要(headers、模型、状态、延迟、token 用量、thinking 配置、output_config)
--body显示完整请求体--messages显示 system blocks + 消息列表(role + 内容前 100 字),system blocks 标注[cached]--tools显示工具定义列表,标注[cached]的 cache_control 状态--stream解析 stream.log 中的 SSE 事件- 无
response.json时,自动从stream.log提取响应(stop_reason、thinking、content、tool_calls)
session — 追踪一个 session 的完整请求链
bun run scripts/llm-log-query.mjs session <session-id> [--dir ./data] [--full]
按时间序列展示同一 session 的所有请求,显示每轮的角色和工具调用。--full 输出完整消息内容。
diff — 对比两个请求的差异
bun run scripts/llm-log-query.mjs session <session-id> diff <round1> <round2> [--dir ./data]
对比同一 session 中第 N 轮和第 M 轮请求的 messages 差异,高亮新增/删除/修改的消息块。用于观察 agent 如何逐步构建上下文。
也可以直接对比两个请求 ID:
bun run scripts/llm-log-query.mjs diff <request-id-1> <request-id-2> [--dir ./data]
stats — 统计汇总
bun run scripts/llm-log-query.mjs stats [--dir ./data] [--by model|session|route|hour]
输出汇总:总请求数、按维度分组(模型/session/路由/小时)的请求数、错误率。
cache — 缓存率深度分析(重要)
bun run scripts/llm-log-query.mjs cache [--dir ./data] [--session <id>] [--by-session] [--after YYYY-MM-DD] [--before YYYY-MM-DD]
这是最常用的诊断命令之一。 Prompt Cache 命中率直接影响 API 成本和延迟,每次分析日志时都应主动运行此命令,即使没有明确要求。
输出内容:
- 全局缓存率:缓存命中 token / 总输入 token,以及缓存写入率、冷 miss 率
- 缓存健康度:按阈值(无缓存 / < 30% / >= 30%)分级统计
- Session 缓存趋势(
--by-session或--session):逐轮展示缓存命中率变化,判断前缀是否稳定 - 自动诊断:
- 所有请求无缓存 → 检查 prompt caching 是否启用
- 部分请求无缓存 → 区分冷启动(正常)vs 前缀不稳定(异常)
- 缓存写入 > 缓存读取 → 缓存投入未回收,前缀在请求间变化
- 逐轮下降 → messages 前缀被 prepend 打乱、tools 顺序变化、system prompt 动态段过大
cache-debug — 缓存诊断深度分析
bun run scripts/llm-log-query.mjs cache-debug <request-id-1> [request-id-2] ... [--dir ./data]
对指定请求进行深度缓存诊断:自动查找同 session 的前一轮请求,对比缓存变化类型(冷启动/缓存失效/缓存稀释/正常),分析 cache_control 断点地图,定位缓存失效的具体原因(system prompt 变化、tools 数组变化、消息前缀变化等)。
cache-control — 断点地图与问题检测
bun run scripts/llm-log-query.mjs cache-control <request-id> [--dir ./data]
展示指定请求的 cache_control 断点地图:system blocks、tools、messages 中的缓存标记位置,累积 token 估算,以及断点覆盖问题检测。
缓存率分析检查清单
分析日志时,务必关注以下指标:
| 指标 | 正常范围 | 异常信号 |
|---|---|---|
| 缓存命中率 | > 50%(ReAct 第 2 轮起) | < 30% 持续出现 |
| 缓存写入率 | 首轮高、后续低 | 每轮都很高(前缀总在变) |
| 冷 miss 率 | 仅首轮 | 多轮后仍有冷 miss |
| 逐轮趋势 | 稳定或微升 | 持续下降 |
常见缓存失效原因排查顺序
- system prompt 变化:用
session <id> diff 1 2对比前两轮的 system 消息,检查是否有日期、cwd 等动态占位符在边界标记之前 - tools 数组顺序:用
session <id> diff 1 2检查 tools 列表是否一致(HashMap 迭代顺序不确定会导致序列化不稳定) - 消息前缀被 prepend 打乱:用
session <id> diff 1 2检查消息序列开头是否被插入了新消息 - cache_control 断点丢失:用
cache-control <id>或show <id> --body检查请求体中 system/tools/messages 的 cache_control 标记是否存在
context-growth — 上下文膨胀轨迹分析
bun run scripts/context-growth.mjs --dir <data-dir> --session <id>
这是诊断"上下文为什么消耗这么快"的核心工具。 输出 session 内每轮的消息数、估算 token、消息组成占比(system/thinking/tool_calls/tool_results/compacted)、新增消息摘要、compact 事件、LLM 实际调用 usage。
输出内容:
- 关键轮次表格:每轮的消息数、Δ变化、估算 tokens(K)、组成占比(SYS/THK/CALL/TOOL/CMP)、新增消息类型摘要、工具调用、thinking 文本、compact 事件标记
- 汇总:首末轮消息数/tokens、平均每轮增长、LLM 实际调用次数、full compact 次数
- 组成贡献:末轮的 system/工具结果/已压缩/思考/工具调用/对话占比
诊断要点:
| 指标 | 正常 | 异常信号 |
|---|---|---|
| 每轮消息增长 | +2(1 assistant + 1 tool_result) | +10 以上(回注 compressed 消息) |
| 工具结果占比 | < 50% | > 70%(大文件读取/cargo test 输出堆积) |
| Micro-compact 首次触发 | 消息数 < 50 | 消息数 > 100(太晚,上下文已膨胀) |
| Full compact 后缓存 | 逐步恢复 | 暴跌至 < 20%(前缀全丢) |
| 估算 token 增长率 | < 1K/轮 | > 5K/轮(大工具输出未截断) |
典型异常模式:
- 工具结果肿胀:
cargo test/cargo build输出逐轮累积,单次 +10-15K tokens,无自动截断 - Micro-compact 延迟:到 100+ 轮才首次压缩,此时上下文已膨胀到 100K+ tokens
- Compact 回注风暴:compacted 消息一次性恢复(单轮 +100+ 条消息),而非渐进回注
- Full compact 缓存惩罚:压缩后前缀结构全变,缓存命中率从 ~99% 暴跌至 ~15%,每轮浪费数万 tokens
分析流程
面对用户的日志分析需求,按以下步骤工作:
- 定位范围:先用
list了解日志总量和时间范围,必要时用--after/--before缩小范围 - 缓存率检查(每次分析都应执行):运行
cache命令,检查缓存命中率和健康度。如果发现问题,用--by-session查看逐轮趋势,再用session <id> diff定位前缀不稳定的具体原因 - 上下文膨胀分析(长时间 session 必须执行):运行
context-growth命令,分析消息增长轨迹、工具结果大小、compact 时机和效果。找出上下文消耗的根本原因(是大工具输出、compact 太晚、还是回注风暴) - 按需下钻:
- 查某个 session 的完整交互 →
session - 查某个具体请求 →
show - 深度缓存诊断 →
cache-debug - 断点地图 →
cache-control - 对比两轮请求差异 →
session ... diff或diff - 统计全局概况 →
stats
- 查某个 session 的完整交互 →
- 解读结果:将工具输出翻译为用户能理解的结论(如"这个 session 共 5 轮 LLM 调用,缓存命中率 72%,第 3 轮因 tools 列表变化导致缓存失效")
注意事项
- 工具路径相对于 skill 目录:
scripts/llm-log-query.mjs、scripts/context-growth.mjs - 数据目录默认为
side-projects/llm-gateway/data/,如果用户指定了其他目录用--dir覆盖 request.json的 headers 字段中x-api-key等敏感字段已被脱敏(只保留前 12 字符 +…),分析时注意不要试图还原- stream.log 是原始 SSE 文本,内容可能很大,展示时注意截断
- 新格式不再有
response.json和log.txt,路由从headers.host推导,状态从 stream 内容判断,usage 从message_delta事件提取 - 脚本同时兼容旧格式(有
response.json/log.txt)和新格式(仅request.json+stream.log) context-growth.mjs从request.json的body.messages估算 token(chars/3.5),LLM usage 从stream.log的最后一个message_delta事件提取。估算值与实际值有 ±15% 偏差,用于趋势分析而非精确计量