☰
小模型Agent提速10倍:把决策推理从生成式改成选择题
2026/10/13 11:26:46 网站建设 项目流程

如果你在本地跑过一个小模型 agent,大概率见过这种场面:你问它要不要查天气,它不急着给结果,而是先憋出一段“用户想了解天气情况,因此需要调用天气查询工具,然后把结果整理给用户……”绕了半天才轮到真正的动作。小模型尤其如此,参数量越小,越容易把简单问题想复杂。之前我在内部搭一个小型 agent 时,最受不了的就是这种“话太多”的思考过程。后来我把推理从“生成”改成“做选择题”——让模型不再自由编写决策文本,而是从预设的几个选项里直接挑一个——同一套任务下,Small Agent 的思考速度快了差不多 10 倍。这篇文章就聊聊我具体怎么改的、哪些环节改了有效、哪些地方千万别乱改。

1. 一个容易忽略的事实:推理慢的根源往往不是模型笨,而是话太多

1.1 生成式推理到底在生成什么

小模型 agent 的常见链路大概是这样的:接收用户请求,理解上下文,决定调用什么工具,生成工具参数,拿到结果后再生成回复。很多实现会把“决定调用什么工具”这一步也设计成自然语言输出,甚至要求模型输出一长段 JSON,里面带着 thought、action、action_input 这些字段。这看起来没什么问题,因为大模型就是这么用的。

但这里有个巨大的差异:大模型参数多、算力足,多生成几十个 token 根本不痛;小模型不行。当模型只有 1B 到 3B 时,生成速度本身就不快,如果再要求它先写一段理由再写动作,延迟会被成倍放大。更麻烦的是,小模型生成长文本时经常出现绕圈、自我修正、重复某句话的现象,这些都会额外消耗 token,而且不一定让结果更可靠。

我一开始就是按大模型的习惯来搭小 agent:先让它输出“thought + action + action_input”,然后写解析器去处理这段文本。跑通是跑通了,但每个决策步骤平均要生成几十个 token,整体体验非常拖沓。后来我才意识到一个更本质的问题:这个 agent 里绝大多数步骤并不需要“表达”,只需要“选择”。比如从五个工具里选一个来调用,本质上是五分类问题,为什么非要让模型写一段话才能决定?

1.2 从延迟拆解看为什么话多就必然慢

大语言模型的推理延迟可以拆成两部分:第一部分是读取 prompt,把所有输入 token 并行处理一遍,一般叫 prefill;第二部分是逐个生成回答里的新 token,叫 decode。对体感影响最大的是 decode,因为它是串行的,前一个 token 算完才能算下一个,跑批也没有用。

小模型的单 token 生成延迟通常在十几毫秒到几十毫秒之间,取决于显存、带宽、量化方式。假设一个 3B 模型在某块消费级显卡上每秒生成 40 个 token,那么生成 50 个 token 的思考文本需要 1.25 秒;如果只让模型输出 1 个 token,理论上只要 25 毫秒。即便把 prefill、解析、前后处理都加进去,从一秒多降到一百毫秒左右也完全是可以实现的。

这就是“10 倍速度”的来源:不是模型变聪明了,也不是推理框架变强了,而是把需要串行生成的内容压缩了大约一到两个数量级。token 少,等待时间自然断崖式下降。很多人遇到小模型 agent 慢,第一反应是换更大显存的卡,其实不如先看一眼每一步到底生成了多少 token,往往这才是性价比最高的优化点。

2. “选答案”的本质:把开放式文本生成压缩成一次 forward 分类

2.1 不是让你改模型结构,而是改决策接口

很多朋友听到“把推理改成做选择题”,第一反应是“这算哪门子推理”。其实这里核心不是取消推理,而是重新设计模型的输出接口。过去我们希望模型“写”出答案,现在我们希望模型从候选集合里“选”出答案。

从工程上看,有两种常见实现方式。第一种叫约束解码:模型还是在自回归生成,但每一步只允许输出你指定的 token。比如你告诉它只能输出 0、1、2 这三个数字,它本质上是在做三分类,只是借用生成模型的外壳。第二种叫分类头:在模型最后一层 hidden state 上接一个线性分类器,直接输出每个候选选项的概率,完全不走自回归。分类头延迟更低,但一般需要微调;约束解码可以零训练直接试,效果取决于模型本身和提示词的配合。

这个选择本质上是降低“输出的自由度”。自由度越高,天花板越高,但方差也大;自由度低,表达力受限,但在决策场景下更稳定、更快。agent 的每一步不一定都需要高自由度,大部分步骤只是在有限动作空间里挑一个执行而已。

2.2 哪些推理步骤适合改成选择题,哪些不适合

先说适合的:意图识别、工具选择、动作路由、对话状态判断、是否需要请求用户确认、错误重试策略,这些都属于离散决策。比如“用户这句话是想查询还是想办理”,给两个选项就够了;“这个异常是重试、转人工还是直接报错”,给三个选项就够了。这些场景天然是分类问题,改成选择题几乎没有损失。

