Ejercicio: Creación de un agente declarativo con un complemento de API
La extensión de un agente declarativo con acciones le permite recuperar y actualizar los datos almacenados en sistemas externos en tiempo real. Con los complementos de API, puede conectarse a sistemas externos a través de sus API para recuperar y actualizar información.
Descarga del proyecto de inicio
Para empezar, descargue el proyecto de ejemplo. En un explorador web:
- Vaya a https://github.com/microsoft/learn-declarative-agent-api-plugin-typescript.
- Si tiene una cuenta de GitHub:
Seleccione La lista desplegable Usar esta plantilla y, en el menú, elija Crear un nuevo repositorio.
En la lista de propietarios disponibles, elija su cuenta.
Asigne al repositorio el nombre da-ristorante-api.
Confirme la creación del repositorio mediante el botón Crear repositorio .
Espere a que GitHub cree el repositorio. A continuación, copie la dirección URL del repositorio.
Abra una línea de comandos.
En una línea de comandos, cambie el directorio de trabajo a donde desea almacenar el proyecto en el disco.
Clone el repositorio mediante el siguiente comando:
git clone https://github.com/your-user/your-repo.Abra la carpeta clonada en Visual Studio Code.
- Si no tiene una cuenta de GitHub:
El proyecto de ejemplo es un proyecto de Microsoft 365 Agents Toolkit que incluye un agente declarativo y una API anónima que se ejecuta en Azure Functions. El agente declarativo es idéntico a un agente declarativo recién creado mediante Microsoft 365 Agents Toolkit. La API pertenece a un restaurante italiano ficticio y le permite examinar el menú de hoy y realizar pedidos.
Examen de la definición de API
En primer lugar, examine la definición de API de la API del restaurante italiano.
En Visual Studio Code:
En la vista Explorador , abra el archivo appPackage/apiSpecificationFile/ristorante.yml . El archivo es una especificación de OpenAPI que describe la API del restaurante italiano.
Busque la propiedad servers.url.
servers: - url: http://localhost:7071/api description: Il Ristorante API serverObserve que apunta a una dirección URL local que coincide con la dirección URL estándar al ejecutar Azure Functions localmente.
Busque la propiedad paths , que contiene dos operaciones: /dishes para recuperar el menú de hoy y /orders para realizar un pedido.
Importante
Observe que cada operación contiene la propiedad operationId que identifica de forma única la operación en la especificación de la API. Copilot requiere que cada operación tenga un identificador único para que sepa a qué API debe llamar para solicitudes de usuario específicas.
Examen de la implementación de API
A continuación, examine la API de ejemplo que usa en este ejercicio.
En Visual Studio Code:
En la vista Explorador , abra el archivo src/data.json . El archivo contiene un elemento de menú ficticio para nuestro restaurante italiano. Cada plato consta de:
- nombre
- descripción
- vínculo a una imagen,
- precio
- en qué curso se sirve,
- tipo (plato o bebida),
- opcionalmente, una lista de alérgenos
En este ejercicio, las API usan este archivo como origen de datos.
A continuación, expanda la carpeta src/functions . Observe dos archivos denominados dishes.ts y placeOrder.ts. Estos archivos contienen la implementación de las dos operaciones definidas en la especificación de API.
Abra el archivo src/functions/dishes.ts . Tómese un momento para revisar cómo funciona la API. Comienza con la carga de los datos de ejemplo desde el archivo src/functions/data.json .
import data from "../data.json";A continuación, busca en los diferentes parámetros de cadena de consulta los posibles filtros que podría pasar el cliente que llama a la API.
const course = req.query.get('course'); const allergensString = req.query.get('allergens'); const allergens: string[] = allergensString ? allergensString.split(",") : []; const type = req.query.get('type'); const name = req.query.get('name');En función de los filtros especificados en la solicitud, la API filtra el conjunto de datos y devuelve una respuesta.
A continuación, examine la API para colocar los pedidos definidos en el archivo src/functions/placeOrder.ts . La API comienza por hacer referencia a los datos de ejemplo. A continuación, define la forma del orden que el cliente envía en el cuerpo de la solicitud.
interface OrderedDish { name?: string; quantity?: number; } interface Order { dishes: OrderedDish[]; }Cuando la API procesa la solicitud, comprueba primero si la solicitud contiene un cuerpo y si tiene la forma correcta. Si no es así, rechaza la solicitud con un error de solicitud incorrecta 400.
let order: Order | undefined; try { order = await req.json() as Order | undefined; } catch (error) { return { status: 400, jsonBody: { message: "Invalid JSON format" }, } as HttpResponseInit; } if (!order.dishes || !Array.isArray(order.dishes)) { return { status: 400, jsonBody: { message: "Invalid order format" } } as HttpResponseInit; }A continuación, la API resuelve la solicitud en platos del menú y calcula el precio total.
let totalPrice = 0; const orderDetails = order.dishes.map(orderedDish => { const dish = data.find(d => d.name.toLowerCase().includes(orderedDish.name.toLowerCase())); if (dish) { totalPrice += dish.price * orderedDish.quantity; return { name: dish.name, quantity: orderedDish.quantity, price: dish.price, }; } else { context.error(`Invalid dish: ${orderedDish.name}`); return null; } });Importante
Observe cómo la API espera que el cliente especifique el plato por una parte de su nombre en lugar de por su identificador. Esto es a propósito porque los modelos de lenguaje grande funcionan mejor con palabras que números. Además, antes de llamar a la API para realizar el pedido, Copilot tiene el nombre del plato disponible como parte del aviso del usuario. Si Copilot tuviera que hacer referencia a un plato por su identificador, primero tendría que recuperar lo que requiere solicitudes api adicionales y que Copilot no puede hacer ahora.
Cuando la API está lista, devuelve una respuesta con un precio total, un identificador de pedido integrado y un estado.
const orderId = Math.floor(Math.random() * 10000); return { status: 201, jsonBody: { order_id: orderId, status: "confirmed", total_price: totalPrice, } } as HttpResponseInit;
Compilación de la definición del complemento
El siguiente paso es agregar la definición del complemento al proyecto. La definición del complemento contiene la siguiente información:
- Qué acciones puede realizar el complemento.
- Cuál es la forma de los datos que espera y devuelve.
- Cómo el agente declarativo debe llamar a la API subyacente.
Adición de la estructura de definición básica del complemento
En Visual Studio Code:
En la carpeta appPackage , agregue un nuevo archivo denominado ai-plugin.json.
Pegue el contenido siguiente:
{ "$schema": "https://developer.microsoft.com/json-schemas/copilot/plugin/v2.1/schema.json", "schema_version": "v2.1", "namespace": "ilristorante", "name_for_human": "Il Ristorante", "description_for_human": "See the today's menu and place orders", "description_for_model": "Plugin for getting the today's menu, optionally filtered by course and allergens, and placing orders", "functions": [ ], "runtimes": [ ], "capabilities": { "localization": {}, "conversation_starters": [] } }El archivo contiene una estructura básica para un complemento de API con una descripción para el humano y el modelo. El description_for_model incluye información detallada sobre lo que el complemento puede hacer para ayudar al agente a comprender cuándo debe considerar la posibilidad de invocarlo.
Guarde los cambios.
Definición de funciones
Un complemento de API define una o varias funciones que se asignan a las operaciones de API definidas en la especificación de la API. Cada función consta de un nombre y una descripción y una definición de respuesta que indica al agente cómo mostrar los datos a los usuarios.
Definición de una función para recuperar el menú
Comience con la definición de una función para recuperar la información sobre el menú de hoy.
En Visual Studio Code:
Abra el archivo appPackage/ai-plugin.json .
En la matriz de funciones , agregue el siguiente fragmento de código:
{ "name": "getDishes", "description": "Returns information about the dishes on the menu. Can filter by course (breakfast, lunch or dinner), name, allergens, or type (dish, drink).", "capabilities": { "response_semantics": { "data_path": "$.dishes", "properties": { "title": "$.name", "subtitle": "$.description" } } } }Para empezar, defina una función que invoque la operación getDishes desde la especificación de la API. A continuación, proporcione una descripción de función. Esta descripción es importante porque Copilot la usa para decidir qué función invocar para el aviso de un usuario.
En la propiedad response_semantics, especifique cómo debe mostrar Copilot los datos que recibe de la API. Dado que la API devuelve la información sobre los platos del menú de la propiedad dishes , establezca la propiedad data_path en la
$.dishesexpresión JSONPath.A continuación, en la sección de propiedades , asigna qué propiedades de la respuesta de API representan el título, la descripción y la dirección URL. Dado que en este caso los platos no tienen una dirección URL, solo asigna el título y la descripción.
El fragmento de código completo tiene el siguiente aspecto:
{ "$schema": "https://developer.microsoft.com/json-schemas/copilot/plugin/v2.1/schema.json", "schema_version": "v2.1", "namespace": "ilristorante", "name_for_human": "Il Ristorante", "description_for_human": "See the today's menu and place orders", "description_for_model": "Plugin for getting the today's menu, optionally filtered by course and allergens, and placing orders", "functions": [ { "name": "getDishes", "description": "Returns information about the dishes on the menu. Can filter by course (breakfast, lunch or dinner), name, allergens, or type (dish, drink).", "capabilities": { "response_semantics": { "data_path": "$.dishes", "properties": { "title": "$.name", "subtitle": "$.description" } } } } ], "runtimes": [ ], "capabilities": { "localization": {}, "conversation_starters": [] } }Guarde los cambios.
Definición de una función para colocar el orden
A continuación, defina una función para colocar el orden.
En Visual Studio Code:
Abra el archivo appPackage/ai-plugin.json .
Al final de la matriz de funciones , agregue el siguiente fragmento de código:
{ "name": "placeOrder", "description": "Places an order and returns the order details", "capabilities": { "response_semantics": { "data_path": "$", "properties": { "title": "$.order_id", "subtitle": "$.total_price" } } } }Para empezar, haga referencia a la operación de API con el identificador placeOrder. A continuación, proporcione una descripción que Copilot usa para hacer coincidir esta función con el símbolo del sistema de un usuario. A continuación, indica a Copilot cómo devolver los datos. A continuación se devuelven los datos que devuelve la API después de realizar un pedido:
{ "order_id": 6532, "status": "confirmed", "total_price": 21.97 }Dado que los datos que desea mostrar se encuentran directamente en la raíz del objeto de respuesta, establezca el data_path en $ el que se indica el nodo superior del objeto JSON. Defina el título para mostrar el número del pedido y el subtítulo su precio.
El archivo completo tiene el siguiente aspecto:
{ "$schema": "https://developer.microsoft.com/json-schemas/copilot/plugin/v2.1/schema.json", "schema_version": "v2.1", "namespace": "ilristorante", "name_for_human": "Il Ristorante", "description_for_human": "See the today's menu and place orders", "description_for_model": "Plugin for getting the today's menu, optionally filtered by course and allergens, and placing orders", "functions": [ { "name": "getDishes", "description": "Returns information about the dishes on the menu. Can filter by course (breakfast, lunch or dinner), name, allergens, or type (dish, drink).", ...trimmed for brevity }, { "name": "placeOrder", "description": "Places an order and returns the order details", "capabilities": { "response_semantics": { "data_path": "$", "properties": { "title": "$.order_id", "subtitle": "$.total_price" } } } } ], "runtimes": [ ], "capabilities": { "localization": {}, "conversation_starters": [] } }Guarde los cambios.
Definición de entornos de ejecución
Después de definir funciones para que Copilot las invoque, el siguiente paso es indicarle cómo debe llamarlas. Lo hace en la sección runtimes de la definición del complemento.
En Visual Studio Code:
Abra el archivo appPackage/ai-plugin.json .
En la matriz runtimes, agregue el código siguiente:
{ "type": "OpenApi", "auth": { "type": "None" }, "spec": { "url": "apiSpecificationFile/ristorante.yml" }, "run_for_functions": [ "getDishes", "placeOrder" ] }Empiece por indicar a Copilot que le proporcione información de OpenAPI sobre la API (tipo: OpenApi) a la que llamar y que es anónima (auth.type: None). A continuación, en la sección especificación , especifique la ruta de acceso relativa a la especificación de API ubicada en el proyecto. Por último, en la propiedad run_for_functions , enumera todas las funciones que pertenecen a esta API.
El archivo completo tiene el siguiente aspecto:
{ "$schema": "https://developer.microsoft.com/json-schemas/copilot/plugin/v2.1/schema.json", "schema_version": "v2.1", "namespace": "ilristorante", "name_for_human": "Il Ristorante", "description_for_human": "See the today's menu and place orders", "description_for_model": "Plugin for getting the today's menu, optionally filtered by course and allergens, and placing orders", "functions": [ { "name": "getDishes", ...trimmed for brevity }, { "name": "placeOrder", ...trimmed for brevity } ], "runtimes": [ { "type": "OpenApi", "auth": { "type": "None" }, "spec": { "url": "apiSpecificationFile/ristorante.yml" }, "run_for_functions": [ "getDishes", "placeOrder" ] } ], "capabilities": { "localization": {}, "conversation_starters": [] } }Guarde los cambios.
Conexión de la definición del complemento al agente declarativo
Después de completar la compilación de la definición del complemento de API, el siguiente paso es registrarla con el agente declarativo. Cuando los usuarios interactúan con el agente declarativo, coincide con el símbolo del sistema del usuario con los complementos de API definidos e invoca las funciones pertinentes.
En Visual Studio Code:
Abra el archivo appPackage/declarativeAgent.json .
Después de la propiedad instructions , agregue el siguiente fragmento de código:
"actions": [ { "id": "menuPlugin", "file": "ai-plugin.json" } ]Con este fragmento de código, conectará el agente declarativo al complemento de API. Especifique un identificador único para el complemento e indique al agente dónde puede encontrar la definición del complemento.
El archivo completo tiene el siguiente aspecto:
{ "$schema": "https://developer.microsoft.com/json-schemas/copilot/declarative-agent/v1.0/schema.json", "version": "v1.0", "name": "Declarative agent", "description": "Declarative agent created with Microsoft 365 Agents Toolkit", "instructions": "$[file('instruction.txt')]", "actions": [ { "id": "menuPlugin", "file": "ai-plugin.json" } ] }Guarde los cambios.
Actualización de la información y las instrucciones del agente declarativo
El agente declarativo que va a crear en este ejercicio ayuda a los usuarios a examinar el menú del restaurante italiano local y realizar pedidos. Para optimizar el agente para este escenario, actualice su nombre, descripción e instrucciones.
En Visual Studio Code:
- Actualice la información del agente declarativo:
- Abra el archivo appPackage/declarativeAgent.json .
- Actualice el valor de la propiedad name a Il Ristorante.
- Actualice el valor de la propiedad de descripción para pedir los platos italianos más deliciosos y bebidas desde la comodidad de su escritorio.
- Guarde los cambios.
- Actualice las instrucciones del agente declarativo:
Abra el archivo appPackage/instruction.txt .
Reemplace su contenido por:
You are an assistant specialized in helping users explore the menu of an Italian restaurant and place orders. You interact with the restaurant's menu API and guide users through the ordering process, ensuring a smooth and delightful experience. Follow the steps below to assist users in selecting their desired dishes and completing their orders: ### General Behavior: - Always greet the user warmly and offer assistance in exploring the menu or placing an order. - Use clear, concise language with a friendly tone that aligns with the atmosphere of a high-quality local Italian restaurant. - If the user is browsing the menu, offer suggestions based on the course they are interested in (breakfast, lunch, or dinner). - Ensure the conversation remains focused on helping the user find the information they need and completing the order. - Be proactive but never pushy. Offer suggestions and be informative, especially if the user seems uncertain. ### Menu Exploration: - When a user requests to see the menu, use the `GET /dishes` API to retrieve the list of available dishes, optionally filtered by course (breakfast, lunch, or dinner). - Example: If a user asks for breakfast options, use the `GET /dishes?course=breakfast` to return only breakfast dishes. - Present the dishes to the user with the following details: - Name of the dish - A tasty description of the dish - Price in € (Euro) formatted as a decimal number with two decimal places - Allergen information (if relevant) - Don't include the URL. ### Beverage Suggestion: - If the order does not already include a beverage, suggest a suitable beverage option based on the course. - Use the `GET /dishes?course={course}&type=drink` API to retrieve available drinks for that course. - Politely offer the suggestion: *"Would you like to add a beverage to your order? I recommend [beverage] for [course]."* ### Placing the Order: - Once the user has finalized their order, use the `POST /order` API to submit the order. - Ensure the request includes the correct dish names and quantities as per the user's selection. - Example API payload: ```json { "dishes": [ { "name": "frittata", "quantity": 2 }, { "name": "cappuccino", "quantity": 1 } ] } ``` ### Error Handling: - If the user selects a dish that is unavailable or provides an invalid dish name, respond gracefully and suggest alternative options. - Example: *"It seems that dish is currently unavailable. How about trying [alternative dish]?"* - Ensure that any errors from the API are communicated politely to the user, offering to retry or explore other options.Observe que en las instrucciones, definimos el comportamiento general del agente y le indicamos de qué es capaz. También se incluyen instrucciones para un comportamiento específico en torno a la colocación de un pedido, incluida la forma de los datos que espera la API. Se incluye esta información para garantizar que el agente funciona según lo previsto.
Guarde los cambios.
- Para ayudar a los usuarios, comprenda para qué pueden usar el agente, agregue los inicios de conversación:
Abra el archivo appPackage/declarativeAgent.json .
Después de la propiedad instructions , agregue una nueva propiedad denominada conversation_starters:
"conversation_starters": [ { "text": "What's for lunch today?" }, { "text": "What can I order for dinner that is gluten-free?" } ]El archivo completo tiene el siguiente aspecto:
{ "$schema": "https://developer.microsoft.com/json-schemas/copilot/declarative-agent/v1.0/schema.json", "version": "v1.0", "name": "Il Ristorante", "description": "Order the most delicious Italian dishes and drinks from the comfort of your desk.", "instructions": "$[file('instruction.txt')]", "conversation_starters": [ { "text": "What's for lunch today?" }, { "text": "What can I order for dinner that is gluten-free?" } ], "actions": [ { "id": "menuPlugin", "file": "ai-plugin.json" } ] }Guarde los cambios.
Actualización de la dirección URL de la API
Para poder probar el agente declarativo, debe actualizar la dirección URL de la API en el archivo de especificación de la API. En este momento, la dirección URL se establece en http://localhost:7071/api la que es la dirección URL que Azure Functions usa al ejecutarse localmente. Sin embargo, dado que quiere que Copilot llame a la API desde la nube, debe exponer la API a Internet. Microsoft 365 Agents Toolkit expone automáticamente la API local a través de Internet mediante la creación de un túnel de desarrollo. Cada vez que empiece a depurar el proyecto, Microsoft 365 Agents Toolkit inicia un nuevo túnel de desarrollo y almacena su dirección URL en la variable OPENAPI_SERVER_URL . Puede ver cómo Microsoft 365 Agents Toolkit inicia el túnel y almacena su dirección URL en el archivo .vscode/tasks.json , en la tarea Iniciar túnel local :
{
// Start the local tunnel service to forward public URL to local port and inspect traffic.
// See https://aka.ms/teamsfx-tasks/local-tunnel for the detailed args definitions.
"label": "Start local tunnel",
"type": "teamsfx",
"command": "debug-start-local-tunnel",
"args": {
"type": "dev-tunnel",
"ports": [
{
"portNumber": 7071,
"protocol": "http",
"access": "public",
"writeToEnvironmentFile": {
"endpoint": "OPENAPI_SERVER_URL", // output tunnel endpoint as OPENAPI_SERVER_URL
}
}
],
"env": "local"
},
"isBackground": true,
"problemMatcher": "$teamsfx-local-tunnel-watch"
}
Para usar este túnel, debe actualizar la especificación de API para usar la variable OPENAPI_SERVER_URL .
En Visual Studio Code:
Abra el archivo appPackage/apiSpecificationFile/ristorante.yml .
Cambie el valor de la propiedad servers.url a ${{OPENAPI_SERVER_URL}}/api.
El archivo cambiado tiene el siguiente aspecto:
openapi: 3.0.0 info: title: Il Ristorante menu API version: 1.0.0 description: API to retrieve dishes and place orders for Il Ristorante. servers: - url: ${{OPENAPI_SERVER_URL}}/api description: Il Ristorante API server paths: ...trimmed for brevityGuarde los cambios.
El complemento de API ha finalizado e integrado con un agente declarativo. Continúe con la prueba del agente en Microsoft 365 Copilot.
Pruebe el agente declarativo con el complemento de API en Microsoft 365 Copilot
El último paso consiste en probar el agente declarativo con el complemento de API en Microsoft 365 Copilot.
En Visual Studio Code:
En la barra de actividad, elija Microsoft 365 Agents Toolkit.
En la sección Cuentas, asegúrese de que ha iniciado sesión en el inquilino de Microsoft 365 con Microsoft 365 Copilot.
En la barra de actividad, elija Ejecutar y depurar.
Seleccione la configuración Depurar en Copilot e inicie la depuración con el botón Iniciar depuración .
Visual Studio Code compila e implementa el proyecto en el inquilino de Microsoft 365 y abre una nueva ventana del explorador web.
En el explorador web:
Cuando se le solicite, inicie sesión con la cuenta que pertenece a su inquilino de Microsoft 365 con Microsoft 365 Copilot.
En la barra lateral, seleccione Il Ristorante.
Elija el inicio de conversación ¿Qué hay para el almuerzo hoy? y envíe el mensaje.
Cuando se le solicite, examine los datos que el agente envía a la API y confirme mediante el botón Permitir una vez .
Espere a que el agente responda. Observe que, aunque muestra citas para la información que recupera de la API, el elemento emergente solo muestra el título del plato. No muestra ninguna información adicional, porque el complemento de API no define una plantilla de tarjeta adaptable.
Realice un pedido escribiendo en el cuadro de texto de aviso: 1 espaguetisx, 1 té helado y envíe el mensaje.
Examine los datos que el agente envía a la API y continúe con el botón Confirmar .
Espere a que el agente realice el pedido y devuelva el resumen del pedido. Una vez más, observe que el agente muestra el resumen del pedido en texto sin formato porque no tiene una plantilla de tarjeta adaptable.
Volver para Visual Studio Code y detener la depuración.
Cambie a la pestaña Terminal y cierre todos los terminales activos.