はじめに — 課題と動機
GMOコネクトの永田です。
チームでAIツールを業務に使っていると、実は記事になりそうなナレッジが日々のログに埋まっています。ただ、本人は「ただの作業メモ」だと思っていて、記事ネタだと気づいていないことがほとんどです。
また、数十名のメンバーが月300件以上のタスクログを生成する環境で、埋もれたネタを誰かが代わりに手動で発掘するのも無理があります。
そこで、Gemini APIにログを渡して「これ記事になりませんか?」を自動判定させる仕組みを作りました。4軸で評価して★レーティングを付け、★4以上のネタをメンバーに通知するパイプラインです。
設計のポイントは2つあります。
- Thinking Levelの使い分け(LOW / HIGH)で速度と品質のバランスを取る
- 日次チャンクキャッシュで差分ビルドし、APIコストを削減する
全体アーキテクチャ
パイプラインの全体像は以下のとおりです。
YAML業務ログ(Google Drive)
↓
① 前処理(翻訳・分類・要約など)
↓
② Qiita記事候補の評価
├─ Step 1: 日次チャンク評価(Gemini Flash / Thinking LOW)
│ キャッシュ単位 = ユーザー/日付
│ → 各日のタスクから候補を抽出
│
└─ Step 2: 週次統合(Gemini Flash / Thinking HIGH)
キャッシュ単位 = ユーザー/週
→ 複数日にまたがるテーマを統合・重複排除
↓
③ ★レーティング算出(Python側で決定論的に計算)
↓
④ ダッシュボードに出力(単一HTMLファイル)
フィルター・検索・執筆ガイド付き
入力となる業務ログはYAML形式で、1日1ファイルです。
- tasks:
- task: "Claude CodeでCDKデプロイを自動化"
use_ai_tool: ["Claude Code"]
work_summary: "既存のシェルスクリプトをCDKに移行..."
knowhow: "CDKのbootstrapでリージョンごとに..."
- issues:
- "Gemini APIのレート制限に引っかかった"
4軸評価フレームワークの設計
記事ネタの質を定量的に判断するため、以下の4軸でA/B/Cの3段階評価を行っています。
| 観点 | A(高い) | B(普通) | C(低い) |
|---|---|---|---|
| なるほど感 | 既存記事にない発見・意外な制約 | 実用的だが特に驚きはない | やってみた・公式をなぞっただけ |
| 読者層の広さ | 開発者全般・LLM利用者全般 | 特定技術スタックのユーザー | 特定クラウド×特定ツール限定 |
| 記事化しやすさ | 一般化しても知見が残る | 一部案件固有だが記事化可能 | 案件固有で一般化すると薄い |
| AI活用の工夫 | AIの使い方自体に方法論的発見 | AIを効果的に活用している | なし(技術知見のみ) |
この4軸の評価から、★1〜5のレーティングをPython側で算出します。
どの観点を重視するか、重みづけをどうするかは、「チームとしてどんな記事を増やしたいか」次第で変わります。
2段階評価パイプラインの実装
Step 1: 日次チャンク評価
ユーザー/日付の単位でGemini APIにタスクを評価させます。実装のポイントは以下のとおりです。
モデルとThinking Level: gemini-3-flash-preview + thinking_level="LOW" + response_mime_type="application/json"の組み合わせです。日次評価は「1日分の数タスクから記事候補を抽出する」比較的単純な判断なので、LOWで十分です。LOWにすることで応答速度を保ちつつ、300件以上の日次チャンクを現実的な時間で処理できます。
日次評価プロンプト(実際に使っているものを一般化したもの):
以下はあるメンバーの1日分のAI活用タスク・ナレッジです。
Qiita記事の題材として適した候補があれば抽出・評価してください。
## 評価軸(4軸、各A/B/C)
| 観点 | A(高い) | B(普通) | C(低い) |
|------|----------|----------|----------|
| なるほど感 | 既存記事にない発見・意外な制約の発見 | 実用的だが特に驚きはない | やってみた・公式をなぞっただけ |
| 読者層の広さ | 開発者全般・LLM利用者全般 | 特定技術スタックのユーザー | 特定クラウド×特定ツール限定 |
| 記事化しやすさ | 一般化しても知見が残る | 一部案件固有だが記事化可能 | 案件固有で一般化すると薄い |
| AI活用の工夫 | AIの使い方自体に方法論的発見 | AIを効果的に活用している | なし(技術知見のみ) |
## 高評価の条件
- 読者の「そういう使い方があるのか」「この制約は知らなかった」というAha!体験
- 再現性: 読者が試せる具体的手順がある
- ストーリー性: 問題発見→調査→試行錯誤→解決
- 定量データ: Before/After等の具体的数値
## 低評価の条件
- 公式ドキュメントをなぞっただけで独自知見がない
- 当たり前の結論(「大きいモデルの方が正確」等)
- 案件固有で機密除去すると核心が消える
## タスク一覧(メンバー: {user}、日付: {date})
{task_list}
## 出力形式
候補をJSON配列で返してください。候補がない場合は空配列 [] を返してください。
なるほど感・読者層・記事化しやすさの平均がB以上の候補を全て出力してください。
[
{
"proposed_title": "記事タイトル案",
"content_summary": "記事の概要(2-3文)",
"source_tasks": [{"date": "2026-03-18", "task": "タスク名"}],
"axes": {
"naruhodo": "A",
"audience_breadth": "A",
"writability": "B",
"ai_innovation": "A"
},
"merit": "この題材が記事として優れている理由",
"demerit": "記事化する上での懸念・課題",
"article_summary_draft": "記事概要を1段落で"
}
]
プロンプト設計のポイント:
- 4軸の評価基準表をそのままプロンプトに埋め込み、LLMに採点基準を明示する
- 「高評価の条件」「低評価の条件」を具体例で示し、評価のブレを抑える
- 出力はJSON配列で、「平均がB以上の候補のみ」という仕切り条件をプロンプト側で指定する
-
axesにA/B/Cを返させ、★レーティングの算出はPython側で行う(LLMに★を直接出させない)
Step 2: 週次統合
日次で抽出された候補を、1週間単位でグループ化し、重複テーマを統合します。
モデルとThinking Level: 同じ gemini-3-flash-preview ですが、ここでは thinking_level="HIGH" にしています。「このテーマとあのテーマは実は同じ話題か?」という類似性判断には、より深い推論が必要です。タイトルが異なっていても内容が重複しているケースや、一見似ていても異なるテーマのケースを正しく判別しなければなりません。
呼び出し回数の違い: 週次統合の呼び出し回数はユーザー×週数程度で、日次に比べて大幅に少ないため、HIGHにしてもパイプライン全体のコスト・時間への影響は限定的です。
週次統合プロンプト:
以下は1週間分のQiita記事候補(日次で個別評価済み)です。
同一テーマ・類似テーマの候補を統合し、重複を排除してください。
## ルール
- 同一テーマが複数日に分散している候補はsource_tasksをマージして1つに統合
- 統合時はより良い評価を維持し、内容を充実させる
- テーマが異なる候補はそのまま維持
- 統合後の候補が0件でも空配列を返す
## 候補一覧({user}、{week_label})
{candidates_json}
## 出力形式
統合後の候補をJSON配列で返してください(日次評価と同じスキーマ)。
実装上のポイント: 候補が1件のみの週はAPI呼び出しをスキップします(統合不要なため)。
なぜ2段階にするのか
「最初からまとめて全タスクを評価すればいいのでは?」と思うかもしれません。2段階にした理由はキャッシュの粒度です。
- 日次チャンク(=キャッシュ単位): 新しい日のログが追加されたときに、その日のチャンクだけを評価すればよい
- 週次統合: 日次候補に変更があった週だけを再統合すればよい
つまり、日次データの更新頻度に合わせた差分ビルドが可能になります。
キャッシュ戦略 — 差分ビルドでAPIコストを削減
2層キャッシュ構造
キャッシュは以下のJSON構造で管理しています。
{
"daily": {
"user-A": {
"2026-03-18": {
"content_hash": "a1b2c3d4e5f67890",
"candidates": [...]
}
}
},
"weekly": {
"user-A": {
"2026-W12": {
"candidates_hash": "f0e1d2c3b4a59687",
"merged": [...]
}
}
}
}
コンテンツハッシュによる変更検出
キャッシュの判定にはYAMLファイルの内容のSHA256ハッシュ(先頭16文字)を使っています。ハッシュが一致すればAPI呼び出しをスキップし、不一致なら再評価します。
週次統合のキャッシュも同じ考え方で、日次候補のJSON全体のハッシュを使います。日次候補に変更がなければ統合結果もそのまま再利用されます。つまり、日次キャッシュのヒット/ミスが週次キャッシュの有効性を連鎖的に決める構造です。
初回ビルド vs 差分ビルドの効果
| シナリオ | 日次API呼び出し | 週次API呼び出し |
|---|---|---|
| 初回ビルド(1週間・数十名) | 〜60回 | 〜15回 |
| 差分ビルド(1日分追加) | 〜5回(新しい日のみ) | 〜5回(変更のあった週のみ) |
| 差分ビルド(変更なし) | 0回 | 0回 |
実運用では日次でログが追加される想定なので、毎回のビルドでは全体の5〜10%程度のAPI呼び出しで済みます。
中間セーブによる障害耐性
日次評価は件数が多いため、50件ごとにキャッシュをディスクに中間保存しています。API呼び出し中にプロセスが中断しても、次回実行時にキャッシュ済みの分はスキップされるため、途中から再開できます。
トークン使用量の可視化と料金推定
各API呼び出しの usage_metadata から入力/出力トークン数を集計し、ビルド完了後にPaid Tier換算のコスト推定を出力する仕組みを入れています。
ここで重要なのが Thinkingトークンの扱い です。usage_metadata には通常の出力トークン(candidates_token_count)とは別に thoughts_token_count が含まれており、Thinkingトークンも出力トークンとして課金されます。つまり、Thinking Level HIGHにすると出力トークンが増えるので、コスト面でもLOW/HIGHの使い分けは重要です。
運用して分かったこと
パイプラインの出力は社内ダッシュボードに★フィルターや検索付きで表示し、メンバーが自分の記事ネタに気づけるようにしています。ここでは、実際に運用して得られた知見を共有します。
評価分布の実績
1週間・数十名のデータで実行した結果は以下のとおりでした。
| ★レーティング | 件数 |
|---|---|
| ★★★★★ | 3件 |
| ★★★★☆ | 5件 |
| ★★★☆☆ | 12件 |
| ★★☆☆☆ | 1件 |
| 合計 | 21件 |
閾値の調整
★3と★4のどちらを閾値にするか迷いましたが、結局 ★4以上 を「記事候補あり」の閾値にしました。
- ★3閾値の場合: 1週間で20件。候補が多すぎて「おすすめ」の意味が薄まる
- ★4閾値の場合: 1週間で8件。メンバーに通知するには十分絞り込まれている
ダッシュボード上では★3以上も一覧で見られるようにしつつ、メンバーへの通知や「おすすめ」ハイライトは★4以上に絞っています。
Rate Limit対策
Gemini APIのレート制限に引っかからないよう、日次評価は5件ごとに1秒、週次統合は毎回1秒のsleepを挟んでいます。Paid Tier(Tier 3)でもバースト的に呼び出すとレート制限に到達するため、この程度のスロットリングは入れておいたほうが安全です。
まとめ
「記事ネタに気づいていない」という課題に対して、LLMに判定を任せるアプローチを取りました。
- LLMにはA/B/C評価だけを任せ、★レーティングはコード側で算出する — LLMの出力を決定論的なスコアに変換することで、再現性を確保できる
- 日次チャンク→週次統合の2段階にする — キャッシュの粒度が日次になり、差分ビルドでAPIコストを大幅に削減できる
- Thinking LevelはステップごとにLOW/HIGHを切り替える — 「横断的な比較が必要か?」で判断すると、速度とコストのバランスが取りやすい
「業務ログの自動評価」という用途で作りましたが、入力を渡してJSON構造の評価を返させるパイプラインのパターン自体は、コードレビュー・コンテンツ分類・品質スコアリングなどにも使い回せると思います。
最後に、GMOコネクトではサービス開発支援や技術支援をはじめ、幅広い支援を行っておりますので、何かありましたらお気軽にお問合せください。