共用方式為


使用代理程式產生結構化輸出

本教學課程步驟示範如何使用代理程式產生結構化輸出,其中代理程式是以 Azure OpenAI 聊天完成服務為基礎。

這很重要

並非所有代理類型都原生支援結構化輸出。 與相容的聊天用戶端搭配使用時,支援 ChatClientAgent 結構化輸出。

先決條件

如需必要條件和安裝 NuGet 套件,請參閱本教學課程中的 建立並執行簡單代理程式 步驟。

定義結構化輸出的型別

首先,定義一個代表你希望代理人輸出結構的型別。

public class PersonInfo
{
    public string? Name { get; set; }
    public int? Age { get; set; }
    public string? Occupation { get; set; }
}

建立代理程式

使用 Azure OpenAI 聊天客戶端建立一個 ChatClientAgent

using System;
using Azure.AI.OpenAI;
using Azure.Identity;
using Microsoft.Agents.AI;

AIAgent agent = new AzureOpenAIClient(
    new Uri("https://<myresource>.openai.azure.com"),
    new AzureCliCredential())
        .GetChatClient("gpt-4o-mini")
        .AsAIAgent(name: "HelpfulAssistant", instructions: "You are a helpful assistant.");

使用 RunAsync<T> 執行的結構化輸出

RunAsync<T> 方法在 AIAgent 基底類別中可用。 它接受一個通用型別參數,用以指定結構化輸出型態。 此方法適用於編譯時已知結構化輸出型別,且需要型別化結果實例時。 它支援原語、陣列和複雜型態。

AgentResponse<PersonInfo> response = await agent.RunAsync<PersonInfo>("Please provide information about John Smith, who is a 35-year-old software engineer.");

Console.WriteLine($"Name: {response.Result.Name}, Age: {response.Result.Age}, Occupation: {response.Result.Occupation}");

結構化輸出與 ResponseFormat 一起使用

結構化輸出可以在調用時通過設定ResponseFormat屬性來配置,也可以在初始化支援該屬性的代理(例如ChatClientAgent和Foundry代理)時完成。

此方法適用於以下情況:

  • 編譯時結構化輸出型別尚未確定。
  • 該結構以原始 JSON 表示。
  • 結構化輸出只能在代理建立時設定。
  • 只需原始 JSON 文字,無需反序列化即可。
  • 採用代理間協作。

各種 ResponseFormat 選項可用。

備註

ResponseFormat 方法不支援原始元素與陣列。 如果你需要處理原件或陣列,可以用這種 RunAsync<T> 方法或建立包裝型別。

// Instead of using List<string> directly, create a wrapper type:
public class MovieListWrapper
{
    public List<string> Movies { get; set; }
}
using System.Text.Json;
using Microsoft.Extensions.AI;

AgentRunOptions runOptions = new()
{
    ResponseFormat = ChatResponseFormat.ForJsonSchema<PersonInfo>()
};

AgentResponse response = await agent.RunAsync("Please provide information about John Smith, who is a 35-year-old software engineer.", options: runOptions);

PersonInfo personInfo = JsonSerializer.Deserialize<PersonInfo>(response.Text, JsonSerializerOptions.Web)!;

Console.WriteLine($"Name: {personInfo.Name}, Age: {personInfo.Age}, Occupation: {personInfo.Occupation}");

ResponseFormat也可以使用原始的 JSON schema 字串來指定,當沒有對應的 .NET 類型可用時,例如宣告式代理或從外部設定載入的結構,這非常有用:

string jsonSchema = """
{
    "type": "object",
    "properties": {
        "name": { "type": "string" },
        "age": { "type": "integer" },
        "occupation": { "type": "string" }
    },
    "required": ["name", "age", "occupation"]
}
""";

AgentRunOptions runOptions = new()
{
    ResponseFormat = ChatResponseFormat.ForJsonSchema(JsonElement.Parse(jsonSchema), "PersonInfo", "Information about a person")
};

