
これは継続中のシリーズの一部です。最初の投稿と2 番目の投稿を参照してください。
恐ろしい怪物。経験豊富なエンジニアなら誰でも見たことがあるでしょう。コードが膨大で、リスクが高く、理解しにくいため、誰も触れようとしません。ユニット テストはなく、変更するたびに軽い心臓発作を起こします。これに近づくのは古参の人だけです。怪物が構築されたときに存在していた人たちで、他に選択肢がない場合にのみ近づきます。古く、モジュール化されておらず、依存関係が古くなっています。コンポーネントを本格的に変更するには危険すぎます。
私が最初に遭遇した怪物は今でも覚えています。数億ドルの価値があるビジネスの運営の中心となる 5,000 行の機能で、ほとんど誰もそれを触る自信がありませんでした。それが壊れたとき、チーム全体が夜中に起こされました。この重要なコンポーネントに依存していたため、会社内のすべての開発が遅れました。この怪物を管理するため、何百万ドルも費やされました。
これらすべてが LLM プロンプトとどう関係するのでしょうか? LLM プロンプトも怪物になる可能性があります。変更するのが非常に怖いため、誰も変更しません。または逆に、チームが修正を試みて、インシデントの雪崩を引き起こします。
顧客は、火曜日と木曜日にしか正しく動作しないソフトウェアにお金を払いたくありません。顧客は、一貫した信頼性と新機能の連続を要求します。長期にわたる高信頼性システムを構築する場合、常に稼働し続けながらアプリケーションを進化させることが不可欠です。これは、従来のソフトウェアと同様に、Gen AI 搭載アプリケーションにも当てはまります。
では、AI を活用した健全なアプリケーションを怪物ではなく実現するにはどうすればよいでしょうか。このシリーズでは、12 を超えるアプローチをすべて取り上げます。それらはすべて、1 つの原則から始まります。つまり、1 つの巨大なプロンプトではなく、それぞれが 1 つの問題を解決することを目的とした、複数の小さな焦点を絞ったプロンプトが必要なのです。
モジュール化とは、複雑なシステムを、より小さく、自己完結的で、再利用可能なコンポーネントに分割する手法です。従来のソフトウェア エンジニアリングでは、これは、それぞれが特定のタスクを処理する関数、クラス、およびサービスを作成することを意味します。LLM のプロンプト エンジニアリングのコンテキストでは、モジュール化とは、大規模でモノリシックなプロンプトを、それぞれが単一の明確に定義されたジョブを実行するように設計された、より小さく、焦点を絞ったプロンプトに分割することを意味します。
モジュール化により、時間の経過とともにシステムに変更を安全に導入できます。次の場合にその重要性が高まります。
システムを計画する際には、これらすべての側面を理解する必要があります。
しかし、モジュール化は具体的にどのようにシステムの維持に役立つのでしょうか? 主な利点を以下に説明します。
LLM プロンプトのパフォーマンスは、本質的に不安定です。その性質上、変更を加えると、予期しない方法で出力に影響する可能性があります。このリスクを管理するには、大きなプロンプトをコンポーネントに分割します。コンポーネントでは、変更がシステムの一部のパフォーマンスにのみ影響します。1 つのプロンプトが壊れても、システムの残りの部分は変更前と同じように動作します。
しかし、プロンプトがチェーンとして動作する場合はどうなるでしょうか。1 つのコンポーネントが壊れると、チェーンも壊れるのではないでしょうか。はい、壊れますが、このシナリオでは被害は軽減されます。プロンプト チェーン内の誤った出力は、下流のプロンプトに誤った入力を供給する可能性がありますが、各コンポーネントは有効な入力セットの変更前と同じように動作します。これを巨大なプロンプトの変更と比較してください。変更は、そのプロンプトにエンコードされたロジックのあらゆる部分に影響を与える可能性があります (実際に影響します)。システムの 1 つの側面を壊したのではなく、システムのあらゆる部分を壊した可能性があります。
(プロンプトのチェーンを安全に操作する方法は、このシリーズの今後の章で説明します。さまざまな種類の障害を想定して、緊急時対応計画を立てる必要があります。ただし、これはここでは取り上げません)
ユニット テストを書いたことがある人なら誰でも、1 つのことだけを行う単純な関数の方が、さまざまなことを行う複雑な関数よりもテストがはるかに簡単であることを知っています。同じことはプロンプトにも当てはまります。小さくて焦点が絞られたプロンプトは、手動でも完全に自動化された方法でも、はるかに徹底的にテストできます。
幅広い証拠から、短いプロンプトの方が長いプロンプトよりもパフォーマンスが優れている傾向があることがわかっています: 1 、 2 、 3 。
マルチタスクがプロンプトのパフォーマンスに与える影響に関する研究は、より複雑です: 4 、 5 。完全に最適化されたプロンプトは、適切な状況下ではマルチタスクを実行できます。ただし、実際には、単一の主要な次元に沿ってパフォーマンスを追跡できる、焦点を絞ったプロンプトを最適化する方がはるかに簡単です。可能な限り、より焦点を絞ったプロンプトを目指す必要があります。
3,000 語のスーパープロンプトの複雑さを新しいチームメンバーに説明するのは大変な作業です。どれだけ説明しても、この難題を理解できるのは寄稿者だけです。
各部分が比較的シンプルなプロンプト システムにより、オンボーディングが大幅に高速化され、エンジニアはより早く生産性を発揮できるようになります。
システムのさまざまな部分でさまざまなモデルを使用することで、応答品質に影響を与えることなく、コストとレイテンシを大幅に削減できます。
たとえば、入力言語を判別するプロンプトは、特にスマートである必要はなく、最新で最も高価なモデルは必要ありません。一方、ドキュメントに基づいて応答を生成するプロンプトは、ハイエンド モデルに組み込まれている思考連鎖推論の恩恵を受けることができます。
ソフトウェア駆動型アプリケーションのほとんどは、長期間にわたって安全に機能を追加する必要があります。ただし、例外があります。プロトタイプ アプリケーションは長期間維持されることを意図したものではありません。新しい機能が追加されることはなく、高い信頼性も期待できません。したがって、プロトタイプを構築するときは、モジュール化に時間を無駄にしないでください。実際、このシリーズのパターンのほとんどは、プロトタイプ アプリケーションには適用されません。プロトタイプを構築するときは、素早く進め、重要な不明点を検証してから、コードを破棄してください。
もう 1 つの考慮事項は、モジュール化を停止するタイミングを知ることです。追加のプロンプトを管理するにはオーバーヘッドがあり、さらにモジュール化してもメリットが低い場合は、システムの分割を停止する必要があります。
プロンプトのモジュール化が簡単なら、誰もがそれを実行するでしょう。システム内の多くのプロンプトを管理するには、インフラストラクチャに投資する必要があります。インフラストラクチャがなければ、混乱が生じます。LLM プロンプト インフラストラクチャの最小要件は次のとおりです。
標準化された方法で、プロンプトをすばやく簡単に追加する機能。プロンプトがコードベースの外部からロードされる場合に特に重要です。 「原則 II: プロンプトを安全にロードする (本当に必要な場合)」を参照してください。
プロンプトを自動的に展開する機能。
個々のプロンプトの入力/出力をログに記録して監視する機能。
プロンプトをカバーする自動テストを追加する機能。
さまざまなプロンプトでのトークン/ドルの支出を簡単に追跡する方法。
モジュール化の有無にかかわらず、Gen AI 搭載システムの構築が実際にどのように行われるかを見てみましょう。
テクニカル サポート アプリを構築しており、単一のプロンプトで実装することにします。最も単純なバージョンでは、 RAGを介して関連ドキュメントをロードしながら応答を生成するモノリス プロンプトを想像できます。
見た目は素晴らしくて簡単そうですよね? しかし、機能を追加すると、このアーキテクチャの問題が浮上します。
固定された言語リストのメッセージに応答し、他の言語は処理しないようにします。これを実現するには、特定の言語でのみ応答するようにプロンプト指示を追加し、レポート目的で LLM に「言語」フィールドを返すようにします。
すべての会話を分類したい場合、プロンプト出力にフィールド「ラベル」を追加します。
ユーザーが不満の場合は、ケースを人間のサポートにエスカレーションします。プロンプトに指示とともに「escalate_to_human」出力変数を追加します。
内部監査用に送信されたすべてのメッセージの翻訳が必要です。英語のメッセージを含む「翻訳済み」フィールドを返します。
アプリがユーザーの現在地や前回の選挙で誰に投票したかを尋ねないように保護する必要があります。プロンプトの指示を追加して手動でテストします。
すべての会話の要約が必要ですか? すべての出力に「要約」フィールドを追加します。
おそらく、問題が見え始めているでしょう。このプロンプトには現在 6 つの出力があります。これをテストするのは悪夢です。別の言語のサポートを追加すると、突然、アプリが英語ではなくスペイン語で要約を返し始めます。なぜでしょう? 誰にもわかりません。LLM 出力は不安定なので、プロンプトを変更すると予測できない結果になります。
おめでとうございます。モンスターを生み出しました。時間が経つにつれてモンスターは大きくなり、さらに大きな痛みを引き起こします。
プロンプト チェーンと完全に分離された分類プロンプトの両方が使用されます。元の大きなプロンプトは、実用的な範囲でモジュール化されています。
1 つのプロンプトが言語を検出し、1 つが翻訳を提供し、1 つがユーザーが怒っているかどうかを判断して人間にエスカレーションし、応答プロンプトが応答を生成し、ガードレールが応答のコンプライアンスを検証します。1 つのプロンプトの出力は、次のプロンプトの入力として連鎖されます。従来のコードは、LLM を介さずに、たとえば言語の適格性を確認するために、これらのプロンプト間で動作できます。
変更によって特定のプロンプトが機能しなくなる可能性はありますが、次の理由によりリスクは大幅に軽減されます。
Gen AI のメリットをすべて享受しながら、リスクは大幅に軽減されます。さらに、一部のコンポーネントに安価なモデルを使用してコストを節約できます。
モジュール化により、エラーを分離し、保守性を向上させ、より信頼性の高いシステムを構築できます。中規模のアプリケーションでも、コンポーネント プロンプトは数十、場合によっては数百になります。プロンプトは、それぞれが 1 つのタスクを実行するまで、また、モジュール化によるメリットが運用の複雑さの増加を上回るまで分割します。AI 駆動型アプリケーションの信頼性を維持し、長期にわたって機能を追加し続けるには、プロンプトのモジュール化が不可欠です。すでに「モンスター」システムはたくさんあります。新しいシステムを作成しないように注意してください。
このシリーズをお楽しみいただけましたら、ぜひ購読して他の投稿もご覧ください。