← 回到知识图谱
概念持续完善

工程化

本文总结模型怎么选、LoRA 怎么省显存、KV Cache 和延迟怎么算、每千次请求花多少钱、高峰怎么扛:把这些数一个个算出来。

创建 2026-10-31更新 2026-11-02阅读约 95 分钟
Agent面试工程化

主线第 7 站 · 上一站:评测 / 安全 · 下一站:Coding / Harness · 全程:主线

本站在主线里回答什么:员工那句「我上周出差打车超标了,还能报吗?能的话帮我提报销工单。」走到这里,前六站已经让助手能答对、能建对单,也能拿数据证明它可靠。第 6 站交过来一套可重复执行的报销评测集(超标可报、超标不可报、缺发票、重复发票、工单字段错误、发票文本里藏注入等用例),以及结果、过程、安全、代价四类基线和发布门槛。本站的问题更现实:正确率一样的前提下,用什么模型,花多少钱,多快,能扛多少人。

落到这个请求上:意图识别、"要不要查制度"这类轻判断交给便宜的小模型;读《差旅费管理办法》2026 版第 4.2 条,判断 INV-102 的 118.5 元比 80 元标准超了 38.5 元、有没有例外审批,并且带引用,这一步交给强模型;从 query_invoices 的返回里抽字段、拼 create_reimbursement_ticket 的参数,用开了结构化输出的中档模型,或者微调过的小模型;制度原文始终走 RAG,不进微调数据。我们还要算清一次成功报销平均用多少 token、花多少钱,首 token 延迟和端到端耗时各是多少,月底集中报销时峰值并发有多高,出了故障怎么降级(比如发票系统挂了,就只给制度依据,建单排队)。每一项优化都要先过第 6 站的门槛。本站交给第 8 站的是一份可执行的运行时配置:模型路由表,每一步的 token、步数和超时预算,限流与熔断阈值,降级路径,监控看板。Harness 再把它做成产品。

先给结论

工程化的题目最后都落到四个数上:效果、成本、延迟、稳定性。面试官想听你怎么选模型、怎么估显存、怎么降本、怎么扛并发。只说一句"vLLM 很快"拿不到分;能说出"7B 模型 FP16 权重 14 GB,8k 上下文每条请求的 KV 还要 4 GB 左右,所以一张 24 GB 的卡只能同时跑两路",才算答到点上。

1. 模型选型

榜单只能回答"效果"这一项里的一小部分。选型至少要看六项:

维度 要问的问题
效果 任务成功率、事实性、指令遵循
成本 输入/输出单价、上下文长度、重试
延迟 首 token、总时长、并发下的 P95
上下文 多长、长文效果、Lost in the Middle
工具调用 Function Calling/JSON 稳定性
部署 开源/闭源、私有化、合规、中文

做法是先用强模型打样,同时把评估集建起来;然后逐步试小模型、路由、微调、量化,看能不能在效果可接受的前提下把钱省下来。举个示意的例子:报销助手先用最强的模型把 50 条评测用例跑到 90% 成功率,这个数就是基线;接着只把"意图识别"这一步换成小模型重跑,成功率还在门槛以上就换,掉下去就退回。一开始就为了省钱上小模型,你连"掉了多少"都没法知道;反过来,简单步骤一直用最贵的模型,钱就白花了。

2. 成本怎么算

一次模型调用的钱,先看输入和输出各多少 token,再加上工具和重试:

creq=Tin⋅pin+Tout⋅pout+ctool+cretryc_{\text{req}}=T_{\text{in}}\cdot p_{\text{in}}+T_{\text{out}}\cdot p_{\text{out}}+c_{\text{tool}}+c_{\text{retry}}
符号 含义 单位
creqc_{\text{req}} 一次请求的总成本 元
TinT_{\text{in}}、ToutT_{\text{out}} 输入、输出 token 数(推理模型的思考 token 一般按输出计费) token
pinp_{\text{in}}、poutp_{\text{out}} 输入、输出单价,输出通常比输入贵好几倍 元 / token
ctoolc_{\text{tool}} 检索、rerank、外部接口等按次收费的部分 元
cretryc_{\text{retry}} 失败重试多花的钱,按重试比例摊到每次请求上 元

代入一组示意数字算每千次请求:输入单价 2 元 / 百万 token,输出 8 元 / 百万 token;每次输入 6000 token(系统提示、工具定义、制度条款、发票结果),输出 400 token。

  • 输入:1000 次 × 6000 = 600 万 token,× 2 元 / 百万 = 12 元;
  • 输出:1000 次 × 400 = 40 万 token,× 8 元 / 百万 = 3.2 元;
  • 小计 15.2 元,也就是每次 0.0152 元;
  • 10% 的请求因为 JSON 不合法重试一次:多 100 次 × 0.0152 = 1.52 元;
  • 每次调一次 rerank,示意按 0.001 元 / 次:1 元。

合计每千次 17.72 元。输出单价是输入的 4 倍,可输入 token 是输出的 15 倍,所以 12 元的输入才是大头。Agent 场景几乎都这样:输入长、输出短。所以下面的优化顺序把减上下文和缓存放在最前面。这个式子没算的东西也要记得:命中提示缓存的输入通常有折扣,人工兜底的成本不在 token 账里。

Agent 会把成本放大:多轮调用,每轮重复带上下文,加上工具结果、记忆检索、rerank,都是钱。优化顺序大致是:

  1. 减上下文:压缩、裁剪、摘要,只带相关证据。制度只带命中的第 4.2 条,不带整本《差旅费管理办法》;
  2. 缓存:系统提示、稳定前缀、只读工具结果;
  3. 路由:简单任务走小模型;
  4. 结构化输出:减少因为格式错误引起的重试;
  5. 微调或量化:把行为固化下来,降低推理成本;
  6. 推理优化:batching、KV Cache、PagedAttention。

3. LoRA 与微调

全量微调要更新一个 4096×4096 的矩阵,就得给 1677 万个数都存梯度和优化器状态。LoRA 的做法是:原权重冻住不动,只额外训练一个增量,并且规定这个增量必须能写成两个瘦长矩阵的乘积:

W′=W+BA,B∈Rd×r,A∈Rr×d,r≪dW'=W+BA,\qquad B\in\mathbb{R}^{d\times r},\quad A\in\mathbb{R}^{r\times d},\quad r\ll d
  • WW 是预训练好的原权重,形状 d×dd\times d,训练时冻结;W′W' 是微调后实际使用的权重。
  • Rd×r\mathbb{R}^{d\times r} 读作"d 行 r 列的实数矩阵",∈\in 读作"属于"。所以 B 是 d 行 r 列,A 是 r 行 d 列。
  • rr 是秩(rank),可以理解为"这个增量最多包含 r 个独立的方向"。r≪dr\ll d 读作"r 远小于 d",常见取值 8、16,而 d 是几千。
  • BABA 是矩阵乘法,结果是 d×dd\times d,和 W 形状一样,所以能直接相加。

用一个 3×3、r = 1 的小例子看 BA 长什么样:

[120]⏟B (3×1)[0.50−1]⏟A (1×3)=[1×0.51×01×(−1)2×0.52×02×(−1)0×0.50×00×(−1)]=[0.50−110−2000]\underbrace{\begin{bmatrix}1\\2\\0\end{bmatrix}}_{B\,(3\times1)} \underbrace{\begin{bmatrix}0.5&0&-1\end{bmatrix}}_{A\,(1\times3)} =\begin{bmatrix}1\times0.5&1\times0&1\times(-1)\\2\times0.5&2\times0&2\times(-1)\\0\times0.5&0\times0&0\times(-1)\end{bmatrix} =\begin{bmatrix}0.5&0&-1\\1&0&-2\\0&0&0\end{bmatrix}

结果是一个 3×3 矩阵,9 个数,但只用了 B 的 3 个数加 A 的 3 个数,共 6 个参数。而且你会发现第二行正好是第一行的 2 倍,第三行全是 0:每一行都是 A 那一行乘上一个系数,这就是"秩为 1"的意思。r = 2 时,相当于两个这样的秩 1 矩阵相加,能表达的变化多一些。

前向计算时,一般不把 BA 先乘出来,而是先算 AxAx 再乘 B。取输入 x=[2,1,0]⊤x=[2,1,0]^\top(上标 ⊤\top 表示转置,这里把一行数竖过来当列向量):

  • Ax=0.5×2+0×1+(−1)×0=1Ax=0.5\times2+0\times1+(-1)\times0=1,得到一个数(r 维向量,这里 r = 1);
  • B(Ax)=[1,2,0]⊤×1=[1,2,0]⊤B(Ax)=[1,2,0]^\top\times1=[1,2,0]^\top。

直接用上面乘好的 BA 去乘 x,第一行 0.5×2 = 1,第二行 1×2 = 2,第三行 0,结果一样。先算 Ax 只要 3 + 3 = 6 次乘法;拿乘好的 BA 去乘 x 要 9 次,还没算乘出 BA 本身的 9 次。一般地,前者是 2rd2rd 次,后者是 d2d^2 次,d = 4096、r = 8 时差 256 倍。训练时 adapter 是旁路,按这种顺序算;部署时如果把 BA 合并进 W,就没有这条旁路了。

换成真实尺寸:d = 4096、r = 8 时,原矩阵 4096 × 4096 = 16,777,216 个参数;LoRA 只有 A 的 8 × 4096 = 32,768 个加 B 的 4096 × 8 = 32,768 个,共 65,536 个,是原来的 1/256,约 0.39%。

这个设计赌的是"微调需要的改动本来就是低秩的"。如果任务真要大改模型(比如灌入大量新领域语料),r 太小会学不动,这时要调大 r 或者改用全量微调。其余几个要点:

  • r 增大,容量和开销都增加,效果是变好还是过拟合,要在验证集上看;
  • alpha 控制缩放,实际乘到增量上的是 alpha/r,下文"扩展"一节有算例;
  • target modules 通常选 attention 的 q、k、v、o,也可以加上 FFN。

QLoRA:把基座量化到 4-bit,再在上面训练 LoRA。显存省了不少,但可能损失一点精度,速度也未必更快(显存算例见下文 LoRA / QLoRA 一题)。

微调适合固定格式、风格、行为,领域术语,稳定的工具调用;不适合实时事实和经常变更的知识。比如报销工单的 JSON 字段顺序可以靠微调学,"市内交通标准 80 元"这种数字就不行:制度一改版,模型里记住的还是旧数。经验是事实用 RAG,行为用微调,简单约束写进 Prompt。

SFT、DPO、RLHF 的区别:

  • SFT:用成对的输入和理想输出做示范,教格式和行为;
  • DPO:用"好回答 vs 差回答"的偏好对来优化,不需要奖励模型;
  • RLHF/PPO:先训练奖励模型,再做强化学习,成本高;
  • GRPO:同一题采样一组回答做组内比较,省掉 critic(价值模型),适合答案能自动判对错的推理任务。

4. 推理与显存

权重显存就是"参数量 × 每个参数占几个字节":

FP16:参数量 × 2 bytes
INT8:参数量 × 1 byte
INT4:参数量 × 0.5 byte

7B 模型 FP16:7×10⁹ × 2 字节 = 14×10⁹ 字节,约 14 GB(换成 GiB 约 13 GiB);70B 约 140 GB。这只是权重,还要加 KV Cache、激活和框架开销。

KV Cache(逐项解释和算例见下文 KV Cache 一题):

2 × layers × kv_heads × head_dim × seq × batch × bytes

常见的优化手段:

  • MQA、GQA 减少 KV 头数;MLA 用低维潜变量压缩缓存;
  • PagedAttention 按页管理 KV,减少碎片;
  • Continuous Batching 动态拼批,提高吞吐;
  • Prefix Caching 复用相同前缀;
  • 量化 KV Cache(可能损失精度);
  • Prefill 和 Decode 分离,按阶段分别扩缩容。

首 token 延迟主要由 prefill 和排队决定;总延迟还要算 decode 速度、工具调用和网络。

5. 高并发与稳定性

  • 限流:按用户、租户、API Key 分别限;
  • 排队:优先级队列,每个请求带超时;
  • 熔断:下游故障时快速失败,比如 query_invoices 连续超时就先不调它;
  • 降级:切小模型、用缓存,或者给固定回答;
  • 多实例:服务本身无状态,状态放共享存储;
  • 监控:QPS、P95/P99、错误率、token、GPU 利用率、队列长度;
  • 压测:按峰值和故障场景压,只看平均值会漏掉尖峰。

6. 一个估算例子

