3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

C#でAzureOpenAIに2つの型のどちらか一方で構造化出力させたいとき

Last updated at Posted at 2025-10-27

概要

前回の記事
👉 C#の型をスキーマにして構造化出力させる方法

上の記事では、C#クラスをスキーマ化してAzureOpenAIに構造化出力を行わせる方法を紹介しました。
しかし、実際のユースケースでは次のような状況が出てきます。

「2つ以上の型のうち、どれかで応答してほしい」

C#単体ではUnion型がないため、
このような「複数型のいずれかで出力」を自然に扱うのが難しいです。

前提:JSON SchemaのoneOfを利用する

Azure OpenAIでは、構造化出力をJSON Schemaで制御できます。
このスキーマにoneOfを追加することで、複数の型のうちいずれかを許容する応答を得られます。

課題:NJsonSchemaではoneOfを生成できない

前述のとおりAzure OpenAIに渡すスキーマにoneOfが必要なのですが、NJsonSchema内で自動で追加するようなことはできないため、仕方なくスキーマに直接手を加えて構造化出力させます。(なにか他にいい方法が見つかりましたら、ぜひご教示ください。)

実装の流れ

以下のように処理すれば、
C#でも「Union型的な出力」をLLMに指示できます。

// 1️⃣ モデル型からスキーマを生成(処理の中身は前回の記事を参照)
var schema = ConvertToSchema(typeof(MainModel));

// 2️⃣ 特定のノードに oneOf を追加
if (schema.Definitions.TryGetValue(nameof(TargetNode), out var targetNode))
{
    // 複数のサブ型(例:AType, BType)を oneOf に設定
    targetNode.OneOf.Add(new JsonSchema { Reference = schema.Definitions[nameof(AType)] });
    targetNode.OneOf.Add(new JsonSchema { Reference = schema.Definitions[nameof(BType)] });
}

// 3️⃣ Azure OpenAIに構造化出力を依頼
var options = new ChatCompletionOptions
{
    ResponseFormat = ChatResponseFormat.CreateJsonSchemaFormat(
        jsonSchemaFormatName: "MainModel",
        jsonSchema: BinaryData.FromString(schema.ToJson())
    )
};

var response = await client.GetChatClient("AzureOpenAIでデプロイしたモデル名")
    .CompleteChatAsync(messages, options);

// 4️⃣ 結果をC#モデルにデシリアライズ
var result = JsonSerializer.Deserialize<ResultWrapper<MainModel>>(response.Value.Content.First().Text);

上記だけではC#モデルにデシリアライズする際にエラーとなるので下記のようなポリモーフィズムが必要です。

    /// <summary>
    /// 基底クラス
    /// </summary>
    [JsonPolymorphic(TypeDiscriminatorPropertyName = "Type")]
    [JsonDerivedType(typeof(AType), typeDiscriminator: "AType")]
    [JsonDerivedType(typeof(BType), typeDiscriminator: "BType")]
    public abstract class MainModel
    {
    }

    public sealed class AType : MainModel
    {
        [Description("文字列「AType」で固定")]
        [JsonPropertyName("Type")]
        [Required]
        public string Type { get; set; } = "AType";

        //任意のプロパティを追加してください。
    }
    public sealed class BType : MainModel
    {
        [Description("文字列「BType」で固定")]
        [JsonPropertyName("Type")]
        [Required]
        public string Type { get; set; } = "Btype";

        //任意のプロパティを追加してください。
    }
3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?