第48篇|通往 AI Infra 和 Agent:vLLM/SGLang 与 Agent 框架全景
本篇定位:专栏四「PyTorch 与 AI Infra 入门」的收官篇,也是 48 篇 Python 系列的最后一篇。承接第 47 篇的 KV Cache、batching 与采样,把已经学过的 Python、NumPy、PyTorch 和推理机制,放进 vLLM、SGLang 与 Agent 系统的全景地图。
读完你将能够:复述从 Python 到 AI Infra 的学习路径;解释推理运行时为什么需要 PagedAttention、continuous batching、radix cache 等机制;区分 Agent 的工具、记忆与编排层;读懂一个最小工具调用循环;为下一步学习框架、服务和系统性能设计实验,而不是只记产品名。
预计用时:阅读 80~100 分钟,动手 60~90 分钟。
学完前 47 篇,手里已经有一条完整的线索:Python 负责表达控制流,NumPy 负责把数据变成有形状的数组,PyTorch 负责自动求导、GPU Tensor 和 Transformer,最后几篇把模型推向真实服务中的 KV Cache、batching 和采样。但当我们打开一个大模型服务仓库时,仍会觉得东西突然变多了:调度器、分页缓存、并行、tokenizer、流式响应、工具调用、记忆、评测,似乎每个词都在另一个领域。
其实它们仍然围绕同一个问题:如何在有限的计算、显存和时间预算里,让模型可靠地完成一段可观测的工作。本篇不把任何一个产品讲成教程,也不承诺一张图能替代源码阅读;我们要做的是建立导航。知道每个组件解决什么问题、和前面哪一个概念相连,下一步遇到新框架时就能提出正确的问题。
一、从 Python 到 AI Infra:把 48 篇串成一条路
1.1 四层能力不是四个孤岛
第一层是 Python 语言本身。变量与对象、容器、函数、异常、类、迭代器、装饰器、类型注解和异步编程,决定我们能否清楚地写出一个服务。第 23~28 篇已经把线程、进程、asyncio、HTTP 和最小 Agent 接起来。这里的重点不只是语法,而是状态、资源生命周期和失败路径:一个请求何时创建,何时等待,谁负责取消,错误如何返回。
第二层是数值计算。NumPy 的 dtype、shape、stride、广播和视图,让我们理解一块内存如何被解释;手写 MLP 的前向和反向,让矩阵乘法不再是黑盒。到了 PyTorch,Tensor 携带 device 和梯度关系,CUDA 把一段看似普通的运算变成 GPU kernel、显存读写与同步事件。
第三层是模型执行。Attention、Transformer、mini-GPT、混合精度和 torch.compile 告诉我们模型怎样算。这里要关心算子、形状、精度、权重和临时激活。第四层是服务运行时:请求会同时到达、长度不一样、有人中途取消、输出需要流式返回,模型执行必须服从一个动态时间线。
可以把这条路径压缩成一个问题表:
| 学习阶段 | 你掌握的对象 | 在 AI Infra 中对应的对象 |
|---|---|---|
| Python 基础 | 变量、函数、异常、协程 | 请求状态、调度循环、服务边界 |
| NumPy | 数组、shape、stride、内存 | Tensor 布局、批量维度、kernel 输入 |
| PyTorch | Tensor、autograd、module、CUDA | 模型权重、算子图、显存与设备 |
| 推理基础 | KV Cache、batching、采样 | token 预算、调度、缓存块、响应质量 |
| Agent 工程 | 工具调用、循环、状态 | 工作流、记忆、权限、观测与评测 |

