AI 产品效果上不去,先别急着训模型:Hamel Husain 整理的 13 场实战分享

评估是地基,先改检索和上下文,再改系统,最后才训练模型。13 场分享覆盖错误分析、模型级联、多向量检索、OCR 选型、推理延迟、Agent 沙箱和后训练,每场都有能照着做的方法和实测数字。

Hamel Husain(做大模型评估的头部实践者,AI Evals 课主讲之一)办了 13 场「AI 产品工程」分享,他把 9.5 小时压成 20 分钟能读完的笔记。这篇解读把 13 篇完整讲一遍;每场分享的完整录像都放在 YouTube 或 Maven 上,各节末尾附了观看链接。

把大模型做成好用的产品,能调的地方很多。这组分享给出的先后顺序是:

  1. 评估是地基。评估就是一套固定的测试样例,加上明确的判分方法。能准确衡量系统表现,后面每一次改动才知道是变好还是变坏。
  2. 先改检索和上下文。这是最容易见效的地方:先把喂给模型的材料弄对。
  3. 再改系统和外壳。外壳(harness)指包在模型外面、负责调工具和管流程的那层程序;这一层还包括推理速度、硬件成本和运行环境。
  4. 后训练放在最后。前面几项都试过了,才考虑改模型本身。改评估、检索和外壳,动的是代码、提示词和配置,花钱少、见效快,改坏了也容易退回去;后训练要准备数据、花算力和时间。没有可靠的评估,训练完也没法判断模型是真变好了,还是在别处变差了。

遇到具体问题,可以直接跳到对应小节:

  • 效果不稳定、不知道具体错在哪:错误分析、教育平台教训
  • 分类任务调用大模型开销太大:模型级联
  • 数据分析 Agent 查库频频出错:数据分析 Agent
  • RAG 检索丢失关键细节或抓不准:多向量检索、选嵌入模型、搜索 Agent
  • PDF 和扫描件排版解析错乱:选 OCR
  • 让 Agent 改写长文总丢失关键决定:Subtext
  • 回答生成太慢、用户等太久:推理延迟
  • 打算部署开源模型却算不清显存:开源模型经济学
  • Agent 跑代码存在安全隐患或意外崩溃:给 Agent 配沙箱
  • 想微调模型却吃不准是否有必要:后训练

评估:先弄清错在哪

这一层解决「怎么知道改动有没有用」。每次调了提示词、改了业务逻辑,就把固定的测试样例跑一遍,用分数确认系统是进步了还是倒退了。下面四节讲的是:好的评测该长什么样、怎么先找到真实的错误、怎么在守住准确率的同时省钱,以及一个团队真实踩过的坑。

数据分析 Agent:错得最多的是计划,不是找错数据

数据分析 Agent 连上公司的数据库,自己写查询或代码,回答「哪一批用户流失率最高」这类业务问题。这类问题原本由数据分析师来答,在知识工作里占比很大,但能好好衡量 Agent 答得怎么样的评测基准很少。

Shreya Shankar 介绍了数据 Agent 基准 Data Agent Benchmark(DAB)。真实的数据仓库很乱,DAB 刻意还原了其中四类麻烦:

  • 多数据库整合:单次任务所需的数据分散在至少两种数据库系统里。
  • 关联键格式不规范:比如(示意)同一个用户,在用户表里记作 USER_1001,在订单表里却是 usr-1001 或 1001,Agent 得自己判断它们是同一个人,才能把两张表连起来。
  • 非结构化文本转换:关键数值埋在自由填写的文字里,要先从文字里提取出来。
  • 领域知识:要懂这个行业的专门概念,才能把问题答对。

论文对比了五类已有基准(Text-to-SQL、表格问答、数据科学与工程、语义查询处理、通用工具调用)。只有 DAB 四项全部覆盖;前两项,其他五类基准都没有覆盖。

DAB 是通用基准,回答的问题和你为自己产品做的评估不同,但它的失败分析对做数据 Agent 很有用:Agent 错得最多的地方是计划和实现本身写错了,而不是选错了数据。 Agent 常常开工时先写好一份计划,做到一半数据已经和计划里的假设对不上,它还是照原计划往下做。调试自己的数据 Agent 时,这类失败值得留意。DAB 公开接受提交,结果在 DAB 排行榜上,团队会在新模型发布时刷新,值得关注。

Shreya 讲 DAB 的完整分享可以在 YouTube 上观看。

模型级联:有把握的交给小模型,没把握的再交大模型

许多大模型落地场景本质上是分类任务,例如客服工单按紧急程度打标签、内容违规判定等。大模型用起来太方便,很容易什么请求都丢给最大、最贵的模型;可调用量一大,成本就很高。

Shreya 介绍的 BARGAIN(论文 arXiv:2509.02896)目标是把大部分分类工作挪给小模型,同时守住你设定的准确率。白板上演示的场景是给工单判「是/否」:直接用 gpt-5.6 sol 判,准但太贵。BARGAIN 的做法是让便宜的 gpt-5-nano 先判;小模型给出答案的同时,还给出一个把握度(0 到 1 之间的数,表示它对这个答案有多确定,比如 0.98 是很确定,0.55 是差不多靠猜)。再设一个阈值 τ:把握度高于 τ,说明小模型够确定,直接用它的答案;不高于 τ,就把这条交给大模型重新判。