不适合的:需要生成自然语言回复、需要写代码、需要总结长文本、需要构造工具调用的动态参数。你说让模型从几个候选文案里选一条,如果候选集固定也可以,但大多数真实回复没有现成候选集,仍然要生成。

我个人的判断标准很简单:如果这个步骤的最终产物是一个“动作标签”,而不是一段“内容文本”,那就可以考虑改造成选择题。如果一个步骤最终要产出用户直接能看到的文字,那大概率还是得保留生成能力。

3. 最快的落地路径:约束解码,零训练也能先测试

3.1 用 logits 掩码把非法输出限定在几个选项里

约束解码的原理很简单。大模型每次输出下个 token 时,会在整个词表上算出一组概率;我们只要在采样前把“非法 token”的概率直接设成负无穷,模型就只能从合法 token 里选。

举个例子,想让模型只输出“0”“1”“2”三种选项,在 PyTorch 里可以这样处理:

import torch def mask_logits(logits, allowed_token_ids): mask = torch.full_like(logits, float("-inf")) mask[:, allowed_token_ids] = 0.0 return logits + mask # 假设已经取到了最后一个位置的 logits next_logits = model(input_ids).logits[:, -1, :] allowed = [token_id_0, token_id_1, token_id_2] next_logits = mask_logits(next_logits, allowed) next_token = torch.argmax(next_logits, dim=-1)

如果你用的推理框架支持 logits bias 或 grammar 约束,通常能更优雅地实现。这里有个容易踩的细节:不同 tokenizer 对数字的切分不一样,“0”可能是一个 token,也可能和前后字符粘在一起。你需要提前用 tokenizer 查一下编码结果,找到对应选项文本的具体 token 列表,必要时做多 token 约束,而不是只约束一个 token。

3.2 一个可直接复用的提示词模板与代码思路

约束解码虽然能限制输出范围,但不代表模型一定“理解”自己只能输出选项。所以提示词要写得很明确,不能含糊。我自己测试下来比较稳的模板结构是:先给背景,再给选项定义,最后要求只输出一行编号。

你是一个任务路由模块。 根据用户的意图,从以下选项中选择一个,只输出编号,不要输出任何解释。 0: 查询天气 1: 设置提醒 2: 闲聊 3: 其他 用户输入:明天上海会下雨吗? 输出:

配合约束解码,模型能输出的就只有 0-3 这些数字字符。如果一个数字被 tokenizer 拆成多个 token,可以让模型输出更保险的短词,比如“0”;或者干脆给每个选项设计一个专门触发词,比如 query_weather、set_reminder,然后用正则约束整个单词。

这一步真正的价值在于:不需要训练,只要改 prompt 加一个 mask,就能先验证“选答案”的思路在你的任务上到底快多少、准不准。如果这一步效果就很差,说明候选集合设计有问题,或者模型实在太小、理解不了你的选项,后面也就不用急着微调了。

4. 更稳的落地路径:分类头微调,把 agent 的“思考”变成真正的分类

4.1 训练数据怎么来:从日志里挖,或者用大模型标注

约束解码只能把“生成”伪装成“选择”,模型内部行为还是生成式的。如果想让延迟和稳定性都达到理想状态,分类头微调是更推荐的做法。做法是:在模型的最后一层 hidden state 上接一个全连接层,输出维度等于候选类别数,然后拿标注好的“文本 → 动作标签”数据做交叉熵训练。

训练数据主要有两个来源。第一个是线上历史日志:你原来用生成式 agent 时,每一步都会记录 prompt 和最终动作,把日志整理成“输入文本 → 标签”的样本就行。第二个是还没有日志时,用更大的模型来标注:把一批用户话术和动作定义发给大模型,让它批量给出标签,人工抽检一部分即可。

我建议初始类别数不要太多,尽量控制在 10 个以内。类别太多,小模型的分类准确率会明显下降,而且边界样本容易互相混淆。可以把出现频率低的长尾动作合并成一个“其他”类,后续根据线上数据再慢慢拆分。

4.2 微调与推理阶段的要点

微调过程中有几个点特别值得注意。第一,不要只给模型看用户的那一句话,要把 agent 的系统提示、可用工具、历史上下文一并作为输入,否则模型不知道当前处于什么状态。第二,正负样本要尽量均衡,尤其要给“其他”类留足够多的样本,否则模型会把所有不确定的输入都猜成高频类。

推理阶段和生成式完全不一样:不需要调用 generate,只跑一次 forward,取最后一层 hidden state,过分类头,然后 argmax 或 top-k。因为只算一次前向,延迟基本就是 prefill 时间加一个矩阵乘法,几乎可以忽略不计。

