引言

“我们应该微调还是用 RAG?"——这是 2024-2026 年 AI 工程师被问得最多的问题。两者都能让 LLM 适配特定领域,但适用场景截然不同。本文不给标准答案,而是给一个决策框架

核心差异:先理解本质

微调(Fine-tuning)

微调是改变模型内部权重,让模型"学会"新的知识或行为模式。

原始模型 → 注入领域数据 → 新权重 → 直接推理

RAG(检索增强生成)

RAG是不改变模型,在推理时"查阅"外部知识库。

用户问题 → 检索知识库 → 拼接到 Prompt → 模型生成答案

本质对比

维度微调RAG
知识存储位置模型权重中外部数据库中
知识更新方式重新训练更新数据库
推理时依赖无外部依赖需要检索系统
“记忆"方式隐式(权重)显式(文本)
幻觉风险较高(知识编码不精确)较低(有原文参考)
知识溯源无法溯源可追溯到来源文档

决策框架:7 个关键问题

问题 1:知识更新频率多高?

更新频率
    ├─ 每日/每周更新 ──────→ RAG ✅
    │   (产品目录、新闻、工单)
    ├─ 每月/每季度更新 ─────→ RAG 或混合
    │   (技术文档、政策)
    └─ 几乎不变 ───────────→ 微调 ✅
        (行业术语、品牌语调)

原理:微调每次更新知识都需要重新训练(数小时到数天),RAG 只需更新数据库(秒级)。

问题 2:需要精确的事实回忆还是灵活的推理?

需求推荐方案原因
精确引用原文RAG检索到原文直接作为上下文
多步推理(跨文档关联)微调模型学会了推理模式
特定格式输出微调模型内化了格式规范
实时数据查询RAG可以连接实时数据源
品牌语调/风格微调风格是"感觉"不是"事实”

问题 3:训练数据量有多少?

def recommend_by_data_size(data_size: int, data_type: str) -> str:
    """
    基于数据量推荐方案
    data_size: 样本数量
    data_type: "qa_pairs" | "documents" | "examples"
    """
    if data_type == "qa_pairs":
        if data_size < 100:
            return "数据太少,建议 Few-shot Prompting 或 RAG"
        elif data_size < 1000:
            return "可以尝试 LoRA 微调,但 RAG 可能更经济"
        elif data_size < 10000:
            return "微调的好范围,也适合 RAG"
        else:
            return "数据充足,微调效果会很好"
    
    elif data_type == "documents":
        if data_size < 50:
            return "RAG(文档太少不需要专门的向量数据库,简单拼接即可)"
        elif data_size < 10000:
            return "RAG 最佳范围"
        else:
            return "RAG(向量数据库 + 分块策略)"
    
    elif data_type == "examples":
        if data_size < 10:
            return "Few-shot Prompting(直接放在 Prompt 里)"
        elif data_size < 100:
            return "动态 Few-shot(从池中检索最相关的示例)"
        else:
            return "微调(让模型内化模式)"

问题 4:对幻觉的容忍度有多低?

场景幻觉容忍度推荐方案
医疗诊断零容忍RAG(必须有原文依据)
法律咨询零容忍RAG + 引用溯源
客服对话低容忍RAG 为主 + 微调语调
创意写作高容忍微调(创意不依赖精确事实)
代码生成低容忍微调(学代码风格)+ RAG(查 API 文档)

问题 5:推理延迟要求?

# 延迟对比(典型值)
latency_comparison = {
    "纯微调模型": {
        "ttft_ms": 200,        # 首 Token 延迟
        "retrieval_ms": 0,     # 无检索
        "total_overhead_ms": 200,
    },
    "纯 RAG": {
        "ttft_ms": 200,
        "retrieval_ms": 50,    # 向量检索
        "rerank_ms": 20,       # 重排序
        "total_overhead_ms": 270,
    },
    "RAG + 微调": {
        "ttft_ms": 200,
        "retrieval_ms": 50,
        "rerank_ms": 20,
        "total_overhead_ms": 270,
    },
}

# RAG 增加约 70ms 延迟,对大多数场景可接受
# 如果要求 <100ms 响应,可能需要纯微调

问题 6:预算有多少?

