Skip to content

推理优化

推理优化关注的是:模型已经能回答之后,如何让它 更快、更稳、更便宜、更可控

一句话理解:

推理优化不是单纯把模型换小,而是在答案质量、首 token 延迟、总耗时、并发、成本和稳定性之间做工程平衡。

AI 应用上线后,用户最直观的感受通常不是“这个模型参数量是多少”,而是:

  1. 多久开始输出。
  2. 多久完整回答完。
  3. 高峰期会不会一直转圈。
  4. 一天 token 成本会不会爆。
  5. 模型失败时有没有降级。
  6. RAG、工具、模型到底是哪一步慢。

学习目标

学完本页,你应该能回答:

  1. 什么是推理,推理和训练有什么区别。
  2. 为什么大模型通常逐 token 生成,所以会比普通接口慢。
  3. TTFT、TPOT、P95、输入 token、输出 token、吞吐分别是什么意思。
  4. 为什么上下文越长、输出越长、并发越高,延迟和成本越高。
  5. KV Cache、流式输出、批处理、模型路由、缓存、限流分别解决什么问题。
  6. RAG 场景为什么要同时优化召回质量、token 成本和延迟。
  7. 缓存 Key 为什么必须包含租户、权限、Prompt 版本和知识库版本。
  8. 如何写一个带缓存、限流、超时、fallback 的最小 Demo。
  9. 线上 AI 慢、贵、超时、排队、质量下降时怎么排查。
  10. 面试时如何讲推理优化。

什么是推理

训练是让模型学习参数,推理是使用已经训练好的模型生成结果。

mermaid
flowchart TD
    A["训练阶段"] --> B["大量数据和算力"]
    B --> C["更新模型参数"]
    C --> D["得到模型权重"]
    D --> E["推理阶段"]
    E --> F["输入 Prompt"]
    F --> G["逐 token 生成答案"]

商业应用大多数时候不训练模型,而是做推理服务:把用户问题、RAG 资料、工具结果和 Prompt 交给模型,然后拿到回答。

推理为什么慢

大模型通常是自回归生成:每次预测下一个 token,再把这个 token 加回上下文,继续预测下一个。

mermaid
flowchart TD
    A["输入 Prompt"] --> B["Tokenizer 转 token"]
    B --> C["模型计算下一个 token 概率"]
    C --> D["采样或选择 token"]
    D --> E["新 token 加入上下文"]
    E --> F{"是否生成结束"}
    F -- "否" --> C
    F -- "是" --> G["解码成文本返回"]

所以延迟主要受这些因素影响:

因素为什么影响
模型大小参数越多,每步计算越重
输入 tokenPrompt、历史消息、RAG 片段越多,预填充越慢
输出 token逐 token 生成,输出越长循环越多
上下文长度KV Cache 占用和注意力计算压力增加
并发数多个请求争抢 GPU、队列和显存
工具/RAG检索、重排、数据库、外部接口也会耗时

关键指标

不要一上来就说“模型慢”。要先量化。

指标含义说明
TTFTTime To First Token,首 token 时间用户多久看到第一段输出
TPOTTime Per Output Token,平均每个输出 token 时间生成速度
Total Latency总耗时从请求进入到完成返回
P95/P9995% / 99% 请求耗时看高峰和长尾
Input Tokens输入 token 数Prompt、历史、RAG 上下文
Output Tokens输出 token 数模型生成长度
Throughput吞吐每秒 token 或每秒请求
Queue Time排队时间并发高时尤其重要
Error Rate错误率超时、限流、模型失败
Cost Per Request单次成本云模型费用或本地资源摊销

TTFT 和总耗时区别

流式输出主要改善 TTFT 感知,不一定减少总耗时。

text
非流式:用户等 8 秒,然后看到完整答案。
流式:用户等 1 秒看到第一个 token,8 秒后完整结束。

用户体验通常更看重 TTFT,但系统容量仍然要看总耗时和吞吐。

优化总流程

mermaid
flowchart TD
    A["采集全链路指标"] --> B["拆分耗时"]
    B --> C{"瓶颈在哪里"}
    C -- "入口排队" --> D["限流、队列、扩容"]
    C -- "RAG 检索慢" --> E["索引、TopK、过滤、缓存"]
    C -- "Rerank 慢" --> F["减少候选、异步、轻量模型"]
    C -- "Prompt 太长" --> G["压缩上下文和历史"]
    C -- "模型生成慢" --> H["流式、路由、小模型、批处理"]
    C -- "工具慢" --> I["超时、并发、缓存、降级"]
    D --> J["压测和评估"]
    E --> J
    F --> J
    G --> J
    H --> J
    I --> J

