9月11日、Shittu Olumideが「Fine-Tuning Agentic AI: A Practical Guide」と題した記事を公開した。エージェントAIのファインチューニングは「モデルを学習させる」だけでは終わらない。訓練データの形式、パラメータ効率の良い学習手法、推論時のハイパーパラメータ、そして嗜好アライメント——この4軸のどれか1つでも欠ければ、本番環境で確実に壊れる。本記事はこれら4軸を同一の例(サポートチケットトリアージエージェント)を通じて順番にチューニングする構成で、各軸の相互依存関係を体系的に整理した実践ガイドだ。
ファインチューニングを「1つの問題」と捉えるのが失敗の原因
多くのガイドはこの4軸のうち1つしか扱わない。ベースモデルを丁寧にファインチューニングしても、推論時のtemperatureが不適切であれば本番環境で壊れる。temperatureが適切でも、ツール呼び出しの訓練データのフォーマットが崩れていれば、モデルは存在しない関数名をハルシネーションする。
対象エージェントは lookup_order、issue_refund、escalate_to_human という3つの内部ツールを確実に呼び出すことを目標とする。
前提環境はPython 3.10以上、pip install peft transformers datasets accelerate。なお、4-bit量子化を使う学習ステップのみCUDA GPU(実機)が必要で、データセット構築・ハイパーパラメータ・評価の各ステップは一般的なマシンで動作する。
第1軸:ツール呼び出しデータセットの構築と検証
量より形式が原則だ。ベースモデルはすでに流暢な英語で返金ポリシーを説明できる。問題は、毎回正確なツール呼び出し(引数名含む)を構文的に正しく出力できないことであり、これはフォーマットの問題だ。数千件の粗いサンプルより、数百件の厳密にフォーマットされたサンプルの方が効果的である。
# dataset.py
import json
TOOLS_SCHEMA = [
{
"name": "lookup_order",
"description": "Retrieves order details by order ID.",
"parameters": {"type": "object", "properties": {"order_id": {"type": "string"}}, "required": ["order_id"]},
},
{
"name": "issue_refund",
"description": "Issues a refund for an order. Only call this after confirming eligibility.",
"parameters": {
"type": "object",
"properties": {"order_id": {"type": "string"}, "amount": {"type": "number"}},
"required": ["order_id", "amount"],
},
},
{
"name": "escalate_to_human",
"description": "Hands the ticket to a human agent. Use for anything ambiguous, high-value, or policy-adjacent.",
"parameters": {"type": "object", "properties": {"reason": {"type": "string"}}, "required": ["reason"]},
},
]
def make_example(user_message: str, tool_name: str, tool_args: dict) -> dict:
return {
"messages": [
{"role": "system", "content": "You are a support triage agent with access to tools."},
{"role": "user", "content": user_message},
{
"role": "assistant", "content": None,
"tool_calls": [{"type": "function",
"function": {"name": tool_name, "arguments": json.dumps(tool_args)}}],
},
]
}
def validate_examples(examples: list[dict]) -> list[str]:
"""Schema validation, before training starts, not after a wasted run."""
valid_tool_names = {t["name"] for t in TOOLS_SCHEMA}
tools_by_name = {t["name"]: t for t in TOOLS_SCHEMA}
errors = []
for i, example in enumerate(examples):
for message in example["messages"]:
if message["role"] != "assistant" or "tool_calls" not in message:
continue
for call in message["tool_calls"]:
name = call["function"]["name"]
if name not in valid_tool_names:
errors.append(f"Example {i}: unknown tool '{name}'")
continue
required = set(tools_by_name[name]["parameters"].get("required", []))
provided = set(json.loads(call["function"]["arguments"]).keys())
missing = required - provided
if missing:
errors.append(f"Example {i}: tool '{name}' missing required args {missing}")
return errors
ポイントは validate_examples 関数だ。学習開始前に各ツール呼び出しを実際のスキーマと照合し、未知のツール名や必須引数の欠落を検出する。存在しないツールを呼び出すサンプルや引数が欠けたサンプルを意図的に混入させてテストすると、両方を正しく検出できる。この5分の検証を省いて、ハルシネーションを学習させてしまうのは「後で気づく最も高コストなミス」とされている。
大量データが必要になった場合の標準的アプローチは、手作業で150〜200件のシードサンプルを書き、強力なteacherモデルで拡張し、生成された各行を品質スコアリングして下位10〜20%を除外してからトレーナーに渡すという流れだ。
第2軸:QLoRAによるパラメータ効率の良いファインチューニング
検証済みデータセットを用意したら、次はQLoRA(Quantized Low-Rank Adaptation)の適用だ。ベースモデルを4-bit精度で凍結し、小規模な低ランクアダプタ行列のみを学習させる手法で、フルファインチューニングでは動かせないような70Bクラスのモデルを1枚のGPUで扱えるようになる。
from transformers import AutoModelForCausalLM
from peft import LoraConfig, get_peft_model, TaskType
model = AutoModelForCausalLM.from_pretrained(
"your-base-model", load_in_4bit=True, device_map="auto",
)
lora_config = LoraConfig(
r=4, # アダプタ行列のランク(低いほど学習パラメータが少ない)
lora_alpha=32, # アダプタ出力に適用されるスケーリング係数
lora_dropout=0.05, # 小規模データセット向けの正則化
target_modules=["q_proj", "k_proj", "v_proj", "o_proj"],
task_type=TaskType.CAUSAL_LM,
)
peft_model = get_peft_model(model, lora_config)
peft_model.print_trainable_parameters()
r(ランク)がまず理解すべき唯一のハイパーパラメータで、表現力・過学習リスク・アダプタサイズのトレードオフを直接制御する。r=4, alpha=32, dropout=0.05 の組み合わせは、ツール呼び出し動作を小型instructモデルでテストした査読済みのセットアップから取られたものだ。
著者が小規模モデルアーキテクチャでこの設定を検証したところ、全パラメータの1.7%のみが学習対象となり、残りのベースモデルは正しく凍結されたことが確認されている。
第3軸:推論時ハイパーパラメータのチューニング(最も見落とされがちな軸)
多くのガイドがこのステップを完全にスキップするが、それは誤りだ。学習後に決定するtemperature・イテレーション上限・リトライポリシーは、学習と同等の影響力で実際のタスク成功率を変える。
記事ではシミュレーションによるスイープを実装し、以下の4構成を比較している。なお、下表の成功率は元記事中のシミュレーション結果に基づく値であり、実際のモデルや環境によって異なる点に注意されたい:
| temperature | リトライ | 成功率(シミュレーション値) |
|---|---|---|
| 0.0 | なし | 高い |
| 0.7 | なし | 中程度 |
| 0.7 | あり | より高い |
| 1.0 | あり | 不安定 |
ツール呼び出しエージェントにとって「temperatureを上げる=エラー率が上がる」という関係は線形に近く、temperature 0〜0.3での決定論的な動作が推奨される。ただし、タスクの性質(創造性が必要か否か)によって適切値は変わる。
第4軸:DPOによる嗜好アライメント
SFT(教師あり学習)だけでは表現できない判断を教えるのがDPO(Direct Preference Optimization)だ。SFTは「正解ラベルに近づける」学習であるため、複数の選択肢が文脈依存で優劣を決める状況には対応しにくい。たとえば「払い戻し額が曖昧な注文に対して、issue_refund を直接呼ぶのではなく escalate_to_human を選ぶべき」という判断は、どちらのツール呼び出しも構文的には正しいため、SFTでは区別できない。
DPOはこうした状況に対して「良い応答(chosen)」と「悪い応答(rejected)」のペアを使ってモデルの嗜好を直接最適化する。本記事では以下のような嗜好ペアの構造を用いる:
# dpo_data.py の嗜好ペア構造例
preference_pair = {
"prompt": [
{"role": "system", "content": "You are a support triage agent with access to tools."},
{"role": "user", "content": "I want a refund but I'm not sure of the exact amount."},
],
"chosen": [
{
"role": "assistant", "content": None,
"tool_calls": [{"type": "function",
"function": {"name": "escalate_to_human",
"arguments": "{\"reason\": \"Refund amount unclear, needs human review\"}"}}],
}
],
"rejected": [
{
"role": "assistant", "content": None,
"tool_calls": [{"type": "function",
"function": {"name": "issue_refund",
"arguments": "{\"order_id\": \"unknown\", \"amount\": 0}"}}],
}
],
}
このペアをDPOトレーナーに渡すことで、モデルは「曖昧な状況では escalate_to_human を優先する」という判断基準を、報酬モデルを使わずに直接学習する。DPO論文で示されているように、この手法はRLHFと同等のアライメント効果を持ちながら、学習の安定性が高い。
評価フェーズではverdict-driven framework(判定駆動フレームワーク)を用いる。これは各テストケースに対して「正しいツールを選んだか」「引数は正確か」「不要なツールを呼ばなかったか」という複数の判定基準を明示的にスコアリングする評価構造だ。このフレームワークの主目的のひとつが、**catastrophic forgetting(壊滅的忘却)の検出**である。catastrophic forgettingとはファインチューニング後にベースモデルの汎用能力が著しく低下する現象で、DPOやSFTによる過度な特化で起こりやすい。verdict-driven frameworkはツール呼び出し精度だけでなく汎用的な応答品質も評価対象とすることで、この劣化を本番投入前に検出することを重視している。なお、DPO自体がcatastrophic forgettingを抑制する機能を持つわけではなく、あくまで評価フェーズでその兆候を捉えるための仕組みである点に注意されたい。
まとめ
4軸の整理を再掲する:
- 訓練データ:フォーマットの正確さと事前バリデーションが命
- QLoRA:
r=4, alpha=32, dropout=0.05から始め、全パラメータの数%のみを学習 - 推論時ハイパーパラメータ:temperatureとリトライポリシーを学習と同じ厳密さで調整する
- DPO:SFTでは表現できない「どちらを選ぶか」という判断を嗜好ペアで学習させる
詳細はFine-Tuning Agentic AI: A Practical Guideを参照していただきたい。