上期回顾:长上下文管理的四种策略(滑动窗口、摘要压缩、重要性加权、层级摘要)全部讲透,给出了成本对比和选型指南。这一篇进入微调与训练——当 Prompt 工程走到极限,下一步该怎么做。
一、先问自己:真的需要微调吗?
微调(Fine-tuning)是一个高成本、高收益但也高风险的决策。在花几周时间、几千美元数据标注费用、若干 GPU 小时之前,先问自己三个问题:
``` 问题1:Prompt 工程做到极限了吗?
还没做到的话,先把这些做完: ✅ 详细的系统提示词(五层结构,第八篇) ✅ Few-Shot 示例(3-10个高质量示例) ✅ CoT / ToT 推理增强(第九篇) ✅ 结构化输出约束(第十篇) ✅ 换更强的底座模型(Sonnet → Opus)
只有以上全部做完还不满意,才考虑微调。
问题2:微调能解决的问题是什么?
微调适合解决: ✅ 特定格式的输出需求(公司内部报告格式) ✅ 领域专有术语和知识(法律、医疗、金融) ✅ 特定风格和语调(品牌声音) ✅ 降低延迟(小模型微调后媲美大模型) ✅ 降低成本(用微调的小模型替代昂贵的大模型)
微调解决不了的问题: ❌ 知识截止(要用 RAG,不是微调) ❌ 实时数据(要用工具调用) ❌ 推理能力根本性提升(要换更大的基础模型) ❌ 幻觉问题(微调可能反而让幻觉更顽固)
问题3:你有足够好的训练数据吗?
没有好数据,微调只会让模型"更稳定地犯错"。 通常需要: - SFT:500-5000 条高质量标注样本 - RLHF:10000+ 条人工偏好对比数据 ```
二、微调的三种方式
``` Fine-tuning 技术谱系:
全量微调(Full Fine-tuning) └── 更新所有参数 └── 效果最好,但成本极高 └── 适合:有大量 GPU 资源的大公司
参数高效微调(PEFT) └── LoRA / QLoRA:只训练低秩适配矩阵 └── 效果接近全量,成本低 10-100 倍 └── 适合:中小团队,单 GPU 可运行 └── ✅ 当前最主流的选择
API 微调(Fine-tuning API) └── OpenAI、Anthropic 等提供的托管微调 └── 无需 GPU,按用量付费 └── 最简单,但可控性最低 └── ✅ 入门首选 ```
三、数据准备:微调成败的关键
3.1 数据格式(以 OpenAI 格式为通用标准)
```python
data/prepare.py
import json from dataclasses import dataclass from typing import Optional from pathlib import Path
@dataclass class TrainingExample: """微调训练样本""" system: str # 系统提示词 user: str # 用户输入 assistant: str # 期望的 Agent 输出 quality_score: float = 1.0 # 数据质量评分(0-1) source: str = "human" # human / synthetic / distilled
def to_openai_format(self) -> dict:
"""转换为 OpenAI Fine-tuning 格式"""
return {
"messages": [
{"role": "system", "content": self.system},
{"role": "user", "content": self.user},
{"role": "assistant", "content": self.assistant}
]
}
def to_alpaca_format(self) -> dict:
"""转换为 Alpaca 格式(Llama 微调常用)"""
return {
"instruction": self.system + "\n\n" + self.user,
"input": "",
"output": self.assistant
}
class DatasetBuilder: """训练数据集构建器"""
def __init__(self):
self.examples: list[TrainingExample] = []
def add(self, example: TrainingExample):
if example.quality_score >= 0.8: # 只加入高质量样本
self.examples.append(example)
def add_from_logs(self, conversation_logs: list[dict], min_rating: float = 4.0):
"""
从生产日志中提取高质量对话作为训练数据
(这是获取真实训练数据的最佳来源)
"""
for log in conversation_logs:
if log.get("user_rating", 0) >= min_rating:
example = TrainingExample(
system=log.get("system_prompt", ""),
user=log["user_input"],
assistant=log["agent_output"],
quality_score=log["user_rating"] / 5.0,
source="production_log"
)
self.add(example)
def generate_synthetic(self, n: int = 100, domain: str = "general") -> list[TrainingExample]:
"""
使用强模型生成合成训练数据
(当真实数据不足时的补充方案)
"""
from anthropic import Anthropic
client = Anthropic()
generated = []
batch_prompts = {
"general": "生成多样化的用户问题和高质量回答",
"coding": "生成 Python 编程相关的问题和详细解答",
"analysis": "生成数据分析请求和专业分析报告",
}
for i in range(n // 10): # 每次生成 10 条
response = client.messages.create(
model="claude-sonnet-4-20250514", # 用强模型生成
max_tokens=3000,
messages=[{
"role": "user",
"content": f"""为 AI Agent 微调生成 10 个高质量的训练样本。
领域:{domain} 要求: 1. 问题多样化(不同难度、不同类型) 2. 回答高质量(准确、完整、格式规范) 3. 包含工具调用场景(搜索、计算、代码执行)
以 JSON 数组格式输出,每条包含: {{"user": "用户输入", "assistant": "期望的 Agent 回答"}}""" }] )
text = response.content[0].text
import re
match = re.search(r'\[.*\]', text, re.DOTALL)
if match:
items = json.loads(match.group())
for item in items:
generated.append(TrainingExample(
system=f"你是一个专业的 {domain} AI 助手。",
user=item.get("user", ""),
assistant=item.get("assistant", ""),
quality_score=0.85,
source="synthetic"
))
print(f"✅ 生成了 {len(generated)} 条合成训练数据")
return generated
def validate_quality(self) -> dict:
"""验证数据集质量"""
if not self.examples:
return {"status": "empty"}
# 基础统计
avg_user_len = sum(len(e.user) for e in self.examples) / len(self.examples)
avg_assistant_len = sum(len(e.assistant) for e in self.examples) / len(self.examples)
# 检查重复
user_inputs = [e.user for e in self.examples]
unique_ratio = len(set(user_inputs)) / len(user_inputs)
# 检查长度分布
short_examples = sum(1 for e in self.examples if len(e.assistant) < 50)
return {
"total_examples": len(self.examples),
"avg_user_length": int(avg_user_len),
"avg_assistant_length": int(avg_assistant_len),
"unique_ratio": round(unique_ratio, 2),
"short_examples_count": short_examples,
"quality_issues": [
f"⚠️ {short_examples} 条回答过短(<50字符)" if short_examples > 0 else None,
f"⚠️ 唯一性只有 {unique_ratio:.0%},可能有重复" if unique_ratio < 0.9 else None,
]
}
def split_and_save(self, output_dir: str, train_ratio: float = 0.9):
"""切分训练集和验证集并保存"""
import random
random.shuffle(self.examples)
split_idx = int(len(self.examples) * train_ratio)
train_set = self.examples[:split_idx]
val_set = self.examples[split_idx:]
output_path = Path(output_dir)
output_path.mkdir(exist_ok=True)
# 保存为 JSONL 格式(Fine-tuning API 要求)
for name, dataset in [("train", train_set), ("validation", val_set)]:
path = output_path / f"{name}.jsonl"
with open(path, "w", encoding="utf-8") as f:
for example in dataset:
f.write(json.dumps(example.to_openai_format(), ensure_ascii=False) + "\n")
print(f"✅ 数据集已保存:{len(train_set)} 训练 / {len(val_set)} 验证")
return str(output_path / "train.jsonl"), str(output_path / "validation.jsonl")
```
3.2 数据质量过滤
```python class DataQualityFilter: """训练数据质量过滤器"""
def __init__(self):
from anthropic import Anthropic
self.client = Anthropic()
def filter_batch(self, examples: list[TrainingExample], threshold: float = 0.7) -> list[TrainingExample]:
"""批量过滤低质量样本"""
filtered = []
rejected = 0
for example in examples:
score = self._score_example(example)
if score >= threshold:
example.quality_score = score
filtered.append(example)
else:
rejected += 1
print(f" 过滤结果:保留 {len(filtered)}/{len(examples)},拒绝 {rejected} 条低质量样本")
return filtered
def _score_example(self, example: TrainingExample) -> float:
"""评估单个样本的质量"""
# 快速规则过滤(不消耗 API)
if len(example.user.strip()) < 10:
return 0.0 # 问题太短
if len(example.assistant.strip()) < 20:
return 0.1 # 回答太短
if example.assistant.strip() == example.user.strip():
return 0.0 # 回答等于问题
# LLM 质量评估(更准确,但有成本)
response = self.client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=128,
messages=[{
"role": "user",
"content": f"""评估以下 AI 训练样本的质量(0.0-1.0):
用户:{example.user[:300]} 助手:{example.assistant[:500]}
评分标准: - 回答是否准确且完整(0.4分) - 回答格式是否规范(0.2分) - 是否展示了良好的 Agent 行为(0.2分) - 是否有教学价值(0.2分)
只输出数字分数,如 0.85""" }] )
try:
import re
score_text = response.content[0].text.strip()
score = float(re.search(r'0?\.\d+|[01]', score_text).group())
return min(1.0, max(0.0, score))
except Exception:
return 0.5 # 解析失败时给中等分
```
四、SFT:监督微调
SFT(Supervised Fine-Tuning,监督微调)是最基础的微调方式:用(输入, 期望输出)对训练模型直接模仿高质量回答。
4.1 使用 OpenAI Fine-tuning API(最简单)
```python
training/openai_ft.py
import openai import time from pathlib import Path
class OpenAIFineTuner: """ 使用 OpenAI Fine-tuning API 进行 SFT 支持 gpt-3.5-turbo、gpt-4o-mini 等模型 """
def __init__(self, api_key: str = None):
import os
openai.api_key = api_key or os.getenv("OPENAI_API_KEY")
self.client = openai.OpenAI()
def upload_dataset(self, file_path: str) -> str:
"""上传训练数据集,返回 file_id"""
print(f"📤 上传训练数据:{file_path}")
with open(file_path, "rb") as f:
response = self.client.files.create(file=f, purpose="fine-tune")
file_id = response.id
print(f"✅ 数据上传完成,file_id: {file_id}")
return file_id
def create_job(
self,
train_file_id: str,
val_file_id: str = None,
model: str = "gpt-3.5-turbo",
n_epochs: int = 3,
batch_size: str = "auto",
suffix: str = "agent-v1"
) -> str:
"""创建微调任务"""
print(f"\n🚀 开始微调:{model} × {n_epochs} epochs")
hyperparams = {"n_epochs": n_epochs, "batch_size": batch_size}
kwargs = {
"training_file": train_file_id,
"model": model,
"hyperparameters": hyperparams,
"suffix": suffix
}
if val_file_id:
kwargs["validation_file"] = val_file_id
job = self.client.fine_tuning.jobs.create(**kwargs)
print(f"任务 ID:{job.id}")
return job.id
def monitor_job(self, job_id: str, poll_interval: int = 60) -> dict:
"""监控微调进度,直到完成"""
print(f"\n⏳ 监控微调进度(每 {poll_interval}s 查询一次)...")
while True:
job = self.client.fine_tuning.jobs.retrieve(job_id)
status = job.status
# 获取最新事件
events = self.client.fine_tuning.jobs.list_events(job_id, limit=3)
for event in events.data[::-1]:
print(f" [{event.created_at}] {event.message}")
if status == "succeeded":
print(f"\n✅ 微调完成!模型 ID:{job.fine_tuned_model}")
return {
"status": "succeeded",
"model_id": job.fine_tuned_model,
"trained_tokens": job.trained_tokens
}
elif status in ("failed", "cancelled"):
print(f"\n❌ 微调失败:{job.error}")
return {"status": status, "error": str(job.error)}
print(f" 状态:{status},等待 {poll_interval}s...")
time.sleep(poll_interval)
def evaluate_model(self, model_id: str, test_cases: list[dict]) -> dict:
"""评估微调后的模型"""
print(f"\n🧪 评估模型:{model_id}")
results = []
for case in test_cases:
response = self.client.chat.completions.create(
model=model_id,
messages=[
{"role": "system", "content": case.get("system", "")},
{"role": "user", "content": case["user"]}
],
max_tokens=500
)
output = response.choices[0].message.content
# 简单评估:检查关键词是否存在
expected_keywords = case.get("expected_keywords", [])
keyword_hits = sum(1 for kw in expected_keywords if kw.lower() in output.lower())
score = keyword_hits / max(len(expected_keywords), 1)
results.append({"user": case["user"][:50], "score": score, "output": output[:200]})
avg_score = sum(r["score"] for r in results) / len(results) if results else 0
print(f"平均得分:{avg_score:.2f}")
return {"avg_score": avg_score, "details": results}
完整 SFT 流程
def run_openai_sft_pipeline( train_jsonl: str, val_jsonl: str, model: str = "gpt-3.5-turbo", test_cases: list = None ) -> dict: """端到端的 OpenAI SFT 流程""" ft = OpenAIFineTuner()
# 1. 上传数据
train_id = ft.upload_dataset(train_jsonl)
val_id = ft.upload_dataset(val_jsonl) if val_jsonl else None
# 2. 创建微调任务
job_id = ft.create_job(
train_file_id=train_id,
val_file_id=val_id,
model=model,
n_epochs=3
)
# 3. 等待完成
result = ft.monitor_job(job_id)
# 4. 评估(如果有测试用例)
if result["status"] == "succeeded" and test_cases:
eval_result = ft.evaluate_model(result["model_id"], test_cases)
result["evaluation"] = eval_result
return result
```
4.2 使用 LoRA 微调开源模型
```python
training/lora_ft.py
""" 使用 LoRA 微调开源模型(如 Llama、Mistral、Qwen) 需要:pip install transformers peft datasets bitsandbytes 适合:有 GPU 资源,需要完全控制模型 """
def prepare_lora_training( base_model: str = "Qwen/Qwen2-7B-Instruct", train_data_path: str = "./data/train.jsonl", output_dir: str = "./lora_output", epochs: int = 3, lora_rank: int = 16 # LoRA 秩,越大效果越好但越慢 ): """ 配置并启动 LoRA 微调 单张 A100 (40GB) 可以微调 7B 模型 """ from transformers import ( AutoModelForCausalLM, AutoTokenizer, TrainingArguments, Trainer ) from peft import LoraConfig, get_peft_model, TaskType from datasets import load_dataset import torch
print(f"📦 加载基础模型:{base_model}")
tokenizer = AutoTokenizer.from_pretrained(base_model)
# 4-bit 量化加载(降低显存需求)
from transformers import BitsAndBytesConfig
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=torch.float16,
)
model = AutoModelForCausalLM.from_pretrained(
base_model,
quantization_config=bnb_config,
device_map="auto"
)
# 配置 LoRA
lora_config = LoraConfig(
task_type=TaskType.CAUSAL_LM,
r=lora_rank, # LoRA 秩
lora_alpha=lora_rank * 2, # 缩放因子(通常是 r 的 2 倍)
target_modules=["q_proj", "v_proj", "k_proj", "o_proj"], # 目标层
lora_dropout=0.05,
bias="none",
)
model = get_peft_model(model, lora_config)
trainable_params = sum(p.numel() for p in model.parameters() if p.requires_grad)
total_params = sum(p.numel() for p in model.parameters())
print(f"可训练参数:{trainable_params:,} / {total_params:,} ({trainable_params/total_params:.2%})")
# 加载数据集
def format_example(example):
"""将样本转换为模型输入格式"""
messages = example.get("messages", [])
text = tokenizer.apply_chat_template(messages, tokenize=False)
return {"text": text}
dataset = load_dataset("json", data_files={"train": train_data_path})
formatted = dataset["train"].map(format_example)
# 训练配置
training_args = TrainingArguments(
output_dir=output_dir,
num_train_epochs=epochs,
per_device_train_batch_size=4,
gradient_accumulation_steps=4, # 等效 batch_size=16
learning_rate=2e-4,
fp16=True,
logging_steps=10,
save_steps=100,
save_total_limit=3,
warmup_ratio=0.05,
lr_scheduler_type="cosine",
)
# 开始训练
from trl import SFTTrainer
trainer = SFTTrainer(
model=model,
train_dataset=formatted,
args=training_args,
tokenizer=tokenizer,
dataset_text_field="text",
max_seq_length=2048,
)
print("\n🚀 开始 LoRA 微调...")
trainer.train()
# 保存 LoRA 权重
model.save_pretrained(output_dir)
tokenizer.save_pretrained(output_dir)
print(f"\n✅ LoRA 权重已保存:{output_dir}")
return output_dir
```
五、RLHF:基于人类反馈的强化学习
SFT 让模型模仿示例,RLHF 让模型学会"什么样的输出人类更喜欢"。
```python
training/rlhf.py
""" RLHF 的简化版本:DPO(Direct Preference Optimization) 比完整 RLHF 更简单,效果接近 需要:pip install trl transformers """
from dataclasses import dataclass
@dataclass class PreferenceExample: """偏好对比样本(DPO 训练数据格式)""" prompt: str # 用户输入 chosen: str # 人类认为更好的回答 rejected: str # 人类认为较差的回答 reason: str = "" # 为什么 chosen 更好(可选,用于质量过滤)
class PreferenceDataCollector: """ 收集偏好数据 通常通过以下方式: 1. 人工标注:给两个回答让人选择哪个更好 2. AI 辅助标注:用强模型评判,人工抽样验证 3. 生产反馈:用户的点赞/点踩行为 """
def collect_from_ab_test(
self,
prompts: list[str],
model_a: str, # 基础模型
model_b: str, # 候选模型
human_annotator_func = None # 人工标注函数(或 AI 替代)
) -> list[PreferenceExample]:
"""
通过 A/B 测试收集偏好数据
"""
from anthropic import Anthropic
client = Anthropic()
examples = []
for prompt in prompts:
# 获取两个模型的回答
def get_response(model):
resp = client.messages.create(
model=model,
max_tokens=1000,
messages=[{"role": "user", "content": prompt}]
)
return resp.content[0].text
response_a = get_response(model_a)
response_b = get_response(model_b)
# 人工或 AI 标注哪个更好
if human_annotator_func:
winner = human_annotator_func(prompt, response_a, response_b)
else:
winner = self._ai_judge(prompt, response_a, response_b, client)
if winner == "A":
examples.append(PreferenceExample(
prompt=prompt, chosen=response_a, rejected=response_b
))
elif winner == "B":
examples.append(PreferenceExample(
prompt=prompt, chosen=response_b, rejected=response_a
))
# winner == "TIE" 时跳过(偏好不明确的样本效果差)
return examples
def _ai_judge(self, prompt: str, response_a: str, response_b: str, client) -> str:
"""使用 Claude 判断哪个回答更好(AI 辅助标注)"""
import json, re
response = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=256,
messages=[{
"role": "user",
"content": f"""对于以下问题,判断回答 A 还是 B 更好:
问题:{prompt}
回答 A:{response_a[:500]}
回答 B:{response_b[:500]}
评判维度: 1. 准确性(最重要) 2. 完整性 3. 清晰度 4. 格式规范性
以 JSON 输出:{{"winner": "A" 或 "B" 或 "TIE", "reason": "简短原因"}}""" }] )
text = response.content[0].text
match = re.search(r'\{.*\}', text, re.DOTALL)
if match:
result = json.loads(match.group())
return result.get("winner", "TIE")
return "TIE"
def run_dpo_training( preference_data: list[PreferenceExample], base_model: str = "Qwen/Qwen2-7B-Instruct", output_dir: str = "./dpo_output" ): """ 使用 DPO(Direct Preference Optimization)训练 比 RLHF 更稳定,不需要奖励模型 """ from trl import DPOTrainer, DPOConfig from transformers import AutoModelForCausalLM, AutoTokenizer from datasets import Dataset import torch
# 准备数据集
data_dict = {
"prompt": [e.prompt for e in preference_data],
"chosen": [e.chosen for e in preference_data],
"rejected": [e.rejected for e in preference_data],
}
dataset = Dataset.from_dict(data_dict)
# 加载模型
model = AutoModelForCausalLM.from_pretrained(
base_model, torch_dtype=torch.float16, device_map="auto"
)
tokenizer = AutoTokenizer.from_pretrained(base_model)
# DPO 训练配置
training_args = DPOConfig(
output_dir=output_dir,
num_train_epochs=1,
per_device_train_batch_size=2,
gradient_accumulation_steps=8,
learning_rate=5e-7, # DPO 通常用更小的学习率
beta=0.1, # KL 惩罚系数(防止偏离基础模型太多)
fp16=True,
logging_steps=10,
)
trainer = DPOTrainer(
model=model,
ref_model=None, # 使用基础模型作为参考(自动处理)
args=training_args,
train_dataset=dataset,
tokenizer=tokenizer,
)
print("🚀 开始 DPO 训练...")
trainer.train()
trainer.save_model(output_dir)
print(f"✅ DPO 模型已保存:{output_dir}")
```
六、Agent 专项微调技巧
6.1 工具调用格式对齐
```python
创建专门针对工具调用的训练样本
TOOL_CALL_TRAINING_TEMPLATE = """ 你是一个 AI 助手,可以使用以下工具: - web_search(query): 搜索互联网 - calculate(expression): 数学计算 - execute_python(code): 执行 Python 代码
当需要工具时,以如下格式输出:
工具结果会以如下格式返回:
获得足够信息后,给出最终答案。 """
tool_call_examples = [
TrainingExample(
system=TOOL_CALL_TRAINING_TEMPLATE,
user="苹果公司今天的股价是多少?",
assistant="""我需要搜索实时股价信息。
苹果公司(AAPL)今日股价为 $182.63,较昨日上涨 1.2%。""" ), # ... 更多示例 ] ```
6.2 推理格式对齐
```python
训练模型输出结构化的 CoT 推理
COT_TRAINING_EXAMPLES = [ TrainingExample( system="你是一个严谨的分析师,在给出结论前先进行系统性推理。", user="Python 的 GIL 对多线程性能有什么影响?", assistant="""分析过程:
理解问题: 需要分析 GIL(全局解释器锁)对 Python 多线程的具体影响。
关键事实: 1. GIL 是 CPython 的实现细节,每次只允许一个线程执行 Python 字节码 2. I/O 操作会释放 GIL(文件读写、网络请求等) 3. CPU 密集型操作不释放 GIL
推理: - I/O 密集型任务(Web 请求、文件操作):多线程有效,因为等待 I/O 时 GIL 被释放,其他线程可以运行 - CPU 密集型任务(数学计算、数据处理):多线程几乎无效,GIL 让线程串行执行,甚至因为线程切换开销而比单线程更慢
结论: GIL 对 Python 多线程的影响是双面的: - ✅ I/O 密集型任务:多线程提速显著 - ❌ CPU 密集型任务:多线程无效,应改用多进程(multiprocessing)或异步(asyncio)""" ) ] ```
七、微调效果评估
```python class FineTuneEvaluator: """微调效果评估器"""
def compare_base_vs_finetuned(
self,
base_model_func,
finetuned_model_func,
test_cases: list[dict]
) -> dict:
"""对比基础模型和微调后模型的效果"""
from evaluation.llm_judge import LLMJudge
judge = LLMJudge()
base_scores = []
ft_scores = []
for case in test_cases:
prompt = case["input"]
expected = case.get("expected_keywords", [])
base_output = base_model_func(prompt)
ft_output = finetuned_model_func(prompt)
# 使用 LLM-as-Judge 评估
base_score = judge.judge_relevance(prompt, base_output).score
ft_score = judge.judge_relevance(prompt, ft_output).score
base_scores.append(base_score)
ft_scores.append(ft_score)
print(f"\n问题:{prompt[:50]}...")
print(f" 基础模型:{base_score:.2f} | 微调后:{ft_score:.2f} | {'✅ 提升' if ft_score > base_score else '❌ 下降'}")
avg_base = sum(base_scores) / len(base_scores)
avg_ft = sum(ft_scores) / len(ft_scores)
improvement = (avg_ft - avg_base) / avg_base
return {
"base_avg_score": round(avg_base, 3),
"finetuned_avg_score": round(avg_ft, 3),
"improvement": f"{improvement:+.1%}",
"is_better": avg_ft > avg_base,
"recommendation": "✅ 建议部署微调模型" if avg_ft > avg_base + 0.05 else "⚠️ 提升不显著,评估是否值得"
}
```
八、微调 vs Prompt 工程决策树
你遇到的问题是什么?
│
├── 输出格式不稳定
│ └── 先试:Few-Shot + 结构化输出约束
│ → 仍不满足:SFT(格式对齐)
│
├── 领域知识不足
│ └── 先试:RAG(知识检索)
│ → 专有术语/推理模式问题:SFT
│
├── 语气/风格不符合品牌要求
│ └── 先试:详细的系统提示词 + Few-Shot
│ → 仍不满足:SFT(风格对齐)
│
├── 推理能力不足
│ └── 先试:CoT / ToT + 换更大的模型
│ → 特定推理模式:SFT
│
├── 用户更喜欢另一种回答方式
│ └── 先试:收集反馈优化 Prompt
│ → 有大量偏好数据:DPO/RLHF
│
└── 延迟/成本问题
└── 先试:换小模型 + 优化 Prompt
→ 质量不够:SFT 小模型 ≈ 大模型效果
九、总结
微调的核心认知:
微调是放大器,不是修复工具:好数据 + 微调 = 更好的模型;坏数据 + 微调 = 更稳定地犯错。数据质量是一切的基础。
SFT vs DPO 的选择:
- SFT:知道"正确答案是什么",有标注好的(输入,输出)对
- DPO:知道"哪个答案更好",有偏好对比数据(chosen vs rejected)
- 实践中:先做 SFT,再用 DPO 精调对齐
什么时候停止 Prompt 工程转向微调:当你已经有 500+ 条高质量的"这是我希望模型输出的"样本,而且这个模式足够稳定可以泛化,此时微调的 ROI 才是正的。
下一篇是进阶专题篇的倒数第二篇——垂直领域 Agent 实战,用代码 Agent、数据分析 Agent、客服 Agent 三个完整案例,把系列所有知识综合运用。
下期预告:AI Agent 系列(二十)—— 垂直领域 Agent 实战:代码、数据分析与客服 Agent 完整案例
作者:[你的署名] 转载:请注明出处和系列链接
夜雨聆风