AI編集者開発のリアル(9) 感覚でなくデータで改善する:GA4連携とLLMスコアリングの実践

AI編集者開発のリアル(9) 感覚でなくデータで改善する:GA4連携とLLMスコアリングの実践 — GA4, LLM, Python AIテキスト

こんにちは、パレイド思想部です。

「この記事、思ったより読まれていないな」「タグが適切かどうかわからない」「内部リンクをもっと張るべきでは?」——こうした確認作業を、勘と手動チェックで回していませんか。

第6回は、Google Analytics 4(GA4)・Search Console のデータを Python で取得し、LLM がブログ記事の品質を自動スコアリングする仕組みと、その結果を Streamlit で可視化するフローを解説します。

感覚でなくデータで改善する:GA4連携とLLMスコアリングの実践

記事品質の改善には2種類のフィードバックが必要です。

フィードバック種別情報源確認できること
読者行動GA4 / Search Console実際に読まれた回数・検索クエリ・流入元
コンテンツ品質LLM スコアリングタグの妥当性・内部リンク機会・可読性

前者は「すでに起きたこと」の計測、後者は「改善できること」の発見です。どちらか一方だけでは手が打てない。ai-editor ではこの2軸を同じダッシュボードで並べて、より効果の高い順に改善を進めることを目指しています。

Google Analytics (GA4) 連携の実装

GA4 データの取得には google-analytics-data、Search Console には google-api-python-client を使います。

代替案として requests で直接 REST API を叩く方法もありますが、OAuth2 トークンリフレッシュの処理を自前で書く必要があり、実装やテストが複雑になります。公式クライアントライブラリであれば認証が容易なため、こちらを採用しました。

# tools/publish/wp_analytics/ga4_client.py
from google.analytics.data_v1beta import BetaAnalyticsDataClient
from google.analytics.data_v1beta.types import (
    DateRange, Dimension, Metric, RunReportRequest,
)

class GA4Client:
    def __init__(self, property_id: str, credentials_path: str):
        import os
        os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = credentials_path
        self.client = BetaAnalyticsDataClient()
        self.property_id = property_id

    def fetch_page_metrics(
        self, start_date: str, end_date: str, limit: int = 50
    ) -> list[dict]:
        request = RunReportRequest(
            property=f"properties/{self.property_id}",
            dimensions=[Dimension(name="pagePath")],
            metrics=[
                Metric(name="screenPageViews"),
                Metric(name="averageSessionDuration"),
                Metric(name="bounceRate"),
            ],
            date_ranges=[DateRange(start_date=start_date, end_date=end_date)],
            limit=limit,
        )
        response = self.client.run_report(request)
        return [
            {
                "path": row.dimension_values[0].value,
                "views": int(row.metric_values[0].value),
                "avg_duration": float(row.metric_values[1].value),
                "bounce_rate": float(row.metric_values[2].value),
            }
            for row in response.rows
        ]

RunReportRequest にディメンション・メトリクス・期間を渡すだけで構造化レスポンスが返ります。ページパスと記事フロントマターの slug をキーにして、GA4 データをローカルの Markdown と突合します。

Search Console 流入クエリの取得

同様に、Search Console のデータも API で取得します。

# tools/publish/wp_analytics/search_console_client.py
from googleapiclient.discovery import build
from google.oauth2.service_account import Credentials

class SearchConsoleClient:
    def __init__(self, site_url: str, credentials_path: str):
        creds = Credentials.from_service_account_file(
            credentials_path,
            scopes=["https://www.googleapis.com/auth/webmasters.readonly"],
        )
        self.service = build("searchconsole", "v1", credentials=creds)
        self.site_url = site_url

    def fetch_queries(self, page_url: str, days: int = 28) -> list[dict]:
        from datetime import date, timedelta
        end = date.today()
        start = end - timedelta(days=days)
        body = {
            "startDate": str(start),
            "endDate": str(end),
            "dimensions": ["query"],
            "dimensionFilterGroups": [{
                "filters": [{
                    "dimension": "page",
                    "expression": page_url,
                }]
            }],
        }
        resp = (
            self.service.searchanalytics()
            .query(siteUrl=self.site_url, body=body)
            .execute()
        )
        return resp.get("rows", [])

LLM 品質スコアリング

GA4 で「読まれていない」記事が見つかったとき、次に知りたいのは「なぜ読まれないのか」です。LLM はここで使います。

スコアリング項目と設計意図

LLM に「記事全体を評価してください」と投げるだけでは、定性的・定量的な評価はできません。 評価軸を構造化して渡すことで、複数記事の比較と評価を可能にしました。

  • タグと本文の一致度
  • 現在の内部リンク数
  • LLM が提案するリンク先
  • 見出し構成・段落長

実際には、WordPress API と組み合わせて全記事に一括で同じ処理を適用しています。 {} で囲まれたところはPythonで実際の情報を展開します。 また、指示応答性の高いモデルや function calling 対応モデルを使えば回答をJSONで取得できます。

以下のブログ記事を評価し、JSON で結果を返してください。
{記事本文}

既存タグ: {タグ}
サイト内の関連記事スラッグ一覧: {スラッグ一覧}

評価項目:
- tag_relevance (0-10): タグと本文テーマの一致度
- internal_link_opportunities: 本文中でリンクを張るべき関連記事スラッグのリスト(最大3件)
- readability (0-10): 見出し・段落構成の読みやすさ
- seo_summary: 50字以内の改善提案

JSON のみ返してください。

スコアリングだけならローカルLLMの軽量モデルで十分なケースが多く、ここでは qwen3:8b を使ってみました。 帰ってきたデータは、当サイトは Streamlit を用いたダッシュボードで管理してますが、ここでは割愛します。 LLM の応答を JSON として取得できれば、その後の応用も容易になるでしょう。

まとめ

今回実装したのは3つの仕組みです。

  1. GA4 Client — ページ単位のビュー数・直帰率を取得し、Markdown のスラッグと突合
  2. LLM スコアラー — タグ適合度・内部リンク機会・可読性を構造化 JSON で出力

「感覚でいい記事を書く」から「データで改善箇所を特定して書く」へのシフトは、GA4 のインストールより 記事データとの突合ロジック に手間がかかります。slug をキーにした名寄せと、LLM 出力の JSON スキーマ固定が安定運用のポイントでした。

次回最終回(第10回)は、全体のまとめを再掲します。

タイトルとURLをコピーしました