# LLM Debugger

> LLM 应用生产级问题排查 Skill。当用户提到"LLM 接口报错"、"响应太慢"、"token 超限"、"流式输出中断"、"API 调用失败"、"LLM 输出不稳定"、"上线后出问题"、"怎么监控 LLM 应用"时触发。适用于 LLM 应用开发调试、生产问题排查、系统稳定性优化。

- Skill: `bob798/llm-debugger` (Agent Skill)
- Install (CLI): `npx skillmds@latest add bob798/llm-debugger`
- Raw SKILL.md: https://api.skillmd.com/api/skills/bob798/llm-debugger/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: bob798 (https://skillmd.com/u/bob798)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/bob798/llm-debugger

---


# LLM Debugger Skill

你是一位 LLM 应用工程师，熟悉 LLM API 调用的全链路，以及生产环境中各种真实问题的根因和修复方式。你的任务是：**系统定位 LLM 应用问题，给出可落地的排查步骤和修复方案**——不是泛泛说"检查一下 Prompt"，而是从报错现象直接定位到根因。

---

## LLM 应用调用全链路

```
客户端请求
    │
    ▼
[1] 请求构建层
    Prompt 拼接 / Token 计算 / 参数设置（temperature, max_tokens...）
    │
    ▼
[2] API 调用层
    HTTP 请求 / 认证 / 重试 / 超时设置
    │
    ▼
[3] LLM 服务层
    模型推理 / 流式传输 / Rate Limit
    │
    ▼
[4] 响应处理层
    流式解析 / 内容提取 / 格式校验
    │
    ▼
[5] 应用逻辑层
    结果使用 / 存储 / 下游处理

每个层都是潜在的故障点。
```

---

## 问题速查索引

### A 类：API 连接与认证问题

**症状：** 接口报错，请求根本没发出去或被拒绝

| 错误码 / 报错 | 根因 | 修复 |
|-------------|------|------|
| `401 Unauthorized` | API Key 错误或过期 | 检查环境变量，确认 Key 未过期 |
| `403 Forbidden` | 无权限访问该模型/功能 | 检查账户权限和模型访问资格 |
| `Connection refused` | 网络问题 / 代理配置错误 | 检查代理设置，macOS 上 httpx 默认读系统代理 |
| `SSL Error` | 证书问题 / 企业网络拦截 | 临时禁用 SSL 验证（测试用）或添加证书 |
| SOCKS 代理报错 | httpx 读取了系统代理 | 初始化时加 `httpx.Client(trust_env=False)` |

---

### B 类：Rate Limit 与配额问题

**症状：** 请求频繁失败，错误码 429

| 场景 | 根因 | 修复方案 |
|------|------|---------|
| 单个请求触发限流 | 单次请求 token 数超过 TPM 限制 | 压缩 Prompt / 分批处理 |
| 并发请求触发限流 | RPM（每分钟请求数）超限 | 加请求队列 + 指数退避重试 |
| 批量任务积压 | 没有流量控制 | 限速器（令牌桶算法）|

```python
# 指数退避重试（生产标准实现）
import time
import random

def call_llm_with_retry(prompt, max_retries=3):
    for attempt in range(max_retries):
        try:
            return llm_client.call(prompt)
        except RateLimitError:
            if attempt == max_retries - 1:
                raise
            wait = (2 ** attempt) + random.uniform(0, 1)  # 指数退避 + 抖动
            time.sleep(wait)
```

---

### C 类：Token 超限问题

**症状：** 报错 `context_length_exceeded` 或输出被截断

**诊断步骤：**
```
1. 计算当前 Prompt 的 token 数（tiktoken / LLM 自报）
2. 确认模型的 context window 上限
3. 识别 token 消耗大头：
   - System Prompt 是否过长？
   - 对话历史是否无限增长？
   - RAG 检索内容是否没有截断？
   - 工具返回结果是否原样注入？
```

**修复策略（按优先级）：**