推理优化的原则:

  1. 先监控,再优化。
  2. 先定位瓶颈,再改方案。
  3. 每次优化都要同时看质量、成本、延迟。
  4. 不能为了快牺牲安全和权限。
  5. 不能只测单请求,要压测并发和长尾。

KV Cache

KV Cache 是推理服务里非常重要的概念。

大模型 Transformer 每一层都会计算 Key、Value。生成下一个 token 时,历史 token 的 Key/Value 可以缓存起来,避免每一步都重复计算完整历史。

mermaid
flowchart TD
    A["输入历史 token"] --> B["计算 Key / Value"]
    B --> C["写入 KV Cache"]
    C --> D["生成新 token"]
    D --> E["只为新 token 追加 KV"]
    E --> F["继续生成"]

KV Cache 的好处:

  1. 加快连续生成。
  2. 降低重复计算。
  3. 支持更好的流式体验。

KV Cache 的代价:

  1. 占用显存或内存。
  2. 上下文越长,占用越大。
  3. 并发越高,占用越大。
  4. 显存不足会导致 OOM、排队或请求失败。

所以长上下文不是免费的。你把 RAG TopK 从 5 提到 30,可能不仅 token 变贵,还会让 KV Cache 压力上升。

Prefill和Decode为什么必须分开看

一次自回归请求通常有两个模型阶段:

mermaid
flowchart TD
    A["输入Prompt的S个Token"] --> B["Prefill并行处理全部输入"]
    B --> C["为每层写入历史KV Cache"]
    C --> D["Decode读取历史KV并生成1个Token"]
    D --> E["把新Token的KV追加到Cache"]
    E --> F{"是否结束"}
    F -- "否" --> D
    F -- "是" --> G["释放请求KV页"]

Prefill

Prefill 一次处理输入序列,矩阵规模较大、并行度高,通常更偏计算密集。输入越长,需要处理的 Token 越多,注意力和线性层计算增加,因此 TTFT 往往上升。

Decode

Decode 每轮只产生一个或少量 Token,但每一层都要读取模型权重以及该请求历史 KV。单步矩阵较小、重复次数多,常更容易受内存带宽、批大小和调度影响。

因此:

  • TTFT 高但 TPOT 正常:优先查排队、长 Prompt、Prefill、RAG 前置耗时和冷启动。
  • TTFT 正常但 TPOT 高:优先查 Decode 批处理、显存带宽、量化、并发和输出长度。
  • 两者都高:先把 Queue、RAG、Prefill、Decode 分开,不要只说“GPU慢”。

总耗时可近似理解为:

text
totalLatency
≈ queueTime
+ applicationAndRagTime
+ prefillTime
+ outputTokenCount × TPOT
+ networkAndPostProcessTime

实际 TPOT 会随并发和批次变化,不能把单请求 TPOT 直接乘到高峰流量。

KV Cache容量怎样计算

对常见 Decoder-only Transformer,一个 Token 每层需要保存 Key 和 Value。简化公式:

text
KVBytesPerToken
= layers
× 2                 // Key和Value
× kvHeads
× headDim
× bytesPerElement

一个请求:

text
RequestKVBytes
≈ KVBytesPerToken × (inputTokens + generatedTokens)

多个并发请求:

text
TotalKVBytes
≈ Σ RequestKVBytes

这里使用 kvHeads,不是总 Query Heads。MHA 中二者常相同;GQA/MQA 通过多个 Query Head 共享较少的 K/V Head,能显著降低 KV 容量和读取量。

示例

假设:

text
layers=32
kvHeads=8
headDim=128
dtype=FP16/BF16,即2字节

则:

text
每Token KV = 32 × 2 × 8 × 128 × 2
             = 131072字节
             = 128KiB

一个总序列 8192 Token 约占:

text
128KiB × 8192 ≈ 1GiB

8 个同等并发理论上仅 KV 就约 8 GiB,还没有计算模型权重、激活、CUDA Context、临时工作区、碎片和运行时预留。

为什么上下文上限不能直接乘并发