教学算例:按 Llama-2-7B 这类 MHA 结构(32 层、32 个 KV 头、head_dim 128)、FP16、8k 上下文、batch = 8 来估:

  • 权重约 14 GB;
  • KV Cache 每 token:2 × 32 × 32 × 128 × 2 字节 = 524,288 字节 = 0.5 MiB;8k(8192)token 一条是 4 GiB,× 8 条 = 32 GiB;
  • 再加激活和框架开销,一张 48 GB 的卡也未必放得下,需要量化、换 GQA 结构或者减小 batch。换成 8 个 KV 头的 GQA 结构,KV 直接降到四分之一,8 条只要 8 GiB。

这种估算比背一句"7B 要 14G"更能体现工程能力。

7. 60 秒回答模板

选型看效果、成本、延迟、上下文、工具调用和部署约束。成本按输入、输出 token 加工具和重试来算,Agent 里输入通常是大头;优化顺序是上下文压缩、缓存、路由、结构化输出、微调或量化、推理优化。微调用 LoRA 或 QLoRA,事实靠 RAG,行为靠微调。推理显存看权重和 KV Cache,7B FP16 权重约 14 GB,KV Cache 随序列长度和并发线性增长;vLLM 用 PagedAttention 和 continuous batching 提高吞吐。高并发靠限流、排队、熔断、降级和监控。

8. 常见追问(带答法)

7B/70B 要多少显存?怎么估?

简答:显存 = 权重 + KV Cache + 激活与临时缓冲 + 框架预留。推理时先按精度算权重,再按上下文长度和并发数算 KV;训练还要加梯度和优化器状态,量级完全不同。

细说:按四块来算。

第一块是权重,参数量乘每个参数的字节数:

精度 7B 70B
FP16 / BF16 约 14 GB 约 140 GB
INT8 / FP8 约 7 GB 约 70 GB
INT4 约 3.5 GB(另有 scale 等元数据,实际略多) 约 35 GB

第二块是 KV Cache,每 token 字节数 × 上下文长度 × 并发数(公式和逐项解释见下文"KV Cache"一题)。教学算例:Llama-2-7B 这类 MHA 结构每 token 约 0.5 MB,8k 一条约 4 GB;Llama-2-70B 用 GQA(80 层、8 个 KV 头),每 token 约 320 KB,8k 一条约 2.5 GB。70B 的权重是 7B 的十倍,每 token 的 KV 反而比 MHA 结构的 7B 小,所以决定 KV 大小的是注意力结构,参数量只是次要因素。

第三块是激活和框架。推理时激活不大,但有临时 buffer、CUDA Graph、通信缓冲。vLLM 这类引擎会按 gpu_memory_utilization 先占住大部分显存,减掉权重后剩下的都做成 KV 块池。比如 80 GB 的卡设 0.9,引擎先占 72 GB,放 7B FP16 权重 14 GB 后,剩下五十多 GB 给 KV 和临时缓冲。

第四块只在训练时有。全参数加 AdamW 混合精度,常用经验是每参数约 16 字节:BF16 权重 2 + 梯度 2 + FP32 主权重 4 + Adam 两个动量 8。7B 就是 7×10⁹ × 16 = 112 GB,还没算激活,必须多卡加 ZeRO 或 FSDP 切分。LoRA 只给少量参数存梯度和优化器,显存主要是基座 14 GB 加激活;QLoRA 把基座压到 4 GB 左右。

面试里可以这样收尾:70B FP16 推理至少要两张 80 GB 的卡做张量并行(权重 140 GB,再加 KV);INT4 权重能放进单张 80 GB 卡,但剩下给 KV 的空间决定了并发上限。

容易踩的坑:只报权重数是最常见的不完整答案。KV 随并发线性增长,而并发恰恰是吞吐的来源,忘了它就会在压测时 OOM。INT4 能省显存,但要看有没有对应的 kernel、业务精度掉没掉。GB 和 GiB 差约 7%,14 GB 只有约 13 GiB。MoE 模型要按总参数备显存。

可能的追问:

  • 一张 24GB 卡能跑 7B 吗?能跑,但 FP16 权重占掉 14 GB 后只剩约 10 GB 给 KV 和开销,按每条 8k 请求 4 GB 算,只能并发两路左右。短上下文、低并发可以;要长上下文或高并发,就量化权重或 KV,或者选 GQA 结构的模型(Llama-3-8B 类 8k 一条只要 1 GB 左右)。
  • 显存够了为什么还慢?显存只决定放不放得下,速度要看 prefill 的算力和 decode 的显存带宽,见下文"Prefill 和 Decode"。

LoRA 为什么有效?r 怎么选?和全量微调怎么选?

简答:下游适配真正需要的权重改动维度很低。LoRA 只学一个低秩增量 ΔW=BA \Delta W = BA (ΔW\Delta W 读作"W 的改变量"),冻结基座,可训练参数压到原来的零头。r 是容量旋钮,用验证集来定;数据量大、和预训练分布差得远、要改深层能力时,才考虑全量微调。

细说:先说为什么有效。有研究发现,预训练模型的微调可以在很低维的子空间里完成(intrinsic dimension 一类工作);LoRA 论文进一步观察到适配所需的 ΔW \Delta W 秩很低,部分任务上很小的 r 已经接近全量效果。直觉上,预训练已经学到了能力,微调多数是在重新组合、放大已有的方向,用不着学一整套新知识。比如让模型学会"输出工单 JSON 时先写 invoice_id 再写 amount",这种改动在几千维的权重空间里只占很少几个方向。

初始化时 A 随机、B 置零,所以训练开始那一刻 ΔW=0 \Delta W=0 ,模型和原模型完全一样,训练从一个稳定的点出发。

部署也方便:可以把 BA 合并回 W,推理没有额外延迟;也可以不合并,一个基座挂多个 adapter,按请求热切换,适合多租户、多任务。

r 怎么选:从 8 或 16 起步。任务越复杂、数据越多、和预训练分布差得越远,就往 32、64 试。alpha 常设为 r 或 2r。改 r 时要注意,实际乘到增量上的是 alpha/r,它相当于一个学习率倍率:r 从 8 改成 16 而 alpha 还留在 16,倍率就从 2 掉到 1,等于偷偷把学习率减半(rsLoRA 改用 α/r\alpha/\sqrt{r},缓解大 r 时更新幅度被压得过小的问题,算例见下文"扩展"一节)。target modules 可以只挂 q、v,也可以挂所有线性层(q、k、v、o 加 FFN),后者通常更稳,开销也更大。判断依据是验证集曲线,训练 loss 降得好看不说明问题。

和全量微调怎么选:

条件 倾向
数据几百到几万条,学格式、风格、工具调用 LoRA
显存有限、要多个任务版本并存 LoRA / QLoRA
数据量大、要注入大量领域语料(类继续预训练) 全量或高 r
追求某项能力的上限,且算力充足 全量,同时防遗忘

容易踩的坑:LoRA 学进大量新知识的能力有限,但对原能力的破坏也更小(可参考论文 LoRA Learns Less and Forgets Less)。r 不是越大越好,几百条数据配 r = 64 很容易过拟合。LoRA 的学习率通常比全量微调大一个量级(比如全量用 1e-5,LoRA 用 1e-4 到 2e-4 比较常见),照搬全量的学习率会学不动。

可能的追问:

  • A、B 为什么一个随机一个置零?都置零行不行?不行。B 的梯度依赖 Ax Ax ,A 的梯度依赖 B,两者都为零时梯度恒为零,训练动不了。都随机的话,初始 ΔW≠0 \Delta W \neq 0 ,一上来就扰动了原模型。一随机一置零时,第一步 B 先拿到非零梯度,A 随后也就有梯度了。
  • 还有哪些变体?DoRA 把权重拆成幅度和方向分别更新;QLoRA 在量化基座上训练。它们都在"可训练参数少"和"接近全量效果"之间找更好的点。

vLLM 为什么快?PagedAttention 解决什么问题?

简答:解码阶段受显存带宽限制,同一张卡的吞吐主要看能同时塞进多少个请求。vLLM 用 PagedAttention 把 KV 显存的浪费压到很低,用 continuous batching 让 GPU 不空转,于是同样的卡能跑更大的有效 batch。它优化的是显存管理和调度,单条请求未必更快。

细说:把吞吐的来龙去脉按顺序捋一遍。

  1. decode 每一步都要把权重从显存读一遍。batch 越大,这次读取就被越多请求分摊。7B FP16 读一遍是 14 GB,batch = 1 时这 14 GB 只产出 1 个 token,batch = 32 时产出 32 个。
  2. batch 能开多大,受 KV Cache 显存的限制。
  3. 传统做法按最大长度给每个请求预留一段连续显存。比如最大长度 2048,实际只生成了 300 个 token,剩下的预留就空着;请求之间的空洞、长度不一造成的碎片,也都是浪费。
  4. PagedAttention 把 KV 分成固定大小的块,按需分配,浪费只剩每个序列最后一块没填满的部分。块大小 16 时,每个序列最多浪费 15 个 token 的位置,同样的显存能放下更多请求。
  5. continuous batching 在每个解码步重新组批,完成的请求立即让位,新请求立即加入。
  6. 在这之上再叠加 prefix caching、chunked prefill、量化 kernel、CUDA Graph、投机解码、张量并行等。

PagedAttention 具体解决三件事。一是预留浪费和碎片,靠按块分配。二是共享:并行采样、beam search 的多个输出共享同一段 prompt 的 KV 块,谁要改写谁复制(写时复制)。三是弹性:显存不够时可以按块换出或重算,不必直接 OOM。参见 vLLM 论文。

容易踩的坑:低并发时优势不明显。最大并发数开得越大,吞吐越高,但每 token 延迟会变差,要按 SLO 调。显存紧张时频繁抢占(重算或换出)会拉高尾延迟。"vLLM 让模型变快"这种说法是错的,它让的是同一张卡服务更多人。

可能的追问:

  • vLLM、SGLang、TensorRT-LLM 怎么选?前两者都支持分页 KV 和连续批处理,SGLang 的 RadixAttention 对多轮对话、共享前缀和结构化生成友好;TensorRT-LLM 在 NVIDIA 硬件上优化得深,但部署流程重。最终要拿自己的负载(输入输出长度分布、并发、前缀复用率)压测来定。

线上 QPS 上来后,先优化模型还是先优化工程?

简答:先工程、后模型。先用数据定位瓶颈,再按"改动小、风险低、见效快"排序:限流排队保证不崩,缓存和裁上下文降负载,调推理参数和扩实例提效率,最后才动模型(量化、蒸馏、换小模型)。每一步都用评测集守住效果。

细说:

步骤 做什么 为什么排在这
0. 定位 拆 trace:排队、TTFT、decode、工具耗时、重试各占多少;看瓶颈是 GPU 打满、API 的 RPM/TPM 限额,还是下游工具慢 不定位就优化,常常优化错地方
1. 保稳 限流、带截止时间的排队、超时、熔断、降级 先不崩,再谈快
2. 减负载 前缀缓存、结果缓存、合并重复请求、裁上下文、减少 Agent 步数、确定性部分改 workflow 不碰模型,效果风险最低
3. 提效率 自部署:调批处理参数、开 prefix caching、加副本;用 API:多 key、多区域、多供应商、预留吞吐 主要花的是钱和运维
4. 动模型 小模型路由、量化、蒸馏或微调小模型、投机解码 效果风险最高,必须过评测门槛

举个示意的定位结果:一次报销任务 P95 是 12 秒,拆开 trace 发现排队 4 秒、四次模型调用的 prefill 合计 3 秒、decode 2 秒、query_invoices 2.5 秒、其他 0.5 秒。这时候换小模型最多省 decode 那 2 秒的一部分;先解决排队(加副本或优先级队列)和前缀缓存(压 prefill),收益大得多。

估算并发用 Little 定律(在途数 = 到达率 × 平均停留时间),见下文"高并发"一题。

容易踩的坑:一上来就换小模型,成功率下降、重试增多,可能反而更贵更慢。GPU 扩容慢,加载权重往往要分钟级,不能指望临时加机器扛尖峰。优化工程也不只是加机器。

可能的追问:

  • 什么时候必须先动模型?单次推理本身就超出 SLO 的时候,例如长输入的 prefill 已经超过首 token 预算;或者单位成本远超预算。这时工程手段只能缓解,必须换更小的、量化过的或结构更省的模型。

放到报销案例里:月底报销高峰,先让交互请求优先、对账类批量任务延后,制度条款检索按制度版本缓存,再考虑把意图识别迁到更小的模型。

扩展:算清低秩更新与系统代价

前面 W′=W+BAW'=W+BA 是为了好懂写的简化版,假设矩阵是方的、也没有缩放。实际的线性层输入输出维度可以不同,LoRA 还会给增量乘一个系数:

