1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

LLMブログ自動化で気づいた「パイプライン分断の罠」— ワンショットに15ステップが惨敗した話

1
Posted at

はじめに

個人ブログの記事を自動生成するシステムをPython + Gemini APIで運用しています。
LINE Webhookからトリガーし、コンセプト生成 → 本文生成 → ファクトチェック → レビュー → 校正 → アフィリエイト付与 → はてなブログ投稿、という15ステップ以上のパイプラインを組みました。

エンジニアリング的には「完璧な分業」のはずでした。

ところが、ワンショット(1回のプロンプト)で生成した記事のほうが圧倒的に品質が高いことに気づきました。

この記事では、なぜそうなるのかを構造的に分析し、得られた教訓をまとめます。

品質の差を見てみる

ワンショットで生成した記事(85/100点)

まいどです。
最近、ほんまに時間が経つのが早うて困りますね。
「あかん、もうこんな時間や…」って、時計見てため息つくこと、ありませんか?

実は私も、ちょっと油断するとすぐネットサーフィンしてしもたり、
ピーナッツ食べ過ぎて後悔したり(笑)、なかなか集中できひんことが多かったんです。

...(中略)...

▼過去の関連記事はこちら
昔の自分に教えてやりたいですわ…。
[ライフハッキング - 楽天アフェリエイト 今年のブログの...](https://lifehacking1919...)
[ライフハッキング - ピーナッツを食いすぎて・・・](https://lifehacking1919...)

パイプラインで生成した記事(60/100点)

<h2>効率化の手法</h2>
<p>時間管理において重要な手法として、ポモドーロ・テクニックが知られています。</p>
<p>この手法は25分の集中と5分の休憩を繰り返すものです。</p>

...(中略)...

## 🔗 あわせて読みたい
<ul>
  <li><a href='...'>ピーナッツを食いすぎて・・・</a></li>
  <li><a href='...'>楽天アフェリエイト 今年の...</a></li>
</ul>

差は歴然です。

ワンショット版は「関西弁のおっちゃんが友人に語りかけている」感じ。過去記事も本文の流れの中で自然に引用されています。

パイプライン版は「教科書を読んでいる」感じ。過去記事は末尾に機械的にリストアップされ、記事全体から浮いています。

なぜこうなるのか?

ワンショットの情報フロー

LLMは全情報を同時に見ているから、「ピーナッツの話をしたあのエントリ、ちょうど怠けグセの文脈で使えるな」 と判断できる。

パイプラインの情報フロー

Step 2(本文生成)の時点で、LLMは過去記事の存在すら知らない。

だから本文中に自然に織り込めない。Step 6で末尾に「あわせて読みたい」として追加するしかない。

具体的な「分断」のコード

問題1: 過去記事が本文生成に渡されていない

# ArticleContentGeneratorTask._generate_article_content()

prompt = (
    f"あなたは『{blog_name}』のライターです。\n"
    f"【コンセプト】\n{article_concept}\n\n"
    f"【参考情報】\n{source_content[:2500]}\n\n"  # ← 2500文字で切断!
    # ← similar_articles がここにない!
)

一方、FinalArticleEnricher ではこう使われている:

# FinalArticleEnricherTask._get_past_articles_html()

def _get_past_articles_html(self, articles):
    html = "<ul>\n"
    for art in articles[:3]:
        html += f"  <li><a href='{art.get('url')}'>{art.get('title')}</a></li>\n"
    html += "</ul>"
    return html  # ← 末尾にリスト追加するだけ

問題2: ペルソナが過度に抑制されている

persona_instructions = (
    f"【ブロガーのペルソナ(参考)】\n{persona_text}\n\n"
    "上記のペルソナの「口調」と「視点」だけを参考にしてください。\n"  # ← 「参考」止まり
    "- ペルソナの感情や内面の語りを加える必要はない。\n"  # ← 個性を殺す指示
    "- 哲学的な自己省察、人生訓、過去の自分への語りかけは禁止\n"
)

禁止事項が多すぎて、LLMは安全策を取って無個性な文体を選択する。

問題3: ソースコンテンツの切断

f"【参考情報】\n{source_content[:2500]}\n\n"

ワンショット版では、過去記事10本分のフルテキスト(数千〜数万トークン)がLLMに渡されている。
パイプライン版は2500文字で打ち切り。文脈の豊かさが段違い。

修正してみた

Fix 1: 過去記事をコンテンツ生成プロンプトに注入

# similar_articles をプロンプトに追加

past_articles_text = ""
if similar_articles:
    for art in similar_articles[:5]:
        past_articles_text += f"- [{art['title']}]({art['url']})\n"

prompt += (
    "【過去記事(本文中に自然に織り込むこと)】\n"
    f"{past_articles_text}\n"
    "上記の記事タイトルとURLを、本文の適切な文脈で自然に引用してください。\n"
    "「あわせて読みたい」のような末尾リストではなく、文の流れの中に組み込んでください。\n\n"
)

Fix 2: ペルソナの自由度を上げる

- "上記のペルソナの「口調」と「視点」だけを参考にしてください。\n"
- "- ペルソナの感情や内面の語りを加える必要はない。\n"
+ "上記のペルソナになりきって記事を書いてください。\n"
+ "- テーマに関係する体験・感想は積極的に語ること。\n"
+ "- ただしテーマと無関係な哲学的内省や人生訓は避けること。\n"

Fix 3: ソースコンテンツの上限を緩和

- f"【参考情報】\n{source_content[:2500]}\n\n"
+ f"【参考情報】\n{source_content[:5000]}\n\n"

得られた教訓

1. 「美しい分業」はLLMには逆効果なことがある

ソフトウェアエンジニアリングの常識:単一責任の原則(SRP) — 各モジュールは1つのことだけをやる。

LLMの現実:文脈こそが品質。情報を分断すると、各ステップが「見えないものは使えない」。

2. パイプラインが有効なケースもある

全部ワンショットにすべきかというと、そうではない。

タスク ワンショット向き パイプライン向き
クリエイティブ生成 ✅ 文脈が品質に直結
構造化チェック ✅ ルールベースで正確
外部API呼び出し ✅ LLMの幻覚を避ける
品質ゲート ✅ 機械的判定が確実

ファクトチェック(外部検索)やアフィリエイト商品取得(Rakuten API)は、パイプラインのほうが正確です。LLMはURLを幻覚するから。

3. 理想形は「太いコンテキストの1本パス + 後段の機械的補強」

前段はLLMの強み(文脈理解・創造性)を最大化し、後段は機械の強み(正確性・ルール準拠)で補強する。 この2層構造が現時点での最適解だと考えています。

まとめ

ワンショット 15ステップ パイプライン
品質スコア 85/100 60/100
過去記事の扱い 本文中に自然に引用 末尾にリスト追加
個性・口調 ペルソナが活きている 無個性・教科書調
見出し 具体的・面白い 短すぎて味気ない
開発の手間 低い 高い

15ステップの分業制を組んで「アーキテクチャ的に正しい」と満足していましたが、肝心のLLMが必要な情報を見れていなかったというオチです。

「エンジニアリングの美学」と「LLMの実効性能」は別物だと痛感しました。

参考

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?