所有请求不一定都达到最大长度,可以按真实输入/输出分布估算 P50、P95 和压力边界。但容量保护仍要设置单请求 Token 上限、全局 KV Token 预算和准入控制,否则少量超长请求就能占满显存。

可运行Demo:KV显存和并发预算

python
from dataclasses import dataclass


@dataclass(frozen=True)
class ModelKVConfig:
    layers: int
    kv_heads: int
    head_dim: int
    bytes_per_element: int


def kv_bytes_per_token(config: ModelKVConfig) -> int:
    return (
        config.layers
        * 2  # Key + Value
        * config.kv_heads
        * config.head_dim
        * config.bytes_per_element
    )


def gib(value: int) -> float:
    return value / (1024 ** 3)


config = ModelKVConfig(
    layers=32,
    kv_heads=8,
    head_dim=128,
    bytes_per_element=2,
)

per_token = kv_bytes_per_token(config)
sequence_tokens = 8192
one_sequence = per_token * sequence_tokens
print("KV per token KiB:", per_token / 1024)
print("one 8192-token sequence GiB:", round(gib(one_sequence), 3))

# 24GiB显存中,假设权重与运行时占16GiB,再预留10%安全空间。
gpu_bytes = 24 * 1024 ** 3
non_kv_bytes = 16 * 1024 ** 3
safety_reserve = int(gpu_bytes * 0.10)
kv_budget = gpu_bytes - non_kv_bytes - safety_reserve
max_theoretical_sequences = kv_budget // one_sequence
print("KV budget GiB:", round(gib(kv_budget), 3))
print("theoretical max sequences:", max_theoretical_sequences)

这是容量上界估算,不是压测结论。真实并发还受计算吞吐、调度、显存碎片、不同长度分布和SLO限制,必须用目标引擎、模型和硬件压测。

静态Batch为什么不适合长短请求混合

传统静态 Batch 往往等一组请求凑齐,再按批内最长序列补 Padding:

text
请求A:100 Token
请求B:500 Token
请求C:2000 Token

如果一起按 2000 Token 处理,短请求会产生大量 Padding 浪费;生成阶段还会出现已经结束的请求占着 Batch 位置,其他新请求必须等待。

Batch 大通常提高 GPU 利用率和总吞吐,但也可能增加排队和单请求延迟。因此吞吐与延迟不是同时无限改善。

Continuous Batching怎样工作

连续批处理不是等整个请求结束才更新 Batch,而是在 Token 迭代边界动态加入新请求、移除已完成请求:

mermaid
flowchart TD
    A["等待队列中的新请求"] --> B["调度本轮Prefill或Decode Token"]
    B --> C["GPU执行当前Batch"]
    C --> D["完成请求释放槽位和KV页"]
    D --> E["未完成请求进入下一轮"]
    E --> F["从队列补入新请求"]
    F --> B

收益:

  • 结束请求快速释放位置。
  • 新请求不必等最长请求完整结束。
  • GPU 保持更高利用率。

但调度策略会影响公平性:大量长 Prefill 可能阻塞 Decode,导致正在流式输出的请求 TPOT 抖动;只偏爱短请求又可能让长请求饥饿。

常见控制:

  • 每轮最大 Batched Token。
  • Prefill Chunking,把超长 Prefill 切成多段。
  • Prefill/Decode 优先级和时间片。
  • 每租户队列、权重和并发上限。
  • 等待时间老化,防止长请求永久饥饿。
  • Deadline 到期前取消,不让过期请求继续占GPU。

Paged Attention解决什么问题

若为每个请求按最大上下文预留一整块连续 KV 空间,会产生内部浪费;请求长短不同、结束时间不同还会形成外部碎片。

Paged Attention 的核心类似操作系统分页:

text
逻辑Token位置
→ Block Table
→ 非连续物理KV Block

请求增长时按需分配固定大小 Block,结束后回收到池中,不要求整段 KV 在物理显存连续。它提升显存利用率并便于共享前缀,但没有减少每个有效 KV 元素本身的理论容量。

Block 太大浪费尾页,太小则 Block Table 和调度开销增加;需要按引擎实现与负载压测。

Prefix Cache和普通答案缓存不同

Prefix Cache 复用相同前缀已计算的 KV,例如固定 System Prompt、工具定义或公共文档前缀。它减少重复 Prefill,但要求 Token 序列真正一致:一个空格、消息顺序、模板版本或Tokenizer变化都可能导致不命中。

