こんにちは!Algomaticネオセールスカンパニーでソフトウェアエンジニアをしている越川と申します。
1月に弊カンパニーでリリースした アポドリ はまだまだたくさんの反響を頂いております。
本記事では、アポドリを開発している中で向き合ってきた、LLMのアウトプット品質をいかにして担保するか?というお話をします。ぜひ最後まで見ていただけると嬉しいです。
※本記事内のプロンプトはサンプルであり、アポドリで実際に使われてるものではありません
なぜLLMのアウトプット品質の担保が難しいのか?
1. LLMのロジックがブラックボックスであるため
従来のシステム開発では、関数やアルゴリズムのロジックが明確で、入力と出力の関係性を追跡できました。
しかし、LLM(大規模言語モデル)は内部処理がブラックボックス化しており、同じ入力でも異なる出力を生成する可能性があります。 これは、モデルが確率的な生成プロセスに基づいているためであり、完全な再現性や予測可能性を保証するのが難しいことを意味します。
2. 1000件大丈夫でも1001件目でNGになることがある
テストケースを一定数実施して「大丈夫そう」と判断したとしても、実際の運用では予想外の問題が発生します。これはLLMの本質的な課題で、どれだけプロンプトをチューニングしても、同じ内容でも表現方法が異なるケースや想定外のエッジケースに対応することが極めて難しいのです。
例えば、カスタマーサポートへの問い合わせでは、「製品が壊れています」という単純な報告も、「昨日から急に音が出なくなった」「電源を入れると変な音がする」「正常に動作しない」など様々な表現で届きます。さらに「使い始めて3日で壊れた高級品なのに、この対応は酷い」といった感情的な要素が加わると、LLMは文脈理解を誤りやすくなります。
多数のテストケースで問題がなくても、新たな入力パターンで予期せぬ結果が生じるというのは、LLMを使ったサービスでよく起こりがちな品質課題の一つです。
LLMを使ったサービスならではの品質担保戦略の必要性
LLMには「絶対」がありません。どれだけ精緻なプロンプトエンジニアリングを施しても、100%の正確性を保証することはできません。従来型のシステムとは異なり、LLMを活用したサービスでは、新たな品質担保の戦略が必要になります。
従来型のシステム開発では、単体テストや結合テストなど事前の品質確認によって問題を排除するアプローチが一般的でした。しかしLLMを活用したシステムでは、事前の網羅的テストだけでは不十分です。むしろ「品質をいかにモニタリングし、継続的に改善するか」という運用面での戦略が重要になります。
私たちは、以下の観点からLLMサービスならではの品質担保戦略を実践しています。
- エラーパターンの言語化と継続的改善: 人間の目による検証と言語化を通じて、LLMの判断精度を継続的に向上させる
- 評価と生成の分離による精度向上: 複雑なタスクを分割し、それぞれに特化したプロンプトで処理することで安定性を確保する
- 不確実性の明示と人間との協働: LLMの限界を認識し、確信が持てないケースは人間による判断を求める仕組みを整備する
これらの考え方を実践するための3つのポイントについて、具体例を交えながら見ていきましょう。
ポイント1: 単純なプロンプトで目検し、判断条件を言語化する
LLMの品質担保の第一歩は、単純なプロンプトからスタートし、出力結果を人間が直接確認することです。そして、どのような誤りが発生しているかを具体的に言語化し、プロンプトを改善していきます。
例えば、顧客からCS(カスタマーサポート)へ来たメールを分類する処理を題材に考えてみましょう。このような分類処理では、LLMが苦手とする判断ポイントやミスを起こしやすい課題が複数あります。
- LLMが苦手なケース: 優先度の判断基準が主観的で、同じ内容でも文脈によって緊急度が変わる
- LLMがミスしやすい判断: クレームと返金要求の境界が曖昧なケースで、意図を正確に把握できない
- LLMが見落としがちな要素: 言葉遣いから緊急度を判断する必要があるが、ニュアンスを捉えられない
- LLMが過剰反応しやすい点: 顧客の感情的な表現に引っ張られ、問題の本質を見失う
人間であれば「できるだけ早く返金対応をお願いします」という表現から優先度が高いことや、「商品が届いていない」という表現が技術的な問題ではなく配送の問題を示していることを直感的に判断できますが、LLMがこれらの判断ポイントを正確に処理できているかを確認することが重要です。
以下にプロンプトの例を掲載します。(※流し見でOKです)
<指示> あなたはカスタマーサポート問い合わせ分類のプロフェッショナルです。 以下の顧客問い合わせを分析し、適切なカテゴリ、優先度、担当部署に振り分けてください。 </指示> <データ> 入力データ: 先週購入したプレミアムプランで請求額が説明と違います。 公式サイトでは月額2,980円とありましたが、実際には3,980円が引き落とされています。 できるだけ早く返金対応をお願いします。 </データ> <作業ステップ> 1. 問い合わせの種類を判定してください。以下から最適なものを選択: - 質問(一般的な問い合わせ) - クレーム(不満・苦情) - 返金・キャンセル要求 - 技術的サポート - 提案・フィードバック - その他 2. 問い合わせの優先度を判定してください。以下から最適なものを選択: - 緊急(24時間以内に対応必要) - 高(48時間以内に対応必要) - 中(3-5営業日以内に対応) - 低(対応は必要だが急ぎではない) 3. 最適な担当部署を特定してください。以下から最適なものを選択: - 課金・決済チーム - 技術サポートチーム - 製品サポートチーム - アカウント管理チーム - コンプライアンスチーム 4. 問い合わせ内容から明確でない情報があれば、特定してください。 </作業ステップ> <フォーマット> 以下のようなJSON形式で出力してください。 { "reason": "分類結果の理由や特記事項", "category": "問い合わせの種類", "priority": "優先度", "department": "担当部署", "additionalInfoNeeded": ["必要な追加情報1", "必要な追加情報2"] } </フォーマット>
このシンプルなプロンプトでアウトプットを目検で評価していくと、複数の要望が混在するケースでの判断ブレや、「できるだけ早く」という表現の解釈の不一致などの課題が見つかります。 人間が求める判断に近づけるには、より細かい条件を指定する必要があります。
そこで、プロンプトを以下のように改善してみます。
<指示> あなたはカスタマーサポート問い合わせ分類のプロフェッショナルです。 以下の顧客問い合わせを分析し、適切なカテゴリ、優先度、担当部署に振り分けてください。 -----------追加部分------------ 分類の際には、以下の点に特に注意してください: - 問い合わせ内に複数の要望/質問が混在する場合は、主要な問題を特定し優先順位をつける - 具体的な影響(金銭的損失、サービス停止など)に基づいて判断する - クレームと返金要求は明示的な返金要求があるかどうかで区別する - 複数部署が関与する場合は、主要な問題を扱う部署に割り振る - 「できるだけ早く」などの言葉遣いから適切に緊急度を判断する </指示> -----------追加部分おわり------------ 省略 -----------追加部分------------ 2. 問い合わせの優先度を判定してください。以下から最適なものを選択: - 緊急(24時間以内に対応必要):サービス停止、金銭的損失、法的問題など - 高(48時間以内に対応必要):重要な機能の不具合、課金問題など - 中(3-5営業日以内に対応):一般的な使い方の質問、改善要望など - 低(対応は必要だが急ぎではない):今後の検討項目、情報提供など 3. 最適な担当部署を特定してください。以下から最適なものを選択: - 課金・決済チーム:支払い、請求書、返金、価格に関する問題 - 技術サポートチーム:システム障害、バグ、技術的問題 - 製品サポートチーム:使用方法、機能、製品仕様に関する質問 - アカウント管理チーム:アカウント作成・削除、パスワード、個人情報 - コンプライアンスチーム:法律・規制関連、違反報告、セキュリティ問題 -----------追加部分おわり------------ 省略
主な改善ポイントは、人間が目検で発見したエラーパターンを具体的に指示に反映している点です。
例えば、優先度の判断が主観的になりやすい問題に対処するため「具体的な影響に基づいて判断する」よう指示したり、「クレームと返金要求の境界が曖昧」という問題に対して判断基準を明示したりしています。
このように、実際に発見したエラーパターンをプロンプトに反映することで、LLMがより正確な判断を行える可能性が高まります。
人間にとっても判断基準の言語化は困難
注意すべき点として、判断条件の言語化は人間にとっても非常に難しい作業であることが挙げられます。カスタマーサポートの熟練者であっても、「なぜそのように判断したのか」を完全に言語化するのは容易ではありません。例えば、「この問い合わせは緊急度が高い」と判断する際、経験に基づく直感や暗黙知が大きく影響していることがあります。
このような状況下では、プロンプトエンジニアリングは「完璧な判断基準の言語化」を目指すのではなく、「主要な判断基準を可能な限り言語化し、それ以外の部分はLLMに委ねる」というアプローチが現実的です。
ポイント2: 評価プロセスは分離する
LLMの回答精度をさらに向上させるための重要なポイントとして、回答生成と評価を分離するアプローチがあります。LLMは複雑な指示を一度に処理しようとすると精度が不安定になる傾向があります。そのため、「回答を生成する処理」と「回答を評価する処理」を別々のプロンプトに分けることで、それぞれの精度を高めることが可能になります。
一体型と分離型の比較
まず、回答生成と評価を一つのプロンプトで行う「一体型」の例を示します。
<指示> あなたはカスタマーサポート問い合わせ分類のプロフェッショナルです。 以下の顧客問い合わせを分析し、適切なカテゴリ、優先度、担当部署に振り分けてください。 また、あなた自身の回答の精度を評価し、確信度も示してください。 分類の際には、以下の点に特に注意してください: - 問い合わせ内に複数の要望/質問が混在する場合は、主要な問題を特定し優先順位をつける - 優先度の判断は主観的になりがちなので、具体的な影響(金銭的損失、サービス停止など)に基づいて判断する - クレームと返金要求は混同しやすいが、明示的な返金要求があるかどうかで区別する - 複数部署が関与する場合は、主要な問題を扱う部署に割り振る - 「できるだけ早く」などの言葉遣いから適切に緊急度を判断する - 顧客の感情的な表現に左右されず、問題の本質を見極める </指示> <データ> 入力データ: 先週購入したプレミアムプランで請求額が説明と違います。 公式サイトでは月額2,980円とありましたが、実際には3,980円が引き落とされています。 できるだけ早く返金対応をお願いします。 </データ> <作業ステップ> 1. 問い合わせの種類を判定してください。以下から最適なものを選択: - 質問(一般的な問い合わせ) - クレーム(不満・苦情) - 返金・キャンセル要求 - 技術的サポート - 提案・フィードバック - その他 2. 問い合わせの優先度を判定してください。以下から最適なものを選択: - 緊急(24時間以内に対応必要):サービス停止、金銭的損失、法的問題など - 高(48時間以内に対応必要):重要な機能の不具合、課金問題など - 中(3-5営業日以内に対応):一般的な使い方の質問、改善要望など - 低(対応は必要だが急ぎではない):今後の検討項目、情報提供など 3. 最適な担当部署を特定してください。以下から最適なものを選択: - 課金・決済チーム:支払い、請求書、返金、価格に関する問題 - 技術サポートチーム:システム障害、バグ、技術的問題 - 製品サポートチーム:使用方法、機能、製品仕様に関する質問 - アカウント管理チーム:アカウント作成・削除、パスワード、個人情報 - コンプライアンスチーム:法律・規制関連、違反報告、セキュリティ問題 4. 問い合わせ内容から明確でない情報があれば、特定してください。 5. あなた自身の判断の確信度を評価し、潜在的な問題点や別の解釈の可能性を検討してください。 </作業ステップ> <フォーマット> 以下のようなJSON形式で出力してください。 { "classification": { "reason": "分類結果の理由や特記事項", "category": "問い合わせの種類", "priority": "優先度", "department": "担当部署", "additionalInfoNeeded": ["必要な追加情報1", "必要な追加情報2"] }, "selfEvaluation": { "accuracyAssessment": "この判断がどの程度正確だと思うか、その理由", "confidence": "高/中/低", "possibleIssues": ["潜在的な問題点1", "問題点2"], "alternativeInterpretations": ["別の解釈1", "別の解釈2"], } } </フォーマット>
対して、回答生成と評価を分離した「分離型」のアプローチでは、まずポイント1で作ったプロンプトを使って分類を行い、その出力結果をインプットにして評価のプロンプトを実行します。
<指示> あなたはカスタマーサポート問い合わせ分類の評価スペシャリストです。 他のAIが処理した問い合わせ分類結果を検証し、その適切さを評価してください。 </指示> <元データ> 入力データ: 先週購入したプレミアムプランで請求額が説明と違います。 公式サイトでは月額2,980円とありましたが、実際には3,980円が引き落とされています。 できるだけ早く返金対応をお願いします。 </元データ> <AIによる処理結果> { "reasoning": "明示的な返金要求があり、金銭的損失が発生しているため", "category": "返金・キャンセル要求", "priority": "高", "department": "課金・決済チーム" } </AIによる処理結果> <評価基準> - カテゴリの正確性: 問い合わせの主要な意図に合致しているか - 優先度の適切さ: 問題の緊急性や影響度に応じた優先度か - 担当部署の妥当性: 問題解決に最適な部署か - 判断理由の論理性: 判断理由が論理的で一貫しているか </評価基準> <フォーマット> { "evaluation": { "category_correct": true/false, "priority_correct": true/false, "department_correct": true/false, "reasoning_valid": true/false "overall_accuracy": "高/中/低", }, "feedback": { "strengths": ["良い点1", "良い点2"], "weaknesses": ["改善点1", "改善点2"] }, "suggested_corrections": { "category": "修正提案(必要な場合のみ)", "priority": "修正提案(必要な場合のみ)", "department": "修正提案(必要な場合のみ)" }, } </フォーマット>
このように分離型のアプローチを採用することで、一体型でLLM出力させるよりも間違いが減り、精度を向上させることができます。
また、評価専用プロンプトを通じて収集された情報は、プロンプト改善の貴重な材料になります。どのような指示が正確に理解され、どのような指示が見落とされがちかを体系的に把握できるようになったことで、より効果的なプロンプト設計が可能になります。
ポイント3: LLMの逃げ道を作る
判断条件を言語化しきれない部分があることを前提に、LLMに「判断できない」という選択肢を与えるアプローチも重要です。LLMに無理に判断させるのではなく、判断に迷うケースや確信が持てないケースでは、その旨を明示するよう指示することで、強引な判断による誤りを減らすことが可能になります。
以下は、ポイント1のプロンプトを拡張して「逃げ道」を追加した例です。
<指示> あなたはカスタマーサポート問い合わせ分類のプロフェッショナルです。 以下の顧客問い合わせを分析し、適切なカテゴリ、優先度、担当部署に振り分けてください。 分類の際には、以下の点に特に注意してください: - 問い合わせ内に複数の要望/質問が混在する場合は、主要な問題を特定し優先順位をつける - 優先度の判断は主観的になりがちなので、具体的な影響(金銭的損失、サービス停止など)に基づいて判断する - クレームと返金要求は混同しやすいが、明示的な返金要求があるかどうかで区別する - 複数部署が関与する場合は、主要な問題を扱う部署に割り振る - 「できるだけ早く」などの言葉遣いから適切に緊急度を判断する - 顧客の感情的な表現に左右されず、問題の本質を見極める -----------追加部分------------- 重要: 判断に迷う場合や、複数の解釈が可能な場合は、無理に判断せず「判断保留」として、その理由を詳細に記述してください。 確信が持てない判断を強制するよりも、判断保留として人間によるレビューを促すことが重要です。 -----------追加部分おわり------------- </指示> <データ> 入力データ: 先週購入したプレミアムプランで請求額が説明と違います。 公式サイトでは月額2,980円とありましたが、実際には3,980円が引き落とされています。 誠意を見せてもらえますか。 </データ> <作業ステップ> 1. 問い合わせの種類を判定してください。以下から最適なものを選択: - 質問(一般的な問い合わせ) - クレーム(不満・苦情) - 返金・キャンセル要求 - 技術的サポート - 提案・フィードバック - その他 - 判断保留(判断できない場合) 2. 問い合わせの優先度を判定してください。以下から最適なものを選択: - 緊急(24時間以内に対応必要):サービス停止、金銭的損失、法的問題など - 高(48時間以内に対応必要):重要な機能の不具合、課金問題など - 中(3-5営業日以内に対応):一般的な使い方の質問、改善要望など - 低(対応は必要だが急ぎではない):今後の検討項目、情報提供など - 判断保留(判断できない場合) 3. 最適な担当部署を特定してください。以下から最適なものを選択: - 課金・決済チーム:支払い、請求書、返金、価格に関する問題 - 技術サポートチーム:システム障害、バグ、技術的問題 - 製品サポートチーム:使用方法、機能、製品仕様に関する質問 - アカウント管理チーム:アカウント作成・削除、パスワード、個人情報 - コンプライアンスチーム:法律・規制関連、違反報告、セキュリティ問題 - 判断保留(判断できない場合) 4. 問い合わせ内容から明確でない情報があれば、特定してください。 5. 判断に迷った点や、複数の解釈が可能な点を詳細に記述してください。 </作業ステップ> <フォーマット> 以下のようなJSON形式で出力してください。 { "reason": "分類結果の理由や特記事項", "category": "問い合わせの種類 または 判断保留", "priority": "優先度 または 判断保留", "department": "担当部署 または 判断保留", "additionalInfoNeeded": ["必要な追加情報1", "必要な追加情報2"], ------------追加部分------------ "uncertaintyPoints": ["判断に迷った点1", "判断に迷った点2"], "requiresHumanReview": true or false, "humanReviewReason": "人間によるレビューが必要な理由(該当する場合)", ----------追加部分おわり--------- } </フォーマット>
このアプローチの利点として、以下が挙げられます。
- 無理な判断による誤りの低減: LLMが確信を持てないケースを「判断保留」とすることで、誤判断のリスクを減らせます
- 人間とLLMの効果的な役割分担: LLMには得意な領域、人間には人間にしかできない判断があります。「判断保留」の仕組みにより、真に人間の判断が必要なケースを明確にして、人間リソースを効率的に活用できるようになります
- 判断理由の可視化: LLMの確信度や判断理由を明示することで、プロンプト改善の手がかりが得られます
この「LLMの逃げ道を作る」アプローチを導入することで、強引な判断による誤りを減少させ、全体的な正確性の向上が期待できます。特に要求が曖昧で人間でも判断に迷うケースや複合的な問題を含む問い合わせに対して効果的でした。
上記のプロンプト例で、「誠意を見せてもらえますか」という表現がありましたが、明示的な返金要求なのか単なる不満の表明なのか判断が難しいケースです。逃げ道を与えないプロンプトでは強引にどちらかに分類せざるを得なかったところ、「判断保留」という逃げ道を提供することで、LLMが「顧客が具体的に何を求めているのか不明確」と正直に伝えられるようになります。この対策により、無理やり与えられた指示に当てはめようと出力してしまうケースを減らすことができます。
おわりに
- 単純なプロンプトで目検し、判断条件を言語化する: まずは人間の目でエラーパターンを特定し言語化
- 評価プロセスは分離する: 回答生成と評価を別々のプロンプトで行い、それぞれの精度を向上
- LLMの逃げ道を作る: 無理に判断させず、判断に迷うケースでは「判断保留」を許容し、人間が判断を補う
これら3つのポイントから見えてくる本質は、「完璧なプロンプトで全ての問題を解決しようとする」のではなく、 「LLMの限界を認識した上で、課題の検知と対処の仕組みを構築する」ことにあります。LLMによる評価と、そもそもLLMで正確に出力できないものを検出することで、人手による品質担保を限定化しつつ、期待を下回るアウトプットをなくしていくことが重要です。
最後に、本記事がLLMを活用したプロダクト開発に取り組まれている方々にとって、少しでも参考になれば嬉しいです。品質担保の取り組みは地道で表面的には目立ちにくい作業ですが、ユーザー体験と信頼性を左右する重要な要素です。今後もLLM活用の現場からの知見を共有していけたらと思います。
ご覧いただき、ありがとうございました。
エンジニアのみなさん、カジュアル面談させてください
LLM活用に興味があるエンジニアの方、ぜひ弊カンパニーCTOの菊池とカジュアル面談しましょう!