AgentResponse response = await agent.RunAsync("Please provide information about John Smith, who is a 35-year-old software engineer.", options: runOptions);

JsonElement result = JsonSerializer.Deserialize<JsonElement>(response.Text);

Console.WriteLine($"Name: {result.GetProperty("name").GetString()}, Age: {result.GetProperty("age").GetInt32()}, Occupation: {result.GetProperty("occupation").GetString()}");

結構化輸出與串流

串流時,代理程式回應會串流為一系列更新,而且只有在收到所有更新後,您才能還原序列化回應。 您必須將所有更新整合為一個回應才能進行反序列化。

using System.Text.Json;
using Microsoft.Extensions.AI;

AIAgent agent = new AzureOpenAIClient(
    new Uri("https://<myresource>.openai.azure.com"),
    new DefaultAzureCredential())
        .GetChatClient("gpt-4o-mini")
        .AsAIAgent(new ChatClientAgentOptions()
        {
            Name = "HelpfulAssistant",
            Instructions = "You are a helpful assistant.",
            ChatOptions = new() { ResponseFormat = ChatResponseFormat.ForJsonSchema<PersonInfo>() }
        });

> [!WARNING]
> `DefaultAzureCredential` is convenient for development but requires careful consideration in production. In production, consider using a specific credential (e.g., `ManagedIdentityCredential`) to avoid latency issues, unintended credential probing, and potential security risks from fallback mechanisms.

IAsyncEnumerable<AgentResponseUpdate> updates = agent.RunStreamingAsync("Please provide information about John Smith, who is a 35-year-old software engineer.");

AgentResponse response = await updates.ToAgentResponseAsync();

PersonInfo personInfo = JsonSerializer.Deserialize<PersonInfo>(response.Text)!;

Console.WriteLine($"Name: {personInfo.Name}, Age: {personInfo.Age}, Occupation: {personInfo.Occupation}");

結構化輸出,代理人卻沒有結構化輸出能力

有些代理本身不支援結構化輸出,可能是因為這不在協議中,或是代理使用的語言模型沒有結構化輸出功能。 一種可能的方法是建立一個自訂的裝飾代理,將任何 AIAgent 包裝起來,並透過聊天客戶端使用額外的大型語言模型的呼叫,將代理的文字回應轉換成結構化的 JSON。

備註

由於此方法依賴額外的大型語言模型呼叫來轉換回應,因此其可靠性可能不足以應對所有情境。

若想參考此模式的實作,可依照自身需求調整,請參考 StructuredOutputAgent 範例

小提示

完整可執行範例請參閱 .NET 範例

串流範例

小提示

完整可執行範例請參閱 .NET 範例

本教學課程步驟示範如何使用代理程式產生結構化輸出,其中代理程式是以 Azure OpenAI 聊天完成服務為基礎。

這很重要

並非所有代理程式類型都支援結構化輸出。 與相容的聊天用戶端搭配使用時,支援 Agent 結構化輸出。

先決條件

如需必要條件和安裝套件,請參閱本教學課程中的 建立並執行簡式代理程式 步驟。

建立具有結構化輸出的代理程式

Agent 置在任何支援結構化輸出的聊天用戶端實作之上。 使用Agentresponse_format參數來指定所需的輸出結構描述。

建立或執行代理程式時,您可以提供定義預期輸出結構的 Pydantic 模型。

根據基礎聊天用戶端功能,支援各種回應格式。

此範例會建立代理程式,以符合 Pydantic 模型結構描述的 JSON 物件形式產生結構化輸出。

首先,定義一個 Pydantic 模型,該模型代表您想要從代理程式輸出的結構:

from pydantic import BaseModel

class PersonInfo(BaseModel):
    """Information about a person."""
    name: str | None = None
    age: int | None = None
    occupation: str | None = None

現在您可以使用 Azure OpenAI 聊天用戶端建立代理程式:

from agent_framework.azure import AzureOpenAIChatClient
from azure.identity import AzureCliCredential