安全边界:

  • 不能跨租户复用含私有上下文的前缀。
  • Key 包含模型、Tokenizer、Prompt和权限版本。
  • 不能把可变时间、随机Nonce放在公共前缀中间破坏命中。
  • 前缀失效要随策略和工具Schema发布。

答案缓存直接复用最终响应,风险更高;Prefix Cache 只复用模型内部中间计算,仍会继续生成当前请求答案。

量化为什么能省显存但可能损失质量

模型权重从 FP16/BF16 压到 INT8、INT4 等,可降低权重显存和内存带宽,并可能提高吞吐。近似权重量化过程:

text
浮点权重w
→ 根据scale和zeroPoint映射到有限整数
→ 计算时反量化或使用量化Kernel

需要区分:

  • Weight-only:主要量化权重,激活仍较高精度。
  • Weight + Activation:更省带宽,但校准和精度挑战更大。
  • KV Cache Quantization:进一步省并发KV,但可能影响长上下文质量。
  • PTQ:训练后量化,部署快。
  • QAT:训练时模拟量化,成本高但可改善精度。

量化不保证一定更快:若硬件或Kernel缺少高效支持,反量化开销可能抵消收益。必须在目标硬件比较 TTFT、TPOT、吞吐、显存、功耗以及分场景质量,特别是数字、代码、结构化输出和长上下文。

Speculative Decoding为什么可能更快

投机解码用较小 Draft Model 一次提出多个候选 Token,再由目标模型并行验证:

mermaid
flowchart TD
    A["当前已接受前缀"] --> B["Draft一次提议多个Token"]
    B --> C["Target并行验证候选"]
    C --> D["接受连续匹配部分"]
    D --> E["在首个不接受位置按Target继续"]
    E --> F["进入下一轮"]

它不应改变目标模型定义的输出分布,前提是算法正确实现采样验证。速度取决于接受率、Draft成本、验证批大小和硬件。

  • Draft与Target行为接近、输出可预测:接受率高,收益大。
  • 高温、复杂推理或领域差异大:频繁拒绝,可能无收益甚至更慢。

必须监控接受Token数、接受率、每轮提议数和端到端TPOT,不能只看Draft模型单独有多快。

模型并行解决什么问题

Tensor Parallel

把同一层矩阵切到多张GPU,并行计算后通信聚合。适合单层权重放不下一张卡,但每层都需要高频通信,对互联带宽和拓扑敏感。

Pipeline Parallel

把不同层放到不同GPU/节点,请求微批依次通过阶段。可容纳更大模型,但存在流水线气泡和阶段不均衡;在线小Batch低延迟场景收益不一定好。

Data Parallel / Replica

每个副本有完整模型,请求在副本间分流。扩吞吐最直接,但每个副本都要完整权重,单副本必须先能放下模型。

Expert Parallel

MoE把不同专家分布到设备,路由Token到对应专家。通信、负载不均和热点专家会影响尾延迟。

并行卡数增加不代表线性加速。通信、同步、负载不均、网络和调度开销会降低扩展效率。

容量估算:吞吐、并发和副本

Little's Law 的稳态直觉:

text
平均在途请求数 L ≈ 到达率 λ × 平均逗留时间 W

若每秒 4 个请求、平均完整耗时 5 秒,平均约 20 个请求在系统内。但容量不能只按平均:到达有突发,输出长度和服务时间是长尾,还要用 P95/P99、队列预算和安全余量压测。

Token吞吐需求可粗估:

text
requiredOutputTokensPerSecond
≈ requestsPerSecond × averageOutputTokens

副本估算:

text
replicas
≥ ceil(requiredThroughput / testedSafeThroughputPerReplica)

testedSafeThroughput 必须是在目标 TTFT/TPOT/P95、错误率和显存安全线下测出的持续吞吐,不是模型刚好不OOM时的峰值。

流式输出

流式输出适合聊天、解释、摘要、报告生成。

mermaid
flowchart TD
    A["请求进入"] --> B["模型生成 token"]
    B --> C["生成一个就发送一个"]
    C --> D["前端持续渲染"]
    D --> E["用户更快看到反馈"]

适合:

  1. 长文本回答。
  2. 聊天助手。
  3. 用户需要感知进度。
  4. 模型总耗时不可避免较长。

不适合:

  1. 必须完整校验 JSON 后再返回。
  2. 需要事务一致性的工具结果。
  3. 高风险回答必须先审核。

