生成AI と API 統合

OpenAI、Azure、Bedrock、Google、RAG、キャッシュ

Python チュートリアルシリーズ

生成AIの概要

生成AIとは?

  • 人間らしいコンテンツを作成するAIモデル
    • テキスト、コード、画像、音声
    • 大規模データセットで訓練
    • プロンプトベースのインターフェース
  • 2024年の主要プロバイダー:
    • OpenAI (GPT-4, ChatGPT)
    • Azure OpenAI (エンタープライズ機能)
    • AWS Bedrock (複数モデル)
    • Google AI (Gemini, Vertex AI)

なぜPythonでAI統合?

# シンプルで読みやすいAPI操作
from openai import OpenAI

client = OpenAI(api_key="your-key")
response = client.chat.completions.create(
    model="gpt-4",
    messages=[{"role": "user", "content": "こんにちはAI!"}]
)
print(response.choices[0].message.content)

Pythonの利点: - 豊富なAIライブラリエコシステム - 簡単なAPI統合 - 優秀なデータ処理能力 - 強力なコミュニティサポート

OpenAI API 統合

基本セットアップと認証

import openai
from openai import OpenAI
import os

# セキュアなAPIキー管理
client = OpenAI(
    api_key=os.getenv("OPENAI_API_KEY")
)

def generate_text(prompt: str, model: str = "gpt-3.5-turbo") -> str:
    response = client.chat.completions.create(
        model=model,
        messages=[{"role": "user", "content": prompt}],
        max_tokens=1000,
        temperature=0.7  # 創造性を制御 (0.0-1.0)
    )
    return response.choices[0].message.content

主要パラメータ: - model: GPT-3.5-turbo(高速) または GPT-4(高品質) - temperature: 0.0 = 一貫性、1.0 = 創造性 - max_tokens: 応答長制限

リアルタイム応答のストリーミング

def stream_response(prompt: str):
    """AI応答を単語ごとにストリーミング"""
    stream = client.chat.completions.create(
        model="gpt-3.5-turbo",
        messages=[{"role": "user", "content": prompt}],
        stream=True
    )
    
    for chunk in stream:
        if chunk.choices[0].delta.content:
            print(chunk.choices[0].delta.content, end="")

ストリーミングを使用するタイミング: - 長い応答(小説、説明) - インタラクティブチャットアプリケーション - より良いユーザーエクスペリエンス

高度な機能

# 行動制御のためのシステムメッセージ
messages = [
    {"role": "system", "content": "あなたは親切なPythonチューターです"},
    {"role": "user", "content": "ループについて説明してください"}
]

# 関数呼び出し(ツール)
functions = [{
    "name": "get_weather",
    "description": "場所の天気を取得",
    "parameters": {
        "type": "object",
        "properties": {
            "location": {"type": "string"}
        }
    }
}]

response = client.chat.completions.create(
    model="gpt-3.5-turbo",
    messages=messages,
    functions=functions
)

Azure OpenAI サービス

エンタープライズ級AI

なぜAzure OpenAI? - データ常駐とコンプライアンス - プライベートエンドポイント - エンタープライズセキュリティ - 同じモデル、より良いガバナンス

from openai import AzureOpenAI

azure_client = AzureOpenAI(
    api_key=os.getenv("AZURE_OPENAI_API_KEY"),
    api_version="2024-02-01",
    azure_endpoint=os.getenv("AZURE_OPENAI_ENDPOINT")
)

OpenAIとの主な違い

# OpenAI: モデル名を使用
response = openai_client.chat.completions.create(
    model="gpt-3.5-turbo",  # モデル名
    messages=[...]
)

# Azure: デプロイメント名を使用
response = azure_client.chat.completions.create(
    model="my-gpt-35-deployment",  # デプロイメント名
    messages=[...]
)

Azureセットアップ手順: 1. Azure OpenAIリソースを作成 2. モデルをエンドポイントにデプロイ 3. モデル名ではなくデプロイメント名を使用

AWS Bedrock 統合

マルチモデルプラットフォーム

複数のAIプロバイダーにアクセス: - Anthropic Claude(推論) - AI21 Jurassic(創造性) - Cohere Command(エンタープライズ) - Meta Llama(オープンソース)

import boto3
import json

bedrock = boto3.client(
    'bedrock-runtime',
    region_name='us-east-1'
)

モデル固有フォーマット

def bedrock_generate(prompt: str, model_id: str):
    # Claudeフォーマット
    if "anthropic.claude" in model_id:
        body = json.dumps({
            "prompt": f"\n\nHuman: {prompt}\n\nAssistant:",
            "max_tokens_to_sample": 1000
        })
    
    # AI21フォーマット
    elif "ai21.j2" in model_id:
        body = json.dumps({
            "prompt": prompt,
            "maxTokens": 1000
        })
    
    response = bedrock.invoke_model(
        modelId=model_id,
        body=body
    )
    return json.loads(response['body'].read())

各モデルには異なる: - 入力/出力フォーマット - 強みとユースケース - 価格モデル

Google AI プラットフォーム

2つのオプション: AI Studio vs Vertex AI