W′=W0+αrBA,A∈Rr×din,B∈Rdout×rW'=W_0+\frac{\alpha}{r}BA,\qquad A\in\mathbb{R}^{r\times d_{in}},\quad B\in\mathbb{R}^{d_{out}\times r}
  • W0W_0:冻结的原权重,形状 dout×dind_{out}\times d_{in}。下标 0 表示"训练开始前的那份"。
  • dind_{in}、doutd_{out}:这一层的输入、输出维度。Llama-2-7B 的 q 投影从 4096 维到 4096 维,FFN 的 up 投影从 4096 维到 11008 维。
  • AA:把 dind_{in} 维输入压到 r 维;BB:再从 r 维放回 doutd_{out} 维。
  • α\alpha(alpha):人为设定的超参数;α/r\alpha/r 是真正乘到增量上的倍率。

可训练参数量是 r(din+dout)r(d_{in}+d_{out}),原矩阵是 dindoutd_{in}d_{out}。两者之比是

r(din+dout)din dout\frac{r(d_{in}+d_{out})}{d_{in}\,d_{out}}

代入 4096×4096、r = 8:分子 8 × (4096 + 4096) = 65,536,分母 4096 × 4096 = 16,777,216,比值 1/256 ≈ 0.39%。这是单个矩阵的比例,不是整模型显存减少的比例:还要计入基座权重、优化器状态和激活。参见 LoRA,§4.1。

再按整个 7B 模型算一次(教学算例,Llama-2-7B 结构,32 层,每层挂全部 7 个线性层,r = 16):

  • attention 的 q、k、v、o 都是 4096 维到 4096 维,每个 16 × 8192 = 131,072,四个共 524,288;
  • FFN 的 gate、up 是 4096 维到 11008 维,down 是 11008 维到 4096 维,每个 16 × (4096 + 11008) = 241,664,三个共 724,992;
  • 每层 1,249,280,乘 32 层约 4000 万(39,976,960),占 Llama-2-7B 约 67 亿参数的 0.6% 左右。

这 4000 万参数按每个 16 字节存权重、梯度和 Adam 状态,约 0.64 GB。下文 LoRA / QLoRA 一题里"几百 MB"就是这么来的。

再说 α/r\alpha/r 这个倍率。接着用第 3 节的 3×3 小例子:设 W0x=[1,0,1]⊤W_0x=[1,0,1]^\top(示意),前面已经算出 BAx=[1,2,0]⊤BAx=[1,2,0]^\top。

  • 取 r = 8、α = 16(真实 r 不是 1,这里只借用倍率),倍率 16 / 8 = 2,W′x=[1,0,1]⊤+2×[1,2,0]⊤=[3,4,1]⊤W'x=[1,0,1]^\top+2\times[1,2,0]^\top=[3,4,1]^\top;
  • 只把 r 改成 16、α 不动,倍率变成 1,同样的 BA 只贡献 [1,2,0]⊤[1,2,0]^\top,结果是 [2,2,1]⊤[2,2,1]^\top;
  • 想保持倍率 2,就要把 α 同步改成 32。

为什么要除以 r?BABA 可以看成 r 个秩 1 矩阵相加,r 翻倍,能叠上去的项也翻倍。除以 r 是为了让更新的量级不随 r 一起涨,改 r 时不用从头重调学习率。rsLoRA 的分析认为,r 大的时候除以 r 压得过头了,更新几乎学不动,所以改成除以 r\sqrt{r}:r = 64、α = 16 时,原版倍率 0.25,rsLoRA 是 16 / 8 = 2。

提高 r 会增加容量和训练开销,效果是变好还是过拟合,要用验证集判断。微调、RAG 和 Prompt 可以组合:它们分别影响行为、外部证据和当前任务的约束,组合后的效果要实测。

系统侧看 可观测性:追踪一条任务的代价:把重试算进实际调用成本,把首 token 时间和任务完成时间分开记,比较的是成功任务的成本。缓存命中、并行和量化是否划算,也要在任务质量不变的前提下判断。

逐题作答

模型选型

Qwen、Llama、DeepSeek、GPT、GLM 这些主流模型怎么对比?你们业务怎么选?(216)

简答:不背榜单。先用几道硬筛子缩小范围:能不能私有化、许可证允不允许、中文和工具调用够不够用、尺寸和手里的硬件匹不匹配;再用自己的评测集打分定版。这个领域的版本按月迭代,面试里讲选择框架就好,具体型号以官方最新发布为准。

细说:截至 2026 年各家族的大致定位如下,这不是排名,具体能力随版本变化:

家族 形态 常被提到的特点 选型时要核对
Qwen(阿里) 开放权重 + API 尺寸梯度全(从端侧小模型到大 MoE),中文强,微调与推理框架支持完善 不同型号许可证不同,逐个看
Llama(Meta) 开放权重 英文社区与工具链成熟,衍生模型多 自定义社区许可证有附加条款;中文和领域能力要实测
DeepSeek 开放权重 + API MoE + MLA 结构,推理模型路线,API 价格有竞争力 全量模型总参数大,私有化需要多卡;按总参数备显存
GPT(OpenAI) 以闭源 API 为主,另有开放权重型号 综合能力、工具调用、结构化输出成熟 数据合规与跨境、无法改权重、供应商绑定
GLM(智谱) 开放权重 + API 中文、Agent 与工具调用方向投入多 同样按具体版本评测

此外还有 Claude、Gemini 等闭源模型,选型逻辑一样。落地分六步:

  1. 硬约束过滤:数据能不能出域、是不是必须私有化、许可证、手里有几张什么卡。比如公司规定员工发票不能出境,境外 API 在这一步就被筛掉了。
  2. 能力初筛:公开榜单只当参考,要考虑数据污染,以及榜单题目和你的业务分布不一样。
  3. 自有评测集:按 Agent 的每一步分别测,只测最终回答会把问题藏起来。
  4. 成本与延迟压测:用真实的输入输出长度分布来压。
  5. 定版、灰度、留好回退。
  6. 定期复评:通过模型网关抽象接口,新模型出来能低成本地对比。

第 3 步的"按步骤测"可以是这样一张表(示意数字):

步骤 候选 A(小模型) 候选 B(强模型)
意图识别准确率 97% 98%
制度判断带正确引用 71% 93%
建单参数 Schema 合法率 99% 99%

这张表一出来,结论很自然:意图识别和建单参数用 A,制度判断用 B。只看最终回答的总分,你会得到一个"两者都差不多"的模糊结论。

容易踩的坑:榜单分高不代表在你的任务上好。"中文强"要拿领域术语和真实口语去测,比如"打车超了能报不""滴滴发票抬头写错了"。推理模型(长思考)效果好,但延迟高、思考 token 也计费,不适合每一步都用。只选一个模型,通常不如按步骤分层路由。

可能的追问:

  • 新模型发布了要不要换?有评测集就跑一遍,对比效果、每成功任务成本和 P95 延迟,过了门槛再灰度。没有评测集,换模型就是赌。
  • 为什么不直接选最强的?强模型单价高、延迟高,简单步骤用它是浪费。选型的目标是在效果门槛之上让成本最低,效果最高的那个未必是答案。

放到报销案例里:报销助手可以做成三档组合:小模型做意图识别和路由,强模型做制度判断和带引用的回答("根据《差旅费管理办法》2026 版第 4.2 条,市内交通标准 80 元,INV-102 超标 38.5 元……"),中档模型加结构化输出做建单参数抽取。

选型除了效果还看什么?效果、成本、延迟、上下文、工具调用、中文怎么权衡?(71)

简答:把六个维度都换成能验收的指标。先设硬门槛,不达标直接淘汰;再在剩下的候选里看"效果和成本"的取舍曲线。别用一个加权总分把短板掩盖掉。

细说:

维度 怎么量 门槛示意
效果 评测集任务成功率、引用正确率 不低于基线模型若干点
成本 每成功任务成本 低于业务可接受值
延迟 TTFT P95、端到端 P95 交互场景首 token 要短
上下文 有效长度(长文中间位置的检索与多跳测试),不看标称窗口 覆盖业务最长输入
工具调用 工具选择准确率、参数 Schema 合法率、多轮一致性 合法率接近 100%,否则重试成本高
中文 领域术语、口语、繁简、中英混写 业务样本上实测

为什么不用加权总分?举个示意的例子:候选 C 效果 95 分、工具 Schema 合法率只有 80%,加权后总分照样最高;可上线后每五次建单就有一次参数不合法要重试,成本和延迟都会被拖垮。所以合法率这种项要当门槛,不该当加分项。

成本一定要按成功的任务折算,因为便宜的模型失败、重试多:

csuccess=∑iciNsuccessc_{\text{success}}=\frac{\sum_i c_i}{N_{\text{success}}}
  • ∑ici\sum_i c_i:∑\sum 是求和号,读作"sigma"。cic_i 是第 i 个任务实际花的钱,失败的任务、重试的调用都算进去。
  • NsuccessN_{\text{success}}:成功完成的任务数。
  • 整个式子就是:总共花的钱 ÷ 真正办成的事。

示意算例,各跑 1000 个报销任务:

模型 每个任务平均花费 成功率 总花费 成功数 每成功任务成本
便宜模型 A 0.05 元 50% 50 元 500 0.100 元
贵模型 B 0.08 元 95% 80 元 950 0.084 元

A 的单价便宜 37.5%,但按办成一件事算反而贵了约 19%。而且失败的那 500 单多半要转人工,人工成本还没进这张表。

分母要和分子用同一批任务,"成功"的判定标准也要在不同模型之间一致,比如都按第 6 站的评测器判。分母很小的时候(成功只有个位数),这个数会剧烈波动,不能拿来做决策。

容易踩的坑:单价便宜不等于真便宜,成功率低、步数多、重试多都会把账做大。推理模型的思考 token 也计费。上下文窗口大不等于长文效果好,128k 窗口的模型可能在 30k 处就开始漏信息。延迟还受部署区域和供应商负载影响,要在目标环境测。

可能的追问:

  • 维度冲突时怎么拍板?先问业务哪个是硬约束(合规、实时性、预算上限)。其余的用每成功任务成本和 P95 延迟画一张散点图,把几个候选点交给业务方选。工程师不该替业务做这个取舍。

放到报销案例里:报销场景的硬门槛可以这样定(示意):建单参数 Schema 合法率 ≥ 99%,制度判断引用正确率 ≥ 90%,首 token P95 ≤ 1.5 秒。过了门槛的候选里,再挑每成功报销成本最低的那个。

开源还是闭源?自研底座还是接第三方 API?

简答:应用团队几乎都不该自研底座。开源和闭源的差别主要在三件事:数据合规、规模化以后的成本、可控性。常见的落地方式是闭源 API 打样,开源模型承接量大、稳定或敏感的场景,中间用模型网关解耦。

细说:

维度 第三方 API 自部署开源
起步成本 低,按量付费 高,GPU、运维、团队
边际成本 线性随用量增长 利用率高时很低,空闲时也在烧钱
数据 出域,依赖供应商合规条款 可完全私有
定制 Prompt、有限的托管微调 可改权重、量化、任意微调
能力上限 通常更高 视型号,差距在缩小
运维与可用性 供应商负责,但受限流与下线影响 自己负责扩缩容、故障

自部署是否划算,主要看利用率。每个 token 的成本可以这样估(示意公式):

cself=CGPU+CopsU⋅Tmax⁡c_{\text{self}}=\frac{C_{\text{GPU}}+C_{\text{ops}}}{U\cdot T_{\max}}
  • CGPUC_{\text{GPU}}:每月的 GPU 费用(租金或折旧),单位元 / 月。
  • CopsC_{\text{ops}}:每月的运维、电费、人力分摊,单位元 / 月。
  • Tmax⁡ T_{\max} :机器满载时每月能产出的 token 数,要在你自己的输入输出长度分布、并且满足延迟 SLO 的条件下压测出来。
  • U U :实际利用率,0 到 1 之间。
  • cselfc_{\text{self}}:摊到每个 token 上的成本,单位元 / token。

代入一组示意数字:GPU 每月 30,000 元,运维 10,000 元,合计 40,000 元;满载吞吐 5000 token/s,一个月 30 天共 2,592,000 秒,Tmax⁡=5000×2,592,000≈1.30×1010T_{\max}=5000\times2{,}592{,}000\approx1.30\times10^{10} token。

  • 利用率 60%:40,000 ÷ (0.6 × 1.296×10¹⁰) = 40,000 ÷ 7.78×10⁹ ≈ 5.1×10⁻⁶ 元 / token,也就是每百万 token 约 5.1 元;
  • 利用率 10%:40,000 ÷ 1.296×10⁹ ≈ 3.1×10⁻⁵ 元 / token,每百万 token 约 30.9 元。