流式输出也要处理断开:

问题处理
用户关闭页面后端取消模型请求或停止写入
网络断开记录中断状态
模型中途失败前端给出失败提示
已输出不完整标记答案不完整,不进入评估集正样本

Prompt 压缩

Prompt 越长,输入 token 越多,通常 TTFT 越长、成本越高。

常见压缩方式:

方式说明风险
删除重复规则合并重复的系统提示词不要删安全规则
历史摘要多轮对话保留摘要摘要可能丢关键信息
RAG 片段筛选只放最相关片段TopK 太小会漏答案
字段裁剪工具结果只保留必要字段裁剪错会影响回答
结构化模板明确输出格式,减少废话格式过严可能丢解释

长对话怎么处理

错误做法:每轮都把所有历史消息塞给模型。

正确做法:

mermaid
flowchart TD
    A["历史对话增长"] --> B{"是否超过窗口或成本阈值"}
    B -- "否" --> C["保留近期消息"]
    B -- "是" --> D["抽取长期记忆和摘要"]
    D --> E["保留关键事实、约束、未完成任务"]
    C --> F["组装 Prompt"]
    E --> F

摘要要保留:

  1. 用户身份和任务目标。
  2. 已确认事实。
  3. 权限和安全限制。
  4. 未完成步骤。
  5. 用户偏好。

不要把敏感数据原样写进长期记忆。

RAG 场景优化

RAG 请求通常比普通问答更慢,因为多了检索和上下文。

mermaid
flowchart TD
    A["用户问题"] --> B["问题改写"]
    B --> C["Embedding"]
    C --> D["向量检索"]
    D --> E["关键词检索"]
    E --> F["合并去重"]
    F --> G["Rerank"]
    G --> H["Prompt 组装"]
    H --> I["模型生成"]

优化点:

环节优化
问题改写只在多轮省略明显时启用
Embedding缓存相同问题或标准化问题
向量检索调整索引、TopK、metadata filter
关键词检索给字段名、错误码、接口名建索引
Rerank先召回多,再重排少,不要候选无限大
Prompt控制片段数、片段长度、引用格式
生成控制输出长度,必要时流式

RAG 优化不能只减少 TopK。TopK 减少会变快,但可能导致召回率下降。必须用评估集看 Recall@K、引用命中率和拒答率。

缓存设计

缓存可以显著降低成本和延迟,但 AI 缓存比普通接口更容易出错。

可以缓存什么

缓存对象注意
Embedding 结果Key 包含文本和 Embedding 模型版本
RAG 检索结果Key 包含权限范围、知识库版本、问题
高频公共问答不包含个性化和敏感数据
Prompt 模板Key 包含 Prompt 版本
模型回答必须包含租户、权限、模型、Prompt、知识库版本

不能随便缓存什么

  1. 用户个人隐私回答。
  2. 实时数据查询结果。
  3. 权限相关答案。
  4. 含敏感字段的模型输出。
  5. 高风险工具调用结果。

安全缓存 Key Demo

python
import hashlib


def build_cache_key(
    *,
    tenant_id: str,
    user_scope_hash: str,
    question: str,
    model: str,
    prompt_version: str,
    knowledge_version: str,
) -> str:
    raw = "|".join([
        tenant_id,
        user_scope_hash,
        question.strip(),
        model,
        prompt_version,
        knowledge_version,
    ])
    return hashlib.sha256(raw.encode("utf-8")).hexdigest()

如果缓存 Key 只有 question,就可能把 A 租户、A 权限、旧知识库版本的答案返回给 B 用户,这是严重事故。

模型路由

模型路由是根据任务选择合适模型,不是所有问题都用最强模型。

场景推荐
简单分类、意图识别小模型或规则
格式转换、摘要中等模型
复杂推理、代码、复杂方案强模型
敏感数据内网处理本地模型
高风险回答强模型 + 规则校验 + 人工兜底

路由流程:

mermaid
flowchart TD
    A["用户请求"] --> B["识别场景、风险、长度"]
    B --> C{"是否高风险"}
    C -- "是" --> D["强模型或人工审核"]
    C -- "否" --> E{"是否简单任务"}
    E -- "是" --> F["小模型或规则"]
    E -- "否" --> G["默认模型"]

模型路由必须用评估集验证。不能只因为小模型便宜就全部切过去。

限流、排队和降级

AI 服务成本高、耗时长,必须保护系统。

