第六章:微调 — 01 什么时候需要微调?#

微调不是万能药,90% 的问题可以用提示工程解决。本章帮你判断什么时候真的需要微调。

很多工程师一遇到 LLM 表现不理想,第一反应就是想到微调。但微调成本高、周期长、维护难,往往是过度工程化的选择。

本章目标:

  • 掌握判断是否需要微调的决策框架

  • 学会用提示工程解决常见问题

  • 了解微调真正适合的场景

  • 估算不同方案的成本

import json
import os
import litellm
litellm.drop_params = True
from dotenv import load_dotenv

load_dotenv()
MODEL = os.getenv("LLM_MODEL", "gpt-4o-mini")

print(f"使用模型: {MODEL}")
# gpt-5/o系列不支持自定义temperature值,统一用安全wrapper
def _c(**kw):
    _m = kw.get('model', MODEL)
    if any(_m.startswith(p) for p in ('openai/gpt-5','openai/o1','openai/o3','openai/o4')):
        kw.pop('temperature', None)
    return litellm.completion(**kw)
使用模型: openai/gpt-5-mini

Section 1:决策树 — 我需要微调吗?#

下面是一个结构化的决策流程,回答每个问题,最终得到一个推荐方案。

开始
 │
 ▼
知识截止日期问题?
 │ 是 → 考虑 RAG(检索增强生成),而非微调
 │ 否
 ▼
需要高度一致的输出格式?
 │ 是 → 先试结构化输出(JSON mode)
 │ 否
 ▼
对延迟极度敏感?(<500ms p99)
 │ 是 → 考虑微调小模型
 │ 否
 ▼
有高质量标注数据?(>1000 对 QA)
 │ 否 → 先优化提示词,收集数据
 │ 是
 ▼
成本是否显著高于可接受水平?
 │ 否 → 继续用 API + 提示工程
 │ 是 → 微调小模型可能値得
def finetune_decision_tree():
    # Interactive decision tree
    print("=" * 60)
    print("  LLM 方案选择决策树")
    print("=" * 60)

    questions = [
        {
            "key": "knowledge_cutoff",
            "question": "\n[Q1] 问题是否因为模型不知道你的私有或最新数据?",
            "explanation": "  例如:公司内部文档、产品手册、最新新闻",
            "if_yes": ("RAG", "建议使用 RAG。微调无法注入动态知识,而 RAG 可以。"),
        },
        {
            "key": "consistent_format",
            "question": "\n[Q2] 是否需要高度一致的输出格式?",
            "explanation": "  例如:始终输出带特定字段的 JSON",
            "if_yes": ("Structured Output", "先试结构化输出(JSON mode)或 function calling。"),
        },
        {
            "key": "latency_critical",
            "question": "\n[Q3] 延迟是否极度敏感?(如需要 p99 < 500ms)",
            "explanation": "  例如:实时语音助手、游戏 NPC",
            "if_yes": ("Fine-tune Small Model", "微调小模型自托管,延迟可控。"),
        },
        {
            "key": "data_available",
            "question": "\n[Q4] 是否有高质量标注数据?(至少 500-1000 对示例)",
            "explanation": "  数据质量比数量重要",
            "if_no": ("Prompt Engineering", "数据不足时微调效果差。先优化提示词。"),
        },
        {
            "key": "cost_unacceptable",
            "question": "\n[Q5] 当前 API 成本是否显著超出预算?",
            "explanation": "  计算当前成本 vs 微调小模型自托管成本",
            "if_yes": ("Fine-tune for Cost", "微调小模型替代大模型 API,长期成本可降低 10x。"),
        },
    ]

    scenario_answers = {
        "knowledge_cutoff": "n",
        "consistent_format": "y",
        "latency_critical": "n",
        "data_available": "y",
        "cost_unacceptable": "n",
    }

    print("\n[演示场景:需要稳定 JSON 输出的客服机器人]")

    for q in questions:
        key = q["key"]
        print(q["question"])
        print(q["explanation"])
        answer = scenario_answers[key]
        print(f"  回答: {'是 (Y)' if answer == 'y' else '否 (N)'}")

        if answer == "y" and "if_yes" in q:
            rec_type, rec_msg = q["if_yes"]
            print(f"\n  推荐方案: [{rec_type}]")
            print(f"  原因: {rec_msg}")
            print("\n" + "=" * 60)
            print(f"  最终建议: {rec_type}")
            print("=" * 60)
            return rec_type

        if answer == "n" and "if_no" in q:
            rec_type, rec_msg = q["if_no"]
            print(f"\n  推荐方案: [{rec_type}]")
            print(f"  原因: {rec_msg}")
            print("\n" + "=" * 60)
            print(f"  最终建议: {rec_type}")
            print("=" * 60)
            return rec_type

    return "Prompt Engineering"


