📏

LLMにルールベースのルーティングを実装したい時のプロンプトエンジニアリング

に公開

こんにちは。
最近、AIによるルールベースのルーティングの機能を実装する機会がありました。そこで得られたプロンプトテクニックを共有したいと思います。

やりたいこと

あるルールと入力値の集合が与えられます。入力値がルールの基準を満たしている場合は「true」、満たしていない場合は「false」を出力させたい。そして、その判定となった根拠(理由)も併せて出力させたいと考えます。

例えば次のような例です。

ルール:
emailのドメインが「example.com」の場合、true、それ以外はfalseを出力して。

入力値:
{
    "email": "test@example.com",
    "name": "hoge"
}

期待するアウトプットは以下の通りです。

{
    "result": true,
    "reason": "入力値のemailは「test@example.com」だった。「emailのドメインが「example.com」の場合、trueを、それ以外はfalseを出力して」という基準を満たすため、true"
}

ルールはユーザーがある程度自由に記述できます。入力値はユーザーが用意したフォームに対する入力値です。この点についてはさほど重要ではないので、細かいところは割愛します。

さて、これに対するシステムプロンプトを考えます。

まず「結論」を書かせる?

直感的には、以下のように「結論」→「理由」という順序で出力させたくなるので、下記のようなフローでAIに依頼したくなります。

  1. ルールと入力値をもとにtrue / falseのresultを出力する
  2. その決定にした理由を出力する

しかし、この順序だと不正確な出力が返ってくるケースが多いことに気づきました。
例えば、resulttrue なのに reason では「falseの理由」が説明されていたり、判定そのものが誤っていたりするのです。
この問題にはしばらく頭を悩ませました。

LLMの生成の特性と問題点

LLMによる生成の特性は、人間の思考回路とは異なります。

人間は「結論を考えてから、その結論に至った理由を書く」ことが多いですが、LLMはそうではありません。LLMは逐次的に次の単語を予測していく仕組みであり、出力中に「考えながら書く」状態にあります。つまり、resultを出力する段階で、明確な理由を持っているわけではないということです。

result: true // <- これを出力する段階では明確な根拠がある状態ではない

そのため、先に result を出力させると、その後の reason が一貫しない リスクが高いのです。むしろ、あり得そうな「結論ラベル」を先に書いてしまい、それに後付けで理由をつけるような挙動をする場合すらあります。

LLMは基本的に「あり得そうな次のワードを確率から決定していく」という方式で言葉を導き出しています。これは「これまでのすべてのトークン(文脈)」を元に、次のトークンを確率的に予測し、それを「確定させて」次のトークン予測へ進むというものです。つまり出力の各部分が段階的に確定していく構造を持っています。これにより、「最初から全体を完成させた結論を頭の中で持っていて、それを後で書き出す」のではなく、「書きながら/出力しながら結論方向へ近づいていく」という性質が生じるのです。

https://medium.com/@sybrandwildeboer/how-large-language-models-predict-the-next-word-and-why-thats-powerful-b1ac78995e74

余談ですが、LLMの料金体系を見ていると、大体は入力トークンと出力トークンに対する課金です。これも人間の感覚からすると不思議です。出力トークンって人間にしてみれば発話とかの感覚に近いと思ってしまうので、LLMの"思考時間"には課金されないのかな、と思っていたのですが、「考えながら書いている」と考えると納得がいきます。

https://openai.com/ja-JP/api/pricing/

「理由から結論」への発想の転換

さて、この問題を避けるために、次の順序で出力させる方法を取ります。

  1. ルールと入力値をもとに、true / falseを判断するための理由を reason に出力する
  2. reason をもとに、true / falseresult を出力する

こうすることで、まず推論過程(reasoning)が明示され、その推論を土台に結論が生成されます。結果的に、結論と理由の一貫性が高まり、誤判定が減ります。

"reason": "入力値のemailは「test@example.com」だった。「emailのドメインが「example.com」の場合、trueを、それ以外はfalseを出力して」という基準を満たすため、true"
↓
reasonの出力が完了し、結論が確定している状態。resultの出力は安定し、精度も上がる。

この手法は、研究分野でいう「Chain-of-Thought (CoT) Prompting」[1]に近い発想です。Chain-of-Thought研究では、回答に至る推論ステップを明示的に生成させることで、複雑な問題の精度が大きく向上することが示されています。

まとめ

今回の機能開発のような課題と同じような課題に直面することは少ないかもしれませんが、LLMの思考過程は人間のそれとは大きく異なることはとても興味深いですし、これを前提知識として持っておくと、いろいろなところで応用が効くのではないかと思います。

  • LLMは逐次予測で出力するため、先に結論を書かせると理由との不整合が生じやすい
  • 先に理由(推論ステップ)を書かせ、その後に結論を出力させると整合性が高まる
  • これは Chain-of-Thought 的なアプローチであり、研究でも有効性が裏付けられている
脚注
  1. Wei, Jason et al. Chain-of-Thought Prompting Elicits Reasoning in Large Language Models. arXiv:2201.11903 (2022). https://arxiv.org/abs/2201.11903 ↩︎

immedioテックブログ

Discussion