阈值 τ 调高,更多样本会转给大模型,结果更准,也更贵;调低,更多样本留给小模型,省钱,但小模型判错的会更多。要注意,这里的准确率算的是最终结果和大模型判断一致的比例,不是和人工标注一致的比例。模型级联的目标是用更低成本得到和大模型一样的答案;大模型本身判得对不对,是另一项评估要回答的问题。

上线前,用一批样本定出阈值 τ,步骤是:

  1. 抽约 500 条样本,让小模型跑一遍,记下每条的答案和把握度。
  2. 让大模型把这 500 条也判一遍,作为对照。
  3. 用同一批样本检查:小模型的把握度越高,是不是真的越常和大模型一致。如果小模型很「确定」的时候也经常和大模型判得不一样,把握度就不可靠,按把握度分流这个办法就不成立。
  4. 把样本里出现过的每个把握度都当成阈值试一遍,模拟分流后的一致率和成本,在达到目标(比如和大模型 95% 一致)的阈值里,选成本最低的那个。

概念示意 · 30 条虚构样本

拖动阈值:多少条交给大模型,结果还剩多少和大模型一致

每个格子是一条样本,按小模型的把握度从低到高排列。✓ 表示小模型的答案和大模型一致,✗ 表示不一致。把握度不高于阈值的样本改交大模型,结果按大模型算,一定一致。

左右拖动或用方向键,阈值只取样本里出现过的把握度。

  1. 把握度 0.52,小模型与大模型不一致
  2. 把握度 0.55,小模型与大模型一致
  3. 把握度 0.57,小模型与大模型不一致
  4. 把握度 0.60,小模型与大模型不一致
  5. 把握度 0.62,小模型与大模型一致
  6. 把握度 0.64,小模型与大模型一致
  7. 把握度 0.66,小模型与大模型不一致
  8. 把握度 0.68,小模型与大模型一致
  9. 把握度 0.70,小模型与大模型一致
  10. 把握度 0.72,小模型与大模型不一致
  11. 把握度 0.74,小模型与大模型一致
  12. 把握度 0.76,小模型与大模型一致
  13. 把握度 0.78,小模型与大模型一致
  14. 把握度 0.80,小模型与大模型不一致
  15. 把握度 0.82,小模型与大模型一致
  16. 把握度 0.84,小模型与大模型一致
  17. 把握度 0.85,小模型与大模型一致
  18. 把握度 0.87,小模型与大模型一致
  19. 把握度 0.88,小模型与大模型不一致
  20. 把握度 0.90,小模型与大模型一致
  21. 把握度 0.91,小模型与大模型一致
  22. 把握度 0.92,小模型与大模型一致
  23. 把握度 0.93,小模型与大模型一致
  24. 把握度 0.94,小模型与大模型一致
  25. 把握度 0.95,小模型与大模型一致
  26. 把握度 0.96,小模型与大模型一致
  27. 把握度 0.97,小模型与大模型一致
  28. 把握度 0.98,小模型与大模型一致
  29. 把握度 0.99,小模型与大模型一致
  30. 把握度 1.00,小模型与大模型一致

灰底已改交大模型 白底直接采用小模型答案

交给大模型
10 / 30 条(33%)
与大模型一致
28 / 30(93.3%)

按原方法自动找阈值:选一个目标一致率,逐个试样本里的把握度,停在交给大模型最少、仍然达标的那个

虚构样本里:目标 90% 时 τ = 0.66,交大模型 7 条;95% 时 τ = 0.80,交 14 条;100% 时 τ = 0.88,交 19 条。

数据是为演示编的,只用来说明「阈值越高越准、也越贵」和选阈值的步骤。真实做法抽约 500 条,由大模型标注;这里的「一致率」指和大模型一致,不是和人工标注一致。

论文在 8 个数据集上测试,报告 BARGAIN 比其他同类方法多省下的成本最高达 86%。

后续的 Task Cascades 论文又加了三项优化:把提示词改写成便宜模型也能答的简化问题;每份文档只读最相关的几段,不读全文,少花输入 token;在多种级联组合里搜索,找出达到准确率目标、成本最低的那一串。和 BARGAIN 这类级联相比,平均再省 48.5% 的成本。

完整讲解见 Shreya 讲模型级联的录像。

错误分析:先看数据,再写评分标准

系统表现不如预期时,常见的错误是还没看任何数据,就先写一套评分细则(rubric)。凭想象列出来的问题,实际运行里未必出现;真正让任务失败的问题,反而可能没列进去。

应该先做错误分析:一条条读实际的运行记录(trace,模型完成一次任务时每一步的输入、输出和工具调用),把反复出现的失误归成一份失败类型分类表,比如下一节教案助手「一次写出两个学习目标」就是一类失败。这是评估里最费人工的环节。Shreya 介绍的 Error Discovery 技能能省掉很多麻烦:你边标注,它边更新失败类型分类表;每发现一种新模式,还会回头检查之前的记录里有没有同类。它用了主动学习(active learning)的思路,说白了就是不随机抽记录给你看,而是根据你已经标过的内容,优先挑最可能暴露新问题、或者最需要人来判断的记录。