图1 从 Python 到 AI Infra 与 Agent 的学习地图
1.2 “更快”不是一个单独的按钮
第 47 篇讲过,prefill 更像批量处理输入,decode 更像反复读取上下文并产生一个 token。一个服务的体验至少分为首 token 延迟(TTFT)、输出 token 间延迟(TPOT)、总吞吐、p95/p99 和错误率。Agent 又会在一次用户任务里发起多轮模型调用,因此还要统计工具等待、重试、上下文增长和总成本。
这解释了为什么“把 batch 调大”不一定更好:GPU 利用率可能上升,短请求却要在队列里等更久;KV Cache 可能装不下,导致更多抢占;Agent 的某一步工具调用很慢,模型再快也不能改变端到端延迟。性能优化必须先画时间线,再决定是优化算子、内存、调度,还是减少不必要的调用。
AI Infra 视角:把一次调用看成事件流:入队 → tokenizer → prefill → decode → 采样 → 工具或流式输出 → 结束。每个事件都应有开始时间、结束时间、输入输出 token 和失败原因。没有这些观测,所谓“变快”只能是感觉。
1.3 从教程代码到生产系统,缺的是什么
教程里的 model(input_ids) 往往是同步的,输入形状也固定;生产系统则要处理并发、背压、超时、限流、鉴权、资源隔离、版本回滚和可重复实验。教程里的 Python list 可以保存 KV,生产系统要按块分配显存;教程里可以使用全局随机数,服务里要为请求管理随机状态;教程里工具函数可以直接读文件,真实 Agent 必须有权限边界。
因此,学习 AI Infra 不等于跳过 Python 直接背引擎参数。恰恰相反,越靠近基础设施,越需要能用小程序验证一个假设:缓存是否复用,batch 是否补位,取消是否释放资源,异常是否被吞掉。小实验是连接抽象设计和实际性能的最短路径。
二、推理运行时:vLLM 与 SGLang 在解决什么
2.1 为什么 PyTorch 循环还不够
用 PyTorch 写一个生成循环并不难:编码 prompt,做一次 prefill,循环调用 forward,从 logits 采样,再把 token 拼回去。第 47 篇的 toy 实验已经展示了核心。但当十几个请求的 prompt 长度不同、生成长度不同,且不断有新请求进入时,简单循环会暴露问题:padding 浪费计算;连续的大 Tensor 难以容纳碎片化请求;一个长请求可能阻塞许多短请求;Python 侧每轮拼接也会增加开销。
推理运行时(inference runtime)把模型和请求管理分层。它通常维护等待队列、活跃序列、停止条件、token 预算、KV Cache 映射、采样配置和流式输出。模型仍由 PyTorch、CUDA 或其他后端执行,但“这一轮让谁前进多少 token”不再由业务代码临时决定。
2.2 PagedAttention:把 KV Cache 当成可管理的页
连续 KV Cache 的直觉是:每个请求预留一段连续空间,新的 token 顺序写进去。问题在于请求有长有短,结束时间也不同。预留太多会浪费显存,预留太少又需要搬迁;多个请求交错分配和释放后,还会出现无法利用的小空洞。
PagedAttention 的高层思路是把 KV Cache 切成固定大小的 block。一个逻辑序列按 token 顺序增长,但它的物理 block 可以分散在显存池中。运行时保存一张 block table,告诉 kernel:逻辑上的第几个 token 对应哪一个物理块。请求结束时,块回收到池中;新请求只申请它当前需要的块,而不必一次占满最大长度。
这和操作系统分页很像,但不要把类比当成实现细节。GPU kernel 要做间接寻址,block 大小、布局、并行方式和硬件访问模式都会影响性能。分页的价值不在于“每次访问都免费”,而在于更有效地管理碎片,提升可用 KV 容量和并发度。它把第 47 篇的显存账本,落实成了一个可分配、可回收的资源管理器。

