every Tech Blog

株式会社エブリーのTech Blogです。

AIワークフロー設計と軌道修正の振り返り

この記事は every Tech Blog Advent Calendar 2025 の25日目の記事です。

目次

はじめに

こんにちは。2025年4月にソフトウェアエンジニアとして新卒入社した黒髙です。普段はデリッシュキッチンの開発に携わっています。

デリッシュキッチンにはレシピだけでなく、料理に関する情報をまとめた記事レシピ特集など、多様なWebコンテンツがあります。コンテンツディレクターの業務効率化の一環として、ユーザーニーズを満たすコンテンツ作成をAIでサポートする試みを、私は設計から実装までを一貫して担当しました。

当初はAIに創造的なコンテンツ生成を任せようと意気込んでいたのですが、実運用では想定外の課題が次々と顕在化し、最終的にはルールベースの制約(ガードレール)を多く追加することになりました。

以下画像は、実際に生成されたアボカドサーモンの魅力を引き出すレシピ集 | デリッシュキッチンのコンテンツの一部です。

ページの一例

実際に自動生成して公開しているコンテンツは、セレクション一覧からもご覧いただけます。

また、記事生成に関わる処理の全体像は次の通りです。AIワークフローの詳細は後述します。

処理の流れとアーキテクチャ

本記事では、AIワークフロー設計で何を期待し、何が起き、どのように軌道修正したかを振り返ります。LLMや生成AI活用の文脈では基本的な話も含みますが、現場で使えるものにするためにどう整えていったのかの判断を中心にまとめます。

設計から軌道修正まで

本章では設計段階から運用に至るまで、AIワークフローに関する部分の振り返りを行います。

1. 何を目指していたか

理想は、AIがWebページの構成を考え、分析結果に応じて組み替えられることです。ただし今回は、早期にコンテンツを公開することを優先したPoC(初期検証フェーズ)として、ページ構成を「見出し+説明文+レシピリンク集」の繰り返しに限定しました。

- 見出し1
  - 見出し1の説明文
  - 見出し1のレシピリスト
    - レシピA
    - レシピB
    - レシピC
- 見出し2
...
(以下繰り返し)

当初は、ニーズ分析→見出し・説明文の生成→関連レシピの検索までをAIが一貫して行うことを目指しました。具体的には、次の5ステップのLLMパイプラインを設計しました。

当初のワークフローと具体例

また、初期検証をできるだけ早く進めるため、デリッシュAIで利用しているレシピ一覧を埋め込みベクトルに変換したテーブルを使いました。

2. 運用して顕在化した問題

数パターンのキーワードで試したところ、課題は残るものの、記事として形になることは確認できました。そこでバックエンド/フロントエンドを実装し、キーワード一覧をもとにバッチで自動生成を開始しました。

ところが「キーワードに沿ったレシピが選ばれているか」の観点の公開前レビューでは、公開できる記事が想定より少ない結果になりました。さまざまな入力パターンを事前に十分検証し、試行段階で見えていた課題にこの時点で対処しておくべきでした。

具体的な問題としては以下のようなものがあります。

検索クエリの生成が安定しない

今回のフローでは画像の通り、見出しに基づいたレシピ検索クエリをLLMに生成させています。例えば 簡単に作れるごぼうと牛肉レシピ を入力しても、簡単レシピ簡単牛肉 のように意図が欠けたクエリになることが稀にありました。結果として後続のLLMにも意図したリクエストを送れず、適切な記事が生成できない問題がありました。

見出しに合うレシピが必ず存在するかどうかはわからない

検索キーワード ごぼう 牛肉 から ごぼうと牛肉のローストビーフ のような見出しが生成されても、デリッシュキッチンにそのようなレシピがあるとは限りません。具体的に絞り込む見出し提案に従った結果、レシピが登場しないといった問題が発生しました。

条件に合わないレシピが含まれてしまう

例えば 簡単に作れるごぼうと牛肉のレシピ に対して、味付け簡単! 牛すじとごぼうの塩麹煮 のような適切なレシピが出る一方で、簡単で本格的な味! 豚バラ肉とごぼうの和風煮 といった、響きは似ているものの材料が欠けているレシピも混ざることがありました。

3. 問題の原因

プロンプトの肥大化

デリッシュキッチンでは、信頼性の高いコンテンツ品質を維持するため、多くの制作ルールが存在しています。以下は一例ですが、実際には数十個程度の記載NG項目があります。