它的工作流程分三步:

  1. 搭一个本地审阅页面:用 Python 标准库起一个本地服务,配一个单文件网页,不用装任何依赖。页面用颜色、间距和字体区分角色、层级和正文与代码,让很长的运行记录读得下去。
  2. 挑第一批样本:提取特征,把记录聚成 6–10 组,挑 15–25 条尽量不同的样本开始看,其中 60%–70% 是各组的代表,30%–40% 随机抽取,并去掉重复。
  3. 人和 Agent 轮流推进:人在页面上读样本、高亮文字、写备注,接受或驳回 Agent 的建议;Agent 根据这些标注更新失败类型分类表,再挑下一批样本推回页面。

图解 · Error Discovery 的审阅循环

人负责判断,Agent 负责归类和挑样本

  1. 人 读样本、做标注 高亮文字、写备注,接受或驳回 Agent 的建议
  2. Agent 整理失败类型 根据标注更新失败类型分类表
  3. Agent 挑下一批样本 广度模式:找没见过的失败类型
    深度模式:每种类型派一个子 Agent 找同类

↺ 新样本推回审阅页面,回到第一步,循环往复

Agent 有两种工作方式:广度模式用来发现还没见过的失败类型,会提议新的样本;深度模式把某一种失败类型挖透,每种类型派一个子 Agent,去更多记录里找同类实例。最后得到一份失败类型分类表、一组审过的样本,以及覆盖和收敛情况的总结。

这个技能具体怎么用,可以看 Shreya 的 Error Discovery 演示录像。

教育平台的教训:两个标注员的一致率比随机还低

Lucas Machado Rocha 用评估来优化巴西非营利教育平台 Nova Escola(月活约 100 万用户,服务 20 万名教师)的教案生成助手。他的团队犯过两个错:

第一,没做错误分析就先写评分标准。结果标准里的大部分条目,在实际输出里从来没出过错,团队白白标注了一批用不上的数据。第二,为一个改提示词就能解决的问题专门做了评估器。助手有时会写出两个学习目标,而正确格式只该有一个;团队改了提示词,问题就没了。什么时候值得为一种失败做自动评估器,Hamel 在评估 FAQ 里专门讨论过这个取舍。

标注时还暴露出另一个问题:团队请了两位标注员分别给同一批教案打分,两人意见一致的比例比随机乱标还低。两个人认真看同一份教案,结论却几乎对不上,说明问题不在标注员,而在团队没把「什么算好教案」定义清楚,每个人按自己的理解在打分。团队和教学专家一起重写了评分标准,重新标注了数据。

通过错误分析找出失败类型之后,Lucas 用 eval skills 自动完成后面的一部分工作,包括编写评判器(让大模型按评分标准打分的程序),以及拿人工标注去核对评判器打得准不准。评判器本身也是模型,不核对就不知道它给的分能不能信。上线后,他们每天拿 2% 的生产流量跑一遍这套评估,尽早发现效果倒退,比如某次改提示词或换模型后,原本正常的输出变差了。

Lucas 从头到尾的完整做法,见 他的分享录像。

检索与上下文:多数时候问题出在喂给模型的材料

模型答错,很多时候不是模型不够聪明,而是喂给它的材料不对:没找到、缺细节,或者格式已经乱了。这一层是最容易见效的地方,下面五节分别讲怎么让检索保留细节、怎么评估和诊断搜索 Agent、怎么选嵌入模型、怎么把 PDF 读对,以及改写文档时怎么不丢关键信息。

多向量检索:每个 token 都留一个向量,再想办法把成本压下来

常见的向量检索把每篇文档压成一个向量(一串表示文字意思的数字),检索时比较问题向量和文档向量有多接近。打个比方,这就像把一整本小说缩成一句话简介:大意还在,具体的数字、人名、参数这些细节就被抹平了,这就是信息瓶颈。

Marek Galovic 讲的多向量检索绕开了这个瓶颈:问题和文档里的每个 token 都各留一个向量。打分方法叫 MaxSim:问题里的每个 token,去文档的所有 token 里找意思最接近的那一个,记下这个最高分;再把问题里所有 token 的最高分加起来,就是这篇文档的得分。这样问题里的每个词都能在文档里找到自己的对应,细节不会在压缩时丢掉。

概念示意 · 分数为虚构

MaxSim 怎么给一篇文档打分

问题「电池 续航 多久」有 3 个 token,文档「这款 手机 电量 能撑 两天」有 5 个。表里每格是一对 token 的相似度;每一列只取最高的那格(加粗高亮),三列最高分相加就是文档得分。

文档 token电池续航多久
这款0.050.050.10
手机0.350.300.05
电量0.82(本列最高)0.600.15
能撑0.300.71(本列最高)0.35
两天0.100.400.66(本列最高)
本列最高0.820.710.66

文档得分 = 0.82 + 0.71 + 0.66 = 2.19

对照单向量检索:整个问题一个向量、整篇文档一个向量,只算一次相似度。MaxSim 让「续航」自己去找「能撑」,「多久」自己去找「两天」,细节不会在压缩时被抹平;代价是每个 token 的向量都要存、都要比。

