第7章:LLM開発
更新日:2025年12月9日
1. LLM API活用
1.1 OpenAI/Anthropic
主要なLLMプロバイダのAPI利用方法を解説する。Table 1に主要プロバイダの比較を示す。
Table 1. LLMプロバイダ比較(2025年時点)
| プロバイダ | 主要モデル | 特徴 |
|---|---|---|
| OpenAI | GPT-4o, o1, o3 | 汎用性高、エコシステム充実 |
| Anthropic | Claude 3.5/4 | 長文対応、安全性重視 |
| Gemini 2.0 | マルチモーダル、長コンテキスト | |
| Mistral | Mistral Large | 欧州拠点、オープンモデルも提供 |
1.1.1 OpenAI API:
from openai import OpenAI
import os
client = OpenAI(api_key=os.environ['OPENAI_API_KEY'])
# 基本的なChat Completion
response = client.chat.completions.create(
model='gpt-4o',
messages=[
{'role': 'system', 'content': 'あなたは親切なアシスタントです。'},
{'role': 'user', 'content': 'Pythonの特徴を3つ教えてください。'}
],
temperature=0.7,
max_tokens=1000,
)
print(response.choices[0].message.content)
# Structured Output(JSON Mode)
response = client.chat.completions.create(
model='gpt-4o',
messages=[
{'role': 'user', 'content': '東京の観光スポットを3つJSON形式で返してください。'}
],
response_format={'type': 'json_object'},
)
import json
data = json.loads(response.choices[0].message.content)
1.1.2 Anthropic API:
from anthropic import Anthropic
client = Anthropic(api_key=os.environ['ANTHROPIC_API_KEY'])
response = client.messages.create(
model='claude-sonnet-4-20250514',
max_tokens=1024,
system='あなたは経験豊富なPythonエンジニアです。',
messages=[
{'role': 'user', 'content': 'async/awaitのベストプラクティスを教えてください。'}
],
)
print(response.content[0].text)
1.2 ストリーミング
長い応答をリアルタイムで表示するためのストリーミング実装。
# OpenAI ストリーミング
from openai import OpenAI
client = OpenAI()
stream = client.chat.completions.create(
model='gpt-4o',
messages=[{'role': 'user', 'content': '長い物語を書いてください。'}],
stream=True,
)
for chunk in stream:
if chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end='', flush=True)
# Anthropic ストリーミング
from anthropic import Anthropic
client = Anthropic()
with client.messages.stream(
model='claude-sonnet-4-20250514',
max_tokens=1024,
messages=[{'role': 'user', 'content': '長い物語を書いてください。'}],
) as stream:
for text in stream.text_stream:
print(text, end='', flush=True)
# FastAPIでのストリーミングエンドポイント
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
app = FastAPI()
async def generate_stream(prompt: str):
stream = client.chat.completions.create(
model='gpt-4o',
messages=[{'role': 'user', 'content': prompt}],
stream=True,
)
for chunk in stream:
if content := chunk.choices[0].delta.content:
yield f"data: {content}\n\n"
@app.get("/stream")
async def stream_response(prompt: str):
return StreamingResponse(
generate_stream(prompt),
media_type="text/event-stream"
)
2. RAG実装
RAG(Retrieval-Augmented Generation)は、外部知識をLLMに注入する手法である[1]。ドメイン固有の質問応答システム構築に有効。
Fig. 1にRAGのアーキテクチャを示す。
2.1 ベクトルストアの構築:
from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.document_loaders import DirectoryLoader, TextLoader
# ドキュメントの読み込み
loader = DirectoryLoader(
'./docs/',
glob='**/*.md',
loader_cls=TextLoader,
)
documents = loader.load()
# チャンク分割
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200,
separators=['\n\n', '\n', '。', '、', ' '],
)
chunks = text_splitter.split_documents(documents)
# Embeddingとベクトルストア作成
embeddings = OpenAIEmbeddings(model='text-embedding-3-small')
vectorstore = Chroma.from_documents(
documents=chunks,
embedding=embeddings,
persist_directory='./chroma_db',
)
# 検索テスト
results = vectorstore.similarity_search('Pythonの型ヒントとは', k=3)
for doc in results:
print(doc.page_content[:200])
2.2 RAGチェーンの構築:
from langchain_openai import ChatOpenAI
from langchain.chains import RetrievalQA
from langchain.prompts import PromptTemplate
# プロンプトテンプレート
template = """以下のコンテキストを参考にして質問に答えてください。
コンテキストに情報がない場合は、「情報が見つかりません」と回答してください。
コンテキスト:
{context}
質問: {question}
回答:"""
prompt = PromptTemplate(
template=template,
input_variables=['context', 'question'],
)
# RAGチェーン
llm = ChatOpenAI(model='gpt-4o', temperature=0)
retriever = vectorstore.as_retriever(search_kwargs={'k': 5})
qa_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type='stuff',
retriever=retriever,
chain_type_kwargs={'prompt': prompt},
return_source_documents=True,
)
# 質問応答
result = qa_chain.invoke({'query': 'Pythonの型ヒントの利点は?'})
print(result['result'])
print('---ソース---')
for doc in result['source_documents']:
print(f"- {doc.metadata.get('source', 'unknown')}")
Table 2. ベクトルストアの比較
| ベクトルストア | 特徴 | 用途 |
|---|---|---|
| Chroma | 軽量、ローカル、簡単 | プロトタイプ、小規模 |
| Pinecone | マネージド、スケーラブル | 本番環境、大規模 |
| Weaviate | ハイブリッド検索、GraphQL | 複雑なクエリ |
| Qdrant | 高性能、フィルタリング | 高速検索必須 |
| pgvector | PostgreSQL拡張 | 既存DB活用 |
3. エージェント開発
LLMエージェントは、ツールを使用して自律的にタスクを遂行するシステムである[2]。
3.1 LangChain Agent:
from langchain_openai import ChatOpenAI
from langchain.agents import AgentExecutor, create_openai_tools_agent
from langchain.tools import tool
from langchain import hub
# カスタムツールの定義
@tool
def search_database(query: str) -> str:
"""データベースを検索して情報を取得します。"""
# 実際のDB検索ロジック
return f"検索結果: {query}に関する情報..."
@tool
def calculate(expression: str) -> str:
"""数式を計算します。"""
try:
result = eval(expression) # 本番では安全な評価器を使用
return str(result)
except Exception as e:
return f"計算エラー: {e}"
@tool
def get_current_weather(location: str) -> str:
"""指定した場所の現在の天気を取得します。"""
# 実際のAPI呼び出し
return f"{location}の天気: 晴れ、気温20度"
# エージェントの作成
llm = ChatOpenAI(model='gpt-4o', temperature=0)
tools = [search_database, calculate, get_current_weather]
# プロンプトテンプレート(hub から取得)
prompt = hub.pull('hwchase17/openai-tools-agent')
agent = create_openai_tools_agent(llm, tools, prompt)
agent_executor = AgentExecutor(
agent=agent,
tools=tools,
verbose=True,
max_iterations=5,
)
# 実行
result = agent_executor.invoke({
'input': '東京の天気を調べて、気温を華氏に変換してください。'
})
print(result['output'])
3.2 Function Calling(Tool Use):OpenAI/Anthropic のネイティブ機能。
from openai import OpenAI
import json
client = OpenAI()
# ツール定義
tools = [
{
'type': 'function',
'function': {
'name': 'get_weather',
'description': '指定した場所の天気を取得',
'parameters': {
'type': 'object',
'properties': {
'location': {
'type': 'string',
'description': '都市名(例:東京)',
},
'unit': {
'type': 'string',
'enum': ['celsius', 'fahrenheit'],
},
},
'required': ['location'],
},
},
},
]
# ツール呼び出しを含むリクエスト
response = client.chat.completions.create(
model='gpt-4o',
messages=[{'role': 'user', 'content': '東京の天気は?'}],
tools=tools,
tool_choice='auto',
)
# ツール呼び出しの処理
message = response.choices[0].message
if message.tool_calls:
for tool_call in message.tool_calls:
function_name = tool_call.function.name
arguments = json.loads(tool_call.function.arguments)
print(f"ツール呼び出し: {function_name}({arguments})")
# ツール実行(実際の実装)
if function_name == 'get_weather':
tool_result = '晴れ、20度'
# 結果を含めて再度リクエスト
follow_up = client.chat.completions.create(
model='gpt-4o',
messages=[
{'role': 'user', 'content': '東京の天気は?'},
message,
{
'role': 'tool',
'tool_call_id': tool_call.id,
'content': tool_result,
},
],
)
print(follow_up.choices[0].message.content)
Fig. 2にエージェントの動作フローを示す。
4. ファインチューニング
ドメイン特化や特定タスクへの適応にはファインチューニングが有効である[3]。
4.1 LoRA(Low-Rank Adaptation):パラメータ効率的なファインチューニング手法。
from transformers import AutoModelForCausalLM, AutoTokenizer, TrainingArguments
from peft import LoraConfig, get_peft_model, TaskType
from datasets import load_dataset
from trl import SFTTrainer
# ベースモデルの読み込み
model_name = 'meta-llama/Llama-3.1-8B'
model = AutoModelForCausalLM.from_pretrained(
model_name,
torch_dtype=torch.bfloat16,
device_map='auto',
)
tokenizer = AutoTokenizer.from_pretrained(model_name)
tokenizer.pad_token = tokenizer.eos_token
# LoRA設定
lora_config = LoraConfig(
task_type=TaskType.CAUSAL_LM,
r=16, # LoRAのランク
lora_alpha=32, # スケーリング係数
lora_dropout=0.1,
target_modules=['q_proj', 'v_proj', 'k_proj', 'o_proj'],
)
# PEFTモデル作成
model = get_peft_model(model, lora_config)
model.print_trainable_parameters()
# trainable params: 6,553,600 || all params: 8,030,261,248 || trainable%: 0.082
# データセット準備
dataset = load_dataset('json', data_files='training_data.jsonl')
# トレーニング設定
training_args = TrainingArguments(
output_dir='./lora_output',
num_train_epochs=3,
per_device_train_batch_size=4,
gradient_accumulation_steps=4,
learning_rate=2e-4,
bf16=True,
logging_steps=10,
save_strategy='epoch',
)
# SFTTrainerによる学習
trainer = SFTTrainer(
model=model,
args=training_args,
train_dataset=dataset['train'],
tokenizer=tokenizer,
dataset_text_field='text',
max_seq_length=2048,
)
trainer.train()
4.2 QLoRA:量子化と組み合わせたメモリ効率の良い手法。
from transformers import BitsAndBytesConfig
import torch
# 4bit量子化設定
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type='nf4',
bnb_4bit_compute_dtype=torch.bfloat16,
bnb_4bit_use_double_quant=True,
)
# 量子化モデルの読み込み
model = AutoModelForCausalLM.from_pretrained(
model_name,
quantization_config=bnb_config,
device_map='auto',
)
# LoRA適用(以降は同様)
model = get_peft_model(model, lora_config)
Table 3. ファインチューニング手法の比較
| 手法 | VRAM要件(8B) | 学習速度 | 性能 |
|---|---|---|---|
| Full Fine-tuning | 〜80GB | 遅い | 最高 |
| LoRA | 〜24GB | 速い | 高い |
| QLoRA(4bit) | 〜12GB | 中程度 | やや高い |
5. ローカルLLM運用
プライバシー要件やコスト削減のため、ローカルでLLMを運用するケースが増えている。
5.1 vLLM:高速推論エンジン[4]。PagedAttentionによりスループットを最大化。
# vLLMのインストール
# pip install vllm
from vllm import LLM, SamplingParams
# モデルの読み込み
llm = LLM(
model='meta-llama/Llama-3.1-8B-Instruct',
tensor_parallel_size=1, # GPU数
gpu_memory_utilization=0.9,
)
# サンプリングパラメータ
sampling_params = SamplingParams(
temperature=0.7,
top_p=0.9,
max_tokens=512,
)
# バッチ推論
prompts = [
'Pythonの利点は何ですか?',
'機械学習とは何ですか?',
'RESTful APIの設計原則を説明してください。',
]
outputs = llm.generate(prompts, sampling_params)
for output in outputs:
print(f"Prompt: {output.prompt}")
print(f"Output: {output.outputs[0].text}\n")
# OpenAI互換サーバーとして起動
# python -m vllm.entrypoints.openai.api_server \
# --model meta-llama/Llama-3.1-8B-Instruct \
# --port 8000
5.2 Ollama:シンプルなローカルLLM実行環境[5]。
# Ollamaのインストールと実行
# curl -fsSL https://ollama.com/install.sh | sh
# ollama pull llama3.1:8b
# ollama serve
import requests
# REST API経由で呼び出し
response = requests.post(
'http://localhost:11434/api/generate',
json={
'model': 'llama3.1:8b',
'prompt': 'Pythonの特徴を教えてください。',
'stream': False,
}
)
print(response.json()['response'])
# OpenAI互換クライアントで利用
from openai import OpenAI
client = OpenAI(
base_url='http://localhost:11434/v1',
api_key='ollama', # ダミー値
)
response = client.chat.completions.create(
model='llama3.1:8b',
messages=[{'role': 'user', 'content': 'Hello!'}],
)
print(response.choices[0].message.content)
Table 4. ローカル推論エンジンの比較
| エンジン | 特徴 | 用途 |
|---|---|---|
| vLLM | 高スループット、本番向け | 高負荷サービス |
| Ollama | シンプル、セットアップ容易 | 開発、個人利用 |
| llama.cpp | CPU対応、量子化 | エッジ、低リソース |
| TGI | HuggingFace製、本番向け | HFモデル運用 |
References
[1] P. Lewis et al., "Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks," NeurIPS 2020.
[2] LangChain, "LangChain Documentation," python.langchain.com, 2024.
[3] E. J. Hu et al., "LoRA: Low-Rank Adaptation of Large Language Models," ICLR 2022.
[4] W. Kwon et al., "Efficient Memory Management for Large Language Model Serving with PagedAttention," SOSP 2023.
[5] Ollama, "Ollama Documentation," ollama.com, 2024.
本コンテンツは2025年12月時点の情報に基づいて作成されている。LLM分野は急速に進化しており、APIやツールの仕様は頻繁に変更される。最新の情報は各プロバイダの公式ドキュメントを参照されたい。