Google AI Studio(Gemini API): - 迅速な実験 - シンプルなAPIアクセス - 無料利用枠あり

Vertex AI: - プロダクションワークロード - エンタープライズ機能 - 高度なML運用

Gemini API 統合

import google.generativeai as genai

genai.configure(api_key=os.getenv("GOOGLE_AI_API_KEY"))

def google_generate(prompt: str):
    model = genai.GenerativeModel("gemini-pro")
    response = model.generate_content(prompt)
    return response.text

# マルチモーダル機能
model = genai.GenerativeModel("gemini-pro-vision")
response = model.generate_content([
    "この画像には何がありますか?",
    image_data
])

Geminiの利点: - 大きなコンテキストウィンドウ(100万+トークン) - マルチモーダル(テキスト + 画像) - 高速推論速度

RAG: Retrieval Augmented Generation

基本AIの問題

# 基本AI - 限定的な知識
response = client.chat.completions.create(
    messages=[{"role": "user", "content": "当社のポリシーは何ですか?"}]
)
# AIは特定の会社のポリシーを知らない!

制限事項: - 訓練データの締切日 - プライベート/最新データへのアクセスなし - 一般的な応答のみ

RAGソリューションアーキテクチャ

graph TD
    A[ユーザークエリ] --> B[文書検索]
    B --> C[関連文書発見]
    C --> D[強化プロンプト]
    D --> E[AI生成]
    E --> F[コンテキスト応答]

RAG = 検索 + 拡張 + 生成

  1. 検索 関連文書
  2. 拡張 プロンプトにコンテキスト追加
  3. 生成 情報に基づいた応答

シンプルRAG実装

from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity

class SimpleRAG:
    def __init__(self):
        self.documents = []
        self.vectorizer = TfidfVectorizer()
        
    def add_documents(self, docs):
        self.documents = docs
        self.vectors = self.vectorizer.fit_transform(docs)
    
    def retrieve(self, query, top_k=3):
        query_vec = self.vectorizer.transform([query])
        similarities = cosine_similarity(query_vec, self.vectors)
        top_indices = similarities.argsort()[0][-top_k:][::-1]
        return [self.documents[i] for i in top_indices]
    
    def generate_response(self, query):
        context = "\n".join(self.retrieve(query))
        prompt = f"コンテキスト: {context}\n\n質問: {query}\n回答:"
        return generate_text(prompt)

ベクトルデータベースを使った高度なRAG

import chromadb
from sentence_transformers import SentenceTransformer

class AdvancedRAG:
    def __init__(self):
        self.client = chromadb.Client()
        self.collection = self.client.create_collection("docs")
        self.encoder = SentenceTransformer('all-MiniLM-L6-v2')
    
    def add_documents(self, documents):
        embeddings = self.encoder.encode(documents)
        self.collection.add(
            embeddings=embeddings.tolist(),
            documents=documents,
            ids=[f"doc_{i}" for i in range(len(documents))]
        )
    
    def semantic_search(self, query, n_results=3):
        query_embedding = self.encoder.encode([query])
        results = self.collection.query(
            query_embeddings=query_embedding.tolist(),
            n_results=n_results
        )
        return results['documents'][0]

ベクトルデータベースの利点: - より良い意味理解 - より高速な類似検索 - メタデータフィルタリング機能

キャッシュ戦略

なぜAI応答をキャッシュ?

キャッシュなしの問題: - 同じ質問への繰り返しAPI呼び出し - 高レイテンシ(ネットワーク往復) - 高額なAPIコスト - 悪いユーザーエクスペリエンス

キャッシュの利点: - 繰り返し応答が90%以上高速化 - 大幅なコスト削減 - より良い信頼性

インメモリキャッシュ

import time
from typing import Dict, Optional

class ResponseCache:
    def __init__(self, ttl_seconds=3600):
        self.cache: Dict[str, dict] = {}
        self.ttl = ttl_seconds
    
    def get(self, key: str) -> Optional[str]:
        if key in self.cache:
            entry = self.cache[key]
            if time.time() - entry['timestamp'] < self.ttl:
                return entry['response']
            del self.cache[key]  # 期限切れ削除
        return None
    
    def set(self, key: str, response: str):
        self.cache[key] = {
            'response': response,
            'timestamp': time.time()
        }

cache = ResponseCache(ttl_seconds=1800)  # 30分

SQLiteを使った永続キャッシュ

import sqlite3
import hashlib
from datetime import datetime

class PersistentCache:
    def __init__(self, db_path="ai_cache.db"):
        self.db_path = db_path
        self.init_db()
    
    def init_db(self):
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()
        cursor.execute("""
            CREATE TABLE IF NOT EXISTS responses (
                prompt_hash TEXT PRIMARY KEY,
                response TEXT,
                created_at TIMESTAMP,
                model TEXT
            )
        """)
        conn.commit()
        conn.close()
    
    def get(self, prompt: str, model: str):
        prompt_hash = hashlib.sha256(f"{model}:{prompt}".encode()).hexdigest()
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()
        cursor.execute(
            "SELECT response FROM responses WHERE prompt_hash = ?",
            (prompt_hash,)
        )
        result = cursor.fetchone()
        conn.close()
        return result[0] if result else None