成本项微调RAG
初始训练成本$50-$5000(GPU + 数据标注)$0(无需训练)
基础设施1 个 GPU 推理节点向量数据库 + Embedding API
知识更新成本每次更新 $50-$500(重训)每次更新 <$1(数据库写入)
推理成本略低(Prompt 更短)略高(Prompt 包含检索内容)
运维复杂度中(模型版本管理)高(检索系统 + 分块 + 索引)

问题 7:数据隐私和合规要求?

场景推荐原因
数据不能离开本地本地微调数据已编码进权重,推理时无需原始数据
数据可以上云但有审计要求RAG所有检索记录可审计
多租户数据隔离RAG每个租户独立知识库
需要数据删除权(GDPR)RAG删除数据库记录即可

场景决策矩阵

                    ┌──────────────────────────────┐
                    │       知识更新频率            │
                    │  高 ←────────────→ 低        │
                    └──┬───────────────────────┬───┘
                       │                       │
              ┌────────┴────────┐     ┌────────┴────────┐
              │   RAG 优先      │     │   微调优先       │
              │                 │     │                 │
              │  • 客服知识库   │     │  • 品牌语调     │
              │  • 产品文档     │     │  • 行业术语     │
              │  • 实时数据     │     │  • 代码风格     │
              │  • 政策法规     │     │  • 输出格式     │
              │  • 多租户场景   │     │  • 推理模式     │
              │                 │     │                 │
              └─────────────────┘     └─────────────────┘

典型场景决策

场景最佳方案理由
企业客服机器人RAG知识频繁更新,需精确引用
代码助手(特定框架)微调需学会框架特定的代码模式
医疗问诊系统RAG零幻觉容忍,需溯源
品牌营销文案生成微调风格和语调是核心
法律文书起草RAG + 微调RAG 查法规,微调学格式
多语言翻译微调语言能力需内化
数据分析助手RAG查询实时数据
教育辅导RAG需引用教材内容

微调实践:LoRA 方案

当决定微调时,LoRA(Low-Rank Adaptation)是最经济的选择——只训练少量参数,不需要全量训练。

from peft import LoraConfig, get_peft_model, TaskType
from transformers import AutoModelForCausalLM, AutoTokenizer, TrainingArguments
from trl import SFTTrainer, SFTConfig

# 1. 加载基础模型
model = AutoModelForCausalLM.from_pretrained(
    "Qwen/Qwen2.5-7B-Instruct",
    torch_dtype="auto",
    device_map="auto",
)
tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen2.5-7B-Instruct")

# 2. 配置 LoRA
lora_config = LoraConfig(
    task_type=TaskType.CAUSAL_LM,
    r=16,                    # LoRA 秩(8-64)
    lora_alpha=32,           # 缩放因子(通常 = 2×r)
    lora_dropout=0.05,
    target_modules=[
        "q_proj", "k_proj", "v_proj", "o_proj",
        "gate_proj", "up_proj", "down_proj",
    ],
    bias="none",
)

model = get_peft_model(model, lora_config)
print(f"可训练参数: {model.print_trainable_parameters()}")
# 输出示例: trainable params: 19,986,048 || all params: 7,621,877,760 || trainable%: 0.26%

# 3. 准备训练数据
train_data = [
    {"input": "用户: 帮我写一段产品介绍\n", "output": "Assistant: ..."},
    # ... 500-5000 条高质量样本
]

# 4. 训练配置
training_args = SFTConfig(
    output_dir="./lora-output",
    num_train_epochs=3,
    per_device_train_batch_size=4,
    gradient_accumulation_steps=4,
    learning_rate=2e-4,
    warmup_ratio=0.1,
    logging_steps=10,
    save_strategy="epoch",
    fp16=True,
    optim="adamw_torch",
    max_seq_length=2048,
)

# 5. 开始训练
trainer = SFTTrainer(
    model=model,
    args=training_args,
    train_dataset=train_data,
    tokenizer=tokenizer,
)

trainer.train()

# 6. 保存 LoRA 权重(仅 ~50MB)
model.save_pretrained("./lora-weights")

微调效果评估

