Algomatic Tech Blog

Algomaticの開発チームによる Tech Blog です

LLMプロダクト開発のことはじめ #02 ~ よい応答を得るためのプロンプト制約

こんにちは。NEO(x) の宮脇(@catshun_)です。

本記事はなつやすみ特集として、『LLMプロダクト開発のことはじめ』をテーマとした 5分で読める コラムを紹介します🌻

といいつつ今回は文量が多くなってしまいました...。 ゆるく書くつもりなので、役に立つかどうかは分かりません 🐈


本記事の内容は『プロダクトわいわいシェア会 #02』という社内LT会で話した内容を踏襲しています。 読むついでに Algomatic ではこういう社内イベントもやっているよ、という雰囲気が少しでも伝われば幸いです!


📝 目次はこちら

  1. 特集の導入、問いをデザインする
  2. LLM から良い回答をえるための第一歩👈
  3. プロンプトエンジニアリング
  4. LLMプロダクトの評価と検証


LLMから良い回答をえるための第一歩

先日の株式会社Lightblueさんが 独自調査 にて、生成AIに代替・効率化可能な業務割合について、プロンプトだけで対応できる業務は全体の33.9% という値を発表されていました。

「問いのデザイン」における ①解くべき課題を定め自然言語に落とし込むという作業が、生成AI活用でも依然として必要とされている のかなと思います。


そこで第2弾では LLM からよい応答を得るためのプロンプト制約 について記述します。 なお、本記事で記述される内容については 未検証かつ主観が含まれます ので、批判思考を持ちつつお読みいただけますと幸いです。


プロンプトとは

計算機(生成AI)を制御するために入力する記号列」をプロンプトと解釈しています。

LLM では入力文に該当し、「トンネルを抜けると」という対して、川端康成コンテキストを含めると「雪国であった」、千と千尋の神隠しコンテキストを含めると「不思議な町でした」といったように、入力文に応じて適切な応答が変化します

プロンプトの役割
LLM から望ましい出力を得るためには、指示文を詳細に記述すれば良い


本記事でお伝えしたいこと

本記事でお伝えたいことは、プロンプトを記述する際は、あいまい性のない詳細な記述を心がけ、地道に改良を重ねること になります 🔄

プロンプトエンジニアリングについては #03 で記述しますが、プロダクト開発においてプロンプトエンジニアリングの優先度はそこまで高くない かなと思います。むしろ 要件を適切に伝えるためのテクニカルライティング の方が優先度が高いと感じています(諸説あり)。

Naohiro Nakata氏 (cybozu) - テクニカルライティングの基本 2023年版 (2023)


言語モデルは大規模なWebコーパスを用いて学習されるため、われわれが普段使用する言葉遣いが少なからず反映されていると思っており、マインドセットとして「ヒトも丁寧な文章には丁寧に返す(i.e. アコモデーション理論)」という考えを置いておくと良いかもしれません。

また LLM のサプライザルとヒトの認知負荷の間には強い関係があることも示されている (Smith and Levy, 2013; Wilcox+', 2020; Kuribayashi+'20) そうで、読みやすいプロンプトの記述を心がける というのも重要かもしれません。


プロンプトのことはじめ 🐾

以降については帰納的な Tips をまとめたものであり、正しい記法を保証するものでは一切ありません。あくまで業務で利用するための Tips であり、実験により妥当性が認められているわけでもありません。

内容としては以下の記事に類似するかなと思いますので、こちらも合わせてお読みください。


タスク構造を明らかにする

例えば RAG システムの generator の評価器として、LLM を用いて応答文からスコアを算出するタスクを考えます。

この場合、最終目的は「システム利用者が受け入れ可能な応答文を出力しているかチェックする」という観点になりますが、評価器のスコア算出においては評価過程をもう少し構造化して捉えます。

RAG システムの generator における応答文の評価観点

上記の場合、「表層情報 ⇆ 意味情報」「成果物 (what) の品質 ⇆ 推論過程 (how) の品質」といった観点で構造化を考慮しています。ただ実際には、システムの良し悪しは「誰がいつ使うか」というユースケースにも依存するため、ドメインエキスパートの思想をプロンプトに組み込みます

