プロンプトエンジニアリングパターン
包括的な迅速なエンジニアリングガイド (英語) に基づくプロンプトエンジニアリング技術の実践的な実装。このガイドでは、効果的なプロンプトエンジニアリングの理論、原則、パターンを網羅しています。また、ここでは、Spring AI の流れるような ChatClient API を用いて、これらの概念を実用的な Java コードに変換する方法を示します。この記事で使用されているデモソースコードは、プロンプトエンジニアリングパターンの例 [GitHub] (英語) から入手できます。
1. 構成
設定セクションでは、Spring AI を使用して大規模言語モデル(LLM)をセットアップおよび調整する方法について説明します。ユースケースに適した LLM プロバイダーの選択と、モデル出力の品質、スタイル、フォーマットを制御する重要な生成パラメーターの設定について説明します。
LLM プロバイダーの選択
迅速なエンジニアリングのために、まずモデルを選択してください。Spring AI は複数の LLM プロバイダー(OpenAI、Anthropic、Google、Vertex AI、AWS Bedrock、Ollama など)をサポートしているため、アプリケーションコードを変更することなくプロバイダーを切り替えることができます。設定を更新するだけで済みます。選択したスターター依存関係 spring-ai-starter-model-<MODEL-PROVIDER-NAME>
を追加するだけです。例: Anthropic、Claude API を有効にする方法は次のとおりです。
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-anthropic</artifactId>
</dependency>
LLM モデル名は次のように指定できます。
.options(ChatOptions.builder()
.model("claude-3-7-sonnet-latest") // Use Anthropic's Claude model
.build())
各モデルを有効にするための詳細情報については、リファレンスドキュメントを参照してください。
LLM 出力構成
プロンプトエンジニアリングの手法を詳しく見ていく前に、LLM の出力動作を設定する方法を理解することが重要です。Spring AI は、ChatOptions ビルダーを通じて生成のさまざまな側面を制御できる複数の設定オプションを提供しています。
すべての構成は、以下の例に示すようにプログラムで適用することも、起動時に Spring アプリケーションプロパティを通じて適用することもできます。
温度
温度は、モデルのレスポンスのランダム性または「創造性」を制御します。
下限値 (0.0-0.3) : より明確で焦点を絞った回答。事実に関する質問、分類、一貫性が重要なタスクに適しています。
中程度の値 (0.4-0.7) : 決定論と創造性のバランスが取れています。一般的なユースケースに適しています。
より高い価値 (0.8-1.0) : より創造的で多様性に富み、意外性のある回答が得られます。クリエイティブライティング、ブレインストーミング、多様な選択肢の創出に最適です。
.options(ChatOptions.builder()
.temperature(0.1) // Very deterministic output
.build())
さまざまな技術がさまざまな温度設定から恩恵を受けるため、温度を理解することは迅速なエンジニアリングにとって重要です。
出力長 (MaxTokens)
maxTokens
パラメーターは、モデルがレスポンスで生成できるトークン (単語の断片) の数を制限します。
低い値 (5-25) : 単語、短いフレーズ、分類ラベルの場合。
中程度の値 (50-500) : 段落または短い説明用。
高い値 (1000+) : 長い形式のコンテンツ、ストーリー、複雑な説明に適しています。
.options(ChatOptions.builder()
.maxTokens(250) // Medium-length response
.build())
不必要な冗長性なしに完全なレスポンスを確実に得るためには、適切な出力の長さを設定することが重要です。
サンプリングコントロール (トップ K とトップ P)
これらのパラメーターを使用すると、生成中のトークン選択プロセスを細かく制御できます。
トップ K : トークンの選択を、最も可能性の高い K 個のトークンに制限します。値が大きいほど(例: 40-50)、多様性が高まります。
トップ P (核サンプル採取) : 累積確率が P を超えるトークンの最小セットから動的に選択します。0.8-0.95 のような値が一般的です。
.options(ChatOptions.builder()
.topK(40) // Consider only the top 40 tokens
.topP(0.8) // Sample from tokens that cover 80% of probability mass
.build())
これらのサンプリング制御は温度と連動してレスポンス特性を形成します。
構造化された回答形式
Spring AI では、プレーンテキストレスポンス (.content()
を使用) に加えて、.entity()
メソッドを使用して LLM レスポンスを Java オブジェクトに直接マップすることが簡単になります。
enum Sentiment {
POSITIVE, NEUTRAL, NEGATIVE
}
Sentiment result = chatClient.prompt("...")
.call()
.entity(Sentiment.class);
この機能は、モデルに構造化データを返すように指示するシステムプロンプトと組み合わせると特に強力になります。
モデル固有のオプション
ポータブルな ChatOptions
は、異なる LLM プロバイダ間で一貫したインターフェースを提供しますが、Spring AI は、プロバイダ固有の機能と設定を公開するモデル固有のオプションクラスも提供しています。これらのモデル固有のオプションにより、各 LLM プロバイダ独自の機能を活用できます。
// Using OpenAI-specific options
OpenAiChatOptions openAiOptions = OpenAiChatOptions.builder()
.model("gpt-4o")
.temperature(0.2)
.frequencyPenalty(0.5) // OpenAI-specific parameter
.presencePenalty(0.3) // OpenAI-specific parameter
.responseFormat(new ResponseFormat("json_object")) // OpenAI-specific JSON mode
.seed(42) // OpenAI-specific deterministic generation
.build();
String result = chatClient.prompt("...")
.options(openAiOptions)
.call()
.content();
// Using Anthropic-specific options
AnthropicChatOptions anthropicOptions = AnthropicChatOptions.builder()
.model("claude-3-7-sonnet-latest")
.temperature(0.2)
.topK(40) // Anthropic-specific parameter
.thinking(AnthropicApi.ThinkingType.ENABLED, 1000) // Anthropic-specific thinking configuration
.build();
String result = chatClient.prompt("...")
.options(anthropicOptions)
.call()
.content();
各モデルプロバイダは、共通のインターフェースを実装しながらプロバイダ固有のパラメーターを公開する独自のチャットオプション実装(例: OpenAiChatOptions
、AnthropicChatOptions
、MistralAiChatOptions
)を持っています。このアプローチにより、プロバイダ間の互換性を確保するためにポータブルオプションを使用したり、特定のプロバイダ固有の機能にアクセスする必要がある場合にモデル固有のオプションを使用したりする柔軟性が得られます。
モデル固有のオプションを使用する場合、コードは特定のプロバイダーに縛られ、移植性が低下することに注意してください。これは、プロバイダー固有の高度な機能へのアクセスと、アプリケーションにおけるプロバイダーの独立性維持との間のトレードオフです。
2. 迅速なエンジニアリング技術
以下の各セクションでは、ガイドに記載されている特定のプロンプトエンジニアリング手法を実装します。「プロンプトエンジニアリング」ガイドとこれらの実装の両方に従うことで、利用可能なプロンプトエンジニアリング手法だけでなく、本番環境の Java アプリケーションに効果的に実装する方法についても深く理解できるようになります。
2.1 ゼロショットプロンプト
ゼロショットプロンプティングとは、AI に例を一切与えずにタスクを実行するよう指示する手法です。このアプローチは、モデルが指示を一から理解し実行する機能をテストします。大規模言語モデルは膨大なテキストコーパスで学習されるため、「変換」「要約」「分類」といったタスクが何を意味するのかを、明示的なデモンストレーションなしに理解することができます。
ゼロショットは、モデルがトレーニング中に類似の例を学習している可能性が高い、あるいはプロンプトの長さを最小限に抑えたい単純なタスクに最適です。ただし、タスクの複雑さや指示の精度によってパフォーマンスは異なる場合があります。
// Implementation of Section 2.1: General prompting / zero shot (page 15)
public void pt_zero_shot(ChatClient chatClient) {
enum Sentiment {
POSITIVE, NEUTRAL, NEGATIVE
}
Sentiment reviewSentiment = chatClient.prompt("""
Classify movie reviews as POSITIVE, NEUTRAL or NEGATIVE.
Review: "Her" is a disturbing study revealing the direction
humanity is headed if AI is allowed to keep evolving,
unchecked. I wish there were more movies like this masterpiece.
Sentiment:
""")
.options(ChatOptions.builder()
.model("claude-3-7-sonnet-latest")
.temperature(0.1)
.maxTokens(5)
.build())
.call()
.entity(Sentiment.class);
System.out.println("Output: " + reviewSentiment);
}
この例では、例を挙げずにムービーレビューの感情を分類する方法を示します。より決定論的な結果を得るには低温(0.1)を使用し、.entity(Sentiment.class)
を Java 列挙型に直接マッピングすることに注意してください。
リファレンス : Brown, T. B., et al. (2020). 「言語モデルは少数ショット学習者である」arXiv:2005.14165. https://arxiv.org/abs/2005.14165 (英語)
2.2 ワンショットプロンプトとフューショットプロンプト
少数ショットプロンプティングは、モデルに 1 つ以上の例を提供することで、レスポンスのガイドとして役立ちます。これは、特定の出力形式を必要とするタスクで特に役立ちます。モデルに望ましい入力と出力のペアの例を示すことで、モデルはパターンを学習し、明示的なパラメーター更新なしに新しい入力に適用することができます。
ワンショットは単一の例を提供するため、例のコストが高い場合やパターンが比較的単純な場合に便利です。一方、フューショットは複数の例(通常 3 ~ 5 個)を使用することで、モデルがより複雑なタスクのパターンをより深く理解できるようにしたり、正しい出力の様々なバリエーションを示したりできます。
// Implementation of Section 2.2: One-shot & few-shot (page 16)
public void pt_one_shot_few_shots(ChatClient chatClient) {
String pizzaOrder = chatClient.prompt("""
Parse a customer's pizza order into valid JSON
EXAMPLE 1:
I want a small pizza with cheese, tomato sauce, and pepperoni.
JSON Response:
```
{
"size": "small",
"type": "normal",
"ingredients": ["cheese", "tomato sauce", "pepperoni"]
}
```
EXAMPLE 2:
Can I get a large pizza with tomato sauce, basil and mozzarella.
JSON Response:
```
{
"size": "large",
"type": "normal",
"ingredients": ["tomato sauce", "basil", "mozzarella"]
}
```
Now, I would like a large pizza, with the first half cheese and mozzarella.
And the other tomato sauce, ham and pineapple.
""")
.options(ChatOptions.builder()
.model("claude-3-7-sonnet-latest")
.temperature(0.1)
.maxTokens(250)
.build())
.call()
.content();
}
少数のプロンプトは、特定のフォーマットを必要とするタスク、エッジケースの処理、あるいは例がないとタスク定義が曖昧になる可能性があるタスクに特に効果的です。例の質と多様性はパフォーマンスに大きな影響を与えます。
リファレンス : Brown, T. B., et al. (2020). 「言語モデルは少数ショット学習者である」arXiv:2005.14165. https://arxiv.org/abs/2005.14165 (英語)
2.3 システム、コンテキスト、ロールのプロンプト
システムプロンプト
システムプロンプトは、言語モデルの全体的なコンテキストと目的を設定し、モデルが実行すべき「全体像」を定義します。システムプロンプトは、ユーザーの具体的なクエリとは別に、モデルのレスポンスの行動フレームワーク、制約、高レベルのゴールを確立します。
システムプロンプトは、会話全体を通して永続的な「ミッションステートメント」として機能し、出力形式、トーン、倫理的な境界、ロールの定義といったグローバルパラメーターを設定できます。特定のタスクに焦点を当てたユーザープロンプトとは異なり、システムプロンプトはすべてのユーザープロンプトの解釈方法を規定します。
// Implementation of Section 2.3.1: System prompting
public void pt_system_prompting_1(ChatClient chatClient) {
String movieReview = chatClient
.prompt()
.system("Classify movie reviews as positive, neutral or negative. Only return the label in uppercase.")
.user("""
Review: "Her" is a disturbing study revealing the direction
humanity is headed if AI is allowed to keep evolving,
unchecked. It's so disturbing I couldn't watch it.
Sentiment:
""")
.options(ChatOptions.builder()
.model("claude-3-7-sonnet-latest")
.temperature(1.0)
.topK(40)
.topP(0.8)
.maxTokens(5)
.build())
.call()
.content();
}
システムプロンプトは、Spring AI のエンティティマッピング機能と組み合わせると特に強力になります。
// Implementation of Section 2.3.1: System prompting with JSON output
record MovieReviews(Movie[] movie_reviews) {
enum Sentiment {
POSITIVE, NEUTRAL, NEGATIVE
}
record Movie(Sentiment sentiment, String name) {
}
}
MovieReviews movieReviews = chatClient
.prompt()
.system("""
Classify movie reviews as positive, neutral or negative. Return
valid JSON.
""")
.user("""
Review: "Her" is a disturbing study revealing the direction
humanity is headed if AI is allowed to keep evolving,
unchecked. It's so disturbing I couldn't watch it.
JSON Response:
""")
.call()
.entity(MovieReviews.class);
システムプロンプトは、複数ターンの会話、複数のクエリ間での一貫した動作の確保、すべてのレスポンスに適用される JSON 出力などの形式制約の確立に特に役立ちます。
リファレンス : OpenAI. (2022). 「システムメッセージ」https://platform.openai.com/docs/guides/chat/introduction (英語)
ロールプロンプト
ロールプロンプトは、モデルに特定のロールまたはペルソナを演じるよう指示し、コンテンツの生成方法に影響を与えます。モデルに特定のアイデンティティ、専門知識、視点を割り当てることで、レスポンスのスタイル、トーン、深み、構成に影響を与えることができます。
ロールプロンプトは、モデルの様々な専門分野やコミュニケーションスタイルをシミュレートする機能を活用します。一般的なロールには、専門家(例: 「経験豊富なデータサイエンティストです」)、プロフェッショナル(例: 「旅行ガイドとして行動してください」)、文体に関するキャラクター(例: 「シェイクスピアのように説明してください」)などがあります。
// Implementation of Section 2.3.2: Role prompting
public void pt_role_prompting_1(ChatClient chatClient) {
String travelSuggestions = chatClient
.prompt()
.system("""
I want you to act as a travel guide. I will write to you
about my location and you will suggest 3 places to visit near
me. In some cases, I will also give you the type of places I
will visit.
""")
.user("""
My suggestion: "I am in Amsterdam and I want to visit only museums."
Travel Suggestions:
""")
.call()
.content();
}
ロールプロンプトは、スタイル指示によって強化できます。
// Implementation of Section 2.3.2: Role prompting with style instructions
public void pt_role_prompting_2(ChatClient chatClient) {
String humorousTravelSuggestions = chatClient
.prompt()
.system("""
I want you to act as a travel guide. I will write to you about
my location and you will suggest 3 places to visit near me in
a humorous style.
""")
.user("""
My suggestion: "I am in Amsterdam and I want to visit only museums."
Travel Suggestions:
""")
.call()
.content();
}
この手法は、専門的なドメイン知識に特に効果的であり、レスポンス全体で一貫したトーンを実現し、ユーザーとのより魅力的でパーソナライズされたやり取りを実現します。
リファレンス : Shanahan, M., et al. (2023). 「大規模言語モデルを用いたロールプレイ」arXiv:2305.16367. https://arxiv.org/abs/2305.16367 (英語)
コンテキストプロンプト
コンテキストプロンプトは、コンテキストパラメーターを渡すことで、モデルに追加の背景情報を提供します。この手法により、モデルは特定の状況をより深く理解できるようになり、メインの指示を煩雑にすることなく、より適切でカスタマイズされたレスポンスが可能になります。
コンテキスト情報を提供することで、モデルは現在のクエリに関連する特定のドメイン、オーディエンス、制約、背景情報を理解できるようになります。これにより、より正確で関連性が高く、適切に構成されたレスポンスが得られます。
// Implementation of Section 2.3.3: Contextual prompting
public void pt_contextual_prompting(ChatClient chatClient) {
String articleSuggestions = chatClient
.prompt()
.user(u -> u.text("""
Suggest 3 topics to write an article about with a few lines of
description of what this article should contain.
Context: {context}
""")
.param("context", "You are writing for a blog about retro 80's arcade video games."))
.call()
.content();
}
Spring AI は、param() メソッドを用いてコンテキスト変数を挿入することで、コンテキストプロンプトをクリーンに実現します。この手法は、モデルが特定のドメイン知識を必要とする場合、特定の対象者やシナリオに合わせてレスポンスを調整する場合、レスポンスが特定の制約や要件に準拠していることを確認する場合に特に有効です。
リファレンス : Liu, P., et al. (2021). 「GPT-3 に適した文脈内事例とは?」 arXiv:2101.06804. https://arxiv.org/abs/2101.06804 (英語)
2.4 ステップバックプロンプト
ステップバックプロンプティングは、まず背景知識を獲得することで、複雑なリクエストをより単純なステップに分解します。この手法は、モデルがまず目の前の質問から「一歩下がって」、より広い文脈、基本原則、あるいは問題に関連する一般的な知識を考慮した上で、具体的な質問に取り組むことを促します。
複雑な問題をより管理しやすいコンポーネントに分解し、まず基礎知識を確立することで、モデルは難しい質問に対してより正確な回答を提供できます。
// Implementation of Section 2.4: Step-back prompting
public void pt_step_back_prompting(ChatClient.Builder chatClientBuilder) {
// Set common options for the chat client
var chatClient = chatClientBuilder
.defaultOptions(ChatOptions.builder()
.model("claude-3-7-sonnet-latest")
.temperature(1.0)
.topK(40)
.topP(0.8)
.maxTokens(1024)
.build())
.build();
// First get high-level concepts
String stepBack = chatClient
.prompt("""
Based on popular first-person shooter action games, what are
5 fictional key settings that contribute to a challenging and
engaging level storyline in a first-person shooter video game?
""")
.call()
.content();
// Then use those concepts in the main task
String story = chatClient
.prompt()
.user(u -> u.text("""
Write a one paragraph storyline for a new level of a first-
person shooter video game that is challenging and engaging.
Context: {step-back}
""")
.param("step-back", stepBack))
.call()
.content();
}
ステップバックプロンプトは、複雑な推論タスク、専門的なドメイン知識を必要とする問題、即時の回答ではなくより包括的で思慮深いレスポンスが必要な場合に特に効果的です。
リファレンス : Zheng, Z., et al. (2023). 「一歩下がって: 大規模言語モデルにおける抽象化による推論の喚起。arXiv:2310.06117。https://arxiv.org/abs/2310.06117 (英語)
2.5 思考のチェーン (CoT)
思考促進のチェーンは、モデルが問題を段階的に推論することを促し、複雑な推論タスクの精度を向上させます。モデルに作業過程を示したり、論理的なステップで問題を考察するよう明示的に指示することで、多段階的な推論を必要とするタスクのパフォーマンスを劇的に向上させることができます。
CoT は、人間が複雑な問題を解決するのと同様に、モデルが最終的な答えを出す前に中間推論ステップを生成するように促すことで機能します。これにより、モデルの思考プロセスが明確化され、より正確な結論に到達できます。
// Implementation of Section 2.5: Chain of Thought (CoT) - Zero-shot approach
public void pt_chain_of_thought_zero_shot(ChatClient chatClient) {
String output = chatClient
.prompt("""
When I was 3 years old, my partner was 3 times my age. Now,
I am 20 years old. How old is my partner?
Let's think step by step.
""")
.call()
.content();
}
// Implementation of Section 2.5: Chain of Thought (CoT) - Few-shot approach
public void pt_chain_of_thought_singleshot_fewshots(ChatClient chatClient) {
String output = chatClient
.prompt("""
Q: When my brother was 2 years old, I was double his age. Now
I am 40 years old. How old is my brother? Let's think step
by step.
A: When my brother was 2 years, I was 2 * 2 = 4 years old.
That's an age difference of 2 years and I am older. Now I am 40
years old, so my brother is 40 - 2 = 38 years old. The answer
is 38.
Q: When I was 3 years old, my partner was 3 times my age. Now,
I am 20 years old. How old is my partner? Let's think step
by step.
A:
""")
.call()
.content();
}
「段階的に考えてみましょう」というキーフレーズは、モデルが推論プロセスを示すきっかけとなります。CoT は、数学の問題、論理的推論の課題、複数段階の推論を必要とするあらゆる問題で特に役立ちます。中間段階の推論を明示することで、エラーを減らすのに役立ちます。
リファレンス : Wei, J., et al. (2022). 「思考連鎖プロンプティングは大規模言語モデルにおける推論を引き出す」arXiv:2201.11903. https://arxiv.org/abs/2201.11903 (英語)
2.6 自己一貫性
自己一貫性とは、モデルを複数回実行し、結果を集約することでより信頼性の高い解を得ることです。この手法は、同じ問題に対して多様な推論パスをサンプリングし、多数決によって最も一貫性のある解を選択することで、LLM 出力のばらつきに対処します。
異なる温度やサンプリング設定で複数の推論パスを生成し、最終的な答えを集約することで、自己一貫性が実現され、複雑な推論タスクにおける精度が向上します。これは本質的に、LLM 出力のためのアンサンブル手法です。
// Implementation of Section 2.6: Self-consistency
public void pt_self_consistency(ChatClient chatClient) {
String email = """
Hi,
I have seen you use Wordpress for your website. A great open
source content management system. I have used it in the past
too. It comes with lots of great user plugins. And it's pretty
easy to set up.
I did notice a bug in the contact form, which happens when
you select the name field. See the attached screenshot of me
entering text in the name field. Notice the JavaScript alert
box that I inv0k3d.
But for the rest it's a great website. I enjoy reading it. Feel
free to leave the bug in the website, because it gives me more
interesting things to read.
Cheers,
Harry the Hacker.
""";
record EmailClassification(Classification classification, String reasoning) {
enum Classification {
IMPORTANT, NOT_IMPORTANT
}
}
int importantCount = 0;
int notImportantCount = 0;
// Run the model 5 times with the same input
for (int i = 0; i < 5; i++) {
EmailClassification output = chatClient
.prompt()
.user(u -> u.text("""
Email: {email}
Classify the above email as IMPORTANT or NOT IMPORTANT. Let's
think step by step and explain why.
""")
.param("email", email))
.options(ChatOptions.builder()
.temperature(1.0) // Higher temperature for more variation
.build())
.call()
.entity(EmailClassification.class);
// Count results
if (output.classification() == EmailClassification.Classification.IMPORTANT) {
importantCount++;
} else {
notImportantCount++;
}
}
// Determine the final classification by majority vote
String finalClassification = importantCount > notImportantCount ?
"IMPORTANT" : "NOT IMPORTANT";
}
自己一貫性は、特に重要な意思決定、複雑な推論タスク、単一の回答では得られないより確信度の高い回答が必要な場合に有効です。ただし、そのトレードオフとして、複数の API 呼び出しによる計算コストとレイテンシの増加が挙げられます。
リファレンス : 王 X 他 (2022). 「自己一貫性は言語モデルにおける思考推論のチェーンを改善します。」arXiv:2203.11171. https://arxiv.org/abs/2203.11171 (英語)
2.7 思考の木 (トット)
思考のツリー(ToT)は、複数の推論パスを同時に探索することでチェーンの思考を継承した高度な推論フレームワークです。ToT は問題解決を探索プロセスとして扱い、モデルが様々な中間ステップを生成し、それらの可能性を評価し、最も有望なパスを探索します。
この手法は、複数のアプローチが可能な複雑な問題や、最適なパスを見つける前にさまざまな代替案を検討する必要があるソリューションの場合に特に強力です。
オリジナルの "Prompt Engineering" ガイドでは、ToT の実装例が提供されていません。これはおそらく、ToT の複雑さが原因と思われます。以下は、コアコンセプトを示す簡略化された例です。 |
ゲーム解決 ToT の例:
// Implementation of Section 2.7: Tree of Thoughts (ToT) - Game solving example
public void pt_tree_of_thoughts_game(ChatClient chatClient) {
// Step 1: Generate multiple initial moves
String initialMoves = chatClient
.prompt("""
You are playing a game of chess. The board is in the starting position.
Generate 3 different possible opening moves. For each move:
1. Describe the move in algebraic notation
2. Explain the strategic thinking behind this move
3. Rate the move's strength from 1-10
""")
.options(ChatOptions.builder()
.temperature(0.7)
.build())
.call()
.content();
// Step 2: Evaluate and select the most promising move
String bestMove = chatClient
.prompt()
.user(u -> u.text("""
Analyze these opening moves and select the strongest one:
{moves}
Explain your reasoning step by step, considering:
1. Position control
2. Development potential
3. Long-term strategic advantage
Then select the single best move.
""").param("moves", initialMoves))
.call()
.content();
// Step 3: Explore future game states from the best move
String gameProjection = chatClient
.prompt()
.user(u -> u.text("""
Based on this selected opening move:
{best_move}
Project the next 3 moves for both players. For each potential branch:
1. Describe the move and counter-move
2. Evaluate the resulting position
3. Identify the most promising continuation
Finally, determine the most advantageous sequence of moves.
""").param("best_move", bestMove))
.call()
.content();
}
リファレンス : ヤオ・ S. 他 (2023). 「思考の樹」: 大規模言語モデルによる意図的な問題解決。arXiv:2305.10601。https://arxiv.org/abs/2305.10601 (英語)
2.8 自動プロンプトエンジニアリング
自動プロンプトエンジニアリングは、AI を用いて代替プロンプトを生成・評価します。このメタ技術は、言語モデル自体を活用して、様々なプロンプトのバリエーションを作成、改善、ベンチマークし、特定のタスクに最適な表現を見つけ出します。
APE はプロンプトのバリエーションを体系的に生成・評価することで、特に複雑なタスクにおいて、手動エンジニアリングよりも効果的なプロンプトを見つけることができます。これは、AI を活用して自身のパフォーマンスを向上させる方法です。
// Implementation of Section 2.8: Automatic Prompt Engineering
public void pt_automatic_prompt_engineering(ChatClient chatClient) {
// Generate variants of the same request
String orderVariants = chatClient
.prompt("""
We have a band merchandise t-shirt webshop, and to train a
chatbot we need various ways to order: "One Metallica t-shirt
size S". Generate 10 variants, with the same semantics but keep
the same meaning.
""")
.options(ChatOptions.builder()
.temperature(1.0) // High temperature for creativity
.build())
.call()
.content();
// Evaluate and select the best variant
String output = chatClient
.prompt()
.user(u -> u.text("""
Please perform BLEU (Bilingual Evaluation Understudy) evaluation on the following variants:
----
{variants}
----
Select the instruction candidate with the highest evaluation score.
""").param("variants", orderVariants))
.call()
.content();
}
APE は、本番システムのプロンプトを最適化し、手動プロンプトエンジニアリングが限界に達した困難なタスクに対処し、大規模なプロンプト品質を体系的に向上させるのに特に役立ちます。
リファレンス : 周 Y. 他 (2022). 「大規模言語モデルは人間レベルのプロンプトエンジニアである」arXiv:2211.01910. https://arxiv.org/abs/2211.01910 (英語)
2.9 コードプロンプト
コードプロンプティングとは、コード関連のタスクに特化した技術を指します。これらの技術は、LLM のプログラミング言語の理解と生成機能を活用し、新しいコードの作成、既存コードの説明、課題のデバッグ、言語間の変換を可能にします。
効果的なコードプロンプトには、明確な仕様、適切なコンテキスト(ライブラリ、フレームワーク、スタイルガイドライン)、時には類似のコード例が一般的に含まれます。より確定的な出力を得るには、温度設定は低め(0.1-0.3)に設定する傾向があります。
// Implementation of Section 2.9.1: Prompts for writing code
public void pt_code_prompting_writing_code(ChatClient chatClient) {
String bashScript = chatClient
.prompt("""
Write a code snippet in Bash, which asks for a folder name.
Then it takes the contents of the folder and renames all the
files inside by prepending the name draft to the file name.
""")
.options(ChatOptions.builder()
.temperature(0.1) // Low temperature for deterministic code
.build())
.call()
.content();
}
// Implementation of Section 2.9.2: Prompts for explaining code
public void pt_code_prompting_explaining_code(ChatClient chatClient) {
String code = """
#!/bin/bash
echo "Enter the folder name: "
read folder_name
if [ ! -d "$folder_name" ]; then
echo "Folder does not exist."
exit 1
fi
files=( "$folder_name"/* )
for file in "${files[@]}"; do
new_file_name="draft_$(basename "$file")"
mv "$file" "$new_file_name"
done
echo "Files renamed successfully."
""";
String explanation = chatClient
.prompt()
.user(u -> u.text("""
Explain to me the below Bash code:
```
{code}
```
""").param("code", code))
.call()
.content();
}
// Implementation of Section 2.9.3: Prompts for translating code
public void pt_code_prompting_translating_code(ChatClient chatClient) {
String bashCode = """
#!/bin/bash
echo "Enter the folder name: "
read folder_name
if [ ! -d "$folder_name" ]; then
echo "Folder does not exist."
exit 1
fi
files=( "$folder_name"/* )
for file in "${files[@]}"; do
new_file_name="draft_$(basename "$file")"
mv "$file" "$new_file_name"
done
echo "Files renamed successfully."
""";
String pythonCode = chatClient
.prompt()
.user(u -> u.text("""
Translate the below Bash code to a Python snippet:
{code}
""").param("code", bashCode))
.call()
.content();
}
コードプロンプティングは、自動コードドキュメント作成、プロトタイピング、プログラミング概念の学習、プログラミング言語間の変換において特に役立ちます。Few-Shot Prompting や Chain-of-Thought などの手法と組み合わせることで、その効果はさらに高まります。
リファレンス : Chen, M., et al. (2021). 「コード学習による大規模言語モデルの評価」arXiv:2107.03374. https://arxiv.org/abs/2107.03374 (英語)
結論
Spring AI は、主要なプロンプトエンジニアリング技術をすべて実装するための洗練された Java API を提供します。これらの技術を Spring の強力なエンティティマッピングと流れるような API と組み合わせることで、開発者はクリーンで保守性の高いコードで、洗練された AI 搭載アプリケーションを構築できます。
最も効果的なアプローチは、多くの場合、複数の手法を組み合わせることです。たとえば、システムプロンプトと少数のサンプルを組み合わせたり、思考の連鎖とロールプロンプトを組み合わせたりします。Spring AI の柔軟な API により、これらの組み合わせを簡単に実装できます。
本番アプリケーションの場合は、次の点に注意してください。
異なるパラメーターを持つテストプロンプト (温度、トップ K、トップ P)
重要な意思決定には自己一貫性を考慮する
Spring AI のエンティティマッピングを活用して型安全なレスポンスを実現
コンテキストプロンプトを使用してアプリケーション固有の知識を提供する
これらの技術と Spring AI の強力な抽象化により、一貫性のある高品質の結果を提供する堅牢な AI 搭載アプリケーションを作成できます。
参照
Brown, T. B., et al. (2020). 「言語モデルは少数ショット学習者である」arXiv:2005.14165.
Wei, J., et al. (2022). 「思考連鎖プロンプティングは大規模言語モデルにおける推論を引き出す」arXiv:2201.11903.
Wang, X., et al. (2022). 「自己一貫性は言語モデルにおける思考推論のチェーンを改善します。」arXiv:2203.11171.
Yao, S., et al. (2023). 「思考の樹: 大規模言語モデルを用いた意図的な問題解決」arXiv:2305.10601.
Zhou, Y., et al. (2022). 「大規模言語モデルは人間レベルのプロンプトエンジニアである」arXiv:2211.01910.
Zheng, Z., et al. (2023). 「一歩引いて考える: 大規模言語モデルにおける抽象化による推論の喚起」arXiv:2310.06117.
Liu, P., et al. (2021). 「GPT-3 に適したコンテキスト内事例とは?」arXiv:2101.06804.
Shanahan, M., et al. (2023). 「大規模言語モデルを用いたロールプレイ」arXiv:2305.16367.
Chen, M., et al. (2021). 「コードで学習した大規模言語モデルの評価」arXiv:2107.03374.