テキスト分類と感情分析が求められる場面
ECサイトのカスタマーレビュー、SNS上のブランド言及、問い合わせメールの仕分け——。テキストデータを自動でカテゴリ分けし、感情の傾向を把握したい場面は増える一方です。従来は専用の NLP モデルを学習させる必要がありましたが、Gemini API の Structured Output を組み合わせると、学習データなしでも高精度なテキスト分類・感情分析パイプラインを Python で素早く構築できます。
環境準備と Gemini API キーの取得
まず、Python 環境と Gemini API キーを用意します。
API キーの取得
- Google AI Studio にアクセスします
- 左メニューから「Get API key」をクリックします
- 新しいプロジェクトを作成するか、既存のプロジェクトを選択して API キーを発行します
Python パッケージのインストール
pip install google-genai接続テスト
from google import genai
client = genai.Client(api_key="YOUR_GEMINI_API_KEY")
response = client.models.generate_content(
model="gemini-2.5-flash",
contents="こんにちは、Gemini!テスト応答を返してください。"
)
print(response.text)
# 期待出力例: こんにちは!テスト応答です。何かお手伝いできることはありますか?API キーが正しく設定されていれば、Gemini からの応答が表示されます。
Structured Output でテキスト分類を実装する
Gemini API の Structured Output(JSON スキーマ応答)を使うと、分類ラベルを厳密な形式で受け取れます。自由文ではなく構造化されたデータが返るため、後続処理に直接渡せる点が大きなメリットです。
分類スキーマの定義
from google import genai
from pydantic import BaseModel, Field
from enum import Enum
from typing import Optional
class Category(str, Enum):
PRODUCT_QUALITY = "product_quality"
SHIPPING = "shipping"
CUSTOMER_SERVICE = "customer_service"
PRICING = "pricing"
OTHER = "other"
class Sentiment(str, Enum):
POSITIVE = "positive"
NEUTRAL = "neutral"
NEGATIVE = "negative"
class TextClassification(BaseModel):
category: Category = Field(description="テキストの主要カテゴリ")
sentiment: Sentiment = Field(description="テキストの感情極性")
confidence: float = Field(description="分類の確信度(0.0〜1.0)", ge=0.0, le=1.0)
keywords: list[str] = Field(description="テキストから抽出した主要キーワード(最大5つ)")
summary: str = Field(description="テキストの1行要約")Pydantic モデルでスキーマを定義し、Category と Sentiment を Enum で固定することで、返却されるラベルが予期しない値になることを防ぎます。
単一テキストの分類
client = genai.Client(api_key="YOUR_GEMINI_API_KEY")
review = "注文から2日で届きました。梱包も丁寧で、商品の品質も期待通りです。ただ、説明書が英語のみだったのが少し残念でした。"
response = client.models.generate_content(
model="gemini-2.5-flash",
contents=f"""以下のカスタマーレビューを分析してください。
レビュー: {review}
カテゴリ、感情、確信度、キーワード、要約を返してください。""",
config={
"response_mime_type": "application/json",
"response_schema": TextClassification,
}
)
result = TextClassification.model_validate_json(response.text)
print(f"カテゴリ: {result.category.value}")
print(f"感情: {result.sentiment.value}")
print(f"確信度: {result.confidence}")
print(f"キーワード: {result.keywords}")
print(f"要約: {result.summary}")
# 期待出力例:
# カテゴリ: product_quality
# 感情: positive
# 確信度: 0.82
# キーワード: ['梱包', '品質', '配送', '説明書', '英語']
# 要約: 商品品質と配送は良好だが、説明書が英語のみで改善の余地ありresponse_schema に Pydantic モデルを渡すだけで、Gemini が指定した JSON 形式で応答を返します。パースエラーを気にする必要がないため、後続のデータ処理がシンプルになります。
バッチ処理で大量テキストを一括分類する
実務では、数百〜数千件のレビューを一度に処理したい場面があります。ループ処理にリトライロジックを加えた実装を見てみましょう。
import time
from google import genai
from google.genai.errors import ClientError
client = genai.Client(api_key="YOUR_GEMINI_API_KEY")
reviews = [
"商品自体は素晴らしいのですが、配送に1週間以上かかりました。",
"カスタマーサポートの対応がとても親切で感動しました。",
"この価格帯でこの品質は正直驚きです。コスパ最高。",
"届いた商品が写真と全く違いました。返品手続きも面倒です。",
"普通に使えています。特に不満はありません。",
]
def classify_review(review_text: str, max_retries: int = 3) -> Optional[TextClassification]:
"""レビューを分類する(リトライ付き)"""
for attempt in range(max_retries):
try:
response = client.models.generate_content(
model="gemini-2.5-flash",
contents=f"以下のカスタマーレビューを分析してください。\n\nレビュー: {review_text}",
config={
"response_mime_type": "application/json",
"response_schema": TextClassification,
}
)
return TextClassification.model_validate_json(response.text)
except ClientError as e:
if "429" in str(e):
wait = 2 ** attempt
print(f"レート制限に到達。{wait}秒後にリトライします...")
time.sleep(wait)
else:
print(f"エラー: {e}")
return None
return None
# バッチ実行
results = []
for i, review in enumerate(reviews):
print(f"処理中: {i + 1}/{len(reviews)}")
result = classify_review(review)
if result:
results.append({"review": review, "classification": result})
time.sleep(0.5) # レート制限を考慮した間隔
# 結果の集計
from collections import Counter
sentiment_counts = Counter(r["classification"].sentiment.value for r in results)
category_counts = Counter(r["classification"].category.value for r in results)
print(f"\n--- 感情分布 ---")
for sentiment, count in sentiment_counts.most_common():
print(f" {sentiment}: {count}件")
print(f"\n--- カテゴリ分布 ---")
for category, count in category_counts.most_common():
print(f" {category}: {count}件")
# 期待出力例:
# --- 感情分布 ---
# positive: 2件
# negative: 2件
# neutral: 1件
# --- カテゴリ分布 ---
# shipping: 1件
# customer_service: 1件
# pricing: 1件
# product_quality: 2件ポイントは time.sleep(0.5) でリクエスト間隔を空けることと、429 エラー時に指数バックオフでリトライすることです。大量データを処理する場合は、さらに Gemini API 本番環境パイプラインアーキテクチャ で解説しているバッチ API の活用も検討してみてください。
多言語テキストの感情分析
Gemini はマルチリンガル対応のため、日本語・英語・中国語など複数言語のテキストを同じパイプラインで処理できます。
class MultilingualAnalysis(BaseModel):
detected_language: str = Field(description="検出された言語コード(ja, en, zh など)")
sentiment: Sentiment
confidence: float = Field(ge=0.0, le=1.0)
translated_summary: str = Field(description="日本語での1行要約")
multilingual_texts = [
"This product exceeded my expectations! Highly recommended.",
"配送が遅すぎます。二度と利用しません。",
"产品质量一般,但价格很合理。",
]
for text in multilingual_texts:
response = client.models.generate_content(
model="gemini-2.5-flash",
contents=f"以下のテキストを分析してください。言語を検出し、感情を判定してください。\n\nテキスト: {text}",
config={
"response_mime_type": "application/json",
"response_schema": MultilingualAnalysis,
}
)
result = MultilingualAnalysis.model_validate_json(response.text)
print(f"言語: {result.detected_language} | 感情: {result.sentiment.value} | 要約: {result.translated_summary}")
# 期待出力例:
# 言語: en | 感情: positive | 要約: 期待を超える商品品質で強く推奨
# 言語: ja | 感情: negative | 要約: 配送遅延への強い不満
# 言語: zh | 感情: neutral | 要約: 品質は普通だが価格は妥当言語ごとにモデルを切り替える必要がなく、1 つのプロンプトで多言語対応できるのが Gemini の強みです。
カスタムカテゴリで柔軟に分類する
ビジネスの要件に合わせて分類カテゴリを自由にカスタマイズできます。問い合わせメールの自動振り分けを例に見てみましょう。
class TicketPriority(str, Enum):
URGENT = "urgent"
HIGH = "high"
MEDIUM = "medium"
LOW = "low"
class TicketCategory(str, Enum):
BUG_REPORT = "bug_report"
FEATURE_REQUEST = "feature_request"
BILLING = "billing"
ACCOUNT = "account"
GENERAL = "general"
class SupportTicket(BaseModel):
category: TicketCategory
priority: TicketPriority
sentiment: Sentiment
requires_human: bool = Field(description="人間のオペレーターへのエスカレーションが必要か")
suggested_response: str = Field(description="推奨される初回応答文(50文字以内)")
ticket_text = "ログインできなくなりました。パスワードリセットのメールも届きません。急ぎで対応してほしいです。"
response = client.models.generate_content(
model="gemini-2.5-flash",
contents=f"""以下のサポート問い合わせを分析し、カテゴリ・優先度・感情・エスカレーション要否・推奨応答を判定してください。
問い合わせ: {ticket_text}""",
config={
"response_mime_type": "application/json",
"response_schema": SupportTicket,
}
)
ticket = SupportTicket.model_validate_json(response.text)
print(f"カテゴリ: {ticket.category.value}")
print(f"優先度: {ticket.priority.value}")
print(f"感情: {ticket.sentiment.value}")
print(f"エスカレーション: {'必要' if ticket.requires_human else '不要'}")
print(f"推奨応答: {ticket.suggested_response}")
# 期待出力例:
# カテゴリ: account
# 優先度: urgent
# 感情: negative
# エスカレーション: 必要
# 推奨応答: ログインの件、早急に確認いたします。担当者より折り返しご連絡します。requires_human フィールドを追加することで、自動応答で済む問い合わせと人間の対応が必要な問い合わせを自動で仕分けできます。
精度を高めるプロンプト設計のコツ
テキスト分類の精度は、プロンプトの書き方に大きく左右されます。以下のテクニックを活用してください。
System Instruction の活用
response = client.models.generate_content(
model="gemini-2.5-flash",
contents=review_text,
config={
"system_instruction": """あなたはテキスト分類の専門家です。
以下のルールに従って分類してください:
- 複数のカテゴリに該当する場合は、最も顕著なものを1つ選ぶ
- 確信度は慎重に設定する(0.9以上は明確な場合のみ)
- 皮肉や反語表現に注意する(例:「素晴らしいですね(笑)」は negative)
- 顔文字・絵文字も感情判定の手がかりにする""",
"response_mime_type": "application/json",
"response_schema": TextClassification,
}
)Few-shot 例の追加
few_shot_prompt = """以下のレビューを分類してください。
## 分類例
レビュー: 「最高の商品です!」→ category: product_quality, sentiment: positive, confidence: 0.95
レビュー: 「まあ普通かな」→ category: product_quality, sentiment: neutral, confidence: 0.70
レビュー: 「返金してほしい」→ category: billing, sentiment: negative, confidence: 0.90
## 分類対象
レビュー: {review_text}"""Few-shot 例を 2〜3 個追加するだけで、境界線上のケース(ポジティブ寄りのニュートラルなど)の判定精度が向上します。Structured Output の詳しい設定方法については Gemini API の JSON Mode と構造化出力ガイド も参考にしてください。
全体を振り返って
Gemini API の Structured Output を活用すると、学習データなしでテキスト分類・感情分析パイプラインを短時間で構築できます。Pydantic モデルでスキーマを定義し、Enum でカテゴリと感情ラベルを固定することで、安定した分類結果を得られるのがポイントです。
今回紹介したパターンは、カスタマーレビューの分析だけでなく、問い合わせメールの振り分け、SNS モニタリング、アンケート集計など、幅広い用途に応用できます。まずは Gemini API クイックスタートガイド で API キーを発行し、小さなデータセットから試してみてください。