Skip to content

Agregar una Herramienta Personalizada (Llamada de Función)

En este capítulo, agregarás una herramienta personalizada a tu agente usando llamadas de función.

¿Por Qué Agregar una Herramienta Personalizada?

A veces, tu agente necesita hacer cosas más allá de solo hablar, como:

  • Buscar datos de usuario
  • Consultar un sistema de backend
  • Ejecutar lógica específica del negocio

Las llamadas de función te permiten exponer código Python al agente, para que pueda llamar a tus funciones cuando sea necesario.

Paso 1 - Actualizar Tus Importaciones

python
import os
import json
import glob
from dotenv import load_dotenv
from azure.identity import DefaultAzureCredential
from azure.ai.projects import AIProjectClient
from azure.ai.projects.models import PromptAgentDefinition, FileSearchTool, FunctionTool, Tool
from openai.types.responses.response_input_param import FunctionCallOutput, ResponseInputParam

Paso 2 - Definir Tu Función

python
## -- LLAMADA DE FUNCIÓN -- ##

# Implementación de la herramienta de consulta
def get_customer_by_phone(phone_number: str) -> str:
    customers = {
        "1234567890": {"name": "John Doe", "email": "john@example.com"},
        "0987654321": {"name": "Jane Doe", "email": "jane@example.com"},
    }
    customer = customers.get(phone_number, None)
    if customer:
        return json.dumps(customer)
    return json.dumps({"error": "Cliente no encontrado"})

Paso 3 - Crear la Especificación de la Herramienta

python
tools_spec = {
    "get_customer_by_phone": {
        "function": get_customer_by_phone,
        "spec": {
            "type": "function",
            "name": "get_customer_by_phone",
            "description": "Recupera información del cliente basado en su número de teléfono.",
            "parameters": {
                "type": "object",
                "properties": {
                    "phone_number": {
                        "type": "string",
                        "description": "El número de teléfono del cliente"
                    }
                },
                "required": ["phone_number"],
                "additionalProperties": False,
            },
        },
    },
}
## -- LLAMADA DE FUNCIÓN -- ##

Paso 4 - Agregar la Herramienta al Conjunto de Herramientas

python
## Definir el conjunto de herramientas para el agente
toolset: list[Tool] = []
toolset.append(FileSearchTool(vector_store_ids=[vector_store.id]))
toolset.append(FunctionTool(functions=[tools_spec["get_customer_by_phone"]["spec"]]))

Paso 5 - Manejar Llamadas de Función en el Bucle de Chat

El bucle de chat ahora necesita manejar llamadas de función:

python
while True:
    user_input = input("Usuario: ")
    if user_input.lower() == "salir":
        break

    input_items: list[ResponseInputParam] = []
    input_items.append({"type": "message", "role": "user", "content": user_input})

    while True:
        response = openai_client.responses.create(
            input=input_items,
            model=os.environ["MODEL_DEPLOYMENT_NAME"],
            extra_body={
                "agent": {
                    "definition": agent.definition.model_dump(mode="json", exclude_none=True),
                }
            },
        )

        # Procesar la respuesta del agente y manejar llamadas de función
        if response.status == "incomplete" and response.incomplete_details.reason == "tool_calls":
            for output in response.output:
                input_items.append(output)
                if output.type == "function_call":
                    tool_result = tools_spec[output.name]["function"](**json.loads(output.arguments))
                    call_output: FunctionCallOutput = {
                        "type": "function_call_output",
                        "call_id": output.call_id,
                        "output": tool_result,
                    }
                    input_items.append(call_output)
        else:
            for output in response.output:
                if output.type == "message":
                    print("Agente:", output.content[0].text)
            break

Paso 6 - Ejecutar el Agente

bash
python agent.py

Resumen

En este capítulo, tú:

  • Creaste una función Python personalizada
  • La expusiste como una FunctionTool para tu agente
  • Manejaste llamadas de función en tu bucle de chat

Muestra de código final

python
import json
import os
import glob
from dotenv import load_dotenv
from azure.identity import DefaultAzureCredential
from azure.ai.projects import AIProjectClient
from azure.ai.projects.models import PromptAgentDefinition, FileSearchTool, FunctionTool, Tool
from openai.types.responses.response_input_param import FunctionCallOutput, ResponseInputParam