Marek 的幻灯片提到,这种方法在跨领域检索和长文本检索上达到了目前最好的效果。问题是贵:文档里每个 token 的向量都要存下来,比较时问题的每个 token 都要和文档的每个 token 比一次,比较次数随两边长度相乘增长。Marek 估算,MaxSim 的计算量约是单次向量点积的 2000 倍,存储要多 10 到 100 倍,所以它长期停留在论文里。

要让它能大规模用起来,Marek 讲的最主要的优化是 SMVE 。它的核心思路是先用便宜的索引筛掉大部分文档,只对剩下的少数做精确 MaxSim。具体做法是把每个 token 的向量投影到一大组随机方向上,只保留数值最大的 8 个;一篇文档所有 token 的结果加起来,得到一个大部分位置为零的向量,放进倒排索引。倒排索引类似书后的关键词索引:按条目直接翻到包含它的页,没出现这个词的页根本不用看。检索时,和问题没有任何共同条目的文档直接跳过,昂贵的 MaxSim 只在剩下的少数候选文档上计算。

Marek 报告,在十亿篇文档的规模下,p99 延迟低于 100 毫秒,也就是 99% 的查询能在 100 毫秒内返回。他的团队还发布了 Iso-ModernColBERT:GTE-ModernColBERT-v1 的修正版,向量的几何分布能和 SMVE 的随机投影配合;用 bf16 精度运行快约 3 倍,排序质量几乎不降。

Marek 讲多向量检索的完整分享在 Maven 上。

搜索 Agent:瓶颈常常在检索器

搜索 Agent 不是搜一次就回答,而是自己决定搜什么、读结果、判断还缺什么、再搜,几轮之后才给答案。做这类 Agent 常见三个难题:没有合适的评测题,不知道它在哪一步走偏,以及搞不清问题出在检索还是模型。

Nandan Thakur 的分享介绍了三个项目,正好对应这三个难题。

ORBIT:不花钱合成需要多轮搜索的评测题。没有标注好的评测题时,常见做法是让模型照着文档出题,但这样出的题往往搜一次就能答对。ORBIT 反过来出题:描述一个对象的几个特征,但不说出它的名字,并检查题目是否难到必须搜好几轮才能答对。比如(示意,非真实题目):「一位作家出生在海边小城,第一部小说拿过新人奖,后来转行拍电影,他拍的第二部电影叫什么?」要答对,得先搜出这个人是谁,再搜他的作品。

合成的题可能有错,所以每道题都要验证:搜索 Agent 必须拿源文档逐条核对题目里的说法,另一个独立的评判模型只看这些文档,也得推出同一个答案。ORBIT 流程生成了 2 万道验证过的题,搜索 API 和付费大模型的费用都是零。整个流程完全合成,没有标注数据的团队也能用,可以把同样的做法用在自己的语料上。

Hawkeye:看清每次运行是怎么展开的。 Hawkeye 是一个可视化分析界面,把搜索 Agent 每次运行是怎么一步步展开的摊开给你看。幻灯片上的一组数据:781 次运行里答对 444 次、答错 46 次、没给出答案 291 次;答对的运行平均搜索 18.9 轮,没给出答案的平均 42.2 轮。也就是说,搜了很多轮还没结果的运行,多半是找不到答案了。有了这样的视图,就能给自己的 Agent 定一个合适的最大搜索轮数,到了上限就停。Hawkeye 的论文还在审稿,暂时没有公开工具,可以让你常用的编程 Agent 照这个思路帮你做一个类似的视图。

实测数据 · Hawkeye 分析幻灯片

答对的运行,搜索轮数少得多

781 次运行的结果(准确率 56.85%)

答对 444 次 · 答错 46 次 · 没给出答案 291 次

平均搜索轮数

答对的运行
18.9 轮
没给出答案的运行
42.2 轮

横条按 0–50 轮的比例绘制。

BrowseComp-Plus:给对文档,模型几乎全答对。 BrowseComp-Plus 是 OpenAI BrowseComp 的可复现版本。BrowseComp 里都是难找的事实题:答案很短、容易核对,但要搜很多次才找得到。它的重要发现是:限制准确率的主要是检索器,而不是模型。做法是让同一个 GPT-4.1 在两种条件下答题:自己用 BM25(按关键词出现的次数给文档打分的传统检索方法)找资料,只答对 14.6%;直接拿到含答案的文档,答对 93.5%。模型没换,差距几乎全来自有没有找到对的文档。

Nandan 讲搜索 Agent 的完整分享在 Maven 上。

选嵌入模型:别只看排行榜,微调常被忽略

嵌入模型(embedding model)在 RAG 里负责把文档片段和问题都变成向量,检索找得准不准,很大程度取决于它。挑模型时常先看 MTEB 排行榜,但它排的是质量,几乎不反映成本。

