Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
En este paso del tutorial se muestra cómo generar salidas estructuradas con un agente, donde el agente se basa en el servicio de finalización de chat de OpenAI Azure.
Importante
No todos los tipos de agente admiten salidas estructuradas de forma nativa.
ChatClientAgent admite salidas estructuradas cuando se usan con clientes de chat compatibles.
Prerrequisitos
Para conocer los requisitos previos e instalar paquetes NuGet, consulte el paso Creación y ejecución de un agente sencillo en este tutorial.
Definición de un tipo para salidas estructuradas
En primer lugar, defina un tipo que represente la estructura de la salida que desea del agente.
public class PersonInfo
{
public string? Name { get; set; }
public int? Age { get; set; }
public string? Occupation { get; set; }
}
Creación del agente
Cree un ChatClientAgent mediante el cliente de proyectos de IA de Azure.
using System;
using Azure.AI.Projects;
using Azure.Identity;
using Microsoft.Agents.AI;
AIAgent agent = new AIProjectClient(
new Uri("<your-foundry-project-endpoint>"),
new DefaultAzureCredential())
.AsAIAgent(
model: "gpt-4o-mini",
name: "HelpfulAssistant",
instructions: "You are a helpful assistant.");
Advertencia
DefaultAzureCredential es conveniente para el desarrollo, pero requiere una consideración cuidadosa en producción. En producción, considere usar una credencial específica (por ejemplo, ManagedIdentityCredential) para evitar problemas de latencia, sondeos de credenciales no deseados y posibles riesgos de seguridad de los mecanismos de respaldo.
Salidas estructuradas con RunAsync<T>
El RunAsync<T> método está disponible en la AIAgent clase base. Acepta un parámetro de tipo genérico que especifica el tipo de salida estructurado.
Este enfoque es aplicable cuando se conoce el tipo de salida estructurado en tiempo de compilación y se necesita una instancia tipada del resultado. Admite primitivos, matrices y tipos complejos.
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}");
Salidas estructuradas con ResponseFormat
Las salidas estructuradas se pueden configurar estableciendo la ResponseFormat propiedad en AgentRunOptions en tiempo de invocación o en el momento de inicialización del agente para los agentes que lo admiten, como ChatClientAgent y Foundry Agent.
Este enfoque es aplicable cuando:
- El tipo de salidas estructuradas no se conoce en tiempo de compilación.
- El esquema se representa como JSON sin formato.
- Las salidas estructuradas solo se pueden configurar en tiempo de creación del agente.
- Solo se necesita el texto JSON sin deserialización.
- Se usa la colaboración entre agentes.
Hay disponibles varias opciones para ResponseFormat :
- Una propiedad integrada ChatResponseFormat.Text : la respuesta será texto sin formato.
- Una propiedad integrada ChatResponseFormat.Json : la respuesta será un objeto JSON sin ningún esquema determinado.
- Una instancia personalizada ChatResponseFormatJson : la respuesta será un objeto JSON que se ajuste a un esquema específico.
Nota:
El enfoque ResponseFormat no admite tipos primitivos ni arreglos. Si necesita trabajar con primitivos o matrices, use el RunAsync<T> enfoque o cree un tipo contenedor.
// 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}");
El ResponseFormat también se puede especificar mediante una cadena de esquema JSON sin formato, lo que resulta útil cuando no hay ningún tipo de .NET correspondiente disponible, como para agentes declarativos o esquemas cargados desde la configuración 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()}");
Salidas estructuradas con streaming
Cuando se realiza una transmisión, la respuesta del agente se envía como una serie de actualizaciones, y solo se puede deserializar la respuesta una vez que se hayan recibido todas las actualizaciones. Debe ensamblar todas las actualizaciones en una única respuesta antes de deserializarla.
using System.Text.Json;
using Microsoft.Extensions.AI;
AIAgent agent = new AIProjectClient(
new Uri("<your-foundry-project-endpoint>"),
new DefaultAzureCredential())
.AsAIAgent(new ChatClientAgentOptions()
{
Name = "HelpfulAssistant",
ChatOptions = new()
{
ModelId = "gpt-4o-mini",
Instructions = "You are a helpful assistant.",
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}");
Salidas estructuradas con agentes que carecen de capacidades de salidas estructuradas.
Algunos agentes no admiten de forma nativa salidas estructuradas, ya sea porque no forma parte del protocolo o porque los agentes usan modelos de lenguaje sin funcionalidades de salidas estructuradas. Un posible enfoque consiste en crear un agente decorador personalizado que envuelva cualquier AIAgent y utilice una llamada LLM adicional a través de un cliente de chat para convertir la respuesta de texto del agente en JSON estructurado.
Nota:
Dado que este enfoque se basa en una llamada LLM adicional para transformar la respuesta, es posible que su confiabilidad no sea suficiente para todos los escenarios.
Para obtener una implementación de referencia de este patrón que puede adaptar a sus propios requisitos, consulte el ejemplo StructuredOutputAgent.
Sugerencia
Consulte los ejemplos de .NET para obtener ejemplos completos ejecutables.
Ejemplo de streaming
Sugerencia
Consulte los ejemplos de .NET para obtener ejemplos completos ejecutables.
En este paso del tutorial se muestra cómo generar salidas estructuradas con un agente, donde el agente se basa en el servicio de finalización de chat de OpenAI Azure.
Importante
No todos los tipos de agente admiten salidas estructuradas.
Agent admite salidas estructuradas cuando se usan con clientes de chat compatibles.
Prerrequisitos
Para conocer los requisitos previos e instalar paquetes, consulte el paso Crear y ejecutar un agente sencillo en este tutorial.
Creación del agente con salidas estructuradas
Agent se construye sobre cualquier implementación de un cliente de chat que admita salidas estructuradas.
Agent usa la response_format clave en el options dict para especificar el esquema de salida deseado.
Al ejecutar el agente, puede proporcionar uno de los siguientes:
- Modelo Pydantic que define la estructura de la salida esperada.
- Una asignación de esquema JSON (
dict) cuando desee analizar JSON sin definir una clase de modelo.
Puede pasar el options dict en tiempo de ejecución a través de agent.run(..., options={"response_format": ...}), o establecerlo en el momento de la creación del agente a través del default_options dict.
Se admiten varios formatos de respuesta en función de las funcionalidades subyacentes del cliente de chat.
En el primer ejemplo se crea un agente que genera salidas estructuradas en forma de un objeto JSON que se ajusta a un esquema de modelo Pydantic.
En primer lugar, defina un modelo Pydantic que represente la estructura de la salida que desea del agente:
from pydantic import BaseModel
class PersonInfo(BaseModel):
"""Information about a person."""
name: str | None = None
age: int | None = None
occupation: str | None = None
Ahora puede crear un agente mediante el Azure cliente de chat de OpenAI:
import os
from agent_framework.openai import OpenAIChatCompletionClient
from azure.identity import AzureCliCredential
# Create the agent using Azure OpenAI Chat Client
agent = OpenAIChatCompletionClient(
model=os.environ["AZURE_OPENAI_CHAT_COMPLETION_MODEL"],
azure_endpoint=os.environ["AZURE_OPENAI_ENDPOINT"],
api_version=os.getenv("AZURE_OPENAI_API_VERSION"),
credential=AzureCliCredential(),
).as_agent(
name="HelpfulAssistant",
instructions="You are a helpful assistant that extracts person information from text."
)
Ahora puede ejecutar el agente con información textual y especificar el formato de las salidas estructuradas mediante la clave response_format en el diccionario options.
response = await agent.run(
"Please provide information about John Smith, who is a 35-year-old software engineer.",
options={"response_format": PersonInfo},
)
Para un formato de respuesta del modelo Pydantic, la respuesta del agente contiene las salidas estructuradas de la value propiedad como una instancia de modelo:
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")
Utilice un mapeo de esquema JSON
Si ya tiene un esquema JSON como un mapeo de Python, pase ese esquema directamente como valor de response_format en el diccionario options. En este modo, response.value contiene el valor JSON analizado (normalmente un dict o list) en lugar de una instancia del modelo Pydantic.
person_info_schema = {
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"},
"occupation": {"type": "string"},
},
"required": ["name", "age", "occupation"],
}
response = await agent.run(
"Please provide information about John Smith, who is a 35-year-old software engineer.",
options={"response_format": person_info_schema},
)
if response.value:
person_info = response.value
print(f"Name: {person_info['name']}, Age: {person_info['age']}, Occupation: {person_info['occupation']}")
Cuando se transmite, agent.run(..., stream=True) devuelve un ResponseStream. El finalizador integrado de la secuencia controla automáticamente el análisis de salidas estructuradas, por lo que puede iterar para recibir actualizaciones en tiempo real y, a continuación, llamar a get_final_response() para obtener el resultado analizado.
# 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}")
La misma regla se aplica cuando response_format es una asignación de esquema JSON: final_response.value contiene JSON analizado en lugar de una instancia del modelo Pydantic.
Si no necesita procesar actualizaciones de streaming individuales, puede omitir la iteración por completo; get_final_response() consumirá automáticamente la secuencia:
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}")
Ejemplo completo
# Copyright (c) Microsoft. All rights reserved.
import asyncio
from agent_framework.openai import OpenAIChatClient
from pydantic import BaseModel
"""
OpenAI Responses Client with Structured Outputs Example
This sample demonstrates using structured outputs capabilities with OpenAI Responses Client,
showing Pydantic model integration for type-safe response parsing and data extraction.
"""
class OutputStruct(BaseModel):
"""A structured outputs model for testing purposes."""
city: str
description: str
async def non_streaming_example() -> None:
print("=== Non-streaming example ===")
agent = OpenAIChatClient().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 Outputs 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 = OpenAIChatClient().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 outputs parsed
result = await stream.get_final_response()
if structured_data := result.value:
print("Structured Outputs (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 Outputs ===")
await non_streaming_example()
await streaming_example()
if __name__ == "__main__":
asyncio.run(main())