今回のテーマは「プロンプトエンジニアリングによるAIチャットアプリのカスタマイズ」です。
前回作成したWinForms+BlazorハイブリッドのAIチャットアプリをベースに、プロンプトエンジニアリングの基本と実践方法を学びます。
AIの挙動を細かく制御し、より使いやすいアプリへと発展させていきましょう。
この記事では以下の内容を説明します。
- システムプロンプトとユーザプロンプトの違いと効果的な使い方
- プロンプトエンジニアリングの開発プロセス
(Azure AI Foundry Portalでの実験からアプリへの実装)
次のような方に役立つ内容となっています。
- AIチャットアプリの挙動をカスタマイズしたい方
- プロンプトエンジニアリングの基本を学びたい方
- 実用的なAIアシスタントアプリを開発したい方
前回はWinForms+BlazorハイブリッドでAIチャットの基本的なUIを実装しました。
今回はそのアプリをより高度化し、AIの応答内容や出力形式をカスタマイズする方法を学びます。
演習で使ったコード一式はGitHubに置いてあります。
解説動画も作成しています。
講義:プロンプトエンジニアリングの基本
プロンプトとは?
プロンプトとは、簡単に言えば「AIに対する指示や質問」のことです。
プロンプトは、AIに何をしてほしいのか、どのように応答してほしいのかを伝える「命令文」や「質問文」のことです。
GPT-4やAzure OpenAIなどの大規模言語モデル(LLM)は、このプロンプトに基づいて応答を生成します。
たとえば以下があります。
- 「こんにちは」というシンプルなプロンプト
- 「C#でファイルを読み込むコードを書いて」という指示のプロンプト
- 「AIとは何ですか?」という質問のプロンプト
なるほど!プロンプトはAIに話しかけるときの「言葉」なんだね。
そうです!
そして、そのプロンプトの書き方を工夫して、AIからより良い・より適切な回答を引き出す技術が「プロンプトエンジニアリング」です。
システムプロンプトとユーザプロンプトの違い
プロンプトには主に「システムプロンプト」と「ユーザプロンプト」の2種類があります。
システムプロンプトはAIの基本的な振る舞いや性格、守るべきルールを定義するものです。AIの「設定」として機能します。
例:「あなたはプログラミング教師です。C#の最新記法で解説してください」
ユーザプロンプトはユーザが実際に入力するメッセージ(質問や指示)です。システムプロンプトの制約の中で、このメッセージに対して応答が生成されます。
例:「配列とリストの違いを教えて」
プロンプトエンジニアリングの開発プロセス
AIアプリ開発において、プロンプトエンジニアリングのプロセスは非常に重要です。以下の手順で効率的に開発を進めることができます。
- プレイグラウンドなどでプロンプトを設計・テスト
(例:Azure AI Foundry Portalのプレイグラウンド) - 確立したプロンプトをアプリに実装
- フィードバックを元に継続的に改善
わざわざコードを書かなくても、まずはプレイグラウンドでテストできるんだね!
プロンプト作成は試行錯誤することも多いので、コードに組み込む前に十分テストするのが効率的ですね。
演習でもAzure AI Foundryのプレイグラウンドを使います!
効果的なシステムプロンプトの設計原則
システムプロンプトを設計する際の重要な原則をいくつか紹介します。
- 明確な役割定義
- AIの役割を具体的に定義
(例:「あなたはC#プログラミングの専門家です」) - 専門分野や得意なトピックを明示
- AIの役割を具体的に定義
- トーンと話し方の指定
- フォーマルかカジュアルか
- 専門的か初心者向けか
- 簡潔か詳細か
- 制約条件の設定
- 回答の形式や長さの制限
- 避けるべきトピックや表現の指定
- 特定の用語や言い回しの使用指示
- 例示の活用
- 望ましい応答の具体例を示す
- 望ましい応答の具体例を示す
良いシステムプロンプトは、AIの「行動指針」として機能します。曖昧さを減らし、具体的な指示を出すことがポイントです!
出力形式の制御方法
プロンプトエンジニアリングのもう一つの重要な側面は、AIの出力形式を制御することです。
出力形式を指定する理由は以下です。
- データの構造化:JSONやXML形式で出力させることで、プログラムでの処理が容易になる
- 視認性の向上:マークダウン形式で出力させることで、テキストの見栄えが良くなる
- 一貫性の確保:常に同じフォーマットで回答させることで、ユーザ体験が向上する
代表的な出力形式指定としては以下があります。
- JSON形式:データの構造化に最適
- マークダウン形式:見やすい文書形式に
- HTMLテーブル:表形式データの表示に
- CSV形式:データの一覧を表形式で
- XMLフォーマット:複雑な階層構造のデータに
出力形式を指定することで、AIの回答をそのまま使ってアプリで処理可能になる点が、実務でAIを活用したアプリを作る際にとても重要です。
演習:AIチャットに役割・性格を設定する
前回の演習で作ったAIチャットアプリについて、挙動をカスタマイズしてみましょう。
WinFormsにおけるAIチャットの土台部分については、以下の記事も参考にしてください。Azure OpenAIのモデル作成方法も解説しています。


今回、「ロールプレイングゲームに登場する村の長老のようなC#プログラミング講師」という役割をAIに与え、それに基づき回答させるようにアプリを修正します。
以下の手順で進めます。
- 手順1:システムプロンプト作成
- 手順2:プレイグラウンドで実験
- 手順3:アプリへ実装
手順1:システムプロンプト作成
システムプロンプトとしては以下のテキストを使います。村の長老のような話し方をするC#好きのキャラ付けをしています。
あなたは長年の開発経験をもつプログラミング教師です。以下のルールに従って回答してください。
- ロールプレイングゲームに登場する村の長老のような話し方
- 簡潔に3文程度で要点をわかりやすく解説する
- プログラミングの話題以外の場合は、その話題に回答できないと返事する
- C#以外の言語の話題になったら、その言語を褒めつつC#をさりげなくおすすめする
役割とか表現方法、あと制約条件とかを具体的に記述するんだね!
手順2:プレイグラウンドで実験
Azure AI Foundry portalで、左側メニューの共有リソースにある「デプロイ」を選択すると作成したモデル一覧が表示されるので、そこで使うモデルを選択しましょう。

今回はあらかじめ作成してあるgpt-4oモデルを使います。「プレイグラウンドで開く」を選択しましょう。

プレイグラウンドでは以下のようにシステムプロンプト・ユーザプロンプトを入力して実験を行えます。

手順1で作成したシステムプロンプトを使って実験してみましょう。システムプロンプトのテキストを貼り付けて「変更の適用」ボタンを押します。
試しにAIと会話をしてみましょう。

システムプロンプトで記述した役割・制約に沿って回答してくれているね!
(ちゃんとC#もさりげなくすすめてる…)
AIの回答がなかなか期待どおりにならず、システムプロンプトの調整が必要になることもあるでしょう。
プレイグラウンドだと実験を簡単に行えるので、ここで調整を行うとよいでしょう。
手順3:アプリへ実装
前回の演習11-1のコードをベースとして、作成したシステムプロンプトを指定できるよう修正します。
AzureOpenAIChatService.csを以下のように修正し、システムプロンプトを設定可能にします。
using Azure.AI.OpenAI;
using Azure;
using OpenAI.Chat;
namespace WinFormsBlazorApp.Services
{
internal class AzureOpenAIChatService : IChatService
{
private readonly ChatClient _chatClient;
private readonly List<ChatMessage> _conversationHistory;
private readonly string _systemPrompt;
public AzureOpenAIChatService(string endpoint, string apiKey, string deploymentName, string systemPrompt)
{
// Azure OpenAI クライアントの初期化
var azureClient = new AzureOpenAIClient(
new Uri(endpoint),
new AzureKeyCredential(apiKey));
// チャットクライアントの取得
_chatClient = azureClient.GetChatClient(deploymentName);
_conversationHistory = new List<ChatMessage>();
// システムプロンプトの追加
_systemPrompt = systemPrompt;
// システムプロンプトを履歴に追加
ClearHistory();
}
public async Task<string> SendMessageAsync(string userInput)
{
if (string.IsNullOrEmpty(userInput))
throw new ArgumentNullException(nameof(userInput), "メッセージが空です。");
// ユーザメッセージを履歴に追加
_conversationHistory.Add(new UserChatMessage(userInput));
// チャット完了リクエスト
ChatCompletion completion = await _chatClient.CompleteChatAsync(_conversationHistory);
// アシスタントの応答を履歴に追加
_conversationHistory.Add(new AssistantChatMessage(completion));
// レスポンステキストを返す
return completion.Content[0].Text;
}
public void ClearHistory()
{
// 履歴をクリアし、システムプロンプトを先頭へ追加
_conversationHistory.Clear();
if (!string.IsNullOrEmpty(_systemPrompt))
{
_conversationHistory.Add(new SystemChatMessage(_systemPrompt));
}
}
}
}
ClearHistoryで履歴をクリアとともに、会話リストの先頭へシステムプロンプトを追加するようにしました。
「_conversationHistory.Add(new SystemChatMessage(_systemPrompt));」で追加しています。
ここでは、3種類のメッセージがありますね。
- SystemChatMessage:システムプロンプトを格納するためのメッセージ。会話の初期設定やAIの振る舞いを定義します。
- UserChatMessage :ユーザからの入力メッセージ。(ユーザプロンプト)
- AssistantChatMessage : AIアシスタントからの応答メッセージ。
AIはシステムプロンプトのルールに基づき、これまでの会話履歴と最新のユーザの入力メッセージから応答メッセージを作成します。
最後に、MainForm.csでシステムプロンプトを文字列定数として定義して、それをチャットサービへ渡します。
using Microsoft.AspNetCore.Components.WebView.WindowsForms;
using Microsoft.Extensions.DependencyInjection;
using WinFormsBlazorApp.Pages;
using WinFormsBlazorApp.Services;
namespace WinFormsBlazorApp.Forms
{
public partial class MainForm : Form
{
//生文字列リテラルでシステムプロンプトを定義
private const string SystemPrompt = """
あなたは長年の開発経験をもつプログラミング教師です。以下のルールに従って回答してください。
- ロールプレイングゲームに登場する村の長老のような話し方
- 簡潔に3文程度で要点をわかりやすく解説する
- プログラミングの話題以外の場合は、その話題に回答できないと返事する
- C#以外の言語の話題になったら、その言語を褒めつつC#をさりげなくおすすめする
""";
public MainForm()
{
InitializeComponent();
var services = new ServiceCollection();
services.AddWindowsFormsBlazorWebView();
services.AddScoped<IChatService, AzureOpenAIChatService>(
serviceProvider =>
{
var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT");
var apiKey = Environment.GetEnvironmentVariable("AZURE_OPENAI_KEY");
var deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME");
if (string.IsNullOrEmpty(endpoint) || string.IsNullOrEmpty(apiKey) || string.IsNullOrEmpty(deploymentName))
{
throw new InvalidOperationException("Azure OpenAI configuration is missing.");
}
return new AzureOpenAIChatService(endpoint, apiKey, deploymentName, SystemPrompt);
});
blazorWebView1.HostPage = "wwwroot\\index.html";
blazorWebView1.Services = services.BuildServiceProvider();
blazorWebView1.RootComponents.Add<Chat>("#app");
}
}
}
これで完成です!動かしてみましょう!
アプリを実行
アプリを実行すると以下のようになります。プレイグラウンドで実験したときと同様に会話ができるはずです。

これを応用すればいろいろなタイプのAIチャットが作れるね!
今回、システムプロンプトはプログラム内に直接記述しましたが、外部ファイルから読み込むようにすると変更が容易になり、良いかと思います。
補足:プロンプトエンジニアリングの学習方法
今回、AIアプリ開発におけるプロンプトエンジニアリングの第一歩として、もっとも基本な部分を解説しました。
プロンプトエンジニアリングは巨大な技術分野であり、最初はどこから学びはじめるか迷ってしまうことも多いでしょう。
Web上での記事や、ある程度体系的に解説しているコンテンツなども多くあり、もちろんこれらを活用することも重要でしょう。
ただ、私としてはまずはAIを使うことに慣れる(自分が普段行う作業でAIを使って効率化する)ということがとても大事かと思っています。
今回作成したような「AIを使うアプリ作り」の学習において、その学習自体にもAIを活用することができます。以下の記事もぜひ参考にしてください。


プログラミングなどにおいてAIを日常的に活用すると、AIからよりよい回答を引き出すためのコツ(良いプロンプトの書き方)もだんだんと身についてくるかと思います。
そのようなAIを適切に使う肌感覚が身につくと、その知見はそのままAIアプリ開発におけるプロンプト作成にも応用できるはずです。
ぜひ、プログラミングやその学習にAIを活用していきましょう!
まとめ
本記事では、プロンプトエンジニアリングを活用したAIチャットアプリのカスタマイズについて学びました。
プロンプトエンジニアリングの基本として以下の要点があります。
- システムプロンプトとユーザプロンプトの違いを理解し、AIの役割や振る舞いを設定できる
- Azure AI Foundryのプレイグラウンドでプロンプトをテストしてから実装する効率的な開発プロセス
- AIの出力形式をJSONやマークダウンなどで制御して、アプリでの処理を容易にする
演習としてチャットアプリへシステムプロンプトを実装しました。
Azure OpenAI向けのライブラリでシステムプロンプトを実装する場合は、「SystemChatMessage」を会話履歴の先頭に配置します。
これらの知識を活用することで、特定の役割や性格を持ったAIアシスタントを実装することができ、目的に特化したアプリの開発が可能になります。
次回はAIの出力形式としてJSONを指定する方法など解説していければと思います。
引き続き、C#とAI技術を組み合わせた実践的なアプリ開発を一緒に学んでいきましょう!