Radu Gheorghe 接着讲了几项值得考虑的取舍。先说「精度」:它指计算机用多少位(bit)存一个数,位数越少,占的空间越小、算得越快,但数值会粗糙一点,就像把 3.14159 记成 3.14。

  • 模型量化(模型自身的数用多高精度存,主要影响计算速度):在 CPU 上,INT8(8 位)模型比 FP32(32 位)快约 3 倍,质量基本保住;在 GPU 上改用 FP16。
  • 向量精度(输出的向量用多高精度存,主要影响存储):从 FP32 改成二值,每个数从 32 位变成只存 0 或 1 的 1 位,存储降到原来的 1/32;改成 bfloat16(16 位),存储减半,质量没有可测到的损失。
  • Matryoshka 截断模型:这类俄罗斯套娃式模型训练时让最重要的信息集中在向量靠前的维度,所以可以直接丢掉靠后的维度,质量大部分保留,既省存储又加快搜索。
  • 在你自己数据上的表现:排行榜看不出来,一定要拿自己的数据测,专业领域里排名会大幅变动。常见的短板是多语言支持和上下文长度。

现成模型在你的领域里还是不够好,微调嵌入模型是常被忽略的办法:它比微调大模型容易得多,效果提升往往还更大。Radu 做了开源的无代码微调工具 VespaEmbed,Apache 2.0 许可:从 Hugging Face 选一个基础模型,上传文本对数据,每一对是一个查询(anchor)加上它应该找到的那段文字(positive),比如界面示例里 anchor 是「What is machine learning?」,positive 是一段解释机器学习的文字;再选一个损失函数,点训练。也可以直接在 Hugging Face 上试用。

VespaEmbed 新建微调项目的设置界面
VespaEmbed 新建项目:任务选文本对(anchor 是查询,positive 是应该被找到的文本),再指定 Hugging Face 上的基础模型。

Radu 讲选嵌入模型的完整分享在 Maven 上,他讲怎么选、怎么微调嵌入模型的幻灯片也公开了。

选 OCR:多数团队该用接口,模型要按 PDF 的坑来挑

要让模型读 PDF 和扫描件,第一步是 OCR,把页面上的文字和结构识别出来。Joe Barrow 用一个决策矩阵帮你选,先回答两个问题:只要纯文字块,还是要完整的文档结构(标题、表格、单元格边界)?调用托管接口,还是自己部署模型?

两个问题交叉,得到四类选择:

  • 大云厂商接口(AWS Textract、Google Cloud Vision):只出文字块,便宜(每千页 0.60–1.50 美元),能给出每个词在页面上的位置框,产品里要在原文上高亮出处时用得上。
  • 文档处理创业公司的接口(Reducto、Datalab、Extend):出完整结构,功能齐全,但贵(每千页 5–20 美元)。
  • 开源流水线(Tesseract、PaddleOCR):只出文字块,便宜、快,但能力范围窄。
  • 开源视觉语言模型(LightOnOCR、Chandra、Docling):出完整结构,需要 GPU 运行,功能基本齐全。

图解 · Joe Barrow 的 OCR 决策矩阵

两个问题,四类选择

横向看要什么输出,纵向看怎么部署。价格为每千页。

调用接口 · 只要文字块

大云厂商

AWS Textract、Google Cloud Vision

便宜,有词级位置框

$0.60–1.50

调用接口 · 要完整结构

文档处理创业公司

Reducto、Datalab、Extend

贵,但功能齐全

$5–20

自己部署 · 只要文字块

开源流水线

Tesseract、PaddleOCR

快、便宜,能力窄

自己部署 · 要完整结构

开源视觉语言模型

LightOnOCR、Chandra、Docling

要 GPU,功能基本齐全

Joe 的建议是:只有大约 5% 的团队应该自己部署。如果你的重心在做产品,通常用接口更好。

就算用接口,选哪个模型也很重要,因为 PDF 会以各种意想不到的方式把提取器搞坏。Joe 点出了这几个:

  • TeX 编译出的 PDF 里没有空格字符:TeX(论文常用的排版系统)是把每个字形放到指定坐标上排版的,文件里并没有空格字符,简单的提取器会拿到一长串连在一起、一个空格都没有的字母。
  • 双栏页面没有阅读顺序:按行提取的工具会横着跨栏读,交给大模型的是一段错乱的文字。Textract 这样的云服务也会犯这个错。
  • 按行输出会丢掉语义结构:标题、表格、单元格边界都没了,后面检索时就失去了上下文。
  • 扫描质量差时,云接口会凭空编出文字:有人见过 Textract 在一张噪点多的扫描件上返回上百个「the」。
  • 空白页会让视觉语言模型(能同时看图和处理文字的模型)编出模板文字:一张空白页可能返回页眉之类的文字。
  • 视觉语言模型输出的 Markdown 没有词级位置框:如果产品要在原 PDF 上高亮出处原文,只靠它输出的 Markdown 可能做不到,因为里面没有每个词的位置。
  • 注意模型许可证:Chandra 与 Surya 只对年收入低于 200 万美元、且不与 Datalab 竞争的机构免费,超出这个范围的公司要另外取得授权;GLM-OCR 本身是 MIT 许可,但会引入 Apache 许可的 Paddle 版面模型,两种许可都得遵守。