还有一个进阶技巧:让分类头同时输出置信度。当置信度低于某个阈值(比如 0.7)时,不要鲁莽执行,而是走兜底分支——要么重新用生成式模型推理,要么要求用户换一种说法。这个“低置信度时不决策”的思路,比强行把所有样本都塞进某个类别里要稳健得多。

5. 效果实测:同一套模拟任务,延迟从秒级到百毫秒级

5.1 测试环境和评测指标

我在自己搭的一个模拟客服 agent 上做了对比测试。模型固定为一个 3B 左右的本地小模型,量化之后显存占用不高。任务集是 120 条用户话术,覆盖查账单、改地址、投诉、咨询进度、闲聊等 8 类动作。评测指标有两个:端到端延迟,以及动作识别准确率。

对照组是原来的生成式方案:agent 每步要求模型输出一段 JSON,包含 thought 和 action,然后解析 JSON 提取 action。实验组是分类头方案:直接输出 8 类动作的概率。两边都用同一批 prompt 前缀,保证比较公平。

延迟测法上有个小讲究:不能只测一次,因为第一次调用有冷启动。我是先 warm up 两次,然后每条测 5 次,取中位数。取中位数是为了避免个别特别慢的样本拖高平均值,比单纯取均值更能反映真实体感。

5.2 延迟对比与成功率变化

最终结果很符合预期。生成式方案下,每个决策步骤的平均延迟在 1.1 秒左右,因为模型平均要生成 40 多个 token 才肯给出一个完整 JSON;换成选择题方案后,同样步骤的延迟降到 100 毫秒出头,差不多刚好 10 倍。

方案平均决策延迟动作准确率端到端成功率
生成式 JSON约 1.1 秒约 95%约 88%
选择题分类头约 105 毫秒约 93%约 93%

动作识别准确率方面,8 个类别里前 6 个高频类的准确率都在 95% 以上,整体准确率 93% 左右。和生成式方案相比,整体准确率略有下降,但下降幅度不大,大约两个百分点。考虑到延迟从秒级降到了百毫秒级,这个代价完全可以接受。

更让我意外的是端到端成功率。原本我以为选项方案会丢失一些上下文信息,导致成功率下降;实际跑下来,端到端任务成功率反而上升了。原因很可能是生成式方案偶尔会输出格式非法的 JSON,导致解析失败重试,拉低了整体成功率。选择题方案没有格式问题,自然少了很多无效往返。

6. 边界与兜底:什么时候这一招会翻车,我踩过的三个坑

6.1 当“选择题”的选项本身需要推理时

第一个坑:我一开始把所有动作都塞进选择题里,包括一些需要复杂计算和判断的动作。比如“根据用户近三个月的消费记录判断是否满足退款条件”,这本质上不是一个固定选项的选择,而是一个需要生成判断依据的推理任务。用分类头强行做,模型只能根据表面特征猜,准确率惨不忍睹。

后来我把动作拆成两层:第一层用选择题做“要不要启用深度推理”的路由判断;第二层对确实需要推理的少量请求,再走生成式大模型。这样既保住了延迟,又保住了效果。简单说,选择题负责“往哪个方向走”,生成式负责“到了之后怎么说话”,两者各管一段。

6.2 兜底方案:混合路由与置信度阈值

第二个坑是低置信度样本。有些用户话术非常模糊,比如“你们这个东西怎么用”,会在多个动作之间摇摆。如果模型硬选一个,经常选错。我的解法是加一个“需要澄清”的类别,让模型在拿不准的时候主动向用户要更多信息,而不是硬着头皮猜。

同时我加了置信度阈值逻辑:如果分类头的最高概率低于 0.6,就认为这个样本不可信,走生成式兜底。这个阈值需要根据你的业务试出来,不要照搬。阈值太高会导致什么都兜底,等于没优化;阈值太低又相当于没加保护。

6.3 给想抄作业的人一个最小改造清单

最后一个坑是“为了快而快”。如果 agent 的决策步骤本来只有两三个动作,改成选择题省下的 token 有限,没必要花大力气。只有决策动作超过五个,或者决策链路很长、每轮都要做多次路由时,这个方案才值得投入。

如果你想照着做,我建议的最小改造路径是:第一步,把所有动作列成 5 到 10 个互斥选项;第二步,用约束解码先跑一段时间,看准确率是否可接受;第三步,如果准确率不够,再攒数据做分类头微调;第四步,加置信度阈值和兜底分支,保证低置信度时不会误决策。这样一步一步来,既能快速见效,又不会一上来就陷入训练数据的泥潭。

我个人在踩过这些坑之后最大的体会是:小模型的瓶颈不在“智商”,而在“表达成本”。让它少说废话、多做选择,比盲目换大模型划算得多。如果能把动作选择、意图识别、状态判断这些高频步骤全部改成选择题,再配合生成式模型只负责最后的内容输出,整套 agent 的体验会有非常明显的变化。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询