假设同档 API 每百万 token 8 元(示意),盈亏平衡点是 U=40,000÷(8×10−6×1.296×1010)≈0.39U=40{,}000\div(8\times10^{-6}\times1.296\times10^{10})\approx0.39。利用率低于 39% 时,自部署每 token 反而更贵。白天忙、夜里闲的业务,日均利用率很容易掉到 20% 以下,"开源免费"的错觉就出在这里。这个式子也有简化:API 的输入、输出单价不同,要按你的输入输出比折算;Tmax⁡T_{\max} 必须在满足延迟 SLO 的 batch 下测,否则会高估。

自研底座(从头预训练)需要海量数据、算力和长期团队,只有把模型本身当战略的公司才做。应用团队能选的中间态有:领域继续预训练(少见)、微调开源模型、从强模型蒸馏小模型。

容易踩的坑:闭源有涨价、模型下线、限流的风险。开源要核对许可证,包括商用条件、用户规模上限、衍生模型的命名要求。私有化也不等于安全,权限和审计照样要做。

可能的追问:

  • 怎么避免被一家供应商绑定?统一模型网关接口,工具 Schema 用中性格式,Prompt 按模型版本管理,评测集常备。切换模型时按新模型重调 Prompt 并回归,别指望无缝替换。

放到报销案例里:报销涉及员工个人信息和发票。如果公司要求数据不出域,就选私有化开源模型或合规的境内云服务;如果允许,可以先用闭源 API 跑通评测,再把调用量最大的意图识别迁到自部署小模型,那一步每天几万次调用,最容易把利用率撑起来。

微调

LoRA / QLoRA 的原理是什么?r、alpha 怎么理解?显存是怎么省下来的?(165)

简答:LoRA 冻结基座,只在目标线性层旁边训练一对低秩矩阵;QLoRA 在此基础上把冻结的基座量化到 4-bit,再训练 LoRA。LoRA 省的是梯度和优化器状态,QLoRA 额外省了基座权重本身。为什么有效、r 怎么选,见上文常见追问。

细说:前向计算是

h=W0x+αrBAxh=W_0x+\frac{\alpha}{r}BAx
  • xx:这一层的输入向量,长度 dind_{in};hh:输出向量,长度 doutd_{out}。
  • W0xW_0x:原模型本来就会算的那部分,W0W_0 冻结、不求梯度。
  • αrBAx\frac{\alpha}{r}BAx:旁路。先用 A 把 x 压到 r 维,再用 B 放回来,乘上倍率 α/r\alpha/r 后加到主路上。只有 A、B 参与反向更新。

还是用前面那组 3 维小数字:W0x=[1,0,1]⊤W_0x=[1,0,1]^\top,BAx=[1,2,0]⊤BAx=[1,2,0]^\top,α = 16、r = 8,所以 h=[1,0,1]⊤+2×[1,2,0]⊤=[3,4,1]⊤h=[1,0,1]^\top+2\times[1,2,0]^\top=[3,4,1]^\top。训练刚开始时 B 全是 0,旁路贡献是 0,h=[1,0,1]⊤h=[1,0,1]^\top,和原模型一模一样;随着 B 被更新,旁路才慢慢把输出往任务需要的方向推。

显存账(7B 教学算例,未计激活):

方案 基座权重 梯度 + 优化器 量级
全参数(AdamW 混合精度) BF16 约 14 GB 梯度 2 + 主权重与两个动量 12,约 14 字节/参数,约 98 GB 百 GB 级,需多卡
LoRA(全线性层,r=16) BF16 约 14 GB,冻结 只为约四千万参数存,几百 MB 单张大显存卡可行
QLoRA NF4 约 4 GB 级 同 LoRA 消费级卡可行

QLoRA 那一行的 4 GB 是怎么来的?把它写成一个粗估式:

MQLoRA≈P⋅4+0.1278+PLoRA⋅16+MactM_{\text{QLoRA}}\approx P\cdot\frac{4+0.127}{8}+P_{\text{LoRA}}\cdot16+M_{\text{act}}
  • PP:基座参数量。每个参数用 4 bit 的 NF4 存,再加量化常数摊下来的 0.127 bit;除以 8 是把 bit 换成字节。
  • 0.127 的来历:每 64 个权重共用一个 FP32 缩放常数,摊到每个权重是 32 / 64 = 0.5 bit。双重量化把这些常数再压成 8 bit(每 256 个常数再共用一个 FP32),变成 8 / 64 + 32 / (64 × 256) ≈ 0.125 + 0.002 = 0.127 bit。
  • PLoRA⋅16P_{\text{LoRA}}\cdot16:LoRA 参数的权重、梯度和 Adam 两个动量,按每参数 16 字节算。
  • MactM_{\text{act}}:激活,取决于序列长度、batch,以及有没有开 gradient checkpointing。

代入 7B:

  • 基座:7×10⁹ × 4.127 / 8 ≈ 3.61×10⁹ 字节,约 3.6 GB;
  • LoRA:4000 万 × 16 字节 = 0.64 GB;
  • 合计约 4.3 GB,再加几 GB 激活(示意,序列越长越多)。

同样的式子代入 65B:65×10⁹ × 4.127 / 8 ≈ 33.5 GB,加上 LoRA 和激活,还在 48 GB 以内,这和 QLoRA 论文"单张 48GB 卡微调 65B"对得上。如果没有双重量化,常数要占 0.5 bit,65B 会多出约 3 GB。

激活这时往往成了最大的开销,所以 QLoRA 常配合 gradient checkpointing(用重算换显存)和较短的序列长度。

QLoRA 有三个关键设计(QLoRA 论文):NF4 数据类型(针对正态分布权重设计的 4-bit 格式);双重量化,也就是上面把量化常数本身再量化一次;分页优化器,显存尖峰时把优化器状态换页到 CPU 内存。计算时 4-bit 权重要先反量化到 BF16 再做矩阵乘,所以它省显存但不提速,通常还更慢。

容易踩的坑:QLoRA 的 adapter 是在量化基座上训出来的,合并回 FP16 基座后行为可能略有偏差,部署前要按实际的部署形态重测。"QLoRA 更快"是错的。LoRA 只省训练显存,推理显存不变,合并后和原模型一样大。

可能的追问:

  • 多个 LoRA 怎么部署?不合并,共享一份基座,按请求加载不同的 adapter。主流推理引擎都支持多 LoRA 服务,适合多租户或多任务;只有一个任务时就合并,省掉旁路开销。按上面的算例,r = 16 的一个 adapter 只有约 4000 万参数,BF16 存约 80 MB,一张卡上挂几十个也放得下。

放到报销案例里:假设要训一个专门抽建单参数的小模型,基座 7B,只有一张 24 GB 的卡。全量微调要 112 GB 起,直接排除;LoRA 要 14 GB 基座加激活,序列一长就紧;QLoRA 只要 4 GB 多加激活,开 gradient checkpointing 后剩下的十几 GB 足够留给激活。训完以后在 create_reimbursement_ticket 的参数合法率上,和第 6 站的基线对比。

SFT、RLHF、DPO、GRPO 是什么关系?什么时候用哪个?(192/115/97)

简答:它们都是后训练手段。SFT 用示范教"怎么答";RLHF 和 DPO 用偏好教"哪个答得更好";GRPO 是一种省掉价值模型的强化学习算法,特别适合答案能被自动判对错的推理任务。应用团队多数停在 SFT,遇到偏好类问题再加 DPO。

细说:

方法 数据 怎么训 成本 典型用途
SFT (指令, 理想回答) 最大似然,只对回答部分算 loss 低 格式、风格、工具调用模板
RLHF(PPO) 偏好对,先用来训奖励模型 训奖励模型,再用 PPO 优化策略并用 KL 约束不偏离参考模型;训练时策略、参考、奖励、价值四个模型都要在场 高,调参难 通用对齐
DPO (prompt, 好回答, 差回答) 直接在偏好对上优化,不训显式奖励模型、不在线采样 中 风格偏好、拒答、减少啰嗦
GRPO 题目 + 可自动判分的奖励 每题采样一组回答,用组内相对得分作优势,不要价值模型 中高 数学、代码等可验证推理

三种数据长什么样,用报销场景各举一条(每行一条样本):

{"sft":  {"prompt": "INV-102 打车 118.5 元能报吗?", "response": "根据第 4.2 条,市内交通标准 80 元,超标 38.5 元……"}}
{"dpo":  {"prompt": "没有发票能先建单吗?", "chosen": "需要先上传发票,我可以先帮你保存草稿。", "rejected": "可以,已为你建单 R-55821。"}}
{"grpo": {"prompt": "金额 118.5 元,标准 80 元,超标多少?", "reward": "答案等于 38.5 记 1,否则记 0"}}

DPO 的损失。大白话:对每一对偏好数据,看新模型相对参考模型"把好回答的概率抬高了多少、把差回答的概率压低了多少",两者之差越大,损失越小。

LDPO=−E[log⁡σ(βlog⁡πθ(yw∣x)πref(yw∣x)−βlog⁡πθ(yl∣x)πref(yl∣x))]\mathcal{L}_{\text{DPO}}=-\mathbb{E}\left[\log\sigma\left(\beta\log\frac{\pi_\theta(y_w\mid x)}{\pi_{\text{ref}}(y_w\mid x)}-\beta\log\frac{\pi_\theta(y_l\mid x)}{\pi_{\text{ref}}(y_l\mid x)}\right)\right]
符号 含义
xx prompt
ywy_w、yly_l 好回答(w 是 win)、差回答(l 是 lose)
πθ(y∣x)\pi_\theta(y\mid x) 正在训练的模型在给定 x 时生成 y 的概率。竖线读作"在……条件下",这是条件概率
πref(y∣x)\pi_{\text{ref}}(y\mid x) 参考模型(通常就是 SFT 后的模型,训练中冻结)给出的同一个概率
log⁡πθπref\log\frac{\pi_\theta}{\pi_{\text{ref}}} 对数比值:大于 0 表示新模型比参考模型更倾向于生成这个回答,小于 0 表示更不倾向。log 是自然对数
β\beta 温度系数,常见取 0.1 左右,控制"允许偏离参考模型多远"
σ(z)\sigma(z) sigmoid 函数 1/(1+e−z)1/(1+e^{-z}),把任意实数压到 0 和 1 之间,σ(0)=0.5\sigma(0)=0.5
E[⋅]\mathbb{E}[\cdot] 期望,这里就是对数据集里所有偏好对取平均
前面的负号 log σ 小于 0,加负号变成正的损失,越小越好

手算一对(示意概率,真实的整句概率是每个 token 概率连乘,小得多,代码里直接用对数概率相加)。取 β = 0.1:

  • 好回答:参考模型给 0.02,新模型给 0.04,比值 2,log⁡2≈0.693\log 2\approx0.693;
  • 差回答:参考模型给 0.03,新模型给 0.015,比值 0.5,log⁡0.5≈−0.693\log 0.5\approx-0.693;
  • 括号里:0.1 × (0.693 − (−0.693)) = 0.1 × 1.386 = 0.1386;
  • σ(0.1386)=1/(1+e−0.1386)=1/(1+0.8706)≈0.5346\sigma(0.1386)=1/(1+e^{-0.1386})=1/(1+0.8706)\approx0.5346;
  • 损失 =−log⁡0.5346≈0.626=-\log 0.5346\approx0.626。

训练刚开始时新模型就是参考模型,两个比值都是 1、对数都是 0,括号里是 0,σ(0)=0.5\sigma(0)=0.5,损失 =−log⁡0.5≈0.693=-\log0.5\approx0.693。上面那一步把损失从 0.693 降到了 0.626。

同样的概率换成 β = 0.5:括号里 0.5 × 1.386 = 0.693,σ(0.693)=1/(1+0.5)≈0.667\sigma(0.693)=1/(1+0.5)\approx0.667,损失 ≈ 0.405。β 越大,同样的偏离换来的损失下降越多,模型只要离参考模型一点点就能把损失压低,所以它也就不会跑太远。β 越小,模型要偏离得更多才能拿到同样的损失,约束更松。

这个损失只看"好回答的对数比值减去差回答的对数比值"这个差,所以会出一种怪事:新模型把好回答的概率从 0.02 降到 0.01(对数比值 −0.693),差回答从 0.03 降到 0.0015(对数比值 −3.0),括号里是 0.1 × (−0.693 + 3.0) ≈ 0.230,损失 ≈ 0.585,比起点还低。好回答的概率减半了,损失却说"进步了"。这就是后面坑里说的"好回答的概率也一起下降"。

PPO 的 clip。RLHF 里用 PPO 更新策略时,每一步不能改得太猛,PPO 的办法是把"新旧策略的概率比"限制在一个小区间里:

LCLIP(θ)=Et[min⁡(ρtA^t, clip⁡(ρt,1−ϵ,1+ϵ) A^t)],ρt=πθ(at∣st)πθold(at∣st)L^{\text{CLIP}}(\theta)=\mathbb{E}_t\Bigl[\min\bigl(\rho_t\hat{A}_t,\ \operatorname{clip}(\rho_t,1-\epsilon,1+\epsilon)\,\hat{A}_t\bigr)\Bigr],\qquad \rho_t=\frac{\pi_\theta(a_t\mid s_t)}{\pi_{\theta_{\text{old}}}(a_t\mid s_t)}
  • tt:第 t 个 token 位置;sts_t 是到这里为止的上下文,ata_t 是这一步生成的 token。
  • ρt\rho_t:新策略和采样时旧策略对同一个 token 的概率之比。等于 1 表示没变,1.5 表示新策略生成它的概率是原来的 1.5 倍。
  • A^t\hat{A}_t:优势,表示这个 token "比平均水平好多少"。正数表示该多做,负数表示该少做。PPO 里它由价值模型(critic)估计,拿实际回报减去价值模型预测的平均水平。
  • clip⁡(ρ,1−ϵ,1+ϵ)\operatorname{clip}(\rho,1-\epsilon,1+\epsilon):把 ρ 截在 [1−ϵ,1+ϵ][1-\epsilon,1+\epsilon] 之间,小于下界取下界,大于上界取上界。ε 常取 0.2,区间就是 [0.8, 1.2]。
  • min⁡(⋅,⋅)\min(\cdot,\cdot):两者取小。这个目标是要最大化的,代码里取负号当损失。

取 ε = 0.2,算三种情况:

情况 ρ Â ρ × Â clip 后 × Â 取 min 含义
好 token,已经抬得很高 1.5 +2 3.0 1.2 × 2 = 2.4 2.4 被截住,不再继续往上推
好 token,还没抬起来 0.9 +2 1.8 0.9 × 2 = 1.8 1.8 正常更新
坏 token,概率反而升高了 1.5 −2 −3.0 1.2 × (−2) = −2.4 −3.0 不截,全力把它压回去

第一行取到的是截断后的常数 2.4,对 θ 没有梯度,这一步就不会把这个 token 的概率推得更高;第三行取的是没截断的 −3.0,梯度照常,错误方向的改动会被纠正。min 的作用就是"好处封顶、坏处不打折"。不加 clip 的话,一批数据上就可能把策略改得面目全非,之后采样出来的数据分布也跟着崩。

GRPO 的优势。GRPO 沿用上面的 clip 目标,但把 A^\hat{A} 的来源换掉了:不训价值模型,对同一道题采样 G 个回答,用组内的相对得分当优势:

A^i=ri−mean⁡(r1,…,rG)std⁡(r1,…,rG)\hat{A}_i=\frac{r_i-\operatorname{mean}(r_1,\dots,r_G)}{\operatorname{std}(r_1,\dots,r_G)}
  • GG:每道题采样的回答数,比如 4 或 8。
  • rir_i:第 i 个回答的奖励,比如答对记 1、答错记 0。
  • mean、std:这 G 个奖励的平均值和标准差。
  • A^i\hat{A}_i:第 i 个回答的优势,这个回答里每个 token 都用同一个值。

拿"118.5 元、标准 80 元,超标多少"这道题采样 4 个回答:38.5、38、118.5、40,只有第一个对,奖励是 [1, 0, 0, 0]。

  • 平均值 = 0.25;
  • 偏差 = [0.75, −0.25, −0.25, −0.25],平方 = [0.5625, 0.0625, 0.0625, 0.0625],平均 = 0.75 / 4 = 0.1875;
  • 标准差 = √0.1875 ≈ 0.433(这里除以 G;有的实现除以 G − 1,结果是 0.5,数值会略有不同);
  • 优势 ≈ [0.75 / 0.433, −0.25 / 0.433, …] = [1.73, −0.58, −0.58, −0.58]。

四个里只有一个答对时,这个答对的回答拿到很大的正优势 1.73。反过来如果奖励是 [1, 1, 1, 0],优势就是 [0.58, 0.58, 0.58, −1.73],那个唯一答错的被重罚。四个全对或全错时,标准差是 0,实现里分母会加一个很小的数防止除零,优势全是 0,这道题对训练没有贡献。所以题目难度要配好:太简单或太难的题都白采样。

典型流水线是先预训练,再 SFT,然后做偏好对齐(RLHF 或 DPO),最后针对推理能力做强化学习。RLHF 三阶段见 InstructGPT,DPO 见 原论文,GRPO 最早在 DeepSeekMath 中提出,后来用于 DeepSeek-R1 的推理强化训练。

容易踩的坑:只做 SFT,模型只见过正例,学不会"不要这样答",还会照抄示范里的错误。DPO 对数据质量和 β 敏感,可能出现上面算过的"好回答概率也一起下降",以及输出越来越长等现象。强化学习有 reward hacking 的风险:GRPO 的效果上限就是 verifier 的可靠程度,比如奖励只检查"答案里有没有 38.5",模型可能学会把好几个数都写上。

可能的追问:

  • 为什么 SFT 之后还要 RLHF?只用 SFT 行不行?SFT 只告诉模型一个好答案长什么样;偏好训练利用"A 比 B 好"这种比写示范便宜得多的信号,还能压制具体的坏行为。简单的格式类任务只做 SFT 往往就够。
  • DPO 相比 PPO 改进了什么?它把"KL 约束下的最优策略"和奖励之间的解析关系代入目标,消掉了显式的奖励模型,变成对偏好对的分类式损失:离线、稳定、省显存。代价是只用离线数据,没有在线探索。

放到报销案例里:报销场景里,SFT 用来教工单 JSON 格式和"缺发票时先追问再建单"。如果评测发现模型在证据不足时爱下结论,就用 DPO 偏好对调偏好,让"带引用的拒答"胜过"臆断",上面 JSON 里的 dpo 那条就是一例。GRPO 一般用不上,除非有能自动判分的计算类子任务,比如超标金额计算。

微调数据怎么构造、清洗、配比?(82)

简答:微调能做到什么程度,主要看数据。先定义"要模型学会的行为"并留出评测集;再从真实日志、专家撰写、强模型蒸馏三路取数据,经过去重、校验、脱敏、质检和防泄漏;最后按"任务数据 + 通用数据 + 负例与边界例"配比,用错例分析驱动迭代。

细说:

  1. 先定目标与评测。最先划出 held-out 测试集并冻结,后续所有数据都要和它去重。
  2. 数据来源有四类:
    • 线上日志:分布最真实,需要授权和脱敏;
    • 专家撰写:质量高、成本高,适合覆盖难例;
    • 强模型合成或蒸馏:规模大,但要确认服务条款允不允许用它的输出训练模型;
    • 拒绝采样:一条输入生成多个回答,用规则或 verifier 只留正确的。
  3. 清洗:精确去重加近似去重(MinHash 或向量相似度);格式校验(JSON 能解析,工具参数能通过 Schema);质量打分(规则 + LLM 评分 + 人工抽检);PII 脱敏;和评测集做 n-gram 或语义去污染。
  4. 配比:业务任务数据为主,混入一定比例的通用指令数据防止遗忘;难例、边界例上采样;必须有负例,比如该拒答的、该追问的、工具报错后怎么恢复的;多轮对话和工具轨迹要保持完整,别只截最后一轮。
  5. 格式对齐:训练时的 chat template、工具描述格式要和推理时完全一致;只对 assistant 部分计算 loss。
  6. 迭代:训练、评测、错例归类、针对性补数据,一味加量没用。

举个清洗前后的对比(示意)。原始日志里有这样一条:

改之前:用户 张三 13812345678:打车 118.5 能报吗
        助手:可以报,标准是 100 元。
改之后:用户 [姓名] [手机号]:打车 118.5 能报吗
        助手:根据第 4.2 条,市内交通标准 80 元,INV-102 超标 38.5 元,需要例外审批。

这一条要同时做三件事:脱敏,改正旧版制度的错误金额,再把回答改成带依据的格式。如果不改,模型会把"100 元"当成正确答案学进去。

配比也可以给个起点(示意,要靠消融调):业务任务 70%,通用指令 20%,负例和边界例 10%。

质量往往比数量重要:LIMA 用约一千条精选样本,就让强基座获得了不错的指令遵循能力。

容易踩的坑:合成数据同质化,会把教师模型的错误放大。配比没有通用公式,只能做消融。只有成功轨迹的话,模型就学不会出错后的恢复。数据里混进过时内容(比如旧版制度金额),微调后就固化成了错误。

可能的追问:

  • 数据要多少才够?看任务。格式和风格类,几百到几千条高质量样本常能见效;复杂能力需要更多。用学习曲线判断:拿一半数据和全部数据各训一次,提升还明显就继续加。比如 1000 条时合法率 92%,2000 条时 97%,那就值得再补;2000 到 4000 只涨到 97.5%,再加就不划算了(示意)。
  • 怎么防灾难性遗忘?混入通用数据,LoRA 配低学习率、少轮次,并且在通用基准和业务集上同时回归。

放到报销案例里:建单参数抽取的数据,从历史工单脱敏构造,必须覆盖缺字段、金额和发票不一致、重复发票等样本。具体报销额度属于制度,不进训练数据,由 RAG 提供。

微调、Prompt、RAG 怎么选?(21)

简答:按"缺的是什么"来选。缺当前任务的约束,用 Prompt;缺外部的、会变的知识,用 RAG;缺稳定的行为模式、格式或领域表达能力,才微调。三者经常叠加,一般先试 Prompt,再上 RAG,最后才微调。

细说:

症状 首选 原因
不知道私有或最新事实 RAG 可更新、可删除、可引用
需要可追溯的出处 RAG 微调没有出处
需要按用户权限给不同答案 RAG(检索时过滤) 微调无法做权限隔离
格式不稳 Prompt + 结构化输出,仍不稳再 SFT 先用便宜手段
风格、语气、术语用法 few-shot,不够再微调 行为类问题适合微调
大模型 + 长 Prompt 太贵太慢 微调小模型,把长指令学进权重 降本降延迟

最后一行可以算一笔账(示意):系统提示里有 3000 token 的建单规则和 few-shot,每天调用 2 万次,就是每天 6000 万输入 token。把这些规则用 SFT 内化到小模型以后,提示缩到 500 token,每天省下 5000 万 token。

判断顺序可以写成:

if 缺的是事实/知识:      RAG(必要时配合长上下文)
elif 缺的是本轮约束:     Prompt / few-shot / 结构化输出
elif 行为在大量样本上仍不稳 or 成本延迟压力大:  微调

容易踩的坑:用微调灌知识,难更新、难删除、无法溯源,还容易一本正经地编。用 RAG 修行为,检索再准,模型也不会因此学会某种格式。"长上下文能替代 RAG"也不成立:成本、长文中间的信息丢失、权限过滤都是问题。

可能的追问:

  • RAG 和微调能一起用吗?能。典型做法是微调模型"如何使用检索到的证据"(按格式引用、证据不足时拒答),知识本身仍由 RAG 提供,RAFT 就是这个思路。

放到报销案例里:报销制度条款走 RAG;"先查制度、再查发票、再建单"的流程由 Agent 编排和 Prompt 约束;工单 JSON 先靠结构化输出,仍然不稳再考虑微调。

推理部署

KV Cache 为什么能加速?显存怎么算?(64)

简答:自回归生成每一步只新增一个 token,前面 token 的 K、V 不会再变。把它们缓存下来,每一步就只需算新 token 的 Q、K、V,再和历史做注意力,不用把整段前缀重算一遍。代价是显存随层数、KV 头数、上下文长度和并发线性增长。

细说:

先看没有缓存时:生成第 t 个 token,要把前 t 个 token 重新过一遍所有层来得到 K、V。生成 n 个 token,投影的计算量是 1 + 2 + … + n,随 n 平方增长。比如 prompt 有 1000 个 token、要生成 100 个,没有缓存时第 100 步要重算 1099 个 token 的投影,有缓存只算 1 个。

有缓存时:每一步只对新 token 做投影,K、V 追加进缓存。注意力仍然要读全部历史的 KV,所以 decode 的瓶颈从算力变成了"读 KV 和权重的显存带宽"。

KV 占多少显存:

MKV=2×L×nkv×dhead×nctx×b×bytesM_{KV}=2\times L\times n_{kv}\times d_{head}\times n_{ctx}\times b\times \text{bytes}
符号 含义 Llama-2-7B 取值
2 K 和 V 各存一份 2
LL 层数 32
nkvn_{kv} KV 头数(MHA 等于注意力头数,GQA 更少) 32
dheadd_{head} 每个头的维度 128
nctxn_{ctx} 上下文长度,单位 token(输入加已生成) 8192
bb 同时在跑的请求数 1
bytes 每个数占几个字节,FP16 是 2 2