想看 PDF 能有多糟,可以翻翻 WTF PDF,里面收集了能搞坏大多数提取器的文件。Joe 的选型流程很短:先回答那两个问题;抽 50–100 页有代表性的页面;让每个候选模型跑同一批页面;然后直接看原始输出、比较各自怎么出错,再做决定。TeX 没空格、双栏读串这类问题,只看一个总分是看不出来的。

完整分享见 Joe 讲 OCR 选型的录像;他对开源 OCR 模型的研究文章也值得一读。

Subtext:写给 Agent 看的脚注,改写时不丢关键决定

让 Agent 总结、润色或改写文档时,它会合并句子、删减、换说法。原文里一句话背后的前提和限定,比如「这里的『下一轮』指哪一轮」「这个决定附带什么条件」,没写在句子里,改写后就可能丢掉。

Bryan Bischof 和 Adam Conway 是 Theory Ventures 的 AI 工程师。这家投资机构的投资人会写详细的投资备忘录,每份里都有重要决定,让 Agent 改写时这些决定有被弄丢的风险。他们的解决办法是 Subtext:一种轻量格式,给每个句子挂上元数据。句子原文不动,作者的澄清说明和还没解决的问题存成元数据,格式是自带说明的 JSON(contextdoc/columnar-v1)。它相当于写给 Agent 看的脚注,让 Agent 改写时不偏离原材料。

举个示意的例子(不是 Theory Ventures 的真实内容):备忘录里有一句「我们建议暂缓跟投 ACME」。这句话挂上的说明可以是:「ACME 指我们 A 轮投过的那家物流软件公司,不是同名的上市公司」「暂缓的意思是等下个季度续约数据出来再决定,不是放弃」。Agent 改写时读得到这些说明,就知道哪些限定不能丢。

仓库里有两个技能:

  • subtext-annotate:把原始文字(邮件、笔记、Markdown)转成 subtext 文档,然后站在一个陌生读者的角度向作者追问(比如没解释的缩写、指代不明、日期含糊、缺背景),再把作者的回答挂到对应的句子上。
  • subtext-read:读取 .subtext.json 文件,在容易看不懂的地方把说明嵌进去展示,并列出作者没能回答的问题。

人也能受益。Adam 演示了一个聊天界面原型:每条消息旁边直接显示发送者关联的工单和通话记录,读的人不用再到处找背景。

Bryan 和 Adam 讲 Subtext 的完整分享在 Maven 上,幻灯片和代码仓库也都公开了。

系统:速度、成本和运行环境

评估和检索理顺之后,要处理的是工程层面的问题:回答太慢、自己部署模型时显存和成本怎么算,以及让 Agent 自己跑代码时怎么防止出事。

推理延迟:先看慢在读输入还是写输出

回答慢,要先分清是慢在读输入,还是慢在写输出,两种情况的解法完全不同。

Abi Aryan 的分享把一次推理分成两个阶段:

  • 预填充(prefill):模型读完全部输入。打个比方,就像答题前先读题:整段输入可以一起送进 GPU 并行计算,所以每秒能处理很多 token。
  • 解码(decode):模型一个接一个地生成输出 token。就像写答案只能一个字一个字写:模型每生成一个 token,都要把它接到前文后面,再算下一个,没法并行,所以按每个 token 算,比预填充慢一个数量级。Abi 在分享里指出,解码的速度主要受内存限制,而不是受算力限制。每生成一个 token,GPU 都要把整个模型的权重和前文的 KV cache 从显存里读一遍,而这一步实际要算的量很小,所以瓶颈在从显存搬数据的速度,GPU 的计算能力大部分用不上。

她的 Colab 笔记本让同一个模型跑三种形状的请求,实测用的是 TinyLlama-1.1B-Chat-v1.0(约 11 亿参数的小模型),跑在一块 Tesla T4 显卡上(显存约 15.6 GB)。换更大的模型或更快的显卡,绝对耗时会变,要看的是各阶段的占比规律。下面是笔记本里一次运行的数字:

长输入、短输出(RAG、分类、信息抽取):最友好的形状。输入虽然长,但预填充能并行,很快就读完。实测输入 1205 个 token、输出 25 个,总共 1087.5 毫秒:预填充 386.4 毫秒(每秒约 3119 个 token),解码 701.1 毫秒(每秒约 36 个)。

短输入、长输出(Agent 运行轨迹、生成代码、长文写作):最差的形状,延迟几乎全花在一个一个生成输出上。实测输入 37 个 token、输出 256 个,总共 7952.4 毫秒,其中解码就占了 7918.8 毫秒(每秒约 32 个 token)。

短输入、短输出(聊天的一轮、单次问答):实测输入 25 个 token、输出 32 个,总共 944 毫秒,其中解码占了 903.5 毫秒。而且 25 个 token 太少,喂不满擅长同时算大量数据的 GPU,预填充阶段 GPU 大部分时间闲着。vLLM 这类推理引擎用连续批处理(continuous batching),把很多用户的小请求拼进同一次计算,让 GPU 不闲着。

实测数据 · Abi Aryan 的计时笔记本

同一个模型,三种请求形状各花了多少时间

