DSPyを用いたLLMの自動プロンプトチューニング

こんにちは.研究開発室の北村 蘭丸です.研究開発室では,意思決定に関連するAIとOperations Research (OR) のビジネス活用についての研究に取り組んでいます.本記事では,DSPyを用いたLLMシステムの自動プロンプトチューニングに関する取り組みについてご紹介いたします.

LLMを組み込んだシステム開発の大変さ

近年のAI技術の発展は目覚ましく,我々が開発するソフトウェアにAI (特にLLM-basedなもの) が組み込まれていることは珍しくなくなりつつあります.しかし,一方で新たな課題も生まれており,プロンプトチューニング作業はその筆頭と言えそうです.

この手のソフトウェア開発においては,とにかくプロンプトチューニングに工数を割くことになりがちです.従来的な機械学習プロジェクトでも類似した課題を抱えることはあったでしょうが,ことLLMに関しては,より定量化が難しくなり,更にもう一段階,抽象的なレベルで作業することになるイメージがあります.また,そのようなプロンプトチューニングを行い,苦労して構築したソフトウェアシステムであっても,「作って終わり」とはなりません.例えば,

  • 元々使っていたモデルがDeprecatedになった*1
  • ユーザーが使い慣れてきて入力のされ方が変わったために,精度が落ちてしまった*2

など,様々な理由により,継続的なチューニング作業が必要になります*3.つまり,今まで作ってきたAIソフトウェアの数に従って,潜在的に再チューニングが必要となるかもしれないシステムの数は増え続けるわけですね.我々のようなSIerにとってはこの点も無視できない課題となりそうです.さらに,挙げればキリがないかもしれませんが,そのプロンプトチューニング作業自体も,

  • LLMという元々確率的な振る舞いをする対象をコントロールしようと試みている
  • ルールや観点を誤解ないように洗い出してまとめておく必要がある
  • 特に開発初期は利用できるデータが少なく,チューニングも少人数体制で,観点も偏りやすい
  • 使い心地など,ロジックで評価しにくいところがある

といった難しさを持っています.そこで,本ブログでは,これらに対するアプローチの1つとして,『仮想ユーザーAIによる合成データを用いた自動プロンプトチューニング』についてお話ししたいと思います.

使用フレームワーク: DSPy

ここでは,Stanford NLPが開発しているDSPy (Declarative Self-improving Python) というLLMソフトウェアのためのフレームワークを使用します.DSPyは『LLMの振る舞いをプロンプトとして書くのではなく,プログラムとして書く』をコアコンセプトとしています.以下では主要なコンポーネントについて手短に解説をします.詳細については,公式ドキュメントなどをご参照ください.

  • Signature: 入出力関係の仕様を定義するコンポーネントであり,いわゆる型の宣言に相当します.

  • Module: Signatureで定義された入出力をLLMによって実現するためのコンポーネントで,PyTorchのModuleなどと似たような位置付けとなっています.ChainOfThoughtやReActなどのModuleがそれぞれ標準で用意されています.

  • Optimizer: Moduleからなるプログラムと,メトリクス,そして学習データを与えることによって,プロンプトチューニングを行うコンポーネントです.例えば,MIROv2GEPAなどの最新のプロンプトチューニング手法が実装されています.

LangChainなどの他のフレームワークと大きく異なるのは,プロンプトだけでLLMの振る舞いを制御するのではなく,それをSignatureやModuleの形で抽象化している点です.プロンプトはOptimizerに任せて,人間は入出力関係とワークフローの設計・構築に集中できる点が挙げられます.

自動プロンプトチューニングの実例

実際に,DSPyを用いて自動プロンプトチューニングを行う例を見てみましょう.ここでは,ユーザーの持つスキルレベルを特定するためのAIチャットシステムを題材とします.

スキルマップAIの構成

ユーザーとAIがチャット形式で技術に関する会話を行い,ユーザーが持っているスキルレベルを特定するというものです.実際にユーザーと会話するAI (質問AI) と,その会話の様子を横で聞いていてスキルレベルを判定するAI (レベル判定AI) に分かれています.実際のユースケースとしては,例えば「プロジェクトアサインのために参考にする」などを想定しています.