load_dotenv()

vector_store_id = ""  # Establece el ID de tu vector store si ya tienes uno

## Configurar el Cliente del Proyecto
project_client = AIProjectClient(
    endpoint=os.environ["PROJECT_ENDPOINT"],
    credential=DefaultAzureCredential(),
)
openai_client = project_client.get_openai_client()


## -- BÚSQUEDA DE ARCHIVOS -- ##

if vector_store_id:
    vector_store = openai_client.vector_stores.retrieve(vector_store_id)
    print(f"Usando vector store existente (id: {vector_store.id})")
else:
    # Crear vector store para búsqueda de archivos
    vector_store = openai_client.vector_stores.create(name="ContosoPizzaStores")
    print(f"Vector store creado (id: {vector_store.id})")

    # Subir archivos al vector store
    for file_path in glob.glob("documentos/*.md"):
        file = openai_client.vector_stores.files.upload_and_poll(
            vector_store_id=vector_store.id, file=open(file_path, "rb")
        )
        print(f"Archivo subido al vector store (id: {file.id})")
## -- BÚSQUEDA DE ARCHIVOS -- ##


## -- Herramienta de Llamada a Función -- ##
func_tool = FunctionTool(
    name="get_pizza_quantity",
    parameters={
        "type": "object",
        "properties": {
            "people": {
                "type": "integer",
                "description": "El número de personas para las que pedir pizza",
            },
        },
        "required": ["people"],
        "additionalProperties": False,
    },
    description="Obtener la cantidad de pizza a pedir basado en el número de personas.",
    strict=True,
)

def get_pizza_quantity(people: int) -> str:
    """Calcular el número de pizzas a pedir basado en el número de personas.
        Asume que cada pizza puede alimentar a 2 personas.
    Args:
        people (int): El número de personas para las que pedir pizza.
    Returns:
        str: Un mensaje indicando el número de pizzas a pedir.
    """
    print(f"[LLAMADA A FUNCIÓN:get_pizza_quantity] Calculando cantidad de pizza para {people} personas.")
    return f"Para {people} personas necesitas pedir {people // 2 + people % 2} pizzas."
## -- Herramienta de Llamada a Función -- ##


## Definir el conjunto de herramientas para el agente
toolset: list[Tool] = []
toolset.append(FileSearchTool(vector_store_ids=[vector_store.id]))
toolset.append(func_tool)


## Crear un Agente Foundry
agent = project_client.agents.create_version(
    agent_name="hello-world-agent",
    definition=PromptAgentDefinition(
        model=os.environ["MODEL_DEPLOYMENT_NAME"],
        instructions=open("instrucciones.txt").read(),
        tools=toolset,
    ),
)
print(f"Agente creado (id: {agent.id}, nombre: {agent.name}, versión: {agent.version})")


## Crear una conversación para la interacción con el agente
conversation = openai_client.conversations.create()
print(f"Conversación creada (id: {conversation.id})")

## Chatear con el agente

while True:
    # Obtener la entrada del usuario
    user_input = input("Tú: ")

    if user_input.lower() in ["salir", "terminar"]:
        print("Saliendo del chat.")
        break

    # Obtener la respuesta del agente
    response = openai_client.responses.create(
        conversation=conversation.id,
        input=user_input,
        extra_body={"agent": {"name": agent.name, "type": "agent_reference"}},
    )

    # Manejar las llamadas a funciones en la respuesta
    input_list: ResponseInputParam = []
    for item in response.output:
        if item.type == "function_call":
            if item.name == "get_pizza_quantity":
                # Ejecutar la lógica de la función para get_pizza_quantity
                pizza_quantity = get_pizza_quantity(**json.loads(item.arguments))
                # Proporcionar los resultados de la llamada a función al modelo
                input_list.append(
                    FunctionCallOutput(
                        type="function_call_output",
                        call_id=item.call_id,
                        output=json.dumps({"pizza_quantity": pizza_quantity}),
                    )
                )

    if input_list:
        response = openai_client.responses.create(
            previous_response_id=response.id,
            input=input_list,
            extra_body={"agent": {"name": agent.name, "type": "agent_reference"}},
        )    

    # Imprimir la respuesta del agente
    print(f"Asistente: {response.output_text}")

Traducido usando GitHub Copilot.