横条长度按总耗时统一比例绘制;浅色段是预填充(读输入),深色段是解码(逐个生成输出)。

  1. 短输入、短输出输入 25 · 输出 32 个 token

    总计 944 ms:预填充 40.5 ms(4.3%),解码 903.5 ms

  2. 长输入、短输出输入 1205 · 输出 25 个 token

    总计 1087.5 ms:预填充 386.4 ms(35.5%),解码 701.1 ms

  3. 短输入、长输出输入 37 · 输出 256 个 token

    总计 7952.4 ms:预填充 33.6 ms(0.4%),解码 7918.8 ms

预填充速度
约 617–3119 token/秒
解码速度
约 32–36 token/秒

数据来自笔记本的一次运行:TinyLlama-1.1B-Chat-v1.0,Tesla T4 显卡。换模型、换机器后绝对数值会变,这里看的是各段占比。

Abi 的建议:如果主要慢在输出,先把回答缩短;然后再考虑推测解码(speculative decoding,用小模型先猜出后面几个 token,大模型一次性检查、对的就直接用)、换更小的模型或换更好的硬件。如果主要慢在预填充,就把请求合批处理,并缩短上下文。

这个笔记本适合动手试,有三个值得做的实验:

  • 批大小:用同样的请求形状跑批大小 4 和 8,看每个请求的吞吐怎么提升、显存在哪里卡住。
  • 推理引擎:把 Hugging Face pipeline 换成 llama.cpp 或 vLLM,这对解码速度的影响最大。
  • 量化:换成 INT8 或 INT4 模型,看权重占的显存变小,吞吐通常还会上升。

Abi 讲推理延迟的完整分享可以在 YouTube 上观看。

开源模型经济学:一个估显存的公式,和几条部署提醒

Zach Mueller 梳理了几个开源模型家族,并分享了他自己日常使用的感受:

  • Qwen:27B 稠密模型是他做日常助理型 Agent 的首选,指个人助理类任务(查邮件、查网站、调 API),不是写代码。他自己的后台 Agent 在这个模型上跑了好几个月。
  • GLM 5.2:差不多是他写代码的主力,干活稳,不容易分心,也不犯小错。
  • DeepSeek V4 Flash:可以直接替代 Claude Haiku。
  • Kimi:他写作时最喜欢用,但全精度部署需要一台 8 卡 B200 的机器,很难自己跑。

挑模型之前,先估一下它能不能放进你的硬件。参数量就是模型里存了多少个数,27B 即 270 亿个。估算权重占多少显存的公式是:

权重大小(GB)≈ 参数量(B,十亿)× N × 0.5

这个公式的道理是:16-bit 精度下每个参数占 2 个字节,10 亿个参数就是约 2 GB;公式里的 N × 0.5,正好是不同精度下每个参数占几个字节:

  • 4-bit 精度:N 取 1(单个参数折合 0.5 字节)
  • 8-bit 精度:N 取 2(单个参数折合 1 字节)
  • 16-bit 精度:N 取 4(单个参数折合 2 字节)
  • 32-bit 精度:N 取 8(单个参数折合 4 字节)

显存不能只够装权重。模型一边生成,一边要把前文每个 token 的中间结果存在显存里,免得每生成一个新 token 都把前文重算一遍,这部分叫 KV cache,上下文越长占得越多;计算过程中的激活值也要占地方。例如 27B 模型用 4-bit,权重约 27 × 1 × 0.5 = 13.5 GB,留出 KV cache 和激活值的空间后,能放进一张 16 GB 的显卡。精确的算法可以看 EleutherAI 的 transformer math 文章。

估算器 · 公式来自 Zach Mueller 的分享

模型权重大概要占多少显存

权重(GB)≈ 参数量(B)× N × 0.5 N:4-bit 取 1,8-bit 取 2,16-bit 取 4,32-bit 取 8

27B 模型在不同精度下的权重大小
精度N权重约
4-bit113.5 GB
8-bit227 GB
16-bit454 GB
32-bit8108 GB

只算权重本身。显卡还得给激活值和 KV cache(生成时缓存的中间结果)留空间:27B 模型 4-bit 约 13.5 GB,放进 16 GB 显卡还有余量。精确算法见 EleutherAI 的 transformer math 文章。

Zach 还提醒了几件事:

  • 别把私有代码发给 OpenRouter 这类第三方路由服务。路由服务帮你把请求转给不同的模型服务商,请求可能落到数据隐私做法各不相同的服务商那里。
  • 小心会在会话中途切换服务商的路由。很多服务商会缓存你已经发过的长前缀(比如很长的系统提示词),下一次请求就更快、更便宜;中途换了服务商,这份缓存可能就用不上了。如果要用路由,就用评估实际量一量它对延迟和成本的影响,别靠猜。
  • 租 GPU 时,他宁可租一台顶配机器,也不租多机 H100 集群。H100 只便宜 10%–20%,多机部署时模型要在几台机器之间来回传数据,机器之间的连接就成了瓶颈。

Zach 讲开源模型经济学的完整分享可以在 YouTube 上观看。

给 Agent 配沙箱:隔离、快速启动、Agent 和工具分开

