微調整と RAG: いつ何を使用するか
AI アプリケーションの開発における最も重要な決定の 1 つは選択です。 の間 微調整 e 検索拡張生成 (RAG)。 どちらのアプローチでも LLM の動作をカスタマイズできますが、 それらは基本的に異なる方法で動作し、異なるシナリオに適しています。
このシリーズの 6 回目の記事では、 Web 開発者のための AI、分析します 両方の手法を深く掘り下げて、実践的な意思決定フレームワークを構築します。 それぞれのアプローチの具体例を実行していきます。最終的には、 各プロジェクトに適切な戦略を選択するためのツール。
何を学ぶか
- 微調整と RAG の基本的な違いを理解する
- 微調整、RAG、またはハイブリッド アプローチをいつ使用するのが最適かを特定する
- OpenAI APIを使用して微調整を実装する
- 効率的な微調整のための LoRA と QLoRA の理解
- 各アプローチのコスト、時間、複雑さを分析する
- フローチャートの意思決定フレームワークを使用する
- 実際のケーススタディを調べる
- ハイブリッド微調整 + RAG アプローチを実装する
シリーズ概要
| # | アイテム | 集中 |
|---|---|---|
| 1 | RAG の概要 | 基本的な概念 |
| 2 | TypeScript と LangChain を使用した RAG | 実用化 |
| 3 | ベクトルデータベースの比較 | Chromadb、松ぼっくり、Weaviate |
| 4 | OpenAI と Anthropic API | API統合 |
| 5 | オラマとLLMローカル | クラウドを使わないAI |
| 6 | 微調整 vs RAG (ここにいます) | いつ何を使うか |
| 7 | AIエージェント | エージェントベースのアーキテクチャ |
| 8 | CI/CD における AI | インテリジェントな自動化 |
1. 基本的な概念
2 つのアプローチを比較する前に、それらが何を行うのかを理解することが重要です 正確に、そしてそれらが言語モデルの動作をどのように変えるかについて説明します。
微調整とは
Il 微調整 モデルのトレーニングを継続することで構成されます ドメイン固有のデータセットを使用して事前トレーニングされています。これで私は変わります 内部分銅 ニューラルネットワークのモデルに新しいパターンを教え、 永続的な部分となる対応スタイルや専門知識 モデル自体の。
RAGとは何ですか
La 検索拡張生成 (RAG) プロンプトを充実させる その時点で外部ソースから取得した情報とともにモデルに送信されます リクエストの。モデルは変更されません。単に受信するだけです。 正確な回答を生成できるようにする、より豊富なコンテキスト そして更新されました。
概念的な比較
| 待ってます | 微調整 | ラグ |
|---|---|---|
| 機種変更 | はい - 内部分銅を変更しました | いいえ - モデルは変更されていません |
| 知識が生きている場所 | モデルパラメータ内 | 外部ソース (データベース、ドキュメント) |
| データ更新 | 再トレーニングが必要 | ソースの即時更新 |
| 引用 | 出典を引用できません | 特定の文書を引用することもできます |
| 初期費用 | 高 (GPU、データセット、時間) | 中(ベクターDB、埋め込み) |
| リクエストあたりのコスト | 低(専用モデル) | 最高(取得 + 生成) |
| レイテンシ | 低 (直接推論) | 上位 (検索 + 推論) |
| 幻覚 | 可能だが制御は難しい | 検証されたコンテキストのおかげで削減されました |
2. 微調整を使用する場合
微調整は、変更が必要な場合に最適な選択です。 行動 独自のものではなくモデルの 知識。 微調整が優れているシナリオは次のとおりです。
微調整に最適なシナリオ
| シナリオ | Esempio | 微調整なので |
|---|---|---|
| 対応スタイル | 特定の口調のビジネスチャットボット | スタイルは一貫していて、指示が繰り返されてはなりません |
| 出力形式 | 特定の構造化された JSON を生成する | 形式は正確で信頼できるものでなければなりません |
| ドメイン言語 | 医学用語または法律用語 | テンプレートには専門用語が含まれている必要があります |
| 反復的なタスク | サポートチケットの分類 | モデルは同じパターンを何千回も実行する必要があります |
| 即時削減 | 長いシステム命令を排除する | 大容量のトークンとコストを節約 |
| 特定の動作 | 特定の種類のリクエストを拒否する | ルールはモデルに固有である必要があります |
微調整を使用しない場合
- 頻繁に変更されるデータ: ニュース、価格、在庫 - RAG を使用する
- 小規模なデータセット (<100 例): 微調整には数百または数千のサンプルが必要です
- 引用が必要: 微調整ではソースを示すことができません
- 限られた予算: 微調整には大量の計算リソースが必要です
- プロトタイピング: 迅速な反復には遅すぎるため、迅速なエンジニアリングを使用してください
3. RAG を使用する場合
モデルを提供する必要がある場合は、RAG が最適な選択です。 具体的かつ最新の知識 モデル自体を変更することなく。 大量の動的な情報の処理に優れています。
RAG の理想的なシナリオ
| シナリオ | Esempio | なぜRAGなのか |
|---|---|---|
| 動的データ | 製品ドキュメントを更新しました | 情報は頻繁に変わります |
| 大きなコーパス | 数千のドキュメントを含む企業ナレッジベース | データが多すぎてモデルの重みに適合しない |
| トレーサビリティ | 規制を参照した法的回答 | 特定の情報源を引用する必要がある |
| マルチテナント | 各顧客は独自の知識ベースを持っています | 1 つのモデル、多数の異なるデータ ソース |
| コンプライアンス | 会社方針に基づく対応 | 回答は検証可能である必要があります |
| 低コスト | 限られた予算でのスタートアップ | トレーニング費用はかかりません |
4. OpenAIによる微調整の実装
OpenAI は、プロセスを簡素化するマネージド微調整サービスを提供します。 GPU やインフラストラクチャを管理する必要はありません。データセットをアップロードし、 ジョブを起動すると、すぐに使用できるカスタム テンプレートが得られます。
データセットを準備する
データセットは JSONL 形式 (JSON Lines) である必要があります。各行は は完全な会話の例を表しています。データセットの品質 それは微調整を成功させるための最も重要な要素です。
// training-data.jsonl (un oggetto JSON per riga)
{"messages": [{"role": "system", "content": "Sei un assistente per il supporto tecnico della piattaforma Acme. Rispondi in modo conciso e professionale."}, {"role": "user", "content": "Non riesco ad accedere al mio account"}, {"role": "assistant", "content": "Mi dispiace per l'inconveniente. Ecco i passaggi per recuperare l'accesso:\n1. Vai su acme.com/reset\n2. Inserisci la tua email\n3. Controlla la casella di posta\n4. Segui il link di reset\n\nSe il problema persiste, verifica che la tua email sia corretta o contatta il supporto su support@acme.com."}]}
{"messages": [{"role": "system", "content": "Sei un assistente per il supporto tecnico della piattaforma Acme. Rispondi in modo conciso e professionale."}, {"role": "user", "content": "Come posso aggiornare il mio piano?"}, {"role": "assistant", "content": "Per aggiornare il piano:\n1. Accedi alla Dashboard\n2. Vai su Impostazioni > Fatturazione\n3. Seleziona il nuovo piano\n4. Conferma il pagamento\n\nIl cambio sarà effettivo dal prossimo ciclo di fatturazione."}]}
{"messages": [{"role": "system", "content": "Sei un assistente per il supporto tecnico della piattaforma Acme. Rispondi in modo conciso e professionale."}, {"role": "user", "content": "L'API restituisce errore 403"}, {"role": "assistant", "content": "L'errore 403 indica un problema di autorizzazione. Verifica:\n1. Che la tua API key sia valida e non scaduta\n2. Che il piano attivo includa l'accesso API\n3. Che gli scope della chiave coprano l'endpoint richiesto\n\nPuoi rigenerare la chiave da Dashboard > API Keys."}]}
微調整を開始する
// src/scripts/fine-tune.ts
import OpenAI from 'openai';
import fs from 'fs';
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
});
async function startFineTuning() {
// 1. Caricare il file di training
console.log('Caricamento dataset...');
const file = await openai.files.create({
file: fs.createReadStream('./training-data.jsonl'),
purpose: 'fine-tune',
});
console.log(`File caricato: ${file.id}`);
// 2. Creare il job di fine-tuning
console.log('Avvio fine-tuning...');
const job = await openai.fineTuning.jobs.create({
training_file: file.id,
model: 'gpt-4o-mini-2024-07-18',
hyperparameters: {
n_epochs: 3,
batch_size: 'auto',
learning_rate_multiplier: 'auto',
},
suffix: 'acme-support', // nome personalizzato
});
console.log(`Job creato: ${job.id}`);
// 3. Monitorare il progresso
console.log('Monitoraggio progresso...');
let status = job.status;
while (status !== 'succeeded' && status !== 'failed') {
await new Promise(r => setTimeout(r, 30000)); // attendi 30s
const updated = await openai.fineTuning.jobs.retrieve(job.id);
status = updated.status;
console.log(`Stato: ${status}`);
if (status === 'succeeded') {
console.log(`Modello pronto: ${updated.fine_tuned_model}`);
return updated.fine_tuned_model;
}
if (status === 'failed') {
console.error('Fine-tuning fallito:', updated.error);
throw new Error('Fine-tuning fallito');
}
}
}
// 4. Usare il modello fine-tuned
async function useFineTunedModel(modelId: string) {
const response = await openai.chat.completions.create({
model: modelId, // es: "ft:gpt-4o-mini-2024-07-18:org:acme-support:abc123"
messages: [
{
role: 'user',
content: 'Come posso integrare il webhook?'
}
],
});
console.log(response.choices[0].message.content);
}
startFineTuning();
5. LoRA と QLoRA: 効率的な微調整
従来の微調整では、すべてのモデルパラメータを更新する必要がありますが、 メモリと計算の点で高価なプロセスです。 LoRA (下位ランク 適応)e QLoRA (量子化された LoRA) は代替手段を提供します 大幅に効率が向上します。
LoRA の仕組み
LoRA は、数十億のパラメータをすべて更新する代わりに、元の重みを凍結します。 そして小さなフィッティング行列を追加します(アダプター) 非常にランクが高い 低い。これにより、トレーニング可能なパラメータの数が 99% 以上減少しますが、 完全な微調整に匹敵する品質。
微調整方法の比較
| 方法 | トレーニング可能なパラメータ | VRAMが必要です | 時間 | 品質 |
|---|---|---|---|---|
| 完全な微調整 | 100% (すべて) | 80GB以上(7Bの場合) | 時間/日数 | 可能な限り最善の |
| LoRA | 0.1~1% | 16~24GB(7Bの場合) | 分/時間 | ほぼフルハウスに近い |
| QLoRA | 0.1-1% + 量子化 | 6~10GB(7Bの場合) | 分/時間 | やや低め |
# Nota: il fine-tuning con LoRA richiede Python e PyTorch
# Questo e un esempio concettuale per comprendere il processo
# requirements.txt
# transformers==4.38.0
# peft==0.8.0
# bitsandbytes==0.42.0
# datasets==2.17.0
# trl==0.7.0
from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import LoraConfig, get_peft_model, TaskType
from trl import SFTTrainer, SFTConfig
from datasets import load_dataset
# Carica il modello base
model_name = "meta-llama/Llama-3.1-8B-Instruct"
model = AutoModelForCausalLM.from_pretrained(
model_name,
load_in_4bit=True, # QLoRA: quantizzazione 4-bit
device_map="auto",
)
tokenizer = AutoTokenizer.from_pretrained(model_name)
# Configura LoRA
lora_config = LoraConfig(
task_type=TaskType.CAUSAL_LM,
r=16, # rango delle matrici di adattamento
lora_alpha=32, # fattore di scala
lora_dropout=0.05, # dropout per regolarizzazione
target_modules=[ # moduli da adattare
"q_proj", "k_proj", "v_proj",
"o_proj", "gate_proj", "up_proj", "down_proj"
],
)
# Applica LoRA al modello
model = get_peft_model(model, lora_config)
model.print_trainable_parameters()
# Output: trainable params: 13,631,488 || all params: 8,043,925,504
# Percentuale: 0.17% dei parametri totali
# Carica e prepara il dataset
dataset = load_dataset("json", data_files="training-data.jsonl")
# Configura e avvia il training
training_config = SFTConfig(
output_dir="./output",
num_train_epochs=3,
per_device_train_batch_size=4,
learning_rate=2e-4,
warmup_steps=100,
logging_steps=10,
save_steps=500,
)
trainer = SFTTrainer(
model=model,
args=training_config,
train_dataset=dataset["train"],
tokenizer=tokenizer,
)
# Avvia il fine-tuning
trainer.train()
# Salva il modello LoRA (solo gli adapter, pochi MB)
model.save_pretrained("./acme-support-lora")
TypeScript 開発者のための LoRA
LoRA での微調整には Python が必要ですが、TypeScript 開発者としては Ollama を使用すると、微調整されたモデルをローカルで利用できます。トレーニングの後は、 モデルを GGUF 形式に変換し、Ollama にインポートして使用します。 前の記事で見たのと同じ REST API 経由で。
6. コスト分析
多くの場合、微調整と RAG のどちらを選択するかを決定するのはコストです。 両方のアプローチの実際のコストを分析してみましょう。 1 日あたり 10,000 件のリクエストがあるアプリケーション。
コストの比較 (10,000 リクエスト/日)
| 原価項目 | 微調整(OpenAI) | RAG (松ぼっくり + GPT-4o) |
|---|---|---|
| 初期設定 | $50-200 (トレーニングの仕事) | $0 ~ 100 (初期埋め込み) |
| インフラストラクチャー | $0 (OpenAI がホストするモデル) | $70/月 (松ぼっくりスターター) |
| リクエストあたりのコスト | ~$0.001 (ミニ微調整モデル) | ~$0.003 (埋め込み + 取得 + 生成) |
| 月額費用 (10,000/日) | ~300ドル | ~970ドル |
| データ更新 | 再トレーニングごとに 50 ~ 200 ドル | ドキュメント更新ごとに ~0.01 ドル |
注記: コストは 2026 年 2 月の価格に基づいた見積もりです 量や複雑さによって大きく異なる場合があります プロンプトの内容と選択したモデル。
考慮すべき隠れたコスト
- 微調整: データセットの準備時間 (人間の作業時間/日数)
- 微調整: データ変更時の再トレーニングのコスト
- ラグ: インデックス作成パイプラインのメンテナンス
- ラグ: 追加された各ドキュメントの埋め込みのコスト
- 両方: 統合の開発とテストにかかる時間
7. 意思決定の枠組み
選択を容易にするために、次の形式の意思決定フレームワークを示します。 フローチャート。そこに到達するには、質問に順番に答えてください あなたのケースに最適な提案を行います。
// Framework decisionale: Fine-tuning vs RAG
function decidiApproccio(requisiti: Requisiti): Approccio {
// DOMANDA 1: I dati cambiano frequentemente?
if (requisiti.datiDinamici) {
// I dati cambiano spesso -> RAG e quasi sempre meglio
if (requisiti.necessitaCitazioni) {
return 'RAG'; // Dati dinamici + citazioni = RAG
}
if (requisiti.stilePersonalizzato) {
return 'IBRIDO'; // Dati dinamici + stile = ibrido
}
return 'RAG';
}
// DOMANDA 2: Serve uno stile/formato specifico?
if (requisiti.stilePersonalizzato || requisiti.formatoOutput) {
if (requisiti.datasetDisponibile && requisiti.dataset.size > 100) {
return 'FINE_TUNING'; // Stile + dataset buono = fine-tuning
}
return 'PROMPT_ENGINEERING'; // Stile ma poco dataset = prompt eng.
}
// DOMANDA 3: Il corpus di conoscenza e grande?
if (requisiti.corpusDocumenti > 50) {
return 'RAG'; // Troppi documenti per il contesto
}
// DOMANDA 4: Serve ridurre i costi per richiesta?
if (requisiti.volumeAlto && requisiti.taskRipetitivo) {
return 'FINE_TUNING'; // Alto volume + task fisso = fine-tuning
}
// Default: inizia con RAG (più flessibile)
return 'RAG';
}
クイック意思決定ガイド
| 必要な場合は... | アメリカ合衆国 |
|---|---|
| 最新の社内文書に基づく対応 | ラグ |
| 特定の口調と個性を持つチャットボット | 微調整 |
| 正確な JSON 形式で出力を生成する | 微調整 |
| 製品カタログに関するご質問にお答えします | ラグ |
| サポートチケットを自動的に分類する | 微調整 |
| 規制に関する参考資料を備えた法務アシスタント | ラグ |
| 業界用語を使った翻訳 | 微調整 |
| ドキュメントを引用した FAQ ボット | ラグ |
| トーンと知識ベースを備えた専門アシスタント | ハイブリッド |
8. ハイブリッド アプローチ: 微調整 + RAG
現実の多くのシナリオでは、両方のアプローチを組み合わせた最適なソリューションが得られます。 モデルはスタイル、形式、動作をキャプチャするために微調整されています RAG は現時点での最新の知識を提供します。 リクエストの。
// src/lib/hybrid-ai.ts
import OpenAI from 'openai';
import { VectorStore } from './vector-store';
interface HybridConfig {
fineTunedModel: string; // Modello fine-tuned per stile
embeddingModel: string; // Modello per embeddings
vectorStore: VectorStore; // Database vettoriale per RAG
topK: number; // Numero di documenti da recuperare
}
export class HybridAI {
private openai: OpenAI;
constructor(private config: HybridConfig) {
this.openai = new OpenAI();
}
async answer(query: string): Promise<{
content: string;
sources: string[];
}> {
// FASE 1: Retrieval (RAG)
// Recupera i documenti rilevanti dal vector store
const relevantDocs = await this.config.vectorStore.search(
query,
this.config.topK
);
// Costruisci il contesto dai documenti recuperati
const context = relevantDocs
.map((doc, i) =>
`[Fonte ${i + 1}: ${doc.metadata.title}]\n${doc.content}`
)
.join('\n\n---\n\n');
// FASE 2: Generazione (con modello fine-tuned)
// Il modello fine-tuned sa GIA come rispondere nel tono giusto
// e nel formato corretto - non servono istruzioni dettagliate
const response = await this.openai.chat.completions.create({
model: this.config.fineTunedModel,
messages: [
{
role: 'user',
content: `Contesto:\n${context}\n\nDomanda: ${query}`,
}
],
temperature: 0.3,
});
return {
content: response.choices[0].message.content ?? '',
sources: relevantDocs.map(d => d.metadata.title),
};
}
}
// Utilizzo
const hybrid = new HybridAI({
fineTunedModel: 'ft:gpt-4o-mini:org:acme-support:abc123',
embeddingModel: 'text-embedding-3-small',
vectorStore: myPineconeStore,
topK: 5,
});
const result = await hybrid.answer(
'Come configuro il webhook per gli eventi di pagamento?'
);
console.log('Risposta:', result.content);
console.log('Fonti:', result.sources);
9. 実際のケーススタディ
違いを明確にするために、3 つの実際のシナリオとその方法を分析してみましょう。 アプローチの選択は最終結果に影響します。
ケース 1: API ドキュメント アシスタント
シナリオ
ある SaaS 企業は、開発者による API の使用を支援するチャットボットを望んでいます。 ドキュメントはリリースごとに (2 週間ごとに) 更新されます。
選択されたアプローチ: RAG
- ドキュメントは 2 週間ごとに変更されます - 微調整にはコストがかかりすぎます
- 開発者は特定のドキュメントへのリンクを必要としています
- コーパスは大きく (200 ページ以上)、継続的に増加しています
結果: 関連ページへのリンク付きの正確な回答、 ドキュメントを展開するたびに自動更新されます。
ケース 2: 電子メール分類子のサポート
シナリオ
サポート チームは 1 日に 500 件以上の機密扱いが必要なメールを受け取ります 12のカテゴリーに分類され、適切な部門に割り当てられます。
選択したアプローチ: 微調整
- カテゴリが固定され、明確に定義されている
- タスクは反復的で量が多い
- 出力形式は構造化されています(カテゴリ + 優先度 + 部門)
- 外部からの引用や情報源は必要ありません
結果: 分類精度 94%、あたりのコスト 迅速なエンジニアリングにより、GPT-4o と比較して電子メールが 80% 削減されました。
事例 3: デジタル税務アドバイザー
シナリオ
会計事務所は税務に関する質問に答えてくれる AI アシスタントを望んでいます 正しい規制を引用し、適切な技術用語を使用します。
選択されたアプローチ: ハイブリッド
- 微調整 技術的な税務用語と回答フォーマットについて
- ラグ 最新の規制と歳入庁の回覧を取得するため
- 規制は頻繁に変更されます (財務、法令)
- 回答では特定の法律条項を引用する必要があります
結果: 引用を含む、予想される専門的な口調での回答 正確かつ最新の規制。
10. リファレンス RAG パイプライン
比較を完了するために、パイプラインのリファレンス実装を次に示します。 プロジェクトの基礎として使用できる RAG。この実装は LangChain を使用してプロセスを調整します。
// src/lib/rag-pipeline.ts
import { ChatOpenAI } from '@langchain/openai';
import { OpenAIEmbeddings } from '@langchain/openai';
import { MemoryVectorStore } from 'langchain/vectorstores/memory';
import { RecursiveCharacterTextSplitter } from 'langchain/text_splitter';
import { Document } from '@langchain/core/documents';
export class RAGPipeline {
private vectorStore: MemoryVectorStore | null = null;
private llm: ChatOpenAI;
private embeddings: OpenAIEmbeddings;
constructor() {
this.llm = new ChatOpenAI({
modelName: 'gpt-4o',
temperature: 0.3,
});
this.embeddings = new OpenAIEmbeddings({
modelName: 'text-embedding-3-small',
});
}
// Indicizza i documenti
async indexDocuments(documents: string[]): Promise<void> {
const splitter = new RecursiveCharacterTextSplitter({
chunkSize: 1000,
chunkOverlap: 200,
});
const docs = documents.map(
(text, i) => new Document({
pageContent: text,
metadata: { source: `doc-${i}` },
})
);
const splits = await splitter.splitDocuments(docs);
this.vectorStore = await MemoryVectorStore.fromDocuments(
splits,
this.embeddings
);
}
// Rispondi a una domanda
async query(question: string): Promise<string> {
if (!this.vectorStore) {
throw new Error('Indicizza i documenti prima di fare query');
}
// Recupera i documenti rilevanti
const relevantDocs = await this.vectorStore
.similaritySearch(question, 4);
// Costruisci il prompt con contesto
const context = relevantDocs
.map(d => d.pageContent)
.join('\n\n');
const prompt = `Basandoti sul seguente contesto, rispondi alla domanda.
Se il contesto non contiene informazioni sufficienti, dillo chiaramente.
Contesto:
${context}
Domanda: ${question}
Risposta:`;
const response = await this.llm.invoke(prompt);
return response.content as string;
}
}
要約: どのアプローチを選択するか
| 基準 | 微調整 | ラグ | ハイブリッド |
|---|---|---|---|
| セットアップの複雑さ | 高い | 平均 | 非常に高い |
| 最初の結果が得られるまでの時間 | 日数 | 営業時間 | 週間 |
| 柔軟性 | 低い | 高い | 最大 |
| メンテナンス | 定期的な再トレーニング | ソースの更新 | 両方 |
| スケーラビリティ | 限定 | 高い | 高い |
| 出力品質 | スタイル抜群 | 精度に優れています | 素晴らしい |
黄金律
常に RAG から始めてください。 実装がより速く、より簡単に 繰り返してより柔軟に。証拠がある場合にのみ微調整に移行する 具体的には、プロンプトエンジニアリングとRAGが要件を満たしていないこと スタイル、フォーマット、コストなど。ハイブリッドアプローチを検討する必要がある 両方のアプローチを個別に検証した後にのみ。
結論
微調整と RAG のどちらを選択するかは、どちらが「優れている」かという問題ではありません。 しかし、特定の使用例にはどれが最適ですか。微調整 モデル化する として モデルが応答すると、RAG は cosa モデルは知っています。両方が必要な場合は、ハイブリッド アプローチが最適な方法を提供します 両方の世界の。
次の記事では、 AIエージェント: アーキテクチャ 言語モデルが推論、計画、行動できるようにする 複雑な複数ステップのタスクを自律的に完了します。







