はじめに
生成AIを活用したアプリケーションにおいて、「プロンプトをいかに効率よく開発するか」は重要なテーマです。
プロンプトは一度作って終わりではなく、何度も試行錯誤を繰り返しながら改善していく必要があり、そのたびにコードや設計書が頻繁に更新されます。
また、プロンプトを過去のバージョンに戻したいケースや、1つのアプリケーション内で複数のプロンプトを組み合わせて使いたいケースがあり、それぞれのプロンプトをメンテナンスしやすい形式で管理しておくことが重要です。
AIアシスタント「たまちゃん」では、現時点で4種類のプロンプトを管理していますが、プロンプトを開発したり改修していく場合に、いくつかの課題がありました。
本記事では、「たまちゃん」で実践した工夫や改善策をご紹介します。生成AIを活用したアプリケーションを開発・運用されている方にとってのヒントになれば幸いです。
改善前
たまちゃんでは、プロンプトの開発や改修において、表1のような課題がありました。
課題 | 詳細 |
---|---|
メンテナンス性 | プロンプトとアプリケーションのコードが混在していることで可読性が低く、以下の問題が発生
|
試行錯誤の容易性 | プロンプトの動作確認を行うためには、実装元の設計書を探し出し、Azure AI Foundryにプロンプトやモデルのパラメータを手動で入力する手間がかかる |
以前のコラム(AIアシスタントが答えられない時、どうサポートする?AIアシスタント「たまちゃん」のUI/UX改善事例 vol.1)でご紹介した「質問に合ったSlackチャンネルを予測する」プロンプトを例に挙げると、改善前は、コード1のように、プロンプトの末尾に次々と文字列を結合していく形式で実装していました。
コード1.プロンプトを作成する
def create_prompt(rules, question, channels): system_prompt = """ あなたはユーザからの問い合わせに対して、適切なチャンネルに誘導するAIアシスタントです。 """ user_prompt = f""" #入力された質問 に対して、回答が得られそうなチャンネルを #チャンネル一覧 から選択してください。 また、チャンネルを選択する時は #チャンネルを選択する時のルール に従ってください。 # チャンネルを選択する時のルール""" for rule in rules: user_prompt += f"\n・{rule}" user_prompt += """ # 出力形式 ## チャンネルを複数選択した場合 [{"channel_name":"選択したチャンネル名", "score": "確信度(0~1の範囲)"}, {"channel_name":"選択したチャンネル名", "score": "確信度(0~1の範囲)"},,,] ## チャンネルを1つ選択した場合 [{"channel_name":"選択したチャンネル名", "score": "確信度(0~1の範囲)"}] ## チャンネルを1つも選択しなかった場合 [] # 入力された質問""" user_prompt += f"\n{question}" user_prompt += """ # チャンネル一覧 [""" for channel in channels: user_prompt += f'\n{"channel_name": {channel['id']}, "channel_description": {channel['description']} }}' user_prompt += "\n]" return system_prompt, user_prompt
この実装形式では、プロンプトの可読性が低く、プロンプトの全体像や実際に生成AIに渡されるプロンプトの内容が、把握しづらいことが分かります。
また、たまちゃんでは、生成AIの管理やプロンプトの動作確認にAzure AI Foundryを活用しています。しかし、プロンプトを見直す際に、Azure AI Foundry上で動作確認を行いたい場合、図1のような手順が必要でした。具体的には、まず実装元の設計書を探し出し、その後、Azure AI Foundryにプロンプトやモデルのパラメータを手動で入力しなければならず、多くの手間がかかっていました。