代入:2 × 32 × 32 × 128 = 262,144 个数每 token,× 2 字节 = 524,288 字节,即 0.5 MiB 每 token;× 8192 token = 4 GiB 一条请求。并发 8 条就是 32 GiB,已经比 14 GB 的权重大一倍多。

不同结构的教学算例(FP16,单条 8k 上下文):

结构 层数 / KV 头 / head_dim 每 token 8k 一条
Llama-2-7B 类(MHA) 32 / 32 / 128 约 0.5 MB 约 4 GB
Llama-3-8B 类(GQA) 32 / 8 / 128 约 128 KB 约 1 GB
Llama-2-70B 类(GQA) 80 / 8 / 128 约 320 KB 约 2.5 GB

GQA 那两行就是把 nkvn_{kv} 从 32 改成 8,KV 直接变成四分之一。这个公式是线性的:任何一项翻倍,KV 就翻倍,所以长上下文和高并发叠在一起时最先爆的往往是 KV。公式没算的部分有:PagedAttention 每个序列最后一块的空位、MLA 这类压缩结构(要按潜向量维度重算),以及滑动窗口注意力(nctxn_{ctx} 封顶在窗口大小)。

减 KV 的手段:MQA / GQA 减少 KV 头;MLA(DeepSeek-V2 起)缓存低维潜向量;KV 量化到 FP8 / INT8,bytes 从 2 变成 1,直接减半;滑动窗口注意力;跨请求共享前缀;把冷的 KV 卸载到 CPU 内存或 SSD。

容易踩的坑:长上下文、高并发时 KV 会超过权重本身。KV 量化可能影响长文精度。"KV Cache 让每步计算量变成常数"不准确:投影是常数,注意力读取的历史仍随长度增长。整体显存预算见上文常见追问。

可能的追问:

  • KV Cache 和 API 的"提示缓存"是什么关系?前者是单个请求内部复用已生成 token 的 K、V;后者是跨请求复用相同前缀已经算好的 KV,省掉重复的 prefill。多数主流 API 对命中缓存的输入按折扣计费,规则各家不同,以官方文档为准。

放到报销案例里:报销助手每次调用大约 6000 token 输入。用 Llama-3-8B 类结构,每条约 6000 × 128 KB ≈ 0.73 GiB;如果卡上留了 40 GB(约 37 GiB)给 KV,大约能同时跑 50 路。换成 MHA 结构的 7B,每条约 2.9 GiB,只能跑 12 路左右。月底高峰时,这个数直接决定要几张卡。

vLLM 的 PagedAttention 和 Continuous Batching 具体怎么工作?(51)

简答:PagedAttention 借鉴操作系统的分页:把每个请求的 KV 切成固定大小的块,用块表映射到不连续的物理显存。Continuous Batching 在每个解码步重新组批,完成的请求立刻退出,新请求立刻加入。前者让更多请求放得下,后者让 GPU 一直有活干。为什么因此变快,见上文常见追问。

细说:

PagedAttention 的做法:

  • 每个块存固定数量 token 的 KV,vLLM 默认 16 个 token 一块;
  • 每个序列维护一张块表,记录每个逻辑块号对应哪个物理块号,生成到哪分配到哪;
  • 注意力 kernel 按块表去不连续的物理块里取 KV;
  • 多个输出共享同一段 prompt 时,物理块用引用计数共享,谁要改写谁复制(写时复制);
  • 块池耗尽时调度器抢占部分请求:把它们的块换出到 CPU,或者直接释放,之后重算。

看一个具体的块表。一个请求已经有 40 个 token,块大小 16,需要 3 个块(16 + 16 + 8),最后一块空着 8 个位置:

序列 A 的块表          物理显存
逻辑块 0  →  物理块 7   [A 的 token 0-15]
逻辑块 1  →  物理块 2   [A 的 token 16-31]
逻辑块 2  →  物理块 11  [A 的 token 32-39,空 8 格]

物理块 7、2、11 不需要挨着。等 A 生成到第 48 个 token,再从空闲池里随便拿一块挂到逻辑块 3 上。按老办法,要按最大长度(比如 2048)一次性预留连续空间,A 只用了 40 个位置,剩下两千多个都闲着。

Continuous Batching(迭代级调度,最早见 Orca 的工作):

while serving:
    batch = running + admit(waiting, free_blocks)   # 有空块就接新请求
    step(batch)                                     # 整批前向一步
    for seq in batch:
        if seq.finished:
            free(seq.blocks); respond(seq)          # 立即让位

对比静态批:整批要等最长的那条生成完,短请求白等,新请求只能排队,还得按最长长度预留 KV。举个示意的例子:一批 4 条,要生成的长度分别是 20、30、40、500 个 token。静态批要跑 500 步,前三条早就结束了,它们占着的位置空转了四百多步;连续批处理下,第 20 步那条一结束,空出来的位置马上给排队的新请求。

Chunked prefill:把长 prompt 切成多段,和其他请求的 decode 混在同一步里调度,免得一个超长输入卡住所有人的吐字。

容易踩的坑:块太小,块表和 kernel 的开销增加;块太大,最后一块的浪费增加。最大并发序列数和每步最大 token 数是调吞吐与延迟的旋钮。抢占后重算浪费算力,换出则占 PCIe 带宽。

可能的追问:

  • 为什么不直接把静态 batch 开大?请求长度差异大,静态批的 padding 和等待浪费很严重,而且必须按最长序列预留显存。只有按步动态进出,才能真正把 GPU 吃满。

放到报销案例里:报销助手的请求长短差异很大:意图识别只输出几个 token,带引用的制度解释可能要两三百个。用静态批的话,意图识别请求要陪着长回答一起等;连续批处理下它几步就退出,把位置让给下一个员工。

量化怎么做?INT8 / INT4、AWQ / GPTQ 有什么区别?精度损失怎么控制?(23)

简答:量化就是用更少的比特表示权重(有时也包括激活和 KV),拿精度换显存和带宽。部署中最常见的是只量化权重的 W8 / W4。GPTQ 和 AWQ 都是训练后的权重量化,区别在于用什么信息来决定怎么量化误差最小。

细说:基本操作是按缩放因子 s 和零点 z 把浮点数映射到整数,用的时候再反量化回来:

q=clip⁡(round⁡(xs)+z,  qmin⁡,  qmax⁡),x^=s (q−z)q=\operatorname{clip}\left(\operatorname{round}\left(\frac{x}{s}\right)+z,\;q_{\min},\;q_{\max}\right),\qquad \hat{x}=s\,(q-z)
符号 含义
xx 原始浮点权重
ss 缩放因子,一个整数台阶对应多大的浮点值
zz 零点,让浮点的 0 落在某个整数上,用于处理不对称的分布
round 四舍五入到最近的整数
clip(v, a, b) 小于 a 取 a,大于 b 取 b,保证结果在整数能表示的范围内
qmin⁡q_{\min}、qmax⁡q_{\max} 整数范围,无符号 4-bit 是 0 到 15,有符号 INT8 是 −128 到 127
qq 存下来的整数
x^\hat{x} 反量化后的近似值,读作"x hat",误差就是 x^−x\hat{x}-x

s 和 z 一般按这一组权重的最大最小值来定:

s=xmax⁡−xmin⁡qmax⁡−qmin⁡,z=round⁡(−xmin⁡s)s=\frac{x_{\max}-x_{\min}}{q_{\max}-q_{\min}},\qquad z=\operatorname{round}\left(-\frac{x_{\min}}{s}\right)

手算一组 4 个权重 [−0.8, −0.1, 0.35, 1.0],量化到无符号 4-bit(0 到 15):s = (1.0 − (−0.8)) / 15 = 1.8 / 15 = 0.12,z = round(0.8 / 0.12) = round(6.67) = 7。

x x / s round + z 得 q x̂ = 0.12 × (q − 7) 误差
−0.8 −6.67 −7 0 −0.84 −0.04
−0.1 −0.83 −1 6 −0.12 −0.02
0.35 2.92 3 10 0.36 +0.01
1.0 8.33 8 15 0.96 −0.04

误差都在 ±0.06(半个台阶 s / 2)以内,16 个整数台阶覆盖这 4 个数绰绰有余。

现在把最后一个权重换成离群值 10.0:s = (10.0 + 0.8) / 15 = 0.72,z = round(0.8 / 0.72) = round(1.11) = 1。−0.1 变成 round(−0.14) + 1 = 1,0.35 变成 round(0.49) + 1 = 1,反量化后两个都是 0.72 × (1 − 1) = 0。原来一个负一个正的两个权重,量化后变成了同一个 0,信息直接丢了。一个离群值把台阶拉宽了 6 倍,其他所有权重都跟着变粗。

这个例子能解释后面几种方案为什么这么设计:分组量化(每 64 或 128 个权重一组,各算各的 s 和 z)把离群值的影响限制在组内;AWQ 找出对激活影响大的通道单独保护;SmoothQuant 把激活里的离群值转移到权重上。分组也有代价,每组要额外存 s 和 z:组大小 128、两个 FP16 常数,摊到每个权重是 32 / 128 = 0.25 bit,INT4 实际是 4.25 bit 左右,7B 权重约 7×10⁹ × 4.25 / 8 ≈ 3.7 GB,比 3.5 GB 略多(示意,具体看格式)。

粒度可以是整个张量、每个输出通道,或者每组若干个权重:组越小越准,元数据也越多。常见方案:

方案 量化对象 核心思路 适用
W8A8(如 SmoothQuant) 权重 + 激活 激活有离群值难量化,按通道把量化难度转移到权重上,可用 INT8 矩阵乘 高吞吐服务
FP8 权重 + 激活(可含 KV) 新一代 GPU 原生支持,精度损失通常较小 硬件支持时首选之一
GPTQ(W4A16) 权重 逐层量化,用近似二阶信息把已量化权重的误差补偿到剩余权重,需少量校准数据 显存紧张的部署
AWQ(W4A16) 权重 根据激活幅度找出少量关键权重通道,用缩放保护它们,无需反向传播 同上,泛化通常稳
GGUF 等 权重 多种比特格式,面向 CPU / 端侧 本地与边缘设备

W4A16 为什么能提速:decode 受带宽限制,读的字节少了,速度就上去了。但计算前要反量化,batch 很大、转为算力受限时,收益会变小。

控制精度损失的办法:校准数据贴近业务分布;embedding、输出层等敏感层保留高精度;先试 8-bit 再试 4-bit;在业务评测集上对比,尤其是长文、数学、代码和工具 JSON 这类对精度敏感的任务。

容易踩的坑:困惑度差不多,不代表业务任务没掉点。小模型比大模型对量化更敏感。没有对应 kernel 的硬件上,量化可能只省显存、不提速。

可能的追问:

  • 量化后效果掉了怎么补?先换更细的分组或更高的比特,换贴近业务的校准集,对关键层用混合精度;还不够,再做量化感知训练,或者在量化基座上训一个 LoRA 做补偿。

放到报销案例里:建单参数里的金额必须精确。量化后要专门测 create_reimbursement_ticket 的 amount 字段,看 118.5 会不会被写成 118 或 118.50 以外的值,只看通用问答的分数发现不了这类退化。

Prefill 和 Decode 有什么区别?首 token 延迟和吞吐怎么优化?

简答:Prefill 一次并行处理整段输入,属于算力受限,决定首 token 延迟(TTFT,Time To First Token)。Decode 逐个生成 token,每一步都要读一遍权重和 KV,属于带宽受限,决定每 token 延迟(TPOT,Time Per Output Token)和吞吐。两个阶段瓶颈不同,优化手段也不同。

细说:

阶段 计算特征 对应指标 主要手段
Prefill 大矩阵乘,算力受限 TTFT 缩短输入、前缀缓存、chunked prefill、FlashAttention、张量并行
Decode 矩阵乘向量,带宽受限 TPOT、吞吐 连续批处理加大 batch、权重与 KV 量化、GQA / MLA、投机解码

端到端时间可以粗拆为:

Te2e≈Tqueue+Tprefill+Nout⋅TtokenT_{\text{e2e}}\approx T_{\text{queue}}+T_{\text{prefill}}+N_{\text{out}}\cdot T_{\text{token}}
  • TqueueT_{\text{queue}}:在服务端排队等待的时间。
  • TprefillT_{\text{prefill}}:处理完整段输入的时间。用户看到的 TTFT 约等于 Tqueue+TprefillT_{\text{queue}}+T_{\text{prefill}}。
  • NoutN_{\text{out}}:输出 token 数。
  • TtokenT_{\text{token}}:每生成一个 token 的时间,就是 TPOT。压测工具里常按 (Te2e−TTFT)/(Nout−1)(T_{\text{e2e}}-\text{TTFT})/(N_{\text{out}}-1) 来算,因为第一个 token 已经算在 TTFT 里了。

