别一上来就换模型:我现在给 Agent 调优的武器库

Agent Tuning Playbook Cover

最近在做一类很典型、也很折磨人的 Agent 质量问题。

表面上看,现象是「grain 不准」「business meaning 不稳」「PK 假阳性偏多」;但如果往下拆,就会发现这些问题其实根本不在同一个层面上。
有的是输入信号不够,有的是任务拆法不对,有的是规则层该做的事被硬塞给了 LLM,还有的是其实模型已经答得差不多了,只差一个后处理校验帮它把明显矛盾拦下来。

所以我后来给自己整理了一套更顺手的调优框架:

先别急着换模型,先判断问题到底发生在哪一层。

一张表先把武器库摆清楚

作用层 手段 性价比 数据集无关性
输入信号 给 LLM 看更多、更好的数据 ⭐⭐⭐
任务拆分 把“一次推全部”拆成多步 ⭐⭐
模型替换 换更强的 LLM,如 deepseek-reasoner / GPT-4o ⭐⭐
后处理校验 LLM 输出后用规则反查矛盾 ⭐⭐⭐
规则前置 不擅长的交还给规则,LLM 只做语言层 ⭐⭐⭐ ⚠️
多次采样 同 prompt 跑 N 次取多数
多 LLM 协作 Generator + Reviewer 分工 ⭐⭐

还有两件我现在基本不会优先考虑的事:

  1. Fine-tune 模型:周期太长,13 天这种节奏根本不现实。
  2. 产品端人工干预:这应该留到决赛产品化阶段,而不是当前自动构建阶段。

这个表对我最大的帮助不是“列手段”,而是提醒我:
不同问题要用不同武器,不要一上来就把所有锅都甩给 prompt。

先判断问题落在哪一层

我现在看 Agent 质量问题,通常先问自己三件事:

  1. LLM 是不是根本没看到足够的结构信号?
  2. 这个判断到底是不是本来就更适合规则做?
  3. 模型已经给出一个差不多的答案了,只是缺了一层兜底校验?

如果这三件事没想清楚,就很容易进入一种假忙碌状态:

  • prompt 越写越长
  • 示例越塞越多
  • token 花得越来越快
  • 结果还是没有稳定提升

很多时候不是模型不行,而是我们把不该让它承担的任务,硬塞给它了。

问题 A:grain 准确率低,优先补输入和规则

当前这个问题里,我最先盯的是 grain

因为一旦 grain 判断错了,后面很多描述都会跟着偏:你以为它在解释一张事件表,实际上它把它当成了维表;你以为它在总结“交易粒度”,它却写成了“实体信息表”。
所以 grain 是上游问题,也是最该先处理的问题。

Lever 1:先增强输入信号

目前只给 5 条 sample 和一些 null / unique 比例,其实经常不够。

更有价值的信号包括:

  1. 行数级别提示row_count: 50000 比单独几条样本更能说明这是一张大事实表还是大维表。
  2. 跨表上下文:把 *_id 的引用关系摘要塞进 prompt。比如 LLM 一旦看到 fact_session.user_id -> dim_user.id,就更容易理解 fact_session 是 transaction 或 event 粒度。
  3. 统计学线索:如果表里有 *_at 字段,可以补时间分布、增长趋势、大小分位数,这些都会帮助模型判断这是一张行为流还是实体快照。

我现在越来越相信一件事:

很多“推理错误”,本质上只是证据不足。

Lever 5:让规则做 70%,LLM 只做边界情况

grain 这种结构化判断,其实规则层就能覆盖掉很大一部分。

例如:

  • fact_ 前缀 + 多个 FK + row_count > 100k,几乎一定是 transaction / event
  • dim_ 前缀 + 单 PK + row_count < 10k,大概率是 dimension

这里最重要的一点不是“把规则写死”,而是:

把规则写成高优先级信号,而不是硬编码真理。

因为不同公司命名并不统一。电商可能用 ods_ / dwd_ / dws_,金融也可能是完全不同的一套体系。
所以规则应该提供强烈提示,但最终仍允许 LLM 覆盖,这样才能避免把系统做成只会识别某一类数据仓库命名习惯的工具。

问题 B:business_meaning 不稳,通常先别单独修

business_meaning 这个字段表面上看是另一个问题,但在我这次实践里,它经常只是 grain 误判的连带伤。

因为语言层面的总结,本来就是 LLM 更擅长的事。
只要它先把这张表的本质看对了,business_meaning 往往会自动跟上。

所以我的顺序不是“同时修两个问题”,而是:

  1. 先把 grain 稳住
  2. 再看 business_meaning 还剩多少问题

如果 grain 已经对了,business_meaning 还不够好,这时候再上 Lever 4 做后处理校验就很划算:

  1. 检查 business_meaning 里是否包含和 grain 对应的关键词
    比如 grain = event 时,描述里至少要出现“事件 / 行为 / 操作”这类词。
  2. 检查长度
    比如要求 < 30 字,避免它一展开就像在写报告。
  3. 检查是否带入数据集特异词
    比如一旦出现“电商”“金融”这类词,就应该报警,因为这很可能说明描述已经开始过拟合当前样本数据集。

这个阶段 LLM 不是不会说,而是需要一个更工程化的护栏,帮它把明显不稳的输出裁掉。