图2 PagedAttention 的逻辑序列与物理 KV 块
2.3 continuous batching:按迭代而非按请求组队
静态 batching 需要等待一组请求结束;continuous batching 把推进单位改成一次迭代。每轮调度器为活跃请求各生成一个或若干 token,完成的请求释放状态,等待队列里的请求补入空位。这样长请求不会让已经完成的短请求一直占着槽位,GPU 的有效 token 利用率通常更好。
不过它也不是“把 list 改成 set”那么简单。调度器要同时满足最大 batch、最大新 token 数、显存可用块、优先级和公平性。一个长 prompt 的 prefill 可能突然吃掉大量计算,于是系统可能使用 chunked prefill,分段处理它,避免正在 decode 的请求长时间得不到机会。选择每轮推进多少 token,本质上是在吞吐、TTFT、TPOT 和 p99 之间做控制。
下面这个纯 Python 模拟器不运行模型,只展示迭代调度和补位。它把“请求数”作为简化预算;真实实现更常以 token 和 KV block 作为预算。
from dataclasses import dataclass
@dataclass
class Job:
name: str
remaining: int
waiting = [Job("A", 2), Job("B", 4), Job("C", 1)]
active = []
capacity = 2
for step in range(5):
while len(active) < capacity and waiting:
active.append(waiting.pop(0))
print(step, [job.name for job in active])
for job in active:
job.remaining -= 1
active = [job for job in active if job.remaining > 0]
0 ['A', 'B']
1 ['A', 'B']
2 ['B', 'C']
3 ['B']
4 ['B']
2.4 radix cache:共享前缀的另一种视角
Agent 经常使用稳定的系统提示、工具描述、格式约束和对话前缀。若每次调用都把同一段前缀重新 prefill,计算和首 token 延迟都会增加。Radix cache 可以把 token 前缀组织成树状结构,多个请求共享相同的前缀节点;新请求沿着已存在的路径复用缓存,分叉处再计算新的 token。
它最重要的前提是“相同真的相同”。tokenizer 版本、空格、系统消息顺序、工具 schema 和模板只要不同,后续 KV 就不能随意复用。缓存还要考虑容量、淘汰、引用关系和租户隔离。命中率高时,Agent 的多轮或多分支生成会受益;命中率低时,管理开销可能不值得。
AI Infra 视角:PagedAttention 主要回答“KV 放在哪里、如何分配”;continuous batching 回答“这一轮让谁运行”;radix cache 回答“相同前缀能否共享”。三者都在优化 token 级资源,但分别属于内存管理、时间调度和计算复用。
2.5 vLLM 与 SGLang 应该怎样理解
vLLM 可以先被理解为一个面向高吞吐生成服务的运行时家族:它把分页式 KV 管理、连续 batching、并行执行、采样和服务接口组合起来。阅读它时,可以从四个问题开始:请求如何进入队列,KV block 如何分配,batch 每轮如何变化,输出如何流回客户端。
SGLang 更强调把生成程序、结构化输出、分支和缓存复用组织在一起。对一个包含计划、工具调用、判断和最终回答的程序,运行时有机会看到比“若干互不相关的 HTTP 请求”更多的结构,并据此复用前缀、安排分支或约束生成。它的价值不只是另一种 API,而是把生成控制流和推理执行联系起来。
两者会持续演进,具体 API、支持模型、kernel 和部署方式应以对应版本文档和压测为准。不要只用“哪个更快”做选型:还要问模型架构是否支持、是否需要结构化输出、是否要多卡、请求长度分布怎样、团队是否能观测和升级。真正可迁移的知识是调度、缓存、并行与指标,而不是某个参数的记忆。
三、Agent 框架全景:工具、记忆与编排
3.1 Agent 不是“会聊天”的新名字
一个 Agent 可以抽象为:观察当前状态,决定下一步动作,调用工具或模型,接收结果,再继续,直到满足停止条件。模型是决策器的一部分,但 Agent 系统还必须处理工具 schema、权限、状态、超时、重试、人工确认和最终答案。仅仅让模型输出一段文字,并不自动产生可靠的 Agent。
最小闭环可以写成:
用户目标 → 上下文 → 模型决定 → 工具调用 → 工具结果 → 更新上下文 → 停止或继续
这里的“工具”可以是数据库查询、搜索、计算函数或另一个模型;“上下文”可以包括短期对话、当前任务状态和检索到的资料;“停止”可能来自最终答案、最大步数、预算耗尽或人工拒绝。框架的工作是把这些边界变得显式、可测试、可观测。
3.2 工具层:把副作用包成可验证的接口
工具不只是一个 Python 函数。一个可用工具至少有名称、输入 schema、输出 schema、错误类型、超时、权限和幂等性说明。例如“查询天气”通常是读操作,可以重试;“发送邮件”是外部副作用,不能因为模型重复调用就真的发送两次;“执行 SQL”要限制表和语句类型。
工具描述会进入 prompt,也会消耗 token,并可能成为 prefix cache 的一部分。工具数量太多会增加选择困难,描述太模糊会导致错误调用,schema 太宽又难以审计。好的工具设计倾向于小而明确:参数有限、返回结构稳定、失败可解释,危险操作必须经过显式确认或策略层批准。
3.3 记忆层:短期、长期与检索不要混为一谈
短期记忆是当前对话和任务轨迹,直接放在上下文中,适合保持最近几步的连续性;长期记忆是跨会话的用户偏好、事实或总结,需要存储、更新和删除策略;外部知识则通常通过检索临时加入,不等于模型永久“记住”。三者的生命周期、隐私和一致性都不同。
上下文窗口不是无限的。把全部历史原样拼进去,会增加 prefill、KV 占用和注意力噪声。摘要能减少长度,却可能丢失细节;向量检索能找相关片段,却可能带来过期事实;结构化状态更可靠,但需要定义 schema 和迁移。工程上应记录每次记忆的来源、时间、置信度和删除方式,不要让 Agent 自己无边界地写入“永久记忆”。
3.4 编排层:链、图、状态机与多 Agent
最简单的编排是链:步骤一的输出传给步骤二,适合固定流程。分支和循环增多后,可以用图表达节点、条件边和状态;状态机则强调允许的状态及转移,适合审批、重试和人工介入。多 Agent 让不同角色分别负责规划、检索或执行,但通信成本、共享状态和错误传播也会增加。
不要因为“多 Agent”听起来先进就拆分。一个清楚的单 Agent 加三个工具,往往比互相转发消息的系统更容易调试。选择编排抽象时,先画出状态和副作用:哪些步骤必须按顺序,哪些可以并行,失败后从哪里恢复,哪些结果需要人工确认。框架只是表达这些约束的语言。