result = finetune_decision_tree()
print(f"\n决策结果: {result}")
============================================================
  LLM 方案选择决策树
============================================================

[演示场景:需要稳定 JSON 输出的客服机器人]

[Q1] 问题是否因为模型不知道你的私有或最新数据?
  例如:公司内部文档、产品手册、最新新闻
  回答: 否 (N)

[Q2] 是否需要高度一致的输出格式?
  例如:始终输出带特定字段的 JSON
  回答: 是 (Y)

  推荐方案: [Structured Output]
  原因: 先试结构化输出(JSON mode)或 function calling。

============================================================
  最终建议: Structured Output
============================================================

决策结果: Structured Output

Section 2:提示工程先行#

以下三个场景,初看似乎需要微调,但用提示工程就能解决。

需求

错误直觉

正确方案

始终输出 JSON

微调

JSON mode / structured output

使用领域术语

微调

Few-shot 示例

特定写作风格

微调

风格示例放入提示词

# 场景 A:始终输出 JSON

print("场景 A:需要稳定的 JSON 输出")
print("-" * 50)

good_prompt = (
    "分析以下产品评论,严格以 JSON 格式输出,不要有任何额外文字:\n\n"
    "评论:手机很好用,但电池续航差\n\n"
    "输出格式:\n"
    '{"sentiment": "positive|negative|mixed", "score": 1-5, "pros": [...], "cons": [...]}'
)

response = _c(
    model=MODEL,
    messages=[{"role": "user", "content": good_prompt}],
    response_format={"type": "json_object"},
    temperature=0
)

result_text = response.choices[0].message.content
result_json = json.loads(result_text)
print("提示词方案输出(稳定 JSON):")
print(json.dumps(result_json, ensure_ascii=False, indent=2))
print("\n结论:JSON mode + 明确格式描述,无需微调!")
场景 A:需要稳定的 JSON 输出
--------------------------------------------------
提示词方案输出(稳定 JSON):
{
  "sentiment": "mixed",
  "score": 3,
  "pros": [
    "手机很好用",
    "使用体验良好"
  ],
  "cons": [
    "电池续航差",
    "续航不足"
  ]
}

结论:JSON mode + 明确格式描述,无需微调!
# 场景 B:使用领域术语

print("场景 B:需要使用特定领域术语")
print("-" * 50)

fewshot_prompt = [
    {
        "role": "system",
        "content": "你是一位心内科医师,用专业医学术语与同行交流。"
    },
    {
        "role": "user",
        "content": "解释一下心衰的诊断标准"
    },
    {
        "role": "assistant",
        "content": "心力衰竭诊断依据 Framingham 标准,需满足 2 项主要标准。主要标准包括:阵发性夜间呼吸困难、颈静脉怒张、肊部噚音。"
    },
    {
        "role": "user",
        "content": "解释一下心肌棗死的治疗流程"
    }
]

response = _c(
    model=MODEL,
    messages=fewshot_prompt,
    temperature=0,
    max_tokens=200
)