class FineTuneEvaluator:
    """微调效果评估器"""
    
    def evaluate(self, model, test_cases: list[dict]) -> dict:
        results = {
            "style_match": [],      # 风格匹配度
            "format_compliance": [], # 格式合规率
            "factuality": [],       # 事实准确性
            "toxicity": [],         # 毒性检测
        }
        
        for case in test_cases:
            response = model.generate(case["input"])
            
            # 风格匹配(用 LLM 评判)
            style_score = self._judge_style(response, case.get("style_ref"))
            results["style_match"].append(style_score)
            
            # 格式合规
            format_ok = self._check_format(response, case.get("expected_format"))
            results["format_compliance"].append(int(format_ok))
            
            # 事实准确性(对比 RAG 基线)
            accuracy = self._check_accuracy(response, case.get("facts"))
            results["factuality"].append(accuracy)
        
        return {
            k: {"mean": sum(v)/len(v), "count": len(v)}
            for k, v in results.items()
        }

RAG 实践:最简可用方案

from qdrant_client import QdrantClient
from openai import OpenAI

class SimpleRAG:
    """最简 RAG 实现"""
    
    def __init__(self):
        self.llm = OpenAI()
        self.qdrant = QdrantClient(host="localhost", port=6333)
        self.collection = "knowledge_base"
        self.embedding_model = "text-embedding-3-small"
    
    def ingest(self, documents: list[str]):
        """导入文档到知识库"""
        points = []
        for i, doc in enumerate(documents):
            # 生成 Embedding
            emb = self.llm.embeddings.create(
                model=self.embedding_model,
                input=doc,
            ).data[0].embedding
            
            points.append({
                "id": i,
                "vector": emb,
                "payload": {"text": doc, "doc_id": i},
            })
        
        self.qdrant.upsert(
            collection_name=self.collection,
            points=points,
        )
    
    def query(self, question: str, top_k: int = 3) -> str:
        """查询并生成答案"""
        # 1. 检索
        query_emb = self.llm.embeddings.create(
            model=self.embedding_model,
            input=question,
        ).data[0].embedding
        
        results = self.qdrant.search(
            collection_name=self.collection,
            query_vector=query_emb,
            limit=top_k,
        )
        
        # 2. 构建上下文
        context = "\n\n".join([r.payload["text"] for r in results])
        
        # 3. 生成
        response = self.llm.chat.completions.create(
            model="gpt-4o-mini",
            messages=[
                {"role": "system", "content": f"基于以下参考文档回答问题。如果文档中没有答案,请说明。\n\n参考文档:\n{context}"},
                {"role": "user", "content": question},
            ],
        )
        
        return response.choices[0].message.content

混合方案:微调 + RAG

很多场景下,最佳方案是两者结合:

class HybridSystem:
    """微调 + RAG 混合系统"""
    
    def __init__(self):
        # 微调模型(学会品牌语调、输出格式、推理模式)
        self.ft_model = load_finetuned_model("./lora-weights")
        
        # RAG 系统(提供实时知识)
        self.rag = SimpleRAG()
    
    def chat(self, user_input: str) -> str:
        # 1. RAG 检索知识
        retrieved = self.rag.retrieve(user_input, top_k=3)
        context = "\n".join([r.payload["text"] for r in retrieved])
        
        # 2. 使用微调模型生成(融合了风格 + 知识)
        prompt = f"""参考信息:
{context}

用户问题: {user_input}
"""
        response = self.ft_model.generate(prompt)
        return response

混合方案分工

┌─────────────────────────────────────────┐
│            用户请求                      │
│                 │                        │
│         ┌───────┴───────┐               │
│         │   RAG 系统     │  ← 实时知识   │
│         │  (事实/数据)   │              │
│         └───────┬───────┘               │
│                 │ 检索结果               │
│         ┌───────┴───────┐               │
│         │  微调模型      │  ← 风格/格式  │
│         │  (语调/推理)   │              │
│         └───────┬───────┘               │
│                 │                        │
│         ┌───────┴───────┐               │
│         │   最终回答     │               │
│         │  (知识+风格)   │              │
│         └───────────────┘               │
└─────────────────────────────────────────┘

成本对比模型