図1. プロンプトを動作確認するまでの流れ
このように、改善前では、プロンプトのメンテナンスがしづらい課題や、試行錯誤が容易に行うことができない課題がありました。そこで、これらの課題を解決できるよう、改善に取り組むことにしました。
動的に値を書き換えたい項目はプレースホルダーとしておく
まず、プロンプトの可読性を向上させるには、動的に値を切り替えたい部分をプレースホルダー(変数)としてプロンプト内に記述し、後から文字列置換を用いて、その部分を置換する方法が有効だと考えられます。
実装例はコード2のようになります。
コード2.プレースホルダーを用いてプロンプトを作成する
def create_prompt(rules, question, channels): system_prompt = """ あなたはユーザからの問い合わせに対して、適切なチャンネルに誘導するAIアシスタントです。 """ user_prompt = """ #入力された質問 に対して、回答が得られそうなチャンネルを #チャンネル一覧 から選択してください。 また、チャンネルを選択する時は #チャンネルを選択する時のルール に従ってください。 # チャンネルを選択する時のルール {{rules_st}} # 出力形式 ## チャンネルを複数選択した場合 [{"channel_name":"選択したチャンネル名", "score": "確信度(0~1の範囲)"}, {"channel_name":"選択したチャンネル名", "score": "確信度(0~1の範囲)"},,,] ## チャンネルを1つ選択した場合 [{"channel_name":"選択したチャンネル名", "score": "確信度(0~1の範囲)"}] ## チャンネルを1つも選択しなかった場合 [] # 入力された質問 {{question}} # チャンネル一覧 [ {{channels_st}} ] """ rules_st = "\n".join([f"・{rule}" for rule in rules]) user_prompt = user_prompt.replace("{{rules_st}}", rules_st) user_prompt = user_prompt.replace("{{question}}", question) channels_st = "\n".join([f'{{"channel_name":{channel["id"]}, "channel_description": {channel["description"]} }}' for channel in channels]) user_prompt = user_prompt.replace("{{channels_st}}", channels_st) return system_prompt, user_prompt
改善前のコードと比較すると、最初にプロンプト全体をまとめて記述できるため、プロンプトの内容や構成がひと目で分かりやすくなります。
テンプレートエンジンのJinjaを活用する
さらにプロンプトの可読性を高める方法として、Jinjaを活用することが考えられます。
JinjaはPythonで使えるテンプレートエンジンで、テンプレート(文字列)内に変数や処理を記述しておくことで、動的にテキスト(HTMLやXML、プレーンテキストなど)を生成できます。また、Jinjaは、PythonのWebアプリケーションフレームワークであるDjangoのテンプレートエンジンの影響を受けており、テンプレート内でPythonに似た簡単な構文(加工処理、ループや条件分岐など)を書くことができます。
Jinjaを用いてプロンプトを作成する場合、コード3のような実装になります。
コード3.Jinjaを用いてプロンプトを作成する
from jinja2 import Template def create_prompt(rules, question, channels): system_prompt = """ あなたはユーザからの問い合わせに対して、適切なチャンネルに誘導するAIアシスタントです。 """ user_prompt = """ #入力された質問 に対して、回答が得られそうなチャンネルを #チャンネル一覧 から選択してください。 また、チャンネルを選択する時は #チャンネルを選択する時のルール に従ってください。 # チャンネルを選択する時のルール {%- for rule in rules %} ・{{ rule }} {%- endfor %} # 出力形式 ## チャンネルを複数選択した場合 [{"channel_name":"選択したチャンネル名", "score": "確信度(0~1の範囲)"}, {"channel_name":"選択したチャンネル名", "score": "確信度(0~1の範囲)"},,,] ## チャンネルを1つ選択した場合 [{"channel_name":"選択したチャンネル名", "score": "確信度(0~1の範囲)"}] ## チャンネルを1つも選択しなかった場合 [] # 入力された質問 {{ question }} # チャンネル一覧 [ {%- for channel in channels %} {"channel_name":{{ channel.id }}, "channel_description": {{ channel.description }} } {%- endfor %} ] """ user_prompt = Template(user_prompt).render(rules=rules, question=question, channels=channels) return system_prompt, user_prompt
Jinjaを使わない場合と比べて、コードがよりシンプルになり、配列やオブジェクトの値がどのようにプロンプトに挿入されるかが分かりやすくなり、可読性がさらに向上します。
活用可能なPythonライブラリ
Jinjaを使ってプロンプトを作成する場合、いくつか活用可能なPythonライブラリがあります。主要なPythonライブラリを表2にまとめました。
# | ライブラリ名 | 作成元 | ライセンス | テンプレートで管理可能な項目 | 備考 |
---|---|---|---|---|---|
1 | Prompty | Microsoft | MIT |
|
|
2 | Prompt-Poet | Character.AI | MIT | プロンプト |
|
補足情報として、AWSには「Amazon Bedrock Prompt Management」というサービスがあります。このサービスでは、作成したプロンプトや、プロンプト作成時のモデルのパラメータなどをバージョン管理できます。また、バージョン管理したプロンプトは、Amazon BedrockのAPIから呼び出すことができます。Azure OpenAI ServiceではなくAmazon Bedrockを利用しているケースにおいては、このサービスが活用できると考えられます。
Promptyを導入する
先ほど挙げたPythonライブラリの内、たまちゃんでは、Azure OpenAI ServiceやAzure AI Foundryを利用しているため、Promptyというライブラリを導入することにしました。
Promptyでは、コード4のように「.prompty」という拡張子のテンプレートファイルでプロンプトを管理します。また、テンプレートファイルでは、プロンプトだけでなく、プロンプト実行時のモデルのパラメータや、プロンプトに関する補足情報(説明文や入出力例など)も管理することができます。
コード4.テンプレートファイル(.prompty)
--- name: PredictDestinationChannel description: 質問に適した問い合わせ先のチャンネルを予測するプロンプト model: api: chat configuration: type: azure_openai azure_endpoint: https://dummy api_version: 2024-12-01-preview azure_deployment: gpt-4o-mini parameters: temperature: 0 response: full sample: rules: ${file:./samples/rules.json} question: "PCの設定手順を教えてください。" channels: ${file:./samples/channels.json} --- system: あなたはユーザからの問い合わせに対して、適切なチャンネルに誘導するAIアシスタントです。 user: #入力された質問 に対して、回答が得られそうなチャンネルを #チャンネル一覧 から選択してください。 また、チャンネルを選択する時は #チャンネルを選択する時のルール に従ってください。 # チャンネルを選択する時のルール {%- for rule in rules %} ・{{ rule }} {%- endfor %} # 出力形式 ## チャンネルを複数選択した場合 [{"channel_name":"選択したチャンネル名", "score": "確信度(0~1の範囲)"}, {"channel_name":"選択したチャンネル名", "score": "確信度(0~1の範囲)"},,,] ## チャンネルを1つ選択した場合 [{"channel_name":"選択したチャンネル名", "score": "確信度(0~1の範囲)"}] ## チャンネルを1つも選択しなかった場合 [] # 入力された質問 {{ question }} # チャンネル一覧 [ {%- for channel in channels %} {"channel_name":{{ channel.id }}, "channel_description": {{ channel.description }} } {%- endfor %} ]
このテンプレートファイルをPythonのプログラムから利用する場合は、コード5のような実装になります。プロンプトの作成から生成AIへのリクエストまでを、シンプルに実装できることが分かります。
コード5.テンプレートファイル(.prompty)をPythonのプログラムから利用
import prompty import prompty.azure def chat(rules, question, channels): prompty_file_path = "PredictDestinationChannel.prompty" # 「.promtpyファイル」を使ってプロンプトの作成から生成AIへのチャットまで行う completion = prompty.execute(prompty_file_path, inputs={ "rules": rules, "question": question, "channels": channels }, raw=True) return completion
また、テンプレートファイルはPythonのプログラムから利用できるだけでなく、Azure AI FoundryやVisual Studio CodeのGUI上で動作確認することもできます。これは、Azure AI Foundryがテンプレートファイルのインポートとエクスポートに対応していることや、PromptyがVisual Studio Code用の拡張機能を提供しているためです。
たまちゃんでは、Promptyの導入により、図2のような流れでプロンプトが開発できるようになりました。