今回の題材に用いているような対話システムの場合,プロンプトチューニングには,通常,大量の会話ログデータが必要となります.先ほど,特に開発初期において利用できるデータが少ないことに起因する「コールドスタート問題」に触れましたが,一方で,サービスが全く出来上がっていない状態でユーザーを集めてデータを収集するのはコストがかかり過ぎますし,体験も良くありません.何か別の策を立てる必要があります.

そこで今回は,LLMの「生成AI」としての特性を生かしたアプローチとして,データの動的合成を取り入れてみることにします.概念図としては次のようになります.

仮想ユーザーAIによるプロンプトチューニング

要するに,別のAIに仮想的なユーザーとして振る舞ってもらい,でっち上げの会話を繰り広げてもらって動的に会話データを合成する,という手法です.これをチューニング用のデータセットとして用いることにします.このアプローチによって,ユーザーがいなくてもそれなりに多様な会話ログデータが得られ,コールドスタート問題に対応することができます.また,(お金が許す限り) 合成し放題であり,上手く仕組みに落とし込むことでスケーリングも可能です.

%%{init: {'theme': 'dark'}}%%
sequenceDiagram
  autonumber
  participant SP as ScenarioProvider
  participant SIM as QuestionAnswerSimulator
  participant ORC as SkillAssessmentOrchestrator
  participant QM as QuestionModule
  participant UM as UserSimulatorModule
  participant SM as ScoreModule

  SP->>SIM: provide( persona, skill, label )
  SIM->>ORC: init(config, agents)
  loop max_turns or until confident
    ORC->>QM: generate_question(skill, messages)
    QM-->>ORC: question
    ORC->>UM: answer(persona, question)
    UM-->>ORC: answer
    ORC->>SM: score(skill, question, answer)
    SM-->>ORC: level, confidence
    ORC-->>SIM: intermediate messages
  end
  ORC-->>SIM: Result(level, confidence, finish_reason)

また,同じデータ合成をやるならば,直接的にLLMを用いて対話ログデータを生成させるのも1つの手だと思いますが,ここではペルソナ (ユーザーの経歴や経験,会話スタイルやスキルレベルなど) という静的な表現を用いることで,その後段で合成される会話の多様性と一貫性のバランスをとることを狙っています.

まずは定量的な面を確認してみましょう.チューニングの評価値の様子を示します.

評価値の推移

これを見ると,チューニングが進み,評価値が改善していることが見て取れますね.ある程度このやり方で正解率 (スキルレベルを正しく言い当てられるか) は改善するということがわかりました.

もう一つ,こちらは定性的な面を確認するために,実際のでっち上げ会話の一例を示します.

ASSISTANT:
直近1年以内に、機械学習のパイプラインや評価の枠組みを自分で設計し、
チームや他メンバーが再利用できる形で運用した経験があれば、
どのような構成や工夫を行い、実際にどのように再利用されたかを簡潔に教えてください。
(未経験なら、もし設計する場合に重視したいポイントを1つ挙げてください)

USER:
需要予測回帰モデルのパイプラインを自作し、前処理(外れ値除去: zスコア)、
特徴量生成、学習、評価(RMSE[件], KPI[%]算出)をモジュール化しました。
パラメータや評価指標をconfigで管理し、他メンバーはデータと設定を差し替えるだけで
再利用可能となり、複数案件で同一フローが活用されました。

...

お固めの,いわゆる技術面接のようなスタイルに落ち着いたようですね.個人的にはもう少しカジュアルに,例えば「同僚からの技術相談」のようなスタイルの方が良いかなぁと感じており,この辺りはやはり人手での調節が必要そうです.実際には,開発の初期段階にこの仕組みで60--70点くらいの状態にもっていった上で,人手でプロンプトチューニングを行うなどハイブリッドなやり方が良いのではないかと感じました.

その他,やってみてわかったこと

今回,自動プロンプトチューニングに取り組んでみて,わかったことをいくつか述べていきます.

まず,『観点の出しやすさ』についてです.人間にとって,前もって観点を列挙しておくのはかなり難しいタスクとなります.一方で,ある程度正解のようなものが頭の中にある場合,具体的な出力に「文句」をつけるのは比較的簡単なのですよね.それゆえ,通常のプロンプトチューニングはどうしても「プロンプト修正→出力確認→修正→確認→修正→...」といったイテレーティブな作業になりがちです.自動プロンプトチューニングの仕組みでLLM-as-a-Judgeを利用する場合も,結局はEvaluatorとなるLLM Moduleへのプロンプティングは必要なわけですが,このとき,直接システムの振る舞いを規定するプロンプトを記述するのとは少し質的に異なった作業体験に感じられました.

