Compartilhar via


Produzindo saída estruturada com agentes

Esta etapa do tutorial mostra como produzir uma saída estruturada com um agente, em que o agente é criado no serviço de Conclusão de Chat do Azure OpenAI.

Importante

Nem todos os tipos de agente dão suporte à saída estruturada nativamente. O ChatClientAgent suporta a saída estruturada quando usado com clientes de chat compatíveis.

Pré-requisitos

Para pré-requisitos e instalação de pacotes NuGet, consulte a etapa Criar e executar um agente simples neste tutorial.

Definir um tipo para a saída estruturada

Primeiro, defina um tipo que represente a estrutura da saída desejada do agente.

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

Criar o agente

Crie um ChatClientAgent usando o Cliente de Chat do Azure OpenAI.

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.");

Saída estruturada com RunAsync<T>

O RunAsync<T> método está disponível na AIAgent classe base. Ele aceita um parâmetro de tipo genérico que especifica o tipo de saída estruturado. Essa abordagem é aplicável quando o tipo de saída estruturado é conhecido no momento da compilação e uma instância de resultado tipada é necessária. Ele dá suporte a primitivos, matrizes e tipos complexos.

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}");

Saída estruturada com ResponseFormat

A saída estruturada pode ser configurada definindo a ResponseFormat propriedade no AgentRunOptions momento da invocação ou no momento da inicialização do agente para agentes que dão suporte a ela, como ChatClientAgent e o Foundry Agent.

Essa abordagem é aplicável quando:

  • O tipo de saída estruturado não é conhecido no momento da compilação.
  • O esquema é representado como JSON bruto.
  • A saída estruturada só pode ser configurada no momento da criação do agente.
  • Somente o texto JSON bruto é necessário sem desserialização.
  • A colaboração entre agentes é usada.

Várias opções ResponseFormat estão disponíveis.

  • Uma propriedade interna ChatResponseFormat.Text: a resposta será texto sem formatação.
  • Uma propriedade interna ChatResponseFormat.Json : a resposta será um objeto JSON sem nenhum esquema específico.
  • Uma instância personalizada ChatResponseFormatJson : a resposta será um objeto JSON que está em conformidade com um esquema específico.

Observação

Primitivas e matrizes não são compatíveis com a ResponseFormat abordagem. Se você precisar trabalhar com primitivas ou matrizes, use a RunAsync<T> abordagem ou crie um tipo de wrapper.

// 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 também pode ser especificado usando uma string de esquema JSON bruta, que é útil quando não há nenhum tipo .NET correspondente disponível, como no caso de agentes declarativos ou esquemas carregados de uma configuração externa.

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()}");

Saída estruturada com streaming

Ao transmitir, a resposta do agente é transmitida como uma série de atualizações e você só pode desserializar a resposta depois que todas as atualizações forem recebidas. Você deve agrupar todas as atualizações em uma única resposta antes de desserializá-la.

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}");

Saída estruturada com agentes sem recursos de saída estruturados

Alguns agentes não dão suporte nativo à saída estruturada, seja porque ela não faz parte do protocolo ou porque os agentes usam modelos de linguagem sem recursos de saída estruturados. Uma abordagem possível é criar um agente decorador de funções personalizado que encapsula qualquer AIAgent e usa uma chamada LLM adicional através de um cliente de chat para converter a resposta de texto do agente em JSON estruturado.

Observação

Como essa abordagem depende de uma chamada adicional de LLM para transformar a resposta, sua confiabilidade pode não ser adequada para todos os cenários.

Para obter uma implementação de referência desse padrão que você pode adaptar aos seus próprios requisitos, consulte o exemplo StructuredOutputAgent.

Dica

Consulte os exemplos do .NET para obter exemplos executáveis completos.

Exemplo de streaming

Dica

Consulte os exemplos do .NET para obter exemplos executáveis completos.

Esta etapa do tutorial mostra como produzir uma saída estruturada com um agente, em que o agente é criado no serviço de Conclusão de Chat do Azure OpenAI.

Importante

Nem todos os tipos de agente dão suporte à saída estruturada. O Agent suporta a saída estruturada quando usado com clientes de chat compatíveis.

Pré-requisitos

Para pré-requisitos e instalação de pacotes, consulte a etapa Criar e executar um agente simples neste tutorial.

Criar o agente com saída estruturada

Ele Agent é construído sobre qualquer implementação de cliente de bate-papo que dê suporte à saída estruturada. O Agent usa o response_format parâmetro para especificar o esquema de saída desejado.

Ao criar ou executar o agente, você pode fornecer um modelo Pydantic que define a estrutura da saída esperada.

Há suporte para vários formatos de resposta com base nos recursos subjacentes do cliente de chat.

Este exemplo cria um agente que produz uma saída estruturada na forma de um objeto JSON que está em conformidade com um esquema de modelo Pydantic.

Primeiro, defina um modelo Pydantic que represente a estrutura da saída desejada do agente:

from pydantic import BaseModel

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

Agora você pode criar um agente usando o cliente de chat do 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."
)

Agora você pode executar o agente com algumas informações textuais e especificar o formato de saída estruturado usando o response_format parâmetro:

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

A resposta do agente conterá a saída estruturada na propriedade value, que pode ser acessada diretamente como uma instância do modelo 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")

Ao transmitir, agent.run(..., stream=True) retorna um ResponseStream. O finalizador interno do fluxo lida automaticamente com a análise de saída estruturada, para que você possa iterar para atualizações em tempo real e, em seguida, chamar get_final_response() para obter o resultado analisado:

# 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}")

Se você não precisar processar atualizações de streaming individuais, poderá ignorar totalmente a iteração – get_final_response() consumirá automaticamente o fluxo:

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}")

Exemplo completo

# 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())

Próximas etapas