From 38785ed7d8d9d4398ff968a0bf403ad6a889aeb3 Mon Sep 17 00:00:00 2001 From: Omar Aviles Date: Tue, 7 Apr 2026 18:55:31 +0000 Subject: [PATCH 01/13] Refactor AndersAgent implementation for Azure AI Foundry - Replaced the existing agent code with a new implementation using the Azure.AI.Projects and Microsoft.Agents.AI namespaces. - Updated appsettings.json to reflect new configuration structure. - Removed obsolete nuget.config file. - Simplified the lab documentation, focusing on the new agent implementation and its features. - Added deployment scripts (deploy-foundry-agent.ps1 and deploy-foundry-agent.sh) for easier agent deployment. - Created a new project file (ms_foundry_agent.csproj) and solution file (ms_foundry_agent_v2.sln) for the updated agent. - Introduced a simple weather function as a demonstration tool for the agent. --- .../AndersAgent/ai-foundry/AndersAgent.csproj | 24 -- .../agents/AndersAgent/ai-foundry/Program.cs | 206 ----------- .../AndersAgent/ai-foundry/appsettings.json | 6 - .../AndersAgent/ms-foundry/AndersAgent.csproj | 25 -- .../agents/AndersAgent/ms-foundry/Program.cs | 338 ++++++------------ .../AndersAgent/ms-foundry/appsettings.json | 13 +- .../ms-foundry/deploy-foundry-agent.ps1 | 52 +++ .../ms-foundry/deploy-foundry-agent.sh | 40 +++ .../ms-foundry/ms_foundry_agent.csproj | 35 ++ .../ms-foundry/ms_foundry_agent_v2.sln | 24 ++ .../AndersAgent/ms-foundry/nuget.config | 15 - .../foundry/lab03-anders-executor-agent.md | 320 ++++------------- 12 files changed, 333 insertions(+), 765 deletions(-) delete mode 100644 es/labs/foundry/code/agents/AndersAgent/ai-foundry/AndersAgent.csproj delete mode 100644 es/labs/foundry/code/agents/AndersAgent/ai-foundry/Program.cs delete mode 100644 es/labs/foundry/code/agents/AndersAgent/ai-foundry/appsettings.json delete mode 100644 es/labs/foundry/code/agents/AndersAgent/ms-foundry/AndersAgent.csproj create mode 100644 es/labs/foundry/code/agents/AndersAgent/ms-foundry/deploy-foundry-agent.ps1 create mode 100644 es/labs/foundry/code/agents/AndersAgent/ms-foundry/deploy-foundry-agent.sh create mode 100644 es/labs/foundry/code/agents/AndersAgent/ms-foundry/ms_foundry_agent.csproj create mode 100644 es/labs/foundry/code/agents/AndersAgent/ms-foundry/ms_foundry_agent_v2.sln delete mode 100644 es/labs/foundry/code/agents/AndersAgent/ms-foundry/nuget.config diff --git a/es/labs/foundry/code/agents/AndersAgent/ai-foundry/AndersAgent.csproj b/es/labs/foundry/code/agents/AndersAgent/ai-foundry/AndersAgent.csproj deleted file mode 100644 index 59a2503..0000000 --- a/es/labs/foundry/code/agents/AndersAgent/ai-foundry/AndersAgent.csproj +++ /dev/null @@ -1,24 +0,0 @@ - - - - Exe - net8.0 - enable - enable - AndersAgent.AIFoundry - - - - - - - - - - - - PreserveNewest - - - - diff --git a/es/labs/foundry/code/agents/AndersAgent/ai-foundry/Program.cs b/es/labs/foundry/code/agents/AndersAgent/ai-foundry/Program.cs deleted file mode 100644 index b5f805d..0000000 --- a/es/labs/foundry/code/agents/AndersAgent/ai-foundry/Program.cs +++ /dev/null @@ -1,206 +0,0 @@ -using Azure.AI.Projects; -using Azure.AI.Agents.Persistent; -using Azure.Identity; -using Microsoft.Extensions.Configuration; - -#pragma warning disable CA2252 // API en preview - -// --- Cargar configuración --- -var config = new ConfigurationBuilder() - .AddJsonFile("appsettings.json") - .Build(); - -var foundryEndpoint = config["FoundryProjectEndpoint"] - ?? throw new InvalidOperationException("Falta FoundryProjectEndpoint en appsettings.json"); -var modelDeployment = config["ModelDeploymentName"] - ?? throw new InvalidOperationException("Falta ModelDeploymentName en appsettings.json"); -var functionAppBaseUrl = config["FunctionAppBaseUrl"] - ?? throw new InvalidOperationException("Falta FunctionAppBaseUrl en appsettings.json"); -var tenantId = config["TenantId"]; - -// ===================================================================== -// FASE 1: Obtener la especificación OpenAPI de la Function App -// ===================================================================== - -Console.WriteLine("[OpenAPI] Descargando especificación desde la Function App..."); - -var httpClient = new HttpClient(); -var openApiSpecUrl = $"{functionAppBaseUrl}/openapi/v3.json"; -var openApiSpec = await httpClient.GetStringAsync(openApiSpecUrl); - -Console.WriteLine($"[OpenAPI] Especificación descargada ({openApiSpec.Length} bytes)"); - -// ===================================================================== -// FASE 2: Crear agente con herramienta OpenAPI en Foundry -// ===================================================================== - -// Cliente del proyecto Foundry -// Si TenantId está configurado, se usa explícitamente para evitar conflictos -// en máquinas con múltiples tenants de Azure (error 400 "Token tenant does not match"). -var credentialOptions = new DefaultAzureCredentialOptions(); -if (!string.IsNullOrWhiteSpace(tenantId)) - credentialOptions.TenantId = tenantId; - -var projectClient = new AIProjectClient( - new Uri(foundryEndpoint), - new DefaultAzureCredential(credentialOptions)); - -// Obtener el cliente de agentes persistentes -var agentsClient = projectClient.GetPersistentAgentsClient(); - -// Definir la herramienta OpenAPI a partir de la especificación descargada -var openApiTool = new OpenApiToolDefinition( - new OpenApiFunctionDefinition( - name: "ContosoRetailAPI", - spec: BinaryData.FromString(openApiSpec), - openApiAuthentication: new OpenApiAnonymousAuthDetails()) - { - Description = "API de Contoso Retail para generar reportes de órdenes de compra" - }); - -// Instrucciones del agente Anders -var andersInstructions = """ - Eres Anders, el agente ejecutor de Contoso Retail. - - Tu responsabilidad es ejecutar acciones operativas concretas cuando se te soliciten. - Tu principal capacidad es generar reportes de órdenes de compra de clientes - usando la API de Contoso Retail disponible como herramienta OpenAPI. - - Cuando recibas datos de órdenes, debes construir el JSON del request body - con EXACTAMENTE este schema para invocar el endpoint ordersReporter: - - { - "customerName": "Nombre del Cliente", - "startDate": "YYYY-MM-DD", - "endDate": "YYYY-MM-DD", - "orders": [ - { - "orderNumber": "código de la orden", - "orderDate": "YYYY-MM-DD", - "orderLineNumber": 1, - "productName": "nombre del producto", - "brandName": "nombre de la marca", - "categoryName": "nombre de la categoría", - "quantity": 1.0, - "unitPrice": 0.00, - "lineTotal": 0.00 - } - ] - } - - Reglas: - - TODOS los campos son obligatorios para cada línea de orden. - - Si una orden tiene múltiples productos, cada producto es un elemento - separado en el array "orders" con el mismo "orderNumber" y "orderDate" - pero diferente "orderLineNumber" (secuencial: 1, 2, 3...). - - Las fechas deben estar en formato ISO: YYYY-MM-DD. - - "quantity", "unitPrice" y "lineTotal" son numéricos (double). - - Siempre confirma la acción realizada al usuario, incluyendo la URL del reporte. - Si los datos son insuficientes o inválidos, explica qué falta. - Responde en español. - """; - -Console.WriteLine("[Foundry] Buscando agente Anders existente..."); - -PersistentAgent? agent = null; - -// Buscar si ya existe un agente con el mismo nombre -await foreach (var existingAgent in agentsClient.Administration.GetAgentsAsync()) -{ - if (existingAgent.Name == "Anders - Agente Ejecutor") - { - agent = existingAgent; - Console.WriteLine($"[Foundry] Agente existente encontrado: {agent.Name} (ID: {agent.Id})"); - break; - } -} - -if (agent is null) -{ - Console.WriteLine("[Foundry] Creando agente Anders con herramienta OpenAPI..."); - - agent = (await agentsClient.Administration.CreateAgentAsync( - model: modelDeployment, - name: "Anders - Agente Ejecutor", - description: "Agente ejecutor de Contoso Retail con herramienta OpenAPI", - instructions: andersInstructions, - tools: new List { openApiTool })).Value; - - Console.WriteLine($"[Foundry] Agente creado: {agent.Name} (ID: {agent.Id})"); -} - -// ===================================================================== -// FASE 3: Interactuar con el agente (threads & runs) -// ===================================================================== - -PersistentAgentThread thread = (await agentsClient.Threads.CreateThreadAsync()).Value; -Console.WriteLine($"[Foundry] Thread creado: {thread.Id}"); -Console.WriteLine(); -Console.WriteLine("=== Chat con Anders (escribe 'salir' para terminar) ==="); -Console.WriteLine(); - -while (true) -{ - Console.Write("Tú: "); - var input = Console.ReadLine(); - - if (string.IsNullOrWhiteSpace(input) || - input.Equals("salir", StringComparison.OrdinalIgnoreCase)) - break; - - // Enviar mensaje del usuario al thread - await agentsClient.Messages.CreateMessageAsync( - threadId: thread.Id, - role: MessageRole.User, - content: input); - - // Ejecutar el agente sobre el thread - ThreadRun run = (await agentsClient.Runs.CreateRunAsync(thread, agent)).Value; - - // Esperar a que el run termine (polling) - Console.Write("Anders: "); - while (run.Status == RunStatus.Queued || run.Status == RunStatus.InProgress) - { - await Task.Delay(TimeSpan.FromSeconds(1)); - run = (await agentsClient.Runs.GetRunAsync(thread.Id, run.Id)).Value; - } - - // Procesar resultado - if (run.Status == RunStatus.Completed) - { - // Obtener mensajes del thread (los más recientes primero) - var messages = agentsClient.Messages.GetMessagesAsync(threadId: thread.Id); - - await foreach (PersistentThreadMessage message in messages) - { - // Solo mostrar la primera respuesta del agente (la más reciente) - if (message.Role == MessageRole.Agent) - { - foreach (MessageContent contentItem in message.ContentItems) - { - if (contentItem is MessageTextContent textContent) - { - Console.WriteLine(textContent.Text); - } - } - break; - } - } - } - else - { - Console.WriteLine($"\n[Error] Run terminó con estado: {run.Status}"); - if (run.LastError != null) - Console.WriteLine($"[Error] {run.LastError.Code}: {run.LastError.Message}"); - } - Console.WriteLine(); -} - -// ===================================================================== -// Limpieza del thread (el agente persiste para reutilizarse) -// ===================================================================== - -Console.WriteLine("[Foundry] Limpiando thread..."); -await agentsClient.Threads.DeleteThreadAsync(thread.Id); -Console.WriteLine($"[Foundry] Thread eliminado. El agente '{agent.Name}' (ID: {agent.Id}) permanece disponible."); diff --git a/es/labs/foundry/code/agents/AndersAgent/ai-foundry/appsettings.json b/es/labs/foundry/code/agents/AndersAgent/ai-foundry/appsettings.json deleted file mode 100644 index 3467b94..0000000 --- a/es/labs/foundry/code/agents/AndersAgent/ai-foundry/appsettings.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "FoundryProjectEndpoint": "https://ais-contosoretail-.services.ai.azure.com/api/projects/aip-contosoretail-", - "ModelDeploymentName": "gpt-4.1", - "FunctionAppBaseUrl": "https://func-contosoretail-.azurewebsites.net/api", - "TenantId": "" -} diff --git a/es/labs/foundry/code/agents/AndersAgent/ms-foundry/AndersAgent.csproj b/es/labs/foundry/code/agents/AndersAgent/ms-foundry/AndersAgent.csproj deleted file mode 100644 index 17279f8..0000000 --- a/es/labs/foundry/code/agents/AndersAgent/ms-foundry/AndersAgent.csproj +++ /dev/null @@ -1,25 +0,0 @@ - - - - Exe - net8.0 - enable - enable - AndersAgent.MsFoundry - - - - - - - - - - - - - PreserveNewest - - - - diff --git a/es/labs/foundry/code/agents/AndersAgent/ms-foundry/Program.cs b/es/labs/foundry/code/agents/AndersAgent/ms-foundry/Program.cs index 201a2b9..2a8bae9 100644 --- a/es/labs/foundry/code/agents/AndersAgent/ms-foundry/Program.cs +++ b/es/labs/foundry/code/agents/AndersAgent/ms-foundry/Program.cs @@ -1,223 +1,115 @@ -using Azure.AI.Projects; -using Azure.AI.Projects.OpenAI; -using Azure.Identity; -using Microsoft.Extensions.Configuration; -using System.ClientModel; -using System.ClientModel.Primitives; -using System.Text.Json; -using OpenAI.Responses; - -#pragma warning disable OPENAI001 // OpenAI preview API - -// ===================================================================== -// Anders - Agente Ejecutor (Microsoft Foundry - nueva experiencia) -// -// Esta versión usa el SDK Azure.AI.Projects + Azure.AI.Projects.OpenAI -// con la API de Responses (nueva experiencia de Microsoft Foundry). -// -// La herramienta OpenAPI se configura vía protocol method (BinaryContent) -// ya que los tipos OpenApiAgentTool son internos en el SDK 1.2.x. -// ===================================================================== - -// --- Cargar configuración --- -var config = new ConfigurationBuilder() - .AddJsonFile("appsettings.json") - .Build(); - -var foundryEndpoint = config["FoundryProjectEndpoint"] - ?? throw new InvalidOperationException("Falta FoundryProjectEndpoint en appsettings.json"); -var modelDeployment = config["ModelDeploymentName"] - ?? throw new InvalidOperationException("Falta ModelDeploymentName en appsettings.json"); -var functionAppBaseUrl = config["FunctionAppBaseUrl"] - ?? throw new InvalidOperationException("Falta FunctionAppBaseUrl en appsettings.json"); -var tenantId = config["TenantId"]; -var agentName = "Anders"; - -// ===================================================================== -// FASE 1: Obtener la especificación OpenAPI de la Function App -// ===================================================================== - -Console.WriteLine("[OpenAPI] Descargando especificación desde la Function App..."); - -var httpClient = new HttpClient(); -var openApiSpecUrl = $"{functionAppBaseUrl}/openapi/v3.json"; -var openApiSpec = await httpClient.GetStringAsync(openApiSpecUrl); - -Console.WriteLine($"[OpenAPI] Especificación descargada ({openApiSpec.Length} bytes)"); - -// ===================================================================== -// FASE 2: Crear agente con herramienta OpenAPI (protocol method) -// ===================================================================== - -// Instrucciones del agente Anders -var andersInstructions = """ - Eres Anders, el agente ejecutor de Contoso Retail. - - Tu responsabilidad es ejecutar acciones operativas concretas cuando se te soliciten. - Tu principal capacidad es generar reportes de órdenes de compra de clientes - usando la API de Contoso Retail disponible como herramienta OpenAPI. - - Cuando recibas datos de órdenes, debes construir el JSON del request body - con EXACTAMENTE este schema para invocar el endpoint ordersReporter: - - { - "customerName": "Nombre del Cliente", - "startDate": "YYYY-MM-DD", - "endDate": "YYYY-MM-DD", - "orders": [ - { - "orderNumber": "código de la orden", - "orderDate": "YYYY-MM-DD", - "orderLineNumber": 1, - "productName": "nombre del producto", - "brandName": "nombre de la marca", - "categoryName": "nombre de la categoría", - "quantity": 1.0, - "unitPrice": 0.00, - "lineTotal": 0.00 - } - ] - } - - Reglas: - - TODOS los campos son obligatorios para cada línea de orden. - - Si una orden tiene múltiples productos, cada producto es un elemento - separado en el array "orders" con el mismo "orderNumber" y "orderDate" - pero diferente "orderLineNumber" (secuencial: 1, 2, 3...). - - Las fechas deben estar en formato ISO: YYYY-MM-DD. - - "quantity", "unitPrice" y "lineTotal" son numéricos (double). - - Siempre confirma la acción realizada al usuario, incluyendo la URL del reporte. - Si los datos son insuficientes o inválidos, explica qué falta. - Responde en español. - """; - -// Cliente del proyecto Foundry (nueva experiencia) -// Si TenantId está configurado, se usa explícitamente para evitar conflictos -// en máquinas con múltiples tenants de Azure (error 400 "Token tenant does not match"). -var credentialOptions = new DefaultAzureCredentialOptions(); -if (!string.IsNullOrWhiteSpace(tenantId)) - credentialOptions.TenantId = tenantId; - -AIProjectClient projectClient = new( - endpoint: new Uri(foundryEndpoint), - tokenProvider: new DefaultAzureCredential(credentialOptions)); - -// Verificar si el agente ya existe -bool shouldCreateAgent = true; -AgentRecord? existingAgent = null; - -Console.WriteLine($"[Foundry] Buscando agente existente '{agentName}'..."); -try -{ - existingAgent = projectClient.Agents.GetAgent(agentName); - Console.WriteLine($"[Foundry] Agente encontrado: {existingAgent.Name} (ID: {existingAgent.Id})"); - Console.Write("[Foundry] ¿Desea sobreescribirlo con una nueva versión? (s/N): "); - var answer = Console.ReadLine(); - shouldCreateAgent = answer?.Trim().Equals("s", StringComparison.OrdinalIgnoreCase) == true - || answer?.Trim().Equals("si", StringComparison.OrdinalIgnoreCase) == true - || answer?.Trim().Equals("sí", StringComparison.OrdinalIgnoreCase) == true; - - if (!shouldCreateAgent) - { - Console.WriteLine("[Foundry] Se conserva el agente existente."); - } -} -catch (ClientResultException ex) when (ex.Status == 404) -{ - Console.WriteLine($"[Foundry] No se encontró un agente existente con nombre '{agentName}'. Se creará uno nuevo."); -} - -AgentRecord agentRecord; - -if (shouldCreateAgent) -{ - // Construir el JSON con la definición del agente incluyendo herramienta OpenAPI - // (los tipos OpenApiAgentTool son internos, se usa protocol method con BinaryContent) - Console.WriteLine("[Foundry] Creando/actualizando agente Anders con herramienta OpenAPI..."); - - var openApiSpecJson = JsonSerializer.Deserialize(openApiSpec); - - var agentDefinitionJson = new - { - definition = new - { - kind = "prompt", - model = modelDeployment, - instructions = andersInstructions, - tools = new object[] - { - new - { - type = "openapi", - openapi = new - { - name = "ContosoRetailAPI", - description = "API de Contoso Retail para generar reportes de órdenes de compra", - spec = openApiSpecJson, - auth = new { type = "anonymous" } - } - } - } - } - }; - - var jsonContent = JsonSerializer.Serialize(agentDefinitionJson, new JsonSerializerOptions { WriteIndented = false }); - var result = await projectClient.Agents.CreateAgentVersionAsync( - agentName, - BinaryContent.Create(BinaryData.FromString(jsonContent)), - new RequestOptions()); - - // Parsear respuesta para obtener info del agente - var responseJson = JsonDocument.Parse(result.GetRawResponse().Content.ToString()); - var version = responseJson.RootElement.TryGetProperty("version", out var vProp) ? vProp.GetString() : "?"; - Console.WriteLine($"[Foundry] Agente creado/actualizado: {agentName} (v{version})"); -} - -// Obtener el agente registrado -agentRecord = projectClient.Agents.GetAgent(agentName); -Console.WriteLine($"[Foundry] Agente obtenido: {agentRecord.Name} (ID: {agentRecord.Id})"); - -// ===================================================================== -// FASE 3: Interactuar con el agente (Responses API + Conversations) -// ===================================================================== - -// Crear conversación para multi-turn -ProjectConversation conversation = projectClient.OpenAI.Conversations.CreateProjectConversation(); -Console.WriteLine($"[Foundry] Conversación creada: {conversation.Id}"); - -// Obtener cliente de Responses vinculado al agente y conversación -ProjectResponsesClient responseClient = projectClient.OpenAI.GetProjectResponsesClientForAgent( - defaultAgent: agentName, - defaultConversationId: conversation.Id); - -Console.WriteLine(); -Console.WriteLine("=== Chat con Anders (escribe 'salir' para terminar) ==="); -Console.WriteLine(); - -while (true) -{ - Console.Write("Tú: "); - var input = Console.ReadLine(); - - if (string.IsNullOrWhiteSpace(input) || - input.Equals("salir", StringComparison.OrdinalIgnoreCase)) - break; - - // Enviar mensaje y obtener respuesta del agente - Console.Write("Anders: "); - try - { - ResponseResult response = responseClient.CreateResponse(input); - Console.WriteLine(response.GetOutputText()); - } - catch (Exception ex) - { - Console.WriteLine($"\n[Error] {ex.Message}"); - } - - Console.WriteLine(); -} - -Console.WriteLine("[Foundry] Chat finalizado."); -Console.WriteLine($"[Foundry] El agente '{agentName}' permanece disponible."); +using System.ComponentModel; +using Azure.AI.Projects; +using Azure.Identity; +using Microsoft.Agents.AI; +using Microsoft.Extensions.AI; +using Microsoft.Extensions.Configuration; +using System.Collections.Generic; + +namespace MsFoundryAgent; + +public static class Program +{ + private const string DefaultAgentName = "Andres-Agent"; + private const string DefaultAgentInstructions = + "You are an analytical AI agent specialized in reading, understanding, and extracting insights from provided information."; + + // ----------------------------------------------------------------------- + // Tool: a simple weather stub to demonstrate function calling + // ----------------------------------------------------------------------- + [Description("Get the current weather for a given location.")] + public static string GetWeather( + [Description("The city or location name, e.g. 'Seattle'")] string location) + { + var rand = new Random(); + string[] conditions = ["sunny", "cloudy", "rainy", "stormy"]; + return $"The weather in {location} is {conditions[rand.Next(conditions.Length)]} " + + $"with a high of {rand.Next(10, 35)}°C."; + } + + public static async Task Main(string[] args) + { + IConfiguration config = new ConfigurationBuilder() + .AddJsonFile("appsettings.json", optional: false) + .AddEnvironmentVariables() + .Build(); + + string projectEndpoint = config["Foundry:ProjectEndpoint"] + ?? throw new InvalidOperationException("Foundry:ProjectEndpoint is not configured."); + + string modelDeployment = config["Foundry:ModelDeployment"] + ?? throw new InvalidOperationException("Foundry:ModelDeployment is not configured."); + + string agentName = config["Foundry:AgentName"] ?? DefaultAgentName; + string agentInstructions = config["Foundry:AgentInstructions"] ?? DefaultAgentInstructions; + + var aiProjectClient = new AIProjectClient( + new Uri(projectEndpoint), + new DefaultAzureCredential()); + + Console.WriteLine($"Creating agent '{agentName}' on Azure AI Foundry..."); + + ChatClientAgent agent = await aiProjectClient.CreateAIAgentAsync( + name: agentName, + model: modelDeployment, + instructions: agentInstructions, + description: null, + tools: [AIFunctionFactory.Create(GetWeather)]); + + if (args.Length > 0 && args[0].Equals("deploy", StringComparison.OrdinalIgnoreCase)) + { + Console.WriteLine($"Agent '{agent.Name}' deployed successfully."); + Console.WriteLine("The agent was left in Azure AI Foundry and was not deleted."); + return; + } + + if (args.Length > 0 && args[0].Equals("verify", StringComparison.OrdinalIgnoreCase)) + { + ChatClientAgent found = await aiProjectClient.GetAIAgentAsync(agentName, tools: [AIFunctionFactory.Create(GetWeather)]); + Console.WriteLine("Agent verification succeeded."); + Console.WriteLine($"Name: {found.Name}"); + Console.WriteLine($"Model: {modelDeployment}"); + Console.WriteLine($"Endpoint: {projectEndpoint}"); + Console.WriteLine("If the portal does not show it, confirm you are in the same Foundry project and tenant."); + return; + } + + Console.WriteLine("Agent created. Starting multi-turn conversation (type 'quit' to exit).\n"); + + var history = new List(); + + while (true) + { + Console.Write("You: "); + string? userInput = Console.ReadLine(); + + if (string.IsNullOrWhiteSpace(userInput) || + userInput.Equals("quit", StringComparison.OrdinalIgnoreCase)) + { + break; + } + + history.Add(new ChatMessage(ChatRole.User, userInput)); + + Console.Write("Agent: "); + var assistantText = new System.Text.StringBuilder(); + await foreach (AgentResponseUpdate update in agent.RunStreamingAsync(history)) + { + if (!string.IsNullOrEmpty(update.Text)) + { + Console.Write(update.Text); + assistantText.Append(update.Text); + } + } + Console.WriteLine("\n"); + + // Append the assistant reply so future turns have full context + if (assistantText.Length > 0) + history.Add(new ChatMessage(ChatRole.Assistant, assistantText.ToString())); + } + + Console.WriteLine("Cleaning up agent..."); + await aiProjectClient.Agents.DeleteAgentAsync(agent.Name); + Console.WriteLine("Done."); + } +} diff --git a/es/labs/foundry/code/agents/AndersAgent/ms-foundry/appsettings.json b/es/labs/foundry/code/agents/AndersAgent/ms-foundry/appsettings.json index 3467b94..9e792c4 100644 --- a/es/labs/foundry/code/agents/AndersAgent/ms-foundry/appsettings.json +++ b/es/labs/foundry/code/agents/AndersAgent/ms-foundry/appsettings.json @@ -1,6 +1,7 @@ -{ - "FoundryProjectEndpoint": "https://ais-contosoretail-.services.ai.azure.com/api/projects/aip-contosoretail-", - "ModelDeploymentName": "gpt-4.1", - "FunctionAppBaseUrl": "https://func-contosoretail-.azurewebsites.net/api", - "TenantId": "" -} +{ + "Foundry": { + "ProjectEndpoint": "https://.services.ai.azure.com/api/projects/", + "ModelDeployment": "gpt-4.1", + "AgentName": "AndersAgent" + } +} diff --git a/es/labs/foundry/code/agents/AndersAgent/ms-foundry/deploy-foundry-agent.ps1 b/es/labs/foundry/code/agents/AndersAgent/ms-foundry/deploy-foundry-agent.ps1 new file mode 100644 index 0000000..8b330fa --- /dev/null +++ b/es/labs/foundry/code/agents/AndersAgent/ms-foundry/deploy-foundry-agent.ps1 @@ -0,0 +1,52 @@ +param( + [string]$FoundryProjectEndpoint = $env:FOUNDRY_PROJECT_ENDPOINT, + [string]$FoundryModelDeploymentName = $env:FOUNDRY_MODEL_DEPLOYMENT_NAME, + [string]$FoundryAgentName = $(if ($env:FOUNDRY_AGENT_NAME) { $env:FOUNDRY_AGENT_NAME } else { "AndersAgent" }), + [string]$FoundryAgentInstructions = $(if ($env:FOUNDRY_AGENT_INSTRUCTIONS) { $env:FOUNDRY_AGENT_INSTRUCTIONS } else { "You are an analytical AI agent specialized in reading, understanding, and extracting insights from provided information." }), + [string]$ProjectFile = "./ms_foundry_agent.csproj" +) + +$ErrorActionPreference = "Stop" + +function Require-Command { + param([Parameter(Mandatory = $true)][string]$Name) + + if (-not (Get-Command $Name -ErrorAction SilentlyContinue)) { + throw "Missing required command: $Name" + } +} + +function Require-Value { + param( + [Parameter(Mandatory = $true)][string]$Name, + [Parameter(Mandatory = $true)][string]$Value + ) + + if ([string]::IsNullOrWhiteSpace($Value)) { + throw "Missing required value: $Name" + } +} + +Require-Command -Name "az" +Require-Command -Name "dotnet" + +Require-Value -Name "FoundryProjectEndpoint (or FOUNDRY_PROJECT_ENDPOINT)" -Value $FoundryProjectEndpoint +Require-Value -Name "FoundryModelDeploymentName (or FOUNDRY_MODEL_DEPLOYMENT_NAME)" -Value $FoundryModelDeploymentName + +$resolvedProjectFile = Resolve-Path -Path $ProjectFile -ErrorAction Stop + +try { + az account show | Out-Null +} +catch { + Write-Host "Azure CLI is not logged in. Running az login..." + az login | Out-Null +} + +$env:Foundry__ProjectEndpoint = $FoundryProjectEndpoint +$env:Foundry__ModelDeployment = $FoundryModelDeploymentName +$env:Foundry__AgentName = $FoundryAgentName +$env:Foundry__AgentInstructions = $FoundryAgentInstructions + +Write-Host "Deploying agent '$FoundryAgentName' to Azure AI Foundry..." +dotnet run --project $resolvedProjectFile -- deploy diff --git a/es/labs/foundry/code/agents/AndersAgent/ms-foundry/deploy-foundry-agent.sh b/es/labs/foundry/code/agents/AndersAgent/ms-foundry/deploy-foundry-agent.sh new file mode 100644 index 0000000..1735283 --- /dev/null +++ b/es/labs/foundry/code/agents/AndersAgent/ms-foundry/deploy-foundry-agent.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_FILE="$SCRIPT_DIR/ms_foundry_agent.csproj" + +require_command() { + if ! command -v "$1" >/dev/null 2>&1; then + echo "Missing required command: $1" >&2 + exit 1 + fi +} + +require_value() { + local name="$1" + if [[ -z "${!name:-}" ]]; then + echo "Missing required environment variable: $name" >&2 + exit 1 + fi +} + +require_command az +require_command dotnet + +require_value FOUNDRY_PROJECT_ENDPOINT +require_value FOUNDRY_MODEL_DEPLOYMENT_NAME + +if ! az account show >/dev/null 2>&1; then + echo "Azure CLI is not logged in. Running az login..." + az login >/dev/null +fi + +export Foundry__ProjectEndpoint="$FOUNDRY_PROJECT_ENDPOINT" +export Foundry__ModelDeployment="$FOUNDRY_MODEL_DEPLOYMENT_NAME" +export Foundry__AgentName="${FOUNDRY_AGENT_NAME:-AndersAgent}" +export Foundry__AgentInstructions="${FOUNDRY_AGENT_INSTRUCTIONS:-You are an analytical AI agent specialized in reading, understanding, and extracting insights from provided information.}" + +echo "Deploying agent '$Foundry__AgentName' to Azure AI Foundry..." +dotnet run --project "$PROJECT_FILE" -- deploy diff --git a/es/labs/foundry/code/agents/AndersAgent/ms-foundry/ms_foundry_agent.csproj b/es/labs/foundry/code/agents/AndersAgent/ms-foundry/ms_foundry_agent.csproj new file mode 100644 index 0000000..822ba5d --- /dev/null +++ b/es/labs/foundry/code/agents/AndersAgent/ms-foundry/ms_foundry_agent.csproj @@ -0,0 +1,35 @@ + + + + Exe + net8.0 + enable + enable + MsFoundryAgent + + + + + + + + + + + + + + + + + + + + + PreserveNewest + + + + diff --git a/es/labs/foundry/code/agents/AndersAgent/ms-foundry/ms_foundry_agent_v2.sln b/es/labs/foundry/code/agents/AndersAgent/ms-foundry/ms_foundry_agent_v2.sln new file mode 100644 index 0000000..659b903 --- /dev/null +++ b/es/labs/foundry/code/agents/AndersAgent/ms-foundry/ms_foundry_agent_v2.sln @@ -0,0 +1,24 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.2.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ms_foundry_agent", "ms_foundry_agent.csproj", "{AB0D0B82-6185-D0D9-6EBF-DBBC85DEBF29}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {AB0D0B82-6185-D0D9-6EBF-DBBC85DEBF29}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AB0D0B82-6185-D0D9-6EBF-DBBC85DEBF29}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AB0D0B82-6185-D0D9-6EBF-DBBC85DEBF29}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AB0D0B82-6185-D0D9-6EBF-DBBC85DEBF29}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {07312557-C0C5-4DBD-8CE0-783FDC2D9B9B} + EndGlobalSection +EndGlobal diff --git a/es/labs/foundry/code/agents/AndersAgent/ms-foundry/nuget.config b/es/labs/foundry/code/agents/AndersAgent/ms-foundry/nuget.config deleted file mode 100644 index 2735cce..0000000 --- a/es/labs/foundry/code/agents/AndersAgent/ms-foundry/nuget.config +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/es/labs/foundry/lab03-anders-executor-agent.md b/es/labs/foundry/lab03-anders-executor-agent.md index 04236f4..329106a 100644 --- a/es/labs/foundry/lab03-anders-executor-agent.md +++ b/es/labs/foundry/lab03-anders-executor-agent.md @@ -6,31 +6,16 @@ - [Tabla de contenido](#tabla-de-contenido) - [Introducción](#introducción) - [¿Qué vamos a hacer en este lab?](#qué-vamos-a-hacer-en-este-lab) - - [3.1 — Verificar soporte OpenAPI (ya viene preconfigurado)](#31--verificar-soporte-openapi-ya-viene-preconfigurado) - - [Checklist rápido de validación](#checklist-rápido-de-validación) - - [Paso 1: Verificar paquetes NuGet](#paso-1-verificar-paquetes-nuget) - - [Paso 2: Verificar endpoints expuestos](#paso-2-verificar-endpoints-expuestos) + - [Verificar endpoints expuestos](#paso-2-verificar-endpoints-expuestos) - [Endpoints OpenAPI generados](#endpoints-openapi-generados) - [3.2 — Verificar la especificación OpenAPI](#32--verificar-la-especificación-openapi) - [Obtener la especificación JSON](#obtener-la-especificación-json) - [Explorar el Swagger UI](#explorar-el-swagger-ui) - - [3.3 — El agente Anders: Dos versiones de SDK](#33--el-agente-anders-dos-versiones-de-sdk) - - [¿Por qué dos versiones?](#por-qué-dos-versiones) - - [¿Cuál versión debo usar?](#cuál-versión-debo-usar) - - [Entendiendo el código (versión `ms-foundry/` — recomendada)](#entendiendo-el-código-versión-ms-foundry--recomendada) - - [Fase 1 — Descargar la especificación OpenAPI](#fase-1--descargar-la-especificación-openapi) - - [Fase 2 — Verificar agente existente o crear uno nuevo](#fase-2--verificar-agente-existente-o-crear-uno-nuevo) - - [Fase 3 — Chat interactivo con Responses API](#fase-3--chat-interactivo-con-responses-api) - - [Paso 1: Configurar `appsettings.json`](#paso-1-configurar-appsettingsjson) - - [Paso 2: Compilar y ejecutar](#paso-2-compilar-y-ejecutar) - - [Paso 3: Inspeccionar el agente en Azure AI Foundry](#paso-3-inspeccionar-el-agente-en-azure-ai-foundry) - - [Paso 4: Probar el agente](#paso-4-probar-el-agente) - - [Solución de problemas](#solución-de-problemas) - - [Storage Account bloqueado por política (error 503)](#storage-account-bloqueado-por-política-error-503) - - [Challenge: Respuestas en streaming](#challenge-respuestas-en-streaming) - - [Pista](#pista) - - [Criterio de éxito](#criterio-de-éxito) - - [Siguiente paso](#siguiente-paso) + - [3.3 — El agente Anders](#33--el-agente-anders-dos-versiones-de-sdk) + - [Paso 1: Entendiendo el código (`ms-foundry/`)](#entendiendo-el-código-versión-ms-foundry--recomendada) + - [Paso 2: Inspeccionar el agente en Azure AI Foundry](#paso-3-inspeccionar-el-agente-en-azure-ai-foundry) + - [Paso 3: Probar el agente](#paso-4-probar-el-agente) + - [Notas Importantes](#notas-impotantes) --- @@ -38,30 +23,11 @@ Anders es el **agente ejecutor** de la arquitectura multi-agéntica de Contoso Retail. Su rol es recibir solicitudes de acciones operativas — como la generación y publicación de reportes de órdenes — y ejecutarlas interactuando con servicios externos como la Azure Function `FxContosoRetail`. -Para que Anders pueda interactuar con la API de Contoso Retail, definiremos una **OpenAPI Tool** que permite al agente descubrir e invocar automáticamente los endpoints de la Function App a partir de su especificación OpenAPI. Adicionalmente, agregaremos soporte **OpenAPI** a la Function App para documentar la API y facilitar la exploración de sus endpoints. +Para que Anders pueda interactuar con la API de Contoso Retail, usaremos una **Microsoft Foundry Tool** que permite al agente descubrir e invocar automáticamente los endpoints de la Function App a partir de su especificación OpenAPI. Adicionalmente, agregaremos soporte **OpenAPI** a la Function App para documentar la API y facilitar la exploración de sus endpoints. ### ¿Qué vamos a hacer en este lab? -| Paso | Descripción | -|------|-------------| -| **3.1** | Verificar el soporte OpenAPI de la Azure Function `FxContosoRetail` | -| **3.2** | Verificar la especificación OpenAPI | -| **3.3** | Entender, configurar, ejecutar y probar el agente Anders | - -## 3.1 — Verificar soporte OpenAPI (ya viene preconfigurado) - -En la versión actual del taller, la Function App `FxContosoRetail` **ya incluye** OpenAPI y endpoints decorados en el código base. En este paso no vas a implementar OpenAPI desde cero: solo validar que todo está correcto antes del despliegue. - -### Checklist rápido de validación - -### Paso 1: Verificar paquetes NuGet - -Abre [`es/labs/foundry/code/api/FxContosoRetail/FxContosoRetail.csproj`](../code/api/FxContosoRetail/FxContosoRetail.csproj) y confirma que existen estas referencias: - -- `Microsoft.Azure.Functions.Worker.Extensions.OpenApi` -- `Microsoft.Data.SqlClient` - -### Paso 2: Verificar endpoints expuestos +### Paso 1: Verificar endpoints expuestos Abre [`es/labs/foundry/code/api/FxContosoRetail/FxContosoRetail.cs`](../code/api/FxContosoRetail/FxContosoRetail.cs) y confirma que existen estos endpoints: @@ -128,191 +94,70 @@ Desde la interfaz de Swagger UI puedes explorar los endpoints y probarlos intera --- -## 3.3 — El agente Anders: Dos versiones de SDK - -La implementación del agente Anders se proporciona en **dos versiones separadas**, cada una ubicada bajo `es/labs/foundry/code/agents/AndersAgent/`: +## 3.3 — El agente Anders -| Carpeta | SDK | Paradigma de API | Estado | -|---------|-----|------------------|--------| -| `ai-foundry/` | `Azure.AI.Projects` + `Azure.AI.Agents.Persistent` | Persistent Agents (threads, runs, polling) | GA — se conserva por retrocompatibilidad | -| `ms-foundry/` | `Azure.AI.Projects` + `Azure.AI.Projects.OpenAI` | Responses API (conversaciones, respuestas de proyecto) | **Preview** (a febrero 2026) — **recomendada** | +La implementación del agente Anders se proporciona bajo `es/labs/foundry/code/agents/AndersAgent/ms-foundry`: -### ¿Por qué dos versiones? - -A finales de 2025, Microsoft introdujo una **nueva experiencia para Microsoft Foundry** basada en la **Responses API** y una superficie de gestión de agentes rediseñada. Esta nueva experiencia — expuesta a través del paquete `Azure.AI.Projects.OpenAI` — reemplaza el modelo anterior de Persistent Agents (`Azure.AI.Agents.Persistent`) con un enfoque más ágil que utiliza **agentes con nombre y versionado**, **conversaciones** y la **Responses API** en lugar de threads y runs con polling. - -Las diferencias clave entre ambos enfoques son: +### Entendiendo el código (versión `ms-foundry/` — recomendada) -| Aspecto | `ai-foundry/` (Persistent Agents) | `ms-foundry/` (Responses API) | -|---------|-----------------------------------|-------------------------------| -| **Ciclo de vida del agente** | Se crea con un ID generado; se busca por nombre iterando la lista | Se crea/actualiza por nombre con versionado explícito (`CreateAgentVersionAsync`) | -| **Modelo de conversación** | `PersistentAgentThread` + `ThreadRun` con polling | `ProjectConversation` + `ProjectResponsesClient` — respuesta síncrona | -| **Definición de herramientas** | `OpenApiToolDefinition` con clases tipadas | Protocol method vía `BinaryContent` (los tipos son internos en SDK 1.2.x) | -| **Patrón de chat** | Crear run → hacer polling hasta completar → leer mensajes | Una sola llamada a `CreateResponse()` retorna la salida directamente | +Abre el archivo `es/labs/foundry/code/agents/AndersAgent/ms-foundry/Program.cs` y observa como está organizado -### ¿Cuál versión debo usar? +### Paso 1: Configuración del Entorno en GitHub Codespaces -**Se recomienda la versión `ms-foundry/`** para desarrollo nuevo. Está alineada con la dirección de la plataforma Microsoft Foundry y ofrece un modelo de programación más simple — particularmente la eliminación del loop de polling en favor de una sola llamada síncrona de respuesta. +1. Abre el repositorio en **GitHub Codespaces**. +2. Autentícate en tu cuenta de Azure desde la terminal: + ```bash + az login --use-device-code + ``` -La versión `ai-foundry/` se conserva en este taller por **retrocompatibilidad**. +### Paso 2: Variables de Entorno de Azure AI Foundry -> [!IMPORTANT] -> A febrero de 2026, el paquete `Azure.AI.Projects.OpenAI` y la Responses API están en **preview pública**. Las formas de la API, schemas de payload y tipos del SDK pueden cambiar antes de alcanzar disponibilidad general (GA). Si encuentras problemas como propiedades faltantes o renombradas (por ejemplo, el campo `kind` requerido en el payload de definición del agente), consulta las últimas [notas de versión de Azure.AI.Projects.OpenAI](https://www.nuget.org/packages/Azure.AI.Projects.OpenAI) para conocer los cambios que rompen compatibilidad. +Desde el portal de Azure AI Foundry, navega a la sección Overview de tu proyecto y obtén los siguientes valores para configurarlos en tu terminal de Codespaces: ---- - -### Entendiendo el código (versión `ms-foundry/` — recomendada) +# Reemplaza los valores entre comillas con tu información real -Abre el archivo `es/labs/foundry/code/agents/AndersAgent/ms-foundry/Program.cs` y observa que está organizado en **3 fases**: - -#### Fase 1 — Descargar la especificación OpenAPI - -```csharp -var openApiSpecUrl = $"{functionAppBaseUrl}/openapi/v3.json"; -var openApiSpec = await httpClient.GetStringAsync(openApiSpecUrl); ``` - -El programa descarga la especificación OpenAPI de la Function App **en tiempo de ejecución**. Esto significa que si la API cambia (nuevos endpoints, nuevos parámetros), el agente lo detecta automáticamente al reiniciarse. - -#### Fase 2 — Verificar agente existente o crear uno nuevo - -Esta fase tiene dos partes clave: - -**Detección de agente existente:** - -Antes de crear una nueva versión, el programa verifica si el agente ya existe llamando a `GetAgent`. Si lo encuentra, le pregunta al usuario si desea conservar el agente existente o sobreescribirlo con una nueva versión. Esto evita la proliferación innecesaria de versiones del agente durante el desarrollo iterativo. - -**Definición del agente con herramienta OpenAPI (protocol method):** - -```csharp -var agentDefinitionJson = new -{ - definition = new - { - kind = "prompt", - model = modelDeployment, - instructions = andersInstructions, - tools = new object[] - { - new - { - type = "openapi", - openapi = new - { - name = "ContosoRetailAPI", - description = "API de Contoso Retail...", - spec = openApiSpecJson, - auth = new { type = "anonymous" } - } - } - } - } -}; +export FOUNDRY_PROJECT_ENDPOINT="https://ais-contosoretail-XXXX.services.ai.azure.com/..." +export FOUNDRY_MODEL_DEPLOYMENT_NAME="gpt-4.1" ``` -Dado que los tipos `OpenApiAgentTool` son internos en el SDK 1.2.x, la definición de la herramienta se construye como un objeto anónimo y se serializa vía `BinaryContent`. El campo `kind = "prompt"` es requerido por la API para indicar un agente basado en prompt. - -**System prompt (instrucciones):** - -El system prompt incluye el schema JSON exacto que Anders debe construir al invocar la API: - -```json -{ - "customerName": "Nombre del Cliente", - "startDate": "YYYY-MM-DD", - "endDate": "YYYY-MM-DD", - "orders": [ - { - "orderNumber": "código de la orden", - "orderDate": "YYYY-MM-DD", - "orderLineNumber": 1, - "productName": "nombre del producto", - "brandName": "nombre de la marca", - "categoryName": "nombre de la categoría", - "quantity": 1.0, - "unitPrice": 0.00, - "lineTotal": 0.00 - } - ] -} -``` +Las siguientres variables de entorno son opcionales, no es necesario crearlas. Pero si lo consideras necesario lo puede hacer para cambiar el nombre del agente y su compotanmiento. -> [!TIP] -> Incluir el schema en las instrucciones es una buena práctica cuando el agente debe construir payloads complejos. Aunque la especificación OpenAPI ya describe el schema, reforzarlo en el system prompt reduce significativamente los errores de formato. - -**Reutilización del agente:** - -```csharp -try -{ - existingAgent = projectClient.Agents.GetAgent(agentName); - // Pregunta al usuario si desea sobreescribir o conservar -} -catch (ClientResultException ex) when (ex.Status == 404) -{ - // Agente no encontrado — crear uno nuevo -} +``` +export FOUNDRY_AGENT_NAME="AndersAgent" +export FOUNDRY_AGENT_INSTRUCTIONS="Eres un agente analítico especializado en lectura, comprensión y extracción de insights a partir de información proporcionada." ``` -Antes de crear una nueva versión del agente, el programa intenta recuperar el agente existente por nombre. Si lo encuentra, le pide al usuario que confirme si desea sobreescribirlo. Esto evita crear versiones innecesarias en Foundry al reiniciar la aplicación. +### Paso 3: Despliegue del Agente -#### Fase 3 — Chat interactivo con Responses API +Ejecuta el script de despliegue para registrar el agente en el servicio: -```csharp -ProjectConversation conversation = projectClient.OpenAI.Conversations.CreateProjectConversation(); -ProjectResponsesClient responseClient = projectClient.OpenAI.GetProjectResponsesClientForAgent( - defaultAgent: agentName, - defaultConversationId: conversation.Id); +./deploy-foundry-agent.sh -ResponseResult response = responseClient.CreateResponse(input); -Console.WriteLine(response.GetOutputText()); -``` +### Paso 4: Configuración de Herramientas (Portal) -El patrón de interacción en la versión `ms-foundry/` es más simple que el enfoque de Persistent Agents: -1. Se crea una `ProjectConversation` (el contexto de conversación) -2. Se obtiene un `ProjectResponsesClient`, vinculado al agente y la conversación -3. Cada mensaje del usuario se envía vía `CreateResponse()` que retorna la salida **síncronamente** — sin necesidad de loop de polling -4. El texto de respuesta se extrae con `GetOutputText()` +Una vez desplegado, sigue estos pasos en el portal web: -> **¿Qué pasa durante una llamada de respuesta?** Cuando el modelo decide que necesita llamar a la API, Foundry ejecuta la llamada HTTP automáticamente usando la especificación OpenAPI. El resultado se envía de vuelta al modelo, que formula la respuesta final al usuario. Todo esto ocurre dentro de la única llamada a `CreateResponse()` — el código simplemente recibe la respuesta terminada. +Ve a Build > Agents y selecciona AndersAgent. -**Limpieza al salir:** +Añadir Herramienta OpenAPI: -Cuando el usuario escribe `salir`, el loop de chat termina. El agente **persiste** en Foundry y se reutiliza automáticamente en la siguiente ejecución. +Haz clic en Add Tool > OpenAPI tool. -### Paso 1: Configurar `appsettings.json` +Ingresa el nombre de la herramienta (ej. myapitool). -Abre el archivo `es/labs/foundry/code/agents/AndersAgent/ms-foundry/appsettings.json` y reemplaza los valores con los de tu entorno: +Pega la definición JSON de la API (Swagger/OpenAPI) de tu servicio (ej. una Azure Function que consulta pedidos). -```json -{ - "FoundryProjectEndpoint": "", - "ModelDeploymentName": "gpt-4.1", - "FunctionAppBaseUrl": "https://func-contosoretail-.azurewebsites.net/api" -} ``` - -> **¿Dónde encuentro estos valores?** -> - **FoundryProjectEndpoint**: El `AI Foundry Endpoint` de la salida del despliegue. -> - **ModelDeploymentName**: `gpt-4.1` (nombre del deployment creado por el Bicep). -> - **FunctionAppBaseUrl**: La URL de tu Function App + `/api`. - -### Paso 2: Compilar y ejecutar - -```bash -cd es/labs/foundry/code/agents/AndersAgent/ms-foundry -dotnet build +https://func-contosoretail-.azurewebsites.net/api/openapi/v3.json ``` -Asegúrate de que no haya errores de compilación. Luego ejecuta: +Haz clic en Create tool. -```bash -dotnet run -``` +Activar Code Interpreter: Asegúrate de que el interruptor de "Code Interpreter" esté encendido para permitir cálculos matemáticos complejos sobre los datos. -Verás en consola que el agente verifica si ya existe una versión en Foundry. Si la encuentra, te preguntará si deseas conservarla o sobreescribirla. Si no existe, se crea un agente nuevo automáticamente. -### Paso 3: Inspeccionar el agente en Azure AI Foundry +### Paso 5: Inspeccionar el agente en Azure AI Foundry **Antes de interactuar con Anders**, ve al portal para inspeccionar lo que se creó: @@ -328,9 +173,20 @@ Observa dos cosas clave: > [!TIP] > El system prompt y las tools son los dos pilares que determinan qué puede hacer un agente y cómo lo hace. Entender esta relación es clave para diseñar agentes efectivos. -### Paso 4: Probar el agente +### Paso 6: Validación y Pruebas + +Inicia una sesión de chat en el Playground del agente y prueba con prompts de negocio como: + +``` +"Genera un reporte para el cliente Marco Rivera del periodo de enero a febrero de 2026. Muestra los pedidos en una tabla y calcula el total gastado." +``` + +Resultados Esperados: +El agente debe identificar qué herramienta llamar, procesar los datos y gGenerar una tabla de salida y un enlace de descarga para el reporte. + -De vuelta en la consola, pruébalo primero con un saludo: +### Paso 7: Validación y Pruebas +Inicia una sesión de chat en el Playground del agente y prueba con prompts de negocio como: ``` Tú: Hola Anders, ¿qué puedes hacer? @@ -343,8 +199,8 @@ Genera un reporte para Izabella Celma (periodo: 1-31 enero 2026). Orden ORD-CID- ``` Lo que ocurre internamente: -1. Anders analiza el mensaje y decide que necesita llamar al endpoint `ordersReporter` -2. **Foundry ejecuta la llamada HTTP** automáticamente a la Function App con los datos estructurados según el schema +1. Anders analiza el mensaje y decide que necesita llamar al endpoint `ordersReporter` por medio del tool configurado. +2. **Foundry Tool ejecuta la llamada HTTP** automáticamente a la Function App con los datos estructurados según el schema 3. La Function App genera el reporte HTML, lo sube a Blob Storage y retorna la URL 4. Foundry envía el resultado de vuelta al modelo 5. Anders formula su respuesta y presenta la URL al usuario @@ -357,70 +213,14 @@ Ahora prueba con un caso más sencillo — un solo pedido con dos productos: Tú: Genera un reporte para Marco Rivera (periodo: 5-10 febrero 2026). Orden ORD-CID-112-001 (2026-02-07): Mountain Bike Socks M, Contoso Outdoor, Socks, 3x$9.50=$28.50 | Water Bottle 30oz, Contoso Outdoor, Bottles and Cages, 1x$6.99=$6.99. ``` -> **Nota:** Al escribir `salir`, solo se termina la conversación. El agente **persiste** en Foundry y se reutiliza automáticamente en la siguiente ejecución. - --- -## Solución de problemas - -### Storage Account bloqueado por política (error 503) - -En suscripciones con políticas estrictas de Azure, el Storage Account que respalda la Function App puede tener su **acceso público de red deshabilitado** automáticamente después del aprovisionamiento. Esto impide que el host de Functions alcance su propio almacenamiento, causando un error persistente **503 (Site Unavailable)** — aunque la app reporte como `Running` y `Enabled`. - -**Síntomas:** -- La Function App aparece como `Running` en el Portal de Azure y CLI -- Todas las restricciones de acceso de red muestran "Allow all" -- Cada solicitud HTTP a cualquier endpoint retorna 503 después de un timeout de ~60 segundos - -**Diagnóstico:** -```bash -az storage account show --name stcontosoretail --resource-group rg-contoso-retail --query "publicNetworkAccess" -o tsv -``` - -Si retorna `Disabled`, esa es la causa raíz. - -**Solución:** - -Se incluye un script de conveniencia en el repositorio: - -```bash -cd es/labs/foundry/setup -pwsh ./unlock-storage.ps1 -``` +## Notas Importantes -El script detecta automáticamente el sufijo desde la Function App. Si necesitas forzarlo, también acepta `-Suffix` o `-FunctionAppName`. +Cierre de sesión: Al finalizar, recuerda detener tu Codespace para evitar consumos innecesarios. -Este script habilita el acceso público de red en el Storage Account y reinicia la Function App. Ver [unlock-storage.ps1](setup/unlock-storage.ps1) para detalles. - ---- - -## Challenge: Respuestas en streaming - -Actualmente el chat de Anders espera a que el agente complete toda su respuesta antes de mostrarla. Esto puede generar una pausa perceptible cuando el modelo razona y ejecuta la herramienta OpenAPI. - -**Tu reto:** modifica el loop de chat en `ms-foundry/Program.cs` para que la respuesta de Anders se imprima token a token a medida que llega, usando la API de streaming. - -### Pista - -El SDK expone `CreateResponseStreamingAsync()` como alternativa a `CreateResponse()`. Devuelve un `IAsyncEnumerable` de eventos que puedes iterar para imprimir cada fragmento de texto conforme llega: - -```csharp -await foreach (var update in responseClient.CreateResponseStreamingAsync(input)) -{ - // Filtra los eventos de tipo delta de texto e imprime el fragmento -} -``` - -Los eventos que contienen texto son de tipo `StreamingResponseOutputTextDeltaUpdate` (namespace `OpenAI.Responses`, ya importado), y su propiedad con el fragmento se llama `Delta`. - -### Criterio de éxito - -- La respuesta de Anders aparece progresivamente en la consola, letra a letra (o fragmento a fragmento), sin esperar a que complete toda la respuesta. -- El prompt `Tú:` solo aparece una vez que Anders termina de responder. -- El comportamiento de `salir` y el manejo de errores se mantienen igual que antes. - ---- +Regiones: Verifica que tu proyecto de Foundry esté en una región que soporte agentes (ej. East US, East US 2). ## Siguiente paso -Continúa con el [Lab 4 — Julie (Planner Agent)](lab04-julie-planner-agent.md). +Continúa con el [Lab 5 — Laboratrios de Copilot Studio](lab05-mcs-setup.md). From 7242e22074ee67ae79c7791e89b10a477a450bc1 Mon Sep 17 00:00:00 2001 From: Omar Aviles Date: Tue, 7 Apr 2026 19:02:22 +0000 Subject: [PATCH 02/13] =?UTF-8?q?fix:=20actualizar=20t=C3=ADtulos=20de=20l?= =?UTF-8?q?aboratorios=20en=20la=20documentaci=C3=B3n=20de=20Azure=20AI=20?= =?UTF-8?q?Foundry=20y=20Copilot=20Studio?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- es/readme.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/es/readme.md b/es/readme.md index 4e4dd43..d494258 100644 --- a/es/readme.md +++ b/es/readme.md @@ -115,17 +115,17 @@ El workshop está dividido en laboratorios independientes pero conectados, organ ### 2. Laboratorios de Azure AI Foundry -- [Setup de infraestructura de Foundry](./labs/foundry/codespaces-setup.md) -- [Lab 3 – Agente Anders: soporte OpenAPI, despliegue de Function App y ejecución del agente executor](./labs/foundry/lab03-anders-executor-agent.md) -- [Lab 4 – Agente Julie: workflow agent con sub-agentes SqlAgent y MarketingAgent](./labs/foundry/lab04-julie-planner-agent.md) +- [Lab 3 – Setup de infraestructura de Foundry](./labs/foundry/codespaces-setup.md) +- [Lab 4 – Agente Anders: soporte OpenAPI, despliegue de Function App y ejecución del agente executor](./labs/foundry/lab03-anders-executor-agent.md) +- [Lab 5 – Agente Julie: workflow agent con sub-agentes SqlAgent y MarketingAgent (Opcional)](./labs/foundry/lab04-julie-planner-agent.md) ### 3. Laboratorios de Copilot Studio -- [Lab 5 – Setup de Copilot Studio: entorno, solución y publisher](./labs/copilot/lab05-mcs-setup.md) -- [Lab 6 – Agente Charles: Q&A de producto con SharePoint y análisis de mercado](./labs/copilot/lab06-charles-copilot-agent.md) -- [Lab 7 – Agente Ric: agente hijo para envío de correos + configuración inicial de Bill](./labs/copilot/lab07-ric-child-agent.md) -- [Lab 8 – Orquestador Bill: conexión de agentes externos (Mark, Anders) e internos (Charles) y reglas de orquestación](./labs/copilot/lab08-bill-orchestrator.md) -- [Lab 9 – Publicación de Bill en Microsoft 365 / Teams y pruebas end-to-end](./labs/copilot/lab09-bill-publishing.md) +- [Lab 6 – Setup de Copilot Studio: entorno, solución y publisher](./labs/copilot/lab05-mcs-setup.md) +- [Lab 7 – Agente Charles: Q&A de producto con SharePoint y análisis de mercado](./labs/copilot/lab06-charles-copilot-agent.md) +- [Lab 8 – Agente Ric: agente hijo para envío de correos + configuración inicial de Bill](./labs/copilot/lab07-ric-child-agent.md) +- [Lab 9 – Orquestador Bill: conexión de agentes externos (Mark, Anders) e internos (Charles) y reglas de orquestación](./labs/copilot/lab08-bill-orchestrator.md) +- [Lab 10 – Publicación de Bill en Microsoft 365 / Teams y pruebas end-to-end](./labs/copilot/lab09-bill-publishing.md) --- From 129ebfdcfb0a6ea468f15c2ef875afff87267401 Mon Sep 17 00:00:00 2001 From: Omar Aviles Date: Tue, 7 Apr 2026 19:13:17 +0000 Subject: [PATCH 03/13] =?UTF-8?q?fix:=20corregir=20el=20orden=20de=20los?= =?UTF-8?q?=20laboratorios=20en=20la=20documentaci=C3=B3n=20de=20configura?= =?UTF-8?q?ci=C3=B3n=20de=20Codespaces?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- es/labs/foundry/codespaces-setup.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/es/labs/foundry/codespaces-setup.md b/es/labs/foundry/codespaces-setup.md index 42fbda5..9cb1c8c 100644 --- a/es/labs/foundry/codespaces-setup.md +++ b/es/labs/foundry/codespaces-setup.md @@ -397,8 +397,8 @@ labs/foundry/ | Lab | Archivo | Descripción | | ----- | --------------------------------------------------------- | ------------------------------------------------------------ | -| Lab 3 | [Anders — Executor Agent](lab03-anders-executor-agent.md) | Crear el agente ejecutor que genera reportes e interactúa con servicios de Contoso Retail. | -| Lab 4 | [Julie — Planner Agent](lab04-julie-planner-agent.md) | Crear el agente orquestador de campañas de marketing usando el patrón workflow con sub-agentes (SqlAgent, MarketingAgent) y herramienta OpenAPI. | +| Lab 4 | [Anders — Executor Agent](lab03-anders-executor-agent.md) | Crear el agente ejecutor que genera reportes e interactúa con servicios de Contoso Retail. | +| Lab 5 | [Julie — Planner Agent (Opcional)](lab04-julie-planner-agent.md) | Crear el agente orquestador de campañas de marketing usando el patrón workflow con sub-agentes (SqlAgent, MarketingAgent) y herramienta OpenAPI. | --- From 1a42239ab2d62d1919ed94d5324ed0d70f65b4b2 Mon Sep 17 00:00:00 2001 From: Omar Aviles Date: Tue, 7 Apr 2026 19:14:21 +0000 Subject: [PATCH 04/13] =?UTF-8?q?fix:=20corregir=20el=20orden=20de=20los?= =?UTF-8?q?=20laboratorios=20en=20la=20documentaci=C3=B3n=20de=20configura?= =?UTF-8?q?ci=C3=B3n=20de=20Foundry?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- es/labs/foundry/setup.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/es/labs/foundry/setup.md b/es/labs/foundry/setup.md index 269a964..0e058dc 100644 --- a/es/labs/foundry/setup.md +++ b/es/labs/foundry/setup.md @@ -237,8 +237,8 @@ labs/foundry/ | Lab | Archivo | Descripción | | ----- | --------------------------------------------------------- | ------------------------------------------------------------ | -| Lab 3 | [Anders — Executor Agent](lab03-anders-executor-agent.md) | Crear el agente ejecutor que genera reportes e interactúa con servicios de Contoso Retail. | -| Lab 4 | [Julie — Planner Agent](lab04-julie-planner-agent.md) | Crear el agente orquestador de campañas de marketing usando el patrón workflow con sub-agentes (SqlAgent, MarketingAgent) y herramienta OpenAPI. | +| Lab 4 | [Anders — Executor Agent](lab03-anders-executor-agent.md) | Crear el agente ejecutor que genera reportes e interactúa con servicios de Contoso Retail. | +| Lab 5 | [Julie — Planner Agent](lab04-julie-planner-agent.md) | Crear el agente orquestador de campañas de marketing usando el patrón workflow con sub-agentes (SqlAgent, MarketingAgent) y herramienta OpenAPI. | --- From 05246a456abbb8527f540a6a85efae498235031f Mon Sep 17 00:00:00 2001 From: Omar Aviles Date: Tue, 7 Apr 2026 19:15:40 +0000 Subject: [PATCH 05/13] =?UTF-8?q?fix:=20actualizar=20n=C3=BAmeros=20de=20l?= =?UTF-8?q?aboratorio=20en=20la=20documentaci=C3=B3n=20de=20Foundry?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- es/labs/foundry/lab03-anders-executor-agent.md | 2 +- es/labs/foundry/lab04-julie-planner-agent.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/es/labs/foundry/lab03-anders-executor-agent.md b/es/labs/foundry/lab03-anders-executor-agent.md index 329106a..cfece18 100644 --- a/es/labs/foundry/lab03-anders-executor-agent.md +++ b/es/labs/foundry/lab03-anders-executor-agent.md @@ -1,4 +1,4 @@ -# Lab 3: Anders — Executor Agent +# Lab 4: Anders — Executor Agent ## Tabla de contenido diff --git a/es/labs/foundry/lab04-julie-planner-agent.md b/es/labs/foundry/lab04-julie-planner-agent.md index 9619e51..73e693a 100644 --- a/es/labs/foundry/lab04-julie-planner-agent.md +++ b/es/labs/foundry/lab04-julie-planner-agent.md @@ -1,4 +1,4 @@ -# Lab 4: Julie Planner Agent +# Lab 5: Julie Planner Agent ## Tabla de contenido From 32d8432b723c65ab77c1a0b5c3142ed07433691a Mon Sep 17 00:00:00 2001 From: Omar Aviles Date: Tue, 7 Apr 2026 19:20:25 +0000 Subject: [PATCH 06/13] Add Lab 4: Anders Executor Agent and Lab 5: Julie Planner Agent documentation - Introduced Lab 4 documentation detailing the setup and functionality of the Anders Executor Agent, including OpenAPI integration and agent deployment steps. - Added Lab 5 documentation for the Julie Planner Agent, outlining its architecture, workflow orchestration, and interaction with SQL and marketing agents. - Included detailed instructions for configuring environment variables, validating permissions, and executing end-to-end workflows for both agents. --- .../{lab05-mcs-setup.md => lab07-mcs-setup.md} | 0 ...ric-child-agent.md => lab10-ric-child-agent.md} | 0 ...tor-agent.md => lab04-anders-executor-agent.md} | 0 ...anner-agent.md => lab05-julie-planner-agent.md} | 0 es/readme.md | 14 +++++++------- 5 files changed, 7 insertions(+), 7 deletions(-) rename es/labs/copilot/{lab05-mcs-setup.md => lab07-mcs-setup.md} (100%) rename es/labs/copilot/{lab07-ric-child-agent.md => lab10-ric-child-agent.md} (100%) rename es/labs/foundry/{lab03-anders-executor-agent.md => lab04-anders-executor-agent.md} (100%) rename es/labs/foundry/{lab04-julie-planner-agent.md => lab05-julie-planner-agent.md} (100%) diff --git a/es/labs/copilot/lab05-mcs-setup.md b/es/labs/copilot/lab07-mcs-setup.md similarity index 100% rename from es/labs/copilot/lab05-mcs-setup.md rename to es/labs/copilot/lab07-mcs-setup.md diff --git a/es/labs/copilot/lab07-ric-child-agent.md b/es/labs/copilot/lab10-ric-child-agent.md similarity index 100% rename from es/labs/copilot/lab07-ric-child-agent.md rename to es/labs/copilot/lab10-ric-child-agent.md diff --git a/es/labs/foundry/lab03-anders-executor-agent.md b/es/labs/foundry/lab04-anders-executor-agent.md similarity index 100% rename from es/labs/foundry/lab03-anders-executor-agent.md rename to es/labs/foundry/lab04-anders-executor-agent.md diff --git a/es/labs/foundry/lab04-julie-planner-agent.md b/es/labs/foundry/lab05-julie-planner-agent.md similarity index 100% rename from es/labs/foundry/lab04-julie-planner-agent.md rename to es/labs/foundry/lab05-julie-planner-agent.md diff --git a/es/readme.md b/es/readme.md index d494258..64b644e 100644 --- a/es/readme.md +++ b/es/readme.md @@ -116,16 +116,16 @@ El workshop está dividido en laboratorios independientes pero conectados, organ ### 2. Laboratorios de Azure AI Foundry - [Lab 3 – Setup de infraestructura de Foundry](./labs/foundry/codespaces-setup.md) -- [Lab 4 – Agente Anders: soporte OpenAPI, despliegue de Function App y ejecución del agente executor](./labs/foundry/lab03-anders-executor-agent.md) -- [Lab 5 – Agente Julie: workflow agent con sub-agentes SqlAgent y MarketingAgent (Opcional)](./labs/foundry/lab04-julie-planner-agent.md) +- [Lab 4 – Agente Anders: soporte OpenAPI, despliegue de Function App y ejecución del agente executor](./labs/foundry/lab04-anders-executor-agent.md) +- [Lab 5 – Agente Julie: workflow agent con sub-agentes SqlAgent y MarketingAgent (Opcional)](./labs/foundry/lab05-julie-planner-agent.md) ### 3. Laboratorios de Copilot Studio -- [Lab 6 – Setup de Copilot Studio: entorno, solución y publisher](./labs/copilot/lab05-mcs-setup.md) -- [Lab 7 – Agente Charles: Q&A de producto con SharePoint y análisis de mercado](./labs/copilot/lab06-charles-copilot-agent.md) -- [Lab 8 – Agente Ric: agente hijo para envío de correos + configuración inicial de Bill](./labs/copilot/lab07-ric-child-agent.md) -- [Lab 9 – Orquestador Bill: conexión de agentes externos (Mark, Anders) e internos (Charles) y reglas de orquestación](./labs/copilot/lab08-bill-orchestrator.md) -- [Lab 10 – Publicación de Bill en Microsoft 365 / Teams y pruebas end-to-end](./labs/copilot/lab09-bill-publishing.md) +- [Lab 6 – Setup de Copilot Studio: entorno, solución y publisher](./labs/copilot/lab06-mcs-setup.md) +- [Lab 7 – Agente Charles: Q&A de producto con SharePoint y análisis de mercado](./labs/copilot/lab07-charles-copilot-agent.md) +- [Lab 8 – Agente Ric: agente hijo para envío de correos + configuración inicial de Bill](./labs/copilot/lab08-ric-child-agent.md) +- [Lab 9 – Orquestador Bill: conexión de agentes externos (Mark, Anders) e internos (Charles) y reglas de orquestación](./labs/copilot/lab09-bill-orchestrator.md) +- [Lab 10 – Publicación de Bill en Microsoft 365 / Teams y pruebas end-to-end](./labs/copilot/lab10-bill-publishing.md) --- From 7e4ae5e460996b9e86a524d19f6c983c4647d8e9 Mon Sep 17 00:00:00 2001 From: Omar Aviles Date: Tue, 7 Apr 2026 19:26:14 +0000 Subject: [PATCH 07/13] feat: Add new labs for Microsoft Copilot Studio setup, agent creation, orchestration, and publishing - Created lab06 for setting up the Microsoft Copilot Studio environment. - Added lab07 for building the Charlie agent focused on product analysis. - Introduced lab08 for creating Ric as a child agent of Bill for email notifications. - Developed lab09 for orchestrating agents Mark, Anders, and Charlie under Bill. - Implemented lab10 for publishing the Bill agent and testing through Microsoft 365. - Removed outdated lab10-ric-child-agent.md file. --- es/labs/copilot/{lab07-mcs-setup.md => lab06-mcs-setup.md} | 0 ...06-charles-copilot-agent.md => lab07-charles-copilot-agent.md} | 0 .../{lab10-ric-child-agent.md => lab08-ric-child-agent.md} | 0 .../{lab08-bill-orchestrator.md => lab09-bill-orchestrator.md} | 0 .../{lab09-bill-publishing.md => lab10-bill-publishing.md} | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename es/labs/copilot/{lab07-mcs-setup.md => lab06-mcs-setup.md} (100%) rename es/labs/copilot/{lab06-charles-copilot-agent.md => lab07-charles-copilot-agent.md} (100%) rename es/labs/copilot/{lab10-ric-child-agent.md => lab08-ric-child-agent.md} (100%) rename es/labs/copilot/{lab08-bill-orchestrator.md => lab09-bill-orchestrator.md} (100%) rename es/labs/copilot/{lab09-bill-publishing.md => lab10-bill-publishing.md} (100%) diff --git a/es/labs/copilot/lab07-mcs-setup.md b/es/labs/copilot/lab06-mcs-setup.md similarity index 100% rename from es/labs/copilot/lab07-mcs-setup.md rename to es/labs/copilot/lab06-mcs-setup.md diff --git a/es/labs/copilot/lab06-charles-copilot-agent.md b/es/labs/copilot/lab07-charles-copilot-agent.md similarity index 100% rename from es/labs/copilot/lab06-charles-copilot-agent.md rename to es/labs/copilot/lab07-charles-copilot-agent.md diff --git a/es/labs/copilot/lab10-ric-child-agent.md b/es/labs/copilot/lab08-ric-child-agent.md similarity index 100% rename from es/labs/copilot/lab10-ric-child-agent.md rename to es/labs/copilot/lab08-ric-child-agent.md diff --git a/es/labs/copilot/lab08-bill-orchestrator.md b/es/labs/copilot/lab09-bill-orchestrator.md similarity index 100% rename from es/labs/copilot/lab08-bill-orchestrator.md rename to es/labs/copilot/lab09-bill-orchestrator.md diff --git a/es/labs/copilot/lab09-bill-publishing.md b/es/labs/copilot/lab10-bill-publishing.md similarity index 100% rename from es/labs/copilot/lab09-bill-publishing.md rename to es/labs/copilot/lab10-bill-publishing.md From 945ab1e2a09b4acf3dc6760ef2a19b7be2d9cf7c Mon Sep 17 00:00:00 2001 From: Omar Aviles Date: Tue, 7 Apr 2026 19:32:11 +0000 Subject: [PATCH 08/13] =?UTF-8?q?fix:=20corregir=20n=C3=BAmeros=20de=20lab?= =?UTF-8?q?oratorio=20en=20la=20tabla=20de=20contenido=20de=20los=20docume?= =?UTF-8?q?ntos=20de=20los=20laboratorios?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- es/labs/foundry/lab04-anders-executor-agent.md | 2 +- es/labs/foundry/lab05-julie-planner-agent.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/es/labs/foundry/lab04-anders-executor-agent.md b/es/labs/foundry/lab04-anders-executor-agent.md index cfece18..6f47177 100644 --- a/es/labs/foundry/lab04-anders-executor-agent.md +++ b/es/labs/foundry/lab04-anders-executor-agent.md @@ -2,7 +2,7 @@ ## Tabla de contenido -- [Lab 3: Anders — Executor Agent](#lab-3-anders--executor-agent) +- [Lab 4: Anders — Executor Agent](#lab-4-anders--executor-agent) - [Tabla de contenido](#tabla-de-contenido) - [Introducción](#introducción) - [¿Qué vamos a hacer en este lab?](#qué-vamos-a-hacer-en-este-lab) diff --git a/es/labs/foundry/lab05-julie-planner-agent.md b/es/labs/foundry/lab05-julie-planner-agent.md index 73e693a..2521c29 100644 --- a/es/labs/foundry/lab05-julie-planner-agent.md +++ b/es/labs/foundry/lab05-julie-planner-agent.md @@ -2,7 +2,7 @@ ## Tabla de contenido -- [Lab 4: Julie Planner Agent](#lab-4-julie-planner-agent) +- [Lab 5: Julie Planner Agent](#lab-5-julie-planner-agent) - [Tabla de contenido](#tabla-de-contenido) - [Introducción](#introducción) - [Continuidad del setup](#continuidad-del-setup) From b8d0496241b455f2566c7c6f0190d5c164551274 Mon Sep 17 00:00:00 2001 From: Omar Aviles Date: Tue, 7 Apr 2026 19:33:28 +0000 Subject: [PATCH 09/13] =?UTF-8?q?fix:=20actualizar=20enlaces=20de=20labora?= =?UTF-8?q?torio=20para=20el=20agente=20ejecutor=20y=20el=20agente=20plani?= =?UTF-8?q?ficador=20en=20la=20documentaci=C3=B3n=20de=20Foundry?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- es/labs/foundry/codespaces-setup.md | 2 +- es/labs/foundry/setup.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/es/labs/foundry/codespaces-setup.md b/es/labs/foundry/codespaces-setup.md index 9cb1c8c..b5f7e56 100644 --- a/es/labs/foundry/codespaces-setup.md +++ b/es/labs/foundry/codespaces-setup.md @@ -397,7 +397,7 @@ labs/foundry/ | Lab | Archivo | Descripción | | ----- | --------------------------------------------------------- | ------------------------------------------------------------ | -| Lab 4 | [Anders — Executor Agent](lab03-anders-executor-agent.md) | Crear el agente ejecutor que genera reportes e interactúa con servicios de Contoso Retail. | +| Lab 4 | [Anders — Executor Agent](lab04-anders-executor-agent.md) | Crear el agente ejecutor que genera reportes e interactúa con servicios de Contoso Retail. | | Lab 5 | [Julie — Planner Agent (Opcional)](lab04-julie-planner-agent.md) | Crear el agente orquestador de campañas de marketing usando el patrón workflow con sub-agentes (SqlAgent, MarketingAgent) y herramienta OpenAPI. | --- diff --git a/es/labs/foundry/setup.md b/es/labs/foundry/setup.md index 0e058dc..239595b 100644 --- a/es/labs/foundry/setup.md +++ b/es/labs/foundry/setup.md @@ -237,8 +237,8 @@ labs/foundry/ | Lab | Archivo | Descripción | | ----- | --------------------------------------------------------- | ------------------------------------------------------------ | -| Lab 4 | [Anders — Executor Agent](lab03-anders-executor-agent.md) | Crear el agente ejecutor que genera reportes e interactúa con servicios de Contoso Retail. | -| Lab 5 | [Julie — Planner Agent](lab04-julie-planner-agent.md) | Crear el agente orquestador de campañas de marketing usando el patrón workflow con sub-agentes (SqlAgent, MarketingAgent) y herramienta OpenAPI. | +| Lab 4 | [Anders — Executor Agent](lab04-anders-executor-agent.md) | Crear el agente ejecutor que genera reportes e interactúa con servicios de Contoso Retail. | +| Lab 5 | [Julie — Planner Agent](lab05-julie-planner-agent.md) | Crear el agente orquestador de campañas de marketing usando el patrón workflow con sub-agentes (SqlAgent, MarketingAgent) y herramienta OpenAPI. | --- From eec9176b365f11d44cd82dbd0d7d9d521dfa5865 Mon Sep 17 00:00:00 2001 From: Omar Aviles Date: Tue, 7 Apr 2026 19:34:20 +0000 Subject: [PATCH 10/13] =?UTF-8?q?fix:=20corregir=20enlace=20del=20Lab=205?= =?UTF-8?q?=20en=20la=20documentaci=C3=B3n=20de=20configuraci=C3=B3n=20de?= =?UTF-8?q?=20Codespaces?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- es/labs/foundry/codespaces-setup.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/es/labs/foundry/codespaces-setup.md b/es/labs/foundry/codespaces-setup.md index b5f7e56..1f5b456 100644 --- a/es/labs/foundry/codespaces-setup.md +++ b/es/labs/foundry/codespaces-setup.md @@ -398,7 +398,7 @@ labs/foundry/ | Lab | Archivo | Descripción | | ----- | --------------------------------------------------------- | ------------------------------------------------------------ | | Lab 4 | [Anders — Executor Agent](lab04-anders-executor-agent.md) | Crear el agente ejecutor que genera reportes e interactúa con servicios de Contoso Retail. | -| Lab 5 | [Julie — Planner Agent (Opcional)](lab04-julie-planner-agent.md) | Crear el agente orquestador de campañas de marketing usando el patrón workflow con sub-agentes (SqlAgent, MarketingAgent) y herramienta OpenAPI. | +| Lab 5 | [Julie — Planner Agent (Opcional)](lab05-julie-planner-agent.md) | Crear el agente orquestador de campañas de marketing usando el patrón workflow con sub-agentes (SqlAgent, MarketingAgent) y herramienta OpenAPI. | --- From c48109c9d5415190aa6b531a1e3adf73ce1139da Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Apr 2026 11:38:22 +0000 Subject: [PATCH 11/13] fix: add execute permission to deploy-foundry-agent.sh Agent-Logs-Url: https://github.com/oaviles/multi-agentic-workshop/sessions/0709964a-d3f2-447f-89ae-b83f7d267d42 Co-authored-by: oaviles <1857451+oaviles@users.noreply.github.com> --- .../code/agents/AndersAgent/ms-foundry/deploy-foundry-agent.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 es/labs/foundry/code/agents/AndersAgent/ms-foundry/deploy-foundry-agent.sh diff --git a/es/labs/foundry/code/agents/AndersAgent/ms-foundry/deploy-foundry-agent.sh b/es/labs/foundry/code/agents/AndersAgent/ms-foundry/deploy-foundry-agent.sh old mode 100644 new mode 100755 From 61a69ec17c8ce3e9b87bfd02feb61234bdb365c5 Mon Sep 17 00:00:00 2001 From: Omar Aviles Date: Wed, 8 Apr 2026 12:01:54 +0000 Subject: [PATCH 12/13] =?UTF-8?q?fix:=20actualizar=20rutas=20de=20archivo?= =?UTF-8?q?=20en=20scripts=20de=20despliegue=20y=20documentaci=C3=B3n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ms-foundry/deploy-foundry-agent.ps1 | 2 +- .../ms-foundry/deploy-foundry-agent.sh | 80 +++++++++---------- .../foundry/lab04-anders-executor-agent.md | 28 ++++--- 3 files changed, 59 insertions(+), 51 deletions(-) diff --git a/es/labs/foundry/code/agents/AndersAgent/ms-foundry/deploy-foundry-agent.ps1 b/es/labs/foundry/code/agents/AndersAgent/ms-foundry/deploy-foundry-agent.ps1 index 8b330fa..ad91bde 100644 --- a/es/labs/foundry/code/agents/AndersAgent/ms-foundry/deploy-foundry-agent.ps1 +++ b/es/labs/foundry/code/agents/AndersAgent/ms-foundry/deploy-foundry-agent.ps1 @@ -3,7 +3,7 @@ param( [string]$FoundryModelDeploymentName = $env:FOUNDRY_MODEL_DEPLOYMENT_NAME, [string]$FoundryAgentName = $(if ($env:FOUNDRY_AGENT_NAME) { $env:FOUNDRY_AGENT_NAME } else { "AndersAgent" }), [string]$FoundryAgentInstructions = $(if ($env:FOUNDRY_AGENT_INSTRUCTIONS) { $env:FOUNDRY_AGENT_INSTRUCTIONS } else { "You are an analytical AI agent specialized in reading, understanding, and extracting insights from provided information." }), - [string]$ProjectFile = "./ms_foundry_agent.csproj" + [string]$ProjectFile = "./es/labs/foundry/code/agents/AndersAgent/ms-foundry/ms_foundry_agent.csproj" ) $ErrorActionPreference = "Stop" diff --git a/es/labs/foundry/code/agents/AndersAgent/ms-foundry/deploy-foundry-agent.sh b/es/labs/foundry/code/agents/AndersAgent/ms-foundry/deploy-foundry-agent.sh index 1735283..a8a4321 100755 --- a/es/labs/foundry/code/agents/AndersAgent/ms-foundry/deploy-foundry-agent.sh +++ b/es/labs/foundry/code/agents/AndersAgent/ms-foundry/deploy-foundry-agent.sh @@ -1,40 +1,40 @@ -#!/usr/bin/env bash - -set -euo pipefail - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -PROJECT_FILE="$SCRIPT_DIR/ms_foundry_agent.csproj" - -require_command() { - if ! command -v "$1" >/dev/null 2>&1; then - echo "Missing required command: $1" >&2 - exit 1 - fi -} - -require_value() { - local name="$1" - if [[ -z "${!name:-}" ]]; then - echo "Missing required environment variable: $name" >&2 - exit 1 - fi -} - -require_command az -require_command dotnet - -require_value FOUNDRY_PROJECT_ENDPOINT -require_value FOUNDRY_MODEL_DEPLOYMENT_NAME - -if ! az account show >/dev/null 2>&1; then - echo "Azure CLI is not logged in. Running az login..." - az login >/dev/null -fi - -export Foundry__ProjectEndpoint="$FOUNDRY_PROJECT_ENDPOINT" -export Foundry__ModelDeployment="$FOUNDRY_MODEL_DEPLOYMENT_NAME" -export Foundry__AgentName="${FOUNDRY_AGENT_NAME:-AndersAgent}" -export Foundry__AgentInstructions="${FOUNDRY_AGENT_INSTRUCTIONS:-You are an analytical AI agent specialized in reading, understanding, and extracting insights from provided information.}" - -echo "Deploying agent '$Foundry__AgentName' to Azure AI Foundry..." -dotnet run --project "$PROJECT_FILE" -- deploy +#!/usr/bin/env bash + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_FILE="$SCRIPT_DIR/ms_foundry_agent.csproj" + +require_command() { + if ! command -v "$1" >/dev/null 2>&1; then + echo "Missing required command: $1" >&2 + exit 1 + fi +} + +require_value() { + local name="$1" + if [[ -z "${!name:-}" ]]; then + echo "Missing required environment variable: $name" >&2 + exit 1 + fi +} + +require_command az +require_command dotnet + +require_value FOUNDRY_PROJECT_ENDPOINT +require_value FOUNDRY_MODEL_DEPLOYMENT_NAME + +if ! az account show >/dev/null 2>&1; then + echo "Azure CLI is not logged in. Running az login..." + az login >/dev/null +fi + +export Foundry__ProjectEndpoint="$FOUNDRY_PROJECT_ENDPOINT" +export Foundry__ModelDeployment="$FOUNDRY_MODEL_DEPLOYMENT_NAME" +export Foundry__AgentName="${FOUNDRY_AGENT_NAME:-AndersAgent}" +export Foundry__AgentInstructions="${FOUNDRY_AGENT_INSTRUCTIONS:-You are an analytical AI agent specialized in reading, understanding, and extracting insights from provided information.}" + +echo "Deploying agent '$Foundry__AgentName' to Azure AI Foundry..." +dotnet run --project "$PROJECT_FILE" -- deploy diff --git a/es/labs/foundry/lab04-anders-executor-agent.md b/es/labs/foundry/lab04-anders-executor-agent.md index 6f47177..4ae8755 100644 --- a/es/labs/foundry/lab04-anders-executor-agent.md +++ b/es/labs/foundry/lab04-anders-executor-agent.md @@ -132,7 +132,9 @@ export FOUNDRY_AGENT_INSTRUCTIONS="Eres un agente analítico especializado en le Ejecuta el script de despliegue para registrar el agente en el servicio: -./deploy-foundry-agent.sh +``` +./es/labs/foundry/code/agents/AndersAgent/ms-foundry/deploy-foundry-agent.sh +``` ### Paso 4: Configuración de Herramientas (Portal) @@ -177,22 +179,28 @@ Observa dos cosas clave: Inicia una sesión de chat en el Playground del agente y prueba con prompts de negocio como: +Para iniciar pregunata a Ander que pude hacer + +``` +Hola Anders, ¿qué puedes hacer? +``` + +Anders debería responder explicando que puede generar reportes de órdenes, aAhora pidele un reporte + ``` "Genera un reporte para el cliente Marco Rivera del periodo de enero a febrero de 2026. Muestra los pedidos en una tabla y calcula el total gastado." ``` Resultados Esperados: -El agente debe identificar qué herramienta llamar, procesar los datos y gGenerar una tabla de salida y un enlace de descarga para el reporte. - +El agente debe identificar qué herramienta llamar, pero necesita de algunos datos adiconales como: -### Paso 7: Validación y Pruebas -Inicia una sesión de chat en el Playground del agente y prueba con prompts de negocio como: +- Número de orden +- Fecha de la orden +- Producto, marca, categoría +- Cantidad, precio unitario, total por línea -``` -Tú: Hola Anders, ¿qué puedes hacer? -``` -Anders debería responder explicando que puede generar reportes de órdenes. Luego, prueba con datos reales (pega todo en una sola línea): +Luego, prueba con datos reales (pega todo en una sola línea): ``` Genera un reporte para Izabella Celma (periodo: 1-31 enero 2026). Orden ORD-CID-069-001 (2026-01-04): Sport-100 Helmet Black, Contoso Outdoor, Helmets, 6x$34.99=$209.94 | HL Road Frame Red 62, Contoso Outdoor, Road Frames, 10x$1431.50=$14315.00 | Long-Sleeve Logo Jersey S, Contoso Outdoor, Jerseys, 8x$49.99=$399.92. Orden ORD-CID-069-003 (2026-01-08): HL Road Frame Black 58, Contoso Outdoor, Road Frames, 3x$1431.50=$4294.50 | HL Road Frame Red 44, Contoso Outdoor, Road Frames, 7x$1431.50=$10020.50. Orden ORD-CID-069-002 (2026-01-17): HL Road Frame Red 62, Contoso Outdoor, Road Frames, 2x$1431.50=$2863.00 | LL Road Frame Black 60, Contoso Outdoor, Road Frames, 4x$337.22=$1348.88. @@ -210,7 +218,7 @@ Abre la URL del reporte en el navegador para verificar que se generó correctame Ahora prueba con un caso más sencillo — un solo pedido con dos productos: ``` -Tú: Genera un reporte para Marco Rivera (periodo: 5-10 febrero 2026). Orden ORD-CID-112-001 (2026-02-07): Mountain Bike Socks M, Contoso Outdoor, Socks, 3x$9.50=$28.50 | Water Bottle 30oz, Contoso Outdoor, Bottles and Cages, 1x$6.99=$6.99. +Genera un reporte para Marco Rivera (periodo: 5-10 febrero 2026). Orden ORD-CID-112-001 (2026-02-07): Mountain Bike Socks M, Contoso Outdoor, Socks, 3x$9.50=$28.50 | Water Bottle 30oz, Contoso Outdoor, Bottles and Cages, 1x$6.99=$6.99. ``` --- From 5c2dfaa16a297f546fb84894a25b9bb0df43bdfc Mon Sep 17 00:00:00 2001 From: Omar Aviles Date: Wed, 8 Apr 2026 12:19:02 +0000 Subject: [PATCH 13/13] =?UTF-8?q?fix:=20corregir=20instrucciones=20para=20?= =?UTF-8?q?crear=20herramienta=20y=20actualizar=20nombre=20de=20la=20API?= =?UTF-8?q?=20en=20la=20documentaci=C3=B3n=20de=20Lab=204?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- es/labs/foundry/lab04-anders-executor-agent.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/es/labs/foundry/lab04-anders-executor-agent.md b/es/labs/foundry/lab04-anders-executor-agent.md index 4ae8755..5d8a444 100644 --- a/es/labs/foundry/lab04-anders-executor-agent.md +++ b/es/labs/foundry/lab04-anders-executor-agent.md @@ -154,9 +154,7 @@ Pega la definición JSON de la API (Swagger/OpenAPI) de tu servicio (ej. una Azu https://func-contosoretail-.azurewebsites.net/api/openapi/v3.json ``` -Haz clic en Create tool. - -Activar Code Interpreter: Asegúrate de que el interruptor de "Code Interpreter" esté encendido para permitir cálculos matemáticos complejos sobre los datos. +Haz clic en Create tool y no olvider precionar "Save" o "Guardar los cambios del agente, si no lo hacer tendras un error al probarlo. ### Paso 5: Inspeccionar el agente en Azure AI Foundry @@ -170,7 +168,7 @@ Activar Code Interpreter: Asegúrate de que el interruptor de "Code Interpreter" Observa dos cosas clave: - **System prompt (instrucciones):** Verás las instrucciones completas que le dimos al agente, incluyendo el schema JSON. Esto es lo que guía su comportamiento al decidir cuándo y cómo invocar la API. -- **Tools (herramientas):** Verás **ContosoRetailAPI** listada como herramienta OpenAPI. Puedes expandirla para ver la especificación completa con el endpoint `ordersReporter`, los schemas de request/response, y la configuración de autenticación anónima. +- **Tools (herramientas):** Verás **myapitool** listada como herramienta OpenAPI. Puedes expandirla para ver la especificación completa con el endpoint `ordersReporter`, los schemas de request/response, y la configuración de autenticación anónima. > [!TIP] > El system prompt y las tools son los dos pilares que determinan qué puede hacer un agente y cómo lo hace. Entender esta relación es clave para diseñar agentes efectivos. @@ -188,7 +186,7 @@ Hola Anders, ¿qué puedes hacer? Anders debería responder explicando que puede generar reportes de órdenes, aAhora pidele un reporte ``` -"Genera un reporte para el cliente Marco Rivera del periodo de enero a febrero de 2026. Muestra los pedidos en una tabla y calcula el total gastado." +Genera un reporte para el cliente Marco Rivera del periodo de enero a febrero de 2026. Muestra los pedidos en una tabla y calcula el total gastado. ``` Resultados Esperados: