はじめに
C#でAzure OpenAI連携の機能を実装していたところ、SystemChatMessageとUserChatMessageというクラスが出てきました。
new SystemChatMessage("あなたは親切なアシスタントです。"),
new UserChatMessage("こんにちは!"),
「どっちもメッセージを送ってるだけじゃないの?何が違うの?」と思って調べたので、学んだことをまとめます。
環境
- .NET 10.0
- Azure.AI.OpenAI(安定版)
- Visual Studio 2026
そもそもChat Completion APIの仕組み
まず前提として、Azure OpenAIのChat Completion APIは会話形式でやりとりする仕組みになっています。
APIにメッセージを送るとき、1つの文字列を投げるのではなく、ロール(役割)付きのメッセージの配列を渡します。ロールには主に以下の3つがあります。
| ロール | C#のクラス | 役割 |
|---|---|---|
| system | SystemChatMessage |
AIの振る舞いや前提条件を定義する |
| user | UserChatMessage |
ユーザーからの入力(質問や依頼) |
| assistant | AssistantChatMessage |
AIからの応答(会話履歴として使う) |
コードで見るとこんなイメージです。
var completion = chatClient.CompleteChat(
[
new SystemChatMessage("..."), // system: AIへの指示
new UserChatMessage("..."), // user: ユーザーの発言
new AssistantChatMessage("..."), // assistant: AIの過去の応答
new UserChatMessage("..."), // user: ユーザーの次の発言
]);
この配列全体が「会話の文脈」としてモデルに渡されます。モデル自体は会話を記憶しないため、毎回この配列に会話履歴を含めて送る必要があります。
SystemChatMessage(systemロール)とは
SystemChatMessageは、AIの振る舞いを定義するための指示です。Microsoftのドキュメントでは「system message」「metaprompt」「system prompt」とも呼ばれています。
README.mdにもこう書かれていました。
System messages represent instructions or other guidance about how the assistant should behave
つまり「あなたはこういう存在で、こういうルールで応答してね」という設定を伝えるためのメッセージです。
何を書くのか
Microsoftのドキュメントによると、system messageには主に以下の要素を含められます。
- 役割とタスク: アシスタントが何者で、何をするのか
- トーンと対象: 誰に向けて、どんな口調で話すのか
- スコープと境界: やってはいけないこと、対応できない場合の振る舞い
- 安全ガイドライン: 有害な出力を減らすためのルール
- ツールとデータ(任意): 利用可能なツールやデータソース
具体例
簡単なものから複雑なものまで、いくつか例を見てみます。
シンプルな例
new SystemChatMessage("あなたは親切なアシスタントです。")
業務に特化した例
new SystemChatMessage(
"""
あなたはContoso社のカスタマーサポート担当です。
製品の保証、返品、注文状況に関する質問に回答してください。
## ルール
- Contoso社の製品とポリシーに関する質問のみ回答する
- わからない場合は「その情報は持ち合わせていません。support@contoso.com にお問い合わせください」と回答する
- 法的、医療、金融に関するアドバイスは行わない
- 競合他社について言及しない
## 回答形式
- 簡潔かつフレンドリーに
- 最後に「他にお手伝いできることはありますか?」と添える
""")
参考データを含める例
new SystemChatMessage(
"""
あなたはAzure OpenAIに関する技術的な質問に回答するチャットボットです。
以下のコンテキストの範囲内でのみ回答してください。
わからない場合は「わかりません」と回答してください。
コンテキスト:
- Azure OpenAIはOpenAIの言語モデルへのREST APIアクセスを提供します
- GPT-4o、GPT-4o-miniなどのモデルが利用可能です
- Azureのセキュリティとエンタープライズ機能を備えています
""")
system messageの特徴
- メッセージ配列の先頭に配置する
- 省略可能だが、含めた方がよい結果が得られる(Microsoftのドキュメントでも推奨)
- 会話全体を通じてモデルの振る舞いに影響を与える
- 短い方がレイテンシーが少なく、コンテキストウィンドウを節約できる
注意点としては、system messageに指示を書いたからといって、モデルが100%その通りに動くとは限りません。Microsoftのドキュメントにも、system messageで「わからない場合は『わかりません』と答えて」と指示しても、それが必ず守られる保証はないと明記されています。あくまで「そうなりやすくなる」ものです。
UserChatMessage(userロール)とは
UserChatMessageは、ユーザーからの入力を表します。質問、依頼、処理してほしいテキストなど、ユーザーが実際にAIに投げるメッセージです。
new UserChatMessage("Azure OpenAIとは何ですか?")
こちらはシンプルで、「ユーザーが言ったこと」をそのまま渡すだけです。会話履歴として過去のユーザー発言を含める場合にも使います。
両者の違いを整理する
ここまで調べて理解した違いを整理します。
| 観点 | SystemChatMessage | UserChatMessage |
|---|---|---|
| 誰の発言か | 開発者(システム側) | エンドユーザー |
| 目的 | AIの振る舞い・ルール・前提の定義 | 質問や依頼の入力 |
| 配置 | 通常は配列の先頭に1つ | 会話の中で複数回登場 |
| 影響範囲 | 会話全体のトーンや制約 | その場の質問・タスクに対する応答 |
| 省略 | 省略可能(非推奨) | 最低1つは必要 |
イメージとしては、SystemChatMessageは舞台の設定、UserChatMessageはセリフみたいなものだと思いました。
実際のコード例
Azure.AI.OpenAIを使った実際のコード例をいくつか紹介します。
基本的な使い方
using Azure.AI.OpenAI;
using Azure.Identity;
using OpenAI.Chat;
// クライアントの作成
var azureClient = new AzureOpenAIClient(
new Uri("https://your-resource.openai.azure.com"),
new DefaultAzureCredential());
var chatClient = azureClient.GetChatClient("my-gpt-4o-deployment");
// メッセージの組み立てと送信
var completion = chatClient.CompleteChat(
[
new SystemChatMessage("あなたは親切な日本語アシスタントです。簡潔に回答してください。"),
new UserChatMessage("Azure OpenAIとは何ですか?"),
]);
Console.WriteLine(completion.Content[0].Text);
SystemChatMessageで「日本語で簡潔に」という方針を伝え、UserChatMessageで実際の質問を投げています。この2つを組み合わせることで、期待した形式の回答が返ってきやすくなります。
会話履歴を含める場合
マルチターンの会話では、過去のやりとりも配列に含めます。ここでAssistantChatMessageも登場します。
var completion = chatClient.CompleteChat(
[
new SystemChatMessage("あなたはC#の学習をサポートするアシスタントです。初心者にもわかりやすく説明してください。"),
// 1ターン目
new UserChatMessage("LINQって何ですか?"),
new AssistantChatMessage("LINQは、C#のコレクション操作を簡潔に書ける機能です。SQLのような書き方でデータの抽出や変換ができます。"),
// 2ターン目(これに対する応答が返る)
new UserChatMessage("具体的なコード例を見せてください。"),
]);
モデルには会話の記憶がないため、2ターン目の質問だけ送ると「何の具体例?」となってしまいます。過去のやりとりを含めることで、「LINQの具体例」だと理解してくれます。
Few-shot学習(応答の例を示す)
system messageだけでは意図した形式で回答してくれない場合、UserとAssistantのやりとり例を入れて「こういう風に答えてね」と示すテクニックがあります。Few-shot学習と呼ばれます。
var completion = chatClient.CompleteChat(
[
new SystemChatMessage("ニュース記事の見出しからカテゴリを判定するアシスタントです。"),
// 例1
new UserChatMessage("見出し: 日経平均が年初来高値を更新"),
new AssistantChatMessage("カテゴリ: 経済"),
// 例2
new UserChatMessage("見出し: 大谷翔平が今季30号ホームラン"),
new AssistantChatMessage("カテゴリ: スポーツ"),
// 実際に判定してほしい入力
new UserChatMessage("見出し: 新型iPhoneが来月発売へ"),
]);
// → 「カテゴリ: テクノロジー」のような応答が期待される
例を示すことで、「見出しを受け取ったら『カテゴリ: ○○』の形式で返す」というパターンをモデルが汲み取ってくれます。
まとめ
- SystemChatMessage(systemロール) はAIの振る舞い・ルール・前提条件を定義するもの
- UserChatMessage(userロール) はユーザーからの質問や依頼を伝えるもの
- 両者は役割が明確に異なるので、適切に使い分けることで意図した応答を得やすくなる
- 会話履歴を含める場合はAssistantChatMessageも組み合わせる
- system messageは省略可能だが、含めた方がよい結果が得られる
調べてみると、普段何気なく使っていた「ロール」の概念がちゃんと設計された仕組みだということがわかりました。特にsystem messageの設計次第でAIの応答品質がかなり変わるので、意識して書くようにしたいです。
参考になったら いいね や ストック をお願いします!
同じような経験をされた方のコメントもお待ちしています。
参考
- Safety system messages - Azure OpenAI(Microsoft Learn)
- System message design - Azure OpenAI(Microsoft Learn)
- Prompt engineering techniques - Azure OpenAI(Microsoft Learn)
- Work with chat completion models - Azure OpenAI(Microsoft Learn)
- Chat Completions API Reference(OpenAI)
- Messages API Reference(OpenAI)
- Azure OpenAI client library for .NET(GitHub)
関連リンク
技術ブログでも学びや検証内容をまとめています。
アウトプットで手当がもらえる会社 ONE WEDGE
株式会社ONE WEDGE では一緒に働く仲間を募集中!
技術記事を書くと手当がもらえる「IT系記事寄稿特別手当」という制度があります。
興味があればぜひカジュアルに話しましょう!
👉 採用サイト