手段作用
限流防止单用户或系统被打爆
配额控制每日或每月成本
队列高峰期排队,避免请求全部失败
超时防止接口无限等待
降级模型不可用时返回兜底或使用备用模型
熔断下游连续失败时暂停调用

最小 Demo:缓存、限流、超时、fallback

python
from __future__ import annotations

import hashlib
import time
from collections import defaultdict, deque


cache: dict[str, str] = {}
user_calls: dict[str, deque[float]] = defaultdict(deque)


def cache_key(user_scope: str, question: str, prompt_version: str) -> str:
    raw = f"{user_scope}|{question.strip()}|{prompt_version}"
    return hashlib.sha256(raw.encode("utf-8")).hexdigest()


def check_rate_limit(user_id: str, limit: int = 3, window_seconds: int = 60) -> bool:
    now = time.time()
    calls = user_calls[user_id]
    while calls and now - calls[0] > window_seconds:
        calls.popleft()
    if len(calls) >= limit:
        return False
    calls.append(now)
    return True


def call_model(question: str, timeout_seconds: float = 2.0) -> str:
    start = time.time()
    time.sleep(0.5)  # 模拟模型耗时
    if time.time() - start > timeout_seconds:
        raise TimeoutError("model timeout")
    return f"模型回答:{question}"


def chat(user_id: str, user_scope: str, question: str) -> str:
    if not check_rate_limit(user_id):
        return "当前请求过于频繁,请稍后再试。"

    key = cache_key(user_scope, question, "prompt-v1")
    if key in cache:
        return cache[key]

    try:
        answer = call_model(question)
    except TimeoutError:
        return "当前 AI 服务繁忙,请稍后重试。"

    cache[key] = answer
    return answer


print(chat("u1", "tenantA:roleUser", "什么是 RAG?"))
print(chat("u1", "tenantA:roleUser", "什么是 RAG?"))

这个 Demo 虽然简单,但体现了生产意识:

  1. 先限流。
  2. 缓存 Key 包含权限范围。
  3. 模型调用有超时。
  4. 失败有兜底。

真实项目还要加入 Redis、分布式限流、队列、traceId、token 统计和告警。

成本优化

云模型成本通常与 token 相关,本地模型成本通常与硬件、能耗、运维相关。两类都要算。

云模型成本

text
单次成本 = 输入 token 单价 * 输入 token 数 + 输出 token 单价 * 输出 token 数

优化方向:

  1. 减少无用上下文。
  2. 限制输出长度。
  3. 简单任务走小模型。
  4. 高频公共问答缓存。
  5. RAG 控制 TopK 和片段长度。

本地模型成本

text
综合成本 = GPU/服务器折旧 + 电力 + 运维 + 机房 + 人力 + 低利用率浪费

本地不一定更便宜。如果请求量低、模型质量要求高、运维能力不足,云模型可能更合适。

压测怎么做

推理优化必须压测,不能只本地试一个请求。

压测要覆盖:

说明
短 Prompt普通问答
长 PromptRAG、多轮对话
短输出分类、抽取
长输出总结、报告
高并发多用户同时问
工具慢调用外部接口拖慢
模型失败fallback 是否生效

记录指标:

  1. QPS。
  2. TTFT。
  3. P50/P95/P99 总耗时。
  4. 平均输入/输出 token。
  5. 错误率、超时率。
  6. 队列长度。
  7. GPU/CPU/内存/显存。
  8. 单次成本。

生产排查流程

mermaid
flowchart TD
    A["AI 请求变慢或成本升高"] --> B["确认影响范围"]
    B --> C["拆分入口、RAG、工具、模型耗时"]
    C --> D{"主要瓶颈"}
    D -- "入口排队" --> E["查限流、队列、并发、扩容"]
    D -- "RAG 慢" --> F["查向量库、TopK、Rerank、过滤"]
    D -- "Prompt 长" --> G["查历史、工具结果、Chunk 数"]
    D -- "模型慢" --> H["查 TTFT、TPOT、模型、并发、显存"]
    D -- "工具慢" --> I["查外部接口、超时、重试"]
    D -- "成本高" --> J["查输入输出 token、路由、缓存"]

首 token 慢

可能原因:

  1. 输入 Prompt 太长。
  2. RAG 或工具在模型前耗时。
  3. 模型预填充慢。
  4. 请求排队。
  5. 模型冷启动。

总耗时慢