print("Few-shot 提示词输出(专业术语):")
print(response.choices[0].message.content)
print("\n结论:Few-shot 示例引导术语使用,无需微调!")
场景 B:需要使用特定领域术语
--------------------------------------------------
Few-shot 提示词输出(专业术语):


结论:Few-shot 示例引导术语使用,无需微调!
# 场景 C:特定写作风格

print("场景 C:需要特定写作风格(简洁、口语化)")
print("-" * 50)

style_system = (
    "你是一位科技博主,写作风格:\n"
    "1. 句子简短,每句不超过 20 字\n"
    "2. 多用问句开头吸引读者\n"
    "3. 喜欢用「」引号强调重点\n"
    "4. 结尾总是有一个行动呼吁\n\n"
    "示例文章片段:\n"
    "AI 真的会抗走你的工作吗?答案超出你的想象。\n"
    "不是所有工作都危险。「重复性」工作才是高风险区。\n"
    "现在就学一门 AI 工具,从今天开始。"
)

style_prompt = [
    {"role": "system", "content": style_system},
    {"role": "user", "content": "写一段关于大模型微调的介绍,100字以内"}
]

response = _c(
    model=MODEL,
    messages=style_prompt,
    temperature=0.7,
    max_tokens=150
)

print("风格提示词输出:")
print(response.choices[0].message.content)
print("\n结论:把风格示例放入 system prompt,无需微调!")
场景 C:需要特定写作风格(简洁、口语化)
--------------------------------------------------
风格提示词输出:


结论:把风格示例放入 system prompt,无需微调!

Section 3:微调真正适合的场景#

当提示工程无法解决问题时,以下四种场景是微调的正当理由:

1. 极低延迟(需要小模型)#

  • 大模型 API:p99 延迟 2-5 秒

  • 微调后的 7B 小模型自托管:p99 延迟 200-500ms

  • 适用:实时语音、游戏 AI、高频交易辅助

2. 大量重复任务的 Token 成本优化#

  • 场景:每天 100 万次调用,每次 500 tokens

  • GPT-4o:$2.5/M tokens × 500M = $1250/天

  • 微调 GPT-4o-mini 或自托管 Llama:$50-100/天

3. 高度专业领域(私有数据)#

  • 医院内部病历分析系统

  • 法律事务所的合同审查

  • 特点:训练数据高度专业,外部模型没见过

4. 数据隐私(不能发给 API)#

  • 数据合规要求(GDPR、HIPAA)

  • 商业机密

  • 方案:在私有环境微调,本地推理

Section 4:成本估算#

场景:每天 100 万次请求,每次 500 tokens(300 prompt + 200 completion)

DAILY_REQUESTS = 1_000_000
PROMPT_TOKENS = 300
COMPLETION_TOKENS = 200

models = [
    {"name": "GPT-4o (API)", "input_price": 2.50, "output_price": 10.00, "notes": "无需维护,按量付费"},
    {"name": "GPT-4o-mini (微调后)", "input_price": 0.30, "output_price": 1.20, "notes": "需要微调费用,质量接近 GPT-4o"},
    {"name": "Claude 3 Haiku (API)", "input_price": 0.25, "output_price": 1.25, "notes": "Anthropic 最快最便宜"},
    {"name": "Llama 3 8B (自托管)", "input_price": 0, "output_price": 0, "server_daily_cost": 48, "notes": "需要 GPU 服务器"},
]

print(f"场景:每天 {DAILY_REQUESTS:,} 次请求,每次 {PROMPT_TOKENS}+{COMPLETION_TOKENS} tokens")
print("=" * 80)
print(f"{'\u65b9\u6848':<25} {'\u6bcf\u65e5\u6210\u672c':>12} {'\u6bcf\u6708\u6210\u672c':>12} {'\u5907\u6ce8'}")
print("-" * 80)

total_pt = DAILY_REQUESTS * PROMPT_TOKENS / 1_000_000
total_ct = DAILY_REQUESTS * COMPLETION_TOKENS / 1_000_000