具体例を挙げますと,例えば,LLMに対しては「Xしないでください」といった否定形でのプロンプトが上手く効かないことがあると言われており*4,通常であれば,対話AIのプロンプトに肯定形で言い換えるなど工夫しつつプロンプトを書き換えていくことになるわけです.一方,Evaluatorのプロンプトにはおかまいなく「Xしないでください」と書けるのですね.そして,ちゃんとこのルールに従って採点されます*5.EvaluatorとなるAIのプロンプトを記述するときは,自分と同じ感覚を持ち同じ感覚で評価を下してくれるAIを用意するようなイメージで作業していました.

また,今回のチューニングの仕組みを構築するにあたり,MLflow (on Databricks) とDSPyの連携機能を活用してみましたが,このようなチューニングのロギングとも非常に相性の良いプラットフォームだと感じました.

Moduleの呼び出し履歴の例
実際にLLMにどのような入力があったかの例
MLflowのTracing機能を利用することで,Moduleがどのような流れで呼び出されたか?や,どのような入力がされたのか?など細かな情報を取得することができ,チューニングプロセスの可視化やデバッグに非常に役立ちました.

Databricksについては,今回利用したDSPyとの連携機能含め,最近はLLMなどの生成AIに関連した機能追加も色々と行われているようなので,今後も上手く活用していきたいですね.

まとめ

本記事では,LLMを組み込んだシステム開発におけるプロンプトチューニングに関する課題に対し,アプローチの1つとして「DSPyを用いた自動プロンプトチューニング」についてご紹介しました.

今回は,スキルレベル判定を行う対話システムを題材に,仮想ユーザーによる合成データ生成を用いた自動プロンプトチューニングを試みました.定量的な評価値の改善は確認できた一方で,会話スタイルなど定性的な部分には依然として人手での調整が必要であり,まだまだ改善の余地がありそうです.実際には,「最初は自動チューニング→人手で最終調整」の2段階が良いのではないかと思われます.

最後までお読みいただきありがとうございました.Flectの研究開発室では,今後もお客様にとって付加価値となるような新たな分野や技術に関して研究開発を行っていきます.今回ご紹介したようなLLMに関する取り組み以外にも,業務上の意思決定に関連するAIや,数理最適化を始めとしたORの研究を行っておりますので,ご興味がありましたら是非ご相談ください.

Appendix

今回取り組んだような,LLMに人間を模倣させてその振る舞いを検討する,といった研究はすでにさまざまな分野で取り組まれています.例えば,マーケティング分野では,Maier, Aslak, Fiaschi, et al. (2025)や,Brand, Israeli, Ngwe, et al. (2025)などが挙げられます.もちろん,限界も色々と指摘されているところがあり,例えば今回採用したような,ペルソナを用いたやり方の限界はLi, Chen, Namkoong, Peng. (2025)などで指摘があります.

また,仮想的にAI同士に会話させてデータを合成するような手法は,すでにさまざまなところで提案されています.例えば,Li, Hammoud, Itani, et al. (2023)は,LLMエージェント同士にrole-playingをさせて会話データを生成するフレームワークを提案しており,Ulmer, Mansimov, Lin, et al. (2024)は,強化学習におけるself-playの発想を対話エージェントの学習データ生成に適用しています.これらはいずれもファインチューニングなどLLMの学習 (モデルパラメータの更新を伴う) のためのフレームワークですが,今回はそれと同じような発想で,プロンプトチューニング (モデルパラメータの更新は伴わない) に応用した,ということになります.

*1:モデルの更新に伴ってユーザー体験が変わってしまった事例としては,例えばkeep4o運動などが記憶に新しいところです.

*2:ある意味でのデータドリフトと言えるかも知れません.

*3:もちろん従来のML関連プロジェクトでもこのような課題は存在しており,ゆえにMLOpsなどの考え方が重要となっています.

*4:例えば,最近の文献だとVrabcová, et al. (2025). Negation: A Pink Elephant in the Large Language Models' Room?などが挙げられます.

*5:とは言え,チューニングされた結果,プロンプトがどのような記述になるかはわかりませんが...手作業の場合と比べ,少なくとも,何らかの意味で指標が改善する方向に修正を入れてくれるという違いがあります.