可能原因:

  1. 输出太长。
  2. TPOT 高,模型生成慢。
  3. 并发抢资源。
  4. 网络或流式传输慢。

成本突然升高

可能原因:

  1. Prompt 版本加入了大量规则。
  2. RAG TopK 增大。
  3. 历史消息没有裁剪。
  4. 输出长度没有限制。
  5. 缓存失效或 Key 设计变化。
  6. 模型路由切到了更贵模型。

常见坑

后果正确做法
只看平均耗时长尾用户仍很慢看 P95/P99
只优化模型可能慢在 RAG 或工具拆分链路耗时
盲目减少 TopK速度快但答案错用评估集看召回
缓存 Key 只有问题权限串数据加租户、权限、版本
不限输出长度成本和耗时失控设置 max tokens 和输出格式
不做限流高峰期拖垮模型服务限流、队列、降级
不记录 token成本无法解释记录输入输出 token
只本地单请求测试上线并发崩做压测和灰度

商业场景:医疗数据资产助手

场景:用户问“LIS 检验结果资产 patient_id 是什么?最近采集失败原因是什么?”

链路包括:

  1. RAG 检索字段说明。
  2. 工具查询最近采集状态。
  3. 模型组织答案。
  4. 返回引用和排查建议。

优化思路:

问题优化
字段说明检索慢缓存字段类 Embedding 和热门 Chunk
工具查询慢设置工具超时,失败时只回答文档部分
Prompt 太长只放 patient_id 相关 Chunk 和脱敏工具结果
输出太长限制为“解释 + 原因 + 建议”三段
成本高字段类问题优先本地模型或小模型
高峰排队对普通问答限流,对管理员保留配额

面试标准回答

AI 推理优化怎么做?

可以这样答:

text
我会先监控再优化,不会直接猜模型慢。指标上看 TTFT、总耗时、P95/P99、输入输出 token、错误率、超时率、队列长度和单次成本。链路上拆入口、RAG、Rerank、工具、Prompt 组装和模型生成。常见优化包括流式输出降低首 token 感知,压缩 Prompt 和历史消息,控制 RAG TopK 和片段长度,缓存 Embedding 和高频问题,按场景做模型路由,增加限流、排队、超时、fallback 和压测。优化后必须用评估集验证质量没有下降。

流式输出能不能让模型变快?

流式输出主要降低用户感知等待,让用户更快看到首 token,不一定减少总生成时间。它适合聊天和长文本生成,但如果输出必须完整校验 JSON 或经过高风险审核,就不一定适合直接流式返回。

为什么上下文越长越慢越贵?

上下文越长,输入 token 越多,模型预填充越慢;生成时 KV Cache 占用也会增加。对于云模型,输入 token 会直接增加费用;对于本地模型,长上下文和高并发会增加显存压力,可能导致排队或 OOM。

AI 缓存为什么要带权限和版本?

因为同一个问题在不同租户、不同权限、不同知识库版本、不同 Prompt 版本下答案可能不同。如果缓存 Key 只有问题文本,可能把 A 用户有权限看到的答案返回给 B 用户,也可能把旧知识库答案返回给新版本用户。

AI 请求慢怎么排查?

先看是首 token 慢、总耗时慢还是排队慢。再拆入口、RAG、Rerank、工具、Prompt 和模型耗时。如果 RAG 慢看向量库、TopK、Rerank;如果 Prompt 长看历史和 Chunk;如果模型慢看 TTFT、TPOT、并发和显存;如果成本高看输入输出 token、模型路由和缓存命中率。

关联知识点

知识点作用
大模型基础理解 token、上下文和生成参数
Transformer原理理解逐 token 生成和注意力计算
本地模型部署理解 GPU、显存、量化、KV Cache
RAG知识库理解 RAG 检索和上下文构造
RAG数据治理理解知识库版本和权限对缓存的影响
AI应用架构理解模型网关、异步任务和链路拆分
LLMOps理解监控、成本、灰度和反馈闭环
AI面试题查看推理优化相关标准回答

本章小结

推理优化的核心不是“越快越好”,而是在质量、速度、成本、安全和稳定性之间找平衡。真正的生产优化要先采集指标,再拆链路定位瓶颈,然后用流式输出、Prompt 压缩、RAG 优化、缓存、模型路由、限流降级和压测逐步改进。每次优化都必须回到评估集,确认答案质量和安全没有退化。