图3 Agent 的工具、记忆与编排三层
3.5 一个最小、可审计的工具调用循环
下面的例子故意不用具体 Agent 框架。模型部分用一个确定的规划函数代替,重点是把工具调用、结果、最大步数和未知工具错误写清楚。真实项目把 decide 替换成模型 API 或本地推理服务即可,但边界仍应保留。
from dataclasses import dataclass
@dataclass
class Decision:
tool: str | None
argument: int | None
answer: str | None
def calculate(x: int) -> dict:
return {"value": x * x}
def decide(history: list[dict]) -> Decision:
if not history:
return Decision("calculate", 7, None)
return Decision(None, None, f"结果是 {history[-1]['result']['value']}")
tools = {"calculate": calculate}
history = []
for step in range(3):
decision = decide(history)
if decision.tool is None:
print(decision.answer)
break
if decision.tool not in tools:
raise ValueError(f"unknown tool: {decision.tool}")
result = tools[decision.tool](decision.argument)
history.append({"tool": decision.tool, "result": result})
else:
raise RuntimeError("step budget exceeded")
结果是 49
这个循环有几个值得保留的工程习惯:工具集合是显式的,参数可以在调用前校验,历史记录包含结构化结果,循环有步数上限,未知工具不会静默执行。生产版本还要加入超时、取消、重试预算、日志脱敏和副作用幂等键。
AI Infra 视角:一次 Agent 任务可能产生多次 prefill、decode 和工具等待。推理运行时的 prefix/radix cache 能减少共享前缀成本,但不能替代 Agent 层的状态设计;框架的 memory 也不能替代 GPU 的 KV Cache。一个是任务语义的记忆,一个是一次推理中的中间 Tensor,生命周期完全不同。
四、把模型服务和 Agent 组合成可观测系统
4.1 从请求链路画出资源边界
典型链路可以分成五个边界:客户端和网关,Agent 编排器,模型推理服务,工具服务,存储与观测系统。网关负责鉴权、限流和流式协议;编排器负责状态机和预算;推理服务负责 token、KV 和 GPU;工具服务负责副作用和数据访问;观测系统记录 trace、指标和事件。
边界清晰后,故障更容易归因。模型 TPOT 变慢,检查 decode batch、KV block 和 GPU 带宽;Agent 总耗时变长,检查工具等待和重试;回答错误,检查上下文、工具结果和模型采样;成本异常,检查循环是否没有停止。所有问题都归因给“模型不聪明”会掩盖基础设施缺陷。
4.2 让 token 成为共同度量单位
Token 是模型侧的自然单位,也是系统预算的桥。输入 token 影响 prefill 和上下文长度,输出 token 影响 decode 时间;活跃 token 数影响 KV Cache 容量;工具描述、历史和检索片段共同决定 prompt。可以把每个任务的预算写成:最大输入 token、最大输出 token、最大 Agent 步数、最大工具耗时和最大金额。
这不表示所有成本都能用 token 衡量。数据库慢查询、网络传输和 CPU 序列化也会影响延迟。但 token 账本能让几个层次对话:应用知道一次任务为什么长,调度器知道该请求占多少 cache,评测知道质量和成本如何变化。每次优化都应比较质量、延迟、显存和费用,而不是只看一个数字。
4.3 一个适合初学者的本地实验矩阵
不必一开始就部署大模型。可以用 toy Tensor、短 prompt 和 mock 工具验证机制,再换小模型做趋势测试。每轮只改变一个变量,并保存环境、随机种子、模型、长度分布和结果。
| 实验 | 对照组 | 记录指标 | 要验证的假设 |
|---|---|---|---|
| KV Cache | 重算 / 缓存 | max diff、TPOT、显存 | 缓存减少重复工作 |
| batching | 静态 / continuous | 吞吐、TTFT、p95 | 补位减少空槽但影响等待 |
| 前缀复用 | 命中 / 不命中 | prefill 时间、命中率 | 相同 token 前缀可以共享 |
| Agent 记忆 | 全历史 / 摘要 / 检索 | token、质量、延迟 | 少上下文不一定少信息 |
| 工具策略 | 直接执行 / 确认 | 错误、副作用、耗时 | 权限和幂等提升可靠性 |
实验报告至少给出硬件、软件版本、模型配置、输入输出长度、并发、warmup、统计分位数和正确性检查。随机选择一组漂亮的 tokens/s 没有意义;可重复、可解释、能指出代价的报告才有价值。