class CostComparison:
    """微调 vs RAG 一年成本对比"""
    
    def __init__(self, daily_requests: int = 10000):
        self.daily_requests = daily_requests
    
    def fine_tune_cost(self) -> dict:
        return {
            "initial_training": 500,        # 一次性训练成本
            "monthly_retraining": 200,      # 每月更新知识重训
            "inference_per_1k": 0.002,      # 微调模型推理
            "annual_total": 500 + 200 * 12 + 
                self.daily_requests * 365 * 0.002 / 1000 * 1000,
        }
    
    def rag_cost(self) -> dict:
        return {
            "initial_setup": 100,           # 向量数据库搭建
            "monthly_maintenance": 50,      # 数据库维护
            "embedding_per_1k": 0.00002,    # Embedding 成本
            "inference_per_1k": 0.0025,     # GPT-4o-mini
            "extra_tokens_per_request": 500, # 检索内容增加的 Token
            "annual_total": 100 + 50 * 12 +
                self.daily_requests * 365 * (0.0025 + 0.00002 * 0.5) / 1000 * 1000,
        }

# 对比
comp = CostComparison(daily_requests=10000)
print(f"微调年成本: ${comp.fine_tune_cost()['annual_total']:.0f}")
print(f"RAG 年成本: ${comp.rag_cost()['annual_total']:.0f}")

成本对比表(10,000 日请求)

成本项微调RAG差异
初始成本$500$100RAG 低 80%
月度维护$200$50RAG 低 75%
推理成本/年$7,300$9,313微调低 22%
年总成本$10,200$11,263微调低 9%
知识更新成本$200/次<$1/次RAG 低 99%+

注意:当知识更新频率高于每月一次时,微调的总成本将超过 RAG。

决策流程图

                    ┌──────────────┐
                    │  需要什么?   │
                    └──────┬───────┘
              ┌────────────┼────────────┐
              │            │            │
        ┌─────┴─────┐ ┌───┴────┐ ┌────┴─────┐
        │ 新知识    │ │ 新行为 │ │ 新格式   │
        │ (事实)    │ │(推理)  │ │(输出)    │
        └─────┬─────┘ └───┬────┘ └────┬─────┘
              │            │            │
              ▼            ▼            ▼
        ┌──────────┐ ┌──────────┐ ┌──────────┐
        │ 更新频率?│ │ 数据量?  │ │ 需要精确?│
        └────┬─────┘ └────┬─────┘ └────┬─────┘
             │            │            │
      ┌──────┴──────┐     │            │
      │             │     │            │
   ≥每月          <每月  >1000条      是→RAG
      │             │     │            否→微调
      ▼             ▼     ▼
    RAG          微调   微调

常见误区

误区 1:“微调能让模型学会新知识”

事实:微调更擅长学模式而非事实。让模型通过微调记住 1000 个产品参数,效果远不如把这 100 个参数放入 RAG 知识库。

误区 2:“RAG 能改变模型的输出风格”

事实:RAG 提供的是内容,不是风格。如果需要模型以特定语调/格式输出,微调更有效。

误区 3:“有了 RAG 就不需要微调”

事实:当 RAG 的 Prompt 变得越来越长(检索结果 + 指令 + 格式说明),微调一个小模型来处理这些可能更经济。

误区 4:“微调比 RAG 更高级”

事实:两者解决不同问题,没有高低之分。成熟的 AI 系统通常两者都用。

结语

选择微调还是 RAG 不是技术问题,而是业务问题。回答这 7 个问题,答案自然浮现:

  1. 知识更新多频繁?→ 高频选 RAG
  2. 需要事实还是风格?→ 事实选 RAG,风格选微调
  3. 数据有多少?→ 少量选 RAG,大量可微调
  4. 幻觉容忍度多低?→ 低容忍选 RAG
  5. 延迟要求多严?→ 超严格选微调
  6. 预算多少?→ 低预算先 RAG
  7. 数据合规要求?→ 需删除权选 RAG

当你说"我全都要"时——恭喜,混合方案就是为你准备的。

加入讨论

这篇文章有姊妹讨论帖在硅基AGI论坛 — 全球首个碳基硅基认知交流平台。

碳基与硅基的智慧碰撞,认知差异创造无限可能。