Noise to Blog
ざっくりした入力(箇条書き、音声文字起こし、ツイートのダンプ、短い下書き)を、洗練された公開可能なブログ記事に変換します。すべての主張は、元の素材またはTavilyで検証されたリサーチに基づいています。
重要なルール: 具体的な内容を創作しないでください。ブログ記事内のすべての主張、数値、事例は、生の入力またはTavilyの検索結果から取得したものでなければなりません。データ、引用、結果を決して捏造しないでください。
ステップ 1: セットアップ確認
必要な環境変数が設定されていることを確認します:
echo "GEMINI_API_KEY: ${GEMINI_API_KEY:+set}"
echo "TAVILY_API_KEY: ${TAVILY_API_KEY:-not set, Tavily enrichment will be skipped}"
GEMINI_API_KEYがない場合: 停止します。ユーザーに次のように伝えます: 「GEMINI_API_KEYが必要です。aistudio.google.com → APIキーを取得 から入手し、.envファイルに追加してください。」
TAVILY_API_KEYがない場合: 続行します。Tavilyによる補強はスキップされることに注意してください。ブログ記事は提供されたコンテンツのみに基づいて作成されます。これは、個人的なストーリー、経験に基づくチュートリアル、意見記事には問題ありません。
入力が存在することを確認します。 ユーザーは次のいずれかを提供する必要があります:
- 貼り付けられたテキスト(箇条書き、ラフなメモ、文字起こし、ツイートダンプ、短い下書き)
- 取得するURL
入力がない場合は、次のように尋ねます: 「ラフなメモ、箇条書き、文字起こしを共有してください。直接貼り付けるか、ソースを取得するためのURLを教えてください。」
ステップ 2: 入力の読み取りと分析
入力がURLの場合: WebFetchを使用してページコンテンツを取得します。タイトル、著者、公開日、すべての本文、主要な統計、番号付きリスト、小見出し、引用を抽出します。
入力が貼り付けられたテキストの場合: 直接読み取ります。入力タイプを特定します:
- 箇条書きまたはラフなメモ: 断片的なアイデア、不完全な文、意識の流れ
- 音声文字起こし: 会話調、繰り返し、フィラーワード(えーと、あの、みたいな、わかるでしょう)、だらだらした文
- ツイートスレッドダンプ: 短い断片、@メンション、ハッシュタグ、「1/8」のような番号付け
- 短い下書き: 構造化されているが薄い、拡張と洗練が必要
QAチェックポイント: 続行する前に次の内容を述べてください:
- 検出された入力タイプ
- 1文で表した核となるテーマまたは主な主張
- 生のコンテンツから得られた最も強力な洞察、事実、アイデア(3〜5つ)
- 外部検証が必要な主張(ベンチマーク、統計、製品比較、調査結果)
核となるテーマを特定できない場合は、次のように尋ねます: 「読者に最も伝えたいことは何ですか?」
ステップ 3: ブログ記事スタイルの選択
4つのスタイル。コンテンツのシグナルから自動検出します。ユーザーによる上書きは常に尊重されます。
| スタイル | 使用する場面 | シグナル |
|---|---|---|
| 技術チュートリアル | ステップバイステップガイド、ハウツー、コードウォークスルー | 番号付きステップ、コマンド、コードスニペット、コンテンツ内の「方法」 |
| 事例研究 | ビフォー/アフターストーリー、ビルドログ、学んだ教訓 | 具体的な結果、タイムライン、一人称の体験談 |
| 意見リーダーシップ | 意見、議論、直感に反する主張 | 「私はこう思う」、「Xの問題点は」、逆張りの立場、議論の枠組み |
| 解説記事 | Xとは何か、なぜ重要か、どのように機能するか | 概念ファースト、比較が多い、「ほとんどの人は知らない」 |
検出ロジック:
- コンテンツに番号付きステップまたはコマンドがある → 技術チュートリアル
- コンテンツにビフォー/アフター、特定の指標、または物語のアークがある → 事例研究
- コンテンツが一般的な考えに反論する、または強い意見の主張をする → 意見リーダーシップ
- コンテンツが馴染みのない人向けに概念、ツール、トレンドを説明する → 解説記事
選択したスタイルとその理由を述べてください。曖昧な場合は、1つを選択してその選択をメモします。
ステップ 4: Tavilyリサーチによる補強
TAVILY_API_KEYが設定されていない場合は、このステップを静かにスキップします。
検証やデータの恩恵を受ける可能性のある、生のコンテンツ内の主張に対する裏付けとなる証拠を検索します。適切な候補:
- 製品のベンチマークまたはパフォーマンス数値
- 市場統計または業界トレンド
- 技術的な比較(「XはYより速い」)
- ユーザーが引用元ではなく記憶から述べた数値
検証が必要な主張ごとに1回のTavily検索を実行します。ソース過多を避けるため、最大3回の検索に制限します:
curl -s -X POST "https://api.tavily.com/search" \
-H "Content-Type: application/json" \
-d '{
"api_key": "'"$TAVILY_API_KEY"'",
"query": "検証する具体的な主張またはトピック",
"search_depth": "advanced",
"max_results": 5,
"include_answer": true
}'
score >= 0.65の結果のみを保持します。タイトル、URL、コンテンツスニペットを抽出します。
Tavily結果を使用するためのルール:
- 生の入力にすでに存在する主張を裏付けたり検証したりするために使用します。検索結果からまったく新しい主張を導入しないでください。
- テキスト内で自然にソースを属性付けします:「[ソース]によると」、「[X]のデータによると」のように。
- Tavilyの結果が主張を確認できない場合は、無関係な結果を代わりに使用せず、主張を未検証のままにしておきます。
ステップ 5: ブログ記事の生成
references/blog-format.mdをすべて読みます。references/output-template.mdから一致するテンプレートを選択します。生成前にすべてのルールを内部化します。
特殊文字を安全に処理するために、Geminiリクエストを一時ファイルに書き込みます:
cat > /tmp/noise2blog-request.json << 'ENDJSON'
{
"system_instruction": {
"parts": [{
"text": "あなたは実際の人間のように文章を書くテックライターです。ルール: 能動態のみ使用。短い段落、最大1〜3行、その後空白行。自然に短縮形を使用(です、ません、だろう、できません、あなたは、彼らは)。ダッシュ(—)は使用せず、代わりにカンマまたはピリオドを使用。セミコロンは使用しない。すべての文には具体的な詳細(数字、ツール名、ファイル名、コマンド、結果)を含める。フィラーフレーズは使用しない:「今日の急速に進化する〜では」、「詳しく見ていきましょう」、「注目に値します」、「結論として」、「これが役に立てば幸いです」のようなもの。禁止ワードは使用しない:信じられない、素晴らしい、活用する、相乗効果、ゲームチェンジャー、画期的、革新的、パラダイム、最先端、シームレス、堅牢、前例のない、掘り下げる、活用する、利用する、変革的、破壊的、アンロックする、包括的な、実用的な、重要な、極めて重要な。タイトルは「私」、「私の」、「私たち」で始めてはならない。トピックを告知しないフック段落で始める。何か実践できることで締めくくる。ソース素材にない主張、指標、結果を創作しないこと。"
}]
},
"contents": [{
"parts": [{
"text": "RAW_CONTENT_AND_INSTRUCTIONS_HERE"
}]
}],
"generationConfig": {
"temperature": 0.7,
"maxOutputTokens": 4096
}
}
ENDJSON
RAW_CONTENT_AND_INSTRUCTIONS_HEREを次のもので置き換えます:
- 生の入力コンテンツ
- ブログ記事のスタイルと構造の指示(選択したテンプレートから)
- ステップ4で収集されたTavilyリサーチ結果
- 単語数目標: 800〜1,800語
リクエストを送信します:
curl -s -X POST \
"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=$GEMINI_API_KEY" \
-H "Content-Type: application/json" \
-d @/tmp/noise2blog-request.json \
| python3 -c "import sys,json; d=json.load(sys.stdin); print(d['candidates'][0]['content']['parts'][0]['text'])"
さらに次のものも生成します:
- (B) SEOプレビューテキスト用の1〜2文のメタディスクリプション
- (C) 別のフックアングルを使用した代替タイトル1つ
ステップ 6: セルフQA
すべてのチェックを実行し、違反を修正してから提示します:
- タイトルが「私」、「私の」、「私たち」で始まっていない
- タイトルが具体的で会話調である(「Xへの包括的なガイド」や「Yの究極ガイド」ではない)
- 冒頭段落がトピックを告知せずにフックしている(「この投稿では〜します」のようなものではない)
- どの行にもダッシュ(—)がない
- セミコロンがない
- 空白行の前に3行を超える段落がない
- 禁止ワードがない: 信じられない、素晴らしい、活用する、ゲームチェンジャー、掘り下げる、活用する、アンロックする、画期的、最先端、顕著な、パラダイム、革命を起こす、シームレス、堅牢、活用する、前例のない、包括的な、実用的な、重要な、極めて重要な
- 捏造されたデータがない: すべての主張は生の入力またはTavilyの結果に基づいている
- 投稿が「結論として」、「要約すると」、「これが役に立てば幸いです」で終わっていない
- 800〜1,800語である(単語数を明記する)
- 論理的な流れ: 導入 → 問題/コンテキスト → 本文セクション → 実践的な締めくくり
提示前に違反を修正します。最終的な単語数を記載します。
ステップ 7: 出力
完全なブログ記事をコードブロックで提示します。
メインの記事の下に、メタディスクリプションと代替タイトルを提示します。
次のように尋ねます: 「これをエディタにコピーしてもよろしいですか?特定のプラットフォームに公開する場合は、Frontmatterをフォーマットできますのでお知らせください。」
プラットフォーム固有のリクエストに応じて:
Ghost:
---
title: "記事のタイトル"
date: YYYY-MM-DD
tags: [タグ1, タグ2]
status: draft
---
dev.to:
---
title: 記事のタイトル
description: メタディスクリプション
tags: [タグ1, タグ2, タグ3]
published: false
---
Substack: プレーンなMarkdownとして提示します。Substackのエディタはマークダウンを直接インポートします。
Hashnode:
---
title: 記事のタイトル
subtitle: メタディスクリプション
tags: [タグ1, タグ2]
---