この辺りは(プロンプト設計とは少し異なりますが)ドメイン駆動開発におけるドメインモデル等の話が参考になりましたので合わせてご覧ください。

文章の構造を意識する

タスク構造を明らかにしたら、指示文や、対話履歴、コンテキストなどの入力情報を記述します。 この際、xmlタグやマークダウン言語を用いて、章・節・項に役割を付与することで 文章の構造(談話構造)を明確にします

文章の構造を意識したプロンプトのイメージ例. いろんな流派があるので

よく利用されるものとして 深津式プロンプト などが挙げられますが、自分が使いやすい再現可能な型を持つと良い のかなと思います。


プロンプトに制約を設ける🚫

ここでいう制約は、「柔軟な解釈ができる指示文」や「複数の出力形式が受け入れ可能となりうる指示文」をできるだけなくすことで 一意な出力を得る、という意味で使用しています。


1. 語句の意味表現に制約を設ける

特定性を上げる

プロンプトを記述する際は、問題となっている名詞が具体的に指している対象を話し手が頭に思い浮かべられるような 特定性の高い表現 を使用します。

  • 🙅 ユーザ からの問い合わせ
  • 🙆‍♀️ 蔵書検索システムの利用者 からの問い合わせ

  • 🙅 データサイエンティスト
  • 🙆‍♀️ PythonなどのAI・機械学習プログラミングに適した言語でデータマイニングを行うためのプログラムを開発し、データに基づいた合理的な意思決定をサポートする職務。統計学パターン認識などのデータ解析手法を用いて、大量のデータから法則性や関連性といった意味 のある情報を抽出する。


表記揺れ・表記誤りを防ぐ

特定性を上げることに加え、表記揺れを防ぐことも重要です。言語モデルは語句をIDとして変換するため、表記揺れ・表記誤りが発生すると同じ語句でも異なるIDとして認識されてしまいます

また 出力してほしい形式でプロンプトを記述する ことも重要かと思います。例えば議事録では「(山田)こんにちは」のように誰が発言したかを記述することがあります。このような場合も 表記揺れをなくす ため、プロンプトでは敬称略をつけずに「山田」と記述したりします。

議事録で「(山田)こんにちは」のように出力して欲しい場合

  • 🙅 登場人物は、山田さん、... の3名です。
  • 🙆‍♀️ 登場人物は、山田、... の3名です。


項の省略を避ける

表記揺れのほか、述語に着目した 項(誰が, 何を, 何で, 誰に, など)の省略 や、言い換えを伴う照応詞(彼, 彼女ら, 結果, など)の使用 には、十分注意します。

述語だけでなく名詞に対しても同様で、以下の例ではグループに対して連体修飾句を付与することでより詳細な説明文を記述することができます。

  • 🙅 グループ
  • 🙆‍♀️ 地元バスケット部員の グループ


2. 思考過程に制約を設ける

プロンプトに対する制約として、語句の意味表現のほか、思考過程に制約を設けることも可能です。

具体的には「{ソクラテス, 商品開発企画担当}のように」といった ロールプレイ や、「{批判思考, 水平思考, 仮説思考}で」という 思考方法の指定、「{行動経済学, 心理学}の観点で」といった 思考基準の指定 などによって、思考過程を明確に指示します


一方で、思考過程のフローが明確な場合は、思考過程を自然言語や疑似コードなどで記述する こともあります。 また記事生成などの場合は、LLM に 思考過程を逐一復唱させる ことで、フロー全体に対する現在地を LLM に確認させたりすることもあります。


3. 形式に制約を設ける

プロンプトの記法

出力形式については様々な選択肢がありますが、マークダウン言語や xml タグのような記法を用いる のがデファクトスタンダードかなと思います。

コードブロックや引用などを用いる場合は、地の文と区別する ことを意識します。 特にプロンプト中に コードや json を記述する場合はコンパイルエラーやパースエラーが発生しない 形式とします。

  • 🙅 ```\n{'hoge': 1, 'fuga': 2}\n```
  • 🙆‍♀️ ```json\n{"hoge": 1, "fuga": 2}```


出力形式の制約