# Create the agent using Azure OpenAI Chat Client
agent = AzureOpenAIChatClient(credential=AzureCliCredential()).as_agent(
    name="HelpfulAssistant",
    instructions="You are a helpful assistant that extracts person information from text."
)

現在,您可以使用一些文字資訊執行代理程式,並使用參數指定 response_format 結構化輸出格式:

response = await agent.run(
    "Please provide information about John Smith, who is a 35-year-old software engineer.",
    response_format=PersonInfo
)

代理回應將在value屬性中包含結構化輸出,並且可以直接作為 Pydantic 模型實例存取。

if response.value:
    person_info = response.value
    print(f"Name: {person_info.name}, Age: {person_info.age}, Occupation: {person_info.occupation}")
else:
    print("No structured data found in response")

串流時,agent.run(..., stream=True) 回傳ResponseStream. 串流內建的終結器會自動處理結構化輸出解析,因此你可以即時迭代更新,然後呼叫 get_final_response() 取得解析結果:

# Stream updates in real time, then get the structured result
stream = agent.run(query, stream=True, options={"response_format": PersonInfo})
async for update in stream:
    print(update.text, end="", flush=True)

# get_final_response() returns the AgentResponse with the parsed value
final_response = await stream.get_final_response()

if final_response.value:
    person_info = final_response.value
    print(f"Name: {person_info.name}, Age: {person_info.age}, Occupation: {person_info.occupation}")

如果你不需要處理個別串流更新,可以完全跳過迭代—— get_final_response() 它會自動消耗整個串流:

stream = agent.run(query, stream=True, options={"response_format": PersonInfo})
final_response = await stream.get_final_response()

if final_response.value:
    person_info = final_response.value
    print(f"Name: {person_info.name}, Age: {person_info.age}, Occupation: {person_info.occupation}")

完整範例

# Copyright (c) Microsoft. All rights reserved.

import asyncio

from agent_framework.openai import OpenAIResponsesClient
from pydantic import BaseModel

"""
OpenAI Responses Client with Structured Output Example

This sample demonstrates using structured output capabilities with OpenAI Responses Client,
showing Pydantic model integration for type-safe response parsing and data extraction.
"""


class OutputStruct(BaseModel):
    """A structured output for testing purposes."""

    city: str
    description: str


async def non_streaming_example() -> None:
    print("=== Non-streaming example ===")

    agent = OpenAIResponsesClient().as_agent(
        name="CityAgent",
        instructions="You are a helpful agent that describes cities in a structured format.",
    )

    query = "Tell me about Paris, France"
    print(f"User: {query}")

    result = await agent.run(query, options={"response_format": OutputStruct})

    if structured_data := result.value:
        print("Structured Output Agent:")
        print(f"City: {structured_data.city}")
        print(f"Description: {structured_data.description}")
    else:
        print(f"Failed to parse response: {result.text}")


async def streaming_example() -> None:
    print("=== Streaming example ===")

    agent = OpenAIResponsesClient().as_agent(
        name="CityAgent",
        instructions="You are a helpful agent that describes cities in a structured format.",
    )

    query = "Tell me about Tokyo, Japan"
    print(f"User: {query}")

    # Stream updates in real time using ResponseStream
    stream = agent.run(query, stream=True, options={"response_format": OutputStruct})
    async for update in stream:
        if update.text:
            print(update.text, end="", flush=True)
    print()

    # get_final_response() returns the AgentResponse with structured output parsed
    result = await stream.get_final_response()

    if structured_data := result.value:
        print("Structured Output (from streaming with ResponseStream):")
        print(f"City: {structured_data.city}")
        print(f"Description: {structured_data.description}")
    else:
        print(f"Failed to parse response: {result.text}")


async def main() -> None:
    print("=== OpenAI Responses Agent with Structured Output ===")

    await non_streaming_example()
    await streaming_example()


if __name__ == "__main__":
    asyncio.run(main())

後續步驟