零样本、少样本与微调:Agent 能力获取的三条路径

举报
柠檬🍋 发表于 2026/10/19 11:30:03 2026/10/19
【摘要】 零样本、少样本与微调:Agent 能力获取的三条路径 一、一个典型的选型困惑团队要把 Agent 的意图识别准确率从 78% 提到 95%。方案会上出现了三种意见:“再优化一下提示词,把意图定义写清楚,加几个例子。”“把示例从 3 个加到 20 个,做个动态示例选择。”“收集几百条标注数据,微调一个小模型专门做分类。”三种意见对应三种能力获取路径:零样本(zero-shot)、少样本(fe...

零样本、少样本与微调:Agent 能力获取的三条路径

一、一个典型的选型困惑

团队要把 Agent 的意图识别准确率从 78% 提到 95%。方案会上出现了三种意见:

  • “再优化一下提示词,把意图定义写清楚,加几个例子。”
  • “把示例从 3 个加到 20 个,做个动态示例选择。”
  • “收集几百条标注数据,微调一个小模型专门做分类。”

三种意见对应三种能力获取路径:零样本(zero-shot)、少样本(few-shot)、微调(fine-tuning)。它们不是"哪个更先进"的关系,而是成本、效果、维护难度各不相同的三种工程选择。

本文讨论:三条路径的机制差异;各自的成本结构;以及如何在具体场景下做选择。

二、三条路径的机制

2.1 零样本:只给指令

把任务描述写进提示,不提供任何示例。

请判断用户消息属于以下哪一类意图:
- 退款:用户要求退款、退货、取消订单
- 咨询:用户询问产品信息、使用方法
- 投诉:用户表达不满、要求处理问题

用户消息:{message}
输出:只输出类别名称。

机制:完全依赖模型预训练时获得的能力。模型通过指令理解任务,通过概念定义(“退款:用户要求退款……”)来对齐分类标准。

特点:零标注成本、迭代最快;但效果受模型能力与任务复杂度限制,且对边界案例的处理不稳定(比如"我要退掉这个但我想换个新的",算退款还是咨询?)。

2.2 少样本:给几个例子

在提示中加入若干"输入 → 输出"示例。

用户消息:我要退货        → 退款
用户消息:这个怎么用       → 咨询
用户消息:你们太差了       → 投诉
用户消息:{message}       → ?

机制:模型从示例中归纳模式(in-context learning)。示例不仅说明"要做什么",还隐式定义了"边界怎么划"。

特点:标注成本低(几个到几十个例子)、效果通常明显优于零样本;但消耗上下文预算,且效果对示例选择很敏感。

2.3 微调:改参数

用标注数据继续训练模型,让模式固化到参数里。

机制:通过梯度下降调整模型权重,把任务模式从"上下文中的临时提示"变成"参数中的固有能力"。

特点:效果上限最高(尤其对特定领域、特定格式)、推理时不需要示例(省上下文、降延迟);但需要标注数据、需要训练流程、迭代周期长,且模型更新后需要重新适配。

2.4 机制对比

维度 零样本 少样本 微调
改变对象 提示 提示(含示例) 模型参数
数据需求 无 3–50 条示例 数百到数万条标注
上下文占用 低 中高 无(或极低)
效果上限 中 中高 高
迭代速度 分钟级 分钟级 天级
推理成本 低 较高(示例占 token) 低
维护成本 低 中 高(需重训、需版本管理)

三、少样本的关键工程问题

少样本看起来简单(“加几个例子”),但效果差异极大,原因在于几个工程细节。

3.1 示例数量

示例不是越多越好。常见观察:

  • 3–5 个示例通常能带来最大的边际收益;
  • 继续增加到 10–20 个,收益递减;
  • 示例过多会占用大量上下文,且可能引入噪声(示例之间的风格不一致反而干扰模型)。

建议:从 3 个开始,逐步增加到收益不再明显为止。

3.2 示例选择

示例的质量比数量重要。好的示例集应满足:

  • 覆盖各类别:每个类别至少一个示例,避免类别不平衡;
  • 包含边界案例:把最容易混淆的情况作为示例(比如"我要换货"该归哪类);
  • 格式一致:所有示例的输入输出格式必须完全统一,否则模型会学到不一致的模式;
  • 典型性:示例应代表该类别最常见的情况,而不是极端特例。

3.3 动态示例选择

当示例库较大(几十到几百条)时,可以根据当前输入动态挑选最相关的几个示例。

实现方式:把示例库向量化,对当前输入做相似度检索,取 top-k 作为示例。这本质上是把 RAG(检索增强生成)的思路用在示例选择上。

收益:在示例库大、类别细分的任务上效果明显。
代价:增加一次检索的开销,且检索质量影响示例质量。

3.4 示例顺序

示例的排列顺序会影响结果,尤其当示例中存在与当前输入相似的样本时,模型可能受最近的示例影响更大。

实践建议:把最有代表性的示例放在最后(靠近实际输入),或通过实验确定顺序。

四、微调的适用判断

微调的成本很高,不应该作为第一选择。以下是明确适合微调的信号。

信号一:任务格式高度固定,且重复调用量大。
如果每天要调用百万次,示例占用的 token 成本会累积成显著开支,此时微调能省下这部分。

信号二:领域术语与常识差异大。
比如医疗、法律、特定行业术语。模型对这些领域的理解可能不足,微调能显著改善。

信号三:输出格式要求严格且复杂。
比如必须输出特定结构的 JSON、必须遵循特定的分类体系。微调能让格式遵循更稳定。

信号四:需要更小的模型来降本。
用小模型 + 微调,可能达到大模型 + 提示的效果,同时显著降低推理成本与延迟。

信号五:少样本已经达到瓶颈。
如果加示例的收益已经停滞,且仍有明显错误,说明需要参数级的适配。

不适合微调的信号:

  • 任务本身还在频繁变化(今天这么分类,明天改标准)——微调跟不上变化;
  • 数据量不足以支撑(几十条数据微调容易过拟合);
  • 零样本/少样本已经满足需求——没必要增加复杂度;
  • 团队没有模型训练与部署的能力。

五、成本结构的完整对比

选型时最容易犯的错误是只看效果,不看全生命周期成本。

5.1 一次性成本

路径 一次性成本
零样本 写提示的时间(小时级)
少样本 写提示 + 挑选示例(天级)
微调 数据标注 + 训练 + 评估 + 部署(周级到月级)

5.2 持续成本

路径 持续成本
零样本 每次调用都带完整指令
少样本 每次调用都带示例(token 更多)
微调 推理便宜,但模型更新需重新适配

5.3 变更成本

这一项常被忽略。当任务需求变化时:

  • 零样本:改提示即可,分钟级;
  • 少样本:改示例即可,分钟级;
  • 微调:需要重新标注、重新训练、重新评估、重新部署——周期以周计。

这意味着:如果任务需求不稳定,微调会成为负担。微调适合"任务定义已经稳定、且调用量大"的场景。

5.4 一个粗略的决策框架

任务需求是否稳定?
  ├─ 否 → 零样本 / 少样本(便于快速迭代)
  └─ 是 → 效果是否已满足?
            ├─ 是 → 零样本 / 少样本(够用就别加复杂度)
            └─ 否 → 调用量是否大?
                      ├─ 否 → 少样本 + 动态示例
                      └─ 是 → 考虑微调(或小模型 + 微调)

六、三段式混合:把三条路径组合起来

实践中,三条路径常常组合使用,而不是二选一。

一个典型的混合结构:

第一层:小模型 + 微调  → 处理高频、简单的意图分类(快、便宜)
第二层:大模型 + 少样本 → 处理第一层不确定的样本
第三层:人工            → 处理前两层都不确定的样本

这种"级联"结构的优势是成本与效果的平衡:大部分请求由便宜的路径处理,只有少数复杂请求走昂贵路径。

类似地,也可以:

  • 用零样本做快速原型验证,验证可行后再补示例;
  • 用少样本收集数据(把线上请求与模型输出作为候选标注),积累到一定量后再考虑微调;
  • 微调模型负责格式约束,大模型负责语义理解。

关键洞察:三条路径不是互斥选项,而是可以按请求特征分流的处理层。

七、一段示例选择的实现(示意代码)

下面实现一个"动态示例选择"的最小版本,演示如何根据当前输入挑选最相关的示例。这是示意代码,用简单的词重叠度代替向量检索,以便离线运行。

环境与依赖:Python 3.9+,仅用标准库。保存为 example_select.py,执行 python example_select.py。

"""少样本动态示例选择的示意实现(离线可运行)。

说明:
- 用"字符 bigram 重叠度"近似语义相似度,避免引入向量库依赖;
- 真实系统应使用嵌入模型做语义检索;
- 这是示意代码,用于说明动态示例选择的结构。
"""
import re
from dataclasses import dataclass

# ---------- 示例库(每类至少一条,含边界案例) ----------
EXAMPLES = [
    {"text": "我要退货,这个东西不合适", "label": "退款"},
    {"text": "订单下错了,帮我取消", "label": "退款"},
    {"text": "这个功能怎么用啊", "label": "咨询"},
    {"text": "支持哪些支付方式", "label": "咨询"},
    {"text": "等了三天还没发货,太差了", "label": "投诉"},
    {"text": "你们的服务态度让人失望", "label": "投诉"},
    # 边界案例:既有退款意图又像咨询
    {"text": "我想换成另一个颜色,能退吗", "label": "退款"},
]

LABELS = ["退款", "咨询", "投诉"]

def bigrams(s: str) -> set:
    s = re.sub(r"\s+", "", s)
    return {s[i:i + 2] for i in range(len(s) - 1)} or {s}

def similarity(a: str, b: str) -> float:
    """字符 bigram 的 Jaccard 相似度(近似语义相似度)。"""
    ba, bb = bigrams(a), bigrams(b)
    if not ba or not bb:
        return 0.0
    return len(ba & bb) / len(ba | bb)

def select_examples(query: str, k: int = 4, ensure_per_label: bool = True) -> list:
    """动态挑选 k 个最相关示例;保证每个类别至少出现一次。"""
    ranked = sorted(EXAMPLES, key=lambda e: similarity(query, e["text"]), reverse=True)
    picked, seen = [], set()

    if ensure_per_label:
        for label in LABELS:                     # 先保底每类一个
            for e in ranked:
                if e["label"] == label and e["text"] not in seen:
                    picked.append(e); seen.add(e["text"]); break

    for e in ranked:                             # 再按相似度补齐
        if len(picked) >= k:
            break
        if e["text"] not in seen:
            picked.append(e); seen.add(e["text"])
    return picked[:k]

def build_prompt(query: str, examples: list) -> str:
    lines = ["请判断用户消息的意图类别,只输出类别名称。", ""]
    for e in examples:
        lines.append(f"用户消息:{e['text']}  →  {e['label']}")
    lines.append("")
    lines.append(f"用户消息:{query}  →  ?")
    return "\n".join(lines)

if __name__ == "__main__":
    for q in ["买错了想退款", "怎么修改密码"]:
        exs = select_examples(q, k=4)
        print(f"=== 查询:{q} ===")
        print(build_prompt(q, exs))
        print()

输出(示意):

=== 查询:买错了想退款 ===
请判断用户消息的意图类别,只输出类别名称。

用户消息:我要退货,这个东西不合适  →  退款
用户消息:这个功能怎么用啊  →  咨询
用户消息:等了三天还没发货,太差了  →  投诉
用户消息:买错了想退款  →  ?
...

注意 select_examples 里的两个设计:

  • 保底每类一个(ensure_per_label):避免动态选择时某个类别完全缺席,导致模型对该类无参照;
  • 按相似度补齐:在保证类别覆盖的前提下,优先选与当前输入最相似的示例。

这正是动态示例选择的实际做法——类别覆盖 + 相似度优先,而不是单纯的 top-k 相似。

八、常见错误

错误一:默认"微调效果最好",直接上微调。
微调的一次性成本与变更成本都很高。多数场景先用少样本就能满足需求。

错误二:示例只堆数量,不管覆盖。
10 个示例全是"退款",模型对"投诉"的判断就缺乏参照。

错误三:示例格式不统一。
有的示例输出是"退款",有的是"退款类",有的是"REFUND"。模型会学到混乱的模式。

错误四:忽略上下文成本。
示例占用的 token 是每次调用都要付的。在调用量大的场景,这部分成本可能超过微调的一次性投入。

错误五:任务变化后忘记更新示例/模型。
分类标准变了,但示例还是旧的,或者微调模型还是按旧标准训练的。

错误六:不评估就直接替换。
从零样本换到少样本、或从少样本换到微调,都应该在固定测试集上做对比,而不是凭感觉。

九、局限

  • 本文的成本量级描述(“小时级/天级/周级”)是粗略的示意,实际取决于团队能力与任务复杂度。
  • 微调的具体效果高度依赖数据质量与训练方法,本文未展开训练细节,具体数值不应外推。
  • 动态示例选择的相似度实现(本文用 bigram)只是近似,真实系统应使用嵌入模型;近似方法的检索质量会明显低于语义检索。
  • 三条路径的效果对比必须基于你自己的任务实测,公开研究中的结论不必然适用于你的场景。

十、小结

  • 三条路径分别是:零样本(只给指令)、少样本(给示例)、微调(改参数),改变的对象依次是提示、提示、模型。
  • 少样本的关键不在数量,而在示例的覆盖、边界案例与格式一致性;示例库大时应做动态选择。
  • 微调适合任务稳定、调用量大、领域特殊、需要降本的场景;需求频繁变化的场景不适合。
  • 选型必须看全生命周期成本:一次性成本、持续成本、以及最易被忽略的变更成本。
  • 三条路径可以级联组合:小模型微调处理高频简单请求,大模型少样本处理复杂请求,人工兜底。
  • 原则:从最简单的路径开始,只在遇到明确瓶颈时升级。

动手练习:从你 Agent 的线上日志里随机抽 50 条请求,用零样本跑一遍,统计准确率;再从错误案例中挑出 5 条作为示例,用少样本跑一遍,对比准确率变化。如果少样本带来的提升很明显,说明示例质量是关键变量,此时应该继续优化示例集(补边界案例、统一格式);如果提升不明显,说明问题可能在任务定义本身,而不是示例不足——此时加更多示例或微调都不会有本质改善。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。