图4 从 Agent 任务到推理服务的可观测链路
五、面向下一阶段的学习与选型方法
5.1 先学机制,再选框架
遇到新的推理框架,先按四个层次阅读:API 层怎样提交请求,调度层怎样组 batch,内存层怎样管理权重和 KV,执行层怎样调用 kernel 与通信。遇到新的 Agent 框架,也按四个问题阅读:状态在哪里,工具如何注册,循环怎样停止,失败如何恢复。这样框架换名字,问题仍然相同。
不要把 benchmark 的数字直接搬到自己的业务。吞吐会受模型、量化、GPU、上下文长度、并发、采样和服务协议影响;Agent 质量会受工具设计、数据和评测集影响。至少建立一个与真实流量相近的小基准,先测正确性,再测性能,再测尾延迟和资源峰值。
5.2 一条可执行的后续路线
第一步,继续用 PyTorch 写小实验:实现带 mask 的 Attention,比较重算和缓存,观察 dtype、device 和 shape。第二步,使用一个小模型的本地服务,学会 tokenizer、流式输出、超时和指标,不急于多卡。第三步,阅读 vLLM 或 SGLang 的调度、KV 管理和 prefix/radix cache 相关代码,配合 trace 验证概念。
第四步,选择一个 Agent 框架,只实现一个有边界的任务,例如查询本地资料后生成带引用的答案。为工具写 schema、单元测试、超时和权限;为 Agent 写成功率、工具错误率、步骤数和 token 成本指标。第五步,做故障演练:工具超时、模型返回坏 JSON、客户端取消、cache 不命中、显存不足时,系统是否能停止、回收并给出可解释结果。
最后再研究多模型路由、量化、张量并行、容错和多 Agent。每增加一个复杂组件,都要说明它解决的瓶颈和新增的失败模式。系统复杂度不是能力的同义词。
5.3 用“可验证的假设”替代产品崇拜
一个好的工程问题长这样:“在相同模型、长度和并发下,按 block 管理 KV 是否降低峰值显存?”“共享完全相同的工具前缀,能否减少 prefill?”“把工具结果摘要后,任务成功率是否下降?”问题里包含变量、对照和指标,答案可以通过实验得到。
反过来,“某框架是不是最强”“Agent 会不会取代程序”都太宽泛,无法指导实现。框架只是实现假设的工具,模型只是系统中的一个组件。你真正要积累的是拆解问题、建立基线、测量、解释反常结果和安全地回滚的能力。
六、从学习项目走向生产思维
6.1 正确性、可靠性、安全性先于吞吐
推理服务要验证 logits、tokenizer、停止词、流式顺序和取消回收;Agent 要验证工具参数、权限、提示注入、敏感数据泄露和副作用。性能提升如果改变了输出语义,或让失败请求占住资源,就不是成功优化。先写最小的 golden cases,再做压测。
可靠性还包括资源上限:最大上下文、最大输出、最大步骤、最大重试、单工具超时和总预算。每一个循环都要有停止条件,每一块 cache 都要有释放路径,每一次外部写操作都要考虑幂等。系统宁可明确拒绝,也不要无限等待或悄悄扩大成本。
6.2 让观测能回答“为什么”
日志不应只写“请求失败”。一次 trace 至少串起请求 ID、Agent step、模型调用 ID、工具调用 ID 和租户;模型调用记录输入输出 token、TTFT、TPOT、采样设置、cache 命中;工具记录名称、耗时、状态和脱敏后的参数;资源侧记录 active sequences、KV 使用量、GPU 显存和队列长度。
观测也要注意隐私。prompt、工具参数和长期记忆可能包含个人信息或密钥,不应无条件写入日志。保留哈希、长度、版本和错误码,按权限查看必要内容。安全不是上线后再加的一层,而是状态和数据流设计的一部分。
6.3 代码阅读的抓手
读大型仓库时,不必从第一行读起。先找请求对象和状态转移,再找调度主循环;然后找 KV block 或 cache manager 的接口,最后追到模型执行和输出队列。给每个对象画生命周期:谁创建,谁持有,谁更新,何时释放,异常时走哪条边。
同样的方法适用于 Agent 框架:找 State、Tool、Node、Router、Memory 或类似概念,追踪一次真实任务的事件。把复杂名词翻译成前面学过的 Python 对象、Tensor、队列和资源,就不会被目录规模吓住。
七、踩坑大全
坑 1:把 KV Cache 和 Agent 记忆当成同一个东西。 前者是一次模型推理中的 K/V Tensor,服务于当前序列的注意力;后者是跨步骤或跨会话的任务数据。它们的格式、生命周期和隐私策略都不同,不能因为都叫 cache 或 memory 就混用。
坑 2:只看 GPU 利用率判断服务变快。 GPU 忙可能是在做无效 padding,也可能是长 prefill 挤压了 decode。请同时记录 TTFT、TPOT、吞吐、p95/p99、活跃 token、KV 峰值和错误率。
坑 3:认为 PagedAttention 会自动消除所有显存问题。 分页减少碎片并改善分配,但权重、临时 buffer、通信和每个请求的真实 token 仍然占显存。先算预算,设置最大长度和并发,遇到 OOM 再看具体账本。
坑 4:把 continuous batching 写成不断修改一个列表。 真实系统还要同步 block table、位置、停止状态、采样状态和输出顺序。先用确定性模拟器验证生命周期,再接模型和 GPU。
坑 5:以为 prefix/radix cache 只要“看起来相同”就能复用。 必须保证 token 序列、模板、工具 schema、tokenizer 和租户边界一致;还要处理淘汰和引用。错误复用会产生比 cache miss 更严重的语义问题。
坑 6:Agent 工具没有超时和步数上限。 工具失败会触发重试,重试又可能触发更多调用,最后形成无限循环。为单步和全局设置预算,未知工具直接拒绝,危险副作用需要确认和幂等键。
坑 7:把“多 Agent”当成默认架构。 多角色会增加通信、上下文和调试成本。先证明单 Agent 加清晰工具不能满足需求,再用图或状态机拆分;每个节点都应有输入、输出和失败转移。
坑 8:只优化 tokens/s,不检查输出质量。 采样、量化、摘要和缓存可能改变结果。建立固定评测集,比较答案质量、工具成功率、延迟、显存和费用,任何一个指标下降都要记录原因。
八、小结
- Python、NumPy、PyTorch、推理运行时和 Agent 不是断裂的主题:它们分别负责控制流、数据布局、模型计算、token 级调度和任务闭环。
- vLLM/SGLang 这类系统的核心价值,是把请求时间线、KV Cache、batching、缓存复用和模型执行组合成可管理的运行时。
- PagedAttention 管理物理 KV block,continuous batching 管理每轮参与者,radix cache 复用相同前缀;它们优化的是不同维度。
- Agent 的工具、记忆和编排必须有 schema、生命周期、权限、超时和停止条件;模型只是决策环节,不是全部系统。
- 选择框架前先画状态、资源和指标,使用可重复的对照实验,不要把单一 benchmark 或产品名当作结论。
- 下一阶段真正重要的能力是提出可验证假设,并在正确性、可靠性、安全、延迟、显存和成本之间做取舍。
九、课后练习
- (基础) 用一张图画出从 Python 函数、NumPy 数组、PyTorch Tensor 到 GPU kernel 的数据路径,并为每一层写出一个可能的瓶颈。
- (基础) 改写本文的调度模拟器,加入请求取消和最大 token 预算,验证取消后槽位与状态都能释放。
- (理解) 用 toy Tensor 比较连续 KV Cache 与固定 block KV Cache 的分配方式,打印活跃块、空闲块和碎片情况。
- (理解) 构造三个共享系统提示但后缀不同的请求,统计 radix 前缀命中长度;改变一个空格或模板字段,观察命中如何变化。
- (进阶) 为最小 Agent 增加工具 schema 校验、超时、重试上限和幂等键,并为未知工具写测试。
- (进阶) 设计一个实验,比较“全历史”“摘要”和“检索”三种记忆策略,至少报告 token、延迟、任务成功率和错误案例。
- (挑战) 用 vLLM 或 SGLang 部署一个小模型,固定模型和输入分布,比较不同并发、最大 token 预算与缓存设置下的 TTFT、TPOT、p95 和显存峰值。
- (挑战) 阅读一个推理运行时或 Agent 框架的调度入口,画出一次请求的状态转移图,并指出一个可以通过单元测试验证的资源释放不变量。

图5 系列之后的 AI Infra 与 Agent 学习路线
下一篇预告
这是 48 篇系列的最后一篇,没有下一篇文章。接下来的路线不再按编号展开:可以沿着 PyTorch → 小模型推理 → vLLM/SGLang → 可观测服务 → 有边界的 Agent 逐步深入,也可以回到前面的基础篇,为每个概念补一个测试和一个性能实验。把本篇的全景地图带到真实代码仓库里,先从一个能测量、能停止、能回滚的小系统开始。