问题 C:PK 假阳性,不要再怪 LLM

created_atname 这种字段也被判成 PK,我现在会直接把它归类为:

这不是 LLM 问题,是 dataset ingestor 的规则层问题。

这里最合适的组合是:

  1. Lever 5 规则前置
    先加排除规则:

    • 即使 unique = 1.0
    • 但字段名如果命中 *_at / *_time / name / email / description
    • 就不要直接进入 PK 候选
  2. Lever 4 LLM 校验
    把规则筛完之后的候选再丢给 LLM,只让它回答一件事:

    • 这是主键
    • 还是“碰巧唯一”

这才是比较健康的分工方式。
规则负责先把明显错的候选过滤掉,LLM 负责处理那些“规则不敢拍板”的剩余情况。

真正执行时,我会分三轮推进

Phase 1:高 ROI、低成本,先上 1 + 4 + 5

第一阶段我最看重的是这三个:

  1. 输入信号增强
  2. 后处理校验
  3. 规则前置

因为这三个都是结构性改进,而且基本不依赖某个具体数据集。
它们做好了,不只是当前这个任务受益,后面别的数据集也会一起吃到提升。

Phase 2:还不够,再上 2 + 3

如果第一轮做完还不够,再考虑:

  1. 任务拆分:把一次性推 grain + business_meaning + PK,拆成多步生成
  2. 换更强模型:比如切到 deepseek-reasonerGPT-4o

我把这两个放到第二阶段,是因为它们更像“针对性加强”,不是第一时间就该动的通用解法。

Phase 3:最后兜底,再上 7 + 6

只有前面都不够时,我才会考虑更贵的方案:

  1. 多 LLM 协作
    Generator -> Reviewer -> Final Rewrite
  2. 多次采样投票
    同一个 prompt 跑 3 次,取多数

这两个方案确实有效,但 token 成本基本都会翻倍,甚至更多。
所以在我这里,它们更像“最后的暴力兜底”,不是第一反应。

如果要自审,我会这么拆

如果进入第二轮还不够,我最愿意加的是一个轻量 reviewer:

Generator: 根据 profile 推 grain + business_meaning

Reviewer: 检查两件事
- 这个 grain 和 PK 数量、行数、外键结构一致吗?
- business_meaning 里有没有数据集特异业务名词?

Final: 根据 reviewer 反馈重写一次

这套模式的好处在于,它不是简单地“多调用一次模型”,而是把一次性生成拆成了:

  1. 先产出答案
  2. 再用另一个视角挑错
  3. 最后基于错误反馈修正

很多原本第一轮就会漏掉的矛盾,在 reviewer 视角下反而很容易被抓出来。

第一轮跑完后,别只看总准确率

我现在越来越不相信单一总分。

如果 grain 准确率低于 60%,我不会马上重写 prompt,而会先按错误类型拆:

  1. dim_* 表到底错了多少
    看维表是不是总被认成事实表。
  2. fact_* 表到底错了多少
    看事实表是不是总被认成维表。
  3. 复合键表到底错了多少
    看桥表、SCD 表这类结构是不是完全没被识别出来。
  4. 没有 fact_ / dim_ 前缀的表错了多少
    这才是真正检验系统通用性的地方。

因为不同错误,应该用不同武器:

  • 全错集中在某类表:优先补输入信号
  • 看起来像对了、其实结构不一致:补后处理校验
  • 明显结构信号都没用上:上规则前置,把 LLM 退回到边界情况
  • 各类都错一点:再考虑换模型

有三件事我现在会坚决不做

  1. Fine-tune
    太慢,太贵,短周期里性价比极低。
  2. 把规则写成数据集特异硬编码
    这直接违背了“数据集无关”的目标。
  3. 把已知陷阱直接写进 prompt 喂答案
    这看起来像优化,实际上是在破坏自动构建的考核意义。

我现在最警惕的一类“优化”,就是那种短期分数会涨,但系统通用性会悄悄塌掉的做法。

最后一个提醒:产品手动测试仍然是最强武器

自动化测试很重要,但它永远不是全部。

哪怕 grain 从 0% 提升到 80%,也还是可能藏着一些自动化测不出来的问题:

  1. fact_subscriptionbusiness_meaning 写成“订阅信息表”,看着像对,但 grain 其实仍然错了
  2. KB 工具调用成功了,但返回结果根本没真正影响 system prompt,因为模板里有 bug
  3. search_tables 返回了 5 张表,LLM 却选错了去做 JOIN

这些问题只有端到端手测才抓得出来。

所以我现在对“自动化”和“手动测试”的理解是:

  • 自动化负责覆盖率、规则一致性、结构正确性
  • 产品手测负责端到端可用性和真实任务完成度

它们不是替代关系,而是互补关系。

我的结论:调优先看层,再看招

如果让我把这次经验压缩成一句话,那就是:

Agent 调优最怕的,不是招数不够,而是还没分清问题在哪一层,就开始乱出招。

真正高 ROI 的路径,通常不是第一时间换更大的模型,而是先把:

  1. 输入信号补够
  2. 规则边界划清
  3. 后处理兜底补上
  4. 再把昂贵的多模型协作和多次采样,留到最后

这样调出来的系统,才更像一个可复用的工程能力,而不是一段只在当前数据集上偶然奏效的 prompt 魔法。