- 健康・栄養に関する過度な表現禁止: ダイエット, ヘルシー ... 
- 食欲・体調への直接効果を謳う表現禁止: 食欲, 体力, 元気 ... 

料理系コンテンツでは連想語がNGになることも多く、AIにとって制約の強い要求です。さらに、これらをすべてのステップで指示していたため、禁止事項を追加しても一度で要求を満たし切れないことがありました。

不要な思考(ニーズ分析)を挟んでいた

ニーズに合致した構成にするためにStep 1としてニーズ分析を入れましたが、禁止事項を守らせることや、ヒットするレシピ数を担保するために無難な見出しに寄せる要件とは相性が悪く、逆にノイズになっていました。

ベクトル検索に対して除外の前後処理を入れていなかった

レシピ検索としてベクトル検索とコサイン類似度によるスコアリングを用いていましたが、関係のないレシピも検索結果に含まれてしまう問題が発生していました。ベクトル検索は意味的な類似性を捉えるのには優れていますが、それ単体で材料として実際に使われているかどうかを判定することはできません。

根本にあった認識の甘さ

これらの問題に共通していたのは、以下の点に対する認識の甘さでした。

  • 運用では検証段階より多種多様なキーワードを扱うこと
  • LLMによる不安定な出力が次段階の生成内容にも影響をもたらすこと

当初は創造的なWebサイト構築を漠然と想像していましたが、実際に必要だったのはAIが生成した記事をできるだけ早く公開し、生成から公開までのサイクルを回しながら検証を進めることでした。

しかし、少数のキーワードではそれなりに記事が生成できたことで安心してしまい、安定運用できるかという観点が抜け落ちたまま次の開発に進んでしまっていました。

4. どう軌道修正したか

問題を踏まえて、以下のような軌道修正を行いました。

以前のステップ 変更点
1. キーワードのニーズ分析 削除
2. 小見出し生成 LLMによるテンプレート選択
3. 検索キーワード抽出 ルールベースで生成
4. コンテンツ生成 変更なし
5. レシピ検索 OpenSearch + LLM-as-a-Judgeの追加

ニーズ分析のステップを排除

前述の通り、ノイズとなっていたStep 1(キーワードのニーズ分析)を削除しました。 将来的には分析データが揃い次第、数値に基づくニーズ分析を復活させ、LLMによる構成検討も行いたいと考えています。

見出しの自由生成・クエリ生成を排除

  • 対象を絞り込まない抽象的な見出しにしたいこと
  • 禁止ワードを含めたくないこと

を踏まえると、見出しのパターンはある程度限られてくることが分かりました。ヒットするレシピを安定して出すために、LLMに自由生成させるのではなく、見出し候補を用意して選択してもらう方針に転換しました。

SUBHEADING_OPTIONS = [
    {"kind": 1, "template": "簡単に作れるxxのレシピ"},
    {"kind": 2, "template": "xxとxxの絶妙な組み合わせ"},
    {"kind": 3, "template": "xxを使ったアレンジレシピ"},
    {"kind": 4, "template": "新しいxxの料理アイデア"},
    # ...
]

KIND_KEYWORD_MAPPING: dict[int, str] = {
    1: "{keyword} 簡単",
    2: "{keyword} 組み合わせ",
    3: "{keyword} アレンジ",
    4: "{keyword} アイデア",
    # ...
}

また、見出しに基づくクエリ生成もLLMに任せるのをやめ、(検索キーワード+見出しの特徴)をルールベースで組み立てる方針に戻しました。HyDE(Hypothetical Document Embeddings)のような手法も検討しましたが、1見出しに最大9個のレシピを網羅的に取得したい要件には合わないと判断し、簡易的で汎用的なクエリで対応することにしました。

レシピをベクトル検索からOpenSearch利用に変更

構想段階の試用では、デリッシュAIの基盤を一部利用したベクトル検索が手軽で都合が良かったので採用していました。

しかし、ワークフローを見直し、扱うキーワードや見出し表現をある程度固定化した結果、検索に求める要件も変化しました。

以下の理由から、レシピ検索機能で既に用いているOpenSearchを利用する方針としています。

  • 多くの対象キーワードにおいて、通常検索の方が期待したレシピが安定してヒットすること
  • レシピ検索機能で用いられているシノニム辞書をそのまま活用できること
  • 数パターンに固定化した見出し表現においては、ベクトル検索の強みである意味的類似度の必要性が相対的に薄れたこと