図2. Prompty導入後におけるプロンプト開発の流れ
Azure AI FoundryやVisual Studio CodeのGUI上で作成したプロンプトは、そのままテンプレートファイルとしてアプリケーションに簡単に組み込むことができます。また、アプリケーションに組み込まれたテンプレートファイルは、素早くAzure AI FoundryやVisual Studio CodeのGUI上で動作確認することもできます。これにより、プロンプトの開発や修正をスムーズに進められるようになりました。
改善後
Prompty導入後、改善前に抱えていた課題は表3のように解決されました。
課題 | 詳細 | Prompty導入後 |
---|---|---|
メンテナンス性 | プロンプトとアプリケーションのコードが混在していることで可読性が低く、以下の問題が発生
|
|
試行錯誤の容易性 | プロンプトの動作確認を行うためには、実装元の設計書を探し出し、Azure AI Foundryにプロンプトやモデルのパラメータを手動で入力する手間がかかる |
|
さいごに
今回は、AIアシスタント「たまちゃん」で実践した、プロンプトの開発を効率化するための取り組みについてご紹介しました。
ご紹介した取り組みは、生成AIを活用した様々なアプリケーションでも応用できるものだと考えられます。これから生成AIを活用したアプリケーションの開発を始める方はもちろん、すでに運用されている方にも役立つ情報となれば嬉しいです。
本記事に記載されているロゴ、システム名称、企業名称、製品名称は各社の登録商標または商標です。
執筆者紹介

連載コラム:エクサの生成AIチャレンジ日記
本コラムでは、エクサ社内における生成AIの活用に向けた技術的な取り組みと、実際の業務適用事例をご紹介いたします。生成AIによる業務効率化や新たな価値創造のヒントとなれば幸いです。
関連コラム
関連ソリューション
関連事例
お問い合わせ
CONTACT
Webからのお問い合わせ
エクサの最新情報と
セミナー案内を
お届けします
