2026年3月下旬、当サイト「AI Heartland」を公開した。AI関連OSSの日本語解説に特化した個人ブログだ。
公開直後のトラフィックはほぼXからのソーシャル流入で、オーガニック検索の比率はわずか5.5%だった。1ヶ月後の現時点(2026年4月25日)で同比率は62.2%になっている。PV 34,734・ユニークユーザー24,646人・Google Search Consoleのウィークリークリック数は84→1,041→1,031→1,290と推移している。
先に重要な前提を書いておく。
まず、これは立ち上げ1ヶ月目の数字だ。Googleは新規サイトに対してインデックスを優遇するケースがあり、初月のデータがそのまま続くとは限らない。3〜6ヶ月後に同水準が維持されているかどうかを見て初めて評価できる。今この時点では「成功」でも「証明」でもなく、ただの「初月レポート」だ。
次に、規模の小ささを正直に書く。週次クリック1,290は、大手メディアの1日分にも満たない。「62%」という比率の数字は目立つが、絶対値は小さい。
そういう前提のもとで、試行錯誤の記録として読んでもらえれば幸いだ。
この記事はAI自動化ツール完全ガイドで紹介している自動化の思想を、当サイト自身の運用に適用した実例としても読める。
なぜソーシャル依存をやめようとしたか
公開前に一つの方針を決めた。「ソーシャルトラフィックに依存するメディアは作らない」というものだ。
理由は単純だ。X(Twitter)のポスト1本がインプレッションを稼いでも、その流入は48〜72時間で終わる。翌週には同じ数字を出すために別の投稿が必要になる。この繰り返しは、コンテンツの「賞味期限」を極端に短くする。
一方でオーガニック検索は、一度Googleのインデックスに入って評価を得ると、同じ記事が数ヶ月〜数年にわたってトラフィックを生み続ける可能性がある。
ソーシャルは「フロー資産」、SEOは「ストック資産」だ。当サイトはストック資産を積み上げることを試みた。
ただし落とし穴がある。「ストック資産」という言葉の響きは心地よいが、実際には「インデックスされてから評価が上がるまでに3〜6ヶ月かかる」という現実がある。その間は数字が出ない。モチベーションが続かなければ、3ヶ月後に成果を見ることなくサイトを閉鎖することになる。
そこで自動化が必要になった。手動で1記事ずつ書いていたら、2週間でモチベーションが尽きる自信があった。
技術スタック:シンプルさを優先した選定
(記事・設定)"] --> B["GitHub Actions
(ビルド・自動生成)"] B --> C["Cloudflare Pages
(CDN・本番配信)"] D["Claude Code
(記事生成・SEO)"] --> A E["Claude API
(脆弱性速報)"] --> A F["GA4 + GSC
(分析)"] --> D style A fill:#1a2332,color:#fff style B fill:#1a2332,color:#fff style C fill:#f38020,color:#fff style D fill:#cc785c,color:#fff style E fill:#cc785c,color:#fff style F fill:#1a5276,color:#fff
Jekyll:静的サイトジェネレーター
Jekyll を選んだ理由は「Markdown + YAML frontmatter で完結する」からだ。LLMが生成したテキストをそのままファイルに書き込めば、あとはビルドが走って本番に反映される。WordPressのようなDBは必要ない。APIはない。CMS管理画面もない。
シンプルさは脆弱性の少なさにも繋がる。静的サイトはサーバーサイドのコード実行がないため、SQLインジェクションやXSSの攻撃面が存在しない。
Cloudflare Pages:旧 GitHub Pages からの移行
公開当初はGitHub Pagesを使っていたが、途中でCloudflare Pagesに移行した。移行した理由は3つある。
- CDNパフォーマンス:Cloudflare のエッジネットワークは日本国内でのレイテンシが低い
- プライベートリポジトリ対応:GitHub Pagesの無料プランはパブリックリポジトリが前提
- ビルド速度:Cloudflare PagesはJekyllビルドが並列実行でGitHub Actions比で速い
Claude Code:記事生成エンジン
記事の下書き生成に使っている。Claude Code(CLI)をGitHub Actionsのワーカー上で動かし、指定したURLをフェッチして記事を生成するパイプラインを組んだ。
# GitHub Actionsワーカー上での実行例
claude \
--model claude-sonnet-4-6 \
--no-interactive \
--input-file article_request.json \
--output article_draft.md
実際のパイプラインはもう少し複雑で、Research → Links → Metadata → Body → FactCheck の5ステップに分割している。各ステップを別々のLLM呼び出しにすることで、モデルに「今何をすべきか」を明確に伝え、全部を一度にやらせようとする場合より品質が安定した(それでも後述の問題は起きたが)。
GitHub Actions:オーケストレーション
毎日定刻に走るCronジョブと、特定のイベント(脆弱性検知・PR作成)をトリガーにしたワークフローが混在している。
# .github/workflows/daily-article.yml(抜粋)
on:
schedule:
- cron: '0 2 * * *' # UTC 02:00 = JST 11:00
workflow_dispatch:
jobs:
generate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Generate articles
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
FAL_KEY: ${{ secrets.FAL_KEY }}
run: |
pip install -r requirements.txt
python tools/daily_generator.py
- name: Create PR
uses: peter-evans/create-pull-request@v6
トピッククラスタ設計:10クラスタ・ピラー+スポーク構造
SEOの文脈でよく出てくる「トピッククラスタ」という概念を設計に取り入れた。
1つのテーマに関して、包括的な「ピラー記事」と個別トピックを扱う「クラスタ記事(スポーク)」を組み合わせ、内部リンクで繋ぐことでGoogleに専門性を示す、という考え方だ。
本当にこれが効いているのかは、まだ判断できる段階にない。ただ設計として当サイトは以下の10クラスタを設定した。
| クラスタ | ハブ(ピラー)記事 | 記事数 |
|---|---|---|
| Claude Code | Claude Code完全ガイド2026 | 約80本 |
| AIエージェント | AIエージェントフレームワーク比較2026 | 約60本 |
| AIコーディング | Vibe Coding完全ガイド | 約30本 |
| MCP | MCPサーバーの作り方2026完全ガイド | 約40本 |
| RAG&ナレッジ | RAGとは?2026完全ガイド | 約50本 |
| LLM/ローカルAI | LLMとは?2026完全ガイド | 約60本 |
| セキュリティ | サプライチェーンセキュリティ完全ガイド | 約30本 |
| DevOps&自動化 | AI自動化ツール完全ガイド | 約40本 |
| Claude API | Claude料金まとめ | 約20本 |
| UI生成&デザイン | デザインシステム入門ガイド | 約10本 |
ピラー記事の設計基準
ピラー記事は12,000文字以上を必須とした。「Googleに専門性を示す」という意図からすれば、表面的な情報をなぞっただけのページでは不十分だと判断した。ただしこれが実際に効いているかどうかは、まだデータが少なくて断言できない。
なおピラー記事を全クラスタで作成する計画だったが、4月25日時点でLLM・RAG・Security・DevOpsの4クラスタのピラーはまだ未完成だ。計画通りに進んでいないことも正直に書いておく。
反省:41件の架空コンテンツを出してしまった
一番書きにくいが、一番重要な話だ。
量産の過程で、以下の問題を含む記事が41件生まれた。
- 実在しないGitHubリポジトリへのリンク:記事中に「このリポジトリを参照」とあるが、実際にはそのURLが存在しない
- 公式ドキュメントに存在しない機能の説明:特にAPIオプションやコマンドフラグの部分に多い
- 古いバージョンの情報をそのまま記述:2024年時点の情報を2026年の記事として提示
責任の所在はどこにあるか。LLMが「それらしい情報」を生成する性質を理解したうえで量産に踏み切ったにもかかわらず、ファクトチェックのステップを最初から組み込んでいなかった。自分の設計の甘さだ。「AIが嘘をついた」という話ではなく、「事実確認の仕組みを整えないまま公開した」という問題だ。
発覚後、41件を非公開化し、現在は以下のフローに変更した。
# tools/shared/pipeline.py — ファクトチェックステップ(抜粋)
def factcheck_body(client, body: str, facts: dict, source_url: str) -> str:
"""本文をソースと照合して事実誤認を修正する。"""
prompt = f"""以下の本文をソースと照合し、事実誤認・架空URLを修正してください。
ソースURL: {source_url}
確認済み事実: {json.dumps(facts, ensure_ascii=False)}
修正ルール:
- ソースに存在しないURLは削除(「公式ドキュメント参照」等に置換)
- ソースに記載のない機能は「[要確認]」タグを追加
- バージョン情報は必ずソースから引用
本文:
{body}"""
return call_llm(client, prompt)
ファクトチェックをパイプラインに組み込んでから架空コンテンツの発生率は下がった。ただしゼロではない。LLMによる自動ファクトチェックもLLMが間違える。最終的には人間がレビューするステップが不可欠だということを、この失敗で学んだ。
(ソースから事実抽出)"] B --> C["Links Agent
(内部リンク候補選定)"] C --> D["Metadata Agent
(title/description/FAQ生成)"] D --> E["Body Agent
(本文執筆 12,000文字+)"] E --> F["FactCheck Agent
(ソースとの照合・架空URL除去)"] F --> G{"人間レビュー
※当初なかった"} G -->|OK| H["PR作成 → マージ → 自動デプロイ"] G -->|NG| I["差し戻し・修正依頼"] I --> E style F fill:#2d1520,color:#fff style G fill:#1a2e1e,color:#fff
内部リンク:192記事に一括追加したスクリプト
トピッククラスタ理論の核心は「内部リンク」だ。ピラー記事への参照がなければ、クラスタ記事はただの孤立した記事になる。
問題は、クラスタ戦略を設計した時点では既に100本超の記事が公開済みだったことだ。過去記事に手動で内部リンクを追加するのは現実的ではないので、一括追加スクリプトを作った。
# tools/bulk_internal_links.py(概念コード)
import re
from pathlib import Path
CLUSTER_PILLAR_MAP = {
"claude-code": ("/explain/claude-code-complete-guide-2026/",
"Claude Code完全ガイド2026:インストールから本番運用まで"),
"ai-agent": ("/explain/ai-agent-framework-comparison-2026/",
"AIエージェントフレームワーク比較2026"),
"mcp": ("/mcp/mcp-server-build-guide/",
"MCPサーバーの作り方2026完全ガイド"),
# ... 全10クラスタ
}
def inject_pillar_link(post_path: Path, cluster_id: str) -> bool:
"""導入文の直後、最初のH2の直前にピラーリンクを挿入する。"""
content = post_path.read_text()
pillar_url, pillar_title = CLUSTER_PILLAR_MAP[cluster_id]
# 既にリンクがある場合はスキップ
if pillar_url in content:
return False
# 最初のH2の位置を特定
h2_match = re.search(r'^## ', content, re.MULTILINE)
if not h2_match:
return False
link_text = f"\nこの記事は[{pillar_title}]({pillar_url})の関連解説記事です。\n\n"
insert_pos = h2_match.start()
new_content = content[:insert_pos] + link_text + content[insert_pos:]
post_path.write_text(new_content)
return True
このスクリプトで192記事に対してピラーリンクを一括挿入した。処理時間は全記事で約8分。
リンクの粗さも反省点のひとつ
一括挿入にはリスクがある。機械的にリンクを挿入すると、文脈に合わないリンクが生まれることがある。LLMの量子化手法を解説する記事に「Claude Code完全ガイド」へのリンクが入っても、読者にとって意味がない。スクリプト実行後に全リンクを人力でレビューし、不自然なものは修正したが、見落としがある可能性は否定できない。
脆弱性速報の自動生成:GHSA + OSV.dev + Claude API
セキュリティ速報は特殊なコンテンツだ。CVE(Common Vulnerabilities and Exposures)の情報は公開されると数時間以内に複数のメディアが記事を出す。
ここで差別化できるのは「スピード」よりも「日本語での詳細解説」だという仮説で動いている。英語の速報を日本語で詳しく解説した記事は、検索結果で競合が少ない。これが実際にトラフィック増加に繋がっているかは検証中だ。
自動化パイプラインは以下の構成だ。
# GHSA・OSV.devのフィード監視(毎15分実行)
python tools/security/vuln_monitor.py \
--sources ghsa,osv \
--severity high,critical \
--since 4h
# 検知時:記事生成ワークフローをトリガー
gh workflow run generate-security-article.yml \
-f cve_id="CVE-2026-XXXXX" \
-f source_url="https://github.com/advisories/GHSA-xxxx"
GHSA(GitHub Security Advisories)はGitHub上のパッケージに特化しており、npm・PyPI・Go・RubyGems等のエコシステムを網羅する。OSV.dev(Open Source Vulnerabilities)はより広範で、Maven・Debian・Alpine等も含む。
記事の生成から公開まで30分以内を目標にしている。実際の所要時間の内訳は:
| ステップ | 所要時間 |
|---|---|
| CVE検知 → ワークフロートリガー | 〜1分 |
| ソース収集(GHSA + NVD + 関連PR) | 3〜5分 |
| 記事生成(Claude API) | 5〜8分 |
| ファクトチェック | 2〜3分 |
| OGP画像生成 | 3〜5分 |
| PR作成 → マージ(重大度High以上は自動) | 1〜2分 |
| Cloudflare Pages ビルド | 3〜4分 |
| 合計 | 17〜24分 |
GSCデータで振り返る:週次の推移
週次のGoogle Search Consoleクリック数の推移を公開する。比率の変化と一緒に見てほしい。
| 週 | GSCクリック | 前週比 | オーガニック比率 |
|---|---|---|---|
| Week 1(公開直後) | 84 | - | 5.5% |
| Week 2 | 1,041 | +1,139% | 約31% |
| Week 3 | 1,031 | ▲1% | 約48% |
| Week 4 | 1,290 | +25% | 62.2% |
Week 2の急伸は「インデックス登録ラグ」の解消が主因だと考えている。Googleは新しいサイトのクロールを最初は慎重に行う傾向がある。ただしこれは推測であり、Googleがどういう基準で判断しているかは公開されていない。
Week 3が微減になったのはやや意外だった。一部のクラスタ記事がピラー記事より先にランクインし、その後ピラー記事が追い上げた際に同一テーマで検索順位が競合した(カニバリゼーション)と仮説を立てている。内部リンク整備後に徐々に解消したようだが、因果関係は断定できない。
効果があったと思われるコンテンツ・効果が薄かったコンテンツ
実際にクリックを獲得しているのは「ツール名 + 日本語」という検索クエリに対応した記事が多い。英語圏では解説が豊富なOSSツールでも、日本語の詳細解説が少ないものは多い。「[ツール名] 使い方」「[ツール名] インストール」というクエリで上位に入りやすかった。
一方で効果が薄かったのは「AI活用事例」「最新AIトレンド」系の記事だ。競合が強く、検索意図が曖昧で、量産しても検索流入はほぼなかった。
autoreasonフレームワーク:SEOリライトの試み
4週目に、GSCデータを使ったリライト自動化を試みた。「autoreason」と呼んでいる。
仕組みは以下の通りだ。
- Google Search Consoleのデータを取得し「インプレッションが多いのにCTRが低い記事」を抽出する
- 対象記事のtitle・descriptionをClaude Codeにリライトさせる
- キーワードの自然な挿入位置を特定し、本文を部分的に書き換える
# tools/rewrite_posts.py(抜粋)
def find_quick_wins(gsc_client, ga_client, threshold_impr=500, max_ctr=0.03):
"""インプレッション高・CTR低の記事を抽出するQuick Win検出。"""
rows = gsc_client.get_performance(
start_date="2026-04-01",
end_date="2026-04-24",
dimensions=["page", "query"],
)
quick_wins = []
for row in rows:
if row["impressions"] >= threshold_impr and row["ctr"] < max_ctr:
quick_wins.append({
"url": row["page"],
"query": row["query"],
"impressions": row["impressions"],
"ctr": row["ctr"],
"position": row["position"],
})
return sorted(quick_wins, key=lambda x: x["impressions"], reverse=True)
このフレームワークを導入したのはWeek 4の途中なので、今のところ効果の測定は難しい。Week 4全体では+25%のクリック増加があったが、リライトの影響かインデックス増加の影響かを切り分けられていない。
GSCとGA4を組み合わせる意図
GSC(Google Search Console)はキーワード・順位・CTRを教えてくれるが、ページ内でのユーザー行動はわからない。GA4は「どのページに来て、どれくらい滞在して、どこへ離脱したか」を教えてくれるが、そのページに到達したキーワードはわからない。
両方を組み合わせることで「このキーワードで来た人が、このページで3分以上滞在している」という情報が得られる。滞在時間が長い記事は優先してリライトする価値がある、という仮説で動かしている。
# tools/shared/google_analytics.py — GA4 APIクライアント(使用例)
from tools.shared.google_analytics import GA4Client
ga = GA4Client(property_id="YOUR_PROPERTY_ID")
low_engagement = ga.get_pages_with_low_engagement(
start_date="2026-04-01",
end_date="2026-04-24",
min_sessions=100,
max_avg_session_seconds=60, # 1分未満を「低エンゲージメント」と定義
)
OGP画像も自動生成している
記事を量産するには、OGP画像(SNSシェア時に表示されるサムネイル)の生成も自動化が必要だった。手動でデザインしていたら記事数に追いつかない。
当サイトでは2種類の画像生成を使い分けている。
1. Playwright によるHTML/CSSレンダリング(標準)
# tools/shared/ogp_image.py — フォールバックOGP生成
from tools.shared.ogp_image import generate_fallback_ogp
image_url = generate_fallback_ogp(
title="MCPサーバーの作り方2026完全ガイド",
slug="mcp-server-build-guide",
emoji="🔌",
category="mcp",
cluster_id="mcp",
)
# → /generated_images/fallback_mcp-server-build-guide.webp
Playwright でヘッドレスChromiumを起動し、HTML/CSSで組んだカード画像をスクリーンショットとして取得する。フォント・配色・レイアウトはコードで制御できる。
2. fal.ai による銅版画風AI生成(重要記事用)
重要度が高い記事には fal.ai の画像生成APIを使った「銅版画風」のカバー画像を生成している。
# Claude Code関連記事(タグに 'claude' を含む場合)
from tools.shared.ogp_image import generate_cover_image
cover_path = generate_cover_image(
title="Claude Code完全ガイド2026",
tags=["claude", "claude-code"],
style="copper", # 銅版画スタイル
output_name="cover_claude-code-complete-guide-2026.webp",
)
FAL_KEYが設定されていない環境では自動的にPlaywrightフォールバックに切り替わる。
claude/mcp/agent
が含まれる?"} B -->|Yes + FAL_KEY有| C["fal.ai
銅版画生成
(重要記事)"] B -->|No / FAL_KEY無| D["Playwright
HTML/CSS
(標準)"] C --> E["webp変換
docs/generated_images/"] D --> E E --> F["frontmatterに
imageパスを記載"] style C fill:#2e1c14,color:#fff style D fill:#1e2130,color:#fff
現在の数字と残っている課題
1ヶ月間の実績をまとめると以下の通りだ。
| 指標 | 公開時(3月下旬) | 1ヶ月後(4月25日) |
|---|---|---|
| 累計PV | 〜500 | 34,734 |
| ユニークユーザー | 〜350 | 24,646 |
| オーガニック比率 | 5.5% | 62.2% |
| GSCウィークリークリック | 84 | 1,290 |
| 公開記事数 | 〜20 | 423(削除済み除く) |
| インデックス済み記事 | 〜15 | 約380 |
自動化によって何が変わったか
自動化の有無で作業量を比較すると、規模感がわかりやすい。
| 項目 | 手動の場合(推定) | 自動化後 |
|---|---|---|
| 記事1本の平均作業時間 | 3〜5時間 | 15〜30分(レビュー含む) |
| OGP画像作成 | 20〜40分/枚 | 3〜5分/枚 |
| 内部リンク追加(192記事) | 数週間 | スクリプトで8分 |
| 脆弱性速報の公開 | 翌日〜翌々日 | 30分以内 |
| GSC分析〜リライト実行 | 半日 | 自動化済み(Cron) |
423記事を手動で書いた場合、おそらく数ヶ月から半年以上かかる。自動化により1ヶ月で公開できたのは事実だが、そのうち41件は品質問題で後から削除している。「量」と「質」はトレードオフがあり、解決できていない。
認識している問題点
1. E-E-A-Tの「経験」要素が薄い
GoogleのE-E-A-T(Experience, Expertise, Authoritativeness, Trustworthiness)において、自動生成コンテンツは「経験(Experience)」の要素が弱い。「実際に試した記録」という要素をどう組み込むかが未解決だ。
2. ピラー記事4本が未完成
LLM・RAG・Security・DevOpsの4クラスタのピラー記事がまだない。ピラー記事がないクラスタのクラスタ記事は、内部リンク効果を十分に受けられていない。
3. CLSが高いページが残っている
Cloudflare Pages移行でLCP(Largest Contentful Paint)は改善したが、CLS(Cumulative Layout Shift)がまだ高いページが残っている。OGP画像のサイズ指定が不統一なことが主因で、対応中だ。
やるとしたらこの順序で
この記事で書いた施策を参考にする場合、以下の順序を推奨する。
1. コンテンツ戦略を先に決める ツールや自動化より先に「どのトピッククラスタで戦うか」を決める。競合の少ないニッチを選ばないと、記事をいくら量産しても検索流入はほぼゼロになる。
2. ピラー記事から作る クラスタを決めたら、まずピラー記事を1本仕上げる。12,000文字以上、コード例・比較表・FAQ付き。これが土台になる。
3. 自動化パイプラインを構築する 記事生成・ファクトチェック・画像生成・PR作成・デプロイの流れを1本のワークフローにまとめる。ファクトチェックを最初から入れること。後から追加すると、既に公開した記事の問題が残る。
4. 内部リンクを一括整備する 記事が50本を超えたタイミングで、既存記事への内部リンク一括挿入スクリプトを実行する。放置すると孤立した記事が増え、クロール効率が下がる。
5. GSC + GA4でリライトを繰り返す 公開から4週間後にGSCデータが蓄積され始める。その時点でインプレッション高・CTR低の記事を特定し、優先リライトを実施する。
この5ステップの中で1→2の順序だけは守ること。自動化を先に作って戦略を後回しにすると、ゴミを量産するパイプラインになる。
まとめ:初月レポートとして読んでほしい
この記事で書いたことをまとめる。
- 立ち上げ1ヶ月でオーガニック比率が5.5%から62.2%になった
- Jekyll + Cloudflare Pages + Claude Code + GitHub Actions の自動化パイプラインを組んだ
- 423記事を公開したが、41件はファクトチェック体制の甘さによる架空コンテンツ問題で削除した
- トピッククラスタ設計・内部リンク一括追加・脆弱性速報自動化を試みた
- これらが検索評価に繋がっているかは、数ヶ月後にならないとわからない
62%というオーガニック比率は、小規模サイトの初月としては悪くない数字だと思っている。ただしこれは「成功」ではなく「スタート地点」だ。3ヶ月後・6ヶ月後に同水準が続いているかどうかを見て、初めて評価できる。
失敗も含めてすべて公開したのは、きれいな数字だけを見せることに意味がないと思っているからだ。試行錯誤の過程が誰かの参考になれば幸いだ。