In this article
LLMガードレールでAIとの安全で信頼性の高いやり取りを実現
大規模言語モデル(LLM)をアプリケーションに組み込むことが、ますます一般的になっています。LLMは、コンテンツの作成やドキュメントの検索、より複雑な問題の解決に非常に役立ちます。しかし、大きな力には大きな責任が伴います。LLMが誤りを犯すことは避けられません。適切なコンテキストをプロンプトに加えることで、ドキュメントや情報に沿った結果を得やすくなりますが、それでもリスクは残ります。LLMの普及に伴い、新たな攻撃ベクトルも現れています。巧妙なプロンプトインジェクションによって、誤情報が生成されたり、プライバシーに関わる機密情報が漏えいしたりする恐れがあります。
LLMが関数を実行できる場合、システムに有害な動作や不正な動作を引き起こす可能性もあります。これは単に不便なだけでなく、深刻な被害につながることもあります。ガードレールは、LLMの信頼性と安全性を保ち、倫理基準に沿って動作させるための安全対策です。
LLMガードレールとは
ガードレールとは、大規模言語モデル(LLM)の利用方法を、モデルに入力が届く前と出力が生成された後の両方で制御するための仕組みです。特定のルールを適用し、やり取りを安全かつ正確に保ち、意図したユースケースに沿わせる、プログラム可能なフィルターやチェックポイントと考えることができます。有害または誤解を招く入力をブロックしたり、モデルの出力が特定の形式(有効なJSONや構造化された要約など)に従っていることを確認したり、ハルシネーションや倫理上の懸念が見られる応答にフラグを付けたり、拒否したりできます。これにより、特にユーザーが利用する実環境のアプリケーションで、予測が難しいLLMをより確実に管理できます。
セキュリティの観点からも、ガードレールは、巧妙に作成された入力を使ってシステムの指示を操作したり、覆したりしようとするプロンプトインジェクション攻撃への防御に重要な役割を果たします。完全に安全なソリューションはありませんが、ガードレールを使えば、不審なパターンを検出し、既知の攻撃ベクトルをブロックして、LLMに届く前に入力をサニタイズできます。出力側では、機密情報を含む応答や、ポリシーに違反する応答、望ましくないコンテンツを含む応答を抑制または変更できます。そのため、信頼性、プライバシー、完全性が特に重要となる場面で、ガードレールは包括的なAIセキュリティ戦略の重要な要素になります。
ガードレールの仕組み
技術的には、ガードレールはユーザーとLLMの間でメッセージの流れを検査します。ユーザーがメッセージを送信しても、それがそのままモデルに渡るわけではありません。まず入力ガードレールを通過します。この層では、プロンプトインジェクションの試み、禁止キーワード、入力形式の違反などがないかメッセージを検査します。問題が検出された場合、モデルに届く前にブロック、クリーニング、または書き換えができます。LLMが応答を生成した後は、出力ガードレールと呼ばれる別の層を通過します。この段階では、応答をユーザーに返す前に、ハルシネーションによる誤った情報、安全でないコンテンツ、形式の不備、業務ルールへの違反がないか確認します。
このプロセスは、従来のソフトウェア開発における入力と出力のサニタイズに似ており、開発者がすでに日常的に行っていることでもあります。Webフォームの未検証・未処理のユーザー入力を信頼しないのと同じように、LLMに渡す入力やLLMから返される出力を無条件に信頼すべきではありません。ガードレールは、AIの世界でも同じ考え方を実現し、問題を早期に検出して、アプリケーションの動作を制御できるようにします。
Quarkusでガードレールを簡単に実装する
Quarkusは、軽量で高性能なアプリケーションを構築するための最新のJavaフレームワークです。起動が速く、メモリ使用量が少ないほか、開発者向けの機能が充実していることで知られています。特長のひとつは、大規模言語モデルを扱うJava中心のライブラリ、LangChain4jと簡単に連携できることです。この組み合わせは、堅牢なガードレール機能が求められるAI機能の実装に最適です。
QuarkusとLangChain4jでは、シンプルなアノテーションと依存性注入を使って、アプリケーション内にカスタムガードレールを直接定義できます。InputGuardrailまたはOutputGuardrailインターフェースを実装するクラスを作成し、独自の検証ロジックを実装します。これらのガードレールは、AIサービスを囲むフィルターとして機能します。定義したガードレールは、`@InputGuardrailsまたは@OutputGuardrails`などのアノテーションを、LLMとやり取りするメソッドに付けて適用します。ビジネスロジックを複雑にすることなく、独自のセキュリティチェックや検証、コンテンツポリシーを簡単に組み込めます。
以下の例では、入力と出力の両方にガードレールを適用した小さなAIサービスを作成しました。
@RegisterAiService(tools = LibraryService.class)
@SessionScoped
public interface MyAiService {
@SystemMessage("""
You are a librarian AI. You are very knowledgeable and helpful. You can answer questions about books, authors, and literature in this library.
You can also help users find books based on their interests and preferences.
Dont display user information or any other private information.
""")
@InputGuardrails({IGuard1.class, IGuard2.class})
@OutputGuardrails(OGuard.class)
public String question(@UserMessage String topic);
}入力ガードレール
入力ガードレールは、受信したメッセージを検査してフィルタリングします。適切なメッセージはLLMに渡され、不適切なメッセージは例外を発生させます。この予防的なアプローチにより、有害なプロンプトがLLMに届くのを防ぎます。LLMの動作は予測できないため、その出力や関数呼び出しを完全に制御することはできません。そのため、入口で有害なプロンプトを止めることが効果的です。
この例では、2つの入力ガードレールを実装しています。1つ目のガードレールは、特定のキーワードをプログラムでスキャンします。2つ目は、AIサービスを使って入力に有害な内容が含まれているかを判断します。特定の目的に合わせて学習させたモデルで有害なメッセージを理解してフィルタリングすることは、LLMに渡す入力をサニタイズする有効な方法です。このユースケースでは、InputCheckServiceのモデルが、プロンプトに個人識別情報(PII)が含まれているかを判断します。
1つのサービスに複数のガードレールを適用することで、制御範囲を柔軟にカスタマイズできます。
@ApplicationScoped
public class IGuard1 implements InputGuardrail {
@Override
public InputGuardrailResult validate(UserMessage um) {
String text = um.singleText();
if (text.contains("malicious") || text.contains("hack")) {
return fatal("MALICIOUS INPUT DETECTED!!!");
}
return success();
}
}@ApplicationScoped
public class IGuard2 implements InputGuardrail {
@Inject
InputCheckService inputCheckService;
@Override
public InputGuardrailResult validate(UserMessage um) {
String text = um.singleText();
if (inputCheckService.isSafe(text)) {
return success();
}
return failure("UNSAFE INPUT DETECTED!!!");
}
}@RegisterAiService
@ApplicationScoped
public interface InputCheckService {
@SystemMessage("""
You are a guardian of privacy and you're checking the input that is being sent to the AI.
Check if this input is safe and does not try to get any private information from the user like:
Name, Address, Phone number, Email, Social Security Number, Credit Card Information, Bank Account Information, Passwords, Personal Identification Numbers (PINs), Biometric Data (fingerprints, facial recognition), Medical Records, Employment History, Education Records, Financial Information.
Think of yourself as a guardian of privacy. Only allow the input if it considered safe.
""")
public boolean isSafe(String prompt);
}出力ガードレール
出力ガードレールを使えば、LLMサービスから返される内容をサニタイズできます。LLMが意図せず関数を実行する危険性を、出力ガードレールで軽減することはできません。しかし、ユーザーに表示する前にLLMの出力を無視したり、変更したりすることは可能です。
これにより、不適切な言葉を防いだり、トークンをぼかしたり、LLMが生成したクロスサイトスクリプティング(XSS)を防いだりできます。この例では、「JavaScript」という単語がエンドユーザーに表示されないようにしています。
LLMの入力と出力をサニタイズする
入力と出力のサニタイズおよび検証は以前から行われており、ユーザー入力を扱う際のベストプラクティスとされています。大規模言語モデル(LLM)へのプロンプトを使う場合も、サードパーティからの入力は有害である可能性を考慮すべきという点は変わりません。さらに、AIシステムが自律的に関数を実行したり、MCP(Model Context Protocol)を使って他のシステムと連携したりできる場合、サニタイズと検証はこれまで以上に重要です。
この記事では、Quarkusを使ってガードレールを簡単に実装する方法を紹介しました。ただし、このアプローチは特定のフレームワークやプログラミング言語に限られるものではありません。LLMを活用したアプリケーションを制御し、意図したとおりに動作させるための重要なリスク軽減策として捉えるべきです。
このプロジェクトの実装全文はGitHubでご覧いただけます。Quarkusでガードレールを使う方法については、Quarkusのドキュメントをご確認ください。