for model in models:
    if "server_daily_cost" in model:
        daily_cost = model["server_daily_cost"]
    else:
        daily_cost = total_pt * model["input_price"] + total_ct * model["output_price"]
    monthly_cost = daily_cost * 30
    print(f"{model['name']:<25} ${daily_cost:>10,.2f} ${monthly_cost:>10,.2f}  {model['notes']}")

print("-" * 80)
print("\n\u5173\u952e\u6d1e\u5bdf\uff1a")
print("  GPT-4o mini \u6bd4 GPT-4o \u4fbf\u5b9c\u7ea6 8-10x")
print("  \u81ea\u6258\u7ba1 Llama \u5728 100\u4e07+/\u5929 \u7684\u89c4\u6a21\u4e0b\u6700\u7ecf\u6d4e")
print("  \u5fae\u8c03\u6210\u672c\uff08\u4e00\u6b21\u6027\uff09\u901a\u5e38\u5728 2-3 \u5929\u7684\u8282\u7701\u5185\u56de\u6536")
场景:每天 1,000,000 次请求,每次 300+200 tokens
================================================================================
方案                                每日成本         每月成本 备注
--------------------------------------------------------------------------------
GPT-4o (API)              $  2,750.00 $ 82,500.00  无需维护,按量付费
GPT-4o-mini (微调后)         $    330.00 $  9,900.00  需要微调费用,质量接近 GPT-4o
Claude 3 Haiku (API)      $    325.00 $  9,750.00  Anthropic 最快最便宜
Llama 3 8B (自托管)          $     48.00 $  1,440.00  需要 GPU 服务器
--------------------------------------------------------------------------------

关键洞察:
  GPT-4o mini 比 GPT-4o 便宜约 8-10x
  自托管 Llama 在 100万+/天 的规模下最经济
  微调成本(一次性)通常在 2-3 天的节省内回收

Section 5:数据质量比数量重要#

微调的效果 80% 取决于数据质量。以下是好数据和坏数据的对比。

bad_examples = [
    {
        "problem": "输入过于模糊",
        "input": "帮我写点东西",
        "output": "好的,我来帮你写。",
        "issue": "输入没有具体任务,输出没有实际内容"
    },
    {
        "problem": "输出包含错误信息",
        "input": "Python 中如何反转字符串?",
        "output": "用 reverse() 方法:s.reverse()",
        "issue": "str 没有 reverse() 方法,这是错误答案"
    },
]

good_examples = [
    {
        "quality": "具体明确的任务",
        "input": "用简单语言解释 API,适合非技术读者,100字以内",
        "output": "API 就像餐厅的服务员。你(APP)告诉服务员(API)你想要什么,服务员去厨房取来给你。",
        "quality_note": "输入有明确约束,输出有类比、简洁"
    },
    {
        "quality": "正确且完整的答案",
        "input": "Python 中如何反转字符串?",
        "output": "Python 反转字符串:\n1. 切片:s[::-1]\n2. join+reversed\n\n推荐用切片。",
        "quality_note": "给出多种方法,有推荐,有示例"
    },
]

print("坏数据示例:")
print("-" * 60)
for i, ex in enumerate(bad_examples, 1):
    print(f"\n[坏例 {i}] 问题:{ex['problem']}")
    print(f"  输入: {ex['input']}")
    print(f"  输出: {ex['output']}")
    print(f"  问题: {ex['issue']}")

print("\n\n好数据示例:")
print("-" * 60)
for i, ex in enumerate(good_examples, 1):
    print(f"\n[好例 {i}] 优点:{ex['quality']}")
    print(f"  输入: {ex['input']}")
    print(f"  好在: {ex['quality_note']}")

print("\n\n数据质量检查清单:")
checklist = [
    "输入明确具体,有足够上下文",
    "输出正确无误,经过人工验证",
    "风格一致(格式、语气、长度)",
    "覆盖多样的输入变体",
    "包含边界情况和困难样本",
    "无重复样本(去重)",
]
for item in checklist:
    print(f"  {item}")