ヒトに対する指示と同様に、「中学生でも分かるように」「53字以内で」といった感じで、どのようなレベル感での成果物を期待しているか を指示文に含めたりもします。

また LLM が どのトークンで生成を開始するかというリーディングワードを用いる ことで、後続の解析エラーを軽減することも可能です。

# JSON の途中まで出力する
messages = [
    {"system": "請求書から企業や金額等の情報を JSON で出力してください ...(省略)"},
    {"assistant": '{\"dummy_key\": \"dummy_value\", \"請求者\": \"'}
]

出力データの構造化に関しては Structured Outputs モード が有効ですが、プロンプト制約の方法論の一つとして以下紹介します


句切り符号は一貫性を持たせる

マークダウン/アップ言語を用いると、プロンプトの作成者に応じて表記が異なるため一貫性を持たせるのが難しくなります。

そこで句切り符号などは、以下のように使い分けてます。

  1. 'シングルクオテーション' ... 文字リテラル, 選択肢などの文
  2. "ダブルクオテーション" ... ラベル名(文字列が格納されたアドレス)
  3. '''トリプルクオテーション''' ... 説明文などの文章
  4. 「かぎかっこ」 ... 会話, 引用, 固有表現の強調
  5. 『二重かぎかっこ』 ... かぎかっこ内の会話
  6. **強調** ... 説明文などの文章
  7. <tag>xmlタグ</tag> ... セクション名(談話構造のレイアウト情報)

とはいえ、上記の使い分けを厳密に遵守せずとも LLM はよしなに解釈してくれます。どちらかというと 一貫性のある使い分けをすることでプロンプト作成における思考負荷を減らす 方が重要だと思ったりしています。

また句切り符号を記述する際は、以下に注意したりもします。

  • ""hoge"" としない("" + hoge + "" と解釈されるため)
  • "fuga' としない(" + fuga + ' と解釈されるため)
  • xml で記述されたセクション名を参照する場合は <セクション名> のみで記述しない(閉じタグが存在しないため)


このあたりは以下の記事で使い分けが紹介されていますので、こちらもご覧ください。

ベストプラクティスに従う👌

LLM プロダクトを開発する際は GPT, Claude, Gemini などの選択肢がありますが、これらのモデル間ではプロンプトの互換性が担保されていないため、各社が公開しているベストプラクティスに従います。

OpenAI

  • 最新モデルを使用する
  • 指示をプロンプトの先頭に置き、### または """ を使用して指示とコンテキストを区切ります。
  • 望ましい状況、結果、長さ、形式、スタイルなどについて、できるだけ具体的に、詳細に記述してください。
  • 例を挙げて望ましい出力形式を明確にする
  • ゼロショットから始めて、次に数ショットを試してもうまくいかなかったら、微調整する
  • 曖昧な説明や不正確な説明を減らす
  • 何をしてはいけないかと言うのではなく、代わりに何をすべきかを言う
  • コード生成の特定 - 「リーディングワード」を使用してモデルを特定のパターンに誘導する

Anthropic

  • 明確かつ直接的に伝える
  • 例を使用する(複数ショット)
  • Claudeに考えさせる(思考の連鎖)
  • XMLタグを使用する
  • Claudeに役割を与える(システムプロンプト)
  • Claudeの応答を事前に記入する
  • 複雑なプロンプトを連鎖させる
  • 長いコンテキストに関するヒント

また Anthropic からは、プロンプトエンジニアリングについて学べる教材も公開されています。

Gemini

  • 自然な言語を使う
  • 明確で簡潔に伝える
  • コンテキスト(背景や状況)を提供する
  • 具体的で関連性の高いキーワードを使用する
  • 複雑なタスクは別々の指示に分ける

次回予告

次回は以下の資料を踏襲して、プロンプトエンジニアリングについて紹介します 🙌

おわりに

ここまでお読みいただきありがとうございました!

Algomatic では LLM を活用したプロダクト開発等を行っています。 LLM を活用したプロダクト開発に興味がある方は、下記リンクからカジュアル面談の応募ができるのでぜひお話ししましょう!

お題「生成AIプロダクト開発のことはじめ」