ホームページ制作に使える補助金情報収集
Grant Radar
ホームページ制作に使える補助金情報を、AI が 24 時間自律で収集・解析・LP 化する自己進化型システム。


なぜ作るか
中小企業・個人事業主のための「ホームページ制作に使える補助金」は全国で数百種類存在しますが:
- 自治体ごとに散在し、検索が困難
- 制度が頻繁に更新され、追跡コストが高い
- 民間アグリゲータは情報が古い・不正確なケースが多い
このシステムは 7 つの AI エージェントが連携し、収集 → 構造化 → 品質監査 → 自己修復まで自動で回します。最終ゴールは地域別 LP を量産して月 100 万円 MRR の事業化。
詳細な設計方針は CLAUDE.md を参照。
現在の状況(Phase 0 完了)
| 項目 | 状態 |
|---|---|
| 登録補助金 | 15 件(毎日 daily_discover で増加中) |
| カバー地域 | 6 都府県(東京・新潟・愛知・静岡・石川・長野) |
| 稼働エージェント | 7 種(自己進化ループ完成、L1+L2+L3 自動化済) |
| 自律実行 | 毎朝 06:00 JST(daily_check)+ 07:00 JST(daily_discover) |
| 月間運用コスト | $4〜$6(収集 + 監査 + 発見 + 自己修復、Tier 1) |
| 完全自動度 | 約 80%(残: L4 publisher / Q1 evaluator v2 / Q4 hp-no archive / S2 Brave) |
カバー中の補助金(一部)
| 地域 | 制度名 | 上限額 | 補助率 |
|---|---|---|---|
| 東京都中央区 | 中小企業ホームページ作成費補助金 | ¥300,000 | 1/2 |
| 東京都足立区 | ホームページ作成・更新補助金 | ¥200,000 | 1/2 |
| 東京都荒川区 | ホームページ作成補助金 | ¥200,000 | 1/2 |
| 東京都江東区 | 中小企業ホームページ作成費補助金 | ¥100,000 | 1/2 |
| 東京都葛飾区 | ホームページ作成費補助金交付事業 | ¥50,000 | 1/2 |
| 東京都練馬区 | ネリサポ HP 作成費補助事業 | ¥50,000 | 1/2 |
| 愛知県 | 商業振興事業費補助金 | ¥900,000 | — |
| 石川県金沢市 | 外国語版 HP 整備補助 | ¥400,000 | — |
| 新潟県新潟市 | 新規採用活動支援事業補助金 | ¥200,000 | — |
全 15 件のリアルタイム状況は Streamlit ダッシュボードで確認可能(後述)。
7 エージェントの自己進化ループ
06:00 JST 毎日 → collector → analyzer → grants ──┬→ evaluator
↑ │ │
│ ↓ ↓
│ diff_detector reject なら hash 削除
│ │ │
│ ↓ └─ 翌朝再抽出
│ Discord 通知
│
└─ http_error 検知 → healer 自動起動
↓
sources.yaml を自動更新
07:00 JST 毎日 → discoverer ─→ 曜日別スコープを少量探索 → sources.yaml に自動追加
人間トリガ → writer ─→ 地域別 LP の HTML を生成
| # | エージェント | 役割 | 発火 | モデル |
|---|---|---|---|---|
| 1 | collector/scraper | HTTP 取得 + 本文抽出。robots.txt 尊重、1 秒/ドメイン rate limit、Accept-Language ja | 毎朝 | — |
| 2 | analyzer/extractor | 構造化抽出(金額・補助率・期間・HP 制作対象判定)。プロンプト v1→v2→v3 で精度改善 | 毎朝(hash 不一致時) | Sonnet 4.6 |
| 3 | detector/diff_detector | 前後スナップショット比較 → ChangeReport(new/updated/expired/removed) | 毎朝 | 純関数 |
| 4 | evaluator | rule + AI 整合性チェック → スコア + issue list + recommendation | 毎朝 | Haiku 4.5 |
| 5 | writer/lp_generator | LP 構造化 JSON 生成 + HTML レンダリング(インライン CSS、レスポンシブ) | 手動 | Sonnet 4.6 |
| 6 | discoverer | web_search + filter + extract で新 URL を自動発見 → sources.yaml 追記 | 毎日 07:00 JST(曜日別スコープ) | Haiku 4.5 |
| 7 | healer | 404 検知 → 同一管轄の代替 URL 探索 → 自動置換 | daily_check 中(http_error 検知時) | Haiku 4.5 |
Healer の実例
金沢市の補助金 URL(zyoseiseido/foreignhp.html)が突然 404 に。Healer が:
- ✅ 404 を 3 回リトライ後検知
- ✅ web_search で「金沢市 海外販路開拓支援事業」を検索
- ✅ 同じドメインの新 URL(
gyomuannai/3/4/6584.html)を発見・検証 - ✅
sources.yamlを自動更新、healing_actionsに履歴記録
人間の手介入ゼロで URL 移行が完了。
完全自動運用フロー
2 つの cron + 4 つのフィードバックループで、人手介入ゼロで補助金データが日々増殖・改善・修復されます。
24 時間のサイクル
┌────────────────────────────────────────────────────────────────┐
│ 1 日のループ │
├────────────────────────────────────────────────────────────────┤
│ │
│ 06:00 JST daily_check.yml ───────────────────────────────── │
│ │ │
│ ├─ collector が 15 ソースを取得(並列 3) │
│ ├─ ハッシュ比較 → 変化なしならスキップ │
│ ├─ 変化分は extractor (Sonnet) で構造化抽出 │
│ ├─ diff_detector で new / updated / expired を分類 │
│ │ │
│ │ ⚙ ループ #1: 自己修復 │
│ ├─ http_error 検知 → healer を自動起動 │
│ │ → web_search で代替 URL 探索 │
│ │ → sources.yaml を自動更新 │
│ │ │
│ ├─ evaluator (Haiku) が全件再評価 │
│ │ │
│ │ ⚙ ループ #2: 品質フィードバック │
│ ├─ reject 判定 → content_hash を削除 │
│ │ → 翌朝 daily_check で再抽出される │
│ │ │
│ ├─ Discord に通知(INFO/NOTICE/WARNING/ERROR) │
│ └─ SQLite を [skip ci] で main に auto-commit │
│ │
│ 07:00 JST daily_discover.yml ────────────────────────────── │
│ │ │
│ │ ⚙ ループ #3: 範囲拡張 │
│ ├─ 曜日別スコープを選択 │
│ ├─ discoverer (Haiku + web_search) が候補 URL を発見│
│ ├─ filter (Haiku) で HP focused を判定 │
│ ├─ extract (Sonnet) で構造化抽出 │
│ └─ sources.yaml + grants に自動追記、main に commit │
│ │
│ ⚙ ループ #4: プロンプト改善 (将来 Phase 1) │
│ evaluation_logs の issues を集計 → extractor v4 へ反映 │
│ │
└────────────────────────────────────────────────────────────────┘
4 つのフィードバックループ詳細
ループ #1: 自己修復(Healer、daily_check 内自動発火)
scraper → 404 / Connection error → 3 回リトライ全失敗
↓
healer.heal_source(source)
↓
Claude + web_search で「同一管轄の代替 URL」を検索
↓
候補を validate (HEAD 200 を確認)
↓
sources.yaml を新 URL に書き換え(notes に履歴)
↓
healing_actions テーブルに行追加
↓
Discord 🔥 NOTICE
↓
**翌朝 daily_check で新 URL を scrape**
実例: 金沢市 URL zyoseiseido/foreignhp.html が 404 → 自動で gyomuannai/3/4/6584.html に書き換え、人手介入ゼロで復旧。
ループ #2: 品質フィードバック(Evaluator → Extractor 再実行)
evaluator.evaluate_grant(extraction, raw_text)
↓
score < 0.4 → recommendation = "reject"
↓
clear_content_hash(url_hash)
↓
**翌朝 daily_check が hash 不一致と判断 → extractor が再抽出**
↓
新しい抽出が evaluator に渡される → 品質が高ければ ok、低ければまた reject
↓
(無限ループにならないよう、extractor prompt のバージョンアップで根本対処)
ループ #3: 範囲拡張(Discoverer、曜日別スコープ)
07:00 JST 毎日:
date +%u で曜日番号取得
→ スコープ決定(後述の表)
→ discoverer で 3〜5 件まで自動追加
→ 翌日は別スコープへ
1 週間で全スコープを一巡:
| 曜日 | スコープ | 残り対象(参考) |
|---|---|---|
| 月 | tokyo-23ku --remaining | 23 区中 16 区が未カバー |
| 火 | chubu-prefectures --remaining | 9 県中 7 県が未カバー |
| 水 | kinki-prefectures --remaining | 7 府県すべて未カバー |
| 木 | chubu-kinki-ordinance-cities | 8 政令市 |
| 金 | chubu-kinki-core-cities | 28 中核市 |
| 土 | tokyo-23ku --remaining(再試行) | rate limit でこぼれた分を救済 |
| 日 | chubu-kinki-prefectures --remaining | 中部+近畿合計 |
→ 1〜2 ヶ月で東京 + 中部 + 近畿が埋まる想定。
ループ #4: プロンプト改善(Phase 1 構想)
evaluation_logs.issues を週次集計
↓
頻出フィールド・指摘パターンを抽出
↓
(現状は手動で v3 prompt を v4 に更新、将来は AI が prompt diff を提案)
1 週間のサンプルタイムライン
月 06:00 daily_check 変化なし → 📊 INFO「15 件確認、変化なし」
月 07:00 daily_discover tokyo-23ku → 🔥 NOTICE「新宿区 中小企業支援補助金を追加」
火 06:00 daily_check 1 件更新 → 🔥 NOTICE「新宿区 申請締切が更新」
火 07:00 daily_discover chubu-pref → 📊 INFO「3 件評価、追加 0 件」
水 06:00 daily_check 404 検知 → 🔥 NOTICE「Healer 自動起動: 港区 URL を修復」
水 07:00 daily_discover kinki-pref → 🔥 NOTICE「京都府 デジタル化補助金を追加」
木 06:00 daily_check reject あり → ⚠️ WARNING「Evaluator: 要再抽出 1 件」
木 07:00 daily_discover ord-cities → 🔥 NOTICE「京都市 EC 補助金を追加」
金 06:00 daily_check 前日 reject 分を再抽出 → 📊 INFO「品質改善」
金 07:00 daily_discover core-cities → 📊 INFO「5 件評価、追加 0 件」
土 06:00 daily_check 変化なし → 📊 INFO
土 07:00 daily_discover tokyo-23ku → 🔥 NOTICE「文京区 商業活性化補助金を追加」
日 06:00 daily_check 変化なし → 📊 INFO
日 07:00 daily_discover chubu-kinki → 📊 INFO
Discord 通知サンプル
📊 [INFO] 日次チェック完了
変化なし。15 ソースを確認しました。
🔥 [NOTICE] 補助金データに変化を検知(15 ソース)
🆕 新規 1 件
• 新宿区中小企業支援補助金
🔄 更新 1 件
• 中央区中小企業ホームページ作成費補助金 (application_period_end)
新規: 1 / 更新: 1 / 期限切れ: 0 / 削除: 0
🔥 [NOTICE] Healer: 1 件の URL を自動修復
• https://www4.city.kanazawa.lg.jp/.../foreignhp.html →
https://www4.city.kanazawa.lg.jp/.../6584.html
⚠️ [WARNING] Evaluator: 要レビュー 2 件
• www.city.uonuma.lg.jp (score=0.65)
• minato-sansin.com (score=0.55)
review: 2 / ok: 13
障害時の自動復旧シナリオ
| 障害 | 自動対応 | 通知 | 人手介入 |
|---|---|---|---|
| URL が 404 | Healer が代替 URL を探索・置換 | 🔥 NOTICE | 不要(代替が見つかった場合) |
| URL が 403 / 5xx | リトライ 3 回後 http_error として記録、翌朝再試行 | ⚠️ WARNING | 永続的なら手動調査 |
| Anthropic rate limit | 22 秒間隔の自動 pacing、超過時は次回リトライ | ⚠️ WARNING | 不要(次の minute window で復旧) |
| 抽出失敗(low_confidence) | content_hash に書き込まれず、翌朝再試行 | 含まれず | 永続的なら prompt 改善 |
| 評価で reject | content_hash 削除 → 翌朝再抽出 | ⚠️ WARNING | 持続するなら prompt v4 検討 |
| GitHub Actions タイムアウト | 25 分でジョブが kill、artifact アップロード | 🚨 ERROR | 原因調査 |
| Sonnet API ダウン | リトライ 3 回後失敗、daily_check 全体は継続 | 🚨 ERROR | Anthropic 復旧待ち |
通知レベルの意味
| レベル | 絵文字 | 用途 | 例 |
|---|---|---|---|
| INFO | 📊 | ルーチン完了、変化なし | 「15 件確認、変化なし」 |
| NOTICE | 🔥 | 注目すべきポジティブ変化 | 「新規補助金 1 件追加」「URL 修復成功」 |
| WARNING | ⚠️ | 部分失敗、要レビュー | 「2 ソースで HTTP エラー」「評価低スコア 1 件」 |
| ERROR | 🚨 | パイプライン全体失敗 | 「全 15 ソース失敗」「タイムアウト」 |
人手介入が必要なこと(残り 15%)
現在自動化されていない領域:
- 新地域スコープの追加: 関東以外の地方を追加する場合、
SCOPES辞書への追記が必要 - prompt 改善: evaluator の指摘が累積した時の prompt v4 作成(Phase 1 で半自動化予定)
- LP の生成・公開: 現状
python scripts/generate_lp.pyを手動で叩く(Phase 1 で自動化予定) - 完全廃止された補助金の論理削除: Healer は代替 URL を探すが、制度自体が消えた場合は手動で sources.yaml から削除推奨
- API キーの更新: Anthropic / Discord Webhook のローテーション
それ以外は全て自動:
- スクレイプ、抽出、評価、変化検知、通知、URL 修復、新ソース発見、SQLite 履歴管理
ディレクトリ構成
grant-radar/
├── .github/workflows/ # daily_check.yml, daily_discover.yml, healthcheck.yml
├── agents/
│ ├── collector/scraper.py # 1️⃣ HTTP + 本文抽出
│ ├── analyzer/extractor.py # 2️⃣ 構造化抽出(プロンプト v3)
│ ├── detector/diff_detector.py # 3️⃣ 変化検知(純関数)
│ ├── evaluator/evaluator.py # 4️⃣ 品質監査
│ ├── writer/lp_generator.py # 5️⃣ LP 生成
│ ├── discoverer/discoverer.py # 6️⃣ 新 URL 自動発見
│ └── healer/healer.py # 7️⃣ 死 URL 自動修復
├── lib/
│ ├── claude_client.py # Anthropic SDK ラッパ + Sonnet rate limiter
│ ├── orchestrator.py # daily_check のビジネスロジック
│ ├── db.py # SQLite スキーマ + helpers
│ ├── migrations.py # schema_migrations 管理
│ ├── notifier.py # Discord webhook + ファイルフォールバック
│ ├── scraping.py # RobotsCache + RateLimiter
│ └── sources_yaml.py # sources.yaml の安全な読み書き
├── prompts/
│ ├── extractor_v1.txt / v2.txt / v3.txt # 抽出プロンプト世代
│ ├── evaluator_v1.txt
│ ├── discoverer_search_v1.txt / discoverer_filter_v1.txt
│ ├── healer_search_v1.txt
│ └── lp_v1.txt
├── config/sources.yaml # 情報源リスト(自動 + 手動追加)
├── scripts/
│ ├── daily_check.py # 日次オーケストレータ CLI
│ ├── run_evaluation.py # 品質監査 CLI
│ ├── run_healer.py # URL 自動修復 CLI
│ ├── discover_grants.py # 新ソース発見 CLI
│ ├── generate_lp.py # LP 生成 CLI
│ ├── run_batch.py # 手動バッチ抽出
│ ├── test_daily_check.py # ローカル dry-run
│ ├── run_migrations.py # マイグレーション適用
│ └── migrations/ # 002〜006 SQL マイグレーション
├── dashboard/app.py # Streamlit 4 タブダッシュボード
├── data/
│ ├── grant_radar.sqlite # 唯一の真実(main にコミットされる)
│ ├── raw/ # スクレイプ生テキスト(gitignore)
│ ├── extracted/ # 抽出 JSON 履歴(gitignore)
│ ├── llm_logs/ # LLM 入出力全量(gitignore)
│ └── lp/ # 生成 LP HTML(gitignore)
└── tests/ # (Phase 1 で整備予定)
クイックスタート
必要要件
- Python 3.11 以上(推奨: 3.12)
- macOS / Linux
- Anthropic API キー(Tier 1 以上推奨、web_search 機能利用のため)
- Discord Webhook URL(任意、未設定時は
data/notifications.logにフォールバック)
セットアップ
# 1. リポジトリのクローン
git clone https://github.com/Ippei4114/grant-radar.git
cd grant-radar
# 2. 仮想環境の作成と有効化
python3.12 -m venv .venv
source .venv/bin/activate
# 3. 依存関係のインストール
pip install -e ".[dev]"
# 4. 環境変数の設定
cp .env.example .env
# .env に ANTHROPIC_API_KEY と DISCORD_WEBHOOK_URL を設定
# 5. マイグレーション適用
python scripts/run_migrations.py
GitHub Actions で自律実行を有効化
- GitHub repo の Settings → Secrets and variables → Actions で登録:
ANTHROPIC_API_KEYDISCORD_WEBHOOK_URL
- Actions タブ → Health Check → Run workflow で接続確認
- Discord に ✅ メッセージが届けば OK
- 各ワークフローを 1 度ずつ手動実行で動作確認:
- Daily Grant Check → 全 15 ソースを処理、Discord に結果通知
- Daily Discovery → 曜日別スコープで新ソース探索
- 翌朝から自動実行開始
cron 設定:
daily_check.yml:0 21 * * *UTC(= 06:00 JST 毎日)daily_discover.yml:0 22 * * *UTC(= 07:00 JST 毎日)
CLI の使い方
日次チェック
python scripts/daily_check.py # 全 15 ソースを処理
python scripts/daily_check.py --dry-run # DB 書き込み・通知なし
python scripts/daily_check.py --limit 2 # 先頭 2 件だけ
品質監査
python scripts/run_evaluation.py # 全 grants を再評価
python scripts/run_evaluation.py --url <URL> # 1 件だけ
python scripts/run_evaluation.py --no-notify # Discord 通知なし
新ソースの自動発見
python scripts/discover_grants.py --scope chubu-kinki-prefectures
python scripts/discover_grants.py --cities 名古屋市 京都市
python scripts/discover_grants.py --scope tokyo-23ku --remaining
利用可能スコープ: tokyo-23ku / chubu-prefectures / kinki-prefectures /
chubu-kinki-prefectures / chubu-kinki-ordinance-cities / chubu-kinki-core-cities
⚠️ Anthropic Tier 1 の場合、web_search ヘビーな処理で rate limit に当たります。
一度に 3〜5 件に絞るか、Tier 2 へのアップグレードを検討。
死んだ URL の自動修復
python scripts/run_healer.py # 全 URL をプローブ、404 を修復
python scripts/run_healer.py --url <URL> # 特定 URL のみ
LP 生成
python scripts/generate_lp.py --region "東京23区" --slug tokyo-23ku
# → data/lp/tokyo-23ku.html + tokyo-23ku.json
# ブラウザで開く
open data/lp/tokyo-23ku.html
Streamlit ダッシュボード(4 タブ)
streamlit run dashboard/app.py
# → http://localhost:8501
- 📊 概要: 件数 metric、地域別棒グラフ、フィルタ可能な一覧、詳細パネル
- 🔍 評価: evaluation_logs の最新スコア、要レビュー / 要再抽出リスト、個別 issue
- 🤖 メタエージェント: Discoverer / Healer 実行履歴と判定内訳
- ⚙️ 実行ログ: daily_check の cron 履歴と変化サマリー
データレイヤ
data/grant_radar.sqlite がシステムの唯一の真実。GitHub Actions が毎朝 main にコミットするので、Git の履歴 = データの進化の証拠。
| テーブル | 役割 | レコード数 |
|---|---|---|
grants | 補助金構造化データ正本(url_hash キー) | 15 |
source_hashes | スクレイプ本文ハッシュ(AI 抽出スキップ判定) | 15 |
execution_logs | daily_check 実行履歴 | 日次 +1 |
evaluation_logs | 評価結果履歴 | 日次 +15 |
discovery_runs / discovered_urls | Discoverer 監査 | 手動 |
healing_actions | Healer 修復履歴 | 手動 |
schema_migrations | 適用済みマイグレーション | 5 |
LLM 入出力は data/llm_logs/{ts}_{uuid}.json に全件保存(プロンプト改善・コスト追跡用、gitignore)。
コスト試算(実測ベース)
| シナリオ | API call | コスト |
|---|---|---|
| daily_check 全 15 件 hash 一致 | 15 Haiku(evaluator のみ) | ~$0.03 |
| daily_check 1 件のみ更新 | 1 Sonnet + 15 Haiku | ~$0.05 |
| daily_check 全 15 件再抽出 | 15 Sonnet + 15 Haiku | ~$0.35 |
| daily_discover(曜日別、3〜5 件) | 5×(web_search+Haiku+Sonnet) | ~$0.10/日 |
| Healer 自動発火(404 検知時) | 1 Haiku + 1 fetch | ~$0.01 |
| LP 1 件生成(手動) | 1 Sonnet(max_tokens=8192) | ~$0.05 |
| 月次運用(典型) | daily_check + daily_discover + evaluator | $4〜$6 |
プロンプトのバージョン管理
| Prompt | バージョン | 主な変更点 |
|---|---|---|
| extractor | v1 → v2 → v3 | v2: 通常枠優先 / v3: 補助額 vs 経費額の判別ルール、reasoning の創作禁止、期限の混同防止 |
| evaluator | v1 | rule + AI 二層チェック |
| discoverer | v1 | 検索 prompt + filter prompt 2 段階 |
| healer | v1 | 同一管轄の代替 URL 探索 |
| lp | v1 | 中小企業診断士視点のコピー |
各バージョンは prompts/{agent}_v{N}.txt として履歴保持。grants テーブルの prompt_version カラムでどの世代で抽出されたか追跡可能。
ロードマップ(Phase 1 構想)
| 優先度 | 項目 | 目的 |
|---|---|---|
| 高 | publisher エージェント (L4) | grants 更新 → LP 自動再生成 → GitHub Pages デプロイ |
| 高 | Evaluator v2 (Q1) | extractor v3 ポリシーを理解させ false positive を半減 |
| 中 | extractor v4 | compound 補助金の HP-tier 専用 amount 抽出 |
| 中 | Brave Search API 統合 (S2) | Discoverer の大規模化(Anthropic web_search 制限から解放) |
| 中 | hp_eligible=no の自動 archive (Q4) | LP に誤って混入するのを防止 |
| 中 | テストスイート | tests/ 整備(diff_detector の単体テスト等) |
| 低 | LP の SEO 最適化 | meta タグ・OGP・サイトマップ |
開発
# Lint
ruff check .
# Format
ruff format .
# テスト(Phase 1 で整備予定)
pytest
セキュリティ
- API キーは
.envのみ。コードへのハードコード、ログ出力、他ファイル書き込みは禁止 - LLM 入出力ログには API キーを含めない設計
- Discord Webhook URL も同様に環境変数経由のみ
- スクレイピングは robots.txt を尊重、1 秒/ドメインの rate limit
- 詳細は CLAUDE.md のセキュリティルールセクション参照
設計の自慢ポイント
- 疎結合: 各エージェントは pydantic モデル経由で I/O 交換。1 つコケても他は動く
- 冪等性: SQLite への書き込みは UPSERT のみ、再実行で副作用が雪だるま化しない
- 段階的自動化: 手動 → 半自動 → 自動 の順で構築、リスクを最小化
- 思考の保存: 全 LLM 入出力を
data/llm_logs/に保存、後でプロンプト改善に活用 - メタ自己進化: discoverer(範囲拡張)/ evaluator(品質監査)/ healer(自己修復)が外輪で回る
- Git をデータ層に: SQLite を main にコミット、変化の歴史が
git logで追える
ライセンス
未設定(Phase 1 で検討)
謝辞
- Anthropic Claude API — Sonnet 4.6 / Haiku 4.5 / web_search tool
- Streamlit — ダッシュボード
- Pydantic — 構造化 I/O
- httpx — 非同期 HTTP
- BeautifulSoup — 本文抽出
- Loguru — ロギング