プロダクションベストプラクティス

エラーハンドリングとリトライ

import time
import random

def robust_ai_call(prompt, max_retries=3):
    for attempt in range(max_retries):
        try:
            return generate_text(prompt)
        except Exception as e:
            if attempt == max_retries - 1:
                raise e
            
            # ジッター付き指数バックオフ
            delay = (2 ** attempt) + random.uniform(0, 1)
            time.sleep(delay)

エラーハンドリング戦略: - レート制限の指数バックオフ - サービス障害のサーキットブレーカー - フォールバック付き優雅な劣化

レート制限

from collections import deque
import time

class RateLimiter:
    def __init__(self, max_calls=60, window_seconds=60):
        self.max_calls = max_calls
        self.window = window_seconds
        self.calls = deque()
    
    def acquire(self):
        now = time.time()
        # 古い呼び出しを削除
        while self.calls and self.calls[0] <= now - self.window:
            self.calls.popleft()
        
        if len(self.calls) < self.max_calls:
            self.calls.append(now)
            return True
        return False
    
    def wait_if_needed(self):
        while not self.acquire():
            time.sleep(0.1)

マルチプロバイダーフォールバック

class MultiProviderAI:
    def __init__(self):
        self.providers = [
            ("openai", self.openai_generate),
            ("azure", self.azure_generate),
            ("bedrock", self.bedrock_generate),
        ]
    
    def generate_with_fallback(self, prompt):
        for name, provider in self.providers:
            try:
                result = provider(prompt)
                if result:
                    return result
            except Exception as e:
                print(f"{name}が失敗しました: {e}")
                continue
        
        raise Exception("すべてのプロバイダーが失敗しました")

実世界のアプリケーション

文書Q&Aシステム

class DocumentQASystem:
    def __init__(self):
        self.rag = AdvancedRAG()
        self.cache = PersistentCache()
        self.rate_limiter = RateLimiter()
    
    def load_documents(self, file_path):
        with open(file_path, 'r') as f:
            content = f.read()
        
        # チャンクに分割
        chunks = content.split('\n\n')
        self.rag.add_documents(chunks)
    
    def ask_question(self, question):
        # 最初にキャッシュをチェック
        cached = self.cache.get(question, "gpt-3.5-turbo")
        if cached:
            return cached
        
        # レート制限
        self.rate_limiter.wait_if_needed()
        
        # RAGで生成
        response = self.rag.generate_response(question)
        
        # 結果をキャッシュ
        self.cache.set(question, response, "gpt-3.5-turbo")
        
        return response

コードアシスタント

class CodeAssistant:
    def __init__(self):
        self.system_prompt = """
        あなたは専門のPythonプログラマーです。
        明確で動作するコード例を提供してください。
        複雑な概念をシンプルに説明してください。
        """
    
    def get_code_help(self, question):
        messages = [
            {"role": "system", "content": self.system_prompt},
            {"role": "user", "content": question}
        ]
        
        response = client.chat.completions.create(
            model="gpt-4",
            messages=messages,
            temperature=0.3  # より一貫したコードのため低く設定
        )
        
        return response.choices[0].message.content

まとめと次のステップ

主要なポイント

複数のAIプロバイダー: - OpenAI: 最高の汎用性能 - Azure: エンタープライズ機能 - Bedrock: マルチモデルアクセス - Google: マルチモーダル機能

必須技術: - ドメイン知識のためのRAG - パフォーマンスのためのキャッシュ - 信頼性のためのエラーハンドリング - 安定性のためのレート制限

プロダクションチェックリスト

セキュリティ: 環境変数のAPIキー ✅ キャッシュ: 永続キャッシュの実装 ✅ 監視: 使用状況とエラーのログ ✅ レート制限: API制限の尊重 ✅ フォールバック: 複数プロバイダーサポート ✅ テスト: 包括的なエラーシナリオ

次のステップ

  1. 実験 様々なモデルとプロバイダー
  2. RAG実装 特定ドメイン向け
  3. 監視設定 アラート付き
  4. コスト最適化 知的キャッシュで
  5. ファインチューニング検討 専門タスク用

覚えておいて: シンプルに始めて、必要に応じて複雑さを追加!

質問と議論

よくある質問

Q: どのAIプロバイダーを選ぶべき? A: ニーズによります: - OpenAI: 汎用利用に最適 - Azure: エンタープライズ要件 - Bedrock: 複数モデルが欲しい - Google: マルチモーダルアプリケーション

Q: キャッシュはどのくらい役立つ? A: 通常、繰り返しクエリで80-95%のコスト削減

Q: RAGは常に必要? A: 以下が必要な場合にRAGを使用: - 最新情報 - プライベート/独自データ - ドメイン固有の知識

ありがとうございました!

リソース: - OpenAI API ドキュメント - Azure OpenAI サービスガイド - AWS Bedrock 開発者ガイド - Google AI Studio ドキュメント

楽しいAIコーディングを! 🤖✨