・ AI / 自動化 / GitHub Actions

こんにちは!H×H のセンリツ大好きエンジニアです!✌️
最近、AI 関連のニュースが毎日たくさん出てきて追いかけるのが大変なため、自動で収集・整理してくれるシステムを作成しました!💡
この記事はおよそ 4000 字なので、8 分で読めます!
※この記事は 7 割程度が AI によって執筆されています。
目次 📚
システム全体の概要 🎯
AI News 自動配信システムは、以下の特徴を持つ自動化されたニュース収集・配信プラットフォームです。
主な機能
- 自動収集: 毎日 7 時(JST)に最新の AI ニュースを自動収集 🕖
- 複数ソース対応: 海外主要メディア(TechCrunch、The Verge、VentureBeat など)から横断検索 🌍
- キーワードベース検索: AI 関連企業やサービス名での絞り込み検索 🔍
- ランキング形式: 政治的・経済的・技術的影響度でソート 📊
- 日本語対応: 自動翻訳による日本語タイトル・要約の生成 🇯🇵
バックエンド技術スタック
Backend & Infrastructure:
- Dify Workflow API (ニュース収集・処理)
- SearchAPI.io (Bing News Search API)
- GitHub Actions (定期実行)
- Supabase (PostgreSQL)
システムアーキテクチャ
┌────────────────────────────────────────────────┐
│ GitHub Actions (Scheduler) │
│ 毎日 7:00 JST に自動実行 │
└─────────────────┬──────────────────────────────┘
│
▼
┌────────────────────────────────────────────────┐
│ Dify Workflow API │
│ ┌──────────────────────────────────────┐ │
│ │ 1. キーワード/ドメイン検索 │ │
│ │ 2. SearchAPI.io で並列検索 │ │
│ │ 3. 結果を YAML 形式に整形 │ │
│ │ 4. LLM でランキング & 翻訳 │ │
│ └──────────────────────────────────────┘ │
└─────────────────┬──────────────────────────────┘
│
▼
┌────────────────────────────────────────────────┐
│ Supabase (PostgreSQL) │
│ ai_news テーブルに保存 │
└────────────────────────────────────────────────┘
Dify ワークフローの詳細設計 🔄
ここからが本記事のメインコンテンツです!Dify のワークフローを詳しく見ていきます。
ワークフロー全体図
┌─────────┐
│ Start │
└────┬────┘
│
┌─────────────┴─────────────┐
│ │
▼ ▼
┌─────────┐ ┌──────────┐
│Template │ │Template │
│Keywords │ │Domains │
└────┬────┘ └────┬─────┘
│ │
▼ ▼
┌─────────┐ ┌──────────┐
│ Code │ │ Code │
│ Parse │ │ Parse │
└────┬────┘ └────┬─────┘
│ │
▼ ▼
┌──────────┐ ┌───────────┐
│Iteration │ │Iteration │
│Keywords │ │Domains │
│(並列10) │ │(並列10) │
└────┬─────┘ └─────┬─────┘
│ │
└─────────┬───────────────┘
│
▼
┌───────────────┐
│HTTP Requests │
│SearchAPI.io │
└───────┬───────┘
│
▼
┌───────────────┐
│ Template │
│ Merge │
└───────┬───────┘
│
▼
┌───────────────┐
│ Code │
│ Parse YAML │
└───────┬───────┘
│
▼
┌───────────────┐
│ LLM │
│ (Grok 4) │
└───────┬───────┘
│
▼
┌───────────────┐
│ Answer │
└───────────────┘
ノード詳細解説
1. Template - キーワードとドメインの定義
キーワードテンプレート
AI 関連のサービスや企業名を定義します。
CUTIE STREET
KAWAII LAB
きゅーすと
キュースト
ドメインテンプレート
信頼性の高い海外メディアのドメインを列挙します。
techcrunch.com
theverge.com
venturebeat.com
arstechnica.com
technologyreview.com
2. Code - 配列変換処理
Python コード - 配列化とsite:プレフィックス付与
def main(arg1: str) -> dict:
# 行ごとに分割して配列化
words = [line.strip() for line in arg1.splitlines() if line.strip()]
return {"result": words}
ドメイン用には site: プレフィックスを付与:
def main(arg1: str) -> dict:
domains = [line.strip() for line in arg1.strip().splitlines() if line.strip()]
queries = [f"site:{d}" for d in domains]
return {"result": queries}
3. Iteration - 並列検索処理
- 並列数: 10
- エラーハンドリング: continue-on-error
各キーワード・ドメインについて並列で SearchAPI.io に HTTP リクエストを実行します。
4. HTTP Request - SearchAPI.io への検索
各キーワード・ドメインについて、24 時間以内のニュースを最大 10 件取得します。
https://www.searchapi.io/api/v1/search
?engine=bing_news
&q={{keyword}}
&last_24_hours
&num=10
&api_key={{api_key}}
5. Code - YAML 化パーサー
Python コード - JSON から YAML への変換
import re
def main(arg1):
# 記事抽出パターン
pat = re.compile(
r'"title"\s*:\s*"(?P<title>(?:\\.|[^"\\])*)".*?'
r'"link"\s*:\s*"(?P<link>(?:\\.|[^"\\])*)".*?'
r'"source"\s*:\s*"(?P<source>(?:\\.|[^"\\])*)".*?'
r'"date"\s*:\s*"(?P<date>(?:\\.|[^"\\])*)"',
re.S
)
seen, items = set(), []
for m in pat.finditer(str(arg1)):
# 24時間以内フィルター
date = unesc(m.group('date'))
if not is_within_24_hours(date):
continue
url = unesc(m.group('link'))
if url not in seen:
seen.add(url)
items.append({
"title": unesc(m.group('title')),
"url": url,
"source": unesc(m.group('source')),
})
# YAML風のテキストに整形
lines = []
for i, it in enumerate(items, 1):
lines.append(f"{i}")
lines.append(f"url: {it['url']}")
lines.append(f"title: {it['title']}")
lines.append(f"source: {it['source']}")
lines.append("")
return {"result": "\n".join(lines)}
このパーサーは以下の処理を行います:
- JSON パース: SearchAPI.io のレスポンスから記事情報を抽出
- 時間フィルター: 24 時間以内の記事のみ許可
- 重複除去: URL ベースで重複を排除
- YAML 変換: LLM が理解しやすい YAML 形式に変換
YAML 出力例
1
url: https://techcrunch.com/2025/11/05/openai-gpt-5
title: OpenAI announces GPT-5
source: TechCrunch
2
url: https://theverge.com/2025/11/05/google-gemini-update
title: Google Gemini gets major update
source: The Verge
6. LLM (Grok 4) - AI ニュース選定と翻訳
システムプロンプト
Please provide the top 10 AI news articles for today that have significant
political, economic, or technical impact. Remove any duplicates and
translate all content to Japanese.
Return your response in the following JSON format only:
{
"articles": [
{
"title": "Article title in Japanese",
"url": "https://example.com/article-url",
"summary": "Article summary in Japanese (1-2 sentences)"
}
]
}
Important Instructions:
- Return ONLY the JSON format (no additional explanation text)
- The "articles" array must contain exactly 10 articles
- Each article object must include: title, url, and summary
- All titles and summaries must be in Japanese
- Remove duplicate articles before outputting
LLM (Grok 4) が以下の処理を実行します:
- 重複除去: 類似記事を統合
- 影響度評価: 政治的・経済的・技術的影響を評価
- ランキング: 影響度が高い順に上位 10 件を選定
- 日本語翻訳: タイトルと要約を日本語化
- JSON 生成: 構造化データとして出力
データフロー
[キーワード/ドメイン定義]
↓
[配列化処理]
↓
[並列検索 (10並列)]
↓
[SearchAPI.io 検索結果 (100+件)]
↓
[YAML 形式に整形]
↓
[LLM: Top 10 選定 + 翻訳]
↓
[JSON: 10記事 (日本語)]
GitHub Actions による自動実行 ⚙️
毎日 7 時(JST)に AI ニュースを自動収集・保存する仕組みです。
ワークフロー構成
name: Fetch AI News Daily
on:
schedule:
- cron: "0 22 * * *" # 毎日22時(UTC) = 7時(JST)
workflow_dispatch: # 手動実行も可能
jobs:
fetch-and-store-ai-news:
runs-on: ubuntu-latest
steps:
- name: Fetch AI News from Dify API
- name: Parse and Store to Supabase
- name: Cleanup
Step 1: Dify API からニュース取得
curl による Dify API 呼び出し(リトライ機能付き)
# リトライ設定
MAX_RETRIES=3
RETRY_DELAY=10
for i in $(seq 1 $MAX_RETRIES); do
RESPONSE=$(curl -s --max-time 300 -w "\n%{http_code}" \
-X POST "https://api.dify.ai/v1/chat-messages" \
-H "Authorization: Bearer $DIFY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"inputs": {},
"query": "今日のAIニュースを5〜10件教えてください。",
"response_mode": "blocking",
"user": "github-actions-kuboyage"
}')
HTTP_CODE=$(echo "$RESPONSE" | tail -n 1)
if [[ "$HTTP_CODE" =~ ^2[0-9][0-9]$ ]]; then
echo "✅ Success"
exit 0
fi
sleep $RETRY_DELAY
done
リトライポリシー
- 最大リトライ回数: 3 回
- リトライ間隔: 10 秒
- タイムアウト: 300 秒(5 分)
Step 2: パースと Supabase への保存
Node.js スクリプトによるパースと保存
const { createClient } = require('@supabase/supabase-js');
const fs = require('fs');
const supabase = createClient(
process.env.SUPABASE_URL,
process.env.SUPABASE_SERVICE_KEY
);
async function parseAndStore() {
// レスポンス読み込み
const rawResponse = fs.readFileSync('ai_news_response.json', 'utf8');
const responseData = JSON.parse(rawResponse);
// Dify レスポンスから記事データ抽出
let newsContent = responseData.data?.outputs
? Object.values(responseData.data.outputs)[0]
: responseData.answer;
// JSON パース
const parsed = JSON.parse(newsContent.match(/```json\s*([\s\S]*?)\s*```/)?.[1] || newsContent);
// 記事保存
for (const article of parsed.articles) {
await supabase.from('ai_news').insert({
title: article.title,
url: article.url,
summary: article.summary,
source: 'Dify MCP',
published_at: new Date().toISOString()
});
}
}
parseAndStore();
処理フロー
- レスポンス読み込み: Dify API レスポンスをファイルから読み込み
- JSON パース: 記事データを抽出
- 時刻変換: JST 時刻に変換
- 既存データ削除: 今日分のニュースを削除(重複防止)
- 新規データ挿入: 各記事を Supabase に保存
パーサーの実装
AI が生成する多様な出力形式に対応するための柔軟なパーサーを実装しています。
対応パターン
- JSON Format (推奨)
- Bold Markdown:
**URL:** https://... - Plain Markdown:
URL: https://... - Flexible Format:
url: https://... - Markdown Links:
[タイトル](https://...) - Title First: タイトル改行URL
- One Line Format:
https://... - タイトル
複数のパーサーを順に試行し、最もマッチするものを自動選択します。
エラーハンドリング
1. リトライ機構(GitHub Actions)
MAX_RETRIES=3
for i in $(seq 1 $MAX_RETRIES); do
# API 呼び出し
if [ 成功 ]; then
exit 0
fi
sleep 10
done
2. フォールバック機構(パーサー)
const parsers = [parseJSON, parseBoldMarkdown, ...];
for (const parser of parsers) {
const results = parser(content);
if (results.length > 0) {
return results; // 最初に成功したパーサーを使用
}
}
ログ出力例
🔄 Attempt 1 of 3...
✅ AI News fetched successfully (HTTP 200)
✅ Parsed 10 articles using JSON Format
🎉 Successfully stored 10 AI news articles!
まとめと今後の展望 🚀
実装のポイント
この AI News 自動配信システムでは、以下のポイントを重視して実装しました。✨
- 柔軟なパーサー設計: LLM の出力が変動しても対応できる 7 種類のパーサー
- 堅牢なエラーハンドリング: 3 回のリトライ + タイムアウト処理
- 並列処理: Dify イテレーションによる高速な検索実行(10並列)
- スケーラビリティ: GitHub Actions による低コスト運用
システムの強み 💪
- 低コスト: サーバーレスアーキテクチャによる運用コスト削減
- 高信頼性: リトライ機構とフォールバックによる高可用性
- 拡張性: ドメインやキーワードの追加が容易
- 保守性: Dify ワークフローによる視覚的な処理管理
今後の改善案 🔮
- 画像取得機能: OGP 画像を取得してカード表示に追加
- カテゴリー分類: LLM でカテゴリー(技術・ビジネス・政治など)を分類
- 通知機能: メール・Slack/Discord 連携、RSS フィード生成
- マルチ言語対応: 英語版と日本語版を並行配信
学びと所感 📝
この実装を通じて、以下のことを学びました。
- Dify の柔軟性: ワークフローでの複雑な処理の組み立てが直感的
- LLM の不確実性: 出力形式が変動するため、柔軟なパーサーが必須
- タイムゾーンの難しさ: JST ↔ UTC の変換は注意深く実装する必要がある
- GitHub Actions の便利さ: 定期実行とシークレット管理が簡単
特に、Dify のイテレーション機能を使った並列処理は、検索速度が大幅に向上して感動しました!🎉