一次大模型 API 请求是怎么跑起来的:从 Harness 到 GPU、并发与 KV Cache
前面聊模型部署时,我一直有一个疑问:一个模型实例明明只有一份模型参数,为什么它可以同时处理很多人的请求?
要理解这个问题,不能只盯着模型本身。一次 API 请求真正经过的是一整套系统:
可以把它简单理解成:
- Harness 决定这次准备什么材料、调用哪个模型。
- 服务商前台决定请求能不能进入、应该送到哪里。
- 推理引擎决定多个请求怎么一起使用 GPU。
- 模型负责根据输入计算下一个 Token 的概率。
1. 请求到达模型厂商之前
用户在客户端里提出问题后,通常不会立刻把原始文字发送给模型。客户端或 Harness 还要做一些准备工作:
- 根据用户选择、Agent 配置或路由规则确定模型。
- 获取这个模型的上下文上限、输出上限和能力配置。
- 选择历史消息、记忆、Skill、工具定义和附件。
- 使用对应 Tokenizer 估算输入长度。
- 必要时压缩、裁剪上下文,并给输出预留空间。
- 组织最终 API 参数并发出请求。
原始会话和资料
↓
Harness 选择与组装
↓
本次真正发送的上下文
↓
模型 API
这里的模型路由不一定调用模型。简单场景可以使用普通程序规则,例如按任务类型、成本、延迟和模型能力进行选择;只有语义难以判断时,才可能额外调用一个分类模型。
API Key 主要用于证明调用者身份和计算配额。它不是模型的记忆,也不代表一个模型实例或一个会话。
2. 服务商前台:鉴权、限流、路由和排队
请求到达模型厂商后,通常先经过网关和调度层,而不是直接进入 GPU。
2.1 鉴权
检查 API Key 是否有效、是否有权调用目标模型,以及账户状态是否正常。
2.2 限流
控制请求数、Token 数和并发量,避免某个调用者占满整个集群。常见限制可以按分钟请求数、分钟 Token 数、同时进行中的请求数或账户额度计算。
2.3 路由
把请求送到支持目标模型的健康集群。这里可能综合考虑地区、模型版本、机器负载、故障状态和容量。
需要区分两种路由:
Harness 的模型路由:决定调用哪个模型
服务商的实例路由:决定送到哪台机器或哪个模型实例
2.4 排队
如果推理容量暂时不足,请求会等待;等待过久则可能超时或被拒绝。
实际上可能存在两层队列:
网关队列:等待进入可用的推理服务
引擎队列:已经进入实例,等待下一轮 GPU 计算
这一层大部分是普通代码、配置和调度算法,一般不需要调用大模型。
3. 什么是模型实例
模型实例可以理解成:模型权重已经加载到显存或内存中,并且处于可以执行推理的状态。
它不是一个用户、一个会话,也不是只能处理一个请求的进程。
多个请求共用的是:
- 同一份模型权重。
- 同一套 GPU 计算资源。
- 推理引擎组织出的计算批次。
每个请求独立保存的是:
- 自己的输入上下文。
- 自己的 KV Cache。
- 自己的生成参数和停止条件。
- 自己已经生成的 Token。
所以,一个模型实例可以同时维护很多请求。它的上限不是一个固定数字,而是受到模型大小、GPU 数量、显存容量、平均上下文长度、输出长度、延迟目标和推理引擎效率共同影响。
上下文越长,每个请求占用的 KV Cache 通常越大,同一实例可以同时容纳的请求就越少。
4. 推理引擎怎么让多个请求一起运行
模型只定义怎么计算,推理引擎负责把真实请求组织成 GPU 能高效执行的任务。
一轮生成大概是:推理引擎把当前活跃请求组成一个 Batch,让 GPU 一起计算下一个 Token;完成的请求退出后,新请求可以立即补入。
这种方式常被称为连续批处理。它不是等一批请求全部结束后再处理下一批,而是在每一轮生成之间动态加入和移除请求。
GPU 擅长同时执行大量相同类型的矩阵计算。推理引擎把不同请求当前需要计算的 Token 组织到一起,GPU 就能并行处理,而不需要给每个请求单独准备一套模型。
因此,多请求共享的不是彼此的上下文,而是模型权重和一次批量计算的机会。
5. KV Cache 到底是什么
Transformer 在生成新 Token 时,需要使用前面所有 Token 的注意力信息。如果每生成一个 Token 都重新计算全部历史,成本会非常高。
KV Cache 会保存历史 Token 已经计算出的 Key 和 Value。下一轮生成时,只计算新增 Token,再读取前面的缓存。
已有 Token:今天 / 天气 / 很好
已有 KV: K1V1 / K2V2 / K3V3
新增 Token:啊
只计算新的 K4V4,再和已有 KV 一起参与注意力计算
KV Cache 不是模型权重的一部分,也通常不会写进模型文件。更准确的说法是:
KV Cache 位于模型权重之外、推理引擎之内,是请求运行时产生的临时状态。
模型结构决定计算中需要 K 和 V,推理引擎负责给缓存分配空间、分页、复用、迁移和释放。
6. 前缀缓存为什么可以命中
如果两个请求开头的 Token 完全一致,推理引擎可能复用这段前缀已经生成的 KV Cache。
请求 A:系统提示 + 工具定义 + 用户问题 A
请求 B:系统提示 + 工具定义 + 用户问题 B
└────相同前缀────┘
它并不是看到文字意思相近就命中,而是通常需要满足:
- Token 前缀完全一致。
- 模型和版本一致。
- Token 位置及相关推理配置兼容。
- Adapter、多模态输入和缓存命名空间等身份一致。
命中后复用的是前缀计算,不是直接复用最终答案。后面的不同内容仍然需要正常计算。
7. KV Cache 放在显存、内存还是 SSD
最常见的分层可以理解成:
| 层级 | 位置 | 适合的数据 | 特点 |
|---|---|---|---|
| L1 | GPU 显存 | 正在生成的活跃请求 | 最快,但容量最贵、最有限 |
| L2 | CPU 内存 | 暂停或等待恢复的缓存 | 容量更大,但搬回 GPU 有延迟 |
| L3 | SSD 或远程存储 | 冷缓存、持久化前缀 | 容量最大,但速度最慢 |
不是所有推理系统都实现完整的三层缓存。最简单的实现可能只使用显存;规模更大的系统才会根据热度做迁移和淘汰。
普通服务器里的 CPU 内存,就是主板上的 DDR 内存。Mac 的 CPU 和 GPU 使用统一内存,物理边界没有独立显卡服务器那么明显,但仍然存在容量、带宽和调度成本。
缓存放在显存里会占用显存容量,也会消耗读取带宽,但不会因为“存在那里”就直接占用 GPU 计算核心。真正执行 Attention 和矩阵运算时,才会使用大量计算资源。
8. 上下文、速度和并发量的关系
模型支持更长上下文,不代表每个请求都能免费使用那么长的内容。
它们之间的关系如上图右侧所示:上下文变长后,单请求 KV Cache 变大,显存可容纳的并发请求减少;注意力计算和内存访问也会增加,因此延迟可能上升、吞吐可能下降。
因此,模型的上下文上限是一种能力边界;能否在高并发下高效运行,还取决于 GPU、缓存管理、批处理和服务目标。
这也是 Harness 要提前控制上下文的原因。少发送无关历史,不只是为了避免超过模型上限,也是在减少推理成本、缓存占用和等待时间。
9. Agent 场景为什么可能调用多次模型
一次普通问答可能只产生一次模型 API 请求。但 Agent 任务中,模型可能先返回工具调用,再由 Harness 执行工具,并把结果放进下一轮上下文。
用户任务
↓
第 1 次模型调用:决定查询工具
↓
Harness 执行工具
↓
第 2 次模型调用:分析工具结果
↓
Harness 发现证据不足,再执行工具
↓
第 3 次模型调用:形成最终回答
所以,一个用户任务不等于一次模型调用。调用几次、何时重试、是否换工具以及什么时候停止,由模型建议和 Harness 规则共同形成闭环。
10. 容易混淆的几个问题
| 常见理解 | 更准确的理解 |
|---|---|
| 一个模型实例一次只能处理一个请求 | 一个实例可以通过批处理同时维护多个请求 |
| API Key 代表一个会话 | API Key 主要用于身份、权限和计费 |
| 多个请求共享上下文 | 请求上下文独立,只共享模型权重和计算资源 |
| KV Cache 是模型文件的一部分 | 它是推理过程中生成的运行时状态 |
| 前缀缓存按照语义相似度命中 | 通常按照完全一致且身份兼容的 Token 前缀命中 |
| 显存只存模型参数 | 显存还可能存放 KV Cache、中间结果和运行时空间 |
| 上下文上限越大,并发能力就越强 | 长上下文通常会增加单请求缓存和计算压力 |
| 路由一定需要调用模型 | 大部分路由可以由规则和普通程序完成 |
总结
一次大模型 API 请求,可以压缩成四层:
客户端 / Harness
准备任务、上下文和模型参数
↓
网关与调度层
鉴权、限流、路由、排队
↓
推理引擎
Token、KV Cache、连续批处理、采样
↓
模型实例与 GPU
共享权重,并行计算各请求的下一个 Token
最核心的理解是:
模型负责计算,推理引擎负责并发,服务商前台负责流量,Harness 负责把用户任务组织成一次或多次可执行的模型调用。
把这四层分开以后,API Key、模型实例、并发请求、上下文、显存和 KV Cache 的关系就比较清楚了。