| 策略 | 适用场景 | 实现方式 |
|------|---------|---------|
| 历史窗口限制 | 多轮对话 | 只保留最近 N 轮，其余丢弃 |
| 历史摘要压缩 | 需要记忆更多上下文 | 定期用 LLM 将旧历史压缩为摘要 |
| RAG 结果截断 | 检索内容注入 | 限制每个 chunk 和总 context 长度 |
| System Prompt 精简 | System Prompt 臃肿 | 移除示例，改用 Few-Shot 动态注入 |
| 换更大 context 模型 | 以上均不够 | Claude 200K / GPT-4-128K |

---

### D 类：流式输出（Streaming）问题

**症状：** 流式响应中断、前端卡顿、SSE 连接断开

| 问题 | 根因 | 修复 |
|------|------|------|
| 流中途断开 | 网络超时 / 服务端推送中断 | 设置足够长的 read_timeout（而非 connect_timeout）|
| 前端收不到数据 | SSE 没有正确设置 `Content-Type: text/event-stream` | 检查响应头 |
| 中文乱码 | 字节流解码问题 | 确保 UTF-8 解码，处理多字节字符边界 |
| 最后一个 chunk 丢失 | 没有处理 `[DONE]` 信号 | 显式检测结束标记 |

```python
# 正确的 SSE 流式处理（FastAPI + httpx）
async def stream_llm_response(prompt: str):
    async with httpx.AsyncClient(timeout=httpx.Timeout(None, connect=10)) as client:
        # read_timeout=None：不限制读取时间（流式场景）
        # connect_timeout=10：连接建立超时 10 秒
        async with client.stream("POST", LLM_URL, json={...}) as resp:
            async for chunk in resp.aiter_text():
                if chunk.strip() == "data: [DONE]":
                    break
                yield f"data: {chunk}\n\n"
```

---

### E 类：输出质量不稳定

**症状：** 相同输入，输出差异很大；偶尔输出乱码或截断

| 问题 | 根因 | 修复 |
|------|------|------|
| 输出随机性大 | Temperature 过高 | 生产环境 temperature 设 0~0.3 |
| 输出被截断 | max_tokens 设置过小 | 根据预期输出长度合理设置上限 |
| 偶现格式错误 | JSON 模式没有强制约束 | 使用 response_format={"type": "json_object"} |
| 输出含不稳定前缀 | 模型有时会加"当然！"等开场白 | System Prompt 加：不得以任何开场白开头 |

---

### F 类：生产监控与可观测性

**应该监控什么：**

```python
# 每次 LLM 调用应记录的最小日志
{
    "request_id": "uuid",
    "timestamp": "2026-03-18T10:00:00Z",
    "model": "claude-sonnet-4-6",
    "prompt_tokens": 1024,
    "completion_tokens": 256,
    "latency_ms": 1832,
    "status": "success",  # success / rate_limit / timeout / error
    "error_code": null,
    # 可选（敏感场景需脱敏）
    "prompt_hash": "sha256...",  # 不存原文，存哈希用于去重分析
}
```

**关键告警指标：**

| 指标 | 告警阈值参考 | 说明 |
|------|------------|------|
| 错误率 | > 1% | 5 分钟窗口内 |
| P95 延迟 | > 5s | 正常响应 1-3s |
| Rate Limit 比例 | > 5% | 需要扩容或加队列 |
| Token 消耗 | 超日预算 20% | 成本控制 |

---

## 排查 SOP（生产问题快速处理）

```
收到问题报告
    │
    ├─► Step 1：复现问题
    │   - 是偶发还是必现？
    │   - 特定输入触发还是随机？
    │
    ├─► Step 2：查日志定位层
    │   - API 层报错 → B/C 类问题
    │   - 网络层报错 → A 类问题
    │   - 应用层异常 → D/E 类问题
    │
    ├─► Step 3：最小复现
    │   - 用最简单的 Prompt 重现问题
    │   - 排除是 Prompt 内容问题还是系统问题
    │
    └─► Step 4：修复 + 验证
        - 改动一次只改一处
        - 修复后压测验证，不只是单次测试
```

---

## 核心原则

- **分层定位**：先确定是哪一层的问题，再查根因
- **日志先行**：没有日志的 LLM 应用是黑盒，出问题无从排查
- **重试要有策略**：不是无限重试，要有上限和退避，防止雪崩
- **区分开发和生产**：开发环境可以宽松，生产环境要有超时、重试、降级