なお、今後扱うキーワードの幅や要件が変わった場合には、再びベクトル検索を含めた見直しを行う余地があると考えています。

LLM-as-a-Judgeの導入

更なるガードレールとして、レシピが見出しの意図に合っているかを審査するステップを追加しました。この判定を導入することで、条件に合わないレシピをより厳格に除外できることが期待できます。

    {
      "recipe_id": "206162500360602656",
      "accept": true,
      "reason": "タイトル・材料に「牛すじ(牛肉)」とごぼうが含まれており、サブ見出しの「ごぼう 牛肉 簡単」に合致します(味付けが簡単な点も明記)。調理手順は煮込み工程があるものの、小見出しの意図と矛盾していません。"
    },
    {
      "recipe_id": "163718318442676716",
      "accept": false,
      "reason": "ごぼうは使われていますが、主な肉材が豚バラ肉であり、小見出しが想定する『牛肉』と一致しません。"
    },

まとめると、フローは次の通りです。

ワークフロー

初期段階では1記事あたり3〜10件以上の修正指摘が発生していたものが、これらの改善によって修正指摘が0件のケースが大半となり、あっても1〜2件程度に収まるようになりました。

5. 前提条件の見直し

ワークフローの見直しを進める中で、そもそもどのキーワードを生成対象とすべきかという前提条件にも課題があることが分かりました。

当初は、食に関連するキーワードを一律に処理していましたが、キーワードによって求められる判断や前処理の難易度が大きく異なることが次第に明らかになりました。

例えば カニ レシピ では、カニカマを許容するかどうかの意思決定が必要で、LLMには難しいです。また、酒のあて のように抽象度の高いキーワードでは、どの切り口で掘り下げるかという追加設計が求められます。

こうした性質の異なるキーワードを同一の前提でAIワークフローに流していたことが、後段のLLM-as-a-Judgeや人手レビューの負荷を高め、全体の不安定さにつながっていました。

ひとまずは、AIワークフローの前段としてキーワードの取捨選択を行い、判断コストが高そうなものや、追加設計が必要なものは対象外とする方針で生成していきます。

振り返りから見えたワークフロー設計の勘所

これまでの試行錯誤を振り返ると、技術的な工夫以上にワークフロー設計そのものの考え方が重要だったと感じています。

ステップごとに切り分けて進める

今回のようにLLMの出力結果が後続の処理に大きく依存するワークフローでは、各ステップが単体で正しく機能するかをまず確認する必要があります。想定動作・実装方針・失敗時のハンドリングまで詰めたうえで次に進むことが、品質担保と手戻り削減の観点で重要であると感じています。

AIに任せる部分・ルールベースにすべき部分を見極める

本取り組みを通じて、LLMに任せる部分/任せない部分を明確にすることが、ワークフロー全体の安定性に直結することが分かりました。

特に意識したのは、次の3点です。

  • 決定的に定義できる処理は、LLMに任せない
  • 出力の揺れが後段に大きく影響する箇所では、強いガードレールを設ける
  • 曖昧さを許容できる工程のみ、LLMの生成能力を活かす

このように役割分担を整理したことで、LLMにすべてを任せる構成から、壊れにくいパイプラインへと移行できました。

運用を見据えたガードレール設計

最終目標(ユーザーニーズを満たすコンテンツ制作の工数をできるだけ減らすこと)自体は当初から変わっていません。一方でビジネス側には、まず記事を公開し、ランキングの変化を見ながら改善したいという目的がありました。そのためには、改善サイクルを回せるだけの安定した記事生成が前提になりますが、創造性を高める工夫を優先してしまったことが手戻りの原因でした。

今後の取り組みと展望

今回生成している記事は、文章の整合性や、検索キーワードに紐づくレシピになっているかという点では、一定の信頼性を確保できています。一方で、訪問ユーザーのニーズを満たすコンテンツとしては、まだ伸びしろがあると感じています。

一例ですが、以下を検討しています。

  • Self-ReflectionによるLLMの自動改善サイクルの導入
  • 幅広い検索キーワードに対応するルーティングの追加
  • ベクトル検索の再利用とレシピのスコアリング
  • レシピページ以外のアセット活用(記事、特集、カテゴリページ)
  • トレンドや訪問ユーザーのログを活用した記事の再生成

これらの改善により、更に品質の高い記事生成を目指していきます。

参考文献