坏数据示例:
------------------------------------------------------------

[坏例 1] 问题:输入过于模糊
  输入: 帮我写点东西
  输出: 好的,我来帮你写。
  问题: 输入没有具体任务,输出没有实际内容

[坏例 2] 问题:输出包含错误信息
  输入: Python 中如何反转字符串?
  输出: 用 reverse() 方法:s.reverse()
  问题: str 没有 reverse() 方法,这是错误答案


好数据示例:
------------------------------------------------------------

[好例 1] 优点:具体明确的任务
  输入: 用简单语言解释 API,适合非技术读者,100字以内
  好在: 输入有明确约束,输出有类比、简洁

[好例 2] 优点:正确且完整的答案
  输入: Python 中如何反转字符串?
  好在: 给出多种方法,有推荐,有示例


数据质量检查清单:
  输入明确具体,有足够上下文
  输出正确无误,经过人工验证
  风格一致(格式、语气、长度)
  覆盖多样的输入变体
  包含边界情况和困难样本
  无重复样本(去重)

总结:微调决策框架#

核心原则#

先提示工程,后微调。 微调是最后手段,不是第一选择。

决策检查清单#

在考虑微调之前,确认已完成:

  • [ ] 尝试了详细的 system prompt

  • [ ] 尝试了 few-shot 示例(5-10个)

  • [ ] 尝试了 JSON mode / structured output

  • [ ] 尝试了 RAG(如需私有知识)

  • [ ] 计算了当前 API 成本

微调合理的条件(满足其一):

  • [ ] 延迟要求 < 500ms p99,且 API 无法满足

  • [ ] 每日成本 > $500,且小模型可替代大模型

  • [ ] 数据高度专业,外部模型性能差距 > 20%

  • [ ] 数据合规要求不允许发送给外部 API

user_scenario = {
    "task": "客服邮件自动分类与回复",
    "daily_requests": 50000,
    "current_model": "gpt-4o",
    "current_daily_cost": 125,
    "latency_requirement": "< 3秒",
    "data_available": "有6个月历史邮件,剠8000对",
    "privacy_requirement": "邮件含客户信息,敏感度中等"
}

prompt = (
    "你是一位 LLM 工程顾问。根据以下场景,给出是否需要微调的建议(200字以内):\n\n"
    f"场景:\n"
    f"- 任务:{user_scenario['task']}\n"
    f"- 每日请求量:{user_scenario['daily_requests']:,}\n"
    f"- 当前模型:{user_scenario['current_model']}\n"
    f"- 当前每日成本:${user_scenario['current_daily_cost']}\n"
    f"- 延迟要求:{user_scenario['latency_requirement']}\n"
    f"- 可用数据:{user_scenario['data_available']}\n"
    f"- 隐私要求:{user_scenario['privacy_requirement']}\n\n"
    "请给出:1) 是否推荐微调 2) 推荐的具体方案 3) 预期收益"
)

response = _c(
    model=MODEL,
    messages=[{"role": "user", "content": prompt}],
    temperature=0
)

print("个性化微调建议:")
print("=" * 60)
print(response.choices[0].message.content)
个性化微调建议:
============================================================
1) 推荐微调:是。理由:8000对数据足以显著提升分类与模板化回复效果,并降低调用成本/延迟,前提是做好隐私脱敏与隔离训练。  

2) 方案:两阶段方案——(A)用脱敏数据在可微调的轻量模型(或用 LoRA/adapter 技术在开源大模型上)微调分类器与回复模板化生成;(B)在边缘/私有网络部署分类层,复杂/高敏感度工单回退 gpt-4o。训练留验集、加少量对抗样本,开启加密/访问控制或差分隐私方案。  

3) 预期收益:分类准确率+10–20%,日成本下降约30–50%,端到端延迟更稳定并可达<3s,人工干预与投诉率下降。