「お金を払ってでも解決したい悩み」をネットの投稿からLLM(Structured Output)で自動抽出・構造化してみた
新規事業開発や個人開発を行う際、「ユーザーが本当にお金を払ってでも解決したい深い課題(ペイン)」を発掘することは極めて重要です。
本記事では、Yahoo!知恵袋などの公開投稿データから、OpenAIの Structured Outputs(Pydantic構造化出力) と PostgreSQL (pgvector) を活用し、ペインの核となる原因・キーワード・収益化スコア・切実度スコアを自動抽出・構造化するパイプラインの構築手法を解説します。
🛠️ 全体アーキテクチャ
- データ取得: Webスクレイピングで質問のタイトル・本文を取得
-
LLM構造化抽出:
gpt-4o-miniのStructured Outputsを使用し、決められたJSONスキーマ(Pydanticモデル)でペイン・スコアを抽出 -
ベクトル埋め込み:
text-embedding-3-small(1536次元) で類似検索・クラスタリング用のベクトルを生成 -
PostgreSQL格納:
pgvector拡張機能を導入したDBに保存
💻 Pydantic による構造化データ定義
LLMに抽出させたいペイン構造を Python の Pydantic モデルとして定義します。
from typing import List
from pydantic import BaseModel, Field
class PainStructure(BaseModel):
core_pain: str = Field(description="根本原因・お悩みのコア")
clean_pain_text: str = Field(description="Embedding用ノイズ除去済み標準化テキスト")
pain_keywords: List[str] = Field(description="ペイン構成キーワードタグのリスト (3〜6個)")
bottlenecks: List[str] = Field(description="解決を妨げている障害・ボトルネックのリスト (1〜3個)")
monetization_score: float = Field(description="有償サービス利用意欲スコア (0.0〜1.0)")
urgency_score: float = Field(description="緊急度・深刻度スコア (0.0〜1.0)")
monetization_reason: str = Field(description="スコア判定の理由・根拠")
pain_summary: str = Field(description="ペイン全体の要約 (1〜2文)")
LLM プロンプト設計
SYSTEM_PROMPT = """あなたは新規事業開発およびユーザーペイン(課題)分析の専門家です。
与えられたYahoo!知恵袋の質問を分析し、ユーザーが直面している「課題の本質(ペイン)」を構造化して抽出してください。
【評価基準】
1. monetization_score (0.0〜1.0): 有料サービス・代行・専門家への支払い意思の強さ
2. urgency_score (0.0〜1.0): 今すぐ解決が必要か、深刻な精神的・経済的被害が出ているか
3. clean_pain_text: 原文から雑談を取り除き、Embedding検索に最適化された正確で簡潔なペイン説明文
"""
🐍 抽出 & ベクトル化スクリプトの実装
import json
import os
from openai import OpenAI
import psycopg2
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
def analyze_question(title: str, body: str):
# 1. LLM Structured Output による構造化抽出
completion = client.beta.chat.completions.parse(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": f"【タイトル】: {title}\n【本文】: {body}"},
],
response_format=PainStructure,
)
pain = completion.choices[0].message.parsed
# 2. Embedding ベクトルの生成
emb_res = client.embeddings.create(
model="text-embedding-3-small",
input=pain.clean_pain_text,
)
embedding = emb_res.data[0].embedding
embedding_str = "[" + ",".join(map(str, embedding)) + "]"
return pain, embedding_str
🗄️ PostgreSQL (pgvector) テーブル設計
-- pgvector 拡張機能の有効化
CREATE EXTENSION IF NOT EXISTS vector;
-- ペイン分析テーブル
CREATE TABLE IF NOT EXISTS pain_analysis (
question_id VARCHAR(50) PRIMARY KEY,
core_pain TEXT,
clean_pain_text TEXT,
pain_keywords TEXT[],
bottlenecks TEXT[],
monetization_score FLOAT,
urgency_score FLOAT,
monetization_reason TEXT,
pain_summary TEXT,
embedding vector(1536),
cluster_id INT,
analyzed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- キーワード検索用 GIN インデックス
CREATE INDEX IF NOT EXISTS idx_pain_keywords ON pain_analysis USING GIN (pain_keywords);
📊 実際の抽出結果サンプル
実際にスクレイピングした質問データを本パイプラインに通すと、以下のように高精度で有償解決意欲と切実度がスコアリングされます。
例①: 適応障害による退職のお悩み
-
タイトル:
適応障害で会社を辞めたいのですが… -
抽出結果:
-
コア:
適応障害で出社不能。会社を辞めたいが自力で上示に伝えることができず、即日退職したい -
キーワード:
["適応障害", "退職代行", "即日退職", "メンタルヘルス"] -
収益化スコア:
0.85/ 切実度スコア:0.90 -
判定理由:
自力での解決が困難であり、手元の損失を避けて即日解決するため有料代行サービスへの支払い意欲が極めて高い
-
コア:
例②: 雑学的な質問
-
タイトル:
無銭飲食の別の言い方はありますか? -
抽出結果:
-
収益化スコア:
0.10/ 切実度スコア:0.10 -
判定理由:
単なる言葉の言い換えの疑問であり、金銭的な解決を求めているペインではない
-
収益化スコア:
💡 まとめ
OpenAI の Structured Outputs を使用することで、従来の自然言語処理(NLP)では難しかった「有料サービスを使ってでも解決したいか?」というビジネス的なインテンション(意図)を自動でスコアリング可能になりました。
次回は、この抽出した1536次元のベクトルデータを UMAP × HDBSCAN + LLM自動命名 でクラスタリングし、課題領域を全自動でマッピングする手法を解説します。