对应 Maps to:Stanford CS349E / EE339 Efficient ML Inference at Scale(Autumn 2026),原课第 17 次课 · Thu Nov 19 · Agentic Tool Use(Fredrik Kjolstad)。 依据说明:这次课没有指定阅读材料,课程网站也没有 slides。本讲以公开文献为主线:S. Yao et al., ReAct(ICLR 2023);J. Yang et al., SWE-agent: Agent-Computer Interfaces Enable Automated Software Engineering(NeurIPS 2024);KernelBench(第 14 讲)的迭代优化实验;R. Abhyankar et al., InferCept: Efficient Intercept Support for Augmented Large Language Model Inference(ICML 2024);H. Li et al., Continuum: Efficient and Robust Multi-Turn LLM Agent Scheduling with KV Cache Time-to-Live(2025–2026)。前三篇和 InferCept 用的是全文,Continuum 只用了摘要。内容的取舍是我定的,不代表课堂上实际讲了什么。 配套代码 Code:
code/lecture15/(numpy;一个模块 + 5 个脚本,最长的约 15 秒) 先修:第 14 讲(智能体写内核、评估器)、第 7 讲(KV cache 管理、前缀缓存、Mooncake)、第 5 讲(服务模拟器StepModel)、第 10 讲(分块矩阵乘法、掩码、同步)。 预计阅读时间:2.5–3 小时(含运行代码)
0. 本讲导航 · How to read this lecture
0.1 学习目标 Learning goals
学完这一讲,你应该能够:
- 描述智能体的”思考 → 调用工具 → 观察”循环(ReAct),说出一个写内核的智能体需要哪些工具、各返回什么。
- 用信息论衡量一种反馈有多”细”:一次工具调用能把不确定性减少多少比特;解释为什么”只说对错”的测试不够,以及”下一步调用哪个工具”是信息和时间的权衡。
- 用 SWE-agent 的原则设计工具接口:动作简单、反馈有信息量但简洁、有护栏;会算一个多轮会话的上下文和 prefill 账,知道前缀缓存和历史截断之间的冲突。
- 比较重复采样、单线迭代、树搜索这几种”脚手架”,说出用多轮强化学习训练工具使用时奖励怎么设计。
- 从推理系统的角度描述智能体的负载(多轮、输入远多于输出、频繁暂停),用 InferCept 的公式比较”保留 / 丢掉重算 / 换到 CPU”三种处理,理解 TTL 与租雪板问题,并读懂一个服务模拟的结果。
0.2 本讲地图 Map
§1 从一次调用到一个循环 ← 代码 01
§2 反馈有多少信息 ← 代码 01、02
§3 智能体-计算机接口与上下文 ← 代码 03
§4 脚手架、预算与训练
§5 推理系统的视角:工具调用期间的 KV cache ← 代码 04、05
§6 工具使用的安全与可靠性
§7–11 小结、常见误区、练习(含答案)、延伸阅读、下一讲预告
0.3 本讲的代码
agent_lab.py 有四部分:
- A. 一个有 bug 的内核和六种工具:用 numpy 逐块模拟一个 32×32×32 分块的矩阵乘法内核,可以注入 7 种真实的 bug(第 10 讲的掩码、网格、同步、累加精度、下标);工具有:只说对错的测试、给出错误模式的详细测试、用构造的输入(单位矩阵)测试、跑两次看是否确定、memcheck(越界)、racecheck(竞争),每种有一个假设的耗时。贝叶斯更新、信息增益、挑工具的策略。
- B. 上下文的账:真实的编译器输出、错误清单 vs 摘要的 token 数;多轮会话的上下文、prefill(有 / 没有前缀缓存)。
- C. InferCept 的浪费公式:保留 / 丢掉重算 / 分块重算 / 换出,盈亏平衡点。
- D. 服务模拟器:很多个智能体会话跑在一块 H100 上(Llama-3.1-8B),7 种 KV 处理策略。每步时间用第 5 讲的
StepModel。
0.4 记号 Notation
| 符号 | 含义 |
|---|---|
| 轮 turn | 智能体的一次”生成 → 调用工具 → 拿到观察” |
| 观察 observation | 工具返回、追加进上下文的内容 |
| 香农熵(比特): | |
| 信息增益 | :一次工具调用平均减少多少不确定性 |
| 拦截 interception | InferCept 的说法:生成被工具调用打断(也包括等人回复) |
| 一次拦截(工具调用)的时长 | |
| 、 | 被拦截请求的上下文长度;其他正在运行的请求的上下文长度之和(token) |
| 每个 token 的 KV 字节数(Llama-3.1-8B、BF16:128 KiB) | |
| 一次前向处理 n 个 token 的时间 |
1. 从一次调用到一个循环 · From one call to a loop
1.1 什么是”智能体”
在这门课里,智能体 = 一个 LLM + 一组工具 + 一个循环。每一轮,模型读上下文,决定调用哪个工具、传什么参数;系统执行工具,把结果追加回上下文;如此往复,直到模型认为任务完成。
工具对模型来说是一个有签名的函数:名字、参数(通常用 JSON Schema 描述)、返回值的格式。模型输出一段结构化的”调用”,由外面的程序真正去执行——编译代码、跑测试、查文档、读文件。各家 API 都支持这种”函数调用”;Anthropic 在 2024 年 11 月发布的 MCP(Model Context Protocol)是一个开放协议,让工具以统一的方式接到不同的模型和应用上。
1.2 ReAct:思考和行动交替
ReAct(Yao et al., ICLR 2023)的格式很简单:Thought(为什么做下一步)、Action(调用工具)、Observation(工具的结果)交替出现。两件事一起做的好处:
- 只想不做(思维链)的推理没有接地气,容易幻觉、错误累积;ReAct 在 HotpotQA、Fever 上让模型通过一个简单的维基百科 API 查事实,克服了这类问题。
- 只做不想,模型不知道自己在干什么、什么时候该调整计划;在 ALFWorld 和 WebShop 两个交互式任务上,ReAct 只用一两个示例就比模仿学习、强化学习的方法高出 34% 和 10%(绝对成功率)。
Toolformer(Schick et al. 2023)走的是另一条路:让模型自监督地学会在文本里插入 API 调用(计算器、问答、搜索、翻译、日历)——只保留那些”让后面的文本更好预测”的调用。今天的智能体大多结合两者:训练出会调用工具的模型,再在推理时用 ReAct 式的循环。
1.3 一个写内核的智能体的工具箱
| 工具 | 返回什么 | 典型耗时(量级) |
|---|---|---|
| 编译(nvcc / Triton) | 成功,或者错误信息(行号、原因) | 几秒到一分钟(PyTorch 的 CUDA 扩展编译常要几十秒) |
| 测试 | 对 / 错;好的接口还会给出哪里错、错多少 | 秒 |
| 计时 | 运行时间(第 14 讲 §4 的那些陷阱都在这里) | 秒 |
| profiler(Nsight Compute) | 每个内核的吞吐、占用率、瓶颈 | 几十秒 |
| sanitizer(compute-sanitizer) | 越界读写、共享内存竞争、未初始化的读 | 让程序慢一两个数量级 |
| 文档 / 规格 / 代码搜索 | 文本 | 亚秒 |
01 脚本用一个”有 bug 的分块矩阵乘法”把这些工具具体化。7 种 bug 都是第 10 讲里真会犯的错:K 方向最后一块没加掩码、K 循环少算一块、网格大小向下取整、写回越界、少一次 __syncthreads、FP16 累加、B 的下标写反。每种 bug 在三种形状上(128³ 都是块的整数倍;100×70×90 都不是;64×64×4096 的 K 很长)留下的痕迹:
| bug | 只测对错(128³ / 100×70×90 / K=4096) | 详细测试(100×70×90) | 其他工具 |
|---|---|---|---|
| K 尾巴没掩码 | 对 / 错 / 对 | 几乎全错 | 跑两次不同;memcheck:越界读 |
| K 循环少一块 | 错 / 错 / 错 | 几乎全错 | — |
| 网格向下取整 | 对 / 错 / 对 | 最后 4 行、最后 6 列错 | 跑两次不同(未初始化的显存) |
| 写回越界 | 对 / 错 / 对 | 每行开头 26 列被写成 0 | memcheck:越界写 |
| 少一次同步 | 错 / (常常)对 / 错 | — | 跑两次不同;racecheck:竞争 |
| FP16 累加 | 错 / 错 / 错 | 零星的小误差 | — |
| B 下标写反 | 错 / 错 / 错 | 几乎全错 | 单位矩阵输入时对 |
最重要的一行观察:在”整齐”的形状上,三种和尾巴有关的 bug 完全看不出来——而很多人(和模型)写测试时正好只用这种形状。
2. 反馈有多少信息 · How much information is in feedback
2.1 用比特衡量
假设智能体知道 bug 是 8 种情况之一(含”没有 bug”),一开始同样可能:不确定性是 比特。一次工具调用之后,按贝叶斯公式更新”是哪个 bug”的概率;信息增益是不确定性的期望减少量:
一个只说 PASS / FAIL 的测试只有两种结果,所以一次最多 1 比特。02 演示 1 的结果(从均匀先验出发):
| 动作 | 信息量(比特) | 耗时 | 每秒比特 |
|---|---|---|---|
| 详细测试 · 100×70×90 | 2.09 | 3 s | 0.70 |
| 详细测试 · 128³ | 1.75 | 3 s | 0.58 |
| memcheck · 100×70×90 | 1.06 | 40 s | 0.027 |
| 只测对错 · 128³ | 1.00 | 3 s | 0.33 |
| 只测对错 · 100×70×90 | 0.57 | 3 s | 0.19 |
| racecheck(任一形状) | 0.54 | 120 s | 0.005 |
(构造输入的测试、跑两次在中间。)同样是”测试”,给出错误模式(哪些行列、误差多大)的那一种带来两倍以上的信息;同样是只测对错,换个形状信息量也不同——它决定了”哪些 bug 在这里会出错”。
2.2 下一步调用哪个工具
有了信息量和耗时,“下一步做什么”就是一个决策问题。01 演示 2 的轨迹(真正的 bug 是”写回越界”):
[1] Thought: 还剩 8 个可能;test_verbose·edge 信息量 2.09 比特、3 s,每秒 0.70 比特,最划算。
Action: test_verbose(shape=edge)
Observation: FAIL:2492 / 7000 个元素超出容差(35.6%);几乎整列都错的列 0–25;最大误差 36.6
[2] Thought: 还剩 2 个(store_overflow、missing_sync);test·nice 每秒 0.130 比特,最划算。
Action: test(shape=nice) Observation: PASS
[3] Thought: store_overflow 的概率 1.000。修法:写回 C 时加 n < N 的掩码。
Action: edit + compile
[4] 复查:三种形状都 PASS;memcheck 没有越界。
两次诊断、两次编译、四次复查,假设的工具时间合计 105 秒。这里的”思考”是一个固定的规则(维护概率分布、挑每秒信息量最大的工具),不是语言模型;但它把一个好的工程师调试时做的事情显式化了:先用便宜、信息多的工具缩小范围,再用贵的工具确认。
02 演示 2 比较了七种策略(8 个 bug × 10 个随机种子):
| 策略 | 找到 bug | 平均调用次数 | 平均工具时间 | |
|---|---|---|---|---|
| P1 | 只跑 128³ 的对错测试 | 0% | 8.0 | 24 s |
| P2 | 对错测试轮流用三种形状 | 22% | 6.7 | 20 s |
| P3 | 详细测试轮流用三种形状 | 74% | 3.3 | 10 s |
| P4 | 信息量最大(所有工具) | 100% | 2.1 | 6 s |
| P5 | 每秒信息量最大(所有工具) | 100% | 2.1 | 6 s |
| P6 | 每秒信息量最大(不用 sanitizer) | 100% | 2.1 | 6 s |
| P7 | 每秒信息量最大(不用 sanitizer、也不用构造的输入) | 75% | 1.8 | 5 s |
几个结论:
- 反馈越细,需要的调用越少:只说对错时 8 步都找不到;详细测试 74%。
- 工具集决定了能发现什么:P7 永远分不开”K 循环少一块”和”B 下标写反”——在随机输入上它们一模一样(几乎全错、误差大),修法却完全不同。喂一个单位矩阵(B 转置之后还是它自己)一下就分开了。构造的输入、边界输入是人写测试时会想到、随机测试很难碰到的东西(第 14 讲 §2.3 的 eps bug 也是这样)。
- P4 和 P5 在这里一样,因为信息最多的工具恰好也最便宜;如果最有信息的工具很贵,两者会分开——“每秒信息量”是更合理的目标。
2.3 和 KernelBench 的对应
KernelBench 的迭代优化实验(第 14 讲 §2.5)喂回三种东西:上一次的代码 G、编译和执行的结果 E、profiler 的输出 P。DeepSeek-R1 在 Level 2 上的 fast_1 从 36% 升到 72%(G+E+P);10 轮以内 90% 以上的题都能写出能运行的内核,剩下的几乎都是”结果不对”——论文的解释是”正确性的反馈远没有执行失败的信息具体”。用这一节的语言说:**编译错误带着行号和原因,信息量大;“不等于参考”至多 1 比特。**把”不对”变成”哪里不对、错多少、什么模式”,是工具接口设计里最便宜的一步。
3. 智能体-计算机接口与上下文 · The agent-computer interface
3.1 SWE-agent:接口是为模型设计的
SWE-agent(Yang et al., NeurIPS 2024)提出”智能体-计算机接口”(ACI):LLM 智能体是一类新的”用户”,有它自己的长处和短处,应该给它设计专门的接口,就像给人设计 IDE 一样。用 GPT-4 Turbo,SWE-agent 在 SWE-bench 的 2,294 道题上解决了 12.47%,此前最好的非交互式方法是 3.8%;在 300 道题的 Lite 子集上是 18.0%,比只给一个 Linux shell 的基线多 10.7 个百分点。他们总结了四条原则:
- 动作要简单、好懂:bash 命令有几十个选项;少数几个选项、文档简短的命令更好用。
- 动作要紧凑、高效:重要的操作(浏览、编辑文件)应该一步完成,而不是要组合很多步。
- 反馈要有信息量,但要简洁:告诉智能体环境的状态和它刚做的事情的效果,不要多余的细节。
- 护栏减少错误的传播:例如编辑时自动跑语法检查,有错就拒绝这次编辑并说明原因。
他们的消融(SWE-bench Lite,解决率):
| 改动 | 解决率 |
|---|---|
| 完整的 SWE-agent | 18.0% |
| 编辑命令不带语法检查 | 15.0% |
| 没有编辑命令 | 10.3% |
| 搜索结果逐条翻看(而不是摘要) | 12.0% |
| 没有搜索命令 | 15.7% |
| 文件查看器一次显示 30 行 / 整个文件(而不是 100 行) | 14.3% / 12.7% |
| 保留全部历史(而不是只留最近 5 次观察) | 15.0% |
逐条翻看的搜索比根本没有搜索还差:模型会把每一条结果都看一遍,耗尽预算和上下文。
3.2 工具输出有多长
03 演示 1 量了几个真实的工具输出(token 数按”英文 4 个字符一个、汉字一个字一个”估计):
| 工具输出 | 约多少 token |
|---|---|
| clang 编译一段 20 行的错 C 代码(-Wall -Wextra),完整输出 | 297 |
| 只留 error 行 | 26 |
| 写回越界的测试,每个错误元素一行(2,492 行) | 25,099 |
| 同一个测试的模式摘要 | 37 |
| B 下标写反的测试,每个错误元素一行(16,316 行) | 171,391 |
| 同一个测试的模式摘要 | 36 |
一个”老实地把所有东西都打印出来”的测试工具,一次输出就可能超过很多模型的整个上下文窗口;而摘要只要几十个 token,信息还更有用(§2)。真实的 CUDA 扩展编译出错时,输出常常是这个 C 例子的几十倍(模板实例化、头文件链、ninja 的日志)。
3.3 一个会话的上下文账
每一轮,LLM 要把整个上下文再读一遍(prefill)。设系统提示 + 任务 4,000 token、30 轮,每轮模型输出 6,000(带思维链)或 1,500(只留代码和摘要)token,工具输出 4,000(详细)或 120(精简)token(03 演示 2,单价是假设的,只用来比较):
| 配置 | 最后一轮的上下文 | 超过 128k | prefill(无缓存) | prefill(前缀缓存) | 费用(缓存,美元) |
|---|---|---|---|---|---|
| 思维链全留 + 详细输出 | 294,000 | 第 14 轮 | 4.47 M | 0.29 M | 4.83 |
| 思维链全留 + 精简输出 | 181,480 | 第 22 轮 | 2.78 M | 0.18 M | 4.02 |
| 只留摘要 + 详细输出 | 163,500 | 第 24 轮 | 2.51 M | 0.16 M | 1.87 |
| 只留摘要 + 精简输出 | 50,980 | — | 0.82 M | 0.05 M | 1.06 |
两点:
- 没有前缀缓存,prefill 随轮数平方增长:第 t 轮要读 个 token,30 轮加起来约 。按 Llama-3.1-70B、H100、50% MFU 算,最”啰嗦”的配置一个会话要约 1 个 GPU·小时的 prefill;有了前缀缓存(第 7 讲),每轮只算新增的部分,变成线性。
- 智能体的负载是”输入远多于输出”的:每轮输出几千 token,却要读几万到十几万 token 的上下文。Kevin-32B 在多轮强化学习里也因此不在后面的轮次里带完整的思维链,只带模型写的简短摘要。
3.4 截断历史 vs 前缀缓存
SWE-agent 的经验是只留最近 5 次观察(更早的换成一行占位符)效果更好。可从推理系统的角度:
| 历史的管理 | 最后一轮的上下文 | prefill(前缀缓存) | 缓存命中 |
|---|---|---|---|
| 全部历史 | 163,500 | 0.16 M | 93% |
| 只留最近 5 次观察 | 67,788 | 0.69 M | 47% |
| 只留最近 2 次观察 | 55,824 | 0.31 M | 69% |
上下文短了 2.4 倍,要 prefill 的 token 却多了 4 倍:每一轮都要把”刚刚变旧”的那次观察换成占位符,从那个位置往后的内容都变了,前缀缓存只能用到它之前。上下文管理的策略要和推理引擎一起设计:例如攒到一定长度一次性把一大段旧历史换成摘要(之后又是只追加的),而不是每轮都改中间。
3.5 护栏
护栏的意义是在昂贵的步骤之前拦住便宜就能发现的错误:编辑前的语法检查(SWE-agent:有 18.0%、没有 15.0%);第 14 讲 §6.6 的”所有分支的配置都能启动”的静态检查;编译前的类型检查;测试和 sanitizer 的超时(KernelBench 对超时和死锁只返回一句”你的内核执行超时”)。每一道护栏都把一次”白跑的工具调用”变成一条精确的反馈。
4. 脚手架、预算与训练 · Scaffolds, budgets and training
4.1 三种脚手架
同样的模型、同样的工具,怎样组织调用?
- 重复采样(并行、独立):一次生成 k 个候选,挑最好的。需要一个能自动判分的评估器(第 14 讲);适合”一次就对的概率不低”的任务。
- 单线迭代(顺序):每一轮带着上一轮的代码和反馈改进。KernelBench 表 2 在同样 10 次调用的预算下比较:6 个”模型 × 级别”的组合里,迭代优化在 5 个上好于重复采样;但它依赖模型会不会用反馈——DeepSeek-R1 能持续利用执行和 profiler 的反馈,DeepSeek-V3 和 Llama-3.1-70B 不一定。
- 树搜索 / 进化(并行 + 顺序):METR 的 KernelAgent 同时跑 8 个尝试,每个新尝试从”按加速比抽中的之前的尝试”或参考实现出发,每题最多 300 次;AlphaEvolve(第 14 讲 §5)用程序数据库和岛屿保持多样性。
4.2 预算
智能体的每一步都有代价:LLM 的 token(输入大头,见 §3.3)、工具的时间(编译几十秒、profiler 更久)、评估用的 GPU。METR 的 KernelAgent 每题每个模型约 20 美元,他们估计 o3-mini 写出的内核平均在 H100 上跑 27 小时就能把这笔钱赚回来。决定”还要不要再试一次”,就是在比较下一次尝试的期望收益和它的成本;§2 的”每秒信息量”是同一个思路在诊断问题上的版本。
4.3 用强化学习学会用工具
Kevin-32B(第 14 讲 §7)把”调用工具 → 读反馈 → 改代码”做成多轮强化学习:每一轮把编译错误或运行时间喂回去;一个回答的奖励是它和之后各轮得分的折扣和(折扣 0.4),这样”给后面的改进打好基础”的一步也能拿到奖励;为了控制上下文,后面的轮次只带模型写的摘要。训练中出现的作弊(照抄参考、try/except 退回 PyTorch、继承参考类)靠更严格的格式检查压住——奖励就是评估器,第 14 讲 §3 的每一条都适用。
5. 推理系统的视角:工具调用期间的 KV cache · The serving view
5.1 智能体负载长什么样
从推理引擎看,一个智能体会话是一串请求:每一轮的上下文是上一轮的超集(前缀复用),输入远多于输出,而且两轮之间有一段暂停——等工具、等人。InferCept 测了六类”增强”(表 1,平均值):
| 类型 | 每次拦截的时长 | 每个请求的拦截次数 | 拦截时的上下文 |
|---|---|---|---|
| 算术(计算器) | 0.09 ms | 3.75 | 1,422 |
| 问答(查维基百科) | 0.69 s | 2.52 | 1,846 |
| 虚拟环境(ALFWorld) | 0.09 s | 28.18 | 2,185 |
| 聊天(等人回复,估计) | 28.6 s | 4.45 | 753 |
| 生成图片(部分估计) | 20.03 s | 6.91 | 1,247 |
| 文字转语音(部分估计) | 17.24 s | 6.91 | 1,251 |
时长跨了五个数量级。写内核的智能体落在中间:读文件亚秒、跑测试几秒、编译和 profiler 几十秒(我们的模拟用的是一个假设的分布:读文件 40%、平均 0.3 s;编译 + 测试 30%、25 s;只跑测试 15%、4 s;profiler 15%、15 s)。
程序级的调度(把一个会话的多次调用当成一个整体来排队)是 CS349D 第 13 讲的主题(Parrot、Autellix);这一节只看暂停期间 KV cache 的去留。
5.2 三种做法与 InferCept 的浪费公式
今天的引擎把每次工具调用当成”请求结束”:KV 丢掉,工具回来后作为一个新请求,整个上下文重新 prefill。InferCept 把三种做法的代价统一成”浪费了多少显存 × 时间”:
- 丢掉重算(Discard):重算期间这 个 token 的显存被占着却没产出新 token;而且重算让这一次迭代变长,其他请求( 个 token 的 KV)都在等:
- 保留(Preserve):暂停期间 KV 一直占着显存:。
- 换出(Swap,同步):换出和换入各一次,期间整批请求都在等:。
InferCept 在混合负载上测到:Discard 有 37–40% 的前向时间花在重算上;Preserve 让”近一半的显存被暂停的请求占着”的时间超过 60%;朴素的同步 Swap 浪费 26% 的 GPU 资源、超过 25% 的时间在等传输。
04 演示 1 按这些公式算了一遍(Llama-3.1-8B、H100、 = 100k token):毫秒级的工具,保留几乎不浪费;几十秒的工具,保留白占几十秒的显存。按 PCIe 5.0(有效 50 GB/s)算,8B 模型每 token 只有 128 KiB,12k 个 token 换出一次只要 31 ms(重算要 368 ms),所以连同步换出在大多数情况下都是最省的——带宽、模型大小、同时换的请求数一变,结论就变。
5.3 盈亏平衡与租雪板问题
保留和丢掉的盈亏平衡点:
工具调用短于 就该保留。04 演示 2:上下文 4,000、其他请求 50,000 时 s;其他请求越多 越长(重算拖慢的人越多)。
问题是事先不知道工具要等多久。这正是经典的租雪板问题:每天租(保留,按时间付)还是一次买下(丢掉重算,一次性付 ),可你不知道要滑几天。确定性的最优策略是”租到累计的租金等于买价,再买”——TTL = :先保留 ,没回来就丢。它最坏只比”事先知道时长的先知”多花一倍(竞争比 2);随机化的 TTL 能降到 。
04 演示 3(上下文 12k,工具时长按上面的分布抽 20,000 次):
| 策略 | 平均浪费 / 先知 | 单次最坏 / 先知 |
|---|---|---|
| 总是保留 | 4.91 | 18.3 |
| 总是丢掉重算 | 1.60 | 92.1 |
| TTL = (3.4 s,不看分布) | 1.86 | 2.00 |
| TTL = 0.6 s(按这个分布挑的最优值) | 1.22 | 7.1 |
给的是最坏情况的保证;平均意义上它不一定最好(这个分布里 54% 的调用都超过 ,TTL 在它们身上付了两倍)。知道分布就能做得更好:这个分布有两个峰,TTL 设成 0.6 s 时快的保留、慢的马上丢。所以 InferCept 按工具类型离线测时长;没有测量时,它用”已经等了多久”()当估计——等得越久越该放弃,和 TTL 是同一个思路——论文报告达到了先知 93% 的性能。
5.4 InferCept 与 Continuum
InferCept 把三种做法都改进了,再动态组合:
- 按层流水的换出:换第 层的同时传第 层,第 层的显存已经给别人用;把一个序列的换入换出拆到好几个迭代里,每次只换”能藏在计算后面”的量(swap budget:一次迭代的时间 × PCIe 带宽 / 。04 演示 4:批 64 的 decode 迭代约 19 ms,能藏下约 7,400 个 token)。
- 分块重算:利用 decode 用不满的算力(第 5 讲),每个迭代只重算一块,不让整批等。
- 最小浪费的调度:每个被拦截的请求取 ,按浪费从大到小分配换出的预算,其余的保留或分块重算。
在 GPT-J-6B、Vicuna-13B、Llama3-70B 上,相对 vLLM 能承受 1.6–2 倍的请求速率(同样的归一化延迟),每秒完成的请求多一倍以上。
Continuum(Berkeley 等,2025–2026)关注多轮的智能体负载(SWE-bench、BFCL、OpenHands):预测工具调用的时长,给产生工具调用的请求的 KV 设一个 TTL——按”重算或重新加载的代价”和”被挤出显存后的排队延迟”来定;TTL 到了还没回来就释放;配合程序级的先来先服务调度。摘要报告平均作业完成时间改善 8 倍以上。它点出了 InferCept 公式漏掉的一项:排队。
5.5 我们的模拟
05 脚本:很多个智能体会话同时跑在一块 H100 上(Llama-3.1-8B,KV 的容量约 427k token = 55.9 GB);每个会话 3,000 token 的提示、12 轮、每轮生成 150 token、工具输出 600 token;会话结束马上开始下一个(闭环)。每轮在工具上等约 10.5 秒,一个会话大约 115 秒在等工具——大部分时间它的 KV 是冷的。7 种策略(每小时完成的会话数):
| 策略 | 32 个会话 | 96 个会话 | 160 个会话 |
|---|---|---|---|
| 丢掉重算 | 641 | 1,073 | 882 |
| 前缀缓存(释放但可被挤掉) | 767 | 1,541 | 904 |
| 一直保留 | 767 | 342 | 0 |
| TTL = (InferCept 公式) | 767 | 1,271 | 0 |
| TTL,有人排队就不保留 | 767 | 1,422 | 896 |
| 同步换到 CPU | 745 | 1,778 | 2,164 |
| 异步换到 CPU | 763 | 2,063 | 3,010 |
- 显存够时(32 个会话),保留、前缀缓存、两种 TTL 完全一样;丢掉重算少 16%,它的 prefill 里 88% 都在重算。“一直保留”平均有 24.9 GB 显存被暂停的会话占着——将近一半,和 InferCept 的观察一致。
- 显存不够时(96、160 个),“一直保留”掉下悬崖:显存被暂停的会话钉满,排在队首的会话进不来,引擎经常空转,直到什么都跑不动时才被迫丢掉一些。按 InferCept 公式算的 TTL 也被拖累:其他请求越多, 越长,越倾向于保留——可那正是显存最紧的时候。把”排队”算进来(有人排队就不保留)好一些,但仍不如从不保留、只留可被挤掉的缓存。
- 换到 CPU 在 PCIe 5.0 的带宽下最好:CPU 内存相当于一块大得多的 KV 存储。带宽降到 1/4 时异步的几乎不受影响(2,063 → 2,030),同步的掉到 1,112;降到 1/16 时异步的也撑不住(616,不如前缀缓存的 1,541)。
演示 4 把工具全换成 0.3 秒的快工具:48 个会话时,保留(2,588)和 TTL(2,804)明显好于前缀缓存(1,915)——缓存刚被挤掉,它的主人 0.3 秒后就回来了,只好整个重算,这正是”短拦截应该保留”;64 个会话时 KV 总量超过显存,保留又崩了(230)。
没有一种”保留 / 丢掉”的规则在所有负载下都对:工具时长的分布、显存的余量、PCIe 的带宽一起决定答案。这就是 InferCept(按请求算浪费、动态选择)和 Continuum(预测时长、把排队的代价算进 TTL)在做的事,也是第 7 讲 Mooncake”把 KV 当成可以在 HBM、DRAM、SSD 之间搬动的数据”的延续。(模拟的简化:每步时间是 roofline 模型;每轮的显存一次预留;先来先服务、不抢占正在运行的会话;不模拟会话之间的前缀共享。)
6. 工具使用的安全与可靠性 · Safety and reliability
智能体执行的是模型生成的代码,调用的是有副作用的工具:
- 沙箱:在隔离的容器 / 虚拟机里编译和运行,限制文件系统、网络和设备的访问;每次评估用干净的环境(第 14 讲:评估器本身也会被攻击)。
- 资源限制:超时、内存上限、GPU 时间配额;死循环和死锁要能被杀掉,并给出一条清楚的反馈。
- 工具的输出是不可信的输入:网页、文件、日志里的文字可能包含”指令”(提示词注入)。智能体应把它们当数据,不当命令;有副作用的动作(提交代码、发消息、删除文件)要另外确认。
- 权限最小化:只给完成任务需要的工具和权限;记录每一次调用,便于审计和复盘。
7. 小结 · Summary
- 智能体 = LLM + 工具 + 循环。ReAct 让思考和行动交替;工具是有签名的函数,模型输出结构化的调用,系统执行、把观察写回上下文。
- 反馈的信息量可以用比特衡量。只说对错的测试一次至多 1 比特;给出错误模式的测试多一倍以上;构造的输入、不整齐的形状、sanitizer 回答别的工具回答不了的问题。“下一步调用哪个工具”是信息和时间的权衡;工具集决定了能发现什么。
- 接口是为模型设计的(SWE-agent):动作简单紧凑、反馈有信息量但简洁、有护栏。原样打印的工具输出可以比整个上下文窗口还长;摘要几十个 token 就够。
- 上下文的账:没有前缀缓存时 prefill 随轮数平方增长;智能体负载输入远多于输出;“只留最近几次观察”会让前缀缓存失效——上下文管理要和推理引擎一起设计。
- 脚手架:重复采样、单线迭代、树搜索 / 进化,各有适用的场合;用多轮强化学习训练工具使用时,奖励就是评估器。
- 工具调用期间的 KV:保留、丢掉重算、换出各有代价(InferCept 的公式);保留和丢掉的取舍是租雪板问题,TTL = 保证不超过先知的两倍,知道时长分布能做得更好;显存紧张时还要算排队(Continuum)。模拟里:显存够时”保留”最好,不够时它掉下悬崖;PCIe 够快时换到 CPU 最好。
8. 常见误区 · Common pitfalls
- “测试通过了,内核就是对的。” 在 128³ 这种整齐的形状上,三种尾巴相关的 bug 完全看不出来;要用不整齐的形状、构造的输入、sanitizer。
- “工具输出越详细越好。” 详细不等于有信息量:16,000 行的错误清单(17 万 token)不如一行模式摘要。上下文是有限、要付钱的资源。
- “智能体只是一个模型的事。” 同样的模型,换一套工具和接口,成功率差很多(SWE-agent 的消融、§2 的 P1–P7)。
- “截断历史一定省钱。” 每轮改中间的内容会让前缀缓存失效:上下文短了,prefill 反而多了。
- “智能体负载和聊天差不多。” 它输入远多于输出、轮数多、有大量暂停;每轮重新 prefill 整个历史的代价随轮数平方增长。
- “工具调用时保留 KV 总是对的。” 显存够时是;显存不够时,钉住的 KV 让排队的请求进不来,吞吐会掉下悬崖。
- “按 InferCept 公式算出的 T 就是最优的 TTL。”* 只保证最坏情况不超过 2 倍;知道时长分布能做得更好;公式也没算排队的代价。
- “换到 CPU 总是最好。” 依赖 PCIe 带宽、同时换的请求数、模型的 KV 大小和 CPU 内存;同步地换会让整个引擎停下来等。
9. 练习 · Exercises
练习 1(一次测试的信息量) 8 个同样可能的 bug,一个测试在其中 3 个上 FAIL、5 个上 PASS(确定性的)。它的信息增益是多少比特?
答案:后验熵:FAIL 时 ,PASS 时 。信息增益 比特(一个两结果的测试,信息增益就是结果分布的熵)。
练习 2(每秒信息量) 工具 A 2 比特、3 秒;工具 B 3 比特、120 秒。按”每秒信息量”该先用哪个?什么情况下应该先用 B?
答案:A 0.67 比特/秒,B 0.025 比特/秒,先用 A。如果 A 能带来的信息已经用完(剩下的不确定性只有 B 能分开,例如竞争),或者工具的耗时不是瓶颈(比如可以并行调用),才轮到 B。
练习 3(下界) 8 个同样可能的 bug,只用”对 / 错”的测试至少要几次?如果一个测试有 4 种可能的结果呢?
答案:至少 次;4 种结果时一次至多 2 比特,至少 次。这都是下界,前提是每次都能把剩下的候选平均分开。
练习 4(另一种分开 bug 的办法) 不用单位矩阵,能不能从随机输入的误差矩阵里分开”K 循环少一块”和”B 下标写反”?提示:看误差矩阵的秩。
答案:少算最后一块时,误差是 ,秩至多 32;下标写反时误差是 ,几乎满秩。在 128³ 上实测秩分别是 32 和 126。一个报告”误差矩阵的秩”的测试工具就能分开它们——好的工具接口把调试经验变成一条反馈。
练习 5(上下文的账) 提示 个 token,之后每轮新增 个 token,共 轮。没有前缀缓存时总共 prefill 多少?有前缀缓存时呢?代入 、、。
答案:无缓存 M;有缓存约 。和 03 演示 2 的第一行一致。
练习 6(KV 有多大、搬多久) Llama-3.1-8B、BF16,12,000 个 token 的 KV 多大?在 50 GB/s 的 PCIe 上单向要多久?和重算一遍比呢?
答案: GB;31 ms;04 脚本里 s,搬比算快 10 倍以上。
练习 7(盈亏平衡) 、、 s。 是多少?一次 0.7 秒的问答该保留还是丢掉?
答案: s;0.7 s < ,保留。
练习 8(为什么是 T*) 如果把 TTL 设成 或 ,最坏情况相对先知是几倍?
答案:TTL = :时长刚超过 时付 ,先知付约 ,3 倍。TTL = :时长刚超过 时付 ,先知付 ,也是 3 倍。 是让最坏情况最小的选择(2 倍)。
练习 9(暂停的会话占多少显存) 32 个会话,每个会话从开始到结束约 132 秒(05 演示 2),其中约 115 秒在等工具;平均上下文按 7,500 token 算。“一直保留”时,平均有多少显存被暂停的会话占着?
答案:约 GB;05 演示 2 量到 24.9 GB(暂停发生在每一轮结束时,前几轮的上下文比平均短,所以实际略小)。显存给 KV 的只有约 56 GB,将近一半。
练习 10(截断让缓存失效) 每轮模型输出 1,500、工具输出 4,000 token,只留最近 5 次观察。粗略估计每一轮要 prefill 多少 token?和全部历史比呢?
答案:刚变旧的那次观察之后还有约 5 轮的内容要重新 prefill:约 token;全部历史只要新增的 。03 演示 3 里 30 轮合计 0.69 M vs 0.16 M,平均每轮约 23k vs 5k。
练习 11(swap budget) 一次 decode 迭代 19 ms,PCIe 有效带宽 50 GB/s,每 token 128 KiB。每次迭代能”藏住”多少 token 的换入换出?换回一个 12k token 的会话要几个迭代?
答案: token;12k 需要 2 个迭代。
练习 12(值不值得) 一个智能体花 20 美元写出一个内核,能让一块 H100 的某个负载快 10%。H100 每小时按 2 美元算,这个内核要跑多久才回本?
答案:每小时省 美元, 小时。(METR 用真实的数字估计 o3-mini 的内核约 27 个 H100·小时回本;只是量级上的对照。)
10. 延伸阅读 · Further reading
智能体与工具
- S. Yao, J. Zhao, D. Yu, N. Du, I. Shafran, K. Narasimhan, Y. Cao, ReAct: Synergizing Reasoning and Acting in Language Models, ICLR 2023. https://arxiv.org/abs/2210.03629
- T. Schick et al., Toolformer: Language Models Can Teach Themselves to Use Tools, NeurIPS 2023. https://arxiv.org/abs/2302.04761
- J. Yang, C. E. Jimenez, A. Wettig, K. Lieret, S. Yao, K. Narasimhan, O. Press, SWE-agent: Agent-Computer Interfaces Enable Automated Software Engineering, NeurIPS 2024. https://arxiv.org/abs/2405.15793
- Anthropic, Model Context Protocol(2024-11)的规范与文档。
- KernelBench 论文 §5.1(迭代优化与反馈)、Cognition 的 Kevin-32B 博客、METR 的 Measuring Automated Kernel Engineering(第 14 讲的延伸阅读)。
推理系统
- R. Abhyankar, Z. He, V. Srivatsa, H. Zhang, Y. Zhang, InferCept: Efficient Intercept Support for Augmented Large Language Model Inference, ICML 2024. https://arxiv.org/abs/2402.01869
- H. Li et al., Continuum: Efficient and Robust Multi-Turn LLM Agent Scheduling with KV Cache Time-to-Live, 2025–2026. https://arxiv.org/abs/2511.02230
- A. R. Karlin, M. S. Manasse, L. Rudolph, D. D. Sleator, Competitive Snoopy Caching, 1988(租雪板问题与竞争分析的经典来源之一)。
本仓库
- CS349D 第 13 讲:智能体工作流的程序级调度(Parrot、Autellix)。
- 第 7 讲:PagedAttention、前缀缓存、Mooncake 的分层 KV 存储。
- 第 14 讲:评估器、作弊与测量的陷阱——智能体的”奖励”从哪里来。
11. 下一讲预告 · Next lecture
最后一讲把整门课串起来:一个 token 从模型、精度、切分、服务系统、KV 管理、硬件、内核、编译器一路走下来,每一层的瓶颈和对策是什么;然后用前面各讲的模块搭一个端到端的部署规划器——给定模型、硬件、SLO 和负载,选精度、张量并行度、是否解耦、要几块 GPU——并用第 5 讲的模拟器检验它;最后讲怎样做一个推理系统的课程项目(选题、基线、测量、写报告)和这个领域的开放问题。