Azure.AI.Agents.Persistent (.NET)
SDK de bajo nivel para crear y gestionar agentes de IA persistentes con hilos, mensajes, ejecuciones y herramientas.
Instalación
dotnet add package Azure.AI.Agents.Persistent --prerelease
dotnet add package Azure.Identity
Versiones actuales: Estable v1.1.0, Vista previa v1.2.0-beta.8
Variables de entorno
PROJECT_ENDPOINT=https://<resource>.services.ai.azure.com/api/projects/<project>
MODEL_DEPLOYMENT_NAME=gpt-4o-mini
AZURE_BING_CONNECTION_ID=<bing-connection-resource-id>
AZURE_AI_SEARCH_CONNECTION_ID=<search-connection-resource-id>
AZURE_TOKEN_CREDENTIALS=prod
Autenticación
using Azure.AI.Agents.Persistent;
using Azure.Identity;
var projectEndpoint = Environment.GetEnvironmentVariable("PROJECT_ENDPOINT");
var credential = new DefaultAzureCredential(
DefaultAzureCredential.DefaultEnvironmentVariableName
);
PersistentAgentsClient client = new(projectEndpoint, credential);
Jerarquía del cliente
PersistentAgentsClient
├── Administration → Operaciones CRUD de agente
├── Threads → Gestión de hilos
├── Messages → Operaciones de mensajes
├── Runs → Ejecución y transmisión de ejecuciones
├── Files → Carga/descarga de archivos
└── VectorStores → Gestión de almacenes vectoriales
Flujo de trabajo principal
1. Crear agente
var modelDeploymentName = Environment.GetEnvironmentVariable("MODEL_DEPLOYMENT_NAME");
PersistentAgent agent = await client.Administration.CreateAgentAsync(
model: modelDeploymentName,
name: "Tutor de matemáticas",
instructions: "Eres un tutor de matemáticas personal. Escribe y ejecuta código para responder preguntas de matemáticas.",
tools: [new CodeInterpreterToolDefinition()]
);
2. Crear hilo y mensaje
PersistentAgentThread thread = await client.Threads.CreateThreadAsync();
await client.Messages.CreateMessageAsync(
thread.Id,
MessageRole.User,
"Necesito resolver la ecuación `3x + 11 = 14`. ¿Puedes ayudarme?"
);
3. Ejecutar agente (sondeo)
ThreadRun run = await client.Runs.CreateRunAsync(
thread.Id,
agent.Id,
additionalInstructions: "Por favor, dirígete al usuario como Juan Pérez."
);
do
{
await Task.Delay(TimeSpan.FromMilliseconds(500));
run = await client.Runs.GetRunAsync(thread.Id, run.Id);
}
while (run.Status == RunStatus.Queued || run.Status == RunStatus.InProgress);
await foreach (PersistentThreadMessage message in client.Messages.GetMessagesAsync(
threadId: thread.Id,
order: ListSortOrder.Ascending))
{
Console.Write($"{message.Role}: ");
foreach (MessageContent content in message.ContentItems)
{
if (content is MessageTextContent textContent)
Console.WriteLine(textContent.Text);
}
}
4. Respuesta en streaming
AsyncCollectionResult<StreamingUpdate> stream = client.Runs.CreateRunStreamingAsync(
thread.Id,
agent.Id
);
await foreach (StreamingUpdate update in stream)
{
if (update.UpdateKind == StreamingUpdateReason.RunCreated)
{
Console.WriteLine("--- ¡Ejecución iniciada! ---");
}
else if (update is MessageContentUpdate contentUpdate)
{
Console.Write(contentUpdate.Text);
}
else if (update.UpdateKind == StreamingUpdateReason.RunCompleted)
{
Console.WriteLine("\n--- ¡Ejecución completada! ---");
}
}
5. Llamada a funciones
FunctionToolDefinition weatherTool = new(
name: "obtenerClimaActual",
description: "Obtiene el clima actual en una ubicación.",
parameters: BinaryData.FromObjectAsJson(new
{
Type = "object",
Properties = new
{
Ubicacion = new { Type = "string", Description = "Ciudad y estado, ej. San Francisco, CA" },
Unidad = new { Type = "string", Enum = new[] { "c", "f" } }
},
Required = new[] { "ubicacion" }
}, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase })
);
PersistentAgent agent = await client.Administration.CreateAgentAsync(
model: modelDeploymentName,
name: "Bot del clima",
instructions: "Eres un bot del clima.",
tools: [weatherTool]
);
do
{
await Task.Delay(500);
run = await client.Runs.GetRunAsync(thread.Id, run.Id);
if (run.Status == RunStatus.RequiresAction
&& run.RequiredAction is SubmitToolOutputsAction submitAction)
{
List<ToolOutput> outputs = [];
foreach (RequiredToolCall toolCall in submitAction.ToolCalls)
{
if (toolCall is RequiredFunctionToolCall funcCall)
{
string result = EjecutarFuncion(funcCall.Name, funcCall.Arguments);
outputs.Add(new ToolOutput(toolCall, result));
}
}
run = await client.Runs.SubmitToolOutputsToRunAsync(run, outputs, toolApprovals: null);
}
}
while (run.Status == RunStatus.Queued || run.Status == RunStatus.InProgress);
6. Búsqueda de archivos con almacén vectorial
PersistentAgentFileInfo file = await client.Files.UploadFileAsync(
filePath: "documento.txt",
purpose: PersistentAgentFilePurpose.Agents
);
PersistentAgentsVectorStore vectorStore = await client.VectorStores.CreateVectorStoreAsync(
fileIds: [file.Id],
name: "mi_almacen_vectorial"
);
FileSearchToolResource fileSearchResource = new();
fileSearchResource.VectorStoreIds.Add(vectorStore.Id);
PersistentAgent agent = await client.Administration.CreateAgentAsync(
model: modelDeploymentName,
name: "Asistente de documentos",
instructions: "Ayudas a los usuarios a encontrar información en documentos.",
tools: [new FileSearchToolDefinition()],
toolResources: new ToolResources { FileSearch = fileSearchResource }
);
7. Bing Grounding
var bingConnectionId = Environment.GetEnvironmentVariable("AZURE_BING_CONNECTION_ID");
BingGroundingToolDefinition bingTool = new(
new BingGroundingSearchToolParameters(
[new BingGroundingSearchConfiguration(bingConnectionId)]
)
);
PersistentAgent agent = await client.Administration.CreateAgentAsync(
model: modelDeploymentName,
name: "Agente de búsqueda",
instructions: "Usa Bing para responder preguntas sobre eventos actuales.",
tools: [bingTool]
);
8. Azure AI Search
AzureAISearchToolResource searchResource = new(
connectionId: searchConnectionId,
indexName: "mi_indice",
topK: 5,
filter: "category eq 'documentation'",
queryType: AzureAISearchQueryType.Simple
);
PersistentAgent agent = await client.Administration.CreateAgentAsync(
model: modelDeploymentName,
name: "Agente de búsqueda",
instructions: "Busca en el índice de documentación para responder preguntas.",
tools: [new AzureAISearchToolDefinition()],
toolResources: new ToolResources { AzureAISearch = searchResource }
);
9. Limpieza
await client.Threads.DeleteThreadAsync(thread.Id);
await client.Administration.DeleteAgentAsync(agent.Id);
await client.VectorStores.DeleteVectorStoreAsync(vectorStore.Id);
await client.Files.DeleteFileAsync(file.Id);
Herramientas disponibles
| Herramienta | Clase | Propósito |
|---|
| Intérprete de código | CodeInterpreterToolDefinition | Ejecutar código Python, generar visualizaciones |
| Búsqueda de archivos | FileSearchToolDefinition | Buscar en archivos subidos mediante almacenes vectoriales |
| Llamada a funciones | FunctionToolDefinition | Llamar funciones personalizadas |
| Bing Grounding | BingGroundingToolDefinition | Búsqueda web mediante Bing |
| Azure AI Search | AzureAISearchToolDefinition | Buscar en índices de Azure AI Search |
| OpenAPI | OpenApiToolDefinition | Llamar APIs externas mediante especificación OpenAPI |
| Azure Functions | AzureFunctionToolDefinition | Invocar Azure Functions |
| MCP | MCPToolDefinition | Herramientas del Protocolo de Contexto de Modelo |
| SharePoint | SharepointToolDefinition | Acceder a contenido de SharePoint |
| Microsoft Fabric | MicrosoftFabricToolDefinition | Acceder a datos de Fabric |
Tipos de actualización en streaming
| Tipo de actualización | Descripción |
|---|
StreamingUpdateReason.RunCreated | Ejecución iniciada |
StreamingUpdateReason.RunInProgress | Ejecución en proceso |
StreamingUpdateReason.RunCompleted | Ejecución finalizada |
StreamingUpdateReason.RunFailed | Ejecución con error |
MessageContentUpdate | Fragmento de contenido de texto |
RunStepUpdate | Cambio de estado del paso |
Referencia de tipos clave
| Tipo | Propósito |
|---|
PersistentAgentsClient | Punto de entrada principal |
PersistentAgent | Agente con modelo, instrucciones, herramientas |
PersistentAgentThread | Hilo de conversación |
PersistentThreadMessage | Mensaje en un hilo |
ThreadRun | Ejecución del agente contra un hilo |
RunStatus | En cola, en progreso, requiere acción, completado, fallido |
ToolResources | Recursos combinados de herramientas |
ToolOutput | Respuesta de llamada a función |
Buenas prácticas
- Desechar siempre los clientes — Usar instrucciones
using o eliminación explícita
- Sondear con retrasos adecuados — Se recomiendan 500 ms entre comprobaciones de estado
- Limpiar recursos — Eliminar hilos y agentes al terminar
- Manejar todos los estados de ejecución — Verificar
RequiresAction, Failed, Cancelled
- Usar streaming para UX en tiempo real — Mejor experiencia de usuario que el sondeo
- Almacenar IDs, no objetos — Referenciar agentes/hilos por ID
- Usar métodos asíncronos — Todas las operaciones deben ser asíncronas
Manejo de errores
using Azure;
try
{
var agent = await client.Administration.CreateAgentAsync(...);
}
catch (RequestFailedException ex) when (ex.Status == 404)
{
Console.WriteLine("Recurso no encontrado");
}
catch (RequestFailedException ex)
{
Console.WriteLine($"Error: {ex.Status} - {ex.ErrorCode}: {ex.Message}");
}
SDKs relacionados
| SDK | Propósito | Instalación |
|---|
Azure.AI.Agents.Persistent | Agentes de bajo nivel (este SDK) | dotnet add package Azure.AI.Agents.Persistent |
Azure.AI.Projects | Cliente de proyecto de alto nivel | dotnet add package Azure.AI.Projects |
Enlaces de referencia