代入一次报销助手的模型调用(示意):排队 0.1 秒,prefill 0.4 秒,所以 TTFT = 0.5 秒;输出是一次工具调用,60 个 token,TPOT 30 毫秒,decode 用 60 × 0.03 = 1.8 秒;合计约 2.3 秒。一次报销任务要四次模型调用(路由、制度判断、抽参数、最终回复),就是 9.2 秒左右,再加上 search_policy、query_invoices、create_reimbursement_ticket 三次工具调用。式子没算网络和工具耗时,也没算多轮之间的串行等待,Agent 的端到端时间要按 trace 把每一段加起来。

decode 每步至少要把权重和这批请求的 KV 从显存读一遍,所以有一个下界:

Ttoken≳Mw+b⋅MKVreqBW,吞吐≈bTtokenT_{\text{token}}\gtrsim\frac{M_{\text{w}}+b\cdot M_{\text{KV}}^{\text{req}}}{BW},\qquad \text{吞吐}\approx\frac{b}{T_{\text{token}}}
  • MwM_{\text{w}}:权重字节数;MKVreqM_{\text{KV}}^{\text{req}}:一条请求当前的 KV 字节数;bb:batch 里的请求数。
  • BWBW:显存带宽,单位字节每秒。
  • ≳\gtrsim 读作"大于约等于":实际时间只会比这个下界长,因为 kernel 效率达不到 100%。
  • 吞吐:整张卡每秒产出的 token 数。

教学算例(理论上限,示意):7B FP16 权重 14 GB,每条请求当前 2048 token 的 KV 约 1.07 GB(2048 × 0.5 MiB),带宽假设 1 TB/s。

batch 每步要读 TPOT 下界 吞吐上限
1 14 + 1.07 = 15.1 GB 15.1 ms 1 / 0.0151 ≈ 66 token/s
8 14 + 8.6 = 22.6 GB 22.6 ms 8 / 0.0226 ≈ 354 token/s
32 14 + 34.4 = 48.4 GB 48.4 ms 32 / 0.0484 ≈ 661 token/s

batch 从 1 涨到 32,吞吐涨了 10 倍,每个用户感受到的 TPOT 却慢了 3 倍多,吞吐和延迟天然矛盾,指的就是这个。同样 batch 32 换成 GQA 结构(KV 变成四分之一),每步读 14 + 8.6 = 22.6 GB,吞吐上限升到约 1416 token/s。只看权重时,batch = 1 的上限是 1 TB/s ÷ 14 GB ≈ 70 token/s;量化到 INT4 只读约 3.5 GB,上限约提高到 4 倍。实际受 kernel 效率等影响,达不到这些上限。

两个进阶手段:

  • PD 分离:prefill 和 decode 放在不同的 GPU 池里,各自扩缩容,避免长输入的 prefill 打断别人的吐字。代价是 KV 要跨机传输。
  • 投机解码:小模型先草拟几个 token,大模型一次并行验证。按标准的接受规则,输出分布和大模型自己生成的一致;草稿的接受率越高,越快。

容易踩的坑:吞吐和延迟天然矛盾,batch 越大吞吐越高、TPOT 越差,上面那张表就是。Agent 的每一步往往"输入很长、输出很短",一次工具调用只有几十个 token,prefill 占大头,所以前缀缓存比 decode 优化更值钱。

可能的追问:

  • 用户说"慢",先看哪个指标?先分清是首字慢、吐字慢,还是步骤多。首字慢查排队和 prefill(输入太长、没命中缓存);吐字慢查 decode 的并发和带宽;整个任务慢,多半是 Agent 步数和工具耗时。

放到报销案例里:报销助手每一步都带着系统提示、工具定义和证据,输出却只是一次工具调用。把稳定内容固定在前缀、让前缀缓存命中,是降首 token 延迟最直接的一招:上面算例里 0.4 秒的 prefill,如果 6000 token 里有 5000 token 命中缓存,只需处理剩下的 1000 token,粗略估计能降到 0.1 秒左右(示意)。

成本与并发

Agent 怎么降本?模型侧和工程侧各有哪些手段?(35)

简答:先把口径定成"每次成功任务的成本",再从两侧下手。工程侧减少调用次数和 token,最便宜,先做;模型侧让每个 token 更便宜,靠路由、小模型、量化、蒸馏。每个动作都要在评测集上证明效果没掉。

细说:

侧 手段 原理 风险
工程 少调用 确定性步骤改成 workflow 或规则,独立步骤合并或并行,设步数上限 灵活性下降
工程 少 token 见下文"token 优化" 压缩丢信息
工程 缓存 前缀缓存、只读工具结果缓存、语义缓存 语义缓存误命中
工程 离线批处理 非实时任务走批量接口,不少供应商对此有折扣 延迟变长
工程 少重试 结构化输出、参数校验前置、失败快速终止 基本没有
模型 路由 / 级联 先给小模型,置信度低或校验失败再升级 路由误判
模型 小模型 + 微调 / 蒸馏 把大模型在特定任务上的行为迁到小模型 训练与维护成本
模型 量化、推理优化 自部署时降每 token 成本 精度损失

级联是最常问的一种。做法是每个请求先给小模型,小模型没把握或输出没过校验,再升级给大模型。平均下来一个请求花多少钱:

E[c]=csmall+pesc⋅clarge\mathbb{E}[c]=c_{\text{small}}+p_{\text{esc}}\cdot c_{\text{large}}
  • E[c]\mathbb{E}[c]:每个请求的期望成本,就是很多请求平均下来的成本(E\mathbb{E} 同前文 DPO 处的期望)。
  • csmallc_{\text{small}}:小模型调用一次的成本。每个请求都要先过小模型,所以这一项总是全额计入。
  • clargec_{\text{large}}:大模型调用一次的成本。
  • pescp_{\text{esc}}:升级率,即被转给大模型的请求比例,取 0 到 1。

教学算例(示意):小模型每次 0.1 单位、大模型 1 单位、升级率 30%,期望成本 0.1 + 0.3 × 1 = 0.4。用 10 个请求核对一下:10 个都过小模型花 1.0,其中 3 个再过大模型花 3.0,合计 4.0,平均 0.4。全用大模型是 1.0,所以省了六成。

反过来算临界点:令 csmall+pesc clarge=clargec_{\text{small}}+p_{\text{esc}}\,c_{\text{large}}=c_{\text{large}},得 pesc=1−csmall/clarge=0.9p_{\text{esc}}=1-c_{\text{small}}/c_{\text{large}}=0.9。升级率超过 90%,级联反而比直接用大模型贵,比如 95% 时是 0.1 + 0.95 = 1.05。式子还没算两件事:小模型答错却没被拦下的请求(成功率下降),以及误判导致的失败重试。所以最终还是要换算成每成功任务成本来比。

容易踩的坑:只比较单价、不看成功率。语义缓存把依赖用户身份、权限或时间的答案共享给了别人,这是事故。多个降本动作叠在一起后效果下滑,单个看都没问题,所以要整体回归。

可能的追问:

  • 降本第一步做什么?先用 trace 做成本归因,按步骤、模型、工具、是否重试拆开。常见的情况是大头在重复携带的上下文和少数失败的长尾任务,先砍它们。

放到报销案例里:"能不能报"的结论不能按问题文本缓存,因为它依赖个人发票和职级:同样问"打车 118.5 元能报吗",P6 在上海超标 38.5 元,换一个职级或城市答案就可能不同。制度条款的检索结果则可以按"制度版本 + 查询"缓存,第 4.2 条在 2026 版生效期间不会变。

高并发下怎么保证 Agent 服务稳定?限流、排队、熔断、缓存、多实例怎么配合?(30)

简答:沿请求的调用链路分层设防。入口限流挡住超额流量,队列削峰并给每个请求带截止时间,下游出故障时靠熔断快速失败,降级给出能接受的次优结果,服务做成无状态以便水平扩展,缓存用来减少重复负载。

细说:

  1. 入口限流:按用户、租户、API Key 用令牌桶限流。LLM 场景要同时按请求数和 token 数限,供应商的配额也是按 RPM / TPM 给的。超限返回 429,并带上重试时间。
  2. 排队:用优先级队列,交互请求排在批量任务前面。每个请求带截止时间,过期直接丢弃。队列满了就拒绝新请求(背压),不要无限堆积。
  3. 重试:指数退避加随机抖动,只重试幂等操作。建工单这类写操作靠幂等键防重复,见 可靠执行:状态、幂等与恢复。
  4. 熔断:对每个下游(模型供应商、发票接口)维护"关闭、打开、半开"三种状态。失败率超过阈值就打开,后续请求直接快速失败;过一段时间进入半开,放少量探测请求,成功了再关闭。
  5. 降级:顺序是备用模型或备用供应商、小模型、缓存结果、只返回检索到的条款原文、转人工。写操作不能降级成"猜着建"。
  6. 隔离:舱壁模式,不同租户、不同任务类型用独立的资源池和连接池,一个慢工具拖不垮全部。
  7. 多实例:Agent 服务本身无状态,会话和任务状态放外部存储,长任务可以断点恢复。GPU 推理扩容慢,要保留热备实例,按队列长度提前扩。

连接池、并发配额要配多大,先估算同时在途的任务数。排队论里的 Little 定律给了一个很简单的关系:

L=λ⋅WL=\lambda\cdot W
  • LL:系统里同时在途的任务数(平均值)。
  • λ\lambda:到达率,读作"lambda",单位是任务每秒。
  • WW:一个任务从进来到离开的平均停留时间,单位秒。

教学算例(示意):峰值每秒来 20 个任务,每个任务平均停留 15 秒,那么同时在途 L = 20 × 15 = 300 个。连接池、并发配额和内存要按这个数再乘一个安全系数,比如 1.5 倍,就是 450。

这个式子能说明慢下游为什么危险。到达率不变,如果发票接口变慢,让 W 从 15 秒涨到 30 秒,在途任务就变成 600 个,是原来的两倍;在途多了,排队更长,W 再变大,形成恶性循环。熔断和截止时间就是用来切断这个循环的。使用时注意两点:λ 和 W 的时间单位要一致(每分钟的到达率配秒级的停留时间会差 60 倍);它描述的是稳态平均值,突发尖峰要另外留余量。

容易踩的坑:重试风暴,所有客户端同时重试,会把一次小故障放大成雪崩。熔断阈值太敏感会误伤正常请求。降级给出的答案要明确告诉用户。流式输出的长连接会一直占着连接数,要单独规划。

可能的追问:

  • 模型供应商限流了怎么办?通过模型网关在多个 key、多个区域、多个供应商之间负载均衡;客户端按 token 预算先限流,免得撞上供应商的墙;非实时任务转异步;关键路径准备一个提前评测过的备用模型。

放到报销案例里:月底报销截止日流量尖峰时,交互请求优先,批量对账延后。发票接口熔断时,助手回复"发票系统暂不可用,已保存草稿,恢复后提交",不去建一张缺字段的工单。

token 怎么优化?上下文压缩、工具裁剪、缓存命中分别怎么做?(29)

简答:Agent 的 token 大头通常在每轮重复携带的系统提示、工具定义、历史和工具结果上,用户那句话占不了多少。优化就是三件事:少带、带短、带得能命中缓存。

细说:

第一件是上下文压缩,详见 Prompt / Context。常用的做法有滑动窗口加历史摘要;已完成的子任务只留结论;检索证据只带 top-k 并去重;长期记忆按需检索,不全量注入。

第二件是工具裁剪。每个工具的名称、描述和参数 Schema 都要占 token。假设接了 20 个工具、每个定义约 150 token(示意),光工具定义就是 3000 token,每次调用都要带。工具一多,就按意图或阶段只暴露相关的子集:先选类别,再加载这一类工具的定义。报销场景只暴露 4 个工具,就降到 600 token。工具返回也要裁:只留需要的字段,长结果存到外部,只给句柄或摘要。

第三件是缓存命中。前缀缓存要求前缀逐字节相同,所以 Prompt 的排布要讲究:

位置 放什么 稳定性
最前 系统提示、工具定义(顺序固定)、固定 few-shot 跨请求不变
中间 会话历史(只追加、不改写) 跨轮递增
最后 本轮检索结果、当前时间、用户问题 每轮变化

常见的反例是把当前时间戳或用户 ID 放在系统提示开头:

改之前:[当前时间 2026-11-02 09:31:07][用户 u_8812] 你是报销助手……(5000 token 固定内容)……[用户问题]
改之后:你是报销助手……(5000 token 固定内容)……[当前时间 2026-11-02 09:31:07][用户 u_8812][用户问题]

