2025.10.01
DSPyを用いて、プロンプトチューニングから脱出し、プロンプトプログラミングへ
こんにちは、グループ研究開発本部・AI研究開発室のA.Zです
LLMのアプリケーション開発するときに、プロンプト周りの生成やチューニングは結構大変、モデル変更するとまたプロンプト再チューニングが必要、プロンプトの微調整で結果が変わるなどの困っていることがありませんか。今回の記事その問題で、少しでも改善できるプロンプトプログラミングというアプローチを紹介します。
TL;DR
- DSPyというフレームワークを利用し、プロンプトプログラミング実施
- 自動的なプロンプトの最適化例を試した
- MIPROV2のプロンプト最適化の仕組みを簡単に解説
はじめに
近年、大規模言語モデル(LLM)の急速な発展により、AI アプリケーションの開発手法も大きく変化しています。LLMのアプリケーションに多くの開発者がプロンプトエンジニアリングに取り組んでいます。
しかし、従来のプロンプトチューニング手法には多くの課題があります:
- 手動での試行錯誤:効果的なプロンプトを見つけるために、無数の組み合わせを手動でテストする必要がある
- 再現性の欠如:同じプロンプトでも、モデルやバージョンが変わると結果が大きく異なる場合がある
- スケーラビリティの問題:複雑なアプリケーションでは、複数のプロンプトを管理・調整することが困難
- 評価の困難さ:プロンプトの品質を客観的に測定し、改善することが難しい
これらの課題を解決するために、本記事では DSPy というフレームワークを紹介します。DSPyは、手動のプロンプトチューニングから脱却し、より体系的で自動化されたアプローチを提供します。実際のコード例を通じて、従来の手法との違いと、DSPyがもたらす革新的な開発体験を探っていきます。
Dspyとは
DSPy(Declarative Self-improving Python)は、スタンフォード大学のNLPグループによって開発された革新的なフレームワークです。従来の手動プロンプトエンジニアリングの限界を克服し、大規模言語モデル(LLM)を使ったアプリケーション開発を根本的に変革することを目的としています。
DSPyの核心的なアイデアは、プロンプトを手動で作成・調整する代わりに、プログラムの論理構造を宣言的に定義し、その後自動的に最適化するというアプローチです。これにより、開発者は「何をしたいか」に集中でき、「どのようにプロンプトを書くか」という作業から解放されます。
例えば、質問応答システムを構築する際、従来の方法では「あなたは親切なアシスタントです。以下のコンテキストを読んで…」といった詳細なプロンプトを手動で作成し、試行錯誤を繰り返す必要がありました。しかし、DSPyでは、入力(質問とコンテキスト)と出力(回答)の関係を抽象的に定義するだけで、フレームワークが自動的に最適なプロンプトを生成してくれます。
なぜプロンプトチューニングからプロンプトプログラミングへ
現在のLLM進化はものすごく早く、次々の高性能のモデルが出てきます。それに伴い、開発したAIアプリケーションも改修・変更しないといけない場面も出てきます。 例えば、昨年にリリースされたモデルは、今年になって、Deprecatedになり、利用できなくなったりとうこともあります。その場合は、AIアプリケーションはサポートされているモデルに移行する必要があります。該当のAIアプリケーションはLLMのプロンプトに依存する場合は、再度プロンプトのチューニングは行う必要です。
プロンプトチューニングの課題としては
- スケーラビリティの問題:手動プロンプトチューニングは時間がかかり、複雑なシステムでは管理が困難
- 再現性の問題:プロンプトの微調整が他の部分に予期しない影響を与える
- モデル依存性:異なるLLMに対して同じプロンプトが機能しない場合がある
上記を解決するために、予測可能と再現可能と安定的ななアプローチが必要です。そこで、プロンプトのプログラミングというアプローチの出番になります。 プロンプトプログラミングはプロンプトの作成・最適化自体はプログラムの形式で行い、LLMアプリケーションの開発はより体系的で予測可能なプロセスになり、従来の機械学習パイプラインと同様の安定性と品質管理を持つことができるようになります。
DSPyの特徴
DSPyは、以下の3の重要なコンポネントで構成されています。
- シグネチャ(Signatures)
- モジュール(Modules)
- プロンプトオプティマイザ
それそれのコンポネントの詳細と役割は次のとおりです。
1. シグネチャ(Signatures):タスクや意図の宣伝
シグネチャは基本的にDSPyの中に一番重要なコンポネントです。こちらで、LLMから、何を求めたいか具体的に宣言するという方法です。例えば、どんなインプットにLLMにわたすか、そして、LLMからどんな回答期待するかこちらのコンポネントで細かく宣言します。
具体的な例としては以下の質問・回答のタスクの例です。
import dspy class BasicQA(dspy.Signature): """Answer questions based on context.""" context = dspy.InputField(desc="Relevant facts or passages.") question = dspy.InputField(desc="The user's question.") answer = dspy.OutputField(desc="A concise answer, often a single phrase.")
このコードが何をしていて、そしてより重要なことに、何をしていないかを見てください。
- 契約を定義する: このシグネチャを使用するモジュールは、contextとquestionを受け入れ、answerを生成しなければなりません。
- 意味的なガイダンスを提供する: docstringやdescフィールドは、人間が読むためだけのものではありません。DSPyのオプティマイザは、このメタデータを使用して、より良く、より説明的なプロンプトを作成できます。例えば、最終的なプロンプトの指示は、シグネチャのdocstringから「コンテキストに基づいて質問に答えます。」のように導出されるかもしれません。
- プロンプトのテンプレートを含まない: f-stringや、「あなたは親切なアシスタントです…」といった指示、手書きの例は一切ありません。これは純粋な意図の宣言です
他の例はEmailのタイプの分類と緊急度の分類になります:
import dspy import os from typing import List, Optional, Literal from datetime import datetime from pydantic import BaseModel from enum import Enum class EmailType(str, Enum): ORDER_CONFIRMATION = "order_confirmation" SUPPORT_REQUEST = "support_request" MEETING_INVITATION = "meeting_invitation" NEWSLETTER = "newsletter" PROMOTIONAL = "promotional" INVOICE = "invoice" SHIPPING_NOTIFICATION = "shipping_notification" OTHER = "other" class UrgencyLevel(str, Enum): LOW = "low" MEDIUM = "medium" HIGH = "high" CRITICAL = "critical" class ClassifyEmail(dspy.Signature): """Classify the type and urgency of an email based on its content.""" email_subject: str = dspy.InputField(desc="The subject line of the email") email_body: str = dspy.InputField(desc="The main content of the email") sender: str = dspy.InputField(desc="Email sender information") email_type: EmailType = dspy.OutputField(desc="The classified type of email") urgency: UrgencyLevel = dspy.OutputField(desc="The urgency level of the email") reasoning: str = dspy.OutputField(desc="Brief explanation of the classification")
上記のシグネチャでは、
- inputの定義は3のフィールド(email_subject,email_body,sender)を利用しています。
- outputの定義は以下の3のフィールドを使用しています。
- email_type:分類されたメールの種類。分類クラスは自動的にType Annotationで宣言されたEmailTypeの種類中に、分類されています。
- urgency:緊急度の分類。こちらのフィールども自動的に的に、Type Annotationで宣言されたUrgencyLevelの種類中に、分類されています。
- reasoning:分類された結果の裏付けの理由。
上記のように、様々なタスクはjsonの定義やテキストベースのプロンプトで管理されるではなく、ちゃんとコードとして、管理するができます。複雑なタスクや大希望のシステムでは、向いていると思います。 より、管理しやすく、柔軟性が高く、運用・開発にも様々ことの効率できます。 また、こちらの定義することで、DSPyの目玉機能(Prompt Optimizer)で、モデル依存せずに、自動的に最適化できるので、モデルが変わって、プロンプト周りの大変なチューニングはほとんど不要になるかと思います。
2. モジュール:プログラムの構成要素
シグネチャが型定義であるとすれば、モジュールはプログラムの実行可能なコンポーネントです。これらは状態を持ち、学習可能なオブジェクトであり、DSPyのモジュールはシグネチャを受け取り、それを使ってLLMを呼び出します。
最も基礎的なモジュールはdspy.Predict
のモジュールです。これは、シグネチャを使ってLLMから予測を得る最も簡単な方法です。具体的な例は以下のとおりです
# 上記のシグネチャの例から続ける qa_predictor = dspy.Predict(BasicQA) # サンプルデータで実行してみる context_data = "エッフェル塔は1889年に完成し、フランスのパリに位置しています。" question_data = "エッフェル塔が完成したのはいつですか?" prediction = qa_predictor(context=context_data, question=question_data) print(prediction.answer)
上記のコードはBasicQAシグネチャに基づいて、基本的なゼロショットプロンプトを生成します。LLMに何が送られたかを調べると、次のようなものになっているでしょう。
Answer questions based on context. --- Follow the following format. Context: Relevant facts or passages. Question: The user's question. Answer: A concise answer, often a single phrase. --- Context: The Eiffel Tower was completed in 1889 and is located in Paris, France. Question: When was the Eiffel Tower finished? Answer:
DSPyは、いくつかのデフォルトのモジュールが用意されています。一般的によく利用されるモジュールは以下です。
- dspy.Predict: 最もシンプルなモジュールで、直接的な入力→出力の予測を行います。
- dspy.ChainOfThought: LLMに「ステップバイステップで考える」よう指示し、複雑な推論タスクのパフォーマンスを向上させます。
- dspy.ReAct: ReActエージェントフレームワークを実装し、モデルが思考、行動、観察のループの中でツール(検索エンジンなど)を使用できるようにします。
- dspy.MultiChainComparison: 複数の推論チェーンを生成し、それらを比較して最終的により信頼性の高い回答を生成します。
上記の基本モジュールで、要件に満たせない場合は、複雑モジュールで組み合わせて新規モジュールも作成できます。また、追加ロジックなどが必要であればdspy.Module
を継承する独自のクラスを作成し、そのforwardメソッドで独自のプログラムを実施王し、カスタムロジックのモジュールができます。
3. プロンプトオプティマイザ(テレプロンプト)
こちらはDSPyの目玉機能になります。プロンプトオプティマイザは、最適化されていないDSPyプログラム、少数のトレーニング例、そしてパフォーマンス指標を受け取り、プログラム内のモジュールのプロンプトまたはモデルの重みを自動的に最適化する機能です。
このプロセスはコンパイルと呼ばれます。開発者は高レベルのロジックで定義されたプログラムを実装し、LLMのための低レベルで高度に特化した一連の命令にコンパイルしているのです。各オプティマイザの仕組みは異なりますが、次のパーツで、一つのオプティマイザ(MIPRO-V2)の仕組みを詳細説明します。
DSPyの中に、いくつかオプティマイザが用意され、一般的によく使われているオプティマイザは以下です。
- BootstrapFewShot: トレーニングデータのサブセットをサンプリングしてフューショット例として使用し、それらを評価して最良のものを選択します。
- MIPRO/MIPRO-V2 (Multi-prompt Instruction-driven Prompt Optimization): フューショット例を生成するだけでなく、複数のプロンプト提案とブートストラップを用いてプロンプト内の指示自体も改良する、より高度なオプティマイザです。
この3のコンポネント(シグネチャ、モジュール、プロンプトオプティマイザ)の構成で、プロンプトやLLMのタスク周り、より明確にコード化できます。シグネチャでタスクのロジックを定義し、強力なモジュールを使ってそのロジックを組み立て、最後にオプティマイザを使って、特定のタスクとLLMに対してそのロジックの可能な限り最高の実装を自動的に見つけ出すのです。
DSPyの応用例
DSPy自体は様々な応用例がありますが、今回簡単に自動的にプロンプト最適化を簡単に紹介します
自動的のプロンプトのファインチューニング
タスク概要: 非構造化テキストから人の名前とメールアドレスを抽出できるプログラムを作成。テキストの中から、該当項目(名前かメールアドレス)が見つからない場合は、”N/A”を返す。
今回、上記のタスクを作成するために、以下の手順のステップになります。
1: シグネチャーと評価指標の定義
シグネチャーの形式で、タスクの内容と、入力と出力項目を定義します。また、自動のプロンプトチューニングのために、評価指標。今回の評価指標はLLMで出力が期待する項目の内容と同じかどうか(TrueかFalse)だけを設定します。
import dspy import os class ExtractContactInfo(dspy.Signature): """Extract the person's first name, last name, and email from a given text. If the email of the person or the name of the person is not found, return 'N/A'.""" text = dspy.InputField(desc="Unstructured text containing contact information.") first_name = dspy.OutputField(desc="The first name of the person.") last_name = dspy.OutputField(desc="The last name of the person.") email = dspy.OutputField(desc="The email address of the person.") def extraction_metric(gold, pred, trace=None): """Checks if all three fields are extracted correctly.""" return (gold.first_name.lower() == pred.first_name.lower() and gold.last_name.lower() == pred.last_name.lower() and gold.email.lower() == pred.email.lower())
2: 学習データを用意する
プロンプトオプティマイザを利用するときに、高品質のサンプルデータが必要です。今回は簡単な例なので、数件のデータを用意します。
train_examples = [ dspy.Example( text="You can reach Jane Doe at [email protected] for further questions.", first_name="Jane", last_name="Doe", email="[email protected]" ).with_inputs("text"), dspy.Example( text="The main point of contact is John Smith ([email protected]).", first_name="John", last_name="Smith", email="[email protected]" ).with_inputs("text"), dspy.Example( text="For support, email [email protected]. The ticket was assigned to Bob Johnson.", first_name="Bob", last_name="Johnson", email="[email protected]" ).with_inputs("text"), dspy.Example( text="Peter Parker github repository can be cloned from ssh://[email protected]/peterparker/spiderman.git", first_name="Peter", last_name="Parker", email="N/A" ).with_inputs("text"), dspy.Example( text="If you have any questions, please contact the marketing department at [email protected]", first_name="N/A", last_name="N/A", email="[email protected]" ).with_inputs("text") ]
3: プロンプトのオプティマイザの設定と実行
class ContactExtractor(dspy.Module): def __init__(self): super().__init__() self.extractor = dspy.ChainOfThought(ExtractContactInfo) def forward(self, text): return self.extractor(text=text) def run_prompt_optimizer(): # teacher_llm is the powerful model that will be used to create the examples teacher_llm = dspy.LM(model='openai/gpt-5', max_tokens=16000,temperature=1.0) # student_llm is the cheaper model that will be used to run the final, optimized program student_llm = dspy.LM(model='openai/gpt-4o-mini', max_tokens=1000) # set the default LLM to our student model for validation and testing dspy.configure(lm=student_llm) # program_to_optimize is the program that will be optimized student_program = ContactExtractor() # optimizer is the optimizer that will be used to optimize the program #optimizer = BootstrapFewShot(metric=extraction_metric, max_bootstrapped_demos=2) optimizer=MIPROv2(metric=extraction_metric,prompt_model=teacher_llm,task_model=student_llm) # compile the program with the teacher model compiled_program = optimizer.compile(student_program, trainset=train_examples) test_text = "The report was authored by Clark Kent. For questions, contact his supervisor at [email protected]." # test the optimized program with the teacher model prediction = compiled_program(text=test_text) print(f"Extracted First Name: {prediction.first_name}") print(f"Extracted Last Name: {prediction.last_name}") print(f"Extracted Email: {prediction.email}") # Inspect the final, optimized prompt compiled_program.inspect_history(n=1) # test the raw program with the student model raw_prediction = student_program(text=test_text) print(f"Raw Prediction - First Name: {raw_prediction.first_name}") print(f"Raw Prediction - Last Name: {raw_prediction.last_name}") print(f"Raw Prediction - Email: {raw_prediction.email}") # inspect the raw prompt student_program.inspect_history(n=1)
上記のプログラムを実行すると、最適化されたプロンプトと実際に実行した結果は以下です。
最適化されたプロンプト
Your input fields are: 1. `text` (str): Unstructured text containing contact information. Your output fields are: 1. `reasoning` (str): 2. `first_name` (str): The first name of the person. 3. `last_name` (str): The last name of the person. 4. `email` (str): The email address of the person. All interactions will be structured in the following way, with the appropriate values filled in. [[ ## text ## ]] {text} [[ ## reasoning ## ]] {reasoning} [[ ## first_name ## ]] {first_name} [[ ## last_name ## ]] {last_name} [[ ## email ## ]] {email} [[ ## completed ## ]] In adhering to this structure, your objective is: Your extraction feeds an automated incident-response and compliance system. A single mistake could notify the wrong person or miss a legal deadline, causing financial and privacy harm. Work meticulously and avoid guessing. Task: From the input Text, extract exactly one person’s first name, last name, and one email address. Rules for names: - Preserve capitalization exactly as written in the text. - Ignore honorifics/titles and role words (e.g., Mr., Ms., Dr., Prof., CEO). - Do not invent names from the email handle; only use an explicit name present in the text. - First Name = the given name (the first name token after any honorific). - Last Name = the surname. Exclude suffixes (Jr., Sr., III). If particles are present (e.g., de, del, de la, da, dos, van, von, bin, al), include them with the surname when they appear as part of the name. Ignore middle names/initials for output. Rules for email: - Extract a valid email if present. - Normalize by trimming surrounding whitespace, removing any leading “mailto:” and stripping surrounding quotes/brackets/parentheses and any trailing sentence punctuation (e.g., .,;:!?) immediately following the email. - Do not modify internal characters of the email (do not change case, add, or remove characters inside the address). Multiple candidates: - If multiple names/emails appear, select the single person-email pair most clearly associated in the sentence. If unclear, choose the first valid-looking pair in reading order. - If a name or an email is not explicitly present, return N/A for that field (e.g., N/A for both first and last name if only an email appears). Output format (exactly these four lines, in this order, with these exact prefixes): - Reasoning: Let’s think step by step in order to [briefly explain how you identified the name and email]. - First Name: <value or N/A> - Last Name: <value or N/A> - Email: <value or N/A> Do not output anything else. User message: [[ ## text ## ]] The report was authored by Clark Kent. For questions, contact his supervisor at [email protected]. Respond with the corresponding output fields, starting with the field `[[ ## reasoning ## ]]`, then `[[ ## first_name ## ]]`, then `[[ ## last_name ## ]]`, then `[[ ## email ## ]]`, and then ending with the marker for `[[ ## completed ## ]]`. Response: [[ ## reasoning ## ]] Let’s think step by step in order to identify the name and email. The text mentions "Clark Kent" as the author, which gives us the first and last name. The email address provided is "[email protected]", which is clearly associated with the context of the report. Therefore, I will extract "Clark" as the first name, "Kent" as the last name, and "[email protected]" as the email address. [[ ## first_name ## ]] Clark [[ ## last_name ## ]] Kent [[ ## email ## ]] [email protected] [[ ## completed ## ]]
上記の結果は、プロンプト自体が結構変わって、最終的に、様々のルールや条件も追加されました。また、実際の結果も期待通りの結果になっています。 入力自体が少しい曖昧にし、人物の名前と、メールアドレスはマッチしないようにしました。また、文章自体のコンテキストもレポートとしてちゃんと認識し、レポートに対する問い合わせ先はきちんと把握し、適切なコンタクトを抽出することができました。 こちらのケースは、実際の現場にもよくあるケースで、問い合わせ先は個人メールアドレスではなく、別のメールアドレスを使っていました。
一方、最適化されないプロンプトの結果は以下です。
System message: Your input fields are: 1. `text` (str): Unstructured text containing contact information. Your output fields are: 1. `reasoning` (str): 2. `first_name` (str): The first name of the person. 3. `last_name` (str): The last name of the person. 4. `email` (str): The email address of the person. All interactions will be structured in the following way, with the appropriate values filled in. [[ ## text ## ]] {text} [[ ## reasoning ## ]] {reasoning} [[ ## first_name ## ]] {first_name} [[ ## last_name ## ]] {last_name} [[ ## email ## ]] {email} [[ ## completed ## ]] In adhering to this structure, your objective is: Extract the person's first name, last name, and email from a given text. If the email of the person or the name of the person is not found, return 'N/A'. User message: [[ ## text ## ]] The report was authored by Clark Kent. For questions, contact his supervisor at [email protected]. Respond with the corresponding output fields, starting with the field `[[ ## reasoning ## ]]`, then `[[ ## first_name ## ]]`, then `[[ ## last_name ## ]]`, then `[[ ## email ## ]]`, and then ending with the marker for `[[ ## completed ## ]]`. Response: [[ ## reasoning ## ]] The text contains the name "Clark Kent" which can be split into the first name "Clark" and the last name "Kent". Additionally, the email address provided is "[email protected]", but it does not belong to Clark Kent. Since the task requires the extraction of the person's email, and Clark's email is not provided, I will return 'N/A' for the email field. [[ ## first_name ## ]] Clark [[ ## last_name ## ]] Kent [[ ## email ## ]] N/A [[ ## completed ## ]]
上記の最適化されないプロンプトはただの基本的なテンプレートのようなプロンプトで、曖昧なケースがうまく対応できない。もちろん、シグネチャーで、より内容を修正し、チューニングする自体はできますが、それより、エッジケースのサンプル自体を追加し、自動的にプロンプト最適化を回したほうが楽だと思います。
今回使用するMIPROV2のプロンプト最適化は、実際にどのように仕組みになっているか簡単に説明します。
a. Few-Shotsのデータを作成
与えられたサンプルデータから、Bootstrapの手法で、いくつかFew-Shotsのデータセットを作成。 Few-Shotsのデータセットは以下のパーツに構成されています。
- Promptで記載するFew-Shotsのデータ例
- LLMに回答求める評価データ(Validation data)
b. Few-Shotsのデータやサンプルデータから、適切なプロンプトの候補を生成
与えられたサンプルと作成したFew-Shotのデータから、どんなプロンプトが適切化、LLMに生成させる。一般的に、こちらのプロンプト候補を生成するのは、高性能のモデルがよく使われています。今回の例はGPT-5を利用し、生成したプロンプトの候補はタスク用のLLM(コスト面で安いモデル)に与える想定です。
実際に、今回生成された、プロンプトの候補は以下です。
候補1
Extract the person's first name, last name, and email from a given text. If the email of the person or the name of the person is not found, return 'N/A'.
候補2
You are given a single sentence of unstructured text that mentions exactly one person and an email address. Extract that person’s First Name, Last Name, and Email, returning a short reasoning trace and the three fields. Follow these rules strictly: Output format - Return exactly four lines, using these exact prefixes: - Reasoning: Let's think step by step in order to <your concise reasoning here> - First Name: <value or N/A> - Last Name: <value or N/A> - Email: <value or N/A> - Do not include any extra lines, labels, bullets, or commentary. Do not add quotes around values. Name extraction - Identify the human person’s name explicitly written in the text. Ignore titles and suffixes (e.g., Mr., Ms., Dr., Prof., Jr., Sr., III) and any enclosing punctuation near the name. - Preserve capitalization and diacritics exactly as written in the text. Trim surrounding punctuation and whitespace; keep internal characters like hyphens and apostrophes (e.g., O’Neil, Smith-Jones). - If the name appears as “Last, First”, reorder into First Name and Last Name accordingly. - If middle names or initials are present, include only the first given name as First Name and exclude middle names/initials from both fields. - If only one part of the name is present, fill the present part and set the missing part to N/A. - Do not infer a name from the email handle; use only names explicitly present in the text. - When multiple names are mentioned, choose the single person clearly associated with the email; if association is ambiguous, set the name fields to N/A. Email extraction and normalization - Extract one valid email address. Prefer the one adjacent to or clearly associated with the extracted person; if unclear, choose the first valid email address in the text. - Normalize by removing surrounding quotes or brackets and stripping any trailing punctuation or closing delimiters that are not part of the address (e.g., .,;:!?) and ), ], >. Also remove a leading “mailto:” if present. - Do not modify the internal content of the email beyond this normalization (no lowercasing or rewriting). Do not invent or correct domains. Missing data policy - If any value (first name, last name, or email) is not present in the text, output exactly N/A (uppercase) for that field. Never guess or fabricate. Scope - Extract only the person’s name and email. Ignore organizations, roles, phone numbers, URLs, and other data. - Ensure values have no leading/trailing spaces.
候補3
Your extraction feeds an automated incident-response and compliance system. A single mistake could notify the wrong person or miss a legal deadline, causing financial and privacy harm. Work meticulously and avoid guessing. Task: From the input Text, extract exactly one person’s first name, last name, and one email address. Rules for names: - Preserve capitalization exactly as written in the text. - Ignore honorifics/titles and role words (e.g., Mr., Ms., Dr., Prof., CEO). - Do not invent names from the email handle; only use an explicit name present in the text. - First Name = the given name (the first name token after any honorific). - Last Name = the surname. Exclude suffixes (Jr., Sr., III). If particles are present (e.g., de, del, de la, da, dos, van, von, bin, al), include them with the surname when they appear as part of the name. Ignore middle names/initials for output. Rules for email: - Extract a valid email if present. - Normalize by trimming surrounding whitespace, removing any leading “mailto:” and stripping surrounding quotes/brackets/parentheses and any trailing sentence punctuation (e.g., .,;:!?) immediately following the email. - Do not modify internal characters of the email (do not change case, add, or remove characters inside the address). Multiple candidates: - If multiple names/emails appear, select the single person-email pair most clearly associated in the sentence. If unclear, choose the first valid-looking pair in reading order. - If a name or an email is not explicitly present, return N/A for that field (e.g., N/A for both first and last name if only an email appears). Output format (exactly these four lines, in this order, with these exact prefixes): - Reasoning: Let’s think step by step in order to [briefly explain how you identified the name and email]. - First Name: <value or N/A> - Last Name: <value or N/A> - Email: <value or N/A> Do not output anything else.
c. 最適化のプロンプトの選定
プロンプト候補から、どれが一番良いのか、各候補と(a)で作成したいくつかのFew-Shotsのデータセットに対して、数回評価を行います。 具体的に、プロンプトとFew-Shotsのデータを利用し、LLMに流して、そして、LLMから出た結果は評価データの正解の回答を比較します。 評価指標は、上記で定義した評価指標(extraction_metric
関数)です。このプロセスで、一番良い成績のプロンプトが選ばれます。
今回はプロンプト候補3が選ばれました
まとめ
本記事では、DSPyフレームワークを通じて、従来の手動プロンプトチューニングから自動化されたプロンプトプログラミングへの移行について解説しました。 従来のプロンプトチューニングの仕組みに比べて、はるかに構造化されたアプローチになり、プログラミングというパラダイムに移行することで、開発者にとっても実現しやすく、コスト削減に繋がっています。また、APIレベルでも充実されており、他のシステムとつなげることも楽になると思います。
DSPyまたはプロンプトプログラミングは、LLMアプリケーション開発における新しいパラダイムを提示しており、LLMの進化に伴い、AIのアプリケーションの品質・柔軟性の向上は重要アプローチだと思われます。
参考資料
https://www.twosigma.com/articles/a-guide-to-large-language-model-abstractions/
https://huggingface.co/blog/dleemiller/auto-prompt-opt-dspy-cross-encoders
最後に
グループ研究開発本部 AI研究開発室では、データサイエンティスト/機械学習エンジニアを募集しています。ビッグデータの解析業務などAI研究開発室にご興味を持って頂ける方がいらっしゃいましたら、ぜひ 募集職種一覧 からご応募をお願いします。皆さんのご応募をお待ちしています。
グループ研究開発本部の最新情報をTwitterで配信中です。ぜひフォローください。
Follow @GMO_RD