Adam Azzam 讲的是为什么要给 Agent 配沙箱。如果你只在自己电脑上跑编程 Agent,可能还感觉不到沙箱的必要;等写代码的速度上来、同时跑很多个 Agent,或者开始和别人协作,就需要了。沙箱是一个隔离出来、用完可以销毁的临时运行环境;Agent 的代码在里面出了问题,影响范围只限于沙箱。

很多人第一个想到的是现成的 CI/CD(代码提交后自动跑构建和测试的流水线),但它不合适:CI/CD 启动慢、存活时间短,而 Agent 可能在一个任务上连续干几小时甚至几天,还会改动自己运行环境里的依赖;另外,Agent 写的代码没人审过,一次坏改动就可能让机器崩溃,或者暴露环境里的凭据。

Agent 数量一多,启动慢就成了瓶颈。Ramp 的做法是每 30 分钟重新打一次机器镜像(把系统和所需工具都装好的模板),Agent 启动时直接领一台已经准备好的机器,跑一下 git pull 拉最新代码,不到一秒就能开工。

另一条设计原则是把 Agent 和它调用的工具分开,工具崩了不能把 Agent 一起带垮。比如某个工具加载一个很大的 CSV 文件,内存不够崩了,不应该连累一个已经跑了很久的 Agent 任务。Adam 画的示意图里,Agent 在控制面(负责决策和调度的一侧),工具在数据面(负责实际执行的一侧),中间隔着网络。

图解 · 依据 Adam Azzam 的手绘示意图重画

Agent 和工具分开放,中间隔着网络

控制面

负责决策和调度

Agent 工具
网络

数据面

负责实际执行

工具 工具内存不够崩了

例子:数据面里的工具加载超大 CSV 时内存不够崩溃,崩的只是这一侧;控制面里的 Agent 还在,长时间运行的任务不会跟着中断。

Adam 目前在 Modal 工作。Modal 上的代码在远程运行,速度却快得像在本地。在 Modal 上创建沙箱的 hello world 示例如下,详细用法见 Modal 沙箱指南:

import modal

app = modal.App.lookup("sandbox-hello-world", create_if_missing=True)
sb = modal.Sandbox.create(app=app)
process = sb.exec("echo", "hello")
print(process.stdout.read())
sb.terminate()

Adam 讲 Agent 沙箱的完整分享在 Maven 上。

后训练:最后一步,先查评测和环境

评估分数上不去时,很多人的第一反应是收集数据、训练模型。后训练指在现成模型的基础上,用你的数据继续训练(比如监督微调或强化学习),直接改变模型本身的参数。

来自 Prime Intellect 的 Will Brown 和 Florian Brand 提醒:想提高评估分数,微调应该是最后才用的办法,因为很多时候不改模型也能把分数提上去。他们建议先依次做这三件事:

  1. 读运行记录,搞清楚到底哪里失败。
  2. 修评估本身和运行环境。
  3. 改进检索、上下文和外壳。

第二条听起来意外,但评估里的小问题影响可能很大。有些评测为了防止模型没完没了地跑,会设很紧的超时;这时硬件更好、部署更快,模型能在超时前做完更多步,分数也可能跟着变高。他们举的例子是在 Terminal-Bench 2 上把任务超时乘以 5:

  • GPT-5.2 high:52.8% → 60.67%,涨了 7.87 个百分点。
  • GPT-5.2 xhigh:46.3% → 60.97%,涨了 14.67 个百分点。

模型没变,只改了评测环境里的一个设置。放宽之前 high 比 xhigh 高 6.5 个百分点,放宽之后 xhigh 反而略高。如果只看原来的分数,你会得出相反的结论。

实测数据 · Prime Intellect 分享幻灯片

Terminal-Bench 2:只把任务超时放宽 5 倍

模型、题目都没变,只改了评测环境里的超时设置。

原超时下 high 比 xhigh 高 6.5 个百分点;放宽后 xhigh 反而高 0.3 个百分点,排名反转。

他们还列了搭评估环境时常见的坑:

  • 模型训练时按温度 1 采样,评测时却被强制设成温度 0。温度控制模型输出的随机程度:设成 0 时每次都挑最可能的那个 token,设成 1 时按模型算出的概率抽取。强行改成 0,可能影响基准测试结果能否复现。
  • 最大 token 数或轮数上限设得太低,模型推理还没做完就被截断。
  • 给 Agent 沙箱的 CPU 和内存不够。
  • 外壳太臃肿:换成极简的外壳,让模型直接运行 bash 命令,而不是用一堆专门定制的工具。
  • 在断定模型做不了某件事之前,先把针对这项任务的技能(skills)放进上下文试试。

只有两件事同时成立时,才值得去做后训练:

  1. 正确答案能被自动验证,比如能用测试或程序直接判断对错。训练时模型要做大量尝试,每一次都得自动判出对错,才有信号告诉模型该往哪边改。
  2. 模型在当前评测上的得分大于 0 且小于 100。得 0 分说明模型一次都没做对,训练时没有做对的例子可以强化;得 100 分则已经没有提升空间。

想动手试,可以用 Prime Intellect 的 verifiers 库,用 Python 定义任务和奖励;他们的托管强化学习服务只需一个简短的配置文件就能跑训练,也有现成的环境可以直接起步。

Will 和 Florian 讲后训练的完整分享在 Maven 上。