改之前,第一个 token 就每秒都在变,后面 5000 token 的固定内容一个也命中不了;改之后,这 5000 token 跨请求完全相同,每次都能命中(示意)。

最后是输出侧:限制最大输出长度,要求简短的结构化输出;推理模型设思考预算。

容易踩的坑:压缩可能丢掉关键约束,比如用户早先说过"先别建单",这类约束应该抽成结构化状态单独保留。动态工具集会改变前缀、破坏缓存,要在"少带工具"和"前缀稳定"之间权衡,一个折中是按阶段固定几套工具组合,每套各自能命中缓存。摘要本身也要花 token 和延迟。

可能的追问:

  • 怎么知道优化有效?监控每个任务的输入、输出和命中缓存的 token(API 的用量返回里通常有缓存相关字段),同时在评测集上确认成功率没掉。

放到报销案例里:制度条款只带命中的两三条带编号的原文,比如第 4.2 条,不带整本制度。query_invoices 的结果只保留金额、日期、类型、状态四个字段,INV-102 这张就是"118.5 元、市内交通、未报销"这样一行,不把两千 token 的 OCR 全文塞进上下文。

线上要监控哪些指标?QPS、延迟、错误率、token 消耗怎么看?

简答:监控分四层。服务层看流量、延迟分位和错误率;模型层看 token、TTFT / TPOT、缓存命中和 GPU / KV 使用;工具层看每个工具的耗时与失败;业务层看任务成功率和每成功任务成本。指标负责告警,trace 负责定位。

细说:

层 核心指标 用来回答
服务 QPS、并发、P50 / P95 / P99、按类型分的错误率(超时、429、5xx、解析失败)、队列长度与等待时间 稳不稳
模型 输入 / 输出 / 缓存 token、TTFT、TPOT、每任务步数、重试率、GPU 利用率、KV 块使用率、抢占次数 贵不贵、快不快
工具 每个工具的调用量、耗时、失败率、参数校验失败率 慢在谁
业务 任务成功率、人工接管率、用户反馈、每成功任务成本、安全拦截数 值不值

Trace 的做法是一个任务一个 trace,每次模型调用、检索、工具调用各一个 span,记录模型版本、Prompt 版本、token 数、耗时和结果状态。OpenTelemetry 有面向生成式 AI 的语义约定,仍在演进中。具体做法见 可观测性:追踪一条任务的代价。

告警用分位数和 SLO 消耗速度,不用平均值。举个例子:100 个请求里 98 个 1 秒返回,2 个卡了 20 秒,平均是 (98 × 1 + 2 × 20) / 100 = 1.38 秒,看着挺好;P99 却是 20 秒,那 2 个用户已经在骂了。成本类指标看趋势,比如日环比突增,不只看绝对值。

容易踩的坑:记录 Prompt 全文有隐私风险,要脱敏、采样、限权限。指标太多没人看,每层挑三到五个核心指标上看板就够。平均延迟会掩盖长尾,上面那个例子就是。

可能的追问:

  • P99 突然升高怎么查?先按维度切分(模型、租户、工具、输入长度区间),看是不是集中在某一类请求;再抽几条慢 trace,看时间花在排队、prefill、decode、工具还是重试上。

放到报销案例里:报销看板至少有"建单成功率""缺发票追问率""每单成本""发票接口 P95"四个数。比如某天建单成功率从 95% 掉到 88%,切分后发现全集中在上海的打车票,再抽 trace 看到 query_invoices 返回的金额字段变成了字符串,问题就定位到发票接口的一次升级上(示意)。

旁枝串联

Agent 的成本为什么会被放大?一次任务的真实成本怎么算?

简答:单次调用的单价只是一个因子。Agent 一次任务要调用很多轮,每轮都要重新带上越来越长的上下文,再叠加检索、工具、重试和失败任务的分摊,所以累计输入 token 随步数超线性增长。

细说:设初始上下文 c0 c_0 个 token,每步追加 k 个 token(工具结果、模型输出),一共 N 步。第 1 步输入 c0c_0,第 2 步 c0+kc_0+k,第 t 步 c0+(t−1)kc_0+(t-1)k。把 N 步加起来:

Tin=∑t=1N(c0+(t−1)k)=Nc0+N(N−1)2kT_{\text{in}}=\sum_{t=1}^{N}\bigl(c_0+(t-1)k\bigr)=Nc_0+\frac{N(N-1)}{2}k
  • TinT_{\text{in}}:整个任务累计计费的输入 token 数。
  • ∑t=1N\sum_{t=1}^{N}:读作"对 t 从 1 到 N 求和",就是把每一步的输入加起来。
  • c0c_0:第一步的输入,系统提示、工具定义和用户问题。
  • kk:每步新增的 token;NN:步数。
  • 右边的 N(N−1)2\frac{N(N-1)}{2} 来自 0 + 1 + … + (N − 1),这一项是平方增长的来源。

先用 N = 3 核对:逐步加是 4000 + 4800 + 5600 = 14,400;用公式是 3 × 4000 + 3 × 800 = 14,400,对得上。教学算例(示意)取 c0=4000 c_0=4000 、k=800 k=800 、N=8 N=8 :8 × 4000 = 32,000,加上 28 × 800 = 22,400,累计 54,400 token,是单次调用 4000 token 的 13.6 倍。按每百万输入 token 2 元(示意)算,光输入就是 0.109 元一个任务。

如果历史只追加、不改写,每一步除了新增的 k 个 token,前面都和上一步的输入完全相同,可以命中前缀缓存。没命中的部分只有 c0+(N−1)kc_0+(N-1)k = 4000 + 7 × 800 = 9,600 token,其余 44,800 token 命中。假设命中部分按一成价格计费(示意,实际折扣以各家文档为准),等效计费 token 是 9,600 + 4,480 = 14,080,约为不缓存时的 26%。

这个式子假设每步追加量相同、上下文没有被截断或摘要;真实任务里 k 随工具不同而变化,有了压缩后 TinT_{\text{in}} 也不会一直平方增长。真实成本还要加上:

任务成本 = Σ 模型调用(输入 + 输出 + 思考 token)
         + 检索 / rerank + 外部工具费用
         + 重试 + 失败任务的分摊
每成功任务成本 = 总成本 / 成功任务数

对应三个杠杆:降 c0 c_0 ,精简系统提示和工具定义并让它命中前缀缓存;降 k,裁剪工具结果;降 N,把确定性步骤改成 workflow,独立步骤并行。

容易踩的坑:历史只追加不改写时,二次项的大部分可以按缓存价计费,上面算过;一旦中途改写历史(例如重新摘要),从改写的位置往后缓存就断了,那一步要按全价重新付一次。

可能的追问:

  • 多 Agent 会更贵吗?通常更贵:每个子 Agent 有自己的系统提示和上下文,协调消息也要 token。只有并行或上下文隔离带来的成功率、时延收益抵得过时才值得,见 Agent 核心。

放到报销案例里:报销任务一般是 search_policy、query_invoices、query_tickets、create_reimbursement_ticket 四次工具调用,加上最后的回复,大约 5 到 6 步。如果 query_invoices 返回 OCR 全文,k 从几百涨到两三千,二次项会跟着放大几倍;只回传 INV-102 的四个字段,k 就压下来了。

MoE 模型参数那么大,为什么推理不一定贵?

简答:MoE 每个 token 只经过路由选中的少数专家,计算量由激活参数决定;可所有专家都得放在显存里。可以这样记:算得像小模型,存得像大模型。

细说:以 DeepSeek-V3 技术报告 为例,总参数 671B,每个 token 激活约 37B。拿数字算一下两边:

  • 显存按总参数备。FP8 下每个参数 1 字节,权重就是 671 GB,比 8 张 80 GB 卡的 640 GB 还多,还没算 KV;BF16 要翻倍到 1342 GB。私有化的门槛就在这里。
  • 计算量按激活参数算。前向一次约每参数 2 次浮点运算,每个 token 约 2 × 37×10⁹ = 74 GFLOPs;同样 671B 的稠密模型是 1342 GFLOPs,差约 18 倍。单 token 的 FLOPs 接近 37B 的稠密模型。
  • 带宽:batch 小时每步只读被选中的专家;batch 大时几乎所有专家都会被某些 token 用到,带宽优势变小。
  • 部署:需要专家并行和负载均衡,跨卡通信成为新的开销来源。

容易踩的坑:低并发的小规模私有化部署,MoE 往往不划算,显存门槛高,利用率又低;大规模在线服务才能把它的计算优势兑现。

可能的追问:

  • 卡不多时选 MoE 还是同等激活量的稠密模型?同等激活量下 MoE 效果通常更好,但显存需求是总参数级别。卡少、并发低选稠密;规模化服务、卡多选 MoE。

放到报销案例里:一家公司的报销助手,高峰也就每秒几个任务,为它私有化部署一个要十几张卡才放得下的 MoE,大部分时间显卡都在闲着。这种量级用 API,或者私有化一个 7B 到 14B 的稠密模型加 LoRA,更合算。

你说优化后又快又省,怎么证明效果没掉?

简答:把每次成本或延迟优化都当作一次模型变更来发布:同一评测集离线对比、影子流量、小流量灰度。效果设"不可退化"门槛,成本与延迟必须有实际改善才全量。

细说:

  1. 固定评测集:用第 6 站交来的黄金集,并按切片(难例、长文、工具密集、写操作、注入用例)分别统计,见 Agent 评测:从测试集到发布门槛。
  2. 离线对比:基线与新方案跑同一批用例。模型输出有随机性,要重复运行看方差,用同一批用例做配对比较。
  3. 影子流量:新方案并行处理真实请求但不返回给用户,对比输出差异。
  4. 灰度:小比例用户上线,盯业务指标,保留一键回退。
  5. 判定:效果差异在噪声范围内、高风险切片零退化、每成功任务成本或 P95 有显著改善,才全量。

样本量要心里有数。两个方案的成功率差了 2 个百分点,到底是真掉了还是运气?看差值的标准误:

SE=p1(1−p1)n1+p2(1−p2)n2,95% 区间≈(p^2−p^1)±1.96 SE\mathrm{SE}=\sqrt{\frac{p_1(1-p_1)}{n_1}+\frac{p_2(1-p_2)}{n_2}},\qquad \text{95\% 区间}\approx(\hat{p}_2-\hat{p}_1)\pm1.96\,\mathrm{SE}
  • p1p_1、p2p_2:基线和新方案的成功率,p^\hat{p} 表示从样本里算出来的值。
  • n1n_1、n2n_2:两边各跑了多少条用例。
  • p(1−p)/np(1-p)/n:一个比例的方差,p 越接近 0.5、n 越小,波动越大。
  • SE:标准误,差值的波动幅度;1.96 是正态分布 95% 区间对应的倍数。

教学算例:成功率都在 80% 左右,各 1000 条独立样本。0.8 × 0.2 / 1000 = 0.00016,两边相加 0.00032,开根号得 0.0179,乘 1.96 是 0.035,所以区间是差值 ±3.5 个百分点。假如新方案是 78%,差值 −2 个百分点,区间是 [−5.5, +1.5],包含 0:你既不能说它掉了,也排除不了它掉了 5 个点。样本降到每边 300 条,SE 变成 0.0327,区间放宽到 ±6.4 个百分点,只能发现明显退化。

这个式子假设两边是独立样本。如果两个方案跑的是同一批用例,更好的做法是配对比较:只看"基线对、新方案错"和"基线错、新方案对"这两类用例各有多少,噪声会小得多。成功率接近 0 或 1、或者样本很少时(比如高风险切片只有 20 条),正态近似不可靠,直接看失败用例本身。

容易踩的坑:只看总分会掩盖某类任务大跌。LLM-as-judge 换了被评模型可能产生偏差,需要人工校准,见 评测 / 安全。"感觉快了"不算数,必须是同口径的 P95 和每成功任务成本。

可能的追问:

  • 高风险用例怎么处理?写操作、权限、注入类用例单独设门槛,任何退化都阻断发布,不能被总体平均分稀释。

放到报销案例里:把意图识别换成更小的模型之前,先在报销评测集上确认"误把咨询当办事、误触发建单"的比例没有上升。员工只是问"打车超标能报吗",助手却调了 create_reimbursement_ticket,系统里就多出一张没人要的工单,这类错误一次都不该放过。

连接

LLM 基础 解释 KV Cache 和结构,RAG 是“知识注入”的另一条路,评测 决定优化是否真的有效。在主线里,本站接过第 6 站的评测门槛,把模型路由、预算、限流与降级交给 Coding / Harness;一条任务的代价怎么追踪,看练习 可观测性:追踪一条任务的代价。

这是一篇可以继续生长的笔记。沿着关系继续阅读 ↗