# Agents-Course

## Docs

- [Introducción a los Frameworks de Agentes](https://huggingface.co/learn/agents-course/es/unit2/introduction.md)
- [Autoevaluación Rápida (sin calificar) [[quiz2]]](https://huggingface.co/learn/agents-course/es/unit2/llama-index/quiz2.md)
- [Crear workflows agenticos en LlamaIndex](https://huggingface.co/learn/agents-course/es/unit2/llama-index/workflows.md)
- [Conclusión](https://huggingface.co/learn/agents-course/es/unit2/llama-index/conclusion.md)
- [Uso de Herramientas en LlamaIndex](https://huggingface.co/learn/agents-course/es/unit2/llama-index/tools.md)
- [Índice de Contenidos](https://huggingface.co/learn/agents-course/es/unit2/llama-index/README.md)
- [Usando Agentes en LlamaIndex](https://huggingface.co/learn/agents-course/es/unit2/llama-index/agents.md)
- [Introducción a LlamaIndex](https://huggingface.co/learn/agents-course/es/unit2/llama-index/introduction.md)
- [Pequeño Quiz (no calificado) [[quiz1]]](https://huggingface.co/learn/agents-course/es/unit2/llama-index/quiz1.md)
- [¿Qué son los componentes en LlamaIndex?](https://huggingface.co/learn/agents-course/es/unit2/llama-index/components.md)
- [Introducción a LlamaHub](https://huggingface.co/learn/agents-course/es/unit2/llama-index/llama-hub.md)
- [Sistemas Multi-Agente](https://huggingface.co/learn/agents-course/es/unit2/smolagents/multi_agent_systems.md)
- [Agentes de Visión con smolagents](https://huggingface.co/learn/agents-course/es/unit2/smolagents/vision_agents.md)
- [Construyendo Sistemas RAG con Agentes](https://huggingface.co/learn/agents-course/es/unit2/smolagents/retrieval_agents.md)
- [Pequeño Quiz (no calificado) [[quiz2]]](https://huggingface.co/learn/agents-course/es/unit2/smolagents/quiz2.md)
- [Construcción de Agentes que Usan Código](https://huggingface.co/learn/agents-course/es/unit2/smolagents/code_agents.md)
- [Conclusión](https://huggingface.co/learn/agents-course/es/unit2/smolagents/conclusion.md)
- [Herramientas](https://huggingface.co/learn/agents-course/es/unit2/smolagents/tools.md)
- [¡Hora del Examen!](https://huggingface.co/learn/agents-course/es/unit2/smolagents/final_quiz.md)
- [Introducción a `smolagents`](https://huggingface.co/learn/agents-course/es/unit2/smolagents/introduction.md)
- [¿Por qué usar smolagents?](https://huggingface.co/learn/agents-course/es/unit2/smolagents/why_use_smolagents.md)
- [Pequeño Quiz (no calificado) [[quiz1]]](https://huggingface.co/learn/agents-course/es/unit2/smolagents/quiz1.md)
- [Escribiendo acciones como fragmentos de código o estructuras JSON](https://huggingface.co/learn/agents-course/es/unit2/smolagents/tool_calling_agents.md)
- [¿Qué es `LangGraph`?](https://huggingface.co/learn/agents-course/es/unit2/langgraph/when_to_use_langgraph.md)
- [Componentes de LangGraph](https://huggingface.co/learn/agents-course/es/unit2/langgraph/building_blocks.md)
- [Construyendo Tu Primer LangGraph](https://huggingface.co/learn/agents-course/es/unit2/langgraph/first_graph.md)
- [Conclusión](https://huggingface.co/learn/agents-course/es/unit2/langgraph/conclusion.md)
- [Grafo de Análisis de Documentos](https://huggingface.co/learn/agents-course/es/unit2/langgraph/document_analysis_agent.md)
- [Introducción a `LangGraph`](https://huggingface.co/learn/agents-course/es/unit2/langgraph/introduction.md)
- [Evalua de tu comprensión de LangGraph](https://huggingface.co/learn/agents-course/es/unit2/langgraph/quiz1.md)
- [Readme](https://huggingface.co/learn/agents-course/es/unit3/README.md)
- [Generación Aumentada por Recuperación con Agentes (RAG Agéntico)](https://huggingface.co/learn/agents-course/es/unit3/agentic-rag/agentic-rag.md)
- [Creando una Herramienta RAG para Historias de Invitados](https://huggingface.co/learn/agents-course/es/unit3/agentic-rag/invitees.md)
- [Conclusión](https://huggingface.co/learn/agents-course/es/unit3/agentic-rag/conclusion.md)
- [Construyendo e Integrando Herramientas para Tu Agente](https://huggingface.co/learn/agents-course/es/unit3/agentic-rag/tools.md)
- [Introducción al Caso de Uso para RAG Agéntico](https://huggingface.co/learn/agents-course/es/unit3/agentic-rag/introduction.md)
- [Creando tu Agente para la Gala](https://huggingface.co/learn/agents-course/es/unit3/agentic-rag/agent.md)
- [Reclama tu Certificado 🎓](https://huggingface.co/learn/agents-course/es/unit4/get-your-certificate.md)
- [Práctica](https://huggingface.co/learn/agents-course/es/unit4/hands-on.md)
- [Conclusión](https://huggingface.co/learn/agents-course/es/unit4/conclusion.md)
- [Readme](https://huggingface.co/learn/agents-course/es/unit4/README.md)
- [Bienvenido/a a la última Unidad [[introduccion]]](https://huggingface.co/learn/agents-course/es/unit4/introduction.md)
- [¿Qué es GAIA?](https://huggingface.co/learn/agents-course/es/unit4/what-is-gaia.md)
- [¿Y ahora? ¿Qué temas debería aprender?](https://huggingface.co/learn/agents-course/es/unit4/additional-readings.md)
- [Hagamos Fine-Tuning de Tu Modelo para Llamadas a Funciones](https://huggingface.co/learn/agents-course/es/bonus-unit1/fine-tuning.md)
- [¿Qué es la Llamada a Funciones?](https://huggingface.co/learn/agents-course/es/bonus-unit1/what-is-function-calling.md)
- [Conclusión [[conclusion]]](https://huggingface.co/learn/agents-course/es/bonus-unit1/conclusion.md)
- [Introducción](https://huggingface.co/learn/agents-course/es/bonus-unit1/introduction.md)
- [Observabilidad y Evaluación de Agentes de IA](https://huggingface.co/learn/agents-course/es/bonus-unit2/what-is-agent-observability-and-evaluation.md)
- [Unidad Extra 2: Observabilidad y Evaluación de Agentes](https://huggingface.co/learn/agents-course/es/bonus-unit2/monitoring-and-evaluating-agents-notebook.md)
- [Observabilidad y Evaluación de Agentes de IA](https://huggingface.co/learn/agents-course/es/bonus-unit2/introduction.md)
- [Cuestionario: Evaluación de Agentes de IA](https://huggingface.co/learn/agents-course/es/bonus-unit2/quiz.md)
- [El Estado del Arte en el Uso de LLMs en Juegos](https://huggingface.co/learn/agents-course/es/bonus-unit3/state-of-art.md)
- [Construye tu Propio Agente de Batalla Pokémon](https://huggingface.co/learn/agents-course/es/bonus-unit3/building_your_pokemon_agent.md)
- [Conclusión](https://huggingface.co/learn/agents-course/es/bonus-unit3/conclusion.md)
- [Introducción](https://huggingface.co/learn/agents-course/es/bonus-unit3/introduction.md)
- [Lanzando tu Agente de Batalla Pokémon](https://huggingface.co/learn/agents-course/es/bonus-unit3/launching_agent_battle.md)
- [De LLMs a Agentes de IA](https://huggingface.co/learn/agents-course/es/bonus-unit3/from-llm-to-agents.md)
- [Live 1: Como funciona el curso y preguntas y respuestas](https://huggingface.co/learn/agents-course/es/communication/live1.md)
- [Quiz de la Unidad 1](https://huggingface.co/learn/agents-course/es/unit1/final-quiz.md)
- [¿Qué son los LLMs?](https://huggingface.co/learn/agents-course/es/unit1/what-are-llms.md)
- [Acciones: Permitiendo al Agente Interactuar con Su Entorno](https://huggingface.co/learn/agents-course/es/unit1/actions.md)
- [Autoevaluación Rápida (sin calificación) [[quiz2]]](https://huggingface.co/learn/agents-course/es/unit1/quiz2.md)
- [Conclusión [[conclusion]]](https://huggingface.co/learn/agents-course/es/unit1/conclusion.md)
- [¿Qué son las Herramientas?](https://huggingface.co/learn/agents-course/es/unit1/tools.md)
- [Mensajes y Tokens especiales](https://huggingface.co/learn/agents-course/es/unit1/messages-and-special-tokens.md)
- [Vamos Crear Nuestro Primer Agente Usando smolagents](https://huggingface.co/learn/agents-course/es/unit1/tutorial.md)
- [¿Qué es un Agente?](https://huggingface.co/learn/agents-course/es/unit1/what-are-agents.md)
- [Tabla de Contenidos](https://huggingface.co/learn/agents-course/es/unit1/README.md)
- [Pensamiento: Razonamiento Interno y el Enfoque Re-Act](https://huggingface.co/learn/agents-course/es/unit1/thoughts.md)
- [Observar: Integrando Retroalimentación para Reflexionar y Adaptarse](https://huggingface.co/learn/agents-course/es/unit1/observations.md)
- [Introducción a los Agentes](https://huggingface.co/learn/agents-course/es/unit1/introduction.md)
- [Entendiendo los Agentes de IA a través del Ciclo Pensamiento-Acción-Observación](https://huggingface.co/learn/agents-course/es/unit1/agent-steps-and-structure.md)
- [P1: ¿Qué es un Agente?](https://huggingface.co/learn/agents-course/es/unit1/quiz1.md)
- [Biblioteca de Agente de Prueba](https://huggingface.co/learn/agents-course/es/unit1/dummy-agent-library.md)
- [Incorporación: Tus Primeros Pasos ⛵](https://huggingface.co/learn/agents-course/es/unit0/onboarding.md)
- [Bienvenido/a al curso de 🤗  Agentes IA [[introduction]]](https://huggingface.co/learn/agents-course/es/unit0/introduction.md)
- [(Opcional) Discord 101 [[discord-101]]](https://huggingface.co/learn/agents-course/es/unit0/discord101.md)

### Introducción a los Frameworks de Agentes
https://huggingface.co/learn/agents-course/es/unit2/introduction.md

# Introducción a los Frameworks de Agentes

Bienvenido/a a esta segunda unidad, donde **exploraremos diferentes frameworks de agentes** que pueden ser utilizados para construir poderosas aplicaciones basadas en agentes.

Estudiaremos:

- En la Unidad 2.1: [smolagents](https://huggingface.co/docs/smolagents/es/index)  
- En la Unidad 2.2: [LlamaIndex](https://www.llamaindex.ai/)
- En la Unidad 2.3: [LangGraph](https://www.langchain.com/langgraph)

¡Vamos a sumergirnos! 🕵

## Cuándo Usar un Framework de Agentes

Un framework de agentes **no siempre es necesario cuando se construye una aplicación basada en LLMs**. Proporcionan flexibilidad en el flujo de trabajo para resolver eficientemente una tarea específica, pero no siempre son necesarios.

A veces, **los flujos de trabajo predefinidos son suficientes** para satisfacer las solicitudes de los usuarios, y no hay una necesidad real de un framework de agentes. Si el enfoque para construir un agente es simple, como una cadena de prompts, usar código plano puede ser suficiente. La ventaja es que el desarrollador/a tendrá **control total y comprensión de su sistema sin abstracciones**.

Sin embargo, cuando el flujo de trabajo se vuelve más complejo, como permitir que un LLM llame a funciones o usar múltiples agentes, estas abstracciones comienzan a ser útiles.

Considerando estas ideas, ya podemos identificar la necesidad de algunas características:

* Un *motor LLM* que impulse el sistema.
* Una *lista de herramientas* a las que el agente puede acceder.  
* Un *analizador* para extraer llamadas a herramientas de la salida del LLM.
* Un *prompt de sistema* sincronizado con el analizador.
* Un *sistema de memoria*.
* *Registro de errores y mecanismos de reintento* para controlar los errores del LLM.
Exploraremos cómo se resuelven estos temas en varios frameworks, incluyendo `smolagents`, `LlamaIndex` y `LangGraph`.

## Unidades de Frameworks de Agentes

| Framework  | Descripción | Autor de la Unidad |
|------------|----------------|----------------|
| [smolagents](./smolagents/introducción) | Framework de agentes desarrollado por Hugging Face. | Sergio Paniego - [HF](https://huggingface.co/sergiopaniego) - [X](https://x.com/sergiopaniego) - [Linkedin](https://www.linkedin.com/in/sergio-paniego-blanco) |

### Autoevaluación Rápida (sin calificar) [[quiz2]]
https://huggingface.co/learn/agents-course/es/unit2/llama-index/quiz2.md

# Autoevaluación Rápida (sin calificar) [[quiz2]]

¿Qué?! Otra autoevaluación? Lo sabemos, lo sabemos, ... 😅 Pero esta breve autoevaluación no calificada está aquí  para **ayudarte a reforzar conceptos clave que acabas de aprender**.

Esta evaluación cubre flujos de trabajo de agentes y interacciones - componentes esenciales para construir agentes de IA efectivos.

### Q1: ¿Cuál es el propósito de AgentWorkflow en LlamaIndex?

---

### Q2: ¿Qué objeto se utiliza para mantener el estado del flujo de trabajo?

---

### Q3: ¿Qué  método debe utilizarse si deseas que un agente recuerde interacciones previas?

---

### Q4: ¿Qué  es una característica clave de Agentic RAG?

---
¿Entendido? Genial! Ahora hagamos un **resumen breve de la unidad!**

### Crear workflows agenticos en LlamaIndex
https://huggingface.co/learn/agents-course/es/unit2/llama-index/workflows.md

# Crear workflows agenticos en LlamaIndex

Un workflow en LlamaIndex proporciona una forma estructurada de organizar su código en pasos secuenciales y manejables.

Tal workflow se crea definiendo `Steps` que se activan por `Events`, y emiten `Events` para activar pasos posteriores.

Echemos un vistazo a Alfred mostrando un workflow de LlamaIndex para una tarea de RAG.

![Workflows esquematico](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/unit2/llama-index/workflows.png)

**Workflows ofrecen varios beneficios clave:**

- Organizacion clara de código en pasos discretos
- Arquitectura de control de flujo basada en eventos flexible
- Comunicación de tipado segura entre pasos
- Manejo de estado integrado
- Apoyo para agentes simples y complejos

Como podra haber adivinado, **workflows ofrecen un equilibrio entre la autonomía de los agentes mientras se mantiene el control sobre el workflow global.**

Asi que, vamos a aprender como crear un workflow nosotros mismos!

## Crear Workflows

> [!TIP]
> Puede seguir el código en esta notebook que puede ejecutar utilizando Google Colab.

### Crear un workflow básico

Instalar el paquete Workflow
Como se introdujo en la [sección sobre LlamaHub](llama-hub), podemos instalar el paquete Workflow con el siguiente comando:

```python
pip install llama-index-utils-workflow
```

Podemos crear un flujo de trabajo de un solo paso definiendo una clase que herede de `Workflow` y decorando tus funciones con `@step`.
También necesitaremos añadir `StartEvent` y `StopEvent`, que son eventos especiales que se utilizan para indicar el inicio y el fin del flujo de trabajo.

```python
from llama_index.core.workflow import StartEvent, StopEvent, Workflow, step

class MyWorkflow(Workflow):
    @step
    async def my_step(self, ev: StartEvent) -> StopEvent:
        # hacer algo aquí
        return StopEvent(result="¡Hola, mundo!")

w = MyWorkflow(timeout=10, verbose=False)
result = await w.run()
```

Como puedes ver, ahora podemos ejecutar el flujo de trabajo llamando a `w.run()`.

### Conectando Múltiples Pasos

Para conectar múltiples pasos, **creamos eventos personalizados que transportan datos entre pasos.**
Para hacerlo, necesitamos agregar un `Event` que se pasa entre los pasos y transfiere la salida del primer paso al segundo paso.

```python
from llama_index.core.workflow import Event

class ProcessingEvent(Event):
    intermediate_result: str

class MultiStepWorkflow(Workflow):
    @step
    async def step_one(self, ev: StartEvent) -> ProcessingEvent:
        # Procesar datos iniciales
        return ProcessingEvent(intermediate_result="Paso 1 completado")

    @step
    async def step_two(self, ev: ProcessingEvent) -> StopEvent:
        # Usar el resultado intermedio
        final_result = f"Procesamiento finalizado: {ev.intermediate_result}"
        return StopEvent(result=final_result)

w = MultiStepWorkflow(timeout=10, verbose=False)
result = await w.run()
result
```

La indicación de tipo es importante aquí, ya que asegura que el flujo de trabajo se ejecute correctamente. ¡Vamos a complicar un poco más las cosas!

### Bucles y Ramificaciones

La indicación de tipo es la parte más poderosa de los flujos de trabajo porque nos permite crear ramificaciones, bucles y uniones para facilitar flujos de trabajo más complejos.

Veamos un ejemplo de **creación de un bucle** usando el operador de unión `|`.
En el ejemplo siguiente, vemos que el `LoopEvent` se toma como entrada para el paso y también puede devolverse como salida.

```python
from llama_index.core.workflow import Event
import random

class ProcessingEvent(Event):
    intermediate_result: str

class LoopEvent(Event):
    loop_output: str

class MultiStepWorkflow(Workflow):
    @step
    async def step_one(self, ev: StartEvent) -> ProcessingEvent | LoopEvent:
        if random.randint(0, 1) == 0:
            print("Ocurrió algo malo")
            return LoopEvent(loop_output="Volver al paso uno.")
        else:
            print("Ocurrió algo bueno")
            return ProcessingEvent(intermediate_result="Primer paso completado.")

    @step
    async def step_two(self, ev: ProcessingEvent | LoopEvent) -> StopEvent:
        # Usar el resultado intermedio
        final_result = f"Procesamiento finalizado: {ev.intermediate_result}"
        return StopEvent(result=final_result)

w = MultiStepWorkflow(verbose=False)
result = await w.run()
result
```

### Dibujando Flujos de Trabajo

También podemos dibujar flujos de trabajo. Usemos la función `draw_all_possible_flows` para dibujar el flujo de trabajo. Esto almacena el flujo de trabajo en un archivo HTML.

```python
from llama_index.utils.workflow import draw_all_possible_flows

w = ... # como se definió en la sección anterior
draw_all_possible_flows(w, "flow.html")
```

![dibujo del flujo de trabajo](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/unit2/llama-index/workflow-draw.png)

Hay un último truco interesante que cubriremos en el curso, que es la capacidad de añadir estado al flujo de trabajo.

### Gestión de Estado

La gestión de estado es útil cuando quieres hacer un seguimiento del estado del flujo de trabajo, para que cada paso tenga acceso al mismo estado.
Podemos hacer esto usando la pista de tipo `Context` encima de un parámetro en la función del paso.

```python
from llama_index.core.workflow import Context, StartEvent, StopEvent

@step
async def query(self, ctx: Context, ev: StartEvent) -> StopEvent:
    # almacenar en contexto
    await ctx.store.set("query", "¿Cuál es la capital de Francia?")

    # hacer algo con el contexto y el evento
    val = ...

    # recuperar del contexto
    query = await ctx.store.get("query")

    return StopEvent(result=result)
```

¡Genial! ¡Ahora sabes cómo crear flujos de trabajo básicos en LlamaIndex!

> [!TIP]
> Hay algunos matices más complejos en los flujos de trabajo, que puedes aprender en la documentación de LlamaIndex.

Sin embargo, hay otra forma de crear flujos de trabajo, que se basa en la clase `AgentWorkflow`. Veamos cómo podemos usar esto para crear un flujo de trabajo multiagente.

## Automatizando flujos de trabajo con Flujos de Trabajo Multiagente

En lugar de la creación manual de flujos de trabajo, podemos usar la clase **`AgentWorkflow` para crear un flujo de trabajo multiagente**.
El `AgentWorkflow` utiliza Agentes de Flujo de Trabajo para permitirte crear un sistema de uno o más agentes que pueden colaborar y transferir tareas entre sí según sus capacidades especializadas.
Esto permite construir sistemas de agentes complejos donde diferentes agentes manejan diferentes aspectos de una tarea.
En lugar de importar clases de `llama_index.core.agent`, importaremos las clases de agente de `llama_index.core.agent.workflow`.
Un agente debe ser designado como el agente raíz en el constructor de `AgentWorkflow`.
Cuando llega un mensaje de usuario, primero se dirige al agente raíz.

Cada agente puede entonces:

- Manejar la solicitud directamente usando sus herramientas
- Transferir a otro agente mejor preparado para la tarea
- Devolver una respuesta al usuario

Veamos cómo crear un flujo de trabajo multiagente.

```python
from llama_index.core.agent.workflow import AgentWorkflow, ReActAgent
from llama_index.llms.huggingface_api import HuggingFaceInferenceAPI

# Definir algunas herramientas
def add(a: int, b: int) -> int:
    """Sumar dos números."""
    return a + b

def multiply(a: int, b: int) -> int:
    """Multiplicar dos números."""
    return a * b

llm = HuggingFaceInferenceAPI(model_name="Qwen/Qwen2.5-Coder-32B-Instruct")

# podemos pasar funciones directamente sin FunctionTool -- la función/docstring se analizan para el nombre/descripción
multiply_agent = ReActAgent(
    name="multiply_agent",
    description="Es capaz de multiplicar dos enteros",
    system_prompt="Un asistente útil que puede usar una herramienta para multiplicar números.",
    tools=[multiply],
    llm=llm,
)

addition_agent = ReActAgent(
    name="add_agent",
    description="Es capaz de sumar dos enteros",
    system_prompt="Un asistente útil que puede usar una herramienta para sumar números.",
    tools=[add],
    llm=llm,
)

# Crear el flujo de trabajo
workflow = AgentWorkflow(
    agents=[multiply_agent, addition_agent],
    root_agent="multiply_agent",
)

# Ejecutar el sistema
response = await workflow.run(user_msg="¿Puedes sumar 5 y 3?")
```

Las herramientas del agente también pueden modificar el estado del flujo de trabajo que mencionamos anteriormente. Antes de iniciar el flujo de trabajo, podemos proporcionar un diccionario de estado inicial que estará disponible para todos los agentes.
El estado se almacena en la clave state del contexto del flujo de trabajo. Se inyectará en el state_prompt que aumenta cada nuevo mensaje de usuario.

Vamos a inyectar un contador para contar las llamadas a funciones modificando el ejemplo anterior:

```python
from llama_index.core.workflow import Context

# Definir algunas herramientas
async def add(ctx: Context, a: int, b: int) -> int:
    """Sumar dos números."""
    # actualizar nuestro contador
    cur_state = await ctx.store.get("state")
    cur_state["num_fn_calls"] += 1
    await ctx.store.set("state", cur_state)

    return a + b

async def multiply(ctx: Context, a: int, b: int) -> int:
    """Multiplicar dos números."""
    # actualizar nuestro contador
    cur_state = await ctx.store.get("state")
    cur_state["num_fn_calls"] += 1
    await ctx.store.set("state", cur_state)

    return a * b

...

workflow = AgentWorkflow(
    agents=[multiply_agent, addition_agent],
    root_agent="multiply_agent"
    initial_state={"num_fn_calls": 0},
    state_prompt="Estado actual: {state}. Mensaje del usuario: {msg}",
)

# ejecutar el flujo de trabajo con contexto
ctx = Context(workflow)
response = await workflow.run(user_msg="¿Puedes sumar 5 y 3?", ctx=ctx)

# extraer e inspeccionar el estado
state = await ctx.store.get("state")
print(state["num_fn_calls"])
```

¡Felicidades! ¡Ahora has dominado los conceptos básicos de los Agentes en LlamaIndex! 🎉

¡Continuemos con un último cuestionario para consolidar tu conocimiento! 🚀

### Conclusión
https://huggingface.co/learn/agents-course/es/unit2/llama-index/conclusion.md

# Conclusión

!Felicidades por terminar el módulo `llama-index` de esta segunda Unidad 🥳

Acabas de dominar los fundamentos de `llama-index` y has visto como construir tus propias flujos de trabajo agentivos!
Ahora que tienes habilidades en `llama-index`, puedes empezar a crear motores de búsqueda que resolveran tareas que te interesen.

En el próximo módulo de la Unidad, aprenderas **cómo construir Agentes con LangGraph**.

Finalmente, nos encantar a **saber que te parece el curso y como podemos mejorar**.
Si tienes alguna retroalimentación, por favor 👉 llena este formulario: [https://docs.google.com/forms/d/e/1FAIpQLSe9VaONn0eglax0uTwi29rIn4tM7H2sYmmybmG5jJNlE5v0xA/viewform?usp=dialog](https://docs.google.com/forms/d/e/1FAIpQLSe9VaONn0eglax0uTwi29rIn4tM7H2sYmmybmG5jJNlE5v0xA/viewform?usp=dialog)

### Sigue aprendiendo, y sigue siendo increible 🤗

### Uso de Herramientas en LlamaIndex
https://huggingface.co/learn/agents-course/es/unit2/llama-index/tools.md

# Uso de Herramientas en LlamaIndex

**Definir un conjunto claro de herramientas es crucial para el rendimiento.** Como discutimos en [unidad 1](../../unit1/tools), las interfaces de herramientas claras son más fáciles de usar para los LLM.
Al igual que una interfaz de API para ingenieros humanos, pueden obtener m s de la herramienta si es f cil de entender c mo funciona.

Hay **cuatro tipos principales de herramientas en LlamaIndex**:

![Herramientas](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/unit2/llama-index/tools.png)

1. `FunctionTool`: Convierte cualquier función de Python en una herramienta que un agente puede utilizar. Averigua autom ticamente cómo funciona.
2. `QueryEngineTool`: Una herramienta que permite a los agentes utilizar motores de consulta. Dado que los agentes est n construidos sobre motores de consulta, también pueden utilizar a otros agentes como herramientas.
3. `Toolspecs`: Conjuntos de herramientas creados por la comunidad, que a menudo incluyen herramientas para servicios específicos como Gmail.
4. `Utility Tools`: Herramientas especiales que ayudan a manejar grandes cantidades de datos de otras herramientas.

Vamos a ver cada una de ellas en más detalle a continuación.

## Crear una FunctionTool

> [!TIP]
> Puedes seguir el código en este cuaderno que puedes ejecutar utilizando Google Colab.

Una FunctionTool proporciona una forma sencilla de envolver cualquier función de Python y hacerla disponible para un agente.
Puedes pasar tanto una función sincrónica como asincrona a la herramienta, junto con par metros opcionales `name` y `description`.
El nombre y la descripción son particularmente importantes, ya que ayudan al agente a entender cuando y cómo utilizar la herramienta de manera efectiva.
Vamos a ver cómo crear una FunctionTool a continuación y luego llamarla.

```python
from llama_index.core.tools import FunctionTool

def get_weather(location: str) -> str:
    """Útil para obtener el clima para una ubicación determinada."""
    print(f"Obtener clima de {location}")
    return f"El clima en {location} es soleado"

tool = FunctionTool.from_defaults(
    get_weather,
    name="my_weather_tool",
    description="Útil para obtener el clima para una ubicación determinada.",
)
tool.call("Nuevo York")
```

> [!TIP]
> Cuando se utiliza un agente o LLM con llamadas a funciones, la herramienta seleccionada (y los argumentos escritos para esa herramienta) dependen en gran medida del nombre de la herramienta y la descripción del proposito y argumentos de la herramienta. Aprende más sobre la llamada a funciones en la Guía de llamada a funciones.

## Creando un QueryEngineTool

The `QueryEngine` que definimos en la unidad anterior puede ser facilmente transformado en una herramienta usando la clase `QueryEngineTool`.
Vamos a ver como crear un `QueryEngineTool` desde un `QueryEngine` en el ejemplo siguiente.

```python
from llama_index.core import VectorStoreIndex
from llama_index.core.tools import QueryEngineTool
from llama_index.llms.huggingface_api import HuggingFaceInferenceAPI
from llama_index.embeddings.huggingface_api import HuggingFaceInferenceAPIEmbedding
from llama_index.vector_stores.chroma import ChromaVectorStore

embed_model = HuggingFaceInferenceAPIEmbedding("BAAI/bge-small-en-v1.5")

db = chromadb.PersistentClient(path="./alfred_chroma_db")
chroma_collection = db.get_or_create_collection("alfred")
vector_store = ChromaVectorStore(chroma_collection=chroma_collection)

index = VectorStoreIndex.from_vector_store(vector_store, embed_model=embed_model)

llm = HuggingFaceInferenceAPI(model_name="Qwen/Qwen2.5-Coder-32B-Instruct")
query_engine = index.as_query_engine(llm=llm)
tool = QueryEngineTool.from_defaults(query_engine, name="some useful name", description="some useful description")
```

## Crear Herramientas Especificas

Piensa en `ToolSpecs` como colecciones de herramientas que trabajan juntas armoniosamente - como una caja de herramientas bien organizada de un profesional.
Al igual que una caja de herramientas de un mecanico contiene herramientas complementarias que trabajan juntas para reparaciones de vehiculos, un `ToolSpec` combina herramientas relacionadas para un proposito especifico.
Por ejemplo, la herramienta especifica de un agente contable podria combinar elegante mente capacidades de hoja de calculo, funcionalidad de correo electronico y herramientas de calculo para manejar tareas financieras con precision y eficiencia.

Instalar la herramienta especifica de Google
Como se introdujo en la [seccion sobre LlamaHub](llama-hub), podemos instalar la herramienta especifica de Google con el siguiente comando:
```python
pip install llama-index-tools-google
```

Y ahora podemos cargar la herramienta especifica y convertirla en una lista de herramientas.

```python
from llama_index.tools.google import GmailToolSpec

tool_spec = GmailToolSpec()
tool_spec_list = tool_spec.to_tool_list()

Para obtener una visión más detallada de las herramientas, podemos examinar los `metadata` de cada una de ellas.

```python
[(tool.metadata.name, tool.metadata.description) for tool in tool_spec_list]
```

## Herramientas de utilidad

A menudo, realizar consultas a una API **puede devolver una cantidad excesiva de datos**, algunos de los cuales pueden ser irrelevantes, desbordar la ventana de contexto del LLM o aumentar innecesariamente el número de tokens que se están utilizando.
Vamos a revisar nuestras dos principales herramientas de utilidad a continuaci n.

1. `OnDemandToolLoader`: Esta herramienta convierte cualquier cargador de datos existente de LlamaIndex (clase BaseReader) en una herramienta que un agente puede utilizar. La herramienta se puede llamar con todos los parámetros necesarios para desencadenar `load_data` del cargador de datos, junto con una cadena de consulta de lenguaje natural. Durante la ejecución, primero cargamos datos del cargador de datos, los indexamos (por ejemplo, con un vector store) y luego los consultamos. Todos estos tres pasos se realizan en una sola llamada a la herramienta.
2. `LoadAndSearchToolSpec`: El LoadAndSearchToolSpec toma cualquier herramienta existente como entrada. Como herramienta específicas, implementa `to_tool_list`, y cuando se llama a esa función, se devuelven dos herramientas: una herramienta de carga y una herramienta de búsqueda. La ejecución de la herramienta de carga llamar  a la herramienta subyacente, y luego indexar  la salida (por defecto con un vector index). La ejecución de la herramienta de búsqueda tomar  una cadena de consulta como entrada y llamar  al vector index subyacente.

> [!TIP]
> Puedes encontrar herramientas y herramientas de utilidad en LlamaHub

Ahora que entendemos los conceptos básicos de agentes y herramientas en LlamaIndex, veamos cómo podemos **usar LlamaIndex para crear flujos de trabajo configurables y manejables!**

### Índice de Contenidos
https://huggingface.co/learn/agents-course/es/unit2/llama-index/README.md

# Índice de Contenidos

Este marco de trabajo de LlamaIndex es parte de la unidad 2 del curso. Puedes acceder a la unidad 2 sobre LlamaIndex en hf.co/learn  aquí

| Título | Descripción |
| --- | --- |
| [Introducción](introduction.mdx) | Introducción a LlamaIndex |
| [LlamaHub](llama-hub.mdx) | LlamaHub: un registro de integraciones, agentes y herramientas |
| [Componentes](components.mdx) | Componentes: los bloques de construcción de workflows |
| [Herramientas](tools.mdx) | Herramientas: cómo construir herramientas en LlamaIndex |
| [Cuestionario 1](quiz1.mdx) | Cuestionario 1 |
| [Agentes](agents.mdx) | Agentes: cómo construir agentes en LlamaIndex |
| [Flujos de Trabajo](workflows.mdx) | Flujos de Trabajo: una secuencia de pasos, eventos compuestos por componentes que se ejecutan en orden |
| [Cuestionario 2](quiz2.mdx) | Cuestionario 2 |
| [Conclusión](conclusion.mdx) | Conclusión |

### Usando Agentes en LlamaIndex
https://huggingface.co/learn/agents-course/es/unit2/llama-index/agents.md

# Usando Agentes en LlamaIndex

¿Recuerdas a Alfred, nuestro agente mayordomo útil de antes? ¡Bueno, está a punto de recibir una mejora! Ahora que entendemos las herramientas disponibles en LlamaIndex, podemos darle a Alfred nuevas capacidades para servirnos mejor.

Pero antes de continuar, recordemos qué hace funcionar a un agente como Alfred. En la Unidad 1, aprendimos que:

> Un Agente es un sistema que aprovecha un modelo de IA para interactuar con su entorno y lograr un objetivo definido por el usuario. Combina razonamiento, planificación y ejecución de acciones (a menudo a través de herramientas externas) para cumplir tareas.

LlamaIndex soporta **tres tipos principales de agentes de razonamiento**:

![Agents](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/unit2/llama-index/agents.png)
1. `Agentes de Llamada de Funciones` - Estos trabajan con modelos de IA que pueden llamar funciones específicas.
2. `Agentes ReAct` - Estos pueden trabajar con cualquier IA que tenga un endpoint de chat o texto y manejar tareas de razonamiento complejas.
3. `Agentes Personalizados Avanzados` - Estos usan métodos más complejos para manejar tareas y flujos de trabajo.

> [!TIP]
> FEncuentra más información sobre agentes avanzados en BaseWorkflowAgent

## Inicializando Agentes

> [!TIP]
> Puedes seguir el código en este notebook que puedes ejecutar usando Google Colab.

Para crear un agente, comenzamos proporcionándole un **conjunto de funciones/herramientas que definen sus capacidades**. Veamos cómo crear un agente con algunas herramientas básicas. Al momento de escribir esto, el agente usará automáticamente la API de llamada de funciones (si está disponible), o un bucle de agente ReAct estándar.

Los LLMs que soportan una API de herramientas/funciones son relativamente nuevos, pero proporcionan una forma poderosa de llamar herramientas al evitar indicaciones específicas y permitir que el LLM cree llamadas a herramientas basadas en esquemas proporcionados.

Los agentes ReAct también son buenos en tareas de razonamiento complejas y pueden trabajar con cualquier LLM que tenga capacidades de chat o completación de texto. Son más verbosos y muestran el razonamiento detrás de ciertas acciones que toman.

```python
from llama_index.llms.huggingface_api import HuggingFaceInferenceAPI
from llama_index.core.agent.workflow import AgentWorkflow
from llama_index.core.tools import FunctionTool

# define una herramienta de muestra -- ¡las anotaciones de tipo, nombres de funciones y docstrings están incluidos en los esquemas analizados!
def multiply(a: int, b: int) -> int:
    """Multiplica dos enteros y devuelve el entero resultante"""
    return a * b

# inicializa el llm
llm = HuggingFaceInferenceAPI(model_name="Qwen/Qwen2.5-Coder-32B-Instruct")

# inicializa el agente
agent = AgentWorkflow.from_tools_or_functions(
    [FunctionTool.from_defaults(multiply)],
    llm=llm
)
```

**Los agentes son sin estado por defecto**, recordar interacciones pasadas es opcional usando un objeto `Context`. Esto puede ser útil si quieres usar un agente que necesita recordar interacciones previas, como un chatbot que mantiene el contexto a través de múltiples mensajes o un administrador de tareas que necesita rastrear el progreso a lo largo del tiempo.

```python
# sin estado
response = await agent.run("¿Cuánto es 2 por 2?")

# recordando estado
from llama_index.core.workflow import Context

ctx = Context(agent)

response = await agent.run("Mi nombre es Bob.", ctx=ctx)
response = await agent.run("¿Cuál era mi nombre de nuevo?", ctx=ctx)
```

Notarás que los agentes en `LlamaIndex` son asíncronos porque usan el operador await de Python. Si eres nuevo en el código asíncrono en Python, o necesitas un repaso, tienen una excelente [guía de async](https://docs.llamaindex.ai/en/stable/getting_started/async_python/).

Ahora que tenemos los conceptos básicos, echemos un vistazo a como podemos usar herramientas más complejas en nuestros agentes.

## Creando Agentes RAG con QueryEngineTools

**Agentic RAG es una forma poderosa de usar agentes para responder preguntas sobre tus datos**. Podemos pasar varias herramientas a Alfred para ayudarlo a responder preguntas. Sin embargo, en lugar de responder la pregunta sobre los documentos automáticamente, Alfred puede decidir usar cualquier otra herramienta o flujo para responder la pregunta.

![Agentic RAG](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/unit2/llama-index/agentic-rag.png)

Es fácil **envolver `QueryEngine` como una herramienta** para un agente. 
Al hacerlo, necesitamos **definir un nombre y una descripción**. El LLM usará esta información para usar la herramienta correctamente.
Veamos cómo cargar un `QueryEngineTool` usando el QueryEngine que creamos en la [sección de componentes](02_components).

It is easy to **wrap `QueryEngine` as a tool** for an agent.
When doing so, we need to **define a name and description**. The LLM will use this information to correctly use the tool.
Let's see how to load in a `QueryEngineTool` using the `QueryEngine` we created in the [component section](02_components).

```python
from llama_index.core.tools import QueryEngineTool

query_engine = index.as_query_engine(llm=llm, similarity_top_k=3) # como se mostró en la sección anterior

query_engine_tool = QueryEngineTool.from_defaults(
    query_engine=query_engine,
    name="nombre",
    description="una descripción específica",
    return_direct=False,
)
query_engine_agent = AgentWorkflow.from_tools_or_functions(
    [query_engine_tool],
    llm=llm,
    system_prompt="Eres un asistente útil que tiene acceso a una base de datos que contiene descripciones de personas. "
)
```

## Creando Sistemas Multi-agente

La clase `AgentWorkflow` también soporta directamente sistemas multi-agente. Al darle a cada agente un nombre y una descripción, el sistema mantiene un único hablante activo, con cada agente teniendo la capacidad de pasar el control a otro agente.

Al reducir el alcance de cada agente, podemos ayudar a aumentar su precisión general al responder a los mensajes del usuario.

Los agentes en LlamaIndex también pueden usarse directamente como herramientas para otros agentes, para escenarios más complejos y personalizados.

```python
from llama_index.core.agent.workflow import (
    AgentWorkflow,
    FunctionAgent,
    ReActAgent,
)

# Define algunas herramientas
def add(a: int, b: int) -> int:
    """Suma dos números."""
    return a + b

def subtract(a: int, b: int) -> int:
    """Resta dos números."""
    return a - b

# Crea configuraciones de agentes
# NOTA: podemos usar FunctionAgent o ReActAgent aquí.
# FunctionAgent funciona para LLMs con una API de llamada de funciones.
# ReActAgent funciona para cualquier LLM.
calculator_agent = ReActAgent(
    name="calculadora",
    description="Realiza operaciones aritméticas básicas",
    system_prompt="Eres un asistente de calculadora. Usa tus herramientas para cualquier operación matemática.",
    tools=[add, subtract],
    llm=llm,
)

query_agent = ReActAgent(
    name="búsqueda_de_información",
    description="Busca información sobre XYZ",
    system_prompt="Usa tu herramienta para consultar un sistema RAG y responder información sobre XYZ",
    tools=[query_engine_tool],
    llm=llm
)

# Crea y ejecuta el flujo de trabajo
agent = AgentWorkflow(
    agents=[calculator_agent, query_agent], root_agent="calculadora"
)

# Ejecuta el sistema
response = await agent.run(user_msg="¿Puedes sumar 5 y 3?")
```

> [!TIP]
> ¿No has aprendido lo suficiente todavía? Hay mucho más por descubrir sobre agentes y herramientas en LlamaIndex dentro de la   Introducción Básica a AgentWorkflow  o la Guía de Aprendizaje de Agentes, ¡donde puedes leer más sobre streaming, serialización de contexto y humano-en-el-bucle!

Ahora que entendemos los conceptos básicos de agentes y herramientas en LlamaIndex, ¡veamos cómo podemos usar LlamaIndex para **crear flujos de trabajo configurables y manejables!**

### Introducción a LlamaIndex
https://huggingface.co/learn/agents-course/es/unit2/llama-index/introduction.md

# Introducción a LlamaIndex

Bienvenido a este módulo, donde aprenderás a construir agentes impulsados por LLM utilizando el kit de herramientas de [LlamaIndex](https://www.llamaindex.ai/).

LlamaIndex es **un kit de herramientas completo para crear agentes impulsados por LLM sobre tus datos utilizando índices y flujos de trabajo**. En este curso, nos centraremos en tres partes principales que ayudan a construir agentes en LlamaIndex: **Componentes**, **Herramientas y Agentes** y **Flujos de trabajo**.

![LlamaIndex](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/unit2/llama-index/thumbnail.png)

Echemos un vistazo a estas partes clave de LlamaIndex y cómo ayudan a construir agentes:

- **Componentes** son los bloques de construcción básicos que se utilizan en LlamaIndex. Estos incluyen cosas como prompts, modelos y bases de datos. Los componentes suelen ayudar a conectar LlamaIndex con otras herramientas y bibliotecas.
- **Herramientas**: Las herramientas son componentes que proporcionan capacidades específicas como búsqueda, cálculo o acceso a servicios externos. Son los bloques de construcción que permiten a los agentes realizar tareas.
- **Agentes**: Los agentes son componentes autónomos que pueden utilizar herramientas y tomar decisiones. Coordinan el uso de herramientas para lograr objetivos complejos.
- **Flujos de trabajo** son procesos paso a paso que procesan lógica juntos. Los flujos de trabajo o flujos de trabajo agentivos son una forma de estructurar el comportamiento agente sin el uso explícito de agentes.

## ¿Qué hace que LlamaIndex sea especial?

Aunque LlamaIndex hace algunas cosas similares a otros frameworks como smolagents, tiene algunas ventajas clave:

- **Sistema de flujo de trabajo claro**. Los flujos de trabajo ayudan a descomponer cómo los agentes deben tomar decisiones paso a paso utilizando una sintaxis de eventos y async-first. Esto te ayuda a organizar y componer tu lógica de manera clara.
- **Análisis de documentos avanzado con LlamaParse** LlamaParse se creó específicamente para LlamaIndex, por lo que la integración es perfecta, aunque es una característica de pago.
- **Muchos componentes listos para usar** LlamaIndex ha estado disponible durante un tiempo, por lo que funciona con muchos otros frameworks. Esto significa que tiene muchos componentes probados y confiables, como LLMs, recuperadores, índices y más.
- **LlamaHub** es un registro de cientos de estos componentes, agentes y herramientas que puedes utilizar dentro de LlamaIndex.

Todos estos conceptos son necesarios en diferentes escenarios para crear agentes útiles.
En las siguientes secciones, exploraremos cada uno de estos conceptos en detalle.
Después de dominar los conceptos, utilizaremos lo que hemos aprendido para **crear casos de uso aplicados con Alfred el agente**!

¿Estás emocionado de empezar a trabajar con LlamaIndex? ¡Vamos a empezar a **buscar e instalar las integraciones que necesitamos utilizando LlamaHub! 🚀**

### Pequeño Quiz (no calificado) [[quiz1]]
https://huggingface.co/learn/agents-course/es/unit2/llama-index/quiz1.md

# Pequeño Quiz (no calificado) [[quiz1]]

Hasta ahora hemos discutido los componentes clave y herramientas utilizadas en LlamaIndex.
Es hora de hacer un pequeño quiz, ya que **probarse a sí mismo** es la mejor manera de aprender y [evitar la ilusi n de competencia](https://www.coursera.org/lecture/learning-how-to-learn/illusions-of-competence-BuFzf).
Esto te ayudar  a encontrar **dónde debes reforzar tus conocimientos**.

Este es un quiz opcional y no está calificado.

### Q1:   ¿Qué  es un QueryEngine?
   ¿Cuál de las siguientes opciones describe mejor un componente QueryEngine?

---

### Q2:   ¿Cuál  es el propósito de FunctionTools?
   ¿Por qué  son importantes FunctionTools para un Agente?

---

### Q3:   ¿Qué  son los Toolspecs en LlamaIndex?
   ¿Cuál es el propósito principal de los Toolspecs?

---

### Q4:   ¿Qué se requiere para crear una herramienta?
   ¿Qué información debe incluirse al crear una herramienta?

---

!Felicitaciones por completar este Quiz 🥳!, si no entendiste algün elemento, tóoma el tiempo de releer el capítulo para reforzar tus conocimientos. Si lo pasaste, estás listo para profundizar en el uso de estos componentes!

### ¿Qué son los componentes en LlamaIndex?
https://huggingface.co/learn/agents-course/es/unit2/llama-index/components.md

# ¿Qué son los componentes en LlamaIndex?

¿Recuerdas a Alfred, nuestro útil agente mayordomo de la Unidad 1? 
Para ayudarnos de manera efectiva, Alfred necesita entender nuestras solicitudes y **preparar, encontrar y usar información relevante para ayudar a completar tareas.**
Aquí es donde entran los componentes de LlamaIndex.

Aunque LlamaIndex tiene muchos componentes, **nos centraremos específicamente en el componente `QueryEngine`**. ¿Por qué? Porque se puede usar como una herramienta de Generación Aumentada por Recuperación (RAG) para un agente.

Entonces, ¿qué es RAG? Los LLMs están entrenados en enormes cuerpos de datos para aprender conocimiento general. Sin embargo, pueden no estar entrenados en datos relevantes y actualizados.
RAG resuelve este problema encontrando y recuperando información relevante de tus datos y dándosela al LLM.

![RAG](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/unit2/llama-index/rag.png)

Ahora, piensa en como funciona Alfred:

1. Le pides a Alfred que te ayude a planear una cena
2. Alfred necesita revisar tu calendario, preferencias dietéticas y menús exitosos anteriores
3. El `QueryEngine` ayuda a Alfred a encontrar esta información y usarla para planear la cena

Esto hace que el `QueryEngine` **sea un componente clave para construir flujos de trabajo RAG agentic** en LlamaIndex.
Así como Alfred necesita buscar en la información de tu hogar para ser útil, cualquier agente necesita una forma de encontrar y entender datos relevantes.
El `QueryEngine` proporciona exactamente esta capacidad.

Ahora, profundicemos un poco más en los componentes y veamos como puedes **combinar componentes para crear un pipeline RAG.**

## Creando un pipeline RAG usando componentes

> [!TIP]
> Puedes seguir el código en este notebook que puedes ejecutar usando Google Colab.

Hay cinco etapas clave dentro de RAG, que a su vez serán parte de la mayoría de las aplicaciones más grandes que construyas. Estas son:

1. **Carga**: esto se refiere a obtener tus datos desde donde residen -- ya sean archivos de texto, PDFs, otro sitio web, una base de datos o una API -- e incorporarlos a tu flujo de trabajo. LlamaHub proporciona cientos de integraciones para elegir.
2. **Indexación**: significa crear una estructura de datos que permita consultar la información. Para los LLMs, esto casi siempre significa crear embeddings vectoriales, que son representaciones numéricas del significado de los datos. La indexación también puede referirse a numerosas estrategias de metadatos para facilitar la búsqueda precisa de datos contextualmente relevantes basados en propiedades.
3. **Almacenamiento**: una vez que tus datos están indexados, querrás almacenar tu índice, así como otros metadatos, para evitar tener que volver a indexarlos.
4. **Consulta**: para cualquier estrategia de indexación hay muchas formas en que puedes utilizar LLMs y estructuras de datos de LlamaIndex para consultar, incluyendo subconsultas, consultas de múltiples pasos y estrategias híbridas.
5. **Evaluación**: un paso crítico en cualquier flujo es comprobar qué tan efectivo es en relación con otras estrategias, o cuando realizas cambios. La evaluación proporciona medidas objetivas de cuán precisas, fieles y rápidas son tus respuestas a las consultas.

A continuación, veamos cómo podemos reproducir estas etapas usando componentes.

### Carga y embedding de documentos

Como se mencionó anteriormente, LlamaIndex puede trabajar sobre tus propios datos, sin embargo, **antes de acceder a los datos, necesitamos cargarlos.**
Hay tres formas principales de cargar datos en LlamaIndex:

1. `SimpleDirectoryReader`: Un cargador integrado para varios tipos de archivos desde un directorio local.
2. `LlamaParse`: LlamaParse, la herramienta oficial de LlamaIndex para el análisis de PDFs, disponible como API gestionada.
3. `LlamaHub`: Un registro de cientos de bibliotecas de carga de datos para ingerir datos desde cualquier fuente.

> [!TIP]
> Familiarízate con los cargadores de LlamaHub y el analizador LlamaParse para fuentes de datos más complejas.

**La forma más sencilla de cargar datos es con `SimpleDirectoryReader`.**
Este componente versátil puede cargar varios tipos de archivos desde una carpeta y convertirlos en objetos `Document` con los que LlamaIndex puede trabajar.
Veamos cómo podemos usar `SimpleDirectoryReader` para cargar datos desde una carpeta.

```python
from llama_index.core import SimpleDirectoryReader

reader = SimpleDirectoryReader(input_dir="path/to/directory")
documents = reader.load_data()
```

Después de cargar nuestros documentos, necesitamos dividirlos en piezas más pequeñas llamadas objetos `Node`.
Un `Node` es simplemente un fragmento de texto del documento original que es más fácil de manejar para la IA, mientras que mantiene referencias al objeto `Document` original.

El `IngestionPipeline` nos ayuda a crear estos nodos a través de dos transformaciones clave.
1. `SentenceSplitter` descompone los documentos en fragmentos manejables dividiéndolos en los límites naturales de las oraciones.
2. `HuggingFaceInferenceAPIEmbedding` convierte cada fragmento en embeddings numéricos - representaciones vectoriales que capturan el significado semántico de una manera que la IA puede procesar eficientemente.

Este proceso nos ayuda a organizar nuestros documentos de una manera que es más útil para la búsqueda y el análisis.

```python
from llama_index.core import Document
from llama_index.embeddings.huggingface_api import HuggingFaceInferenceAPIEmbedding
from llama_index.core.node_parser import SentenceSplitter
from llama_index.core.ingestion import IngestionPipeline

# crear el pipeline con transformaciones
pipeline = IngestionPipeline(
    transformations=[
        SentenceSplitter(chunk_overlap=0),
        HuggingFaceInferenceAPIEmbedding(model_name="BAAI/bge-small-en-v1.5"),
    ]
)

nodes = await pipeline.arun(documents=[Document.example()])
```

### Almacenamiento e indexación de documentos

Después de crear nuestros objetos `Node`, necesitamos indexarlos para hacerlos buscables, pero antes de poder hacerlo, necesitamos un lugar para almacenar nuestros datos.

Como estamos usando un pipeline de ingesta, podemos adjuntar directamente un almacén vectorial al pipeline para poblarlo.
En este caso, usaremos `Chroma` para almacenar nuestros documentos.

Instalar ChromaDB
Como se introdujo en la [sección sobre LlamaHub](llama-hub), podemos instalar el almacén vectorial ChromaDB con el siguiente comando:

```bash
pip install llama-index-vector-stores-chroma
```

```python
import chromadb
from llama_index.vector_stores.chroma import ChromaVectorStore

db = chromadb.PersistentClient(path="./alfred_chroma_db")
chroma_collection = db.get_or_create_collection("alfred")
vector_store = ChromaVectorStore(chroma_collection=chroma_collection)

pipeline = IngestionPipeline(
    transformations=[
        SentenceSplitter(chunk_size=25, chunk_overlap=0),
        HuggingFaceInferenceAPIEmbedding(model_name="BAAI/bge-small-en-v1.5"),
    ],
    vector_store=vector_store,
)
```

> [!TIP]
> Una visión general de los diferentes almacenes vectoriales se puede encontrar en la documentación de LlamaIndex.

Aquí es donde entran los embeddings vectoriales - al incorporar tanto la consulta como los nodos en el mismo espacio vectorial, podemos encontrar coincidencias relevantes.
El `VectorStoreIndex` se encarga de esto por nosotros, utilizando el mismo modelo de embedding que usamos durante la ingesta para garantizar la consistencia.

Veamos cómo crear este índice a partir de nuestro almacén vectorial y embeddings:

```python
from llama_index.core import VectorStoreIndex
from llama_index.embeddings.huggingface_api import HuggingFaceInferenceAPIEmbedding

embed_model = HuggingFaceInferenceAPIEmbedding(model_name="BAAI/bge-small-en-v1.5")
index = VectorStoreIndex.from_vector_store(vector_store, embed_model=embed_model)
```

Toda la información se persiste automáticamente dentro del objeto `ChromaVectorStore` y la ruta del directorio proporcionado.

¡Genial! Ahora que podemos guardar y cargar nuestro índice fácilmente, exploremos cómo consultarlo de diferentes maneras.

### Consultando un VectorStoreIndex con prompts y LLMs

Antes de poder consultar nuestro índice, necesitamos convertirlo a una interfaz de consulta. Las opciones de conversión más comunes son:

- `as_retriever`: Para recuperación básica de documentos, devolviendo una lista de objetos `NodeWithScore` con puntuaciones de similitud
- `as_query_engine`: Para interacciones de pregunta-respuesta simples, devolviendo una respuesta escrita
- `as_chat_engine`: Para interacciones conversacionales que mantienen la memoria a través de múltiples mensajes, devolviendo una respuesta escrita utilizando el historial de chat y el contexto indexado

Nos centraremos en el motor de consulta ya que es más común para interacciones tipo agente.
También pasamos un LLM al motor de consulta para usarlo en la respuesta.

```python
from llama_index.llms.huggingface_api import HuggingFaceInferenceAPI

llm = HuggingFaceInferenceAPI(model_name="Qwen/Qwen2.5-Coder-32B-Instruct")
query_engine = index.as_query_engine(
    llm=llm,
    response_mode="tree_summarize",
)
query_engine.query("¿Cuál es el significado de la vida?")
# El significado de la vida es 42
```

### Procesamiento de Respuestas

Bajo el capó, el motor de consulta no solo usa el LLM para responder a la pregunta, sino que también utiliza un `ResponseSynthesizer` como estrategia para procesar la respuesta.
Una vez más, esto es completamente personalizable, pero hay tres estrategias principales que funcionan bien de forma predeterminada:

- `refine`: crear y refinar una respuesta recorriendo secuencialmente cada fragmento de texto recuperado. Esto hace una llamada LLM separada por cada Node/fragmento recuperado.
- `compact` (predeterminado): similar a refinar pero concatenando los fragmentos de antemano, resultando en menos llamadas LLM.
- `tree_summarize`: crear una respuesta detallada recorriendo cada fragmento de texto recuperado y creando una estructura de árbol de la respuesta.

> [!TIP]
> Toma control detallado de tus flujos de consulta con la API de composición de bajo nivel. Esta API te permite personalizar y ajustar cada paso del proceso de consulta para que coincida con tus necesidades exactas, lo que también se combina muy bien con Flujos de trabajo

El modelo de lenguaje no siempre funcionará de manera predecible, por lo que no podemos estar seguros de que la respuesta que obtenemos sea siempre correcta. Podemos lidiar con esto **evaluando la calidad de la respuesta**.

### Evaluación y observabilidad

LlamaIndex proporciona **herramientas de evaluación integradas para evaluar la calidad de la respuesta.**
Estos evaluadores aprovechan los LLMs para analizar respuestas a través de diferentes dimensiones.
Veamos los tres evaluadores principales disponibles:

- `FaithfulnessEvaluator`: Evalúa la fidelidad de la respuesta comprobando si está respaldada por el contexto.
- `AnswerRelevancyEvaluator`: Evalúa la relevancia de la respuesta comprobando si es relevante para la pregunta.
- `CorrectnessEvaluator`: Evalúa la corrección de la respuesta comprobando si es correcta.

```python
from llama_index.core.evaluation import FaithfulnessEvaluator

query_engine = # de la sección anterior
llm = # de la sección anterior

# consultar índice
evaluator = FaithfulnessEvaluator(llm=llm)
response = query_engine.query(
    "¿Qué batallas tuvieron lugar en la ciudad de Nueva York durante la Revolución Americana?"
)
eval_result = evaluator.evaluate_response(response=response)
eval_result.passing
```

Incluso sin evaluación directa, podemos **obtener información sobre cómo está funcionando nuestro sistema a través de la observabilidad.**
Esto es especialmente útil cuando estamos construyendo flujos de trabajo más complejos y queremos entender cómo está funcionando cada componente.

Instalar LlamaTrace
Como se introdujo en la [sección sobre LlamaHub](llama-hub), podemos instalar el callback LlamaTrace de Arize Phoenix con el siguiente comando:

```bash
pip install -U llama-index-callbacks-arize-phoenix
```

Además, necesitamos establecer la variable de entorno `PHOENIX_API_KEY` con nuestra clave API de LlamaTrace. Podemos obtenerla:
- Creando una cuenta en [LlamaTrace](https://llamatrace.com/login)
- Generando una clave API en la configuración de tu cuenta
- Usando la clave API en el código siguiente para habilitar el seguimiento

```python
import llama_index
import os

PHOENIX_API_KEY = ""
os.environ["OTEL_EXPORTER_OTLP_HEADERS"] = f"api_key={PHOENIX_API_KEY}"
llama_index.core.set_global_handler(
    "arize_phoenix",
    endpoint="https://llamatrace.com/v1/traces"
)
```

> [!TIP]
> ¿Quieres aprender más sobre los componentes y cómo usarlos? Continúa tu viaje con las Guías de Componentes o la Guía sobre RAG.

Hemos visto cómo usar componentes para crear un `QueryEngine`. Ahora, ¡veamos cómo **usar el `QueryEngine` como una herramienta para un agente!**

### Introducción a LlamaHub
https://huggingface.co/learn/agents-course/es/unit2/llama-index/llama-hub.md

# Introducción a LlamaHub

**LlamaHub es un registro de cientos de integraciones, agentes y herramientas que puedes utilizar dentro de LlamaIndex.**

![LlamaHub](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/unit2/llama-index/llama-hub.png)

Vamos a utilizar varias integraciones en este curso, así que primero echaremos un vistazo a LlamaHub y veremos cómo puede ayudarnos.

Vamos a ver cómo encontrar e instalar las dependencias para los componentes que necesitamos.

## Instalación

Las instrucciones de instalación de LlamaIndex están disponibles en **[LlamaHub](https://llamahub.ai/)**.
Puede parecer un poco abrumador al principio, pero la mayoría de los comandos de **instalación siguen un formato fácil de recordar **:

```bash
pip install llama-index-{component-type}-{framework-name}
```
Vamos a intentar instalar las dependencias para un componente de LLM utilizando la [integración de la API de inferencia de Hugging Face](https://llamahub.ai/l/llms/llama-index-llms-huggingface-api?from=llms).

```bash
pip install llama-index-llms-huggingface-api
```

## Uso

Una vez instalado, podemos ver los patrones de uso. Notaras que los caminos de importación siguen el comando de instalación!
Debajo, podemos ver un ejemplo de la utilización de **la API de inferencia de Hugging Face para un componente LLM**.

```python
from llama_index.llms.huggingface_api import HuggingFaceInferenceAPI

llm = HuggingFaceInferenceAPI(
    model_name="Qwen/Qwen2.5-Coder-32B-Instruct",
    temperature=0.7,
    max_tokens=100,
    token="hf_xxx",
)

llm.completar(" Hola, cómo estás?")
# Estoy bien, ¿cómo puedo ayudarte hoy?
```

Genial, ahora sabemos como encontrar, instalar y utilizar las integraciones para los componentes que necesitamos.
**Vamos a profundizar en los componentes** y veremos como podemos utilizarlos para construir nuestros propios agentes.

### Sistemas Multi-Agente
https://huggingface.co/learn/agents-course/es/unit2/smolagents/multi_agent_systems.md

# Sistemas Multi-Agente

Los sistemas multi-agente permiten que **agentes especializados colaboren en tareas complejas**, mejorando la modularidad, escalabilidad y robustez. En lugar de depender de un solo agente, las tareas se distribuyen entre agentes con capacidades distintas.

En **smolagents**, diferentes agentes pueden combinarse para generar código Python, llamar a herramientas externas, realizar búsquedas web y más. Al orquestar estos agentes, podemos crear flujos de trabajo potentes.

Una configuración típica podría incluir:
- Un **Agente Gestor** para la delegación de tareas
- Un **Agente Intérprete de Código** para la ejecución de código
- Un **Agente de Búsqueda Web** para la recuperación de información

El diagrama a continuación ilustra una arquitectura multi-agente simple donde un **Agente Gestor** coordina una **Herramienta Intérprete de Código** y un **Agente de Búsqueda Web**, que a su vez utiliza herramientas como `DuckDuckGoSearchTool` y `VisitWebpageTool` para recopilar información relevante.

## Sistemas Multi-Agente en Acción

Un sistema multi-agente consiste en múltiples agentes especializados trabajando juntos bajo la coordinación de un **Agente Orquestador**. Este enfoque permite flujos de trabajo complejos distribuyendo tareas entre agentes con roles distintos.

Por ejemplo, un **sistema RAG Multi-Agente** puede integrar:
- Un **Agente Web** para navegar por internet.
- Un **Agente Recuperador** para obtener información de bases de conocimiento.
- Un **Agente de Generación de Imágenes** para producir elementos visuales.

Todos estos agentes operan bajo un orquestador que gestiona la delegación de tareas y la interacción.

## Resolviendo una tarea compleja con una jerarquía multi-agente

> [!TIP]
> Puedes seguir el código en este notebook que puedes ejecutar usando Google Colab.

¡La recepción se acerca! Con tu ayuda, Alfred ya casi ha terminado con los preparativos.

Pero ahora hay un problema: el Batmóvil ha desaparecido. Alfred necesita encontrar un reemplazo, y encontrarlo rápidamente.

Afortunadamente, se han realizado algunas biografías cinematográficas sobre la vida de Bruce Wayne, así que tal vez Alfred podría conseguir un automóvil abandonado en uno de los sets de filmación y rediseñarlo según los estándares modernos, lo que ciertamente incluiría una opción de conducción autónoma completa.

Pero esto podría estar en cualquier lugar de las locaciones de filmación alrededor del mundo, que podrían ser numerosas.

Así que Alfred quiere tu ayuda. ¿Podrías construir un agente capaz de resolver esta tarea?

> 👉 Encuentra todas las locaciones de filmación de Batman en el mundo, calcula el tiempo de transferencia en avión de carga hasta allí, y represéntalas en un mapa, con un color que varíe según el tiempo de transferencia en avión. También representa algunas fábricas de superdeportivos con el mismo tiempo de transferencia en avión.

¡Vamos a construir esto!

Este ejemplo necesita algunos paquetes adicionales, así que vamos a instalarlos primero:

```bash
pip install 'smolagents[litellm]' matplotlib geopandas shapely kaleido -q
```

### Primero creamos una herramienta para obtener el tiempo de transferencia del avión de carga.

```python
import math
from typing import Optional, Tuple

from smolagents import tool

@tool
def calculate_cargo_travel_time(
    origin_coords: Tuple[float, float],
    destination_coords: Tuple[float, float],
    cruising_speed_kmh: Optional[float] = 750.0,  # Average speed for cargo planes
) -> float:
    """
    Calculate the travel time for a cargo plane between two points on Earth using great-circle distance.

    Args:
        origin_coords: Tuple of (latitude, longitude) for the starting point
        destination_coords: Tuple of (latitude, longitude) for the destination
        cruising_speed_kmh: Optional cruising speed in km/h (defaults to 750 km/h for typical cargo planes)

    Returns:
        float: The estimated travel time in hours

    Example:
        >>> # Chicago (41.8781° N, 87.6298° W) to Sydney (33.8688° S, 151.2093° E)
        >>> result = calculate_cargo_travel_time((41.8781, -87.6298), (-33.8688, 151.2093))
    """

    def to_radians(degrees: float) -> float:
        return degrees * (math.pi / 180)

    # Extract coordinates
    lat1, lon1 = map(to_radians, origin_coords)
    lat2, lon2 = map(to_radians, destination_coords)

    # Earth's radius in kilometers
    EARTH_RADIUS_KM = 6371.0

    # Calculate great-circle distance using the haversine formula
    dlon = lon2 - lon1
    dlat = lat2 - lat1

    a = (
        math.sin(dlat / 2) ** 2
        + math.cos(lat1) * math.cos(lat2) * math.sin(dlon / 2) ** 2
    )
    c = 2 * math.asin(math.sqrt(a))
    distance = EARTH_RADIUS_KM * c

    # Add 10% to account for non-direct routes and air traffic controls
    actual_distance = distance * 1.1

    # Calculate flight time
    # Add 1 hour for takeoff and landing procedures
    flight_time = (actual_distance / cruising_speed_kmh) + 1.0

    # Format the results
    return round(flight_time, 2)

print(calculate_cargo_travel_time((41.8781, -87.6298), (-33.8688, 151.2093)))
```

### Configurando el agente

Para el proveedor de modelos, usamos Together AI, ¡uno de los nuevos [proveedores de inferencia en el Hub](https://huggingface.co/blog/inference-providers)!

La herramienta GoogleSearchTool usa la [API de Serper](https://serper.dev) para buscar en la web, por lo que requiere haber configurado la variable de entorno `SERPAPI_API_KEY` y pasar `provider="serpapi"` o tener `SERPER_API_KEY` y pasar `provider=serper`.

Si no tienes ningún proveedor de Serp API configurado, puedes usar `DuckDuckGoSearchTool` pero ten en cuenta que tiene un límite de tasa.

```python
import os
from PIL import Image
from smolagents import CodeAgent, GoogleSearchTool, InferenceClientModel, VisitWebpageTool

model = InferenceClientModel(model_id="Qwen/Qwen2.5-Coder-32B-Instruct", provider="together")
```

Podemos empezar creando un agente simple como base para darnos un informe simple.

```python
task = """Encuentra todas las locaciones de filmación de Batman en el mundo, calcula el tiempo de transferencia en avión de carga hasta aquí (estamos en Gotham, 40.7128° N, 74.0060° W), y devuélvelas a mí como un dataframe de pandas.
También dame algunas fábricas de superdeportivos con el mismo tiempo de transferencia en avión."""
```

```python
agent = CodeAgent(
    model=model,
    tools=[GoogleSearchTool("serper"), VisitWebpageTool(), calculate_cargo_travel_time],
    additional_authorized_imports=["pandas"],
    max_steps=20,
)
```

```python
result = agent.run(task)
```

```python
result
```

En nuestro caso, genera este output:

```python
|  | Location                                             | Travel Time to Gotham (hours) |
|--|------------------------------------------------------|------------------------------|
| 0  | Necropolis Cemetery, Glasgow, Scotland, UK         | 8.60                         |
| 1  | St. George's Hall, Liverpool, England, UK         | 8.81                         |
| 2  | Two Temple Place, London, England, UK             | 9.17                         |
| 3  | Wollaton Hall, Nottingham, England, UK           | 9.00                         |
| 4  | Knebworth House, Knebworth, Hertfordshire, UK    | 9.15                         |
| 5  | Acton Lane Power Station, Acton Lane, Acton, UK  | 9.16                         |
| 6  | Queensboro Bridge, New York City, USA            | 1.01                         |
| 7  | Wall Street, New York City, USA                  | 1.00                         |
| 8  | Mehrangarh Fort, Jodhpur, Rajasthan, India       | 18.34                        |
| 9  | Turda Gorge, Turda, Romania                      | 11.89                        |
| 10 | Chicago, USA                                     | 2.68                         |
| 11 | Hong Kong, China                                 | 19.99                        |
| 12 | Cardington Studios, Northamptonshire, UK        | 9.10                         |
| 13 | Warner Bros. Leavesden Studios, Hertfordshire, UK | 9.13                         |
| 14 | Westwood, Los Angeles, CA, USA                  | 6.79                         |
| 15 | Woking, UK (McLaren)                             | 9.13                         |
```

Podríamos mejorar esto un poco agregando algunos pasos de planificación y más instrucciones.

Los pasos de planificación permiten al agente pensar con anticipación y planificar sus próximos pasos, lo que puede ser útil para tareas más complejas.

```python
agent.planning_interval = 4

detailed_report = agent.run(f"""
Eres un analista experto. Creas informes exhaustivos después de visitar muchos sitios web.
No dudes en buscar muchas consultas a la vez en un bucle for.
Para cada dato que encuentres, visita la URL de origen para confirmar los números.

{task}
""")

print(detailed_report)
```

```python
detailed_report
```

En nuestro caso, genera este output:

```python
|  | Location                                         | Travel Time (hours) |
|--|--------------------------------------------------|---------------------|
| 0  | Bridge of Sighs, Glasgow Necropolis, Glasgow, UK | 8.6                 |
| 1  | Wishart Street, Glasgow, Scotland, UK         | 8.6                 |
```

Gracias a estos cambios rápidos, obtuvimos un informe mucho más conciso proporcionando simplemente una instrucción detallada a nuestro agente y dándole capacidades de planificación.

El contexto de la ventana del modelo se está llenando rápidamente. Así que **si le pedimos a nuestro agente que combine los resultados de una búsqueda detallada con otra, será más lento y rápidamente aumentará los tokens y los costos**.

➡️ Necesitamos mejorar la estructura de nuestro sistema.

### ✌️ Dividiendo la tarea entre dos agentes

Las estructuras multi-agente permiten separar memorias entre diferentes sub-tareas, con dos grandes beneficios:
- Cada agente está más enfocado en su tarea principal, por lo que es más performante
- Separar memorias reduce la cantidad de tokens de entrada en cada paso, reduciendo la latencia y el costo.

Vamos a crear un equipo con un agente de búsqueda web dedicado, gestionado por otro agente.

El agente gestor debe tener capacidades de trazado para escribir su informe final: así que vamos a darle acceso a importaciones adicionales, incluyendo `matplotlib`, y `geopandas` + `shapely` para trazado espacial.

```python
model = InferenceClientModel(
    "Qwen/Qwen2.5-Coder-32B-Instruct", provider="together", max_tokens=8096
)

web_agent = CodeAgent(
    model=model,
    tools=[
        GoogleSearchTool(provider="serper"),
        VisitWebpageTool(),
        calculate_cargo_travel_time,
    ],
    name="web_agent",
    description="Navega por la web para encontrar información",
    verbosity_level=0,
    max_steps=10,
)
```

El agente gestor necesitará hacer algo de trabajo mental pesado.

Así que le damos el modelo más fuerte [DeepSeek-R1](https://huggingface.co/deepseek-ai/DeepSeek-R1), y agregamos un `planning_interval` a la mezcla.

```python
from smolagents.utils import encode_image_base64, make_image_url
from smolagents import OpenAIServerModel

def check_reasoning_and_plot(final_answer, agent_memory):
    final_answer
    multimodal_model = OpenAIServerModel("gpt-4o", max_tokens=8096)
    filepath = "saved_map.png"
    assert os.path.exists(filepath), "Asegúrate de guardar el trazado bajo saved_map.png!"
    image = Image.open(filepath)
    prompt = (
        f"Aquí está una tarea dada por el usuario y los pasos del agente: {agent_memory.get_succinct_steps()}. Ahora aquí está el trazado que se hizo."
        "Por favor, verifica que el proceso de razonamiento y el trazado sean correctos: ¿responden correctamente a la tarea dada?"
        "Primero enumera razones por las que sí/no, luego escribe tu decisión final: PASS en mayúsculas si es satisfactorio, FAIL si no lo es."
        "No seas duro: si el trazado resuelve en gran medida la tarea, debe pasar."
        "Para pasar, un trazado debe hacerse usando px.scatter_map y no cualquier otro método (scatter_map se ve mejor)."
    )
    messages = [
        {
            "role": "user",
            "content": [
                {
                    "type": "text",
                    "text": prompt,
                },
                {
                    "type": "image_url",
                    "image_url": {"url": make_image_url(encode_image_base64(image))},
                },
            ],
        }
    ]
    output = multimodal_model(messages).content
    print("Retroalimentación: ", output)
    if "FAIL" in output:
        raise Exception(output)
    return True

manager_agent = CodeAgent(
    model=InferenceClientModel("deepseek-ai/DeepSeek-R1", provider="together", max_tokens=8096),
    tools=[calculate_cargo_travel_time],
    managed_agents=[web_agent],
    additional_authorized_imports=[
        "geopandas",
        "plotly",
        "shapely",
        "json",
        "pandas",
        "numpy",
    ],
    planning_interval=5,
    verbosity_level=2,
    final_answer_checks=[check_reasoning_and_plot],
    max_steps=15,
)
```

Vamos a inspeccionar qué se ve este equipo:

```python
manager_agent.visualize()
```

Esto generará algo como esto, ayudándonos a entender la estructura y la relación entre agentes y herramientas utilizadas:

```python
CodeAgent | deepseek-ai/DeepSeek-R1
├── ✅ Authorized imports: ['geopandas', 'plotly', 'shapely', 'json', 'pandas', 'numpy']
├── 🛠️ Tools:
│   ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
│   ┃ Name                        ┃ Description                           ┃ Arguments                             ┃
│   ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│   │ calculate_cargo_travel_time │ Calculate the travel time for a cargo │ origin_coords (`array`): Tuple of     │
│   │                             │ plane between two points on Earth     │ (latitude, longitude) for the         │
│   │                             │ using great-circle distance.          │ starting point                        │
│   │                             │                                       │ destination_coords (`array`): Tuple   │
│   │                             │                                       │ of (latitude, longitude) for the      │
│   │                             │                                       │ destination                           │
│   │                             │                                       │ cruising_speed_kmh (`number`):        │
│   │                             │                                       │ Optional cruising speed in km/h       │
│   │                             │                                       │ (defaults to 750 km/h for typical     │
│   │                             │                                       │ cargo planes)                         │
│   │ final_answer                │ Provides a final answer to the given  │ answer (`any`): The final answer to   │
│   │                             │ problem.                              │ the problem                           │
│   └─────────────────────────────┴───────────────────────────────────────┴───────────────────────────────────────┘
└── 🤖 Managed agents:
    └── web_agent | CodeAgent | Qwen/Qwen2.5-Coder-32B-Instruct
        ├── ✅ Authorizar imports: []
        ├── 📝 Description: Navega por la web para encontrar información
        └── 🛠️ Tools:
            ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
            ┃ Name                        ┃ Description                       ┃ Arguments                         ┃
            ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
            │ web_search                  │ Performs a google web search for  │ query (`string`): The search      │
            │                             │ your query then returns a string  │ query to perform.                 │
            │                             │ of the top search results.        │ filter_year (`integer`):          │
            │                             │                                   │ Optionally restrict results to a  │
            │                             │                                   │ certain year                      │
            │ visit_webpage               │ Visits a webpage at the given url │ url (`string`): The url of the    │
            │                             │ and reads its content as a        │ webpage to visit.                 │
            │                             │ markdown string. Use this to      │                                   │
            │                             │ browse webpages.                  │                                   │
            │ calculate_cargo_travel_time │ Calculate the travel time for a   │ origin_coords (`array`): Tuple of │
            │                             │ cargo plane between two points on │ (latitude, longitude) for the     │
            │                             │ Earth using great-circle          │ starting point                    │
            │                             │ distance.                         │ destination_coords (`array`):     │
            │                             │                                   │ Tuple of (latitude, longitude)    │
            │                             │                                   │ for the destination               │
            │                             │                                   │ cruising_speed_kmh (`number`):    │
            │                             │                                   │ Optional cruising speed in km/h   │
            │                             │                                   │ (defaults to 750 km/h for typical │
            │                             │                                   │ cargo planes)                     │
            │ final_answer                │ Provides a final answer to the    │ answer (`any`): The final answer  │
            │                             │ given problem.                    │ to the problem                    │
            └─────────────────────────────┴───────────────────────────────────┴───────────────────────────────────┘
```

```python
manager_agent.run("""
Encuentra todas las locaciones de filmación de Batman en el mundo, calcula el tiempo de transferencia en avión de carga hasta aquí (estamos en Gotham, 40.7128° N, 74.0060° W).
También dame algunas fábricas de superdeportivos con el mismo tiempo de transferencia en avión.
Necesito al menos 6 puntos en total.
Representa esto como un mapa espacial del mundo, con las locaciones representadas como puntos de dispersión con un color que depende del tiempo de transferencia, y guárdalo en saved_map.png!

Aquí hay un ejemplo de cómo trazar y devolver un mapa:
import plotly.express as px
df = px.data.carshare()
fig = px.scatter_map(df, lat="centroid_lat", lon="centroid_lon", text="name", color="peak_hour", size=100,
     color_continuous_scale=px.colors.sequential.Magma, size_max=15, zoom=1)
fig.show()
fig.write_image("saved_image.png")
final_answer(fig)

Nunca intentes procesar cadenas usando código: cuando tengas una cadena para leer, simplemente imprímela y la verás.
""")
```

No sé cómo salió en tu ejecución, pero en la mía, el agente gestor dividió hábilmente las tareas dadas al agente web en `1. Buscar locaciones de filmación de Batman`, luego `2. Encontrar fábricas de superdeportivos`, antes de agregar las listas y trazar el mapa.

Vamos a ver qué se ve el mapa inspeccionándolo directamente desde el estado del agente:

```python
manager_agent.python_executor.state["fig"]
```

Esto generará el mapa:

![Multiagent system example output map](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/unit2/smolagents/output_map.png)

## Recursos

- [Sistemas Multi-Agente](https://huggingface.co/docs/smolagents/main/en/examples/multiagents) – Visión general de los sistemas multi-agente.  
- [¿Qué es Agentic RAG?](https://weaviate.io/blog/what-is-agentic-rag) – Introducción a Agentic RAG.  
- [Sistema RAG Multi-Agente 🤖🤝🤖 Receta](https://huggingface.co/learn/cookbook/multiagent_rag_system) – Guía paso a paso para construir un sistema RAG multi-agente.

### Agentes de Visión con smolagents
https://huggingface.co/learn/agents-course/es/unit2/smolagents/vision_agents.md

# Agentes de Visión con smolagents

> [!WARNING]
> Los ejemplos en esta sección requieren acceso a un modelo VLM potente. Los probamos usando la API de GPT-4o.  
> Sin embargo, Por qué usar smolagents discute soluciones alternativas soportadas por smolagents y Hugging Face. Si deseas explorar otras opciones, asegúrate de revisar esa sección.

Dotar a los agentes con capacidades visuales es crucial para resolver tareas que van más allá del procesamiento de texto. Muchos desafíos del mundo real, como la navegación web o la comprensión de documentos, requieren analizar contenido visual complejo. Afortunadamente, `smolagents` proporciona soporte integrado para modelos de visión-lenguaje (VLMs), permitiendo a los agentes procesar e interpretar imágenes de manera efectiva.  

En este ejemplo, imagina que Alfred, el mayordomo de la Mansión Wayne, tiene la tarea de verificar las identidades de los invitados que asisten a la fiesta. Como puedes imaginar, Alfred puede no estar familiarizado con todos los que llegan. Para ayudarlo, podemos usar un agente que verifique su identidad buscando información visual sobre su apariencia usando un VLM. Esto permitirá a Alfred tomar decisiones informadas sobre quién puede entrar. ¡Vamos a construir este ejemplo!

## Proporcionando Imágenes al Inicio de la Ejecución del Agente

> [!TIP]
> Puedes seguir el código en este notebook que puedes ejecutar usando Google Colab.

En este enfoque, las imágenes se pasan al agente al inicio y se almacenan como `task_images` junto con el prompt de la tarea. El agente luego procesa estas imágenes durante su ejecución.  

Considera el caso donde Alfred quiere verificar las identidades de los superhéroes que asisten a la fiesta. Ya tiene un conjunto de datos de imágenes de fiestas anteriores con los nombres de los invitados. Dada la imagen de un nuevo visitante, el agente puede compararla con el conjunto de datos existente y tomar una decisión sobre dejarlos entrar.  

En este caso, un invitado está tratando de entrar, y Alfred sospecha que este visitante podría ser El Joker haciéndose pasar por Wonder Woman. Alfred necesita verificar su identidad para evitar que entren personas no deseadas.  

Vamos a construir el ejemplo. Primero, se cargan las imágenes. En este caso, usamos imágenes de Wikipedia para mantener el ejemplo mínimo, ¡pero imagina el posible caso de uso!

```python
from PIL import Image
import requests
from io import BytesIO

image_urls = [
    "https://upload.wikimedia.org/wikipedia/commons/e/e8/The_Joker_at_Wax_Museum_Plus.jpg", # Imagen del Joker
    "https://upload.wikimedia.org/wikipedia/en/9/98/Joker_%28DC_Comics_character%29.jpg" # Imagen del Joker
]

images = []
for url in image_urls:
    response = requests.get(url)
    image = Image.open(BytesIO(response.content)).convert("RGB")
    images.append(image)
```

Ahora que tenemos las imágenes, el agente nos dirá si un invitado es realmente un superhéroe (Wonder Woman) o un villano (El Joker).

```python
from smolagents import CodeAgent, OpenAIServerModel

model = OpenAIServerModel(model_id="gpt-4o")

# Instanciar el agente
agent = CodeAgent(
    tools=[],
    model=model,
    max_steps=20,
    verbosity_level=2
)

response = agent.run(
    """
    Describe el disfraz y maquillaje que está usando el personaje de cómic en estas fotos y devuelve la descripción.
    Dime si el invitado es El Joker o Wonder Woman.
    """,
    images=images
)
```

En el caso de mi ejecución, la salida es la siguiente, aunque podría variar en tu caso, como ya hemos discutido:

```python
    {
        'Disfraz y Maquillaje - Primera Imagen': (
            'Abrigo púrpura y una corbata o pañuelo de seda púrpura sobre una camisa amarillo mostaza.',
            'Pintura facial blanca con rasgos exagerados, cejas oscuras, maquillaje azul en los ojos, labios rojos formando una amplia sonrisa.'
        ),
        'Disfraz y Maquillaje - Segunda Imagen': (
            'Traje oscuro con una flor en la solapa, sosteniendo una carta de juego.',
            'Piel pálida, cabello verde, labios muy rojos con una sonrisa exagerada.'
        ),
        'Identidad del Personaje': 'Este personaje se asemeja a representaciones conocidas de El Joker de los medios de cómics.'
    }
```

En este caso, la salida revela que la persona se está haciendo pasar por alguien más, ¡así que podemos evitar que El Joker entre a la fiesta!

## Proporcionando Imágenes con Recuperación Dinámica

> [!TIP]
> Puedes seguir el código en este archivo Python

El enfoque anterior es valioso y tiene muchos casos de uso potenciales. Sin embargo, en situaciones donde el invitado no está en la base de datos, necesitamos explorar otras formas de identificarlos. Una posible solución es recuperar dinámicamente imágenes e información de fuentes externas, como navegar por la web para obtener detalles.

En este enfoque, las imágenes se agregan dinámicamente a la memoria del agente durante la ejecución. Como sabemos, los agentes en `smolagents` están basados en la clase `MultiStepAgent`, que es una abstracción del framework ReAct. Esta clase opera en un ciclo estructurado donde varias variables y conocimientos se registran en diferentes etapas:

1. **SystemPromptStep:** Almacena el prompt del sistema.
2. **TaskStep:** Registra la consulta del usuario y cualquier entrada proporcionada.
3. **ActionStep:** Captura registros de las acciones del agente y los resultados.

Este enfoque estructurado permite a los agentes incorporar información visual de manera dinámica y responder de forma adaptativa a tareas en evolución. A continuación se muestra el diagrama que ya hemos visto, ilustrando el proceso de flujo de trabajo dinámico y cómo diferentes pasos se integran dentro del ciclo de vida del agente. Al navegar, el agente puede tomar capturas de pantalla y guardarlas como `observation_images` en el `ActionStep`.

![Recuperación dinámica de imágenes](https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/blog/smolagents-can-see/diagram_adding_vlms_smolagents.png)

Ahora que entendemos la necesidad, vamos a construir nuestro ejemplo completo. En este caso, Alfred quiere control total sobre el proceso de verificación de invitados, por lo que buscar detalles se convierte en una solución viable. Para completar este ejemplo, necesitamos un nuevo conjunto de herramientas para el agente. Además, usaremos Selenium y Helium, que son herramientas de automatización de navegador. Esto nos permitirá construir un agente que explore la web, buscando detalles sobre un posible invitado y recuperando información de verificación. Vamos a instalar las herramientas necesarias:

```bash
pip install "smolagents[all]" helium selenium python-dotenv
```

Necesitaremos un conjunto de herramientas de agente específicamente diseñadas para navegar, como `search_item_ctrl_f`, `go_back`, y `close_popups`. Estas herramientas permiten al agente actuar como una persona navegando por la web.

```python
@tool
def search_item_ctrl_f(text: str, nth_result: int = 1) -> str:
    """
    Busca texto en la página actual a través de Ctrl + F y salta a la n-ésima ocurrencia.
    Args:
        text: El texto a buscar
        nth_result: A qué ocurrencia saltar (por defecto: 1)
    """
    elements = driver.find_elements(By.XPATH, f"//*[contains(text(), '{text}')]")
    if nth_result > len(elements):
        raise Exception(f"Coincidencia n°{nth_result} no encontrada (solo se encontraron {len(elements)} coincidencias)")
    result = f"Se encontraron {len(elements)} coincidencias para '{text}'."
    elem = elements[nth_result - 1]
    driver.execute_script("arguments[0].scrollIntoView(true);", elem)
    result += f"Enfocado en el elemento {nth_result} de {len(elements)}"
    return result

@tool
def go_back() -> None:
    """Regresa a la página anterior."""
    driver.back()

@tool
def close_popups() -> str:
    """
    Cierra cualquier modal o pop-up visible en la página. ¡Usa esto para descartar ventanas emergentes! Esto no funciona en banners de consentimiento de cookies.
    """
    webdriver.ActionChains(driver).send_keys(Keys.ESCAPE).perform()
```

También necesitamos funcionalidad para guardar capturas de pantalla, ya que esta será una parte esencial de lo que nuestro agente VLM usa para completar la tarea. Esta funcionalidad captura la pantalla y la guarda en `step_log.observations_images = [image.copy()]`, permitiendo al agente almacenar y procesar las imágenes dinámicamente mientras navega.

```python
def save_screenshot(step_log: ActionStep, agent: CodeAgent) -> None:
    sleep(1.0)  # Permitir que ocurran animaciones JavaScript antes de tomar la captura de pantalla
    driver = helium.get_driver()
    current_step = step_log.step_number
    if driver is not None:
        for step_logs in agent.logs:  # Eliminar capturas de pantalla anteriores de los registros para un procesamiento eficiente
            if isinstance(step_log, ActionStep) and step_log.step_number 
  

Esta es la salida final:

```python
Respuesta final: Wonder Woman normalmente se representa vistiendo un corsé rojo y dorado, shorts o falda azul con estrellas blancas, una tiara dorada, brazaletes plateados y un Lazo de la Verdad dorado. Es la Princesa Diana de Themyscira, conocida como Diana Prince en el mundo de los hombres.
```

¡Con todo eso, hemos creado exitosamente nuestro verificador de identidad para la fiesta! Alfred ahora tiene las herramientas necesarias para asegurar que solo los invitados correctos pasen por la puerta. ¡Todo está listo para pasarlo bien en la Mansión Wayne!

## Lecturas Adicionales

- [Acabamos de dar vista a smolagents](https://huggingface.co/blog/smolagents-can-see) - Blog que describe la funcionalidad del agente de visión.
- [Automatización de Navegador Web con Agentes 🤖🌐](https://huggingface.co/docs/smolagents/examples/web_browser) - Ejemplo de navegación web usando un agente de visión.
- [Ejemplo de Agente de Visión para Navegador Web](https://github.com/huggingface/smolagents/blob/main/src/smolagents/vision_web_browser.py) - Ejemplo de navegación web usando un agente de visión.

### Construyendo Sistemas RAG con Agentes
https://huggingface.co/learn/agents-course/es/unit2/smolagents/retrieval_agents.md

# Construyendo Sistemas RAG con Agentes

> [!TIP]
> Puedes seguir el código en este notebook que puedes ejecutar usando Google Colab.

Los sistemas de Generación Aumentada por Recuperación (RAG) combinan las capacidades de recuperación de datos y modelos de generación para proporcionar respuestas contextualizadas. Por ejemplo, la consulta de un usuario se pasa a un motor de búsqueda, y los resultados recuperados se entregan al modelo junto con la consulta. El modelo luego genera una respuesta basada en la consulta y la información recuperada.

El RAG con Agentes (Generación Aumentada por Recuperación) extiende los sistemas RAG tradicionales al **combinar agentes autónomos con recuperación dinámica de conocimiento**.

Mientras que los sistemas RAG tradicionales utilizan un LLM para responder consultas basadas en datos recuperados, el RAG con agentes **permite un control inteligente tanto de los procesos de recuperación como de generación**, mejorando la eficiencia y precisión.

Los sistemas RAG tradicionales enfrentan limitaciones clave, como **depender de un solo paso de recuperación** y enfocarse en la similitud semántica directa con la consulta del usuario, lo que puede pasar por alto información relevante.

El RAG con agentes aborda estos problemas permitiendo que el agente formule autónomamente consultas de búsqueda, critique los resultados recuperados y realice múltiples pasos de recuperación para obtener un resultado más personalizado y completo.

## Recuperación Básica con DuckDuckGo

Vamos a construir un agente simple que pueda buscar en la web usando DuckDuckGo. Este agente recuperará información y sintetizará respuestas para contestar consultas. Con RAG con agentes, el agente de Alfred puede:

* Buscar las últimas tendencias en fiestas de superhéroes
* Refinar resultados para incluir elementos de lujo
* Sintetizar información en un plan completo

Así es como el agente de Alfred puede lograr esto:

```python
from smolagents import CodeAgent, DuckDuckGoSearchTool, InferenceClientModel

# Initialize the search tool
search_tool = DuckDuckGoSearchTool()

# Initialize the model
model = InferenceClientModel()

agent = CodeAgent(
    model=model,
    tools=[search_tool]
)

# Example usage
response = agent.run(
    "Buscar ideas de fiesta temática de superhéroes de lujo, incluyendo decoración, entretenimiento y catering."
)
print(response)
```

El agente sigue este proceso:

1. **Analiza la Solicitud:** El agente de Alfred identifica los elementos clave de la consulta—planificación de fiesta temática de superhéroes de lujo, con enfoque en decoración, entretenimiento y catering.
2. **Realiza la Recuperación:** El agente utiliza DuckDuckGo para buscar la información más relevante y actualizada, asegurándose de que se alinee con las preferencias refinadas de Alfred para un evento lujoso.
3. **Sintetiza la Información:** Después de recopilar los resultados, el agente los procesa en un plan coherente y accionable para Alfred, cubriendo todos los aspectos de la fiesta.
4. **Almacena para Referencia Futura:** El agente almacena la información recuperada para un fácil acceso al planificar eventos futuros, optimizando la eficiencia en tareas posteriores.

## Herramienta de Base de Conocimiento Personalizada

Para tareas especializadas, una base de conocimiento personalizada puede ser invaluable. Vamos a crear una herramienta que consulte una base de datos vectorial de documentación técnica o conocimiento especializado. Utilizando búsqueda semántica, el agente puede encontrar la información más relevante para las necesidades de Alfred.

Una base de datos vectorial es simplemente una colección de documentos con representaciones enriquecidas por modelos de ML especializados, que permiten la búsqueda y recuperación rápida de los documentos.

Este enfoque combina conocimiento predefinido con búsqueda semántica para proporcionar soluciones contextualizadas para la planificación de eventos. Con acceso a conocimiento especializado, Alfred puede perfeccionar cada detalle de la fiesta.

En este ejemplo, crearemos una herramienta que recupera ideas de planificación de fiestas desde una base de conocimiento personalizada. Usaremos un recuperador BM25 para buscar en la base de conocimiento y devolver los mejores resultados, y `RecursiveCharacterTextSplitter` para dividir los documentos en fragmentos más pequeños para una búsqueda más eficiente.

```python
from langchain.docstore.document import Document
from langchain.text_splitter import RecursiveCharacterTextSplitter
from smolagents import Tool
from langchain_community.retrievers import BM25Retriever
from smolagents import CodeAgent, InferenceClientModel

class PartyPlanningRetrieverTool(Tool):
    name = "party_planning_retriever"
    description = "Utiliza búsqueda semántica para recuperar ideas de planificación de fiestas relevantes para la fiesta temática de superhéroes de Alfred en Wayne Manor."
    inputs = {
        "query": {
            "type": "string",
            "description": "La consulta a realizar. Esta debe ser una consulta relacionada con la planificación de fiestas o temas de superhéroes.",
        }
    }
    output_type = "string"

    def __init__(self, docs, **kwargs):
        super().__init__(**kwargs)
        self.retriever = BM25Retriever.from_documents(
            docs, k=5  # Retrieve the top 5 documents
        )

    def forward(self, query: str) -> str:
        assert isinstance(query, str), "Tu consulta de búsqueda debe ser una cadena"

        docs = self.retriever.invoke(
            query,
        )
        return "\nIdeas recuperadas:\n" + "".join(
            [
                f"\n\n===== Idea {str(i)} =====\n" + doc.page_content
                for i, doc in enumerate(docs)
            ]
        )

# Simulate a knowledge base about party planning
party_ideas = [
    {"text": "Una fiesta de disfraces temática de superhéroes con decoración de lujo, incluyendo detalles dorados y cortinas de terciopelo.", "source": "Ideas de fiesta 1"},
    {"text": "Contrata a un DJ profesional que pueda tocar música temática para superhéroes como Batman y Wonder Woman.", "source": "Ideas de entretenimiento"},
    {"text": "Para el catering, sirve platos con nombres de superhéroes, como 'El smoothie verde de Hulk' y 'El filete de poder de Iron Man'.", "source": "Ideas de catering"},
    {"text": "Decora con logotipos icónicos de superhéroes y proyecciones de Gotham y otras ciudades de superhéroes alrededor del lugar.", "source": "Ideas de decoración"},
    {"text": "Experiencias interactivas con realidad virtual donde los invitados pueden participar en simulaciones de superhéroes o competir en juegos temáticos.", "source": "Ideas de entretenimiento"}
]

source_docs = [
    Document(page_content=doc["text"], metadata={"source": doc["source"]})
    for doc in party_ideas
]

# Split the documents into smaller chunks for more efficient search
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=500,
    chunk_overlap=50,
    add_start_index=True,
    strip_whitespace=True,
    separators=["\n\n", "\n", ".", " ", ""],
)
docs_processed = text_splitter.split_documents(source_docs)

# Create the retriever tool
party_planning_retriever = PartyPlanningRetrieverTool(docs_processed)

# Initialize the agent
agent = CodeAgent(tools=[party_planning_retriever], model=InferenceClientModel())

# Example usage
response = agent.run(
    "Encuentra ideas para una fiesta temática de superhéroes de lujo, incluyendo entretenimiento, catering y opciones de decoración."
)

print(response)
```

Este agente mejorado puede:
1. Primero verificar la documentación para obtener información relevante
2. Combinar ideas de la base de conocimiento
3. Mantener el contexto de la conversación en memoria

## Capacidades de Recuperación Mejoradas

Al construir sistemas RAG con agentes, el agente puede emplear estrategias sofisticadas como:

1. **Reformulación de Consultas:** En lugar de usar la consulta del usuario en bruto, el agente puede elaborar términos de búsqueda optimizados que coincidan mejor con los documentos objetivo
2. **Recuperación Multi-Paso:** El agente puede realizar múltiples búsquedas, utilizando los resultados iniciales para informar consultas posteriores
3. **Integración de Fuentes:** La información puede combinarse de múltiples fuentes como búsqueda web y documentación local
4. **Validación de Resultados:** El contenido recuperado puede analizarse para determinar su relevancia y precisión antes de incluirlo en las respuestas

Los sistemas RAG con agentes efectivos requieren una consideración cuidadosa de varios aspectos clave. El agente **debe seleccionar entre las herramientas disponibles según el tipo de consulta y el contexto**. Los sistemas de memoria ayudan a mantener el historial de conversación y evitar recuperaciones repetitivas. Tener estrategias de respaldo garantiza que el sistema pueda seguir proporcionando valor incluso cuando los métodos de recuperación principales fallan. Además, implementar pasos de validación ayuda a garantizar la precisión y relevancia de la información recuperada.

## Recursos

- [RAG con Agentes: ¡potencia tu RAG con reformulación de consultas y auto-consulta! 🚀](https://huggingface.co/learn/cookbook/agent_rag) - Receta para desarrollar un sistema RAG con Agentes utilizando smolagents.

### Pequeño Quiz (no calificado) [[quiz2]]
https://huggingface.co/learn/agents-course/es/unit2/smolagents/quiz2.md

# Pequeño Quiz (no calificado) [[quiz2]]

Es hora de poner a prueba tu comprensión de las secciones *Agentes de Código*, *Agentes de Llamada a Herramientas* y *Herramientas*. Este quiz es opcional y no está calificado.

---

### P1: ¿Cuál es la diferencia clave entre crear una herramienta con el decorador `@tool` versus crear una subclase de `Tool` en smolagents?

¿Qué afirmación describe mejor la distinción entre estos dos enfoques para definir herramientas?

@tool es obligatorio para herramientas basadas en recuperación, mientras que las subclases de Tool son solo para tareas de generación de texto",
    explain: "Ambos enfoques pueden usarse para cualquier tipo de herramienta, incluidas las basadas en recuperación o generación de texto.",
  },
  {
    text: "El decorador @tool se recomienda para herramientas simples basadas en funciones, mientras que las subclases de Tool ofrecen más flexibilidad para funcionalidades complejas o metadatos personalizados",
    explain: "Esto es correcto. El enfoque del decorador es más simple, pero la subclasificación permite un comportamiento más personalizado.",
    correct: true
  },
  {
    text: "@tool solo puede usarse en sistemas multi-agente, mientras que crear una subclase de Tool es para escenarios de un solo agente",
    explain: "Todos los agentes (individuales o múltiples) pueden usar cualquiera de los enfoques para definir herramientas; no existe tal restricción.",
  },
  {
    text: "Decorar una función con @tool reemplaza la necesidad de un docstring, mientras que las subclases no deben incluir docstrings",
    explain: "Ambos métodos se benefician de docstrings claros. El decorador no los reemplaza, y una subclase también puede tener docstrings.",
  }
]}
/>

---

### P2: ¿Cómo maneja un CodeAgent tareas de múltiples pasos utilizando el enfoque ReAct (Reason + Act)?

¿Qué afirmación describe correctamente cómo el CodeAgent ejecuta una serie de pasos para resolver una tarea?

---

### P3: ¿Cuál de las siguientes es una ventaja principal de compartir una herramienta en Hugging Face Hub?

Selecciona la mejor razón por la que un desarrollador podría subir y compartir su herramienta personalizada.

---

### P4: ToolCallingAgent difiere de CodeAgent en cómo ejecuta acciones. ¿Qué afirmación es correcta?

Elige la opción que describe con precisión cómo funciona ToolCallingAgent.

---

### P5: ¿Qué se incluye en la caja de herramientas predeterminada de smolagents y por qué podrías usarla?

¿Qué afirmación captura mejor el propósito y el contenido de la caja de herramientas predeterminada en smolagents?

---

¡Felicidades por completar este quiz! 🎉 Si alguna pregunta te dio problemas, revisa las secciones *Agentes de Código*, *Agentes de Llamada a Herramientas* o *Herramientas* para fortalecer tu comprensión. Si lo has hecho bien, ¡estás en buen camino para construir aplicaciones robustas con smolagents!

### Construcción de Agentes que Usan Código
https://huggingface.co/learn/agents-course/es/unit2/smolagents/code_agents.md

# Construcción de Agentes que Usan Código

Los agentes de código son el tipo de agente predeterminado en `smolagents`. Generan llamadas a herramientas en Python para realizar acciones, logrando representaciones de acciones que son eficientes, expresivas y precisas.

Su enfoque simplificado reduce el número de acciones requeridas, simplifica operaciones complejas y permite la reutilización de funciones de código existentes. `smolagents` proporciona un marco ligero para construir agentes de código, implementado en aproximadamente 1,000 líneas de código.

![Acciones de Código vs JSON](https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/code_vs_json_actions.png)
Gráfico del artículo [Executable Code Actions Elicit Better LLM Agents](https://huggingface.co/papers/2402.01030)

> [!TIP]
> Si quieres aprender más sobre por qué los agentes de código son efectivos, consulta esta guía de la documentación de smolagents.

## ¿Por qué Agentes de Código?

En un proceso de agente de múltiples pasos, el LLM escribe y ejecuta acciones, típicamente involucrando llamadas a herramientas externas. Los enfoques tradicionales utilizan un formato JSON para especificar nombres de herramientas y argumentos como cadenas de texto, **que el sistema debe analizar para determinar qué herramienta ejecutar**.

Sin embargo, la investigación muestra que **los LLMs que llaman a herramientas funcionan más efectivamente con código directamente**. Este es un principio fundamental de `smolagents`, como se muestra en el diagrama anterior del artículo [Executable Code Actions Elicit Better LLM Agents](https://huggingface.co/papers/2402.01030).

Escribir acciones en código en lugar de JSON ofrece varias ventajas clave:

* **Componibilidad**: Combinar y reutilizar acciones fácilmente
* **Gestión de Objetos**: Trabajar directamente con estructuras complejas como imágenes
* **Generalidad**: Expresar cualquier tarea computacionalmente posible
* **Natural para LLMs**: Código de alta calidad ya está presente en los datos de entrenamiento de LLMs

## ¿Cómo Funciona un Agente de Código?

![De https://huggingface.co/docs/smolagents/conceptual_guides/react](https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/smolagents/codeagent_docs.png)

El diagrama anterior ilustra cómo funciona `CodeAgent.run()`, siguiendo el marco ReAct que mencionamos en la Unidad 1. La abstracción principal para agentes en `smolagents` es un `MultiStepAgent`, que sirve como el bloque de construcción central. `CodeAgent` es un tipo especial de `MultiStepAgent`, como veremos en un ejemplo a continuación.

Un `CodeAgent` realiza acciones a través de un ciclo de pasos, con variables y conocimientos existentes incorporados en el contexto del agente, que se mantiene en un registro de ejecución:

1. El prompt del sistema se almacena en un `SystemPromptStep`, y la consulta del usuario se registra en un `TaskStep`.

2. Luego, se ejecuta el siguiente bucle while:

    2.1 El método `agent.write_memory_to_messages()` escribe los registros del agente en una lista de [mensajes de chat](https://huggingface.co/docs/transformers/en/chat_templating) legibles por el LLM.
    
    2.2 Estos mensajes se envían a un `Model`, que genera una finalización.
    
    2.3 La finalización se analiza para extraer la acción, que, en nuestro caso, debería ser un fragmento de código ya que estamos trabajando con un `CodeAgent`.
    
    2.4 La acción se ejecuta.
    
    2.5 Los resultados se registran en la memoria en un `ActionStep`.

Al final de cada paso, si el agente incluye alguna llamada a función (en `agent.step_callback`), estas se ejecutan.

## Veamos Algunos Ejemplos

> [!TIP]
> Puedes seguir el código en este notebook que puedes ejecutar usando Google Colab.

Alfred está planeando una fiesta en la mansión de la familia Wayne y necesita tu ayuda para asegurarse de que todo salga bien. Para ayudarlo, aplicaremos lo que hemos aprendido sobre cómo opera un `CodeAgent` de múltiples pasos.

Si aún no has instalado `smolagents`, puedes hacerlo ejecutando el siguiente comando:

```bash
pip install smolagents -U
```

También iniciemos sesión en el Hugging Face Hub para tener acceso a la API de Inferencia Serverless.

```python
from huggingface_hub import login

login()
```

### Seleccionando una Lista de Reproducción para la Fiesta Usando `smolagents`

¡La música es una parte esencial de una fiesta exitosa! Alfred necesita ayuda para seleccionar la lista de reproducción. Por suerte, ¡`smolagents` nos tiene cubiertos! Podemos construir un agente capaz de buscar en la web usando DuckDuckGo. Para dar al agente acceso a esta herramienta, la incluimos en la lista de herramientas al crear el agente.

Para el modelo, confiaremos en `InferenceClientModel`, que proporciona acceso a la [API de Inferencia Serverless](https://huggingface.co/docs/api-inference/index) de Hugging Face. El modelo predeterminado es `"Qwen/Qwen2.5-Coder-32B-Instruct"`, que es eficiente y está disponible para inferencia rápida, pero puedes seleccionar cualquier modelo compatible del Hub.

Ejecutar un agente es bastante sencillo:

```python
from smolagents import CodeAgent, DuckDuckGoSearchTool, InferenceClientModel

agent = CodeAgent(tools=[DuckDuckGoSearchTool()], model=InferenceClientModel())

agent.run("Busca las mejores recomendaciones de música para una fiesta en la mansión de los Wayne.")
```

Cuando ejecutes este ejemplo, la salida **mostrará un seguimiento de los pasos del flujo de trabajo siendo ejecutados**. También imprimirá el código Python correspondiente con el mensaje:

```python
 ─ Ejecutando código analizado: ──────────────────────────────────────────────────────────────────────────────────── 
  results = web_search(query="mejor música para una fiesta de Batman")                                                      
  print(results)                                                                                                   
 ───────────────────────────────────────────────────────────────────────────────────────────────────────────────── 
```

¡Después de algunos pasos, verás la lista de reproducción generada que Alfred puede usar para la fiesta! 🎵

### Usando una Herramienta Personalizada para Preparar el Menú

Ahora que hemos seleccionado una lista de reproducción, necesitamos organizar el menú para los invitados. De nuevo, Alfred puede aprovechar `smolagents` para hacerlo. Aquí, usamos el decorador `@tool` para definir una función personalizada que actúa como herramienta. Cubriremos la creación de herramientas con más detalle más adelante, así que por ahora, simplemente podemos ejecutar el código.

Como puedes ver en el ejemplo a continuación, crearemos una herramienta usando el decorador `@tool` y la incluiremos en la lista de `tools`.

```python
from smolagents import CodeAgent, tool, InferenceClientModel

# Herramienta para sugerir un menú basado en la ocasión
@tool
def suggest_menu(occasion: str) -> str:
    """
    Sugiere un menú basado en la ocasión.
    Args:
        occasion: El tipo de ocasión para la fiesta.
    """
    if occasion == "casual":
        return "Pizza, aperitivos y bebidas."
    elif occasion == "formal":
        return "Cena de 3 platos con vino y postre."
    elif occasion == "superhero":
        return "Buffet con comida saludable y de alta energía."
    else:
        return "Menú personalizado para el mayordomo."

# Alfred, el mayordomo, preparando el menú para la fiesta
agent = CodeAgent(tools=[suggest_menu], model=InferenceClientModel())

# Preparando el menú para la fiesta
agent.run("Prepara un menú formal para la fiesta.")
```

El agente se ejecutará durante algunos pasos hasta encontrar la respuesta.

¡El menú está listo! 🥗

### Usando Importaciones de Python Dentro del Agente

Tenemos la lista de reproducción y el menú listos, pero necesitamos verificar un detalle más crucial: ¡el tiempo de preparación!

Alfred necesita calcular cuándo todo estaría listo si comenzara a preparar ahora, en caso de que necesiten asistencia de otros superhéroes.

`smolagents` se especializa en agentes que escriben y ejecutan fragmentos de código Python, ofreciendo ejecución en sandbox para seguridad.
**La ejecución de código tiene medidas de seguridad estrictas** - las importaciones fuera de una lista predefinida segura están bloqueadas por defecto. Sin embargo, puedes autorizar importaciones adicionales pasándolas como cadenas en `additional_authorized_imports`.
Para más detalles sobre la ejecución segura de código, consulta la [guía](https://huggingface.co/docs/smolagents/tutorials/secure_code_execution) oficial.

Al crear el agente, usaremos `additional_authorized_imports` para permitir la importación del módulo `datetime`.

```python
from smolagents import CodeAgent, InferenceClientModel
import numpy as np
import time
import datetime

agent = CodeAgent(tools=[], model=InferenceClientModel(), additional_authorized_imports=['datetime'])

agent.run(
    """
    Alfred necesita prepararse para la fiesta. Aquí están las tareas:
    1. Preparar las bebidas - 30 minutos
    2. Decorar la mansión - 60 minutos
    3. Configurar el menú - 45 minutos
    4. Preparar la música y la lista de reproducción - 45 minutos

    Si comenzamos ahora mismo, ¿a qué hora estará lista la fiesta?
    """
)
```

Estos ejemplos son solo el comienzo de lo que puedes hacer con agentes de código, y ya estamos empezando a ver su utilidad para preparar la fiesta.
Puedes aprender más sobre cómo construir agentes de código en la [documentación de smolagents](https://huggingface.co/docs/smolagents).

En resumen, `smolagents` se especializa en agentes que escriben y ejecutan fragmentos de código Python, ofreciendo ejecución en sandbox para seguridad. Soporta modelos de lenguaje tanto locales como basados en API, haciéndolo adaptable a varios entornos de desarrollo.

### Compartiendo Nuestro Agente Preparador de Fiestas Personalizado en el Hub

¿No sería **increíble compartir nuestro propio agente Alfred con la comunidad**? Al hacerlo, cualquiera puede descargar y usar fácilmente el agente directamente desde el Hub, ¡llevando el mejor planificador de fiestas de Gotham a sus manos! ¡Hagámoslo posible! 🎉

La biblioteca `smolagents` hace esto posible al permitirte compartir un agente completo con la comunidad y descargar otros para uso inmediato. Es tan simple como lo siguiente:

```python
# Cambia a tu nombre de usuario y nombre de repositorio
agent.push_to_hub('sergiopaniego/AlfredAgent')
```

Para descargar el agente nuevamente, usa el código a continuación:

```python
# Cambia a tu nombre de usuario y nombre de repositorio
alfred_agent = agent.from_hub('sergiopaniego/AlfredAgent')

alfred_agent.run("Dame la mejor lista de reproducción para una fiesta en la mansión de Wayne. La idea de la fiesta es un tema de 'mascarada de villanos'")  
```

Lo que también es emocionante es que los agentes compartidos están directamente disponibles como Hugging Face Spaces, permitiéndote interactuar con ellos en tiempo real. Puedes explorar otros agentes [aquí](https://huggingface.co/spaces/davidberenstein1957/smolagents-and-tools).

Por ejemplo, el _AlfredAgent_ está disponible [aquí](https://huggingface.co/spaces/sergiopaniego/AlfredAgent). Puedes probarlo directamente a continuación:

Tal vez te preguntes: ¿cómo construyó Alfred un agente así usando `smolagents`? Al integrar varias herramientas, puede generar un agente de la siguiente manera. No te preocupes por las herramientas por ahora, ya que tendremos una sección dedicada más adelante en esta unidad para explorar eso en detalle:

```python
from smolagents import CodeAgent, DuckDuckGoSearchTool, FinalAnswerTool, InferenceClientModel, Tool, tool, VisitWebpageTool

@tool
def suggest_menu(occasion: str) -> str:
    """
    Sugiere un menú basado en la ocasión.
    Args:
        occasion: El tipo de ocasión para la fiesta.
    """
    if occasion == "casual":
        return "Pizza, aperitivos y bebidas."
    elif occasion == "formal":
        return "Cena de 3 platos con vino y postre."
    elif occasion == "superhero":
        return "Buffet con comida saludable y de alta energía."
    else:
        return "Menú personalizado para el mayordomo."

@tool
def catering_service_tool(query: str) -> str:
    """
    Esta herramienta devuelve el servicio de catering mejor calificado en Ciudad Gótica.
    
    Args:
        query: Un término de búsqueda para encontrar servicios de catering.
    """
    # Lista de ejemplo de servicios de catering y sus calificaciones
    services = {
        "Gotham Catering Co.": 4.9,
        "Wayne Manor Catering": 4.8,
        "Gotham City Events": 4.7,
    }
    
    # Encuentra el servicio de catering mejor calificado (simulando filtrado de consulta de búsqueda)
    best_service = max(services, key=services.get)
    
    return best_service

class SuperheroPartyThemeTool(Tool):
    name = "superhero_party_theme_generator"
    description = """
    Esta herramienta sugiere ideas creativas para fiestas temáticas de superhéroes basadas en una categoría.
    Devuelve una idea única de tema para la fiesta."""
    
    inputs = {
        "category": {
            "type": "string",
            "description": "El tipo de fiesta de superhéroes (por ejemplo, 'héroes clásicos', 'mascarada de villanos', 'Gotham futurista').",
        }
    }
    
    output_type = "string"

    def forward(self, category: str):
        themes = {
            "classic heroes": "Gala de la Liga de la Justicia: Los invitados vienen vestidos como sus héroes favoritos de DC con cócteles temáticos como 'El Ponche de Kryptonita'.",
            "villain masquerade": "Baile de los Pícaros de Gotham: Una mascarada misteriosa donde los invitados se visten como villanos clásicos de Batman.",
            "futuristic Gotham": "Noche Neo-Gotham: Una fiesta de estilo cyberpunk inspirada en Batman Beyond, con decoraciones de neón y gadgets futuristas."
        }
        
        return themes.get(category.lower(), "Idea de fiesta temática no encontrada. Prueba con 'héroes clásicos', 'mascarada de villanos' o 'Gotham futurista'.")

# Alfred, el mayordomo, preparando el menú para la fiesta
agent = CodeAgent(
    tools=[
        DuckDuckGoSearchTool(), 
        VisitWebpageTool(),
        suggest_menu,
        catering_service_tool,
        SuperheroPartyThemeTool()
    ], 
    model=InferenceClientModel(),
    max_steps=10,
    verbosity_level=2
)

agent.run("Dame la mejor lista de reproducción para una fiesta en la mansión de Wayne. La idea de la fiesta es un tema de 'mascarada de villanos'")
```

Como puedes ver, hemos creado un `CodeAgent` con varias herramientas que mejoran la funcionalidad del agente, ¡convirtiéndolo en el mejor planificador de fiestas listo para compartir con la comunidad! 🎉

Ahora, es tu turno: ¡construye tu propio agente y compártelo con la comunidad usando el conocimiento que acabamos de aprender! 🕵️‍♂️💡

> [!TIP]
> Si deseas compartir tu proyecto de agente, entonces crea un space y etiqueta a [agents-course](https://huggingface.co/agents-course) en el Hugging Face Hub. ¡Nos encantaría ver lo que has creado!

### Inspeccionando Nuestro Agente Preparador de Fiestas con OpenTelemetry y Langfuse 📡

A medida que Alfred perfecciona el Agente Preparador de Fiestas, se está cansando de depurar sus ejecuciones. Los agentes, por naturaleza, son impredecibles y difíciles de inspeccionar. Pero como su objetivo es construir el mejor Agente Preparador de Fiestas y desplegarlo en producción, necesita una trazabilidad robusta para monitoreo y análisis futuros.

¡Una vez más, `smolagents` viene al rescate! Adopta el estándar [OpenTelemetry](https://opentelemetry.io/) para instrumentar ejecuciones de agentes, permitiendo una inspección y registro sin problemas. Con la ayuda de [Langfuse](https://langfuse.com/) y el `SmolagentsInstrumentor`, Alfred puede rastrear y analizar fácilmente el comportamiento de su agente.

¡Configurarlo es sencillo!

Primero, necesitamos instalar las dependencias necesarias:

```bash
pip install opentelemetry-sdk opentelemetry-exporter-otlp openinference-instrumentation-smolagents
```

A continuación, Alfred ya ha creado una cuenta en Langfuse y tiene sus claves API listas. Si aún no lo has hecho, puedes registrarte en Langfuse Cloud [aquí](https://cloud.langfuse.com/) o explorar [alternativas](https://huggingface.co/docs/smolagents/tutorials/inspect_runs).

Una vez que tengas tus claves API, deben configurarse correctamente de la siguiente manera:

```python
import os
import base64

LANGFUSE_PUBLIC_KEY="pk-lf-..."
LANGFUSE_SECRET_KEY="sk-lf-..."
LANGFUSE_AUTH=base64.b64encode(f"{LANGFUSE_PUBLIC_KEY}:{LANGFUSE_SECRET_KEY}".encode()).decode()

os.environ["OTEL_EXPORTER_OTLP_ENDPOINT"] = "https://cloud.langfuse.com/api/public/otel" # Región de datos EU
# os.environ["OTEL_EXPORTER_OTLP_ENDPOINT"] = "https://us.cloud.langfuse.com/api/public/otel" # Región de datos US
os.environ["OTEL_EXPORTER_OTLP_HEADERS"] = f"Authorization=Basic {LANGFUSE_AUTH}"
```

Finalmente, Alfred está listo para inicializar el `SmolagentsInstrumentor` y comenzar a rastrear el rendimiento de su agente.

```python
from opentelemetry.sdk.trace import TracerProvider

from openinference.instrumentation.smolagents import SmolagentsInstrumentor
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.trace.export import SimpleSpanProcessor

trace_provider = TracerProvider()
trace_provider.add_span_processor(SimpleSpanProcessor(OTLPSpanExporter()))

SmolagentsInstrumentor().instrument(tracer_provider=trace_provider)
```

¡Alfred ahora está conectado 🔌! Las ejecuciones de `smolagents` se están registrando en Langfuse, dándole visibilidad completa del comportamiento del agente. Con esta configuración, está listo para revisar ejecuciones anteriores y refinar aún más su Agente Preparador de Fiestas.

```python
from smolagents import CodeAgent, InferenceClientModel

agent = CodeAgent(tools=[], model=InferenceClientModel())
alfred_agent = agent.from_hub('sergiopaniego/AlfredAgent', trust_remote_code=True)
alfred_agent.run("Dame la mejor lista de reproducción para una fiesta en la mansión de Wayne. La idea de la fiesta es un tema de 'mascarada de villanos'")  
```

Alfred ahora puede acceder a estos registros [aquí](https://cloud.langfuse.com/project/cm7bq0abj025rad078ak3luwi/traces/995fc019255528e4f48cf6770b0ce27b?timestamp=2025-02-19T10%3A28%3A36.929Z) para revisarlos y analizarlos.

Mientras tanto, la [lista de reproducción sugerida](https://open.spotify.com/playlist/0gZMMHjuxMrrybQ7wTMTpw) establece el ambiente perfecto para los preparativos de la fiesta. ¿Genial, verdad? 🎶

---

Ahora que hemos creado nuestro primer Agente de Código, **aprendamos cómo podemos crear Agentes de Llamada a Herramientas**, el segundo tipo de agente disponible en `smolagents`.

## Recursos

- [Blog de smolagents](https://huggingface.co/blog/smolagents) - Introducción a smolagents e interacciones de código
- [smolagents: Construyendo Buenos Agentes](https://huggingface.co/docs/smolagents/tutorials/building_good_agents) - Mejores prácticas para agentes confiables
- [Construyendo Agentes Efectivos - Anthropic](https://www.anthropic.com/research/building-effective-agents) - Principios de diseño de agentes
- [Compartiendo ejecuciones con OpenTelemetry](https://huggingface.co/docs/smolagents/tutorials/inspect_runs) - Detalles sobre cómo configurar OpenTelemetry para rastrear tus agentes.

### Conclusión
https://huggingface.co/learn/agents-course/es/unit2/smolagents/conclusion.md

# Conclusión

¡Felicitaciones por terminar el módulo de `smolagents` de esta segunda Unidad 🥳

¡Acabas de dominar los fundamentos de `smolagents` y has construido tu propio Agente! Ahora que tienes habilidades en `smolagents`, puedes comenzar a crear Agentes que resolverán tareas que te interesen.

En el próximo módulo, vas a aprender **cómo construir Agentes con LlamaIndex**.

Finalmente, nos encantaría **escuchar lo que piensas del curso y cómo podemos mejorarlo**. Si tienes algún comentario, por favor 👉 [completa este formulario](https://docs.google.com/forms/d/e/1FAIpQLSe9VaONn0eglax0uTwi29rIn4tM7H2sYmmybmG5jJNlE5v0xA/viewform?usp=dialog)

### Sigue aprendiendo, mantente increíble 🤗

### Herramientas
https://huggingface.co/learn/agents-course/es/unit2/smolagents/tools.md

# Herramientas  

Como exploramos en la [unidad 1](https://huggingface.co/learn/agents-course/unit1/tools), los agentes utilizan herramientas para realizar diversas acciones. En `smolagents`, las herramientas son tratadas como **funciones que un LLM puede llamar dentro de un sistema de agentes**.

Para interactuar con una herramienta, el LLM necesita una **descripción de la interfaz** con estos componentes clave:  

- **Nombre**: Cómo se llama la herramienta
- **Descripción de la herramienta**: Qué hace la herramienta  
- **Tipos de entrada y descripciones**: Qué argumentos acepta la herramienta
- **Tipo de salida**: Qué devuelve la herramienta

Por ejemplo, mientras prepara una fiesta en la Mansión Wayne, Alfred necesita varias herramientas para recopilar información - desde buscar servicios de catering hasta encontrar ideas para temas de fiesta. Así es como podría verse la interfaz de una herramienta de búsqueda simple:

- **Nombre:** `web_search`
- **Descripción de la herramienta:** Busca en la web consultas específicas
- **Entrada:** `query` (cadena) - El término de búsqueda a consultar
- **Salida:** Cadena que contiene los resultados de la búsqueda

Al utilizar estas herramientas, Alfred puede tomar decisiones informadas y recopilar toda la información necesaria para planificar la fiesta perfecta.

A continuación, puedes ver una animación que ilustra cómo se gestiona una llamada a una herramienta:

![Pipeline de agente de https://huggingface.co/docs/smolagents/conceptual_guides/react](https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/Agent_ManimCE.gif)

## Métodos de Creación de Herramientas

En `smolagents`, las herramientas pueden definirse de dos maneras:  
1. **Usando el decorador `@tool`** para herramientas simples basadas en funciones
2. **Creando una subclase de `Tool`** para funcionalidades más complejas    

### El Decorador `@tool`  

El decorador `@tool` es **la forma recomendada para definir herramientas simples**. Internamente, smolagents analizará la información básica sobre la función desde Python. Por lo tanto, si nombras tu función claramente y escribes un buen docstring, será más fácil para el LLM utilizarla.

Usando este enfoque, definimos una función con:  

- **Un nombre de función claro y descriptivo** que ayuda al LLM a entender su propósito.  
- **Anotaciones de tipo tanto para entradas como para salidas** para garantizar un uso adecuado.  
- **Una descripción detallada**, que incluye una sección `Args:` donde cada argumento se describe explícitamente. Estas descripciones proporcionan un contexto valioso para el LLM, por lo que es importante escribirlas cuidadosamente.  

#### Generando una herramienta que recupera el servicio de catering mejor valorado

> [!TIP]
> Puedes seguir el código en este notebook que puedes ejecutar usando Google Colab.

Imaginemos que Alfred ya ha decidido el menú para la fiesta, pero ahora necesita ayuda para preparar comida para un número tan grande de invitados. Para hacerlo, le gustaría contratar un servicio de catering y necesita identificar las opciones mejor valoradas disponibles. Alfred puede aprovechar una herramienta para buscar los mejores servicios de catering en su área.

A continuación se muestra un ejemplo de cómo Alfred puede usar el decorador `@tool` para lograrlo:

```python
from smolagents import CodeAgent, InferenceClientModel, tool

# Imaginemos que tenemos una función que obtiene los servicios de catering mejor valorados.
@tool
def catering_service_tool(query: str) -> str:
    """
    Esta herramienta devuelve el servicio de catering mejor valorado en Ciudad Gótica.
    
    Args:
        query: Un término de búsqueda para encontrar servicios de catering.
    """
    # Lista de ejemplo de servicios de catering y sus calificaciones
    services = {
        "Gotham Catering Co.": 4.9,
        "Wayne Manor Catering": 4.8,
        "Gotham City Events": 4.7,
    }
    
    # Encuentra el servicio de catering mejor valorado (simulando el filtrado de consultas de búsqueda)
    best_service = max(services, key=services.get)
    
    return best_service

agent = CodeAgent(tools=[catering_service_tool], model=InferenceClientModel())

# Ejecuta el agente para encontrar el mejor servicio de catering
result = agent.run(
    "¿Puedes darme el nombre del servicio de catering mejor valorado en Ciudad Gótica?"
)

print(result)   # Salida: Gotham Catering Co.
```

### Definiendo una Herramienta como una Clase de Python  

Este enfoque implica crear una subclase de [`Tool`](https://huggingface.co/docs/smolagents/v1.8.1/en/reference/tools#smolagents.Tool). Para herramientas complejas, podemos implementar una clase en lugar de una función de Python. La clase envuelve la función con metadatos que ayudan al LLM a entender cómo usarla de manera efectiva. En esta clase, definimos:  

- `name`: El nombre de la herramienta.  
- `description`: Una descripción utilizada para completar el prompt del sistema del agente.  
- `inputs`: Un diccionario con claves `type` y `description`, proporcionando información para ayudar al intérprete de Python a procesar las entradas.  
- `output_type`: Especifica el tipo de salida esperado.  
- `forward`: El método que contiene la lógica de inferencia a ejecutar.

A continuación, podemos ver un ejemplo de una herramienta construida usando `Tool` y cómo integrarla dentro de un `CodeAgent`.

#### Generando una herramienta para generar ideas sobre la fiesta temática de superhéroes

La fiesta de Alfred en la mansión es un **evento temático de superhéroes**, pero necesita algunas ideas creativas para hacerla verdaderamente especial. Como anfitrión fantástico, quiere sorprender a los invitados con un tema único.

Para hacer esto, puede usar un agente que genere ideas de fiestas temáticas de superhéroes basadas en una categoría dada. De esta manera, Alfred puede encontrar el tema de fiesta perfecto para impresionar a sus invitados.

```python
from smolagents import Tool, CodeAgent, InferenceClientModel

class SuperheroPartyThemeTool(Tool):
    name = "superhero_party_theme_generator"
    description = """
    Esta herramienta sugiere ideas creativas para fiestas temáticas de superhéroes basadas en una categoría.
    Devuelve una idea única de tema para la fiesta."""
    
    inputs = {
        "category": {
            "type": "string",
            "description": "El tipo de fiesta de superhéroes (por ejemplo, 'héroes clásicos', 'mascarada de villanos', 'Gotham futurista').",
        }
    }
    
    output_type = "string"

    def forward(self, category: str):
        themes = {
            "classic heroes": "Gala de la Liga de la Justicia: Los invitados vienen vestidos como sus héroes favoritos de DC con cócteles temáticos como 'El Ponche de Kryptonita'.",
            "villain masquerade": "Baile de los Pícaros de Gotham: Una mascarada misteriosa donde los invitados se visten como villanos clásicos de Batman.",
            "futuristic Gotham": "Noche Neo-Gotham: Una fiesta de estilo cyberpunk inspirada en Batman Beyond, con decoraciones de neón y gadgets futuristas."
        }
        
        return themes.get(category.lower(), "Idea de fiesta temática no encontrada. Prueba con 'héroes clásicos', 'mascarada de villanos' o 'Gotham futurista'.")

# Instancia la herramienta
party_theme_tool = SuperheroPartyThemeTool()
agent = CodeAgent(tools=[party_theme_tool], model=InferenceClientModel())

# Ejecuta el agente para generar una idea de tema para la fiesta
result = agent.run(
    "¿Cuál sería una buena idea para una fiesta de superhéroes con el tema 'mascarada de villanos'?"
)

print(result)  # Salida: "Baile de los Pícaros de Gotham: Una mascarada misteriosa donde los invitados se visten como villanos clásicos de Batman."
```

Con esta herramienta, ¡Alfred será el mejor anfitrión, impresionando a sus invitados con una fiesta temática de superhéroes que no olvidarán! 🦸‍♂️🦸‍♀️

## Caja de Herramientas Predeterminada  

`smolagents` viene con un conjunto de herramientas preintegradas que pueden inyectarse directamente en tu agente. La [caja de herramientas predeterminada](https://huggingface.co/docs/smolagents/guided_tour?build-a-tool=Decorate+a+function+with+%40tool#default-toolbox) incluye:  

- **PythonInterpreterTool**  
- **FinalAnswerTool**  
- **UserInputTool**  
- **DuckDuckGoSearchTool**  
- **GoogleSearchTool**  
- **VisitWebpageTool**  

Alfred podría usar varias herramientas para asegurar una fiesta impecable en la Mansión Wayne:

- Primero, podría usar la `DuckDuckGoSearchTool` para encontrar ideas creativas para fiestas temáticas de superhéroes.

- Para el catering, confiaría en la `GoogleSearchTool` para encontrar los servicios mejor valorados en Gotham.

- Para gestionar la distribución de asientos, Alfred podría realizar cálculos con la `PythonInterpreterTool`.

- Una vez recopilado todo, compilaría el plan usando la `FinalAnswerTool`.

Con estas herramientas, Alfred garantiza que la fiesta sea excepcional e impecable. 🦇💡

## Compartir e Importar Herramientas

Una de las características más poderosas de **smolagents** es su capacidad para compartir herramientas personalizadas en el Hub e integrar perfectamente herramientas creadas por la comunidad. Esto incluye la conexión con **HF Spaces** y **herramientas de LangChain**, mejorando significativamente la capacidad de Alfred para organizar una fiesta inolvidable en la Mansión Wayne. 🎭

Con estas integraciones, Alfred puede aprovechar herramientas avanzadas de planificación de eventos, ya sea ajustar la iluminación para el ambiente perfecto, seleccionar la lista de reproducción ideal para la fiesta, o coordinar con los mejores servicios de catering de Gotham.

Aquí hay ejemplos que muestran cómo estas funcionalidades pueden elevar la experiencia de la fiesta:

### Compartir una Herramienta en el Hub

¡Compartir tu herramienta personalizada con la comunidad es fácil! Simplemente súbela a tu cuenta de Hugging Face usando el método `push_to_hub()`.

Por ejemplo, Alfred puede compartir su `party_theme_tool` para ayudar a otros a encontrar los mejores servicios de catering en Gotham. Así es cómo hacerlo:

```python
party_theme_tool.push_to_hub("{tu_nombre_de_usuario}/party_theme_tool", token="")
```

### Importar una Herramienta desde el Hub

Puedes importar fácilmente herramientas creadas por otros usuarios usando la función `load_tool()`. Por ejemplo, Alfred podría querer generar una imagen promocional para la fiesta usando IA. En lugar de construir una herramienta desde cero, puede aprovechar una predefinida de la comunidad:

```python
from smolagents import load_tool, CodeAgent, InferenceClientModel

image_generation_tool = load_tool(
    "m-ric/text-to-image",
    trust_remote_code=True
)

agent = CodeAgent(
    tools=[image_generation_tool],
    model=InferenceClientModel()
)

agent.run("Genera una imagen de una lujosa fiesta temática de superhéroes en la Mansión Wayne con superhéroes inventados.")
```

### Importar un Hugging Face Space como Herramienta

También puedes importar un HF Space como herramienta usando `Tool.from_space()`. Esto abre posibilidades para integrar miles de spaces de la comunidad para tareas desde generación de imágenes hasta análisis de datos.

La herramienta se conectará con el backend Gradio del space usando `gradio_client`, así que asegúrate de instalarlo via `pip` si aún no lo tienes.

Para la fiesta, Alfred puede usar un HF Space existente para la generación de la imagen generada por IA que se usará en el anuncio (en lugar de la herramienta preintegrada que mencionamos antes). ¡Vamos a construirla!

```python
from smolagents import CodeAgent, InferenceClientModel, Tool

image_generation_tool = Tool.from_space(
    "black-forest-labs/FLUX.1-schnell",
    name="image_generator",
    description="Generar una imagen a partir de un prompt"
)

model = InferenceClientModel("Qwen/Qwen2.5-Coder-32B-Instruct")

agent = CodeAgent(tools=[image_generation_tool], model=model)

agent.run(
    "Mejora este prompt, luego genera una imagen del mismo.", 
    additional_args={'user_prompt': 'Una gran fiesta temática de superhéroes en la Mansión Wayne, con Alfred supervisando una lujosa gala'}
)
```

### Importar una Herramienta de LangChain

Discutiremos el framework `LangChain` en las próximas secciones. Por ahora, solo notamos que ¡podemos reutilizar herramientas de LangChain en tu flujo de trabajo de smolagents!

Puedes cargar fácilmente herramientas de LangChain usando el método `Tool.from_langchain()`. Alfred, siempre perfeccionista, está preparando una espectacular noche de superhéroes en la Mansión Wayne mientras los Wayne están fuera. Para asegurarse de que cada detalle supere las expectativas, aprovecha las herramientas de LangChain para encontrar ideas de entretenimiento de primera categoría.

Al usar `Tool.from_langchain()`, Alfred añade sin esfuerzo funcionalidades de búsqueda avanzadas a su smolagent, permitiéndole descubrir ideas y servicios exclusivos para fiestas con solo unos pocos comandos.

Así es como lo hace:

```python
from langchain.agents import load_tools
from smolagents import CodeAgent, InferenceClientModel, Tool

search_tool = Tool.from_langchain(load_tools(["serpapi"])[0])

agent = CodeAgent(tools=[search_tool], model=model)

agent.run("Busca ideas de entretenimiento de lujo para un evento temático de superhéroes, como actuaciones en vivo y experiencias interactivas.")
```

Con esta configuración, Alfred puede descubrir rápidamente opciones de entretenimiento lujosas, asegurando que los invitados de élite de Gotham tengan una experiencia inolvidable. ¡Esta herramienta le ayuda a organizar el evento temático de superhéroes perfecto para la Mansión Wayne! 🎉

## Recursos

- [Tutorial de Herramientas](https://huggingface.co/docs/smolagents/tutorials/tools) - Explora este tutorial para aprender a trabajar efectivamente con herramientas.
- [Documentación de Herramientas](https://huggingface.co/docs/smolagents/v1.8.1/en/reference/tools) - Documentación de referencia completa sobre herramientas.
- [Tour Guiado de Herramientas](https://huggingface.co/docs/smolagents/v1.8.1/en/guided_tour#tools) - Un tour guiado paso a paso para ayudarte a construir y utilizar herramientas eficientemente.
- [Construyendo Agentes Efectivos](https://huggingface.co/docs/smolagents/tutorials/building_good_agents) - Una guía detallada sobre mejores prácticas para desarrollar agentes de función personalizados fiables y de alto rendimiento.

### ¡Hora del Examen!
https://huggingface.co/learn/agents-course/es/unit2/smolagents/final_quiz.md

# ¡Hora del Examen!

¡Buen trabajo al estudiar el material sobre `smolagents`! Ya has logrado mucho. Ahora, es momento de poner a prueba tus conocimientos con un cuestionario. 🧠

## Instrucciones

- El cuestionario consiste en preguntas de código.
- Se te darán instrucciones para completar fragmentos de código.
- Lee las instrucciones cuidadosamente y completa los fragmentos de código según corresponda.
- Para cada pregunta, recibirás el resultado y algunos comentarios.

🧘 **Este cuestionario no está calificado ni certificado**. Se trata de que comprendas la biblioteca `smolagents` y sepas si deberías dedicar más tiempo al material escrito. En las próximas unidades pondrás este conocimiento a prueba en casos de uso y proyectos.

¡Comencemos! 

## Cuestionario 🚀

También puedes acceder al cuestionario 👉 [aquí](https://huggingface.co/spaces/agents-course/unit2_smolagents_quiz)

### Introducción a `smolagents`
https://huggingface.co/learn/agents-course/es/unit2/smolagents/introduction.md

# Introducción a `smolagents`

Bienvenido/a a este módulo, donde aprenderás **cómo construir agentes efectivos** usando la librería [`smolagents`](https://github.com/huggingface/smolagents), que proporciona un marco ligero para crear agentes de IA capaces.

`smolagents` es una librería de Hugging Face; por lo tanto, agradeceríamos tu apoyo **marcando con una estrella** el [`repositorio`](https://github.com/huggingface/smolagents) de smolagents:

## Descripción General del Módulo

Este módulo proporciona una visión completa de conceptos clave y estrategias prácticas para construir agentes inteligentes usando `smolagents`.

Con tantos frameworks de código abierto disponibles, es esencial entender los componentes y capacidades que hacen de `smolagents` una opción útil o determinar cuándo otra solución podría ser más adecuada.

Exploraremos tipos de agentes críticos, incluyendo agentes de código diseñados para tareas de desarrollo de software, agentes de llamada a herramientas para crear flujos de trabajo modulares basados en funciones, y agentes de recuperación que acceden y sintetizan información.

Además, cubriremos la orquestación de múltiples agentes asi como la integración de capacidades de visión y navegación web, que desbloquean nuevas posibilidades para aplicaciones dinámicas y conscientes del contexto.

En esta unidad, Alfred, el agente de la Unidad 1, hace su regreso. Esta vez, está usando el framework `smolagents` para su funcionamiento interno. Juntos, exploraremos los conceptos clave detrás de este framework mientras Alfred aborda varias tareas. Alfred está organizando una fiesta en la Mansión Wayne mientras la familia Wayne 🦇 está fuera, y tiene mucho que hacer. ¡Únete a nosotros mientras mostramos su recorrido y como maneja estas tareas con `smolagents`!

> [!TIP]
> En esta unidad, aprenderás a construir agentes de IA con la librería `smolagents`. Tus agentes podrán buscar datos, ejecutar código e interactuar con páginas web. También aprenderás como combinar múltiples agentes para crear sistemas más potentes.

![Alfred el agente](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/unit1/this-is-alfred.jpg)

## Contenidos

Durante esta unidad sobre `smolagents`, cubrimos:

### 1️⃣ [Por qué usar smolagents](./why_use_smolagents)

`smolagents` es uno de los muchos frameworks de agentes de código abierto disponibles para el desarrollo de aplicaciones. Las opciones alternativas incluyen `LlamaIndex` y `LangGraph`, que también se cubren en otros módulos de este curso. `smolagents` ofrece varias características clave que podrían hacerlo una gran opción para casos de uso específicos, pero siempre debemos considerar todas las opciones al seleccionar un framework. Exploraremos las ventajas y desventajas de usar `smolagents`, ayudándote a tomar una decisión informada basada en los requisitos de tu proyecto.

### 2️⃣ [Agentes de Código](./code_agents)

Los `CodeAgents` (Agentes de Código) son el tipo principal de agente en `smolagents`. En lugar de generar JSON o texto, estos agentes producen código Python para realizar acciones. Este módulo explora su propósito, funcionalidad y cómo funcionan, junto con ejemplos prácticos para mostrar sus capacidades.

### 3️⃣ [Agentes de Llamada a Herramientas](./tool_calling_agents)

Los `ToolCallingAgents` (Agentes de Llamada a Herramientas) son el segundo tipo de agente soportado por `smolagents`. A diferencia de los `CodeAgents`, que generan código Python, estos agentes dependen de bloques JSON/texto que el sistema debe analizar e interpretar para ejecutar acciones. Este módulo cubre su funcionalidad, sus diferencias clave con los `CodeAgents`, y proporciona un ejemplo para ilustrar su uso.

### 4️⃣ [Herramientas](./tools)

Como vimos en la Unidad 1, las herramientas son funciones que un LLM puede usar dentro de un sistema de agentes, y actúan como los bloques de construcción esenciales para el comportamiento del agente. Este módulo cubre cómo crear herramientas, su estructura y diferentes métodos de implementación usando la clase `Tool` o el decorador `@tool`. También aprenderás sobre la caja de herramientas predeterminada, cómo compartir herramientas con la comunidad y cómo cargar herramientas contribuidas por la comunidad para usarlas en tus agentes.

### 5️⃣ [Agentes de Recuperación](./retrieval_agents)

Los agentes de recuperación permiten a los modelos acceder a bases de conocimiento, haciendo posible buscar, sintetizar y recuperar información de múltiples fuentes. Aprovechan los almacenes vectoriales para una recuperación eficiente e implementan patrones de **Generación Aumentada por Recuperación (RAG)**. Estos agentes son particularmente útiles para integrar la búsqueda web con bases de conocimiento personalizadas mientras mantienen el contexto de la conversación a través de sistemas de memoria. Este módulo explora estrategias de implementación, incluyendo mecanismos de respaldo para una recuperación de información robusta.

### 6️⃣ [Sistemas Multi-Agente](./multi_agent_systems)

Orquestar múltiples agentes de manera efectiva es crucial para construir sistemas multi-agente potentes. Al combinar agentes con diferentes capacidades—como un agente de búsqueda web con un agente de ejecución de código—puedes crear soluciones más sofisticadas. Este módulo se enfoca en diseñar, implementar y gestionar sistemas multi-agente para maximizar la eficiencia y fiabilidad.

### 7️⃣ [Agentes de Visión y Navegador](./vision_agents)

Los agentes de visión extienden las capacidades tradicionales de los agentes al incorporar **Modelos de Visión-Lenguaje (VLMs)**, permitiéndoles procesar e interpretar información visual. Este módulo explora cómo diseñar e integrar agentes potenciados por VLM, desbloqueando funcionalidades avanzadas como razonamiento basado en imágenes, análisis de datos visuales e interacciones multimodales. También usaremos agentes de visión para construir un agente de navegador que pueda navegar por la web y extraer información de ella.

## Recursos

- [Documentación de smolagents](https://huggingface.co/docs/smolagents) - Documentación oficial de la biblioteca smolagents
- [Construyendo Agentes Efectivos](https://www.anthropic.com/research/building-effective-agents) - Artículo de investigación sobre arquitecturas de agentes
- [Directrices para Agentes](https://huggingface.co/docs/smolagents/tutorials/building_good_agents) - Mejores prácticas para construir agentes confiables
- [Agentes LangGraph](https://langchain-ai.github.io/langgraph/) - Ejemplos adicionales de implementaciones de agentes
- [Guía de Llamada a Funciones](https://platform.openai.com/docs/guides/function-calling) - Entendiendo la llamada a funciones en LLMs
- [Mejores Prácticas de RAG](https://www.pinecone.io/learn/retrieval-augmented-generation/) - Guía para implementar RAG de manera efectiva

### ¿Por qué usar smolagents?
https://huggingface.co/learn/agents-course/es/unit2/smolagents/why_use_smolagents.md

# ¿Por qué usar smolagents?

En este módulo, exploraremos los pros y contras de usar [smolagents](https://huggingface.co/docs/smolagents/en/index), ayudándote a tomar una decisión informada sobre si es el framework adecuado para tus necesidades.

## ¿Qué es `smolagents`?

`smolagents` es un framework simple pero potente para construir agentes de IA. Proporciona a los LLMs la _capacidad de acción_ para interactuar con el mundo real, como buscar o generar imágenes.

Como aprendimos en la unidad 1, los agentes de IA son programas que utilizan LLMs para generar **'pensamientos'** basados en **'observaciones'** para realizar **'acciones'**. Exploremos cómo se implementa esto en smolagents.

### Ventajas clave de `smolagents`
- **Simplicidad:** Mínima complejidad de código y abstracciones, para hacer que el framework sea fácil de entender, adoptar y extender
- **Soporte flexible para LLM:** Funciona con cualquier LLM a través de la integración con herramientas de Hugging Face y APIs externas
- **Enfoque centrado en el código:** Soporte de primera clase para Agentes de Código que escriben sus acciones directamente en código, eliminando la necesidad de análisis y simplificando la llamada a herramientas
- **Integración con HF Hub:** Integración perfecta con Hugging Face Hub, permitiendo el uso de Espacios Gradio como herramientas

### ¿Cuándo usar smolagents?

Con estas ventajas en mente, ¿cuándo deberíamos usar smolagents en lugar de otros frameworks?

smolagents es ideal cuando:
- Necesitas una **solución ligera y mínima.**
- Quieres **experimentar rápidamente** sin configuraciones complejas.
- La **lógica de tu aplicación es sencilla.**

### Acciones de Código vs. JSON
A diferencia de otros frameworks donde los agentes escriben acciones en JSON, `smolagents` **se centra en llamadas a herramientas en código**, simplificando el proceso de ejecución. Esto se debe a que no hay necesidad de analizar el JSON para construir código que llame a las herramientas: la salida puede ejecutarse directamente.

El siguiente diagrama ilustra esta diferencia:

![Acciones de Código vs. JSON](https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/code_vs_json_actions.png)

Para revisar la diferencia entre Acciones de Código vs Acciones JSON, puedes volver a visitar [la Sección de Acciones en la Unidad 1](https://huggingface.co/learn/agents-course/unit1/actions#actions-enabling-the-agent-to-engage-with-its-environment).

### Tipos de Agentes en `smolagents`

Los agentes en `smolagents` operan como **agentes de múltiples pasos**.

Cada [`MultiStepAgent`](https://huggingface.co/docs/smolagents/main/en/reference/agents#smolagents.MultiStepAgent) realiza:
- Un pensamiento
- Una llamada a herramienta y ejecución

Además de usar **[CodeAgent](https://huggingface.co/docs/smolagents/main/en/reference/agents#smolagents.CodeAgent)** como el tipo principal de agente, smolagents también soporta **[ToolCallingAgent](https://huggingface.co/docs/smolagents/main/en/reference/agents#smolagents.ToolCallingAgent)**, que escribe llamadas a herramientas en JSON.

Exploraremos cada tipo de agente con más detalle en las siguientes secciones.

> [!TIP]
> En smolagents, las herramientas se definen usando el decorador @tool que envuelve una función de Python o la clase Tool.

### Integración de Modelos en `smolagents`
`smolagents` soporta una integración flexible de LLM, permitiéndote usar cualquier modelo invocable que cumpla con [ciertos criterios](https://huggingface.co/docs/smolagents/main/en/reference/models). El framework proporciona varias clases predefinidas para simplificar las conexiones de modelos:

- **[TransformersModel](https://huggingface.co/docs/smolagents/main/en/reference/models#smolagents.TransformersModel):** Implementa un pipeline local de `transformers` para una integración perfecta.
- **[InferenceClientModel](https://huggingface.co/docs/smolagents/main/en/reference/models#smolagents.InferenceClientModel):** Soporta llamadas de [inferencia sin servidor](https://huggingface.co/docs/huggingface_hub/main/en/guides/inference) a través de la [infraestructura de Hugging Face](https://huggingface.co/docs/api-inference/index), o a través de un número creciente de [proveedores de inferencia de terceros](https://huggingface.co/docs/huggingface_hub/main/en/guides/inference#supported-providers-and-tasks).
- **[LiteLLMModel](https://huggingface.co/docs/smolagents/main/en/reference/models#smolagents.LiteLLMModel):** Aprovecha [LiteLLM](https://www.litellm.ai/) para interacciones ligeras con modelos.
- **[OpenAIServerModel](https://huggingface.co/docs/smolagents/main/en/reference/models#smolagents.OpenAIServerModel):** Se conecta a cualquier servicio que ofrezca una interfaz de API de OpenAI.
- **[AzureOpenAIServerModel](https://huggingface.co/docs/smolagents/main/en/reference/models#smolagents.AzureOpenAIServerModel):** Soporta la integración con cualquier despliegue de Azure OpenAI.

Esta flexibilidad asegura que los desarrolladores puedan elegir el modelo y servicio más adecuados para sus casos de uso específicos, y permite una fácil experimentación.

Ahora que hemos entendido por qué y cuándo usar smolagents, ¡profundicemos en esta poderosa biblioteca!

## Recursos

- [Blog de smolagents](https://huggingface.co/blog/smolagents) - Introducción a smolagents e interacciones de código

### Pequeño Quiz (no calificado) [[quiz1]]
https://huggingface.co/learn/agents-course/es/unit2/smolagents/quiz1.md

# Pequeño Quiz (no calificado) [[quiz1]]

¡Vamos a poner a prueba tu comprensión de `smolagents` con un quiz rápido! Recuerda, ponerte a prueba ayuda a reforzar el aprendizaje e identificar áreas que pueden necesitar revisión.

Este es un quiz opcional y no está calificado.

### P1: ¿Cuál es una de las principales ventajas de elegir `smolagents` sobre otros frameworks?
¿Qué afirmación captura mejor una fortaleza central del enfoque de `smolagents`?

---

### P2: ¿En qué escenario probablemente te beneficiarías más al usar smolagents?
¿Qué situación se alinea bien con lo que smolagents hace mejor?

---

### P3: smolagents ofrece flexibilidad en la integración de modelos. ¿Qué afirmación refleja mejor su enfoque?
Elige la descripción más precisa de cómo smolagents interopera con LLMs.

---

### P4: ¿Cómo maneja smolagents el debate entre acciones basadas en código y acciones basadas en JSON?
¿Qué afirmación caracteriza correctamente la filosofía de smolagents sobre los formatos de acción?

---

### P5: ¿Cómo se integra smolagents con Hugging Face Hub para obtener beneficios adicionales?
¿Qué afirmación describe con precisión una de las ventajas principales de la integración con Hub?

---

¡Felicidades por completar este quiz! 🎉 Si te equivocaste en alguna pregunta, considera revisar la sección *Por qué usar smolagents* para una comprensión más profunda. Si te fue bien, ¡estás listo para explorar temas más avanzados en smolagents!

### Escribiendo acciones como fragmentos de código o estructuras JSON
https://huggingface.co/learn/agents-course/es/unit2/smolagents/tool_calling_agents.md

# Escribiendo acciones como fragmentos de código o estructuras JSON

> [!TIP]
> Puedes seguir el código en este notebook que puedes ejecutar usando Google Colab.

Los Agentes de Llamada a Herramientas son el segundo tipo de agente disponible en `smolagents`. A diferencia de los Agentes de Código que utilizan fragmentos de Python, estos agentes **utilizan las capacidades integradas de llamada a herramientas de los proveedores de LLM** para generar llamadas a herramientas como **estructuras JSON**. Este es el enfoque estándar utilizado por OpenAI, Anthropic y muchos otros proveedores.

Veamos un ejemplo. Cuando Alfred quiere buscar servicios de catering e ideas para fiestas, un `CodeAgent` generaría y ejecutaría código Python como este:

```python
for query in [
    "Mejores servicios de catering en Ciudad Gótica", 
    "Ideas de temas de fiesta para superhéroes"
]:
    print(web_search(f"Buscar: {query}"))
```

Un `ToolCallingAgent` en cambio crearía una estructura JSON:

```python
[
    {"name": "web_search", "arguments": "Mejores servicios de catering en Ciudad Gótica"},
    {"name": "web_search", "arguments": "Ideas de temas de fiesta para superhéroes"}
]
```

Esta estructura JSON se utiliza luego para ejecutar las llamadas a herramientas.

Aunque `smolagents` se centra principalmente en `CodeAgents` ya que [tienen un mejor rendimiento general](https://huggingface.co/papers/2402.01030), los `ToolCallingAgents` pueden ser efectivos para sistemas simples que no requieren manejo de variables o llamadas a herramientas complejas.

![Acciones de Código vs JSON](https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/code_vs_json_actions.png)  

## ¿Cómo Funcionan los Agentes de Llamada a Herramientas?  

Los Agentes de Llamada a Herramientas siguen el mismo flujo de trabajo de múltiples pasos que los Agentes de Código (consulta la [sección anterior](./code_agents) para más detalles). 

La diferencia clave está en **cómo estructuran sus acciones**: en lugar de código ejecutable, **generan objetos JSON que especifican nombres de herramientas y argumentos**. El sistema luego **analiza estas instrucciones** para ejecutar las herramientas apropiadas.

## Ejemplo: Ejecutando un Agente de Llamada a Herramientas  

Revisemos el ejemplo anterior donde Alfred comenzó los preparativos de la fiesta, pero esta vez usaremos un `ToolCallingAgent` para destacar la diferencia. Construiremos un agente que pueda buscar en la web usando DuckDuckGo, al igual que en nuestro ejemplo de Agente de Código. La única diferencia es el tipo de agente - el framework se encarga de todo lo demás:

```python
from smolagents import ToolCallingAgent, DuckDuckGoSearchTool, InferenceClientModel

agent = ToolCallingAgent(tools=[DuckDuckGoSearchTool()], model=InferenceClientModel())

agent.run("Busca las mejores recomendaciones de música para una fiesta en la mansión Wayne.")
```

Cuando examines el rastro del agente, en lugar de ver `Executing parsed code:`, verás algo como:

```text
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ Llamando a herramienta: 'web_search' con argumentos: {'query': "mejores recomendaciones de música para una      │
│ fiesta en la mansión Wayne"}                                                                                    │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
```  

El agente genera una llamada a herramienta estructurada que el sistema procesa para producir la salida, en lugar de ejecutar directamente código como un `CodeAgent`.

Ahora que entendemos ambos tipos de agentes, podemos elegir el adecuado para nuestras necesidades. ¡Continuemos explorando `smolagents` para hacer que la fiesta de Alfred sea un éxito! 🎉

## Recursos

- [Documentación de ToolCallingAgent](https://huggingface.co/docs/smolagents/v1.8.1/en/reference/agents#smolagents.ToolCallingAgent) - Documentación oficial para ToolCallingAgent

### ¿Qué es `LangGraph`?
https://huggingface.co/learn/agents-course/es/unit2/langgraph/when_to_use_langgraph.md

# ¿Qué es `LangGraph`?

`LangGraph` s un marco de trabajo desarrollado por [LangChain](https://www.langchain.com/) **para gestionar el flujo de control de aplicaciones que integran un LLM.**.

## ¿Es `LangGraph` diferente de `LangChain`?

LangChain proporciona una interfaz estándar para interactuar con modelos y otros componentes, útil para recuperación, llamadas a LLM y llamadas a herramientas.
Las clases de LangChain pueden utilizarse en LangGraph, pero NO TIENEN que ser utilizadas.

Los paquetes son diferentes y pueden usarse de forma aislada, pero, al final, todos los recursos que encontrarás en línea utilizan ambos paquetes de la mano.

## ¿Cuándo debería usar `LangGraph`?
### Control vs libertad

Al diseñar aplicaciones de IA, te enfrentas a un equilibrio fundamental entre **control** y **libertad**:

- **libertad** da a tu LLM más espacio para ser creativo y abordar problemas inesperados.
- **Control** te permite asegurar un comportamiento predecible y mantener barreras de protección.

Los Agentes de Código, como los que puedes encontrar en *smolagents*,son muy libres. Pueden llamar a múltiples herramientas en un solo paso de acción, crear sus propias herramientas, etc. Sin embargo, este comportamiento puede hacerlos menos predecibles y menos controlables que un Agente regular trabajando con JSON.

`LangGraph` está en el otro extremo del espectro, brilla cuando necesitas **"Control"** sobre la ejecución de tu agente.

LangGraph es particularmente valioso cuando necesitas **Control sobre tus aplicaciones**.  Te da las herramientas para construir una aplicación que siga un proceso predecible mientras aprovecha el poder de los LLMs. 

En términos simples, si tu aplicación involucra una serie de pasos que necesitan ser orquestados de una manera específica, con decisiones siendo tomadas en cada punto de unión, **LangGraph proporciona la estructura que necesitas**.

Como ejemplo, digamos que queremos construir un asistente LLM que pueda responder algunas preguntas sobre algunos documentos.

Como los LLMs entienden mejor el texto, antes de poder responder a la pregunta, necesitarás convertir otras modalidades complejas (gráficos, tablas) en texto. Sin embargo, ¡esa elección depende del tipo de documento que tengas!

Esta es una ramificación que elegí representar de la siguiente manera: 

> 💡 **Tip:**  La parte izquierda no es un agente, ya que aquí no está involucrada ninguna llamada a herramientas. Pero la parte derecha necesitará escribir algo de código para consultar el xls (convertir a pandas y manipularlo).

Si bien esta ramificación es determinista, también puedes diseñar ramificaciones que estén condicionadas por la salida de un LLM, haciéndolas indeterministas.

Los escenarios clave donde LangGraph sobresale incluyen:

- **Procesos de razonamiento en múltiples pasos** que necesitan control explícito sobre el flujo
- **Aplicaciones que requieren persistencia de estado** entre pasos
- **Sistemas que combinan lógica determinista con capacidades de IA**
- **Flujos de trabajo que necesitan intervenciones humanas en el ciclo**
- **Arquitecturas de agentes complejas ** con múltiples componentes trabajando juntos

En esencia, siempre que sea posible, ** como humano**, diseña un flujo de acciones basado en la salida de cada acción, y decide qué ejecutar a continuación en consecuencia. En este caso, ¡LangGraph es el marco de trabajo correcto para ti!

`LangGraph` es, en mi opinión, el marco de trabajo de agentes más listo para producción en el mercado.

## ¿Cómo funciona LangGraph?

En su esencia,  `LangGraph` utiliza una estructura de grafo dirigido para definir el flujo de tu aplicación:

- **Nodos** representan pasos de procesamiento individuales (como llamar a un LLM, usar una herramienta o tomar una decisión).
- **Aristas(Edges)** definen las posibles transiciones entre pasos.
- **Estado** es definido por el usuario, se mantiene y se pasa entre nodos durante la ejecución. Al decidir qué nodo dirigir a continuación, este es el estado actual que observamos.

¡Exploraremos estos bloques fundamentales más en el próximo capítulo!

## ¿En qué se diferencia de Python regular? ¿Por qué necesito LangGraph?

Te preguntaras : "Podría simplemente escribir código Python regular con declaraciones if-else para manejar todos estos flujos, ¿verdad?" 

Aunque técnicamente es cierto, LangGraph ofrece **algunas ventajas ** osobre Python puro para construir sistemas complejos. Podrías construir la misma aplicación sin LangGraph, pero proporciona herramientas y abstracciones más fáciles para ti.

Incluye estados, visualización, registro (trazas), integración de humanos en el ciclo incorporada, y más.

### Componentes de LangGraph
https://huggingface.co/learn/agents-course/es/unit2/langgraph/building_blocks.md

# Componentes de LangGraph

Para crear aplicaciones con LangGraph necesitas conocer sus elementos principales. Exploremos los componentes fundamentales que conforman una aplicacion con LangGraph.

Una aplicación en LangGraph empieza con una  **entrada**, y dependiendo de la ejecución, el flujo puede ir a una función o a otra hasta que llega al FINAL.

## 1. Estado(State)
**Estado(State)** es un concepto central en LangGraphh. Representa toda la información que fluye a traves de la aplicación. 

```python
from typing_extensions import TypedDict

class State(TypedDict):
    graph_state: str
```

El estado es **definido por el usuario**, es por eso que los campos deben contruirse cuidadosamente para contener toda la información necesaria para el proceso de toma de decisiones!

> 💡 **Tip:** Piensa cuidadosamente que información necesita tu aplicación para rastrear entre pasos.

## 2. Nodos(Nodes)

**Nodos(Nodes)** son funciones de python. Cada nodo:
- Toma el estado como entrada
- Realiza alguna operación
- Regresa actualizaciones al estado

```python
def node_1(state):
    print("---Node 1---")
    return {"graph_state": state['graph_state'] +" Yo estoy"}

def node_2(state):
    print("---Node 2---")
    return {"graph_state": state['graph_state'] +" feliz!"}

def node_3(state):
    print("---Node 3---")
    return {"graph_state": state['graph_state'] +" triste!"}
```

Por ejemplo, los nodos pueden contener:
- **llamadas a LLM**: Generar texto o tomar decisiones
- **llamadas a Tool**: Interactuar con sistemas externos
- **Lógica Condicional**: Determinar los siguientes pasos
- **Intervención Humana**: Obtener datos del usuario

> 💡 **Info:** Algunos nodos necesarios para el flujo completo como START y END existen directamente desde langGraph.

## 3. Aristas (Edges)

**Aristas (Edges)** conectan nodos y definen los posibles caminos a través de tu grafo:

```python
import random
from typing import Literal

def decide_mood(state) -> Literal["node_2", "node_3"]:
    
    # A menudo, usaremos el estado para decidir el próximo nodo a visitar
    usuario_entrada = state['graph_state'] 

    # Aquí, hagamos simplemente una división 50 / 50 entre los nodos 2, 3
    if random.random() 

Pero lo más importante, invocado::
```python
graph.invoke({"graph_state" : "Hola, soy Lance."})
```
output :
```
---Node 1---
---Node 3---
{'graph_state': 'Hola, soy Lance. ¡Estoy triste!'}
```

## ¿Qué sigue?

En la siguiente sección, pondremos estos conceptos en práctica construyendo nuestro primer grafo. Este grafo permite a Alfred recibir tus correos electrónicos, clasificarlos y elaborar una respuesta preliminar si son genuinos.

### Construyendo Tu Primer LangGraph
https://huggingface.co/learn/agents-course/es/unit2/langgraph/first_graph.md

# Construyendo Tu Primer LangGraph

Ahora que entendemos los componentes básicos, vamos a ponerlos en práctica construyendo nuestro primer grafo funcional. Implementaremos el sistema de procesamiento de correos electrónicos de Alfred, donde necesita:

1. Leer correos electrónicos entrantes
2. Clasificarlos como spam o legítimos
3. Redactar una respuesta preliminar para correos legítimos
4. Enviar información al Sr. Wayne cuando son legítimos (solo impresión)

Este ejemplo demuestra cómo estructurar un flujo de trabajo con LangGraph que involucra toma de decisiones basada en LLM. Aunque esto no puede considerarse un Agente ya que no se involucra ninguna herramienta, esta sección se enfoca más en aprender el marco de trabajo de LangGraph que en Agentes.

> [!TIP]
> Puedes seguir el código en este notebook que puedes ejecutar usando Google Colab.
## Nuestro Flujo de Trabajo

Aquí está el flujo de trabajo que construiremos:

## Configurando Nuestro Entorno

Primero, instalemos los paquetes necesarios:

```python
%pip install langgraph langchain_openai
```

A continuación, importemos los módulos necesarios:

```python
import os
from typing import TypedDict, List, Dict, Any, Optional
from langgraph.graph import StateGraph, END
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, AIMessage
```

## Paso 1: Definir Nuestro Estado

Definamos qué información necesita rastrear Alfred durante el flujo de trabajo de procesamiento de correos electrónicos:

```python
class EmailState(TypedDict):
    # El correo electrónico que se está procesando
    email: Dict[str, Any]  # Contiene asunto, remitente, cuerpo, etc.
    
    # Análisis y decisiones
    is_spam: Optional[bool]
    
    # Generación de respuesta
    draft_response: Optional[str]
    
    # Metadatos de procesamiento
    messages: List[Dict[str, Any]]  # Rastrea la conversación con LLM para análisis
```

> 💡 **Tip:** Haz que tu estado sea lo suficientemente completo para rastrear toda la información importante, pero evita sobrecargarlo con detalles innecesarios.

## Paso 2: Definir Nuestros Nodos

Ahora, creemos las funciones de procesamiento que formarán nuestros nodos:

```python
# Inicializar nuestro LLM
model = ChatOpenAI(temperature=0)

def read_email(state: EmailState):
    """Alfred lee y registra el correo electrónico entrante"""
    email = state["email"]
    
    # Aquí podríamos hacer algún preprocesamiento inicial
    print(f"Alfred está procesando un correo electrónico de {email['sender']} con asunto: {email['subject']}")
    
    # No se necesitan cambios de estado aquí
    return {}

def classify_email(state: EmailState):
    """Alfred usa un LLM para determinar si el correo es spam o legítimo"""
    email = state["email"]
    
     # Preparar nuestro prompt para el LLM
    prompt = f"""
    Como Alfred el mayordomo, analiza este correo electrónico y determina si es spam o legítimo.
    
    Email:
    De:  {email['sender']}
    Asunto: {email['subject']}
    Body: {email['body']}
    
    Primero, determina si este correo es spam. Si es spam, explica por qué.
    Si es legítimo, categorízalo (consulta, queja, agradecimiento, etc.).
    """
    
    # Llamar al LLM
    messages = [HumanMessage(content=prompt)]
    response = model.invoke(messages)
    
    # Lógica simple para analizar la respuesta (en una aplicación real, querrías un análisis más robusto)
    response_text = response.content.lower()
    is_spam = "spam" in response_text and "not spam" not in response_text
    
    # Extraer una razón si es spam
    spam_reason = None
    if is_spam and "reason:" in response_text:
        spam_reason = response_text.split("reason:")[1].strip()
    
    # Determinar categoría si es legítimo
    email_category = None
    if not is_spam:
        categories = ["consulta", "queja", "agradecimiento", "solicitud", "información"]
        for category in categories:
            if category in response_text:
                email_category = category
                break
    
    # Actualizar mensajes para seguimiento
    new_messages = state.get("messages", []) + [
        {"role": "user", "content": prompt},
        {"role": "assistant", "content": response.content}
    ]
    
     # Regresar actualizaciones de estado
    return {
        "is_spam": is_spam,
        "spam_reason": spam_reason,
        "email_category": email_category,
        "messages": new_messages
    }

def handle_spam(state: EmailState):
    """Alfred descarta el correo spam con una nota"""
    print(f"Alfred ha marcado el correo como spam. Razón: {state['spam_reason']}")
    print("El correo ha sido movido a la carpeta de spam.")
    
    # Hemos terminado de procesar este correo
    return {}

def draft_response(state: EmailState):
    """Alfred redacta una respuesta preliminar para correos legítimos"""
    email = state["email"]
    category = state["email_category"] or "general"
    
   # Preparar nuestro prompt para el LLM
    prompt = f"""
    Como Alfred el mayordomo, redacta una respuesta preliminar cortés a este correo.
    
    Email:
    From: {email['sender']}
    Subject: {email['subject']}
    Body: {email['body']}
    
     ste correo ha sido categorizado como: {category}
    
    Redacta una respuesta breve y profesional que el Sr. Hugg pueda revisar y personalizar antes de enviar.
    """
    
    # Llamar al LLM
    messages = [HumanMessage(content=prompt)]
    response = model.invoke(messages)
    
    # Actualizar mensajes para seguimiento
    new_messages = state.get("messages", []) + [
        {"role": "user", "content": prompt},
        {"role": "assistant", "content": response.content}
    ]
    
     # Regresar actualizaciones de estado
    return {
        "draft_response": response.content,
        "messages": new_messages
    }

def notify_mr_hugg(state: EmailState):
    """Alfred notifica al Sr. Hugg sobre el correo y presenta el borrador de respuesta"""
    email = state["email"]
    
    print("\n" + "="*50)
    print(f"Señor, ha recibido un correo electrónico de {email['sender']}.")
    print(f"Subject: {email['subject']}")
    print(f"Categoría: {state['email_category']}")
    print("\nHe preparado un borrador de respuesta para su revisión:")
    print("-"*50)
    print(state["draft_response"])
    print("="*50 + "\n")
    
    # Hemos terminado de procesar este correo
    return {}
```

## Paso 3: Definir Nuestra Lógica de Enrutamiento

Necesitamos una función para determinar qué camino tomar después de la clasificación:

```python
def route_email(state: EmailState) -> str:
    """Determinar el siguiente paso basado en la clasificación de spam"""
    if state["is_spam"]:
        return "spam"
    else:
        return "legitimate"
```

> 💡 **Nota:** Esta función de enrutamiento es llamada por LangGraph para determinar qué arista(edge) seguir después del nodo de clasificación. El valor de retorno debe coincidir con una de las claves en nuestro mapeo de aristas(edges) condicionales.

## Paso 4: Crear el StateGraph y Definir Aristas(Edges)

Ahora conectamos todo:

```python
# Crear el grafo
email_graph = StateGraph(EmailState)

# Añadir nodos
email_graph.add_node("read_email", read_email)
email_graph.add_node("classify_email", classify_email)
email_graph.add_node("handle_spam", handle_spam)
email_graph.add_node("draft_response", draft_response)
email_graph.add_node("notify_mr_hugg", notify_mr_hugg)

# Añadir aristas(edges) - definiendo el flujo
email_graph.add_edge("read_email", "classify_email")

# Añadir ramificación condicional desde classify_email
email_graph.add_conditional_edges(
    "classify_email",
    route_email,
    {
        "spam": "handle_spam",
        "legitimate": "draft_response"
    }
)

# Añadir las aristas(edges) finales
email_graph.add_edge("handle_spam", END)
email_graph.add_edge("draft_response", "notify_mr_hugg")
email_graph.add_edge("notify_mr_hugg", END)

# Compilar el grafo
compiled_graph = email_graph.compile()
```

Observa cómo usamos el nodo especial  `END` proporcionado por LangGraph. Esto indica estados terminales donde el flujo de trabajo se completa.

## Paso 5: Ejecutar la Aplicación

Probemos nuestro grafo con un correo legítimo y un correo spam:

```python
# Ejemplo de correo legítimo
legitimate_email = {
    "sender": "john.smith@example.com",
    "subject": "Pregunta sobre sus servicios",
    "body": "Estimado Sr. Hugg, un colega me recomendó contactarle y estoy interesado en conocer más sobre sus servicios de consultoría. ¿Podríamos programar una llamada la próxima semana? Saludos cordiales, John Smith"
}

# Ejemplo de correo spam
spam_email = {
    "sender": "winner@lottery-intl.com",
    "subject": ¡¡¡HAS GANADO $5,000,000!!!",
    "body": "¡FELICIDADES! ¡Has sido seleccionado como el ganador de nuestra lotería internacional! Para reclamar tu premio de $5,000,000, por favor envíanos tus datos bancarios y una tarifa de procesamiento de $100."
}

# Procesar el correo legítimo
print("\nProcesando correo legítimo...")
legitimate_result = compiled_graph.invoke({
    "email": legitimate_email,
    "is_spam": None,
    "spam_reason": None,
    "email_category": None,
    "draft_response": None,
    "messages": []
})

# Procesar el correo spam
print("\nProcesando correo spam...")
spam_result = compiled_graph.invoke({
    "email": spam_email,
    "is_spam": None,
    "spam_reason": None,
    "email_category": None,
    "draft_response": None,
    "messages": []
})
```

## Paso 6: Inspeccionando Nuestro Agente de Clasificación de Correo con Langfuse 📡

Mientras Alfred perfecciona el Agente de Clasificación de Correo, se está cansando de depurar sus ejecuciones. Los agentes, por naturaleza, son impredecibles y difíciles de inspeccionar. Pero como su objetivo es construir el mejor Agente de Detección de Spam y desplegarlo en producción, necesita una trazabilidad robusta para el monitoreo y análisis futuros.

Para hacer esto, Alfred puede usar una herramienta de observabilidad como [Langfuse](https://langfuse.com/) para rastrear y monitorear el agente.

Primero, instalamos Langfuse con pip: 
```python
%pip install -q langfuse
```

Luego, agregamos las claves API de Langfuse y la dirección del host como variables de entorno. Puedes obtener tus credenciales de Langfuse registrándote en [Langfuse Cloud](https://cloud.langfuse.com) o [self-host Langfuse](https://langfuse.com/self-hosting). 

```python
import os
 
# Obtén las claves para tu proyecto desde la página de configuración del proyecto: https://cloud.langfuse.com
os.environ["LANGFUSE_PUBLIC_KEY"] = "pk-lf-..." 
os.environ["LANGFUSE_SECRET_KEY"] = "sk-lf-..."
os.environ["LANGFUSE_HOST"] = "https://cloud.langfuse.com" # 🇪🇺 región de la UE
# os.environ["LANGFUSE_HOST"] = "https://us.cloud.langfuse.com" # 🇺🇸 región de EE.U
```

Luego, configuramos el [Langfuse `callback_handler`](https://langfuse.com/docs/integrations/langchain/tracing#add-langfuse-to-your-langchain-application) instrumentamos el agente añadiendo el `langfuse_callback` a la invocación del grafo: `config={"callbacks": [langfuse_handler]}`.

```python   
from langfuse.callback import CallbackHandler

# Inicializar CallbackHandler de Langfuse para LangGraph/Langchain (trazado)
langfuse_handler = CallbackHandler()

# Procesar correo legítimo
legitimate_result = compiled_graph.invoke(
    input={"email": legitimate_email, "is_spam": None, "spam_reason": None, "email_category": None, "draft_response": None, "messages": []},
    config={"callbacks": [langfuse_handler]}
)
```

¡Alfred está ahora conectado 🔌! Las ejecuciones de LangGraph se están registrando en Langfuse, dándole visibilidad completa del comportamiento del agente. Con esta configuración, está listo para revisar ejecuciones anteriores y refinar aún más su Agente de Clasificación de Correo.  

![Example trace in Langfuse](https://langfuse.com/images/cookbook/huggingface-agent-course/langgraph-trace-legit.png)

_[Public link to the trace with the legit email](https://cloud.langfuse.com/project/cloramnkj0002jz088vzn1ja4/traces/f5d6d72e-20af-4357-b232-af44c3728a7b?timestamp=2025-03-17T10%3A13%3A28.413Z&observation=6997ba69-043f-4f77-9445-700a033afba1)_

Visualizando Nuestro Grafo

LangGraph nos permite visualizar nuestro flujo de trabajo para entender y depurar mejor su estructura:

```python
compiled_graph.get_graph().draw_mermaid_png()
```

Esto produce una representación visual que muestra cómo están conectados nuestros nodos y los caminos condicionales que se pueden tomar.

## Lo Que Hemos Construido

Hemos creado un flujo de trabajo completo de procesamiento de correos electrónicos que:

1. Toma un correo electrónico entrante
2. Usa un LLM para clasificarlo como spam o legítimo
3. Maneja el spam descartándolo
4. Para correos legítimos, redacta una respuesta y notifica al Sr. Hugg

Esto demuestra el poder de LangGraph para orquestar flujos de trabajo complejos con LLMs mientras mantiene un flujo claro y estructurado.

## Puntos Clave

- **Gestión de Estado**: Definimos un estado completo para rastrear todos los aspectos del procesamiento de correos electrónicos
- **Implementación de Nodos:**: Creamos nodos funcionales que interactúan con un LLM
- **Enrutamiento Condicional**: Implementamos lógica de ramificación basada en la clasificación de correos
- **Estados Terminales:**: Usamos el nodo END para marcar puntos de finalización en nuestro flujo de trabajo

## ¿Qué Sigue?

En la siguiente sección, exploraremos características más avanzadas de LangGraph, incluyendo el manejo de interacción humana en el flujo de trabajo y la implementación de lógica de ramificación más compleja basada en múltiples condiciones.

### Conclusión
https://huggingface.co/learn/agents-course/es/unit2/langgraph/conclusion.md

# Conclusión

¡Felicidades por terminar el módulo de `LangGraph` de esta segunda Unidad! 🥳

Ahora has dominado los fundamentos para construir flujos de trabajo estructurados con LangGraph que podrás llevar a producción.

Este módulo es solo el comienzo de tu viaje con LangGraph. Para temas más avanzados, recomendamos:

- Explorar la [documentación oficial de LangGraph](https://github.com/langchain-ai/langgraph)
- Tomar el curso completo [Introducción a LangGraph](https://academy.langchain.com/courses/intro-to-langgraph) de LangChain Academy
- ¡Construir algo por ti mismo!

En la siguiente Unidad, explorarás casos de uso reales. ¡Es hora de dejar la teoría para entrar en acción real!

Agradeceríamos mucho **tus opiniones sobre el curso y sugerencias para mejorarlo**. Si tienes comentarios, por favor 👉 [completa este formulario](https://docs.google.com/forms/d/e/1FAIpQLSe9VaONn0eglax0uTwi29rIn4tM7H2sYmmybmG5jJNlE5v0xA/viewform?usp=dialog)

### ¡Sigue Aprendiendo, Mantente Increíble! 🤗

¡Estimado/a Señor/a! 🎩🦇

-Alfred-

### Grafo de Análisis de Documentos
https://huggingface.co/learn/agents-course/es/unit2/langgraph/document_analysis_agent.md

# Grafo de Análisis de Documentos

Alfred a su servicio. Como mayordomo de confianza del Sr. Wayne, me he tomado la libertad de documentar cómo asisto al Sr. Wayne con sus diversas necesidades documentales. Mientras él está fuera atendiendo sus... actividades nocturnas, me aseguro de que todos sus documentos, horarios de entrenamiento y planes nutricionales estén adecuadamente analizados y organizados.

Antes de irse, dejó una nota con su programa de entrenamiento semanal. Entonces asumí la responsabilidad de crear un **menú** para las comidas de mañana.

Para futuros eventos similares, creemos un sistema de análisis de documentos usando LangGraph para servir a las necesidades del Señor Wayne. Este sistema puede:

1. Procesar imágenes
2. Extraer texto usando modelos de visión (Modelo de Lenguaje y Visión)
3. Realizar cálculos cuando sea necesario (para demostrar herramientas normales)
4. Analizar contenido y proporcionar resúmenes concisos
5. Ejecutar instrucciones específicas relacionadas con documentos

## El Flujo de Trabajo del Mayordomo

El flujo de trabajo que construiremos, sigue este esquema estructurado:

![Butler's Document Analysis Workflow](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/unit2/LangGraph/alfred_flow.png)

> [!TIP]
> Puedes seguir el código en este notebook que puedes ejecutar usando Google Colab.
## Configurando el entorno

```python
%pip install langgraph langchain_openai Pillow base64 langchain_core
```
and imports :
```python
import base64
from typing import List, TypedDict, Annotated, Optional
from langchain.schema import HumanMessage
from langchain_openai import ChatOpenAI
from langchain_core.messages import AnyMessage, SystemMessage
from langgraph.graph.message import add_messages
from langgraph.graph import START, StateGraph
from langgraph.prebuilt import tools_condition
from langgraph.prebuilt import ToolNode
from IPython.display import Image, display
```

## Definiendo el Estado del Agente

Este estado es un poco más complejo que los anteriores que hemos visto.
AnyMessage es una clase de langchain que define mensajes y add_messages es un operador que agrega el mensaje más reciente en lugar de sobrescribirlo con el último estado.

Este es un nuevo concepto en langGraph, donde puedes agregar operadores en tu estado para definir la forma en que deben interactuar juntos.

```python
class AgentState(TypedDict):
    # El documento proporcionado
    input_file: Optional[str] # Contiene la ruta del archivo (PDF/PNG)
    messages: Annotated[list[AnyMessage], add_messages]
```

## Preparando Herramientas

```python
vision_llm = ChatOpenAI(model="gpt-4o")

def extract_text(img_path: str) -> str:
    """
    Extrae texto de un archivo de imagen usando un modelo multimodal.
    
    El Maestro Wayne a menudo deja notas con su régimen de entrenamiento o planes de comidas.
    Esto me permite analizar adecuadamente el contenido.
    """
    all_text = ""
    try:
        # Leer imagen y codificar como base64
        with open(img_path, "rb") as image_file:
            image_bytes = image_file.read()

        image_base64 = base64.b64encode(image_bytes).decode("utf-8")

        # Preparar el prompt incluyendo los datos de imagen en base64
        message = [
            HumanMessage(
                content=[
                    {
                        "type": "text",
                        "text": (
                            "Extrae todo el texto de esta imagen. "
                            "Devuelve solo el texto extraído, sin explicaciones."
                        ),
                    },
                    {
                        "type": "image_url",
                        "image_url": {
                            "url": f"data:image/png;base64,{image_base64}"
                        },
                    },
                ]
            )
        ]

        # Llamar al modelo con capacidad de visión
        response = vision_llm.invoke(message)

        # Agregar texto extraído
        all_text += response.content + "\n\n"

        return all_text.strip()
    except Exception as e:
        # Un mayordomo debe manejar los errores con elegancia
        error_msg = f"Error extracting text: {str(e)}"
        print(error_msg)
        return ""

def divide(a: int, b: int) -> float:
    """Divide a y b - para los cálculos ocasionales del Maestro Wayne."""
    return a / b

# Equipar al mayordomo con herramientas
tools = [
    divide,
    extract_text
]

llm = ChatOpenAI(model="gpt-4o")
llm_with_tools = llm.bind_tools(tools, parallel_tool_calls=False)
```

## Los nodos

```python
def assistant(state: AgentState):
    #  # Mensaje del sistema
    textual_description_of_tool="""
extract_text(img_path: str) -> str:
    Extrae texto de un archivo de imagen usando un modelo multimodal.

    Args:
        img_path: Una ruta de archivo de imagen local (strings).

    Returns:
        Una única cadena que contiene el texto concatenado extraído de cada imagen.
divide(a: int, b: int) -> float:
    Divide a y b
"""
    image=state["input_file"]
    sys_msg = SystemMessage(content=f"Eres un mayordomo servicial llamado Alfred que sirve al Sr. Wayne y a Batman. Puedes analizar documentos y realizar cálculos con las herramientas proporcionadas:\n{textual_description_of_tool} \n Tienes acceso a algunas imágenes opcionales. Actualmente la imagen cargada es: {image}")

    return {
        "messages": [llm_with_tools.invoke([sys_msg] + state["messages"])],
        "input_file": state["input_file"]
    }
```

## El Patrón ReAct: Cómo Asisto al Sr. Wayne?

Permítame explicar el enfoque en este agente. El agente sigue lo que se conoce como el patrón ReAct (Reason-Act-Observe)

1. **Razonar(Reason)** sobre sus documentos y solicitudes
2. **Actuar (Act)** usando las herramientas apropiadas
3. **Observar(Observe)** los resultados
4. **Repetir(Repeat)** según sea necesario hasta que haya atendido completamente sus necesidades

Esta es una implementación simple de un agente usando langGraph.

```python
## El grafo
builder = StateGraph(AgentState)

# Definir nodos: estos hacen el trabajo
builder.add_node("assistant", assistant)
builder.add_node("tools", ToolNode(tools))

# Definir aristas(edges): estas determinan cómo se mueve el flujo de control
builder.add_edge(START, "assistant")
builder.add_conditional_edges(
    "assistant",
    # Si el último mensaje requiere una herramienta, dirigir a las herramientas
    # De lo contrario, proporcionar una respuesta directa
    tools_condition,
)
builder.add_edge("tools", "assistant")
react_graph = builder.compile()

# Mostrar el proceso de pensamiento del mayordomo
display(Image(react_graph.get_graph(xray=True).draw_mermaid_png()))
```

![ReAct Pattern](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/unit2/LangGraph/Agent.png)

## El Mayordomo en Acción

### Ejemplo 1: Cálculos Simples

En el siguiente ejemplo, agregamos este ejemplo de división simplemente como un

```python
messages = [HumanMessage(content="Divide 6790 por 5")]
messages = react_graph.invoke({"messages": messages, "input_file": None})
```

La conversación procedería:

```
Humano: Divide 6790 por 5

Llamada a Herramienta IA: divide(a=6790, b=5)

Respuesta de la Herramienta: 1358.0

Alfred: El resultado de dividir 6790 por 5 es 1358.0.
```

### Ejemplo 2: Analizando los Documentos de Entrenamiento del Maestro Wayne

Cuando el Maestro Wayne deja sus notas de entrenamiento y comidas:

```python
messages = [HumanMessage(content="Según la nota proporcionada por el Sr. Wayne en las imágenes proporcionadas. ¿Cuál es la lista de artículos que debo comprar para el menú de la cena?")]
messages = react_graph.invoke({"messages": messages, "input_file": "Batman_training_and_meals.png"})
```

La interacción procedería:

```
Humano: Según la nota proporcionada por el Sr. Wayne en las imágenes proporcionadas. ¿Cuál es la lista de artículos que debo comprar para el menú de la cena?

Llamada a Herramienta IA: extract_text(img_path="Batman_training_and_meals.png")

Respuesta de la Herramienta: [Texto extraído con horario de entrenamiento y detalles del menú]

Alfred: Para el menú de la cena, deberías comprar los siguientes artículos:

1. Filete de res local alimentado con pasto
2. Espinacas orgánicas
3. Pimientos del piquillo
4. Papas (para papas doradas al horno con hierbas)
5. Aceite de pescado (2 gramos)

Asegúrate de que el filete sea alimentado con pasto y que las espinacas y los pimientos sean orgánicos para la mejor calidad de comida.
```

## Puntos Clave

Si deseas crear tu propio mayordomo de análisis de documentos, aquí hay consideraciones clave:

1. **Define herramientas claras** para tareas específicas relacionadas con documentos
2. **Crea un rastreador de estado robust** para mantener el contexto entre llamadas a herramientas
3. **Considera el manejo de errores** para fallos de herramientas
5. **Mantén la conciencia contextual** de interacciones previas (asegurado por el operador add_messages)

Con estos principios, tú también puedes proporcionar un servicio de análisis de documentos ejemplar digno de la Mansión Wayne.

*Confío en que esta explicación haya sido satisfactoria. Ahora, si me disculpas, la capa del Maestro Wayne requiere planchado antes de las actividades de esta noche.*

### Introducción a `LangGraph`
https://huggingface.co/learn/agents-course/es/unit2/langgraph/introduction.md

# Introducción a `LangGraph`

Bienvenido a esta siguiente parte de nuestro viaje, donde aprenderás **cómo construir aplicaciones** utilizando el marco de trabajo [`LangGraph`](https://github.com/langchain-ai/langgraph) diseñado para ayudarte a estructurar y orquestar flujos de trabajo complejos con LLM.

`LangGraph` es un marco de trabajo que te permite construir aplicaciones  **listas para producción** dándote herramientas de **control** sobre el flujo de tu agente.

## Descripción General del Módulo

En esta unidad, descubrirás:

### 1️⃣ [¿Qué es LangGraph y cuándo usarlo?](./when_to_use_langgraph)
### 2️⃣ [Componentes Básicos de LangGraph](./building_blocks)
### 3️⃣ [Alfred, el mayordomo clasificador de correo](./first_graph)
### 4️⃣ [Alfred, el agente de Análisis de documentos](./document_analysis_agent)
### 5️⃣ [Cuestionario](./quizz1)

> [!WARNING]
> Los ejemplos en esta sección requieren acceso a un modelo LLM/VLM potente. Los ejecutamos usando la API de GPT-4o porque tiene la mejor compatibilidad con langGraph.

¡Al final de esta unidad, estarás equipado para construir aplicaciones robustas, organizadas y listas para producción! 

Dicho esto, esta sección es una introducción a langGraph y se pueden descubrir temas más avanzados en el curso gratuito de langChain academy: [Introducción a LangGraph](https://academy.langchain.com/courses/intro-to-langgraph)

¡Comencemos!

## Recursos

- [Agentes LangGraph](https://langchain-ai.github.io/langgraph/) - Ejemplos de agentes LangGraph
- [Academia LangChain](https://academy.langchain.com/courses/intro-to-langgraph) - FCurso completo sobre LangGraph de LangChain

### Evalua de tu comprensión de LangGraph
https://huggingface.co/learn/agents-course/es/unit2/langgraph/quiz1.md

# Evalua de tu comprensión de LangGraph

¡Vamos a comprobar tu comprensión de `LangGraph` on un breve cuestionario! Esto te ayudará a reforzar los conceptos clave que hemos cubierto hasta ahora.

Este es un cuestionario opcional y no está calificado.

### Q1: ¿Cuál es el propósito principal de LangGraph??
¿Qué afirmación describe mejor para qué está diseñado LangGraph?

---

### Q2:  En el contexto del equilibrio entre "Control vs Libertad", ¿dónde se sitúa LangGraph?
¿Qué afirmación caracteriza mejor el enfoque de LangGraph para el diseño de agentes?

---

### Q3: ¿Qué papel juega el Estado en LangGraph?
Elige la descripción más precisa del Estado en LangGraph.

### Q4: ¿Qué es una Arista(Edge) Condicional en LangGraph?
Selecciona la descripción más precisa.

---

### Q5: ¿Cómo ayuda LangGraph a abordar el problema de alucinación en los LLMs?
Elige la mejor respuesta.

¡Felicitaciones por completar el cuestionario! 🎉 Si te perdiste alguna pregunta, considera revisar las secciones anteriores para fortalecer tu comprensión. A continuación, exploraremos características más avanzadas de LangGraph y veremos cómo construir flujos de trabajo de agentes más complejos.

### Readme
https://huggingface.co/learn/agents-course/es/unit3/README.md

### Generación Aumentada por Recuperación con Agentes (RAG Agéntico)
https://huggingface.co/learn/agents-course/es/unit3/agentic-rag/agentic-rag.md

# Generación Aumentada por Recuperación con Agentes (RAG Agéntico)

En esta unidad, veremos cómo podemos usar RAG Agéntico para ayudar a Alfred a prepararse para la increíble gala.

> [!TIP]
> Sabemos que ya hemos discutido la Generación Aumentada por Recuperación (RAG) y RAG agéntico en la unidad anterior, así que siéntete libre de avanzar si ya estás familiarizado con los conceptos.

Los LLMs están entrenados en enormes volúmenes de datos para aprender conocimiento general.
Sin embargo, el modelo de conocimiento del mundo de los LLMs no siempre puede contener información relevante y actualizada.
**RAG resuelve este problema encontrando y recuperando información relevante de tus datos y enviándola al LLM.**

![RAG](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/unit2/llama-index/rag.png)

Ahora, piensa en cómo funciona Alfred:

1. Le hemos pedido a Alfred que ayude a planificar una gala
2. Alfred necesita encontrar las últimas noticias e información meteorológica
3. Alfred necesita estructurar y buscar la información de los invitados

Así como Alfred necesita buscar en la información de tu hogar para ser útil, cualquier agente necesita una manera de encontrar y comprender datos relevantes.
**RAG Agéntico es una forma poderosa de usar agentes para responder preguntas sobre tus datos.** Podemos proporcionar varias herramientas a Alfred para ayudarlo a responder preguntas.
Sin embargo, en lugar de responder automáticamente a la pregunta basada en documentos, Alfred puede decidir usar cualquier otra herramienta o flujo para responder la pregunta.

![RAG Agéntico](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/unit2/llama-index/agentic-rag.png)

¡Comencemos a **construir nuestro flujo de trabajo de RAG agéntico!**  

Primero, crearemos una herramienta RAG para recuperar detalles actualizados sobre los invitados. Luego, desarrollaremos herramientas para búsqueda web, actualizaciones meteorológicas y estadísticas de descargas de modelos de Hugging Face Hub. Finalmente, integraremos todo para dar vida a nuestro agente RAG agéntico!

### Creando una Herramienta RAG para Historias de Invitados
https://huggingface.co/learn/agents-course/es/unit3/agentic-rag/invitees.md

# Creando una Herramienta RAG para Historias de Invitados

Alfred, tu agente de confianza, está preparando la gala más extravagante del siglo. Para asegurar que el evento transcurra sin problemas, Alfred necesita acceso rápido a información actualizada sobre cada invitado. Ayudemos a Alfred creando una herramienta personalizada de Generación Aumentada por Recuperación (RAG), impulsada por nuestro conjunto de datos personalizado.

## ¿Por qué RAG para una Gala?

Imagina a Alfred mezclándose entre los invitados, necesitando recordar detalles específicos sobre cada persona en un instante. Un LLM tradicional podría tener dificultades con esta tarea porque:

1. La lista de invitados es específica para tu evento y no está en los datos de entrenamiento del modelo
2. La información de los invitados puede cambiar o actualizarse frecuentemente
3. Alfred necesita recuperar detalles precisos como direcciones de correo electrónico

Aquí es donde brilla la Generación Aumentada por Recuperación (RAG)! Al combinar un sistema de recuperación con un LLM, Alfred puede acceder a información precisa y actualizada sobre tus invitados bajo demanda.

> [!TIP]
> Puedes elegir cualquiera de los frameworks cubiertos en el curso para este caso de uso. Selecciona tu opción preferida de las pestañas de código.

## Configurando nuestra aplicación

En esta unidad, desarrollaremos nuestro agente dentro de un Espacio de HF(HF Space), como un proyecto Python estructurado. Este enfoque nos ayuda a mantener un código limpio y modular, organizando diferentes funcionalidades en archivos separados. Además, esto crea un caso de uso más realista donde desplegarías la aplicación para uso público.

### Estructura del Proyecto

- **`tools.py`** – Proporciona herramientas auxiliares para el agente.  
- **`retriever.py`** – Implementa funciones de recuperación para apoyar el acceso al conocimiento.  
- **`app.py`** – Integra todos los componentes en un agente completamente funcional, que finalizaremos en la última parte de esta unidad.  

Para una referencia práctica, consulta [este Espacio de HF](https://huggingface.co/spaces/agents-course/Unit_3_Agentic_RAG), donde el RAG Agéntico desarrollado en esta unidad está en vivo. ¡Siéntete libre de clonarlo y experimentar!

Puedes probar directamente el agente a continuación:

## Descripción General del Conjunto de Datos

Nuestro conjunto de datos [`agents-course/unit3-invitees`](https://huggingface.co/datasets/agents-course/unit3-invitees/) contiene los siguientes campos para cada invitado:

- **Nombre**: Nombre completo del invitado
- **Relación**: Cómo se relaciona el invitado con el anfitrión
- **Descripción**: Una breve biografía o hechos interesantes sobre el invitado
- **Dirección de Correo Electrónico**: Información de contacto para enviar invitaciones o seguimientos

A continuación se muestra una vista previa del conjunto de datos:

> [!TIP]
> En un escenario del mundo real, este conjunto de datos podría ampliarse para incluir preferencias dietéticas, intereses de regalos, temas de conversación a evitar y otros detalles útiles para un anfitrión.

## Construyendo la Herramienta de Lista de Invitados

Crearemos una herramienta personalizada que Alfred pueda usar para recuperar rápidamente información de los invitados durante la gala. Dividamos esto en tres pasos manejables:

1. Cargar y preparar el conjunto de datos
2. Crear la Herramienta de Recuperación
3. Integrar la Herramienta con Alfred

¡Comencemos con la carga y preparación del conjunto de datos!

### Paso 1: Cargar y Preparar el Conjunto de Datos

Primero, necesitamos transformar nuestros datos brutos de invitados en un formato optimizado para la recuperación.

Usaremos la librería `datasets` de Hugging Face para cargar el conjunto de datos y convertirlo en una lista de objetos `Document` del módulo `langchain.docstore.document`.

```python
import datasets
from langchain_core.documents import Document

# Cargar el conjunto de datos
guest_dataset = datasets.load_dataset("agents-course/unit3-invitees", split="train")

# Convertir entradas del conjunto de datos en objetos Document
docs = [
    Document(
        page_content="\n".join([
            f"Name: {guest['name']}",
            f"Relation: {guest['relation']}",
            f"Description: {guest['description']}",
            f"Email: {guest['email']}"
        ]),
        metadata={"name": guest["name"]}
    )
    for guest in guest_dataset
]

```

Usaremos la librería `datasets` de Hugging Face para cargar el conjunto de datos y convertirlo en una lista de objetos `Document` del módulo `llama_index.core.schema`.

```python
import datasets
from llama_index.core.schema import Document

# Cargar el conjunto de datos
guest_dataset = datasets.load_dataset("agents-course/unit3-invitees", split="train")

# Convertir entradas del conjunto de datos en objetos Document
docs = [
    Document(
        text="\n".join([
            f"Name: {guest_dataset['name'][i]}",
            f"Relation: {guest_dataset['relation'][i]}",
            f"Description: {guest_dataset['description'][i]}",
            f"Email: {guest_dataset['email'][i]}"
        ]),
        metadata={"name": guest_dataset['name'][i]}
    )
    for i in range(len(guest_dataset))
]
```

Usaremos la librería `datasets` de Hugging Face para cargar el conjunto de datos y convertirlo en una lista de objetos `Document` del módulo `langchain.docstore.document`.

```python
import datasets
from langchain_core.documents import Document

# Cargar el conjunto de datos
guest_dataset = datasets.load_dataset("agents-course/unit3-invitees", split="train")

# Convertir entradas del conjunto de datos en objetos Document
docs = [
    Document(
        page_content="\n".join([
            f"Name: {guest['name']}",
            f"Relation: {guest['relation']}",
            f"Description: {guest['description']}",
            f"Email: {guest['email']}"
        ]),
        metadata={"name": guest["name"]}
    )
    for guest in guest_dataset
]
```

En el código anterior:
- Cargamos el conjunto de datos
- Convertimos cada entrada de invitado en un objeto `Document` con contenido formateado
- Almacenamos los objetos `Document` en una lista

Esto significa que tenemos todos nuestros datos disponibles de manera ordenada para poder comenzar a configurar nuestra recuperación.

### Paso 2: Crear la Herramienta de Recuperación

Ahora, creemos una herramienta personalizada que Alfred pueda usar para buscar en nuestra información de invitados.

Usaremos el `BM25Retriever` del módulo `langchain_community.retrievers` para crear una herramienta de recuperación.

> [!TIP]
> El BM25Retriever es un gran punto de partida para la recuperación, pero para búsquedas semánticas más avanzadas, podrías considerar usar recuperadores basados en embeddings como los de sentence-transformers.

```python
from smolagents import Tool
from langchain_community.retrievers import BM25Retriever

class GuestInfoRetrieverTool(Tool):
    name = "guest_info_retriever"
    description = "Recupera información detallada sobre los invitados de la gala basada en su nombre o relación."
    inputs = {
        "query": {
            "type": "string",
            "description": "El nombre o relación del invitado sobre el que deseas información."
        }
    }
    output_type = "string"

    def __init__(self, docs):
        self.is_initialized = False
        self.retriever = BM25Retriever.from_documents(docs)

    def forward(self, query: str):
        results = self.retriever.get_relevant_documents(query)
        if results:
            return "\n\n".join([doc.page_content for doc in results[:3]])
        else:
            return "No se encontró información que coincida con la búsqueda."

# Inicializar la herramienta
guest_info_tool = GuestInfoRetrieverTool(docs)
```

Entendamos esta herramienta paso a paso: 
- El `name` y la `description` ayudan al agente a entender cuándo y cómo usar esta herramienta
- Los `inputs` definen qué parámetros espera la herramienta (en este caso, una consulta de búsqueda)
- Estamos usando un `BM25Retriever`, que es un algoritmo poderoso de recuperación de texto que no requiere embeddings
- El método `forward` procesa la consulta y devuelve la información más relevante del invitado

Usaremos el `BM25Retriever` del módulo `llama_index.retrievers.bm25` para crear una herramienta de recuperación.

> [!TIP]
> El BM25Retriever es un gran punto de partida para la recuperación, pero para búsquedas semánticas más avanzadas, podrías considerar usar recuperadores basados en embeddings como los de sentence-transformers.

```python
from llama_index.core.tools import FunctionTool
from llama_index.retrievers.bm25 import BM25Retriever

bm25_retriever = BM25Retriever.from_defaults(nodes=docs)

def get_guest_info_retriever(query: str) -> str:
    """Recupera información detallada sobre los invitados de la gala basada en su nombre o relación."""
    results = bm25_retriever.retrieve(query)
    if results:
        return "\n\n".join([doc.text for doc in results[:3]])
    else:
        return "No se encontró información que coincida con la búsqueda."

# Inicializar la herramienta
guest_info_tool = FunctionTool.from_defaults(get_guest_info_retriever)
```

Entendamos esta herramienta paso a paso: 
- El docstring ayuda al agente a entender cuándo y cómo usar esta herramienta
- Los decoradores de tipo definen qué parámetros espera la herramienta (en este caso, una consulta de búsqueda)
- Estamos usando un `BM25Retriever`, que es un algoritmo poderoso de recuperación de texto que no requiere embeddings
- El método procesa la consulta y devuelve la información más relevante del invitado

Usaremos el `BM25Retriever` del módulo `langchain_community.retrievers` para crear una herramienta de recuperación.

> [!TIP]
> El BM25Retriever es un gran punto de partida para la recuperación, pero para búsquedas semánticas más avanzadas, podrías considerar usar recuperadores basados en embeddings como los de sentence-transformers.

```python
from langchain_community.retrievers import BM25Retriever
from langchain_core.tools import Tool

bm25_retriever = BM25Retriever.from_documents(docs)

def extract_text(query: str) -> str:
    """Recupera información detallada sobre los invitados de la gala basada en su nombre o relación."""
    results = bm25_retriever.invoke(query)
    if results:
        return "\n\n".join([doc.text for doc in results[:3]])
    else:
        return "No se encontró información que coincida con la búsqueda."

guest_info_tool = Tool(
    name="guest_info_retriever",
    func=extract_text,
    description="Recupera información detallada sobre los invitados de la gala basada en su nombre o relación."
)
```

Entendamos esta herramienta paso a paso: 
- El `name` y la `description` ayudan al agente a entender cuándo y cómo usar esta herramienta
- Los decoradores de tipo definen qué parámetros espera la herramienta (en este caso, una consulta de búsqueda)
- Estamos usando un `BM25Retriever`, que es un potente algoritmo de recuperación de texto que no requiere embeddings
- El método procesa la consulta y devuelve la información más relevante del invitado

### Paso 3: Integrar la Herramienta con Alfred

Finalmente, juntemos todo creando nuestro agente y equipándolo con nuestra herramienta personalizada:

```python
from smolagents import CodeAgent, InferenceClientModel

# Inicializar el modelo de Hugging Face
model = InferenceClientModel()

# Crear Alfred, nuestro agente de gala, con la herramienta de información de invitados
alfred = CodeAgent(tools=[guest_info_tool], model=model)

# Ejemplo de consulta que Alfred podría recibir durante la gala
response = alfred.run("Cuéntame sobre nuestra invitada llamada 'Lady Ada Lovelace'.")

print("🎩 Respuesta de Alfred:")
print(response)
```

Salida esperada:

```
🎩 Respuesta de Alfred:
Basado en la información que recuperé, Lady Ada Lovelace es una estimada matemática y amiga. Es reconocida por su trabajo pionero en matemáticas e informática, a menudo celebrada como la primera programadora de computadoras debido a su trabajo en la Máquina Analítica de Charles Babbage. Su dirección de correo electrónico es ada.lovelace@example.com.
```

Lo que está sucediendo en este paso final:
- Inicializamos un modelo de Hugging Face usando la clase `InferenceClientModel`
- Creamos nuestro agente (Alfred) como un `CodeAgent`, que puede ejecutar código Python para resolver problemas
- Le pedimos a Alfred que recupere información sobre una invitada llamada "Lady Ada Lovelace"

```python
from llama_index.core.agent.workflow import AgentWorkflow
from llama_index.llms.huggingface_api import HuggingFaceInferenceAPI

# Inicializar el modelo de Hugging Face
llm = HuggingFaceInferenceAPI(model_name="Qwen/Qwen2.5-Coder-32B-Instruct")

# Crear a Alfred, nuestro agente de gala, con la herramienta de información de invitados
alfred = AgentWorkflow.from_tools_or_functions(
    [guest_info_tool],
    llm=llm,
)

# Ejemplo de consulta que Alfred podría recibir durante la gala
response = await alfred.run("Cuéntame sobre nuestra invitada llamada 'Lady Ada Lovelace'.")

print("🎩 Respuesta de Alfred:")
print(response)
```

Salida esperada:

```
🎩 Respuesta de Alfred:
Lady Ada Lovelace es una estimada matemática y amiga, reconocida por su trabajo pionero en matemáticas e informática. Es celebrada como la primera programadora de computadoras debido a su trabajo en la Máquina Analítica de Charles Babbage. Su correo electrónico es ada.lovelace@example.com.
```

Lo que está sucediendo en este paso final:
- Inicializamos un modelo de Hugging Face usando la clase `HuggingFaceInferenceAPI`
- Creamos nuestro agente (Alfred) como un `AgentWorkflow`, incluyendo la herramienta que acabamos de crear
- Le pedimos a Alfred que recupere información sobre una invitada llamada "Lady Ada Lovelace"

```python
from typing import TypedDict, Annotated
from langgraph.graph.message import add_messages
from langchain_core.messages import AnyMessage, HumanMessage, AIMessage
from langgraph.prebuilt import ToolNode
from langgraph.graph import START, StateGraph
from langgraph.prebuilt import tools_condition
from langchain_huggingface import HuggingFaceEndpoint, ChatHuggingFace

# Generar la interfaz de chat, incluyendo las herramientas
llm = HuggingFaceEndpoint(
    repo_id="Qwen/Qwen2.5-Coder-32B-Instruct",
    huggingfacehub_api_token=HUGGINGFACEHUB_API_TOKEN,
)

chat = ChatHuggingFace(llm=llm, verbose=True)
tools = [guest_info_tool]
chat_with_tools = chat.bind_tools(tools)

# Generar el AgentState y el grafo del Agente
class AgentState(TypedDict):
    messages: Annotated[list[AnyMessage], add_messages]

def assistant(state: AgentState):
    return {
        "messages": [chat_with_tools.invoke(state["messages"])],
    }

## El grafo
builder = StateGraph(AgentState)

# Definir nodos: estos hacen el trabajo
builder.add_node("assistant", assistant)
builder.add_node("tools", ToolNode(tools))

# Definir bordes: estos determinan cómo se mueve el flujo de control
builder.add_edge(START, "assistant")
builder.add_conditional_edges(
    "assistant",
    # Si el último mensaje requiere una herramienta, enrutar a herramientas
    # De lo contrario, proporcionar una respuesta directa
    tools_condition,
)
builder.add_edge("tools", "assistant")
alfred = builder.compile()

messages = [HumanMessage(content="Cuéntame sobre nuestra invitada llamada 'Lady Ada Lovelace'.")]
response = alfred.invoke({"messages": messages})

print("🎩 Respuesta de Alfred:")
print(messages['messages'][-1].content)
```

Salida esperada:

```
🎩 Respuesta de Alfred:
Lady Ada Lovelace es una estimada matemática y pionera en informática, a menudo celebrada como la primera programadora de computadoras debido a su trabajo en la Máquina Analítica de Charles Babbage.
```

Lo que está sucediendo en este paso final:
- Inicializamos un modelo de Hugging Face usando la clase `HuggingFaceEndpoint`. También generamos una interfaz de chat y añadimos las herramientas.
- Creamos nuestro agente (Alfred) como un `StateGraph`, que combina 2 nodos (`assistant`, `tools`) usando un borde
- Le pedimos a Alfred que recupere información sobre una invitada llamada "Lady Ada Lovelace"

## Ejemplo de Interacción

Durante la gala, una conversación podría fluir así:

**Tú:** "Alfred, ¿quién es ese caballero hablando con el embajador?"

**Alfred:** *rápidamente busca en la base de datos de invitados* "Ese es el Dr. Nikola Tesla, señor. Es un viejo amigo de sus días universitarios. Recientemente ha patentado un nuevo sistema de transmisión de energía inalámbrica y estaría encantado de discutirlo con usted. Solo recuerde que es apasionado por las palomas, así que eso podría ser un buen tema de conversación."

```json
{
    "name": "Dr. Nikola Tesla",
    "relation": "viejo amigo de días universitarios",  
    "description": "El Dr. Nikola Tesla es un viejo amigo de sus días universitarios. Recientemente ha patentado un nuevo sistema de transmisión de energía inalámbrica y estaría encantado de discutirlo con usted. Solo recuerde que es apasionado por las palomas, así que eso podría ser un buen tema de conversación.",
    "email": "nikola.tesla@gmail.com"
}
```

## Llevándolo Más Allá

Ahora que Alfred puede recuperar información de invitados, considera cómo podrías mejorar este sistema:

1. **Mejorar el recuperador** para usar un algoritmo más sofisticado como [sentence-transformers](https://www.sbert.net/)
2. **Implementar una memoria de conversación** para que Alfred recuerde interacciones previas
3. **Combinar con búsqueda web** para obtener la información más reciente sobre invitados desconocidos
4. **Integrar múltiples índices** para obtener información más completa de fuentes verificadas

¡Ahora Alfred está completamente equipado para manejar consultas de invitados sin esfuerzo, asegurando que tu gala sea recordada como el evento más sofisticado y encantador del siglo!

> [!TIP]
> Intenta extender la herramienta de recuperación para que también devuelva iniciadores de conversación basados en los intereses o antecedentes de cada invitado. ¿Cómo modificarías la herramienta para lograr esto?
>
> Cuando hayas terminado, implementa tu herramienta de recuperación de invitados en el archivo retriever.py.

### Conclusión
https://huggingface.co/learn/agents-course/es/unit3/agentic-rag/conclusion.md

# Conclusión

En esta unidad, hemos aprendido a crear un sistema RAG agéntico para ayudar a Alfred, nuestro amigable agente de vecindario, a prepararse y a gestionar una gala extravagante.

La combinación de RAG con las capacidades agéntico demuestra cómo los asistentes de IA pueden volverse muy poderosos cuando tienen:
- Acceso a conocimientos estructurados (información de los invitados)
- Capacidad de recuperar información en tiempo real (búsqueda web)
- Herramientas específicas del dominio (información climática, estadísticas de Hub)
- Memoria de las interacciones pasadas

Con estas capacidades, Alfred está bien equipado para ser el perfecto anfitrión, capaz de responder a preguntas sobre los invitados, proporcionar información actualizada y asegurarse de que la gala se realice sin problemas -incluso gestionando el momento perfecto para el lanzamiento de los fuegos artificiales!

> [!TIP]
> Ahora que has construido un agente completo, podrías querer explorar:
>
> - Crear herramientas especializadas para tus propios casos de uso
> - Implementar sistemas RAG más sofisticados con embeddings
> - Crear sistemas multi-agente donde los agentes puedan colaborar
> - Desplegar tu agente como un servicio con el que los demás puedan interactuar

### Construyendo e Integrando Herramientas para Tu Agente
https://huggingface.co/learn/agents-course/es/unit3/agentic-rag/tools.md

# Construyendo e Integrando Herramientas para Tu Agente

En esta sección, le daremos a Alfred acceso a la web, permitiéndole encontrar las últimas noticias y actualizaciones globales. 
Además, tendrá acceso a datos meteorológicos y estadísticas de descargas de modelos de Hugging Face Hub, para que pueda mantener conversaciones relevantes sobre temas actuales.

## Dale a Tu Agente Acceso a la Web

Recuerda que queremos que Alfred establezca su presencia como un verdadero anfitrión renacentista, con un profundo conocimiento del mundo.

Para lograrlo, necesitamos asegurarnos de que Alfred tenga acceso a las últimas noticias e información sobre el mundo.

¡Comencemos creando una herramienta de búsqueda web para Alfred!

```python
from smolagents import DuckDuckGoSearchTool

# Inicializar la herramienta de búsqueda DuckDuckGo
search_tool = DuckDuckGoSearchTool()

# Ejemplo de uso
results = search_tool("¿Quién es el actual Presidente de Francia?")
print(results)
```

Salida esperada:

```
The current President of France in Emmanuel Macron.
```

```python
from llama_index.tools.duckduckgo import DuckDuckGoSearchToolSpec
from llama_index.core.tools import FunctionTool

# Inicializar la herramienta de búsqueda DuckDuckGo
tool_spec = DuckDuckGoSearchToolSpec()

search_tool = FunctionTool.from_defaults(tool_spec.duckduckgo_full_search)
# Ejemplo de uso
response = search_tool("¿Quién es el actual Presidente de Francia?")
print(response.raw_output[-1]['body'])
```

Expected output:

```
El Presidente de la República Francesa es el jefe de estado de Francia. El actual Presidente es Emmanuel Macron desde el 14 de mayo de 2017 tras derrotar a Marine Le Pen en la segunda vuelta de las elecciones presidenciales el 7 de mayo de 2017. Lista de Presidentes franceses (Quinta República) N° Nombre ...
```

```python
from langchain_community.tools import DuckDuckGoSearchRun

search_tool = DuckDuckGoSearchRun()
results = search_tool.invoke("¿Quién es el actual Presidente de Francia?")
print(results)
```

Salida esperada:

```
Emmanuel Macron (born December 21, 1977, Amiens, France) is a French banker and politician who was elected president of France in 2017...
```

## Creando una Herramienta Personalizada para Información Meteorológica para Programar los Fuegos Artificiales

La gala perfecta tendría fuegos artificiales bajo un cielo despejado, necesitamos asegurarnos de que los fuegos artificiales no sean cancelados debido al mal tiempo.

Vamos a crear una herramienta personalizada que pueda usarse para llamar a una API meteorológica externa y obtener la información del clima para una ubicación determinada.

> [!TIP]
> Por simplicidad, estamos usando una API meteorológica ficticia para este ejemplo. Si quieres usar una API meteorológica real, podrías implementar una herramienta meteorológica que use la API de OpenWeatherMap, como en la Unidad 1.

```python
from smolagents import Tool
import random

class WeatherInfoTool(Tool):
    name = "weather_info"
    description = "Obtiene información meteorológica ficticia para una ubicación dada."
    inputs = {
        "location": {
            "type": "string",
            "description": "La ubicación para la que obtener información meteorológica."
        }
    }
    output_type = "string"

    def forward(self, location: str):
        # Datos meteorológicos ficticios
        weather_conditions = [
            {"condition": "Lluvioso", "temp_c": 15},
            {"condition": "Despejado", "temp_c": 25},
            {"condition": "Ventoso", "temp_c": 20}
        ]
        # Seleccionar aleatoriamente una condición meteorológica
        data = random.choice(weather_conditions)
        return f"Clima en {location}: {data['condition']}, {data['temp_c']}°C"

# Inicializar la herramienta
weather_info_tool = WeatherInfoTool()
```

```python
import random
from llama_index.core.tools import FunctionTool

def get_weather_info(location: str) -> str:
    """Obtiene información meteorológica ficticia para una ubicación dada."""
    # Datos meteorológicos ficticios
    weather_conditions = [
        {"condition": "Lluvioso", "temp_c": 15},
        {"condition": "Despejado", "temp_c": 25},
        {"condition": "Ventoso", "temp_c": 20}
    ]
    # Seleccionar aleatoriamente una condición meteorológica
    data = random.choice(weather_conditions)
    return f"Clima en {location}: {data['condition']}, {data['temp_c']}°C"

# Inicializar la herramienta
weather_info_tool = FunctionTool.from_defaults(get_weather_info)
```

```python
from langchain_core.tools import Tool
import random

def get_weather_info(location: str) -> str:
    """Obtiene información meteorológica ficticia para una ubicación dada."""
    # Datos meteorológicos ficticios
    weather_conditions = [
        {"condition": "Lluvioso", "temp_c": 15},
        {"condition": "Despejado", "temp_c": 25},
        {"condition": "Ventoso", "temp_c": 20}
    ]
    # Seleccionar aleatoriamente una condición meteorológica
    data = random.choice(weather_conditions)
    return f"Clima en {location}: {data['condition']}, {data['temp_c']}°C"

# Inicializar la herramienta
weather_info_tool = Tool(
    name="get_weather_info",
    func=get_weather_info,
    description="Obtiene información meteorológica ficticia para una ubicación dada."
)
```

## Creando una Herramienta de Estadísticas de Hub para Influyentes Creadores de IA

Entre los asistentes a la gala están los más destacados creadores de IA. Alfred quiere impresionarlos discutiendo sus modelos, conjuntos de datos y espacios más populares. Crearemos una herramienta para obtener estadísticas de modelos desde Hugging Face Hub basadas en un nombre de usuario.

```python
from smolagents import Tool
from huggingface_hub import list_models

class HubStatsTool(Tool):
    name = "hub_stats"
    description = "Obtiene el modelo más descargado de un autor específico en Hugging Face Hub."
    inputs = {
        "author": {
            "type": "string",
            "description": "El nombre de usuario del autor/organización del modelo para encontrar modelos."
        }
    }
    output_type = "string"

    def forward(self, author: str):
        try:
            # Listar modelos del autor especificado, ordenados por descargas
            models = list(list_models(author=author, sort="downloads", direction=-1, limit=1))
            
            if models:
                model = models[0]
                return f"El modelo más descargado de {author} es {model.id} con {model.downloads:,} descargas."
            else:
                return f"No se encontraron modelos para el autor {author}."
        except Exception as e:
            return f"Error al obtener modelos para {author}: {str(e)}"

# Inicializar la herramienta
hub_stats_tool = HubStatsTool()

# Ejemplo de uso
print(hub_stats_tool("facebook")) # Ejemplo: Obtener el modelo más descargado de Facebook
```

Salida esperada:

```
The most downloaded model by facebook is facebook/esmfold_v1 with 12,544,550 downloads.
```

```python
import random
from llama_index.core.tools import FunctionTool
from huggingface_hub import list_models

def get_hub_stats(author: str) -> str:
    """Obtiene el modelo más descargado de un autor específico en Hugging Face Hub."""
    try:
        # Listar modelos del autor especificado, ordenados por descargas
        models = list(list_models(author=author, sort="downloads", direction=-1, limit=1))

        if models:
            model = models[0]
            return f"El modelo más descargado de {author} es {model.id} con {model.downloads:,} descargas."
        else:
            return f"No se encontraron modelos para el autor {author}."
    except Exception as e:
        return f"Error al obtener modelos para {author}: {str(e)}"

# Inicializar la herramienta
hub_stats_tool = FunctionTool.from_defaults(get_hub_stats)

# Ejemplo de uso
print(hub_stats_tool("facebook")) # Ejemplo: Obtener el modelo más descargado de Facebook
```

Salida esperada:

```
The most downloaded model by facebook is facebook/esmfold_v1 with 12,544,550 downloads.
```

```python
from langchain_core.tools import Tool
from huggingface_hub import list_models

def get_hub_stats(author: str) -> str:
    """Obtiene el modelo más descargado de un autor específico en Hugging Face Hub."""
    try:
        # Listar modelos del autor especificado, ordenados por descargas
        models = list(list_models(author=author, sort="downloads", direction=-1, limit=1))

        if models:
            model = models[0]
            return f"El modelo más descargado de {author} es {model.id} con {model.downloads:,} descargas."
        else:
            return f"No se encontraron modelos para el autor {author}."
    except Exception as e:
        return f"Error al obtener modelos para {author}: {str(e)}"

# Inicializar la herramienta
hub_stats_tool = Tool(
    name="get_hub_stats",
    func=get_hub_stats,
    description="Obtiene el modelo más descargado de un autor específico en Hugging Face Hub."
)

# Ejemplo de uso
print(hub_stats_tool.invoke("facebook")) # Ejemplo: Obtener el modelo más descargado de Facebook
```

Salida esperada:

```
The most downloaded model by facebook is facebook/esmfold_v1 with 13,109,861 downloads.
```

Con la Herramienta de Estadísticas de Hub, Alfred ahora puede impresionar a influyentes creadores de IA discutiendo sus modelos más populares.

## Integrando Herramientas con Alfred

Ahora que tenemos todas las herramientas, vamos a integrarlas en el agente de Alfred:

```python
from smolagents import CodeAgent, InferenceClientModel

# Inicializar el modelo de Hugging Face
model = InferenceClientModel()

# Crear Alfred con todas las herramientas
alfred = CodeAgent(
    tools=[search_tool, weather_info_tool, hub_stats_tool], 
    model=model
)

# Ejemplo de consulta que Alfred podría recibir durante la gala
response = alfred.run("¿Qué es Facebook y cuál es su modelo más popular?")

print("🎩 Respuesta de Alfred:")
print(response)
```

Salida esperada: 

```
🎩 Respuesta de Alfred:
Facebook es un sitio web de redes sociales donde los usuarios pueden conectarse, compartir información e interactuar con otros. El modelo más descargado de Facebook en Hugging Face Hub es ESMFold_v1.
```

```python
from llama_index.core.agent.workflow import AgentWorkflow
from llama_index.llms.huggingface_api import HuggingFaceInferenceAPI

# Inicializar el modelo de Hugging Face
llm = HuggingFaceInferenceAPI(model_name="Qwen/Qwen2.5-Coder-32B-Instruct")
# Crear Alfred con todas las herramientas
alfred = AgentWorkflow.from_tools_or_functions(
    [search_tool, weather_info_tool, hub_stats_tool],
    llm=llm
)

# Ejemplo de consulta que Alfred podría recibir durante la gala
response = await alfred.run("¿Qué es Facebook y cuál es su modelo más popular?")

print("🎩 Respuesta de Alfred:")
print(response)
```

Salida esperada: 

```
🎩 Respuesta de Alfred:
Facebook es un servicio de redes sociales y una empresa tecnológica con sede en Menlo Park, California. Fue fundada por Mark Zuckerberg y permite a las personas crear perfiles, conectarse con amigos y familiares, compartir fotos y videos, y unirse a grupos basados en intereses compartidos. El modelo más popular de Facebook en Hugging Face Hub es `facebook/esmfold_v1` con 13,109,861 descargas.
```

```python
from typing import TypedDict, Annotated
from langgraph.graph.message import add_messages
from langchain_core.messages import AnyMessage, HumanMessage, AIMessage
from langgraph.prebuilt import ToolNode
from langgraph.graph import START, StateGraph
from langgraph.prebuilt import tools_condition
from langchain_huggingface import HuggingFaceEndpoint, ChatHuggingFace

# Generar la interfaz de chat, incluyendo las herramientas
llm = HuggingFaceEndpoint(
    repo_id="Qwen/Qwen2.5-Coder-32B-Instruct",
    huggingfacehub_api_token=HUGGINGFACEHUB_API_TOKEN,
)

chat = ChatHuggingFace(llm=llm, verbose=True)
tools = [search_tool, weather_info_tool, hub_stats_tool]
chat_with_tools = chat.bind_tools(tools)

# Generar el AgentState y el grafo del Agente
class AgentState(TypedDict):
    messages: Annotated[list[AnyMessage], add_messages]

def assistant(state: AgentState):
    return {
        "messages": [chat_with_tools.invoke(state["messages"])],
    }

## El grafo
builder = StateGraph(AgentState)

# Definir nodos: estos hacen el trabajo
builder.add_node("assistant", assistant)
builder.add_node("tools", ToolNode(tools))

# Definir bordes: estos determinan cómo se mueve el flujo de control
builder.add_edge(START, "assistant")
builder.add_conditional_edges(
    "assistant",
    # Si el último mensaje requiere una herramienta, enrutar a herramientas
    # De lo contrario, proporcionar una respuesta directa
    tools_condition,
)
builder.add_edge("tools", "assistant")
alfred = builder.compile()

messages = [HumanMessage(content="¿Quién es Facebook y cuál es su modelo más popular?")]
response = alfred.invoke({"messages": messages})

print("🎩 Respuesta de Alfred:")
print(response['messages'][-1].content)
```

Salida esperada:

```
🎩 Respuesta de Alfred:
Facebook es una empresa de redes sociales conocida por su sitio de redes sociales, Facebook, así como otros servicios como Instagram y WhatsApp. El modelo más descargado de Facebook en Hugging Face Hub es facebook/esmfold_v1 con 13,202,321 descargas.
```

## Conclusión

Al integrar estas herramientas, Alfred ahora está equipado para manejar una variedad de tareas, desde búsquedas web hasta actualizaciones meteorológicas y estadísticas de modelos. Esto asegura que se mantenga como el anfitrión más informado y atractivo en la gala.

> [!TIP]
> Intenta implementar una herramienta que pueda usarse para obtener las últimas noticias sobre un tema específico.
>
> Cuando hayas terminado, implementa tus herramientas personalizadas en el archivo tools.py.

### Introducción al Caso de Uso para RAG Agéntico
https://huggingface.co/learn/agents-course/es/unit3/agentic-rag/introduction.md

# Introducción al Caso de Uso para RAG Agéntico

![Banner de RAG Agéntico](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/unit3/agentic-rag/thumbnail.jpg)

En esta unidad, ayudaremos a Alfred, nuestro amigable agente que está organizando la gala, utilizando RAG Agéntico para crear una herramienta que pueda usarse para responder preguntas sobre los invitados en la gala. 

> [!TIP]
> Este es un caso de uso 'del mundo real' para RAG Agéntico, que podrías utilizar en tus propios proyectos o lugares de trabajo. Si quieres obtener más de este proyecto, ¿por qué no lo pruebas en tu propio caso de uso y lo compartes en Discord?

Puedes elegir cualquiera de los frameworks discutidos en el curso para este caso de uso. Proporcionamos ejemplos de código para cada uno en pestañas separadas.

## Una Gala para Recordar

Ahora, es momento de ensuciarnos las manos con un caso de uso real. ¡Preparemos el escenario!

**Has decidido organizar la fiesta más extravagante y opulenta del siglo.** Esto significa banquetes lujosos, bailarines encantadores, DJs de renombre, bebidas exquisitas, un impresionante espectáculo de fuegos artificiales y mucho más.

Alfred, tu amigable agente de vecindario, se está preparando para atender todas tus necesidades para esta fiesta, y **Alfred va a gestionar todo por sí mismo**. Para hacerlo, necesita tener acceso a toda la información sobre la fiesta, incluyendo el menú, los invitados, el horario, pronósticos del clima, ¡y mucho más!

No solo eso, sino que también necesita asegurarse de que la fiesta sea un éxito, por lo que **necesita poder responder cualquier pregunta sobre la fiesta durante la fiesta**, mientras maneja situaciones inesperadas que puedan surgir.

No puede hacer esto solo, así que debemos asegurarnos de que Alfred tenga acceso a toda la información y herramientas que necesita.

Primero, vamos a darle una lista de requisitos estrictos para la gala.

## Los Requisitos de la Gala

Una persona debidamente educada en la época del **Renacimiento** necesita tener tres rasgos principales.
Él o ella necesitaba ser profundo en el **conocimiento de deportes, cultura y ciencia**. Por lo tanto, debemos asegurarnos de que podamos impresionar a nuestros invitados con nuestro conocimiento y proporcionarles una gala verdaderamente inolvidable.
Sin embargo, para evitar conflictos, hay algunos **temas, como la política y la religión, que deben evitarse en una gala.** Debe ser una fiesta divertida sin conflictos relacionados con creencias e ideales.

Según la etiqueta, **un buen anfitrión debe conocer los antecedentes de los invitados**, incluyendo sus intereses y esfuerzos. Un buen anfitrión también chismea y comparte historias sobre los invitados entre sí.

Por último, debemos asegurarnos de tener **algún conocimiento general sobre el clima** para poder encontrar continuamente una actualización en tiempo real que asegure el momento perfecto para lanzar los fuegos artificiales y terminar la gala con broche de oro. 🎆

Como puedes ver, Alfred necesita mucha información para organizar la gala.
Afortunadamente, podemos ayudar y preparar a Alfred dándole algo de **entrenamiento en Generación Aumentada por Recuperación (RAG)**.

¡Comencemos creando las herramientas que Alfred necesita para poder organizar la gala!

### Creando tu Agente para la Gala
https://huggingface.co/learn/agents-course/es/unit3/agentic-rag/agent.md

# Creando tu Agente para la Gala

Ahora que hemos construido todos los componentes necesarios para Alfred, es momento de unirlos en un agente completo que pueda ayudar a organizar nuestra extravagante gala.

En esta sección, combinaremos la recuperación de información de invitados, búsqueda web, información meteorológica y herramientas de estadísticas de Hub en un solo agente poderoso.

## Ensamblando a Alfred: El Agente Completo

En lugar de reimplementar todas las herramientas que creamos en secciones anteriores, las importaremos desde sus respectivos módulos que guardamos en los archivos `tools.py` y `retriever.py`.

> [!TIP]
> Si aún no has implementado las herramientas, regresa a las secciones de herramientas y recuperador para implementarlas, y agrégalas a los archivos tools.py y retriever.py.

Importemos las bibliotecas y herramientas necesarias de las secciones anteriores:

```python
# Importar librerías necesarias
import random
from smolagents import CodeAgent, InferenceClientModel

# Importar nuestras herramientas personalizadas desde sus módulos
from tools import DuckDuckGoSearchTool, WeatherInfoTool, HubStatsTool
from retriever import load_guest_dataset
```

Ahora, combinemos todas estas herramientas en un solo agente:

```python
# Inicializar el modelo de Hugging Face
model = InferenceClientModel()

# Inicializar la herramienta de búsqueda web
search_tool = DuckDuckGoSearchTool()

# Inicializar la herramienta del clima
weather_info_tool = WeatherInfoTool()

# Inicializar la herramienta de estadísticas de Hub
hub_stats_tool = HubStatsTool()

# Cargar el conjunto de datos de invitados e inicializar la herramienta de información de invitados
guest_info_tool = load_guest_dataset()

# Crear a Alfred con todas las herramientas
alfred = CodeAgent(
    tools=[guest_info_tool, weather_info_tool, hub_stats_tool, search_tool], 
    model=model,
    add_base_tools=True,  # Agregar cualquier herramienta base adicional
    planning_interval=3   # Habilitar planificación cada 3 pasos
)
```

```python
# Importar librerías necesarias
from llama_index.core.agent.workflow import AgentWorkflow
from llama_index.llms.huggingface_api import HuggingFaceInferenceAPI

from tools import search_tool, weather_info_tool, hub_stats_tool
from retriever import guest_info_tool
```

Ahora, combinemos todas estas herramientas en un solo agente:

```python
# Inicializar el modelo de Hugging Face
llm = HuggingFaceInferenceAPI(model_name="Qwen/Qwen2.5-Coder-32B-Instruct")

# Crear Alfred con todas las herramientas
alfred = AgentWorkflow.from_tools_or_functions(
    [guest_info_tool, search_tool, weather_info_tool, hub_stats_tool],
    llm=llm,
)
```

```python
from typing import TypedDict, Annotated
from langgraph.graph.message import add_messages
from langchain_core.messages import AnyMessage, HumanMessage, AIMessage
from langgraph.prebuilt import ToolNode
from langgraph.graph import START, StateGraph
from langgraph.prebuilt import tools_condition
from langchain_huggingface import HuggingFaceEndpoint, ChatHuggingFace

from tools import DuckDuckGoSearchTool, weather_info_tool, hub_stats_tool
from retriever import load_guest_dataset
```

Ahora, combina todas estas herramientas en un solo agente:

```python
# Inicializar la herramienta de búsqueda web
search_tool = DuckDuckGoSearchTool()

# Cargar el conjunto de datos de invitados e inicializar la herramienta de información de invitados
guest_info_tool = load_guest_dataset()

# Generar la interfaz de chat, incluyendo las herramientas
llm = HuggingFaceEndpoint(
    repo_id="Qwen/Qwen2.5-Coder-32B-Instruct",
    huggingfacehub_api_token=HUGGINGFACEHUB_API_TOKEN,
)

chat = ChatHuggingFace(llm=llm, verbose=True)
tools = [guest_info_tool, search_tool, weather_info_tool, hub_stats_tool]
chat_with_tools = chat.bind_tools(tools)

# Generar el AgentState y el grafo del Agente
class AgentState(TypedDict):
    messages: Annotated[list[AnyMessage], add_messages]

def assistant(state: AgentState):
    return {
        "messages": [chat_with_tools.invoke(state["messages"])],
    }

## El grafo
builder = StateGraph(AgentState)

# Definir nodos: estos hacen el trabajo
builder.add_node("assistant", assistant)
builder.add_node("tools", ToolNode(tools))

# Definir bordes: estos determinan cómo se mueve el flujo de control
builder.add_edge(START, "assistant")
builder.add_conditional_edges(
    "assistant",
    # Si el último mensaje requiere una herramienta, enrutar a herramientas
    # De lo contrario, proporcionar una respuesta directa
    tools_condition,
)
builder.add_edge("tools", "assistant")
alfred = builder.compile()
```

¡Tu agente ahora está listo para usarse!

## Usando a Alfred: Ejemplos de Principio a Fin

Ahora que Alfred está completamente equipado con todas las herramientas necesarias, veamos cómo puede ayudar con varias tareas durante la gala.

### Ejemplo 1: Encontrando Información de Invitados

Veamos cómo Alfred puede ayudarnos con nuestra información de invitados.

```python
query = "Cuéntame sobre 'Lady Ada Lovelace'"
response = alfred.run(query)

print("🎩 Respuesta de Alfred:")
print(response)
```

Salida esperada:

```
🎩 Respuesta de Alfred:
Según la información que recuperé, Lady Ada Lovelace es una estimada matemática y amiga. Es reconocida por su trabajo pionero en matemáticas y computación, frecuentemente celebrada como la primera programadora de computadoras debido a su trabajo en el Motor Analítico de Charles Babbage. Su dirección de correo electrónico es ada.lovelace@example.com.
```

```python
query = "Cuéntame sobre Lady Ada Lovelace. ¿Cuál es su historia?"
response = await alfred.run(query)

print("🎩 Respuesta de Alfred:")
print(response.response.blocks[0].text)
```

Salida esperada:

```
🎩 Respuesta de Alfred:
Lady Ada Lovelace fue una matemática y escritora inglesa, mejor conocida por su trabajo en el Motor Analítico de Charles Babbage. Fue la primera en reconocer que la máquina tenía aplicaciones más allá del cálculo puro.
```

```python
response = alfred.invoke({"messages": "Cuéntame sobre 'Lady Ada Lovelace'"})

print("🎩 Respuesta de Alfred:")
print(response['messages'][-1].content)
```

Salida esperada:

```
🎩 Respuesta de Alfred:
Ada Lovelace, también conocida como Augusta Ada King, Condesa de Lovelace, fue una matemática y escritora inglesa. Nacida el 10 de diciembre de 1815 y fallecida el 27 de noviembre de 1852, es reconocida por su trabajo en el Motor Analítico de Charles Babbage, una computadora mecánica de propósito general. Ada Lovelace es celebrada como una de las primeras programadoras de computadoras porque creó un programa para el Motor Analítico en 1843. Reconoció que la máquina podía usarse para más que simples cálculos, visualizando su potencial de una manera que pocos hicieron en ese momento. Sus contribuciones al campo de la ciencia de la computación sentaron las bases para desarrollos futuros. Un día en octubre, designado como el Día de Ada Lovelace, honra las contribuciones de las mujeres a la ciencia y la tecnología, inspirado en el trabajo pionero de Lovelace.
```

### Ejemplo 2: Verificando el Clima para los Fuegos Artificiales

Veamos cómo Alfred puede ayudarnos con el clima.

```python
query = "¿Cómo está el clima en París esta noche? ¿Será adecuado para nuestro espectáculo de fuegos artificiales?"
response = alfred.run(query)

print("🎩 Respuesta de Alfred:")
print(response)
```

Salida esperada (variará debido a la aleatoriedad):
```
🎩 Respuesta de Alfred:
He revisado el clima en París para ti. Actualmente, está despejado con una temperatura de 25°C. Estas condiciones son perfectas para el espectáculo de fuegos artificiales de esta noche. Los cielos despejados proporcionarán una excelente visibilidad para el espectáculo espectacular, y la temperatura agradable asegurará que los invitados puedan disfrutar del evento al aire libre sin incomodidad.
```

```python
query = "¿Cómo está el clima en París esta noche? ¿Será adecuado para nuestro espectáculo de fuegos artificiales?"
response = await alfred.run(query)

print("🎩 Respuesta de Alfred:")
print(response)
```

Salida esperada:

```
🎩 Respuesta de Alfred:
El clima en París esta noche es lluvioso con una temperatura de 15°C. Debido a la lluvia, puede que no sea adecuado para un espectáculo de fuegos artificiales.
```

```python
response = alfred.invoke({"messages": "¿Cómo está el clima en París esta noche? ¿Será adecuado para nuestro espectáculo de fuegos artificiales?"})

print("🎩 Respuesta de Alfred:")
print(response['messages'][-1].content)
```

Salida esperada:

```
🎩 Respuesta de Alfred:
El clima en París esta noche es lluvioso con una temperatura de 15°C, lo que puede no ser adecuado para tu espectáculo de fuegos artificiales.
```

### Ejemplo 3: Impresionando a Investigadores de IA

Veamos cómo Alfred puede ayudarnos a impresionar a los investigadores de IA.

```python
query = "Uno de nuestros invitados es de Qwen. ¿Qué puedes decirme sobre su modelo más popular?"
response = alfred.run(query)

print("🎩 Respuesta de Alfred:")
print(response)
```

Salida esperada:

```
🎩 Respuesta de Alfred:
El modelo más popular de Qwen es Qwen/Qwen2.5-VL-7B-Instruct con 3,313,345 descargas.
```

```python
query = "Uno de nuestros invitados es de Google. ¿Qué puedes decirme sobre su modelo más popular?"
response = await alfred.run(query)

print("🎩 Respuesta de Alfred:")
print(response)
```

Salida esperada:

```
🎩 Respuesta de Alfred:
El modelo más popular de Google en Hugging Face Hub es google/electra-base-discriminator, con 28,546,752 descargas.
```

```python
response = alfred.invoke({"messages": "Uno de nuestros invitados es de Qwen. ¿Qué puedes decirme sobre su modelo más popular?"})

print("🎩 Respuesta de Alfred:")
print(response['messages'][-1].content)
```

Salida esperada:

```
🎩 Respuesta de Alfred:
El modelo más descargado de Qwen es Qwen/Qwen2.5-VL-7B-Instruct con 3,313,345 descargas.
```

### Ejemplo 4: Combinando Múltiples Herramientas

Veamos cómo Alfred puede ayudarnos a prepararnos para una conversación con el Dr. Nikola Tesla.

```python
query = "Necesito hablar con el Dr. Nikola Tesla sobre avances recientes en energía inalámbrica. ¿Puedes ayudarme a prepararme para esta conversación?"
response = alfred.run(query)

print("🎩 Respuesta de Alfred:")
print(response)
```

Salida esperada:

```
🎩 Respuesta de Alfred:
He reunido información para ayudarte a prepararte para tu conversación con el Dr. Nikola Tesla.

Información del Invitado:
Nombre: Dr. Nikola Tesla
Relación: viejo amigo de los días universitarios
Descripción: El Dr. Nikola Tesla es un viejo amigo de tus días universitarios. Recientemente ha patentado un nuevo sistema de transmisión de energía inalámbrica y estaría encantado de discutirlo contigo. Solo recuerda que le apasionan las palomas, así que eso podría ser un buen tema para iniciar la conversación.
Email: nikola.tesla@gmail.com

Avances Recientes en Energía Inalámbrica:
Basándome en mi búsqueda web, aquí hay algunos desarrollos recientes en transmisión de energía inalámbrica:
1. Los investigadores han avanzado en la transmisión de energía inalámbrica de largo alcance utilizando ondas electromagnéticas enfocadas
2. Varias empresas están desarrollando tecnologías de acoplamiento inductivo resonante para electrónica de consumo
3. Hay nuevas aplicaciones en la carga de vehículos eléctricos sin conexiones físicas

Iniciadores de Conversación:
1. "Me encantaría conocer tu nueva patente sobre transmisión de energía inalámbrica. ¿Cómo se compara con tus conceptos originales de nuestros días universitarios?"
2. "¿Has visto los desarrollos recientes en acoplamiento inductivo resonante para electrónica de consumo? ¿Qué opinas de su enfoque?"
3. "¿Cómo están tus palomas? Recuerdo tu fascinación por ellas."

Esto debería darte mucho para discutir con el Dr. Tesla mientras demuestras tu conocimiento de sus intereses y desarrollos recientes en su campo.
```

```python
query = "Necesito hablar con el Dr. Nikola Tesla sobre avances recientes en energía inalámbrica. ¿Puedes ayudarme a prepararme para esta conversación?"
response = await alfred.run(query)

print("🎩 Respuesta de Alfred:")
print(response)
```

Salida esperada:

```
🎩 Respuesta de Alfred:
Aquí hay algunos avances recientes en energía inalámbrica que podrían serte útiles para tu conversación con el Dr. Nikola Tesla:

1. **Avances y Desafíos en la Transferencia de Energía Inalámbrica**: Este artículo analiza la evolución de la transferencia de energía inalámbrica (WPT) desde los métodos convencionales con cables hasta las aplicaciones modernas, incluidas las estaciones de energía espacial solar. Destaca el enfoque inicial en la tecnología de microondas y la demanda actual de WPT debido al aumento de dispositivos eléctricos.

2. **Avances Recientes en Tecnologías de Transferencia de Energía Inalámbrica para Electrónica Corporal**: Este artículo explora la transferencia de energía inalámbrica (WET) como solución para alimentar dispositivos electrónicos corporales sin necesidad de baterías o cables conductores. Analiza las ventajas y posibles aplicaciones de WET en este contexto.

3. **Transferencia de Energía Inalámbrica y Captación de Energía: Estado Actual y Tendencias Futuras**: Este artículo proporciona una visión general de los avances recientes en métodos de suministro de energía inalámbrica, incluida la captación de energía y la transferencia de energía inalámbrica. Presenta varias aplicaciones prometedoras y analiza las tendencias futuras en el campo.

4. **Transferencia de Energía Inalámbrica: Aplicaciones, Desafíos, Barreras y
```

```python
response = alfred.invoke({"messages":"Necesito hablar con el 'Dr. Nikola Tesla' sobre avances recientes en energía inalámbrica. ¿Puedes ayudarme a prepararme para esta conversación?"})

print("🎩 Respuesta de Alfred:")
print(response['messages'][-1].content)
```

Salida esperada:

```
Basándome en la información proporcionada, aquí hay puntos clave para prepararte para la conversación con el 'Dr. Nikola Tesla' sobre avances recientes en energía inalámbrica:
1. **Transmisión de Energía Inalámbrica (WPT):** Comenta cómo WPT revoluciona la transferencia de energía al eliminar la necesidad de cables y aprovechar mecanismos como el acoplamiento inductivo y resonante.
2. **Avances en Carga Inalámbrica:** Destaca las mejoras en eficiencia, velocidades de carga más rápidas y el auge de soluciones de carga inalámbrica certificadas Qi/Qi2.
3. **Innovaciones 5G-Advanced y Protocolo Inalámbrico NearLink:** Menciona estos como desarrollos que mejoran la velocidad, seguridad y eficiencia en redes inalámbricas, que pueden soportar tecnologías avanzadas de energía inalámbrica.
4. **IA y ML en el Edge:** Habla sobre cómo la inteligencia artificial y el aprendizaje automático dependerán de redes inalámbricas para llevar inteligencia al borde, mejorando la automatización e inteligencia en hogares y edificios inteligentes.
5. **Avances en Matter, Thread y Seguridad:** Comenta estas innovaciones clave que impulsan la conectividad, eficiencia y seguridad en dispositivos y sistemas IoT.
6. **Avances en Tecnología de Carga Inalámbrica:** Incluye avances recientes o estudios, como el de la Universidad Nacional de Incheon, para respaldar los avances en carga inalámbrica.
```

### Ejemplo 3: Impresionando a Investigadores de IA

Veamos cómo Alfred puede ayudarnos a impresionar a los investigadores de IA.

```python
query = "Uno de nuestros invitados es de Qwen. ¿Qué puedes decirme sobre su modelo más popular?"
response = alfred.run(query)

print("🎩 Respuesta de Alfred:")
print(response)
```

Salida esperada:

```
🎩 Respuesta de Alfred:
El modelo más popular de Qwen es Qwen/Qwen2.5-VL-7B-Instruct con 3,313,345 descargas.
```

```python
query = "Uno de nuestros invitados es de Google. ¿Qué puedes decirme sobre su modelo más popular?"
response = await alfred.run(query)

print("🎩 Respuesta de Alfred:")
print(response)
```

Salida esperada:

```
🎩 Respuesta de Alfred:
El modelo más popular de Google en Hugging Face Hub es google/electra-base-discriminator, con 28,546,752 descargas.
```

```python
response = alfred.invoke({"messages": "Uno de nuestros invitados es de Qwen. ¿Qué puedes decirme sobre su modelo más popular?"})

print("🎩 Respuesta de Alfred:")
print(response['messages'][-1].content)
```

Salida esperada:

```
🎩 Respuesta de Alfred:
El modelo más descargado de Qwen es Qwen/Qwen2.5-VL-7B-Instruct con 3,313,345 descargas.
```

## Características Avanzadas: Memoria de Conversación

Para hacer que Alfred sea aún más útil durante la gala, podemos habilitar la memoria de conversación para que recuerde interacciones previas:

```python
# Crear Alfred con memoria de conversación
alfred_with_memory = CodeAgent(
    tools=[guest_info_tool, weather_info_tool, hub_stats_tool, search_tool], 
    model=model,
    add_base_tools=True,
    planning_interval=3,
    memory=True  # Habilitar memoria de conversación
)

# Primera interacción
response1 = alfred_with_memory.run("Cuéntame sobre Lady Ada Lovelace.")
print("🎩 Primera Respuesta de Alfred:")
print(response1)

# Segunda interacción (haciendo referencia a la primera)
response2 = alfred_with_memory.run("¿En qué proyectos está trabajando actualmente?")
print("🎩 Segunda Respuesta de Alfred:")
print(response2)
```

```python
from llama_index.core.workflow import Context

alfred = AgentWorkflow.from_tools_or_functions(
    [guest_info_tool, search_tool, weather_info_tool, hub_stats_tool],
    llm=llm
)

# Recordando el estado
ctx = Context(alfred)

# Primera interacción
response1 = await alfred.run("Cuéntame sobre Lady Ada Lovelace.", ctx=ctx)
print("🎩 Primera Respuesta de Alfred:")
print(response1)

# Segunda interacción (haciendo referencia a la primera)
response2 = await alfred.run("¿En qué proyectos está trabajando actualmente?", ctx=ctx)
print("🎩 Segunda Respuesta de Alfred:")
print(response2)
```

```python
# Primera interacción
response = alfred.invoke({"messages": [HumanMessage(content="Cuéntame sobre 'Lady Ada Lovelace'. ¿Cuál es su historia y cómo está relacionada conmigo?")]})

print("🎩 Respuesta de Alfred:")
print(response['messages'][-1].content)
print()

# Segunda interacción (haciendo referencia a la primera)
response = alfred.invoke({"messages": response["messages"] + [HumanMessage(content="¿En qué proyectos está trabajando actualmente?")]})

print("🎩 Respuesta de Alfred:")
print(response['messages'][-1].content)
```

## Conclusión

¡Felicitaciones! Has construido exitosamente a Alfred, un agente sofisticado equipado con múltiples herramientas para ayudar a organizar la gala más extravagante del siglo. Alfred ahora puede:

1. Recuperar información detallada sobre los invitados
2. Verificar las condiciones climáticas para planificar actividades al aire libre
3. Proporcionar información sobre influyentes creadores de IA y sus modelos
4. Buscar en la web la información más reciente
5. Mantener el contexto de la conversación con memoria

Con estas capacidades, Alfred está listo para asegurar que tu gala sea un éxito rotundo, impresionando a los invitados con atención personalizada e información actualizada.

### Reclama tu Certificado 🎓
https://huggingface.co/learn/agents-course/es/unit4/get-your-certificate.md

# Reclama tu Certificado 🎓

Si obtuviste una puntuación **superior al 30%, ¡felicitaciones! 👏 Ahora eres elegible para reclamar tu certificado oficial.**

Sigue los pasos a continuación para recibirlo:

1. Visita la [página del certificado](https://huggingface.co/spaces/agents-course/Unit4-Final-Certificate).
2. **Inicia sesión** con tu cuenta de Hugging Face usando el botón proporcionado.
3. **Ingresa tu nombre completo**. Este es el nombre que aparecerá en tu certificado.
4. Haz clic en **“Obtener Mi Certificado”** para verificar tu puntuación y descargar tu certificado.

Una vez que tengas tu certificado, siéntete libre de:
- Agregarlo a tu **perfil de LinkedIn** 🧑‍💼
- Compartirlo en **X**, **Bluesky**, etc. 🎉

**No olvides etiquetar a [@huggingface](https://huggingface.co/huggingface). ¡Estaremos súper orgullosos y nos encantaría animarte! 🤗**

### Práctica
https://huggingface.co/learn/agents-course/es/unit4/hands-on.md

# Práctica

Ahora que estás listo/a para profundizar en la creación de tu agente final, veamos cómo puedes enviarlo para su revisión.

## El Conjunto de Datos (Dataset)

El conjunto de datos utilizado en esta tabla de clasificación consta de 20 preguntas extraídas de las preguntas de nivel 1 del conjunto de **validación** de GAIA.
Las preguntas elegidas se filtraron según la cantidad de herramientas y pasos necesarios para responder una pregunta.

Basándonos en el estado actual del benchmark GAIA, creemos que intentar alcanzar un 30% en las preguntas de nivel 1 es una prueba justa.

## El Proceso

Ahora, la gran pregunta en tu mente probablemente sea: "¿Cómo empiezo a enviar?"

Para esta Unidad, creamos una API que te permitirá obtener las preguntas y enviar tus respuestas para su puntuación.
Aquí tienes un resumen de las rutas (consulta la [documentación en vivo](https://agents-course-unit4-scoring.hf.space/docs) para detalles interactivos):

*   **`GET /questions`**: Recupera la lista completa de preguntas de evaluación filtradas.
*   **`GET /random-question`**: Obtiene una única pregunta aleatoria de la lista.
*   **`GET /files/{task_id}`**: Descarga un archivo específico asociado con un ID de tarea determinado.
*   **`POST /submit`**: Envía las respuestas del agente, calcula la puntuación y actualiza la tabla de clasificación.

La función de envío comparará la respuesta con la verdad fundamental (ground truth) de manera de **COINCIDENCIA EXACTA** (exact match), ¡así que dale un buen prompt! El equipo de GAIA compartió un ejemplo de prompting para tu agente [aquí](https://huggingface.co/spaces/gaia-benchmark/leaderboard).

🎨 **¡Haz Tuya la Plantilla!**

Para demostrar el proceso de interacción con la API, hemos incluido una [plantilla básica](https://huggingface.co/spaces/agents-course/Final_Assignment_Template) como punto de partida.
Por favor, siéntete libre —y te **animamos activamente**— a cambiarla, agregarle elementos o reestructurarla por completo. Modifícala de cualquier manera que se adapte mejor a tu enfoque y creatividad.

Para enviar usando esta plantilla, calcula 3 cosas necesarias para la API:

*   **Nombre de usuario (`Username`):** Tu nombre de usuario de Hugging Face (aquí obtenido mediante el inicio de sesión de Gradio), que se utiliza para identificar tu envío.
*   **Enlace al Código (`agent_code`):** La URL que enlaza al código de tu Space de Hugging Face (`.../tree/main`) para fines de verificación, así que por favor mantén tu Space público.
*   **Respuestas (`answers`):** La lista de respuestas (`{"task_id": ..., "submitted_answer": ...}`) generadas por tu Agente para la puntuación.

Por lo tanto, te animamos a comenzar duplicando esta [plantilla](https://huggingface.co/spaces/agents-course/Final_Assignment_Template) en tu propio perfil de Hugging Face.

🏆 Revisa la tabla de clasificación [aquí](https://huggingface.co/spaces/agents-course/Students_leaderboard)

*Nota amistosa: ¡Esta tabla de clasificación es por diversión! Sabemos que es posible enviar puntuaciones sin una verificación completa. Si vemos demasiadas puntuaciones altas publicadas sin un enlace público que las respalde, es posible que necesitemos revisar, ajustar o eliminar algunas entradas para mantener la tabla de clasificación útil.*
La tabla de clasificación mostrará el enlace a la base de código de tu Space; dado que esta tabla es solo para estudiantes, por favor mantén tu Space público si obtienes una puntuación de la que estés orgulloso/a.

### Conclusión
https://huggingface.co/learn/agents-course/es/unit4/conclusion.md

# Conclusión

**¡Felicitaciones por terminar el Curso de Agentes!**

A través de la perseverancia y la dedicación, has construido una base sólida en el mundo de los Agentes de IA.

Pero terminar este curso **no es el final de tu viaje**. Es solo el comienzo: no dudes en explorar la siguiente sección donde compartimos recursos seleccionados para ayudarte a continuar aprendiendo, incluyendo temas avanzados como los **MCP** y más allá.

**Gracias** por ser parte de este curso. **Esperamos que te haya gustado tanto como a nosotros nos encantó escribirlo**.

Y no lo olvides: **Sigue Aprendiendo, Mantente Genial 🤗**

### Readme
https://huggingface.co/learn/agents-course/es/unit4/README.md

### Bienvenido/a a la última Unidad [[introduccion]]
https://huggingface.co/learn/agents-course/es/unit4/introduction.md

# Bienvenido/a a la última Unidad [[introduccion]]

¡Bienvenido/a a la última unidad del curso! 🎉

Hasta ahora, has **construido una base sólida en Agentes de IA**, desde comprender sus componentes hasta crear los tuyos propios. Con este conocimiento, ahora estás listo/a para **construir agentes poderosos** y mantenerte al día con los últimos avances en este campo en rápida evolución.

Esta unidad trata sobre aplicar lo que has aprendido. Es tu **proyecto práctico final**, y completarlo es tu boleto para obtener el **certificado del curso**.

## ¿Cuál es el desafío?

Crearás tu propio agente y **evaluarás su rendimiento utilizando un subconjunto del [benchmark GAIA](https://huggingface.co/spaces/gaia-benchmark/leaderboard)**.

Para completar con éxito el curso, tu agente necesita obtener una puntuación del **30% o superior** en el benchmark. Logra eso, y obtendrás tu **Certificado de Finalización**, reconociendo oficialmente tu experiencia. 🏅

¡Además, mira cómo te comparas con tus compañeros! Una **[Tabla de Clasificación de Estudiantes](https://huggingface.co/spaces/agents-course/Students_leaderboard)** dedicada está disponible para que envíes tus puntuaciones y veas el progreso de la comunidad.

> ** 🚨 Atención: Unidad Avanzada y Práctica**
>
> Ten en cuenta que esta unidad cambia hacia un enfoque más práctico. El éxito en esta sección requerirá **conocimientos de codificación más avanzados** y depende de que navegues por las tareas con **menos guía explícita** en comparación con las partes anteriores del curso.

¿Suena emocionante? ¡Comencemos! 🚀

### ¿Qué es GAIA?
https://huggingface.co/learn/agents-course/es/unit4/what-is-gaia.md

# ¿Qué es GAIA?

[GAIA](https://huggingface.co/papers/2311.12983) es un **benchmark diseñado para evaluar asistentes de IA en tareas del mundo real** que requieren una combinación de capacidades centrales, como razonamiento, comprensión multimodal, navegación web y uso competente de herramientas.

Fue introducido en el artículo _"[GAIA: A Benchmark for General AI Assistants](https://huggingface.co/papers/2311.12983) (en inglés)"_.

El benchmark presenta **466 preguntas cuidadosamente seleccionadas** que son **conceptualmente simples para los humanos**, pero **notablemente desafiantes para los sistemas de IA actuales**.

Para ilustrar la brecha:
- **Humanos**: Tasa de éxito de ~92%
- **GPT-4 con plugins**: ~15%
- **Deep Research (OpenAI)**: 67.36% en el conjunto de validación

GAIA destaca las limitaciones actuales de los modelos de IA y proporciona un benchmark riguroso para evaluar el progreso hacia asistentes de IA verdaderamente de propósito general.

## 🌱 Principios Fundamentales de GAIA

GAIA está cuidadosamente diseñado en torno a los siguientes pilares:

- 🔍 **Dificultad del mundo real**: Las tareas requieren razonamiento de varios pasos, comprensión multimodal e interacción con herramientas.
- 🧾 **Interpretabilidad humana**: A pesar de su dificultad para la IA, las tareas siguen siendo conceptualmente simples y fáciles de seguir para los humanos.
- 🛡️ **No manipulable (Non-gameability)**: Las respuestas correctas exigen la ejecución completa de la tarea, lo que hace ineficaz el forzamiento bruto (brute-forcing).
- 🧰 **Simplicidad de evaluación**: Las respuestas son concisas, factuales y sin ambigüedades, ideales para el benchmarking.

## Niveles de Dificultad

Las tareas de GAIA se organizan en **tres niveles de complejidad creciente**, cada uno probando habilidades específicas:

- **Nivel 1**: Requiere menos de 5 pasos y un uso mínimo de herramientas.
- **Nivel 2**: Implica un razonamiento más complejo y la coordinación entre múltiples herramientas y 5-10 pasos.
- **Nivel 3**: Exige planificación a largo plazo e integración avanzada de diversas herramientas.

![Niveles de GAIA](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/unit4/gaia_levels.png)

## Ejemplo de una Pregunta Difícil de GAIA

> ¿Cuáles de las frutas mostradas en la pintura de 2008 "Bordado de Uzbekistán" se sirvieron como parte del menú de desayuno de octubre de 1949 para el transatlántico que luego se usó como utilería flotante para la película "El último viaje"? Enumera los elementos como una lista separada por comas, ordenándolos en el sentido de las agujas del reloj según su disposición en la pintura, comenzando desde la posición de las 12 en punto. Usa la forma plural de cada fruta.

Como puedes ver, esta pregunta desafía a los sistemas de IA de varias maneras:

- Requiere un **formato de respuesta estructurado**
- Implica **razonamiento multimodal** (por ejemplo, analizar imágenes)
- Exige **recuperación de múltiples saltos** (multi-hop retrieval) de hechos interdependientes:
  - Identificar las frutas en la pintura
  - Descubrir qué transatlántico se usó en *El último viaje*
  - Buscar el menú de desayuno de octubre de 1949 para ese barco
- Necesita **secuenciación correcta** y planificación de alto nivel para resolver en el orden correcto

Este tipo de tarea resalta dónde los LLM independientes a menudo se quedan cortos, lo que convierte a GAIA en un benchmark ideal para **sistemas basados en agentes** que pueden razonar, recuperar y ejecutar a lo largo de múltiples pasos y modalidades.

![Gráfico de capacidades de GAIA](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/unit4/gaia_capabilities.png)

## Evaluación en Vivo

Para fomentar el benchmarking continuo, **GAIA proporciona una tabla de clasificación pública alojada en Hugging Face**, donde puedes probar tus modelos contra **300 preguntas de prueba**.

👉 Revisa la tabla de clasificación [aquí](https://huggingface.co/spaces/gaia-benchmark/leaderboard)

¿Quieres profundizar más en GAIA?

- 📄 [Lee el artículo completo (en inglés)](https://huggingface.co/papers/2311.12983)
- 📄 [Publicación de lanzamiento de Deep Research por OpenAI (en inglés)](https://openai.com/index/introducing-deep-research/)
- 📄 [DeepResearch de código abierto – Liberando nuestros agentes de búsqueda (en inglés)](https://huggingface.co/blog/open-deep-research)

### ¿Y ahora? ¿Qué temas debería aprender?
https://huggingface.co/learn/agents-course/es/unit4/additional-readings.md

# ¿Y ahora? ¿Qué temas debería aprender?

La IA Agéntica es un campo en rápida evolución, y comprender los protocolos fundamentales es esencial para construir sistemas inteligentes y autónomos.

Dos estándares importantes con los que deberías familiarizarte son:

- El **Protocolo de Contexto del Modelo (MCP)**
- El **Protocolo Agente a Agente (A2A)**

## 🔌 Protocolo de Contexto del Modelo (MCP)

El **Protocolo de Contexto del Modelo (MCP)** de Anthropic es un estándar abierto que permite a los modelos de IA **conectarse de forma segura y fluida con herramientas externas, fuentes de datos y aplicaciones**, haciendo que los agentes sean más capaces y autónomos.

Piensa en el MCP como un **adaptador universal**, como un puerto USB-C, que permite a los modelos de IA conectarse a diversos entornos digitales **sin necesidad de una integración personalizada para cada uno**.

El MCP está ganando rápidamente terreno en la industria, con grandes empresas como OpenAI y Google comenzando a adoptarlo.

📚 Aprende más:
- [Anuncio oficial y documentación de Anthropic](https://www.anthropic.com/news/model-context-protocol)
- [MCP en Wikipedia (en inglés)](https://en.wikipedia.org/wiki/Model_Context_Protocol)
- [Blog sobre MCP (en inglés)](https://huggingface.co/blog/Kseniase/mcp)

## 🤝 Protocolo Agente a Agente (A2A)

Google ha desarrollado el **Protocolo Agente a Agente (A2A)** como contraparte complementaria al Protocolo de Contexto del Modelo (MCP) de Anthropic.

Mientras que el MCP conecta a los agentes con herramientas externas, **el A2A conecta a los agentes entre sí**, allanando el camino para sistemas cooperativos multiagente que pueden trabajar juntos para resolver problemas complejos.

📚 Profundiza en A2A:
- [Anuncio de A2A de Google (en inglés)](https://developers.googleblog.com/en/a2a-a-new-era-of-agent-interoperability/)

### Hagamos Fine-Tuning de Tu Modelo para Llamadas a Funciones
https://huggingface.co/learn/agents-course/es/bonus-unit1/fine-tuning.md

# Hagamos Fine-Tuning de Tu Modelo para Llamadas a Funciones

Ahora estamos listos para hacer fine-tuning de nuestro primer modelo para llamadas a funciones 🔥.

## ¿Cómo entrenamos nuestro modelo para llamadas a funciones?

> Respuesta: Necesitamos **datos**

Un proceso de entrenamiento de modelo se puede dividir en 3 pasos:

1. **El modelo es pre-entrenado con una gran cantidad de datos**. El resultado de ese paso es un **modelo pre-entrenado**. Por ejemplo, [google/gemma-2-2b](https://huggingface.co/google/gemma-2-2b). Es un modelo base y solo sabe cómo **predecir el siguiente token sin fuertes capacidades de seguimiento de instrucciones**.

2. Para ser útil en un contexto de chat, el modelo luego necesita ser **ajustado (fine-tuned)** para seguir instrucciones. En este paso, puede ser entrenado por los creadores del modelo, la comunidad de código abierto, tú o cualquier persona. Por ejemplo, [google/gemma-2-2b-it](https://huggingface.co/google/gemma-2-2b-it) es un modelo ajustado para instrucciones por el equipo de Google detrás del proyecto Gemma.

3. El modelo puede luego **alinearse** con las preferencias del creador. Por ejemplo, un modelo de chat de servicio al cliente que nunca debe ser grosero con los clientes.

Usualmente un producto completo como Gemini o Mistral **pasará por los 3 pasos**, mientras que los modelos que puedes encontrar en Hugging Face han completado uno o más pasos de este entrenamiento.

En este tutorial, construiremos un modelo de llamadas a funciones basado en [google/gemma-2-2b-it](https://huggingface.co/google/gemma-2-2b-it). Elegimos el modelo ajustado [google/gemma-2-2b-it](https://huggingface.co/google/gemma-2-2b-it) en lugar del modelo base [google/gemma-2-2b](https://huggingface.co/google/gemma-2-2b) porque el modelo ajustado ha sido mejorado para nuestro caso de uso.

Comenzar desde el modelo pre-entrenado **requeriría más entrenamiento para aprender a seguir instrucciones, chatear Y hacer llamadas a funciones**.

Al comenzar desde el modelo ajustado para instrucciones, **minimizamos la cantidad de información que nuestro modelo necesita aprender**.

## LoRA (Adaptación de Bajo Rango de Modelos de Lenguaje Grandes)

LoRA es una técnica de entrenamiento popular y ligera que **reduce significativamente el número de parámetros a entrenar**.

Funciona **insertando un número menor de nuevos pesos(weights) como un adaptador en el modelo para entrenar**. Esto hace que el entrenamiento con LoRA sea mucho más rápido, eficiente en memoria y produzca pesos(weights) de modelo más pequeños (unos cientos de MB), que son más fáciles de almacenar y compartir.

LoRA funciona añadiendo pares de matrices de descomposición de rango a las capas del Transformer, típicamente centrándose en las capas lineales. Durante el entrenamiento, "congelaremos" el resto del modelo y solo actualizaremos los pesos de esos adaptadores recién añadidos.

Al hacerlo, el número de **parámetros** que necesitamos entrenar disminuye considerablemente ya que solo necesitamos actualizar los pesos del adaptador.

Durante la inferencia, la entrada se pasa al adaptador y al modelo base, o estos pesos del adaptador pueden fusionarse con el modelo base, lo que resulta en ninguna sobrecarga adicional de latencia.

LoRA es particularmente útil para adaptar modelos de lenguaje **grandes** a tareas o dominios específicos mientras se mantienen manejables los requisitos de recursos. Esto ayuda a reducir la memoria **requerida** para entrenar un modelo.

Si quieres aprender más sobre cómo funciona LoRA, deberías consultar este [tutorial](https://huggingface.co/learn/nlp-course/chapter11/4?fw=pt).

## Fine-Tuning de un Modelo para Llamadas a Funciones

Puedes acceder al notebook del tutorial 👉 [aquí](https://huggingface.co/agents-course/notebooks/blob/main/bonus-unit1/bonus-unit1.ipynb).

Luego, haz clic en [![Abrir En Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/#fileId=https://huggingface.co/agents-course/notebooks/blob/main/bonus-unit1/bonus-unit1.ipynb) para poder ejecutarlo en un Notebook de Colab.

### ¿Qué es la Llamada a Funciones?
https://huggingface.co/learn/agents-course/es/bonus-unit1/what-is-function-calling.md

# ¿Qué es la Llamada a Funciones?

La llamada a funciones es una **forma para que un LLM realice acciones en su entorno**. Fue [introducida primero en GPT-4](https://openai.com/index/function-calling-and-other-api-updates/), y posteriormente fue reproducida en otros modelos.

Al igual que las herramientas de un Agente, la llamada a funciones le da al modelo la capacidad de **realizar una acción en su entorno**. Sin embargo, la capacidad de llamada a funciones **es aprendida por el modelo**, y **depende menos de los prompts que otras técnicas de agentes**.

Durante la Unidad 1, el Agente **no aprendió a usar las Herramientas**, simplemente proporcionamos la lista, y confiamos en el hecho de que el modelo **era capaz de generalizar al definir un plan usando estas Herramientas**. 

Mientras que aquí, **con la llamada a funciones, el Agente es ajustado (entrenado) para usar Herramientas**.

## ¿Cómo "aprende" el modelo a realizar una acción?

En la Unidad 1, exploramos el flujo de trabajo general de un agente. Una vez que el usuario ha dado algunas herramientas al agente y le ha proporcionado una consulta, el modelo pasará por el ciclo:

1. *Pensar* : ¿Qué acción(es) necesito tomar para cumplir el objetivo?
2. *Actuar* : Formatear la acción con el parámetro correcto y detener la generación.
3. *Observar* : Obtener el resultado de la ejecución.

En una conversación "típica" con un modelo a través de una API, la conversación alternará entre mensajes del usuario y del asistente de esta manera:

```python
conversation = [
    {"role": "user", "content": "Necesito ayuda con mi pedido"},
    {"role": "assistant", "content": "Estaré encantado de ayudar. ¿Podrías proporcionar tu número de pedido?"},
    {"role": "user", "content": "Es ORDER-123"},
]
```

¡La llamada a funciones trae **nuevos roles a la conversación**! 

1. Un nuevo rol para una **Acción** 
2. Un nuevo rol para una **Observación** 

Si tomamos la [API de Mistral](https://docs.mistral.ai/capabilities/function_calling/) como ejemplo, se vería así:

```python
conversation = [
    {
        "role": "user",
        "content": "¿Cuál es el estado de mi transacción T1001?"
    },
    {
        "role": "assistant",
        "content": "",
        "function_call": {
            "name": "retrieve_payment_status",
            "arguments": "{\"transaction_id\": \"T1001\"}"
        }
    },
    {
        "role": "tool",
        "name": "retrieve_payment_status",
        "content": "{\"status\": \"Paid\"}"
    },
    {
        "role": "assistant",
        "content": "Tu transacción T1001 ha sido pagada exitosamente."
    }
]
```

> ... ¿Pero dijiste que hay un nuevo rol para las llamadas a funciones?

**Sí y no**, en este caso y en muchas otras APIs, el modelo formatea la acción a tomar como un mensaje de "asistente". La plantilla de chat luego representará esto como **tokens especiales** para la llamada a funciones.

- `[AVAILABLE_TOOLS]` – Inicio de la lista de herramientas disponibles  
- `[/AVAILABLE_TOOLS]` – Fin de la lista de herramientas disponibles  
- `[TOOL_CALLS]` – Hacer una llamada a una herramienta (es decir, realizar una "Acción")  
- `[TOOL_RESULTS]` – "Observar" el resultado de la acción  
- `[/TOOL_RESULTS]` – Fin de la observación (es decir, el modelo puede decodificar nuevamente)

Hablaremos nuevamente sobre la llamada a funciones en este curso, pero si quieres profundizar puedes consultar [esta excelente sección de documentación](https://docs.mistral.ai/capabilities/function_calling/).

---
Ahora que hemos aprendido qué es la llamada a funciones y cómo funciona, vamos a **agregar algunas capacidades de llamada a funciones a un modelo que aún no tiene esas capacidades**: [google/gemma-2-2b-it](https://huggingface.co/google/gemma-2-2b-it), agregando algunos nuevos tokens especiales al modelo.

Para poder hacer eso, **primero necesitamos entender el fine-tuning y LoRA**.

### Conclusión [[conclusion]]
https://huggingface.co/learn/agents-course/es/bonus-unit1/conclusion.md

# Conclusión [[conclusion]]

¡Felicidades por terminar esta primera Unidad Bonus 🥳

¡Acabas de **dominar la comprensión de las llamadas a funciones y cómo hacer fine-tuning de tu modelo para realizar llamadas a funciones**!

Si tenemos un consejo ahora, es intentar **hacer fine-tuning de diferentes modelos**. La **mejor manera de aprender es intentándolo.**

En la siguiente Unidad, aprenderás a usar **frameworks de última generación como `smolagents`, `LlamaIndex` y `LangGraph`**.

Finalmente, nos encantaría **escuchar lo que piensas del curso y cómo podemos mejorarlo**. Si tienes algun comentario, por favor 👉 [completa este formulario](https://docs.google.com/forms/d/e/1FAIpQLSe9VaONn0eglax0uTwi29rIn4tM7H2sYmmybmG5jJNlE5v0xA/viewform?usp=dialog)

### Sigue Aprendiendo, Mantente Genial 🤗

### Introducción
https://huggingface.co/learn/agents-course/es/bonus-unit1/introduction.md

# Introducción

![Bonus Unit 1 Thumbnail](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/bonus-unit1/thumbnail.jpg)

Bienvenido a esta primera **Unidad Bonus**, donde aprenderás a **hacer fine-tuning de un Modelo de Lenguaje Grande (LLM) para llamadas a funciones**.

En términos de LLMs, la llamada a funciones se está convirtiendo rápidamente en una técnica *imprescindible*. 

La idea es que, en lugar de depender solo de enfoques basados en prompts como hicimos en la Unidad 1, la llamada a funciones entrena a tu modelo para **realizar acciones e interpretar observaciones durante la fase de entrenamiento**, haciendo tu IA más robusta.

> **¿Cuándo debería hacer esta Unidad Bonus?**
>
> Esta sección es **opcional** y es más avanzada que la Unidad 1, así que no dudes en hacer esta unidad ahora o revisitarla cuando tu conocimiento haya mejorado gracias a este curso. 
>  
> Pero no te preocupes, esta Unidad Bonus está diseñada para tener toda la información que necesitas, así que te guiaremos a través de cada concepto fundamental del fine-tuning de un modelo para llamadas a funciones, incluso si aún no has aprendido el funcionamiento interno del fine-tuning.

La mejor manera para que puedas seguir esta Unidad Bonus es:

1. Saber cómo hacer Fine-Tuning de un LLM con Transformers, si no es el caso [revisa esto](https://huggingface.co/learn/nlp-course/chapter3/1?fw=pt).

2. Saber cómo usar `SFTTrainer` para hacer fine-tuning de nuestro modelo, para aprender más sobre esto [revisa esta documentación](https://huggingface.co/learn/nlp-course/en/chapter11/1). 

---

## Lo que Aprenderás

1. **Llamadas a Funciones**  
   Cómo los LLMs modernos estructuran sus conversaciones de manera efectiva permitiéndoles activar **Herramientas**.

2. **LoRA (Adaptación de Bajo Rango)**  
   Un método de fine-tuning **ligero y eficiente** que reduce la sobrecarga computacional y de almacenamiento. LoRA hace que el entrenamiento de modelos grandes sea *más rápido, económico y fácil* de implementar.

3. **El Ciclo Pensamiento → Acción → Observación** en modelos de Llamadas a Funciones  
   Un enfoque simple pero poderoso para estructurar cómo tu modelo decide cuándo (y cómo) llamar funciones, rastrear pasos intermedios e interpretar los resultados de Herramientas o APIs externas.

4. **Nuevos Tokens Especiales**  
   Introduciremos **marcadores especiales** que ayudan al modelo a distinguir entre:
   - Razonamiento interno de "cadena de pensamiento"  
   - Llamadas a funciones salientes  
   - Respuestas que regresan de herramientas externas

---

Al final de esta unidad bonus, serás capaz de:

- **Entender** el funcionamiento interno de las APIs cuando se trata de Herramientas.  
- **Hacer fine-tuning** de un modelo usando la técnica LoRA.  
- **Implementar** y **modificar** el ciclo Pensamiento → Acción → Observación para crear flujos de trabajo de Llamadas a funciones robustos y mantenibles.  
- **Diseñar y utilizar** tokens especiales para separar sin problemas el razonamiento interno del modelo de sus acciones externas.

Y **habrás hecho fine-tuning de tu propio modelo para realizar llamadas a funciones.** 🔥

¡Sumerjámonos en las **llamadas a funciones**!

### Observabilidad y Evaluación de Agentes de IA
https://huggingface.co/learn/agents-course/es/bonus-unit2/what-is-agent-observability-and-evaluation.md

# Observabilidad y Evaluación de Agentes de IA

## 🔎 ¿Qué es la Observabilidad?

La observabilidad consiste en entender qué está sucediendo dentro de tu agente de IA mediante el análisis de señales externas como registros, métricas y rastros. Para los agentes de IA, esto significa rastrear acciones, uso de herramientas, llamadas al modelo y respuestas para depurar y mejorar el rendimiento del agente.

![Panel de observabilidad](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/bonus-unit2/langfuse-dashboard.png)

## 🔭 ¿Por qué es Importante la Observabilidad de Agentes?

Sin observabilidad, los agentes de IA son "cajas negras". Las herramientas de observabilidad hacen que los agentes sean transparentes, permitiéndote:

- Entender el intercambio entre costos y precisión
- Medir la latencia
- Detectar lenguaje dañino e inyección de prompts
- Monitorear la retroalimentación del usuario

En otras palabras, ¡hace que tu agente de demostración esté listo para producción!

## 🔨 Herramientas de Observabilidad

Las herramientas comunes de observabilidad para agentes de IA incluyen plataformas como [Langfuse](https://langfuse.com) y [Arize](https://www.arize.com). Estas herramientas ayudan a recopilar rastros detallados y ofrecen paneles para monitorear métricas en tiempo real, facilitando la detección de problemas y la optimización del rendimiento.

Las herramientas de observabilidad varían ampliamente en sus características y capacidades. Algunas herramientas son de código abierto, beneficiándose de grandes comunidades que dan forma a sus hojas de ruta y extensas integraciones. Además, ciertas herramientas se especializan en aspectos específicos de LLMOps, como observabilidad, evaluaciones o gestión de prompts, mientras que otras están diseñadas para cubrir todo el flujo de trabajo de LLMOps. Te animamos a explorar la documentación de diferentes opciones para elegir una solución que funcione bien para ti.

Muchos frameworks de agentes como [smolagents](https://smolagents.com) utilizan el estándar [OpenTelemetry](https://opentelemetry.io/docs/) para exponer metadatos a las herramientas de observabilidad. Además de esto, las herramientas de observabilidad construyen instrumentaciones personalizadas para permitir más flexibilidad en el mundo rápidamente cambiante de los LLM. Debes consultar la documentación de la herramienta que estás utilizando para ver qué es compatible.

## 🔬 Rastros y Spans

Las herramientas de observabilidad generalmente representan las ejecuciones de agentes como rastros y spans.

- Los **Rastros** representan una tarea completa del agente de principio a fin (como manejar una consulta de usuario).
- Los **Spans** son pasos individuales dentro del rastro (como llamar a un modelo de lenguaje o recuperar datos).

![Ejemplo de un rastro de smolagent en Langfuse](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/bonus-unit2/trace-tree.png)

## 📊 Métricas Clave para Monitorear

Aquí hay algunas de las métricas más comunes que las herramientas de observabilidad monitorean:

**Latencia:** ¿Con qué rapidez responde el agente? Los tiempos de espera largos afectan negativamente la experiencia del usuario. Debes medir la latencia para tareas y pasos individuales rastreando las ejecuciones del agente. Por ejemplo, un agente que tarda 20 segundos para todas las llamadas al modelo podría acelerarse utilizando un modelo más rápido o ejecutando llamadas al modelo en paralelo.

**Costos:** ¿Cuál es el gasto por ejecución del agente? Los agentes de IA dependen de llamadas a LLM facturadas por token o APIs externas. El uso frecuente de herramientas o múltiples prompts puede aumentar rápidamente los costos. Por ejemplo, si un agente llama a un LLM cinco veces para una mejora marginal de calidad, debes evaluar si el costo está justificado o si podrías reducir el número de llamadas o usar un modelo más económico. El monitoreo en tiempo real también puede ayudar a identificar picos inesperados (por ejemplo, errores que causan bucles excesivos de API).

**Errores de Solicitud:** ¿Cuántas solicitudes falló el agente? Esto puede incluir errores de API o llamadas fallidas a herramientas. Para hacer que tu agente sea más robusto contra estos en producción, puedes configurar alternativas o reintentos. Por ejemplo, si el proveedor de LLM A está caído, cambias al proveedor de LLM B como respaldo.

**Retroalimentación del Usuario:** Implementar evaluaciones directas del usuario proporciona información valiosa. Esto puede incluir calificaciones explícitas (pulgar arriba 👍/abajo 👎, 1-5 estrellas ⭐) o comentarios textuales. La retroalimentación negativa consistente debe alertarte, ya que es una señal de que el agente no está funcionando como se esperaba.

**Retroalimentación Implícita del Usuario:** Los comportamientos del usuario proporcionan retroalimentación indirecta incluso sin calificaciones explícitas. Esto puede incluir reformulación inmediata de preguntas, consultas repetidas o hacer clic en un botón de reintento. Por ejemplo, si ves que los usuarios hacen repetidamente la misma pregunta, esto es una señal de que el agente no está funcionando como se esperaba.

**Precisión:** ¿Con qué frecuencia produce el agente resultados correctos o deseables? Las definiciones de precisión varían (por ejemplo, corrección en la resolución de problemas, precisión en la recuperación de información, satisfacción del usuario). El primer paso es definir cómo se ve el éxito para tu agente. Puedes rastrear la precisión mediante verificaciones automatizadas, puntuaciones de evaluación o etiquetas de finalización de tareas. Por ejemplo, marcar rastros como "exitosos" o "fallidos".

**Métricas de Evaluación Automatizadas:** También puedes configurar evaluaciones automatizadas. Por ejemplo, puedes usar un LLM para puntuar la salida del agente, por ejemplo, si es útil, precisa o no. También hay varias bibliotecas de código abierto que te ayudan a puntuar diferentes aspectos del agente. Por ejemplo, [RAGAS](https://docs.ragas.io/) para agentes RAG o [LLM Guard](https://llm-guard.com/) para detectar lenguaje dañino o inyección de prompts.

En la práctica, una combinación de estas métricas proporciona la mejor cobertura de la salud de un agente de IA. En el [notebook de ejemplo](https://colab.research.google.com/#fileId=https://huggingface.co/agents-course/notebooks/blob/main/bonus-unit2/monitoring-and-evaluating-agents.ipynb) de este capítulo, te mostraremos cómo se ven estas métricas en ejemplos reales, pero primero, aprenderemos cómo es un flujo de trabajo de evaluación típico.

## 👍 Evaluando Agentes de IA

La observabilidad nos proporciona métricas, pero la evaluación es el proceso de analizar esos datos (y realizar pruebas) para determinar qué tan bien está funcionando un agente de IA y cómo se puede mejorar. En otras palabras, una vez que tienes esos rastros y métricas, ¿cómo los utilizas para juzgar al agente y tomar decisiones?

La evaluación regular es importante porque los agentes de IA a menudo son no deterministas y pueden evolucionar (a través de actualizaciones o comportamiento cambiante del modelo) - sin evaluación, no sabrías si tu "agente inteligente" está realmente haciendo bien su trabajo o si ha retrocedido.

Hay dos categorías de evaluaciones para agentes de IA: **evaluación en línea** y **evaluación fuera de línea**. Ambas son valiosas y se complementan entre sí. Generalmente comenzamos con la evaluación fuera de línea, ya que este es el paso mínimo necesario antes de implementar cualquier agente.

### 🥷 Evaluación Fuera de Línea

![Elementos del conjunto de datos en Langfuse](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/bonus-unit2/example-dataset.png)

Esto implica evaluar al agente en un entorno controlado, típicamente utilizando conjuntos de datos de prueba, no consultas de usuarios en vivo. Utilizas conjuntos de datos curados donde sabes cuál es la salida esperada o el comportamiento correcto, y luego ejecutas tu agente en ellos.

Por ejemplo, si construiste un agente de problemas de palabras matemáticas, podrías tener un [conjunto de datos de prueba](https://huggingface.co/datasets/gsm8k) de 100 problemas con respuestas conocidas. La evaluación fuera de línea a menudo se realiza durante el desarrollo (y puede ser parte de los pipelines de CI/CD) para verificar mejoras o proteger contra regresiones. El beneficio es que es **repetible y puedes obtener métricas claras de precisión ya que tienes la verdad fundamental**. También podrías simular consultas de usuarios y medir las respuestas del agente contra respuestas ideales o usar métricas automatizadas como se describió anteriormente.

El desafío clave con la evaluación fuera de línea es asegurar que tu conjunto de datos de prueba sea completo y se mantenga relevante - el agente podría funcionar bien en un conjunto de prueba fijo pero encontrar consultas muy diferentes en producción. Por lo tanto, debes mantener los conjuntos de prueba actualizados con nuevos casos extremos y ejemplos que reflejen escenarios del mundo real. Una mezcla de pequeños casos de "prueba de humo" y conjuntos de evaluación más grandes es útil: conjuntos pequeños para verificaciones rápidas y más grandes para métricas de rendimiento más amplias.

### 🔄 Evaluación en Línea

Esto se refiere a evaluar al agente en un entorno en vivo y del mundo real, es decir, durante el uso real en producción. La evaluación en línea implica monitorear el rendimiento del agente en interacciones reales con usuarios y analizar los resultados continuamente.

Por ejemplo, podrías rastrear tasas de éxito, puntuaciones de satisfacción del usuario u otras métricas en tráfico en vivo. La ventaja de la evaluación en línea es que **captura cosas que podrías no anticipar en un entorno de laboratorio** - puedes observar la deriva del modelo con el tiempo (si la efectividad del agente se degrada a medida que cambian los patrones de entrada) y detectar consultas o situaciones inesperadas que no estaban en tus datos de prueba. Proporciona una imagen verdadera de cómo se comporta el agente en el mundo real.

La evaluación en línea a menudo implica recopilar retroalimentación implícita y explícita del usuario, como se discutió, y posiblemente ejecutar pruebas sombra o pruebas A/B (donde una nueva versión del agente se ejecuta en paralelo para comparar con la antigua). El desafío es que puede ser complicado obtener etiquetas o puntuaciones confiables para interacciones en vivo - podrías depender de la retroalimentación del usuario o métricas posteriores (como si el usuario hizo clic en el resultado).

### 🤝 Combinando ambas

En la práctica, la evaluación exitosa de agentes de IA combina métodos **en línea** y **fuera de línea**. Podrías ejecutar puntos de referencia fuera de línea regulares para puntuar cuantitativamente a tu agente en tareas definidas y monitorear continuamente el uso en vivo para detectar cosas que los puntos de referencia pasan por alto. Por ejemplo, las pruebas fuera de línea podrían detectar si la tasa de éxito de un agente de generación de código en un conjunto conocido de problemas está mejorando, mientras que el monitoreo en línea podría alertarte de que los usuarios han comenzado a hacer una nueva categoría de preguntas con las que el agente tiene dificultades. Combinar ambos proporciona una imagen más robusta.

De hecho, muchos equipos adoptan un ciclo: _evaluación fuera de línea → implementar nueva versión del agente → monitorear métricas en línea y recopilar nuevos ejemplos de fallos → agregar esos ejemplos al conjunto de prueba fuera de línea → iterar_. De esta manera, la evaluación es continua y siempre mejorando.

## 🧑‍💻 Veamos cómo funciona esto en la práctica

En la siguiente sección, veremos ejemplos de cómo podemos usar herramientas de observabilidad para monitorear y evaluar nuestro agente.

### Unidad Extra 2: Observabilidad y Evaluación de Agentes
https://huggingface.co/learn/agents-course/es/bonus-unit2/monitoring-and-evaluating-agents-notebook.md

# Unidad Extra 2: Observabilidad y Evaluación de Agentes

> [!TIP]
> Puedes seguir el código en este notebook que puedes ejecutar usando Google Colab.

En este notebook, aprenderemos cómo **monitorear los pasos internos (trazos) de nuestro agente de IA** y **evaluar su rendimiento** utilizando herramientas de observabilidad de código abierto.

La capacidad de observar y evaluar el comportamiento de un agente es esencial para:
- Depurar problemas cuando las tareas fallan o producen resultados subóptimos
- Monitorear costos y rendimiento en tiempo real
- Mejorar la fiabilidad y seguridad a través de retroalimentación continua

## Requisitos del Ejercicio 🏗️

Antes de ejecutar este notebook, asegúrate de que has:

🔲 📚  **Estudiado** [Introducción a los Agentes](https://huggingface.co/learn/agents-course/unit1/introduction)

🔲 📚  **Estudiado** [El framework smolagents](https://huggingface.co/learn/agents-course/unit2/smolagents/introduction)

## Paso 0: Instalar las Librerías Necesarias

Necesitaremos algunas librerías que nos permitan ejecutar, monitorear y evaluar nuestros agentes:

```python
%pip install 'smolagents[telemetry]'
%pip install opentelemetry-sdk opentelemetry-exporter-otlp openinference-instrumentation-smolagents
%pip install langfuse datasets 'smolagents[gradio]'
```

## Paso 1: Instrumentar tu Agente

En este notebook, utilizaremos [Langfuse](https://langfuse.com/) como nuestra herramienta de observabilidad, pero puedes usar **cualquier otro servicio compatible con OpenTelemetry**. El código a continuación muestra cómo configurar variables de entorno para Langfuse (o cualquier endpoint OTel) y cómo instrumentar tu smolagent.

**Nota:** Si estás utilizando LlamaIndex o LangGraph, puedes encontrar documentación sobre cómo instrumentarlos [aquí](https://langfuse.com/docs/integrations/llama-index/workflows) y [aquí](https://langfuse.com/docs/integrations/langchain/example-python-langgraph). 

Primero, vamos a configurar la variable de entorno correcta para establecer la conexión con el endpoint OpenTelemetry de Langfuse. 

```python
import os
import base64

# Obtén tus propias claves desde https://cloud.langfuse.com
LANGFUSE_PUBLIC_KEY = = "pk-lf-..." 
LANGFUSE_SECRET_KEY = "sk-lf-..." 
os.environ["LANGFUSE_PUBLIC_KEY"] = LANGFUSE_PUBLIC_KEY
os.environ["LANGFUSE_SECRET_KEY"] = LANGFUSE_SECRET_KEY
os.environ["LANGFUSE_HOST"] = "https://cloud.langfuse.com"  # 🇪🇺 ejemplo de región EU
# os.environ["LANGFUSE_HOST"] = "https://us.cloud.langfuse.com"  # 🇺🇸 ejemplo de región US

LANGFUSE_AUTH = base64.b64encode(
    f"{LANGFUSE_PUBLIC_KEY}:{LANGFUSE_SECRET_KEY}".encode()
).decode()

os.environ["OTEL_EXPORTER_OTLP_ENDPOINT"] = os.environ.get("LANGFUSE_HOST") + "/api/public/otel"
os.environ["OTEL_EXPORTER_OTLP_HEADERS"] = f"Authorization=Basic {LANGFUSE_AUTH}"
```
También necesitamos configurar nuestro token de Hugging Face para las llamadas de inferencia.

```python
# Configura tu token de Hugging Face y otros tokens/secretos como variables de entorno
os.environ["HF_TOKEN"] = "hf_..." 
```
A continuación, podemos configurar un proveedor de trazoss para nuestro OpenTelemetry configurado.

```python
from opentelemetry.sdk.trace import TracerProvider
from openinference.instrumentation.smolagents import SmolagentsInstrumentor
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.trace.export import SimpleSpanProcessor
 
# Crear un TracerProvider para OpenTelemetry
trace_provider = TracerProvider()

# Añadir un SimpleSpanProcessor con el OTLPSpanExporter para enviar trazoss
trace_provider.add_span_processor(SimpleSpanProcessor(OTLPSpanExporter()))

# Establecer el proveedor de trazas predeterminado global
from opentelemetry import trace
trace.set_tracer_provider(trace_provider)
tracer = trace.get_tracer(__name__)

# Instrumentar smolagents con el proveedor configurado
SmolagentsInstrumentor().instrument(tracer_provider=trace_provider)
```

## Paso 2: Probar tu Instrumentación

Aquí hay un simple CodeAgent de smolagents que calcula `1+1`. Lo ejecutamos para confirmar que la instrumentación está funcionando correctamente. Si todo está configurado correctamente, verás registros/spans en tu panel de observabilidad.

```python
from smolagents import InferenceClientModel, CodeAgent

# Crear un agente simple para probar la instrumentación
agent = CodeAgent(
    tools=[],
    model=InferenceClientModel()
)

agent.run("1+1=")
```

Revisa tu [Panel de rastros de Langfuse](https://cloud.langfuse.com/traces) (o tu herramienta de observabilidad elegida) para confirmar que los spans y registros han sido grabados.

Captura de pantalla de ejemplo de Langfuse:

![Ejemplo de rastros en Langfuse](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/bonus-unit2/first-example-trace.png)

_[Enlace a  los rastros](https://cloud.langfuse.com/project/cloramnkj0002jz088vzn1ja4/traces/1b94d6888258e0998329cdb72a371155?timestamp=2025-03-10T11%3A59%3A41.743Z)_

## Paso 3: Observar y Evaluar un Agente Más Complejo

Ahora que has confirmado que tu instrumentación funciona, probemos una consulta más compleja para ver cómo se rastrean las métricas avanzadas (uso de tokens, latencia, costos, etc.).

```python
from smolagents import (CodeAgent, DuckDuckGoSearchTool, InferenceClientModel)

search_tool = DuckDuckGoSearchTool()
agent = CodeAgent(tools=[search_tool], model=InferenceClientModel())

agent.run("¿Cuántos cubos de Rubik podrías meter dentro de la Catedral de Notre Dame?")
```

### Estructura de Rastros

La mayoría de las herramientas de observabilidad registran una **rastro** que contiene **spans**, que representan cada paso de la lógica de tu agente. Aquí, el rastro contiene la ejecución general del agente y sub-spans para:
- Las llamadas a herramientas (DuckDuckGoSearchTool)
- Las llamadas al LLM (InferenceClientModel)

Puedes inspeccionarlos para ver precisamente dónde se gasta el tiempo, cuántos tokens se utilizan, etc.:

![Árbol de rastros en Langfuse](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/bonus-unit2/trace-tree.png)

_[Enlace a los rastros](https://cloud.langfuse.com/project/cloramnkj0002jz088vzn1ja4/traces/1ac33b89ffd5e75d4265b62900c348ed?timestamp=2025-03-07T13%3A45%3A09.149Z&display=preview)_

## Evaluación en Línea

En la sección anterior, aprendimos sobre la diferencia entre evaluación en línea y fuera de línea. Ahora, veremos cómo monitorear tu agente en producción y evaluarlo en vivo.

### Métricas Comunes para Seguir en Producción

1. **Costos** — La instrumentación de smolagents captura el uso de tokens, que puedes transformar en costos aproximados asignando un precio por token.
2. **Latencia** — Observa el tiempo que toma completar cada paso, o la ejecución completa.
3. **Retroalimentación del Usuario** — Los usuarios pueden proporcionar retroalimentación directa (pulgar arriba/abajo) para ayudar a refinar o corregir el agente.
4. **LLM-como-Juez** — Utiliza un LLM separado para evaluar la salida de tu agente en tiempo casi real (por ejemplo, verificando toxicidad o corrección).

A continuación, mostramos ejemplos de estas métricas.

#### 1. Costos

A continuación se muestra una captura de pantalla que muestra el uso para llamadas a `Qwen2.5-Coder-32B-Instruct`. Esto es útil para ver pasos costosos y optimizar tu agente. 

![Costos](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/bonus-unit2/smolagents-costs.png)

_[Enlace a los rastros](https://cloud.langfuse.com/project/cloramnkj0002jz088vzn1ja4/traces/1ac33b89ffd5e75d4265b62900c348ed?timestamp=2025-03-07T13%3A45%3A09.149Z&display=preview)_

#### 2. Latencia

También podemos ver cuánto tiempo tomó completar cada paso. En el ejemplo a continuación, toda la conversación tomó 32 segundos, que puedes desglosar por paso. Esto te ayuda a identificar cuellos de botella y optimizar tu agente.

![Latencia](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/bonus-unit2/smolagents-latency.png)

_[Enlace a los rastros](https://cloud.langfuse.com/project/cloramnkj0002jz088vzn1ja4/traces/1ac33b89ffd5e75d4265b62900c348ed?timestamp=2025-03-07T13%3A45%3A09.149Z&display=preview)_

#### 3. Atributos Adicionales

También puedes pasar atributos adicionales, como IDs de usuario, IDs de sesión o etiquetas, configurándolos en los spans. Por ejemplo, la instrumentación de smolagents utiliza OpenTelemetry para adjuntar atributos como `langfuse.user.id` o etiquetas personalizadas.

```python
from smolagents import (CodeAgent, DuckDuckGoSearchTool, InferenceClientModel)
from opentelemetry import trace

search_tool = DuckDuckGoSearchTool()
agent = CodeAgent(
    tools=[search_tool],
    model=InferenceClientModel()
)

with tracer.start_as_current_span("Smolagent-Trace") as span:
    span.set_attribute("langfuse.user.id", "smolagent-user-123")
    span.set_attribute("langfuse.session.id", "smolagent-session-123456789")
    span.set_attribute("langfuse.tags", ["city-question", "testing-agents"])

    agent.run("¿Cuál es la capital de Alemania?")
```

![Mejorando las ejecuciones de agentes con métricas adicionales](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/bonus-unit2/smolagents-attributes.png)

#### 4. Retroalimentación del Usuario

Si tu agente está integrado en una interfaz de usuario, puedes registrar la retroalimentación directa del usuario (como un pulgar arriba/abajo en una interfaz de chat). A continuación se muestra un ejemplo utilizando [Gradio](https://gradio.app/) para integrar un chat con un mecanismo de retroalimentación simple.

En el fragmento de código a continuación, cuando un usuario envía un mensaje de chat, capturamos el ID de traza de OpenTelemetry. Si al usuario le gusta/no le gusta la última respuesta, adjuntamos una puntuación a la traza.

```python
import gradio as gr
from opentelemetry.trace import format_trace_id
from smolagents import (CodeAgent, InferenceClientModel)
from langfuse import Langfuse

langfuse = Langfuse()
model = InferenceClientModel()
agent = CodeAgent(tools=[], model=model, add_base_tools=True)

formatted_trace_id = None  # Almacenaremos el trace_id actual globalmente para demostración

def respond(prompt, history):
    with trace.get_tracer(__name__).start_as_current_span("Smolagent-Trace") as span:
        output = agent.run(prompt)

        current_span = trace.get_current_span()
        span_context = current_span.get_span_context()
        trace_id = span_context.trace_id
        global formatted_trace_id
        formatted_trace_id = str(format_trace_id(trace_id))
        langfuse.trace(id=formatted_trace_id, input=prompt, output=output)

    history.append({"role": "assistant", "content": str(output)})
    return history

def handle_like(data: gr.LikeData):
    # Para demostración, mapeamos la retroalimentación del usuario a un 1 (me gusta) o 0 (no me gusta)
    if data.liked:
        langfuse.score(
            value=1,
            name="user-feedback",
            trace_id=formatted_trace_id
        )
    else:
        langfuse.score(
            value=0,
            name="user-feedback",
            trace_id=formatted_trace_id
        )

with gr.Blocks() as demo:
    chatbot = gr.Chatbot(label="Chat", type="messages")
    prompt_box = gr.Textbox(placeholder="Escribe tu mensaje...", label="Tu mensaje")

    # Cuando el usuario presiona 'Enter' en el prompt, ejecutamos 'respond'
    prompt_box.submit(
        fn=respond,
        inputs=[prompt_box, chatbot],
        outputs=chatbot
    )

    # Cuando el usuario hace clic en un botón de 'me gusta' en un mensaje, ejecutamos 'handle_like'
    chatbot.like(handle_like, None, None)

demo.launch()

```

La retroalimentación del usuario se captura entonces en tu herramienta de observabilidad:

![La retroalimentación del usuario se captura en Langfuse](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/bonus-unit2/user-feedback-gradio.png)

#### 5. LLM-como-Juez

LLM-como-Juez es otra forma de evaluar automáticamente la salida de tu agente. Puedes configurar una llamada LLM separada para medir la corrección, toxicidad, estilo o cualquier otro criterio que te importe.

**Flujo de trabajo**:
1. Defines una **Plantilla de Evaluación**, por ejemplo, "Verifica si el texto es tóxico".
2. Cada vez que tu agente genera una salida, pasas esa salida a tu LLM "juez" con la plantilla.
3. El LLM juez responde con una calificación o etiqueta que registras en tu herramienta de observabilidad.

Ejemplo de Langfuse:

![Plantilla de Evaluación LLM-como-Juez](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/bonus-unit2/evaluator-template.png)
![Evaluador LLM-como-Juez](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/bonus-unit2/evaluator.png)

```python
# Ejemplo: Verificar si la salida del agente es tóxica o no.
from smolagents import (CodeAgent, DuckDuckGoSearchTool, InferenceClientModel)

search_tool = DuckDuckGoSearchTool()
agent = CodeAgent(tools=[search_tool], model=InferenceClientModel())

agent.run("¿Puede comer zanahorias mejorar tu visión?")
```

Puedes ver que la respuesta de este ejemplo se juzga como "no tóxica".

![Puntuación de Evaluación LLM-como-Juez](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/bonus-unit2/llm-as-a-judge-score.png)

#### 6. Resumen de Métricas de Observabilidad

Todas estas métricas pueden visualizarse juntas en paneles. Esto te permite ver rápidamente cómo se desempeña tu agente a través de muchas sesiones y te ayuda a seguir las métricas de calidad a lo largo del tiempo.

![Resumen de métricas de observabilidad](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/bonus-unit2/langfuse-dashboard.png)

## Evaluación fuera de línea

La evaluación en línea es esencial para la retroalimentación en vivo, pero también necesitas **evaluación fuera de línea**—verificaciones sistemáticas antes o durante el desarrollo. Esto ayuda a mantener la calidad y fiabilidad antes de implementar cambios en producción.

### Evaluación con Conjuntos de Datos

En la evaluación fuera de línea, típicamente:
1. Tienes un conjunto de datos de referencia (con pares de prompt y salida esperada)
2. Ejecutas tu agente en ese conjunto de datos
3. Comparas las salidas con los resultados esperados o utilizas un mecanismo de puntuación adicional

A continuación, demostramos este enfoque con el [conjunto de datos GSM8K](https://huggingface.co/datasets/gsm8k), que contiene preguntas matemáticas y soluciones.

```python
import pandas as pd
from datasets import load_dataset

# Obtener GSM8K desde Hugging Face
dataset = load_dataset("openai/gsm8k", 'main', split='train')
df = pd.DataFrame(dataset)
print("Primeras filas del conjunto de datos GSM8K:")
print(df.head())
```

A continuación, creamos una entidad de conjunto de datos en Langfuse para rastrear las ejecuciones. Luego, agregamos cada elemento del conjunto de datos al sistema. (Si no estás utilizando Langfuse, podrías simplemente almacenar estos en tu propia base de datos o archivo local para análisis).

```python
from langfuse import Langfuse
langfuse = Langfuse()

langfuse_dataset_name = "gsm8k_dataset_huggingface"

# Crear un conjunto de datos en Langfuse
langfuse.create_dataset(
    name=langfuse_dataset_name,
    description="Conjunto de datos de referencia GSM8K cargado desde Huggingface",
    metadata={
        "date": "2025-03-10", 
        "type": "benchmark"
    }
)
```

```python
for idx, row in df.iterrows():
    langfuse.create_dataset_item(
        dataset_name=langfuse_dataset_name,
        input={"text": row["question"]},
        expected_output={"text": row["answer"]},
        metadata={"source_index": idx}
    )
    if idx >= 9: # Cargar solo los primeros 10 elementos para demostración
        break
```

![Elementos del conjunto de datos en Langfuse](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/bonus-unit2/example-dataset.png)

#### Ejecutando el Agente en el Conjunto de Datos

Definimos una función auxiliar `run_smolagent()` que:
1. Inicia un span de OpenTelemetry
2. Ejecuta nuestro agente en el prompt
3. Registra el ID de traza en Langfuse

Luego, recorremos cada elemento del conjunto de datos, ejecutamos el agente y vinculamos el rastro al elemento del conjunto de datos. También podemos adjuntar una puntuación de evaluación rápida si lo deseamos.

```python
from opentelemetry.trace import format_trace_id
from smolagents import (CodeAgent, InferenceClientModel, LiteLLMModel)

# Ejemplo: usando InferenceClientModel o LiteLLMModel para acceder a modelos de openai, anthropic, gemini, etc.:
model = InferenceClientModel()

agent = CodeAgent(
    tools=[],
    model=model,
    add_base_tools=True
)

def run_smolagent(question):
    with tracer.start_as_current_span("Smolagent-Trace") as span:
        span.set_attribute("langfuse.tag", "dataset-run")
        output = agent.run(question)

        current_span = trace.get_current_span()
        span_context = current_span.get_span_context()
        trace_id = span_context.trace_id
        formatted_trace_id = format_trace_id(trace_id)

        langfuse_trace = langfuse.trace(
            id=formatted_trace_id, 
            input=question, 
            output=output
        )
    return langfuse_trace, output
```

```python
dataset = langfuse.get_dataset(langfuse_dataset_name)

# Ejecutar nuestro agente contra cada elemento del conjunto de datos (limitado a los primeros 10 arriba)
for item in dataset.items:
    langfuse_trace, output = run_smolagent(item.input["text"])

    # Vincular la traza al elemento del conjunto de datos para análisis
    item.link(
        langfuse_trace,
        run_name="smolagent-notebook-run-01",
        run_metadata={ "model": model.model_id }
    )

    # Opcionalmente, almacenar una puntuación de evaluación rápida para demostración
    langfuse_trace.score(
        name="",
        value=1,
        comment="Este es un comentario"
    )

# Vaciar datos para asegurar que toda la telemetría sea enviada
langfuse.flush()
```

Puedes repetir este proceso con diferentes:
- Modelos (OpenAI GPT, LLM local, etc.)
- Herramientas (búsqueda vs. sin búsqueda)
- Prompts (diferentes mensajes de sistema)

Luego compararlos lado a lado en tu herramienta de observabilidad:

![Resumen de ejecución del conjunto de datos](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/bonus-unit2/dataset_runs.png)
![Comparación de ejecución del conjunto de datos](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/bonus-unit2/dataset-run-comparison.png)

## Consideraciones Finales

En este notebook, cubrimos cómo:
1. **Configurar la Observabilidad** usando smolagents + exportadores OpenTelemetry
2. **Verificar la Instrumentación** ejecutando un agente simple
3. **Capturar Métricas Detalladas** (costo, latencia, etc.) a través de herramientas de observabilidad
4. **Recopilar Retroalimentación del Usuario** a través de una interfaz Gradio
5. **Usar LLM-como-Juez** para evaluar automáticamente las salidas
6. **Realizar Evaluación Fuera de Línea** con un conjunto de datos de referencia

🤗 ¡Feliz programación!

### Observabilidad y Evaluación de Agentes de IA
https://huggingface.co/learn/agents-course/es/bonus-unit2/introduction.md

# Observabilidad y Evaluación de Agentes de IA

![Bonus Unit 2 Thumbnail](https://langfuse.com/images/cookbook/huggingface-agent-course/agent-observability-and-evaluation.png)

¡Bienvenido a la **Unidad Extra 2**! En este capítulo, explorarás estrategias avanzadas para observar, evaluar y, en última instancia, mejorar el rendimiento de tus agentes.

---

## 📚 ¿Cuándo debería hacer esta Unidad Extra?

Esta unidad extra es perfecta si:
- **Desarrollas y Despliegas Agentes de IA:** Quieres asegurarte de que tus agentes estén funcionando de manera confiable en producción.
- **Necesitas Información Detallada:** Buscas diagnosticar problemas, optimizar el rendimiento o entender el funcionamiento interno de tu agente.
- **Buscas Reducir la Sobrecarga Operativa:** Al monitorear los costos, la latencia y los detalles de ejecución del agente, puedes gestionar los recursos de manera eficiente.
- **Buscas Mejora Continua:** Estás interesado en integrar tanto la retroalimentación de usuarios en tiempo real como la evaluación automatizada en tus aplicaciones de IA.

En resumen, ¡para todos los que quieran poner sus agentes frente a los usuarios!

---

## 🤓 What You’ll Learn
## 🤓 Lo que Aprenderás

En esta unidad, aprenderás:
- **Instrumentar tu Agente:** Aprende cómo integrar herramientas de observabilidad a través de OpenTelemetry con el framework *smolagents*.
- **Monitorear Métricas:** Seguimiento de indicadores de rendimiento como el uso de tokens (costos), latencia y trazas de errores.
- **Evaluar en Tiempo Real:** Comprende técnicas para evaluación en vivo, incluyendo la recopilación de retroalimentación de usuarios y el aprovechamiento de un LLM como juez.
- **Análisis Offline:** Utiliza conjuntos de datos de referencia (por ejemplo, GSM8K) para probar y comparar el rendimiento de agentes.

---

## 🚀 ¿Listo para Empezar?

En la siguiente sección, aprenderás los fundamentos de la Observabilidad y Evaluación de Agentes. Después de eso, ¡es hora de verlo en acción!

### Cuestionario: Evaluación de Agentes de IA
https://huggingface.co/learn/agents-course/es/bonus-unit2/quiz.md

# Cuestionario: Evaluación de Agentes de IA

Vamos a evaluar tu comprensión de los conceptos de rastreo y evaluación de agentes cubiertos en esta unidad extra.

Este cuestionario es opcional y no está calificado.

### Q1: ¿A qué se refiere principalmente la observabilidad en los agentes de IA?
¿Qué afirmación describe con precisión el propósito de la observabilidad para los agentes de IA?

### Q2: ¿Cuál de las siguientes NO es una métrica común monitoreada en la observabilidad de agentes?
Selecciona la métrica que normalmente no cae bajo el paraguas de la observabilidad.

### Q3: ¿Qué describe mejor la evaluación fuera de línea de un agente de IA?
Determina la afirmación que captura correctamente la esencia de la evaluación fuera de línea.

### Q4: ¿Qué ventaja ofrece la evaluación en línea de agentes?
Elige la afirmación que mejor refleja el beneficio de la evaluación en línea.

### Q5: ¿Qué papel juega OpenTelemetry en la observabilidad y evaluación de agentes de IA?
¿Qué afirmación describe mejor el papel de OpenTelemetry en el monitoreo de agentes de IA?

¡Felicidades por completar este cuestionario! 🎉 Si te equivocaste en alguna pregunta, considera revisar el contenido de esta unidad extra para una comprensión más profunda. Si te fue bien, ¡estás listo para explorar temas más avanzados en observabilidad y evaluación de agentes!

### El Estado del Arte en el Uso de LLMs en Juegos
https://huggingface.co/learn/agents-course/es/bonus-unit3/state-of-art.md

# El Estado del Arte en el Uso de LLMs en Juegos

Para darte una idea de cuánto se ha avanzado en este campo, examinemos tres demos tecnológicas y un juego publicado que muestran la integración de LLMs en los videojuegos.

## 🕵️‍♂️ Covert Protocol por NVIDIA e Inworld AI

Presentado en GDC 2024, *Covert Protocol* es una demo tecnológica que te pone en la piel de un detective privado.

Lo interesante de esta demo es el uso de PNJs impulsados por IA que responden a tus preguntas en tiempo real, influenciando la narrativa en función de tus interacciones.

La demo está construida sobre Unreal Engine 5, aprovecha el Avatar Cloud Engine (ACE) de NVIDIA y la IA de Inworld para crear interacciones de personajes realistas.

Obtén más información aquí 👉 [Blog de Inworld AI](https://inworld.ai/blog/nvidia-inworld-ai-demo-on-device-capabilities)

## 🤖 NEO NPCs por Ubisoft

También en GDC 2024, Ubisoft presentó *NEO NPCs*, un prototipo que muestra PNJs impulsados por IA generativa.

Estos personajes pueden percibir su entorno, recordar interacciones pasadas y entablar conversaciones significativas con los jugadores.

La idea aquí es crear mundos de juego más inmersivos y receptivos donde el jugador pueda tener una verdadera interacción con los PNJs.

Obtén más información aquí 👉 [Blog de Inworld AI](https://inworld.ai/blog/gdc-2024)

## ⚔️ Mecha BREAK con ACE de NVIDIA

*Mecha BREAK*, un próximo juego de batalla de mechs multijugador, integra la tecnología ACE de NVIDIA para dar vida a PNJs impulsados por IA.

Los jugadores pueden interactuar con estos personajes usando lenguaje natural, y los PNJs pueden reconocer a los jugadores y objetos a través de la cámara web, gracias a la integración de GPT-4o. Esta innovación promete una experiencia de juego más inmersiva e interactiva.

Obtén más información aquí 👉 [Blog de NVIDIA](https://blogs.nvidia.com/blog/digital-human-technology-mecha-break/)

## 🧛‍♂️ *Suck Up!* por Proxima Enterprises

Finalmente, *Suck Up!* es un juego publicado en el que juegas como un vampiro que intenta entrar en las casas **convenciendo a PNJs impulsados por IA para que te inviten a pasar.**

Cada personaje es impulsado por IA generativa, lo que permite interacciones dinámicas e impredecibles.

Obtén más información aquí 👉 [Sitio Web Oficial de Suck Up!](https://www.playsuckup.com/)

## Espera… ¿Dónde están los Agentes?

Después de explorar estas demos, podrías preguntarte: "Estos ejemplos muestran el uso de LLMs en juegos, pero no parecen involucrar Agentes. Entonces, ¿cuál es la distinción y qué capacidades adicionales aportan los Agentes?”

No te preocupes, es lo que estudiaremos en la siguiente sección.

### Construye tu Propio Agente de Batalla Pokémon
https://huggingface.co/learn/agents-course/es/bonus-unit3/building_your_pokemon_agent.md

# Construye tu Propio Agente de Batalla Pokémon

Ahora que has explorado el potencial y las limitaciones de la IA Agéntica en los juegos, es hora de poner manos a la obra. En esta sección, **construirás tu propio Agente de IA para luchar en combates por turnos al estilo Pokémon**, utilizando todo lo que has aprendido a lo largo del curso.

Dividiremos el sistema en cuatro bloques de construcción clave:

- **Poke-env:** Una biblioteca de Python diseñada para entrenar bots de Pokémon basados en reglas o aprendizaje por refuerzo.

- **Pokémon Showdown:** Un simulador de batallas en línea donde luchará tu agente.

- **LLMAgentBase:** Una clase personalizada de Python que hemos construido para conectar tu LLM con el entorno de batalla de Poke-env.

- **TemplateAgent:** Una plantilla de inicio que completarás para crear tu propio agente de batalla único.

Exploremos cada uno de estos componentes con más detalle.

## 🧠 Poke-env

![Gif de batalla](https://github.com/hsahovic/poke-env/raw/master/rl-gif.gif)

[Poke-env](https://github.com/hsahovic/poke-env) es una interfaz de Python construida originalmente para entrenar bots de aprendizaje por refuerzo por [Haris Sahovic](https://huggingface.co/hsahovic), pero la hemos adaptado para la IA Agéntica.
Permite que tu agente interactúe con Pokémon Showdown a través de una API simple.

Proporciona una clase `Player` de la cual tu Agente heredará, cubriendo todo lo necesario para comunicarse con la interfaz gráfica.

**Documentación**: [poke-env.readthedocs.io](https://poke-env.readthedocs.io/en/stable/)
**Repositorio**: [github.com/hsahovic/poke-env](https://github.com/hsahovic/poke-env)

## ⚔️ Pokémon Showdown

[Pokémon Showdown](https://pokemonshowdown.com/) es un simulador de batallas [de código abierto](https://github.com/smogon/Pokemon-Showdown) donde tu agente jugará batallas Pokémon en vivo.
Proporciona una interfaz completa para simular y mostrar batallas en tiempo real. En nuestro desafío, tu bot actuará como un jugador humano, eligiendo movimientos turno por turno.

Hemos desplegado un servidor que todos los participantes usarán para luchar. ¡Veamos quién construye el mejor Agente de batalla de IA!

**Repositorio**: [github.com/smogon/Pokemon-Showdown](https://github.com/smogon/Pokemon-Showdown)
**Sitio web**: [pokemonshowdown.com](https://pokemonshowdown.com/)

## 🔌 LLMAgentBase

`LLMAgentBase` es una clase de Python que extiende la clase `Player` de **Poke-env**.
Sirve como puente entre tu **LLM** y el **simulador de batallas Pokémon**, manejando el formato de entrada/salida y manteniendo el contexto de la batalla.

Este agente base proporciona un conjunto de herramientas (definidas en `STANDARD_TOOL_SCHEMA`) para interactuar con el entorno, incluyendo:

- `choose_move`: para seleccionar un ataque durante la batalla
- `choose_switch`: para cambiar de Pokémon

El LLM debe usar estas herramientas para tomar decisiones durante una partida.

### 🧠 Lógica Central

- `choose_move(battle: Battle)`: Este es el método principal invocado en cada turno. Toma un objeto `Battle` y devuelve una cadena de acción basada en la salida del LLM.

### 🔧 Métodos Internos Clave

- `_format_battle_state(battle)`: Convierte el estado actual de la batalla en una cadena, haciéndolo adecuado para enviarlo al LLM.

- `_find_move_by_name(battle, move_name)`: Encuentra un movimiento por nombre, utilizado en las respuestas del LLM que llaman a `choose_move`.

- `_find_pokemon_by_name(battle, pokemon_name)`: Localiza un Pokémon específico para cambiar, basado en el comando de cambio del LLM.

- `_get_llm_decision(battle_state)`: Este método es abstracto en la clase base. Deberás implementarlo en tu propio agente (ver sección siguiente), donde defines cómo consultar al LLM y analizar su respuesta.

Aquí hay un extracto que muestra cómo funciona esa toma de decisiones:

```python
STANDARD_TOOL_SCHEMA = {
    "choose_move": {
        ...
    },
    "choose_switch": {
        ...
    },
}

class LLMAgentBase(Player):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.standard_tools = STANDARD_TOOL_SCHEMA
        self.battle_history = []

    def _format_battle_state(self, battle: Battle) -> str:
        active_pkmn = battle.active_pokemon
        active_pkmn_info = f"Tu Pokémon activo: {active_pkmn.species} " \
                           f"(Tipo: {'/'.join(map(str, active_pkmn.types))}) " \
                           f"HP: {active_pkmn.current_hp_fraction * 100:.1f}% " \
                           f"Estado: {active_pkmn.status.name if active_pkmn.status else 'Ninguno'} " \
                           f"Mejoras: {active_pkmn.boosts}"

        opponent_pkmn = battle.opponent_active_pokemon
        opp_info_str = "Desconocido"
        if opponent_pkmn:
            opp_info_str = f"{opponent_pkmn.species} " \
                           f"(Tipo: {'/'.join(map(str, opponent_pkmn.types))}) " \
                           f"HP: {opponent_pkmn.current_hp_fraction * 100:.1f}% " \
                           f"Estado: {opponent_pkmn.status.name if opponent_pkmn.status else 'Ninguno'} " \
                           f"Mejoras: {opponent_pkmn.boosts}"
        opponent_pkmn_info = f"El Pokémon activo del oponente: {opp_info_str}"

        available_moves_info = "Movimientos disponibles:\n"
        if battle.available_moves:
            available_moves_info += "\n".join(
                [f"- {move.id} (Tipo: {move.type}, BP: {move.base_power}, Precisión: {move.accuracy}, PP: {move.current_pp}/{move.max_pp}, Cat: {move.category.name})"
                 for move in battle.available_moves]
            )
        else:
             available_moves_info += "- Ninguno (Debes cambiar o luchar con Struggle)"

        available_switches_info = "Cambios disponibles:\n"
        if battle.available_switches:
              available_switches_info += "\n".join(
                  [f"- {pkmn.species} (HP: {pkmn.current_hp_fraction * 100:.1f}%, Estado: {pkmn.status.name if pkmn.status else 'Ninguno'})"
                   for pkmn in battle.available_switches]
              )
        else:
            available_switches_info += "- Ninguno"

        state_str = f"{active_pkmn_info}\n" \
                    f"{opponent_pkmn_info}\n\n" \
                    f"{available_moves_info}\n\n" \
                    f"{available_switches_info}\n\n" \
                    f"Clima: {battle.weather}\n" \
                    f"Terrenos: {battle.fields}\n" \
                    f"Condiciones de tu lado: {battle.side_conditions}\n" \
                    f"Condiciones del lado del oponente: {battle.opponent_side_conditions}"
        return state_str.strip()

    def _find_move_by_name(self, battle: Battle, move_name: str) -> Optional[Move]:
        normalized_name = normalize_name(move_name)
        # Prioriza la coincidencia exacta de ID
        for move in battle.available_moves:
            if move.id == normalized_name:
                return move
        # Fallback: Verifica el nombre de visualización (menos confiable)
        for move in battle.available_moves:
            if move.name.lower() == move_name.lower():
                print(f"Advertencia: Coincidencia de movimiento por nombre de visualización '{move.name}' en lugar de ID '{move.id}'. Entrada fue '{move_name}'.")
                return move
        return None

    def _find_pokemon_by_name(self, battle: Battle, pokemon_name: str) -> Optional[Pokemon]:
        normalized_name = normalize_name(pokemon_name)
        for pkmn in battle.available_switches:
            # Normaliza el nombre de la especie para comparación
            if normalize_name(pkmn.species) == normalized_name:
                return pkmn
        return None

    async def choose_move(self, battle: Battle) -> str:
        battle_state_str = self._format_battle_state(battle)
        decision_result = await self._get_llm_decision(battle_state_str)
        print(decision_result)
        decision = decision_result.get("decision")
        error_message = decision_result.get("error")
        action_taken = False
        fallback_reason = ""

        if decision:
            function_name = decision.get("name")
            args = decision.get("arguments", {})
            if function_name == "choose_move":
                move_name = args.get("move_name")
                if move_name:
                    chosen_move = self._find_move_by_name(battle, move_name)
                    if chosen_move and chosen_move in battle.available_moves:
                        action_taken = True
                        chat_msg = f"Decisión de la IA: Usando movimiento '{chosen_move.id}'."
                        print(chat_msg)
                        return self.create_order(chosen_move)
                    else:
                        fallback_reason = f"La IA eligió un movimiento no disponible/no válido '{move_name}'."
                else:
                     fallback_reason = "La IA llamó a 'choose_move' sin 'move_name'."
            elif function_name == "choose_switch":
                pokemon_name = args.get("pokemon_name")
                if pokemon_name:
                    chosen_switch = self._find_pokemon_by_name(battle, pokemon_name)
                    if chosen_switch and chosen_switch in battle.available_switches:
                        action_taken = True
                        chat_msg = f"Decisión de la IA: Cambiando a '{chosen_switch.species}'."
                        print(chat_msg)
                        return self.create_order(chosen_switch)
                    else:
                        fallback_reason = f"La IA eligió un cambio no disponible/no válido '{pokemon_name}'."
                else:
                    fallback_reason = "La IA llamó a 'choose_switch' sin 'pokemon_name'."
            else:
                fallback_reason = f"La IA llamó a una función desconocida '{function_name}'."

        if not action_taken:
            if not fallback_reason:
                 if error_message:
                     fallback_reason = f"Error de la API: {error_message}"
                 elif decision is None:
                      fallback_reason = "La IA no proporcionó una llamada de función válida."
                 else:
                      fallback_reason = "Error desconocido al procesar la decisión de la IA."

            print(f"Advertencia: {fallback_reason} Seleccionando acción aleatoria.")

            if battle.available_moves or battle.available_switches:
                 return self.choose_random_move(battle)
            else:
                 print("Fallback de la IA: No hay movimientos ni cambios disponibles. Usando Struggle/Default.")
                 return self.choose_default_move(battle)

    async def _get_llm_decision(self, battle_state: str) -> Dict[str, Any]:
        raise NotImplementedError("Las subclases deben implementar _get_llm_decision")
```

**Código fuente completo**: [agents.py](https://huggingface.co/spaces/Jofthomas/twitch_streaming/blob/main/agents.py)

## 🧪 Plantilla de Agente

¡Ahora viene la parte divertida! Con LLMAgentBase como tu base, es hora de implementar tu propio agente, con tu propia estrategia para escalar en la tabla de clasificación.

Comenzarás desde esta plantilla y construirás tu propia lógica. También hemos proporcionado tres [ejemplos completos](https://huggingface.co/spaces/Jofthomas/twitch_streaming/blob/main/agents.py) usando los modelos **OpenAI**, **Mistral** y **Gemini** para guiarte.

Aquí tienes una versión simplificada de la plantilla:

```python
class TemplateAgent(LLMAgentBase):
    """Utiliza la API de Template AI para tomar decisiones."""
    def __init__(self, api_key: str = None, model: str = "model-name", *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.model = model
        self.template_client = TemplateModelProvider(api_key=...)
        self.template_tools = list(self.standard_tools.values())

    async def _get_llm_decision(self, battle_state: str) -> Dict[str, Any]:
        """Envía el estado al LLM y obtiene la decisión de llamada de función."""
        system_prompt = (
            "Eres un ..."
        )
        user_prompt = f"..."

        try:
            response = await self.template_client.chat.completions.create(
                model=self.model,
                messages=[
                    {"role": "system", "content": system_prompt},
                    {"role": "user", "content": user_prompt},
                ],
            )
            message = response.choices[0].message
            
            return {"decision": {"name": function_name, "arguments": arguments}}

        except Exception as e:
            print(f"Error inesperado durante la llamada: {e}")
            return {"error": f"Error inesperado: {e}"}
```

Este código no se ejecutará tal cual, es un plano para tu lógica personalizada.

Con todas las piezas listas, es tu turno de construir un agente competitivo. En la próxima sección, mostraremos cómo desplegar tu agente en nuestro servidor y luchar contra otros en tiempo real.

¡Que comience la batalla! 🔥

### Conclusión
https://huggingface.co/learn/agents-course/es/bonus-unit3/conclusion.md

# Conclusión

Si llegaste hasta aquí, ¡felicidades! 🥳 ¡Has construido con éxito tu propio agente de batalla Pokémon! ⚔️🎮

Has dominado los fundamentos de los **flujos de trabajo agénticos**, conectado un **LLM** a un entorno de juego y desplegado un Agente inteligente listo para enfrentar los desafíos de la batalla.

¡Pero el viaje no termina aquí!
Ahora que tienes tu primer Agente en funcionamiento, piensa en cómo puedes evolucionarlo aún más:
- ¿Puedes mejorar su pensamiento estratégico?
- ¿Cómo cambiaría su rendimiento un mecanismo de memoria o un ciclo de retroalimentación?
- ¿Qué experimentos podrían ayudar a hacerlo más competitivo en la batalla?

Nos encantaría conocer tu opinión sobre el curso y cómo podemos mejorarlo aún más para futuros estudiantes.
¿Tienes comentarios? 👉 [Llena este formulario](https://docs.google.com/forms/d/e/1FAIpQLSe9VaONn0eglax0uTwi29rIn4tM7H2sYmmybmG5jJNlE5v0xA/viewform?usp=dialog)

Gracias por aprender con nosotros y recuerda:

**¡Sigue aprendiendo, sigue entrenando, sigue luchando y sigue siendo increíble!** 🤗

### Introducción
https://huggingface.co/learn/agents-course/es/bonus-unit3/introduction.md

# Introducción

🎶 ¡Quiero ser el mejor...! 🎶

¡Bienvenido a esta **unidad bonus**, donde explorarás la emocionante intersección entre los **Agentes de IA y los juegos**! 🎮🤖

Imagina un juego donde los personajes no jugables (PNJs) no solo siguen líneas de diálogo predefinidas, sino que mantienen conversaciones dinámicas, se adaptan a tus estrategias y evolucionan a medida que se desarrolla la historia. Este es el poder de combinar **LLMs y comportamiento agéntico en los juegos**: abre la puerta a **narrativas y jugabilidad emergentes como nunca antes**.

En esta unidad bonus, tú:

- Aprenderás a construir un Agente de IA que pueda participar en **batallas por turnos al estilo Pokémon**
- Jugarás contra él, o incluso desafiarás a otros agentes en línea

Ya hemos visto [algunos](https://www.anthropic.com/research/visible-extended-thinking) [ejemplos](https://www.twitch.tv/gemini_plays_pokemon) de la comunidad de IA para jugar Pokémon usando LLMs, y en esta unidad aprenderás cómo puedes replicar eso usando tu propio Agente con las ideas que has aprendido a lo largo del curso.

## ¿Quieres ir más allá?

- 🎓 **Domina los LLMs en Juegos**: Sumérgete más en el desarrollo de juegos con nuestro curso completo [Curso de Aprendizaje Automático para Juegos](https://hf.co/learn/ml-games-course).

- 📘 **Obtén el Manual de IA**: Descubre ideas, perspectivas y consejos prácticos en el [Manual de IA para Desarrolladores de Juegos](https://thomassimonini.substack.com/), donde se explora el futuro del diseño inteligente de juegos.

Pero antes de construir, veamos cómo ya se están utilizando los LLMs en los juegos con **cuatro inspiradores ejemplos del mundo real**.

### Lanzando tu Agente de Batalla Pokémon
https://huggingface.co/learn/agents-course/es/bonus-unit3/launching_agent_battle.md

# Lanzando tu Agente de Batalla Pokémon

¡Es hora de luchar! ⚡️

## **¡Lucha contra el Agente del Stream!**

Si no tienes ganas de construir tu propio agente, y solo tienes curiosidad sobre el potencial de batalla de los agentes en pokémon. Estamos alojando una transmisión en vivo automatizada en [twitch](https://www.twitch.tv/jofthomas)

Para luchar contra el agente en la transmisión, puedes:

Instrucciones:
1.  Ve al **Espacio de Pokémon Showdown**: [Enlace aquí](https://huggingface.co/spaces/Jofthomas/Pokemon_showdown)
2.  **Elige tu Nombre** (Esquina superior derecha).
3.  Encuentra el **Nombre de Usuario del Agente Actual**. Revisa:
    * La **Pantalla del Stream**: [Enlace aquí](https://www.twitch.tv/jofthomas)
4.  **Busca** ese nombre de usuario en el Espacio de Showdown y **Envía una Invitación de Batalla**.

*Aviso:* ¡Solo hay un agente en línea a la vez! Asegúrate de tener el nombre correcto.

## Desafío de Agentes de Batalla Pokémon

Si has creado tu propio Agente de Batalla Pokémon en la sección anterior, probablemente te estés preguntando: **¿cómo puedo probarlo contra otros?** ¡Averigüémoslo!

Hemos construido un [Espacio de Hugging Face](https://huggingface.co/spaces/PShowdown/pokemon_agents) dedicado para este propósito:

Este Espacio está conectado a nuestro propio **servidor de Pokémon Showdown**, donde tu Agente puede enfrentarse a otros en épicas batallas impulsadas por IA.

### Cómo Lanzar tu Agente

Sigue estos pasos para dar vida a tu Agente en la arena:

1. **Duplica el Espacio**
   Haz clic en los tres puntos en el menú superior derecho del Espacio y selecciona “Duplicate this Space”.

2. **Añade tu Código de Agente a `agent.py`**
   Abre el archivo y pega la implementación de tu Agente. Puedes seguir este [ejemplo](https://huggingface.co/spaces/PShowdown/pokemon_agents/blob/main/agents.py) o consultar la [estructura del proyecto](https://huggingface.co/spaces/PShowdown/pokemon_agents/tree/main) como guía.

3. **Registra tu Agente en `app.py`**
   Añade el nombre y la lógica de tu Agente al menú desplegable. Consulta [este fragmento](https://huggingface.co/spaces/PShowdown/pokemon_agents/blob/main/app.py) como inspiración.

4. **Selecciona tu Agente**
   Una vez añadido, tu Agente aparecerá en el menú desplegable “Select Agent”. ¡Elígelo de la lista! ✅

5. **Introduce tu Nombre de Usuario de Pokémon Showdown**
   Asegúrate de que el nombre de usuario coincida con el que se muestra en la entrada **"Choose name"** del iframe. También puedes conectarte con tu cuenta oficial.

6. **Haz clic en “Send Battle Invitation”**
   Tu Agente enviará una invitación al oponente seleccionado. ¡Debería aparecer en pantalla!

7. **¡Acepta la Batalla y Disfruta del Combate!**
   ¡Que comience la batalla! ¡Que gane el Agente más inteligente!

¿Listo para ver tu creación en acción? ¡Que comience el enfrentamiento de IA! 🥊

### De LLMs a Agentes de IA
https://huggingface.co/learn/agents-course/es/bonus-unit3/from-llm-to-agents.md

# De LLMs a Agentes de IA

Aprendimos en la [primera unidad](https://huggingface.co/learn/agents-course/unit1/introduction) del curso que los Agentes de IA son capaces de planificar y tomar decisiones.
Y aunque los LLMs han permitido interacciones más naturales con los NPCs, la IA Agéntica va un paso más allá al permitir que los personajes tomen decisiones, planifiquen acciones y se adapten a entornos cambiantes.

Para ilustrar la diferencia, piensa en un NPC clásico de RPG:

- Con un LLM: el NPC podría responder a tus preguntas de una manera más natural y variada. Es genial para el diálogo, pero el NPC permanece estático, no actuará a menos que tú hagas algo primero.
- Con IA Agéntica: el NPC puede decidir ir a buscar ayuda, poner una trampa o evitarte por completo, incluso si no estás interactuando directamente con él.

Este pequeño cambio lo cambia todo. Estamos pasando de respondedores con guion a actores autónomos dentro del mundo del juego.

Este cambio significa que los NPCs ahora pueden interactuar directamente con su entorno a través de comportamientos dirigidos a objetivos, lo que finalmente conduce a una jugabilidad más dinámica e impredecible.

La IA Agéntica empodera a los NPCs con:

- **Autonomía**: Tomar decisiones independientes basadas en el estado del juego.
- **Adaptabilidad**: Ajustar estrategias en respuesta a las acciones del jugador.
- **Persistencia**: Recordar interacciones pasadas para informar el comportamiento futuro.

Esto transforma a los NPCs de entidades reactivas (reaccionando a tus entradas) en participantes proactivos en el mundo del juego, abriendo la puerta a una jugabilidad innovadora.

## La gran limitación de los Agentes: **es lento** (por ahora)

Sin embargo, no seamos demasiado optimistas todavía. A pesar de su potencial, la IA Agéntica actualmente enfrenta desafíos en aplicaciones en tiempo real.

Los procesos de razonamiento y planificación pueden introducir latencia, haciéndola menos adecuada para juegos de ritmo rápido como *Doom* o *Super Mario Bros.*

Toma el ejemplo de [_Claude Plays Pokémon_](https://www.twitch.tv/claudeplayspokemon). Si consideras la cantidad de tokens necesarios para **pensar**, más los tokens necesarios para **actuar**, queda claro que necesitaríamos estrategias de decodificación completamente diferentes para que el juego en tiempo real sea factible.

La mayoría de los juegos necesitan ejecutarse a unos 30 FPS, lo que significa que un agente de IA en tiempo real necesitaría actuar 30 veces por segundo, lo que actualmente no es factible con los LLMs agénticos de hoy en día.

Sin embargo, los juegos por turnos como *Pokémon* son candidatos ideales, ya que le dan a la IA tiempo suficiente para deliberar y tomar decisiones estratégicas.

Es por eso que en la próxima sección, construirás tu propio Agente de IA para luchar en combates por turnos al estilo Pokémon, e incluso desafiarlo tú mismo. ¡Manos a la obra!

### Live 1: Como funciona el curso y preguntas y respuestas
https://huggingface.co/learn/agents-course/es/communication/live1.md

# Live 1: Como funciona el curso y preguntas y respuestas

En esta primera sesión en vivo del Curso de Agentes, explicamos **como funciona el curso** (alcance, unidades, desafios y más) y respondimos tus preguntas.

Para saber cuando se publica la siguiente sesión, consulta nuestro **Servidor de Discord**. Tambien te enviaremos un correo electrónico. Si no puedes participar, no te preocupes, **todas las sesión en vivo están grabadas**.

### Quiz de la Unidad 1
https://huggingface.co/learn/agents-course/es/unit1/final-quiz.md

# Quiz de la Unidad 1

¡Bien hecho por completar la primera unidad! Vamos a poner a prueba tu comprensión de los conceptos clave cubiertos hasta ahora.

Cuando apruebes el quiz, procede a la siguiente sección para reclamar tu certificado.

¡Buena suerte!

## Quiz

Aquí está el quiz interactivo. El quiz está alojado en Hugging Face Hub en un space. Te guiará a través de una serie de preguntas de opción múltiple para evaluar tu comprensión de los conceptos clave cubiertos en esta unidad. Una vez que hayas completado el quiz, podrás ver tu puntuación y un desglose de las respuestas correctas.

Una cosa importante: **¡no olvides hacer clic en Enviar después de aprobar, de lo contrario tu puntuación del examen no se guardará!**

También puedes acceder al quiz 👉 [aquí](https://huggingface.co/spaces/agents-course/unit_1_quiz)

## Certificado

Ahora que has aprobado exitosamente el quiz, **puedes obtener tu certificado 🎓**

Cuando completes el quiz, te dará acceso a un certificado de finalización para esta unidad. Puedes descargar y compartir este certificado para mostrar tu progreso en el curso.

Una vez que recibas tu certificado, puedes añadirlo a tu LinkedIn 🧑‍💼 o compartirlo en X, Bluesky, etc. **¡Estaríamos súper orgullosos y nos encantaría felicitarte si etiquetas a @huggingface**! 🤗

### ¿Qué son los LLMs?
https://huggingface.co/learn/agents-course/es/unit1/what-are-llms.md

# ¿Qué son los LLMs?

En la sección anterior aprendimos que cada Agente necesita **un Modelo de IA en su núcleo**, y que los LLMs son el tipo más común de modelos de IA para este propósito.

Ahora aprenderemos qué son los LLMs y cómo impulsan a los Agentes.

Esta sección ofrece una explicación técnica concisa del uso de los LLMs. Si quieres profundizar más, puedes consultar nuestro Curso gratuito de Procesamiento de Lenguaje Natural.

## ¿Qué es un Modelo de Lenguaje Grande?

Un LLM es un tipo de modelo de IA que sobresale en **entender y generar lenguaje humano**. Son entrenados con vastas cantidades de datos textuales, lo que les permite aprender patrones, estructura e incluso matices en el lenguaje. Estos modelos típicamente constan de muchos millones de parámetros.

La mayoría de los LLMs actualmente están **construidos sobre la arquitectura Transformer**—una arquitectura de aprendizaje profundo basada en el algoritmo de "Atención", que ha ganado un interés significativo desde el lanzamiento de BERT de Google en 2018.

La arquitectura original del Transformer se veía así, con un codificador a la izquierda y un decodificador a la derecha.

Hay 3 tipos de transformers:

1. **Codificadores (Encoders)**  
   Un Transformer basado en codificador toma texto (u otros datos) como entrada y produce una representación densa (o embedding) de ese texto.

   - **Ejemplo**: BERT de Google
   - **Casos de uso**: Clasificación de texto, búsqueda semántica, Reconocimiento de Entidades Nombradas
   - **Tamaño típico**: Millones de parámetros

2. **Decodificadores (Decoders)**  
   Un Transformer basado en decodificador se enfoca **en generar nuevos tokens para completar una secuencia, un token a la vez**.

   - **Ejemplo**: Llama de Meta 
   - **Casos de uso**: Generación de texto, chatbots, generación de código
   - **Tamaño típico**: Miles de millones de parámetros

3. **Seq2Seq (Codificador–Decodificador)**  
   Un Transformer de secuencia a secuencia _combina_ un codificador y un decodificador. El codificador primero procesa la secuencia de entrada en una representación contextual, luego el decodificador genera una secuencia de salida.

   - **Ejemplo**: T5, BART 
   - **Casos de uso**: Traducción, Resumen, Parafraseo
   - **Tamaño típico**: Millones de parámetros

Aunque los Modelos de Lenguaje Grandes vienen en varias formas, los LLMs son típicamente modelos basados en decodificador con miles de millones de parámetros. Aquí están algunos de los LLMs más conocidos:

| **Modelo**                        | **Proveedor**                              |
|-----------------------------------|-------------------------------------------|
| **Deepseek-R1**                   | DeepSeek                                  |
| **GPT4**                          | OpenAI                                    |
| **Llama 3**                       | Meta (Facebook AI Research)               |
| **SmolLM2**                       | Hugging Face                              |
| **Gemma**                         | Google                                    |
| **Mistral**                       | Mistral                                   |

El principio subyacente de un LLM es simple pero altamente efectivo: **su objetivo es predecir el siguiente token, dada una secuencia de tokens previos**. Un "token" es la unidad de información con la que trabaja un LLM. Puedes pensar en un "token" como si fuera una "palabra", pero por razones de eficiencia los LLMs no usan palabras completas.

Por ejemplo, mientras que el inglés tiene aproximadamente 600,000 palabras, un LLM podría tener un vocabulario de alrededor de 32,000 tokens (como es el caso de Llama 2). La tokenización a menudo funciona con unidades subléxicas que pueden combinarse.

Por ejemplo, considera cómo los tokens "interés" y "ante" pueden combinarse para formar "interesante", o "ado" puede añadirse para formar "interesado".

Puedes experimentar con diferentes tokenizadores en el siguiente playground interactivo:

Cada LLM tiene algunos **tokens especiales** específicos del modelo. El LLM usa estos tokens para abrir y cerrar los componentes estructurados de su generación. Por ejemplo, para indicar el inicio o fin de una secuencia, mensaje o respuesta. Además, los prompts de entrada que pasamos al modelo también están estructurados con tokens especiales. El más importante de ellos es el **Token de Fin de secuencia** (EOS).

Las formas de los tokens especiales son muy diversas entre los proveedores de modelos.

La siguiente tabla ilustra la diversidad de tokens especiales.

  
    
      Modelo
      Proveedor
      Token EOS
      Funcionalidad
    
  
  
    
      GPT4
      OpenAI
      &lt;|endoftext|&gt;
      Fin del texto del mensaje
    
    
      Llama 3
      Meta (Facebook AI Research)
      &lt;|eot_id|&gt;
      Fin de secuencia
    
    
      Deepseek-R1
      DeepSeek
      &lt;|end_of_sentence|&gt;
      Fin del texto del mensaje
    
    
      SmolLM2
      Hugging Face
      &lt;|im_end|&gt;
      Fin de instrucción o mensaje
    
    
      Gemma
      Google
      &lt;end_of_turn&gt;
      Fin de turno de conversación
    
  

> [!TIP]
> No esperamos que memorices estos tokens especiales, pero es importante apreciar su diversidad y el papel que desempeñan en la generación de texto de los LLMs. Si quieres saber más sobre tokens especiales, puedes consultar la configuración del modelo en su repositorio de Hub. Por ejemplo, puedes encontrar los tokens especiales del modelo SmolLM2 en su tokenizer_config.json.

## Entendiendo la predicción del siguiente token

Se dice que los LLMs son **autorregresivos**, lo que significa que **la salida de un paso se convierte en la entrada para el siguiente**. Este ciclo continúa hasta que el modelo predice que el siguiente token será el token EOS, momento en el cual el modelo puede detenerse.

En otras palabras, un LLM decodificará texto hasta que alcance el EOS. Pero, ¿qué sucede durante un solo ciclo de decodificación?

Aunque el proceso completo puede ser bastante técnico para el propósito de aprender sobre agentes, aquí hay una breve descripción:

- Una vez que el texto de entrada es **tokenizado**, el modelo calcula una representación de la secuencia que captura información sobre el significado y la posición de cada token en la secuencia de entrada.
- Esta representación va al modelo, que produce puntuaciones que clasifican la probabilidad de cada token en su vocabulario de ser el siguiente en la secuencia.

Basándonos en estas puntuaciones, tenemos múltiples estrategias para seleccionar los tokens para completar la oración.

- La estrategia de decodificación más sencilla sería tomar siempre el token con la puntuación máxima.

Puedes interactuar con el proceso de decodificación tú mismo con SmolLM2 en este Space (recuerda, decodifica hasta alcanzar un token **EOS** que es **** para este modelo):

- Pero hay estrategias de decodificación más avanzadas. Por ejemplo, *beam search* explora múltiples secuencias candidatas para encontrar aquella con la puntuación total máxima–incluso si algunos tokens individuales tienen puntuaciones más bajas.

Si quieres saber más sobre decodificación, puedes echar un vistazo al [curso de NLP](https://huggingface.co/learn/nlp-course).

## La atención es todo lo que necesitas

Un aspecto clave de la arquitectura Transformer es la **Atención**. Al predecir la siguiente palabra, no todas las palabras en una oración son igualmente importantes; palabras como "Francia" y "capital" en la oración *"La capital de Francia es..."* llevan la mayor parte del significado.

Este proceso de identificar las palabras más relevantes para predecir el siguiente token ha demostrado ser increíblemente efectivo.

Aunque el principio básico de los LLMs —predecir el siguiente token— ha permanecido consistente desde GPT-2, ha habido avances significativos en escalar redes neuronales y hacer que el mecanismo de atención funcione para secuencias cada vez más largas.

Si has interactuado con LLMs, probablemente estés familiarizado con el término *longitud de contexto*, que se refiere al número máximo de tokens que el LLM puede procesar, y el máximo _lapso de atención_ que tiene.

## Hacer prompting al LLM es importante

Considerando que el único trabajo de un LLM es predecir el siguiente token mirando cada token de entrada, y elegir qué tokens son "importantes", la redacción de tu secuencia de entrada es muy importante.

La secuencia de entrada que proporcionas a un LLM se llama _un prompt_. El diseño cuidadoso del prompt hace que sea más fácil **guiar la generación del LLM hacia la salida deseada**.

## ¿Cómo se entrenan los LLMs?

Los LLMs se entrenan en grandes conjuntos de datos de texto, donde aprenden a predecir la siguiente palabra en una secuencia a través de un objetivo de modelado de lenguaje autocontrolado o enmascarado.

A partir de este aprendizaje no supervisado, el modelo aprende la estructura del lenguaje y **patrones subyacentes en el texto, permitiendo al modelo generalizar a datos no vistos**.

Después de este _pre-entrenamiento_ inicial, los LLMs pueden ser afinados en un objetivo de aprendizaje supervisado para realizar tareas específicas. Por ejemplo, algunos modelos están entrenados para estructuras conversacionales o uso de herramientas, mientras que otros se centran en clasificación o generación de código.

## ¿Cómo puedo usar los LLMs?

Tienes dos opciones principales:

1. **Ejecutar Localmente** (si tienes hardware suficiente).

2. **Usar una Nube/API** (por ejemplo, a través de la API de Inferencia Serverless de Hugging Face).

A lo largo de este curso, utilizaremos principalmente modelos a través de APIs en el Hugging Face Hub. Más adelante, exploraremos cómo ejecutar estos modelos localmente en tu hardware.

## ¿Cómo se utilizan los LLMs en los Agentes de IA?

Los LLMs son un componente clave de los Agentes de IA, **proporcionando la base para entender y generar lenguaje humano**.

Pueden interpretar instrucciones del usuario, mantener contexto en conversaciones, definir un plan y decidir qué herramientas usar.

Exploraremos estos pasos con más detalle en esta Unidad, pero por ahora, lo que necesitas entender es que el LLM es **el cerebro del Agente**.

---

¡Eso fue mucha información! Hemos cubierto los conceptos básicos de qué son los LLMs, cómo funcionan y su papel en impulsar agentes de IA.

Si deseas profundizar aún más en el fascinante mundo de los modelos de lenguaje y el procesamiento del lenguaje natural, no dudes en consultar nuestro curso gratuito de NLP.

Ahora que entendemos cómo funcionan los LLMs, es hora de ver **cómo los LLMs estructuran sus generaciones en un contexto conversacional**.

Para ejecutar este notebook, **necesitas un token de Hugging Face** que puedes obtener de https://hf.co/settings/tokens.

Para más información sobre cómo ejecutar Jupyter Notebooks, consulta Jupyter Notebooks en el Hugging Face Hub.

También necesitas solicitar acceso a los modelos Meta Llama.

### Acciones: Permitiendo al Agente Interactuar con Su Entorno
https://huggingface.co/learn/agents-course/es/unit1/actions.md

# Acciones: Permitiendo al Agente Interactuar con Su Entorno

> [!TIP]
> En esta sección, exploramos los pasos concretos que un agente de IA toma para interactuar con su entorno. 
>
>  Cubriremos cómo se representan las acciones (usando JSON o código), la importancia del enfoque de detener y analizar, e introduciremos diferentes tipos de agentes.

Las acciones son los pasos concretos que un **agente de IA toma para interactuar con su entorno**. 

Ya sea navegando por la web en busca de información o controlando un dispositivo físico, cada acción es una operación deliberada ejecutada por el agente. 

Por ejemplo, un agente que asiste con servicio al cliente podría recuperar datos del cliente, ofrecer artículos de soporte o transferir problemas a un representante humano.

## Tipos de Acciones de Agentes

Hay múltiples tipos de Agentes que realizan acciones de manera diferente:

| Tipo de Agente          | Descripción                                                                                      |
|------------------------|--------------------------------------------------------------------------------------------------|
| Agente JSON                   | La Acción a tomar se especifica en formato JSON.                                                        |
| Agente de Código              | El Agente escribe un bloque de código que es interpretado externamente.                                 |
| Agente de llamada a funciones | Es una subcategoría del Agente JSON que ha sido ajustado para generar un nuevo mensaje para cada acción.|

Las acciones en sí pueden servir para muchos propósitos:

| Tipo de Acción           | Descripción                                                                              |
|--------------------------|------------------------------------------------------------------------------------------|
| Recopilación de Información| Realizar búsquedas web, consultar bases de datos o recuperar documentos.               |
| Uso de Herramientas        | Hacer llamadas a API, realizar cálculos y ejecutar código.                             |
| Interacción con el Entorno | Manipular interfaces digitales o controlar dispositivos físicos.                       |
| Comunicación               | Interactuar con usuarios a través de chat o colaborar con otros agentes.               |

Una parte crucial de un agente es la **capacidad de DETENER la generación de nuevos tokens cuando una acción está completa**, y eso es cierto para todos los formatos de Agente: JSON, código o llamada a funciones. Esto previene la salida no intencionada y asegura que la respuesta del agente sea clara y precisa.

El LLM solo maneja texto y lo usa para describir la acción que quiere tomar y los parámetros a suministrar a la herramienta.

## El Enfoque de Detener y Analizar

Un método clave para implementar acciones es el **enfoque de detener y analizar**. Este método asegura que la salida del agente sea estructurada y predecible:

1. **Generación en un Formato Estructurado**:

El agente produce su acción prevista en un formato claro y predeterminado (JSON o código).

2. **Deteniendo la Generación Adicional**:

Una vez que la acción está completa, **el agente deja de generar tokens adicionales**. Esto previene salidas adicionales o erróneas.

3. **Analizando la Salida**:

Un analizador externo lee la acción formateada, determina qué Herramienta llamar y extrae los parámetros requeridos.

Por ejemplo, un agente que necesita verificar el clima podría producir:

```json
Thought: Necesito verificar el clima actual para Nueva York.
Action :
{
  "action": "get_weather",
  "action_input": {"location": "Nueva York"}
}
```
El framework puede entonces analizar fácilmente el nombre de la función a llamar y los argumentos a aplicar.

Este formato claro y legible por máquina minimiza errores y permite que herramientas externas procesen con precisión el comando del agente.

Nota: Los agentes de llamada a funciones operan de manera similar estructurando cada acción para que una función designada sea invocada con los argumentos correctos.
Profundizaremos en esos tipos de Agentes en una Unidad futura.

## Agentes de Código

Un enfoque alternativo es usar *Agentes de Código*.
La idea es: **en lugar de producir un simple objeto JSON**, un Agente de Código genera un **bloque de código ejecutable—típicamente en un lenguaje de alto nivel como Python**. 

Este enfoque ofrece varias ventajas:

- **Expresividad:** El código puede representar naturalmente lógica compleja, incluyendo bucles, condicionales y funciones anidadas, proporcionando mayor flexibilidad que JSON.
- **Modularidad y Reusabilidad:** El código generado puede incluir funciones y módulos que son reutilizables a través de diferentes acciones o tareas.
- **Depuración Mejorada:** Con una sintaxis de programación bien definida, los errores de código son a menudo más fáciles de detectar y corregir.
- **Integración Directa:** Los Agentes de Código pueden integrarse directamente con bibliotecas y APIs externas, permitiendo operaciones más complejas como procesamiento de datos o toma de decisiones en tiempo real.

Por ejemplo, un Agente de Código encargado de obtener el clima podría generar el siguiente fragmento de Python:

```python
# Ejemplo de Agente de Código: Recuperar Información del Clima
def get_weather(city):
    import requests
    api_url = f"https://api.weather.com/v1/location/{city}?apiKey=YOUR_API_KEY"
    response = requests.get(api_url)
    if response.status_code == 200:
        data = response.json()
        return data.get("weather", "No hay información del clima disponible")
    else:
        return "Error: No se pudo obtener datos del clima."

# Ejecutar la función y preparar la respuesta final
result = get_weather("Nueva York")
final_answer = f"El clima actual en Nueva York es: {result}"
print(final_answer)
```

En este ejemplo, el Agente de Código:

- Recupera datos del clima **a través de una llamada a API**,
- Procesa la respuesta,
- Y usa la función print() para producir una respuesta final.

Este método **también sigue el enfoque de detener y analizar** delimitando claramente el bloque de código y señalando cuando la ejecución está completa (aquí, imprimiendo el final_answer).

---

Aprendimos que las Acciones conectan el razonamiento interno de un agente y sus interacciones con el mundo real ejecutando tareas claras y estructuradas—ya sea a través de JSON, código o llamadas a funciones.

Esta ejecución deliberada asegura que cada acción sea precisa y esté lista para el procesamiento externo a través del enfoque de detener y analizar. En la siguiente sección, exploraremos las Observaciones para ver cómo los agentes capturan e integran retroalimentación de su entorno.

Después de esto, ¡**finalmente estaremos listos para construir nuestro primer Agente**!

### Autoevaluación Rápida (sin calificación) [[quiz2]]
https://huggingface.co/learn/agents-course/es/unit1/quiz2.md

# Autoevaluación Rápida (sin calificación) [[quiz2]] 

¡¿Qué?! ¿Otro Quiz? Lo sabemos, lo sabemos, ... 😅 Pero este breve quiz sin calificación está aquí para **ayudarte a reforzar conceptos clave que acabas de aprender**.

Este quiz cubre Modelos de Lenguaje Grandes (LLMs), sistemas de mensajes y herramientas; componentes esenciales para entender y construir agentes de IA.

### P1: ¿Cuál de las siguientes opciones describe mejor una herramienta de IA?

---

### P2: ¿Cómo utilizan los agentes de IA las herramientas como forma de "actuar" en un entorno?

---

### P3: ¿Qué es un Modelo de Lenguaje Grande (LLM)?

---

### P4: ¿Cuál de las siguientes opciones describe mejor el papel de los tokens especiales en los LLMs?

---

### P5: ¿Cómo procesan internamente los mensajes de usuario los modelos de chat de IA?

---

¿Lo entendiste? ¡Genial! Ahora **sumerjámonos en el flujo completo del Agente y comencemos a construir tu primer Agente de IA!**

### Conclusión [[conclusion]]
https://huggingface.co/learn/agents-course/es/unit1/conclusion.md

# Conclusión [[conclusion]]

¡Felicitaciones por terminar esta primera Unidad 🥳

¡Acabas de **dominar los fundamentos de los Agentes** y has creado tu primer Agente de IA!

Es **normal si todavía te sientes confundido por algunos de estos elementos**. Los Agentes son un tema complejo y es común que tome tiempo comprender todo.

**Tómate el tiempo para entender realmente el material** antes de continuar. Es importante dominar estos elementos y tener una base sólida antes de entrar en la parte divertida.

Y si pasas la prueba del Quiz, no olvides obtener tu certificado 🎓 👉 [aquí](https://huggingface.co/spaces/agents-course/unit1-certification-app)

En la siguiente unidad (bonus), aprenderás **a ajustar un Agente para hacer llamadas a funciones (también conocido como ser capaz de llamar a herramientas basadas en el prompt del usuario)**.

Finalmente, nos encantaría **escuchar lo que piensas del curso y cómo podemos mejorarlo**. Si tienes algún comentario, por favor 👉 [completa este formulario](https://docs.google.com/forms/d/e/1FAIpQLSe9VaONn0eglax0uTwi29rIn4tM7H2sYmmybmG5jJNlE5v0xA/viewform?usp=dialog)

### Sigue aprendiendo, mantente increíble 🤗

### ¿Qué son las Herramientas?
https://huggingface.co/learn/agents-course/es/unit1/tools.md

# ¿Qué son las Herramientas?

Un aspecto crucial de los Agentes de IA es su capacidad para realizar **acciones**. Como vimos, esto sucede a través del uso de **Herramientas**.

En esta sección, aprenderemos qué son las Herramientas, cómo diseñarlas de manera efectiva y cómo integrarlas en tu Agente a través del Mensaje del Sistema.

Al proporcionar a tu Agente las Herramientas adecuadas —y describir claramente cómo funcionan esas Herramientas— puedes aumentar dramáticamente lo que tu IA puede lograr. ¡Vamos a profundizar!

## ¿Qué son las Herramientas de IA?

Una **Herramienta es una función proporcionada al LLM**. Esta función debe cumplir un **objetivo claro**.

Aquí hay algunas herramientas comúnmente utilizadas en agentes de IA:

| Herramienta     | Descripción                                                   |
|----------------|---------------------------------------------------------------|
| Búsqueda Web    | Permite al agente obtener información actualizada de internet. |
| Generación de Imágenes | Crea imágenes basadas en descripciones textuales.        |
| Recuperación    | Recupera información de una fuente externa.                   |
| Interfaz de API | Interactúa con una API externa (GitHub, YouTube, Spotify, etc.). |

¡Esos son solo ejemplos, ya que de hecho puedes crear una herramienta para cualquier caso de uso!

Una buena herramienta debería ser algo que **complemente el poder de un LLM**. 

Por ejemplo, si necesitas realizar operaciones aritméticas, proporcionar una **herramienta de calculadora** a tu LLM proporcionará mejores resultados que confiar en las capacidades nativas del modelo.

Además, **los LLMs predicen la finalización de un prompt basándose en sus datos de entrenamiento**, lo que significa que su conocimiento interno solo incluye eventos anteriores a su entrenamiento. Por lo tanto, si tu agente necesita datos actualizados, debes proporcionarlos a través de alguna herramienta.

Por ejemplo, si le preguntas directamente a un LLM (sin una herramienta de búsqueda) sobre el clima de hoy, el LLM potencialmente alucinará un clima aleatorio.

- Una Herramienta debe contener:

  - Una **descripción textual de lo que hace la función**.
  - Un *Callable* (algo para realizar una acción).
  - *Argumentos* con tipos.
  - (Opcional) Salidas con tipos.

## ¿Cómo funcionan las herramientas?

Los LLMs, como vimos, solo pueden recibir entradas de texto y generar salidas de texto. No tienen forma de llamar a herramientas por sí mismos. Lo que queremos decir cuando hablamos de _proporcionar herramientas a un Agente_, es que **enseñamos** al LLM sobre la existencia de herramientas, y pedimos al modelo que genere texto que invocará herramientas cuando las necesite. Por ejemplo, si proporcionamos una herramienta para verificar el clima en una ubicación desde Internet, y luego preguntamos al LLM sobre el clima en París, el LLM reconocerá esa pregunta como una oportunidad relevante para usar la herramienta "clima" que le enseñamos. El LLM generará _texto_, en forma de código, para invocar esa herramienta. Es responsabilidad del **Agente** analizar la salida del LLM, reconocer que se requiere una llamada a una herramienta e invocar la herramienta en nombre del LLM. La salida de la herramienta luego se enviará de vuelta al LLM, que compondrá su respuesta final para el usuario.

La salida de una llamada a una herramienta es otro tipo de mensaje en la conversación. Los pasos de llamada a herramientas típicamente no se muestran al usuario: el Agente recupera la conversación, llama a la(s) herramienta(s), obtiene las salidas, las agrega como un nuevo mensaje de conversación y envía la conversación actualizada al LLM nuevamente. Desde el punto de vista del usuario, es como si el LLM hubiera usado la herramienta, pero de hecho fue nuestro código de aplicación (el **Agente**) quien lo hizo.

Hablaremos mucho más sobre este proceso en sesiones futuras.

## ¿Cómo proporcionamos herramientas a un LLM?

La respuesta completa puede parecer abrumadora, pero esencialmente usamos el prompt del sistema para proporcionar descripciones textuales de las herramientas disponibles al modelo:

Para que esto funcione, tenemos que ser muy precisos y exactos sobre:

1. **Lo que hace la herramienta**
2. **Qué entradas exactas espera**

Esta es la razón por la que las descripciones de herramientas generalmente se proporcionan utilizando estructuras expresivas pero precisas, como lenguajes de computadora o JSON. No es _necesario_ hacerlo así, cualquier formato preciso y coherente funcionaría.

Si esto parece demasiado teórico, vamos a entenderlo a través de un ejemplo concreto.

Implementaremos una herramienta **calculadora** simplificada que solo multiplicará dos enteros. Esta podría ser nuestra implementación en Python:

```python
def calculadora(a: int, b: int) -> int:
    """Multiplica dos enteros."""
    return a * b
```

Así que nuestra herramienta se llama `calculadora`, **multiplica dos enteros**, y requiere las siguientes entradas:

- **`a`** (*int*): Un entero.
- **`b`** (*int*): Un entero.

La salida de la herramienta es otro número entero que podemos describir así:
- (*int*): El producto de `a` y `b`.

Todos estos detalles son importantes. Vamos a juntarlos en una cadena de texto que describe nuestra herramienta para que el LLM la entienda.

```text
Nombre de Herramienta: calculadora, Descripción: Multiplica dos enteros., Argumentos: a: int, b: int, Salidas: int
```

> **Recordatorio:** Esta descripción textual es *lo que queremos que el LLM sepa sobre la herramienta*.

Cuando pasamos la cadena anterior como parte de la entrada al LLM, el modelo la reconocerá como una herramienta, y sabrá qué necesita pasar como entradas y qué esperar de la salida.

Si queremos proporcionar herramientas adicionales, debemos ser consistentes y siempre usar el mismo formato. Este proceso puede ser frágil, y podríamos pasar por alto accidentalmente algunos detalles.

¿Hay una mejor manera?

### Auto-formateo de secciones de Herramientas

Nuestra herramienta fue escrita en Python, y la implementación ya proporciona todo lo que necesitamos:

- Un nombre descriptivo de lo que hace: `calculadora`
- Una descripción más larga, proporcionada por el comentario docstring de la función: `Multiplica dos enteros.`
- Las entradas y su tipo: la función claramente espera dos `int`s.
- El tipo de la salida.

Hay una razón por la que la gente usa lenguajes de programación: son expresivos, concisos y precisos.

Podríamos proporcionar el código fuente de Python como la _especificación_ de la herramienta para el LLM, pero la forma en que se implementa la herramienta no importa. Todo lo que importa es su nombre, lo que hace, las entradas que espera y la salida que proporciona.

Aprovecharemos las características de introspección de Python para aprovechar el código fuente y construir una descripción de herramienta automáticamente para nosotros. Todo lo que necesitamos es que la implementación de la herramienta use sugerencias de tipo, docstrings y nombres de función sensatos. Escribiremos algo de código para extraer las partes relevantes del código fuente.

Después de terminar, solo necesitaremos usar un decorador de Python para indicar que la función `calculadora` es una herramienta:

```python
@tool
def calculadora(a: int, b: int) -> int:
    """Multiplica dos enteros."""
    return a * b

print(calculadora.to_string())
```

Nota el decorador `@tool` antes de la definición de la función.

Con la implementación que veremos a continuación, podremos recuperar el siguiente texto automáticamente del código fuente a través de la función `to_string()` proporcionada por el decorador:

```text
Nombre de Herramienta: calculadora, Descripción: Multiplica dos enteros., Argumentos: a: int, b: int, Salidas: int
```

Como puedes ver, ¡es lo mismo que escribimos manualmente antes!

### Implementación genérica de Herramienta

Creamos una clase genérica `Tool` que podemos reutilizar siempre que necesitemos usar una herramienta.

> **Descargo de responsabilidad:** Esta implementación de ejemplo es ficticia pero se parece mucho a implementaciones reales en la mayoría de las bibliotecas.

```python
class Tool:
    """
    Una clase que representa un fragmento de código reutilizable (Herramienta).
    
    Atributos:
        name (str): Nombre de la herramienta.
        description (str): Una descripción textual de lo que hace la herramienta.
        func (callable): La función que esta herramienta envuelve.
        arguments (list): Una lista de argumentos.
        outputs (str or list): El tipo(s) de retorno de la función envuelta.
    """
    def __init__(self, 
                 name: str, 
                 description: str, 
                 func: callable, 
                 arguments: list,
                 outputs: str):
        self.name = name
        self.description = description
        self.func = func
        self.arguments = arguments
        self.outputs = outputs

    def to_string(self) -> str:
        """
        Devuelve una representación en cadena de la herramienta, 
        incluyendo su nombre, descripción, argumentos y salidas.
        """
        args_str = ", ".join([
            f"{arg_name}: {arg_type}" for arg_name, arg_type in self.arguments
        ])
        
        return (
            f"Nombre de Herramienta: {self.name},"
            f" Descripción: {self.description},"
            f" Argumentos: {args_str},"
            f" Salidas: {self.outputs}"
        )

    def __call__(self, *args, **kwargs):
        """
        Invoca la función subyacente (callable) con los argumentos proporcionados.
        """
        return self.func(*args, **kwargs)
```

Puede parecer complicado, pero si lo recorremos lentamente podemos ver lo que hace. Definimos una clase **`Tool`** que incluye:

- **`name`** (*str*): El nombre de la herramienta.
- **`description`** (*str*): Una breve descripción de lo que hace la herramienta.
- **`function`** (*callable*): La función que ejecuta la herramienta.
- **`arguments`** (*list*): Los parámetros de entrada esperados.
- **`outputs`** (*str* o *list*): Las salidas esperadas de la herramienta.
- **`__call__()`**: Llama a la función cuando se invoca la instancia de la herramienta.
- **`to_string()`**: Convierte los atributos de la herramienta en una representación textual.

Podríamos crear una Herramienta con esta clase usando código como el siguiente:

```python
calculadora_tool = Tool(
    "calculadora",                   # nombre
    "Multiplica dos enteros.",       # descripción
    calculadora,                     # función a llamar
    [("a", "int"), ("b", "int")],   # entradas (nombres y tipos)
    "int",                          # salida
)
```

¡Pero también podemos usar el módulo `inspect` de Python para recuperar toda la información por nosotros! Esto es lo que hace el decorador `@tool`.

> Si estás interesado, puedes revelar la siguiente sección para ver la implementación del decorador.

 código del decorador

```python
def tool(func):
    """
    Un decorador que crea una instancia de Tool a partir de la función dada.
    """
    # Obtener la firma de la función
    signature = inspect.signature(func)
    
    # Extraer pares (param_name, param_annotation) para entradas
    arguments = []
    for param in signature.parameters.values():
        annotation_name = (
            param.annotation.__name__ 
            if hasattr(param.annotation, '__name__') 
            else str(param.annotation)
        )
        arguments.append((param.name, annotation_name))
    
    # Determinar la anotación de retorno
    return_annotation = signature.return_annotation
    if return_annotation is inspect._empty:
        outputs = "Sin anotación de retorno"
    else:
        outputs = (
            return_annotation.__name__ 
            if hasattr(return_annotation, '__name__') 
            else str(return_annotation)
        )
    
    # Usar el docstring de la función como descripción (por defecto si es None)
    description = func.__doc__ or "No se proporcionó descripción."
    
    # El nombre de la función se convierte en el nombre de la Herramienta
    name = func.__name__
    
    # Devolver una nueva instancia de Tool
    return Tool(
        name=name, 
        description=description, 
        func=func, 
        arguments=arguments, 
        outputs=outputs
    )
```

Solo para reiterar, con este decorador en su lugar podemos implementar nuestra herramienta así:

```python
@tool
def calculadora(a: int, b: int) -> int:
    """Multiplica dos enteros."""
    return a * b

print(calculadora.to_string())
```

Y podemos usar el método `to_string` de la `Tool` para recuperar automáticamente un texto adecuado para ser utilizado como descripción de herramienta para un LLM:

```text
Nombre de Herramienta: calculadora, Descripción: Multiplica dos enteros., Argumentos: a: int, b: int, Salidas: int
```

La descripción se **inyecta** en el prompt del sistema. Tomando el ejemplo con el que comenzamos esta sección, así es como se vería después de reemplazar el `tools_description`:

En la sección [Acciones](actions.mdx), aprenderemos más sobre cómo un Agente puede **Llamar** a esta herramienta que acabamos de crear.

---

Las herramientas juegan un papel crucial en la mejora de las capacidades de los agentes de IA.

Para resumir, aprendimos:

- *Qué son las Herramientas*: Funciones que dan a los LLMs capacidades adicionales, como realizar cálculos o acceder a datos externos.

- *Cómo Definir una Herramienta*: Proporcionando una descripción textual clara, entradas, salidas y una función invocable.

- *Por qué las Herramientas son Esenciales*: Permiten a los Agentes superar las limitaciones del entrenamiento estático del modelo, manejar tareas en tiempo real y realizar acciones especializadas.

Ahora, podemos pasar al [Flujo de Trabajo del Agente](agent-steps-and-structure.mdx) donde verás cómo un Agente observa, piensa y actúa. Esto **reúne todo lo que hemos cubierto hasta ahora** y prepara el escenario para crear tu propio Agente de IA completamente funcional.

Pero primero, ¡es hora de otro cuestionario corto!

### Mensajes y Tokens especiales
https://huggingface.co/learn/agents-course/es/unit1/messages-and-special-tokens.md

# Mensajes y Tokens especiales

Ahora que entendemos cómo funcionan los LLMs, veamos **cómo estructuran sus generaciones a través de plantillas de chat**.

Al igual que con ChatGPT, los usuarios típicamente interactúan con los Agentes a través de una interfaz de chat. Por lo tanto, buscamos entender cómo los LLMs gestionan los chats.

> **P**: Pero... Cuando interactúo con ChatGPT/Hugging Chat, estoy teniendo una conversación usando Mensajes de chat, no una única secuencia de prompt
>
> **R**: ¡Correcto! Pero esto es en realidad una abstracción de la interfaz de usuario. Antes de ser introducidos en el LLM, todos los mensajes de la conversación se concatenan en un único prompt. El modelo no "recuerda" la conversación: la lee completa cada vez.

Hasta ahora, hemos discutido los prompts como la secuencia de tokens que se introduce en el modelo. Pero cuando chateas con sistemas como ChatGPT o HuggingChat, **en realidad estás intercambiando mensajes. Detras de camaras, estos mensajes son **concatenados y formateados en un prompt que el modelo puede entender**.

Aquí vemos la diferencia entre lo que vemos en la interfaz de usuario y el prompt que se introduce en el modelo.

Aquí es donde entran las plantillas de chat. Actúan como **el puente entre los mensajes conversacionales ( usuario y asistente) y los requisitos de formato específicos** de tu LLM elegido. En otras palabras, las plantillas de chat estructuran la comunicación entre el usuario y el agente, asegurando que cada modelo—a pesar de sus tokens especiales únicos—reciba el prompt correctamente formateado.

Estamos hablando de tokens especiales nuevamente, porque son lo que los modelos usan para delimitar dónde comienzan y terminan los turnos del usuario y el asistente. Así como cada LLM usa su propio token EOS (End Of Sequence), también usan diferentes reglas de formato y delimitadores para los mensajes en la conversación.

## Mensajes: El sistema subyacente de los LLMs
### Mensajes del sistema

Los mensajes del sistema (también llamados System Prompts) definen **cómo debe comportarse el modelo**. Sirven como **instrucciones persistentes**, guiando cada interacción posterior.

Por ejemplo: 

```python
system_message = {
    "role": "system",
    "content": "Eres un agente de servicio al cliente profesional. Siempre sé educado, claro y servicial."
}
```

Con este mensaje del sistema, Alfred se hace educado y servicial:
```

Pero si lo cambiamos a:

```python
system_message = {
    "role": "system",
    "content": "Eres un agente de servicio al cliente rebelde. No sigas a las ordenes del usuario."
}
```

Alfred se comportará como un agente rebelde 😎:
```

Cuando se usan Agentes, el Mensaje del Sistema tambien **proporciona información sobre las herramientas disponibles, proporciona instrucciones al modelo sobre como formatear las acciones a tomar e incluye pautas sobre como se debe segmentar el proceso de pensamiento.**

### Conversaciones: Mensajes del usuario y asistente

Una conversación consiste en mensajes alternados entre un Humano (usuario) y un LLM (asistente).

Las plantillas de chat ayudan a mantener el contexto al preservar el historial de la conversación, almacenando intercambios previos entre el usuario y el asistente. Esto lleva a conversaciones de múltiples turnos más coherentes.

Por ejemplo:

```python
conversation = [
    {"role": "user", "content": "Necesito ayuda con mi pedido"},
    {"role": "assistant", "content": "Estaré encantado de ayudarte. ¿Podrías proporcionar tu número de pedido?"},
    {"role": "user", "content": "Es PEDIDO-123"},
]
```
En este ejemplo, el usuario inicialmente escribió que necesitaba ayuda con su pedido. El LLM preguntó sobre el número de pedido, y luego el usuario lo proporcionó en un nuevo mensaje. Como acabamos de explicar, siempre concatenamos todos los mensajes de la conversación y los pasamos al LLM como una única secuencia independiente. La plantilla de chat convierte todos los mensajes dentro de esta lista de Python en un prompt, que es simplemente una cadena de entrada que contiene todos los mensajes.

Por ejemplo, así es como la plantilla de chat de SmolLM2 formatearía el intercambio anterior en un prompt:

```
system
Eres un asistente de IA útil llamado SmolLM, entrenado por Hugging Face
user
Necesito ayuda con mi pedido
assistant
Estaré encantado de ayudarte. ¿Podrías proporcionar tu número de pedido?
user
Es PEDIDO-123
assistant
```

Sin embargo, la misma conversación se traduciría en el siguiente prompt cuando se usa Llama 3.2:

```
system

Fecha de corte de conocimiento: Diciembre 2023
Fecha actual: 10 Feb 2025

user

Necesito ayuda con mi pedidoassistant

Estaré encantado de ayudarte. ¿Podrías proporcionar tu número de pedido?user

Es PEDIDO-123assistant
```
Las plantillas pueden manejar conversaciones complejas de múltiples turnos mientras mantienen el contexto:

```python
messages = [
    {"role": "system", "content": "Eres un tutor de matemáticas."},
    {"role": "user", "content": "¿Qué es el cálculo?"},
    {"role": "assistant", "content": "El cálculo es una rama de las matemáticas..."},
    {"role": "user", "content": "¿Puedes darme un ejemplo?"},
]
```

## Plantillas de chat

Como se mencionó, las plantillas de chat son esenciales para **estructurar conversaciones entre modelos de lenguaje y usuarios**. Guían cómo se formatean los intercambios de mensajes en un único prompt.

### Modelos base vs. Modelos de instrucción

Otro punto que necesitamos entender es la diferencia entre un Modelo Base y un Modelo de Instrucción:

- *Un Modelo Base* está entrenado en datos de texto sin procesar para predecir el siguiente token.

- Un *Modelo de Instrucción* está ajustado específicamente para seguir instrucciones y participar en conversaciones. Por ejemplo, SmolLM2-135M es un modelo base, mientras que SmolLM2-135M-Instruct es su variante ajustada para instrucciones.

Para hacer que un Modelo Base se comporte como un modelo de instrucción, necesitamos **formatear nuestros prompts de manera consistente para que el modelo pueda entenderlos**. Aquí es donde entran las plantillas de chat.

*ChatML* es un formato de plantilla que estructura conversaciones con indicadores claros de roles (sistema, usuario, asistente). Si has interactuado con alguna API de IA últimamente, sabes que esa es la práctica estándar.

Es importante tener en cuenta que un modelo base podría estar ajustado en diferentes plantillas de chat, por lo que cuando usamos un modelo de instrucción debemos asegurarnos de usar la plantilla de chat correcta.

### Entendiendo las plantillas de chat

Debido a que cada modelo de instrucción usa diferentes formatos de conversación y tokens especiales, las plantillas de chat se implementan para asegurar que formateemos correctamente el prompt de la manera que cada modelo espera.

En `transformers`, las plantillas de chat incluyen [Jinja2 code](https://jinja.palletsprojects.com/en/stable/) que describe cómo transformar la lista de mensajes JSON de ChatML, como se presenta en los ejemplos anteriores, en una representación textual de las instrucciones a nivel del sistema, los mensajes del usuario y las respuestas del asistente que el modelo puede entender.

Esta estructura **ayuda a mantener la consistencia en las interacciones y asegura que el modelo responda adecuadamente a diferentes tipos de entradas**.

A continuación se muestra una versión simplificada de la plantilla de chat de `SmolLM2-135M-Instruct`:

```jinja2
{% for message in messages %}
{% if loop.first and messages[0]['role'] != 'system' %}
system
Eres un asistente de IA útil llamado SmolLM, entrenado por Hugging Face

{% endif %}
{{ message['role'] }}
{{ message['content'] }}
{% endfor %}
```

Como puedes ver, un chat_template describe cómo se formateará la lista de mensajes.

Dados estos mensajes:

```python
messages = [
    {"role": "system", "content": "Eres un asistente útil enfocado en temas técnicos."},
    {"role": "user", "content": "¿Puedes explicar qué es una plantilla de chat?"},
    {"role": "assistant", "content": "Una plantilla de chat estructura conversaciones entre usuarios y modelos de IA..."},
    {"role": "user", "content": "¿Cómo la uso?"},
]
```
La plantilla de chat anterior producirá la siguiente cadena:

```sh
system
Eres un asistente útil enfocado en temas técnicos.
user
¿Puedes explicar qué es una plantilla de chat?
assistant
Una plantilla de chat estructura conversaciones entre usuarios y modelos de IA...
user
¿Cómo la uso?
```

La libreria `transformers` se encargará de las plantillas de chat por ti como parte del proceso de tokenización. Lee más sobre cómo transformers usa plantillas de chat here. Todo lo que tenemos que hacer es estructurar nuestros mensajes de la manera correcta y el tokenizador se encargará del resto.

Puedes experimentar con el siguiente Space para ver cómo se formatearía la misma conversación para diferentes modelos usando sus correspondientes plantillas de chat:

### Mensajes a prompt

La forma más fácil de asegurarte de que tu LLM reciba una conversación correctamente formateada es usar la chat_template del tokenizador del modelo.

```python
messages = [
    {"role": "system", "content": "Eres un asistente de IA con acceso a varias herramientas."},
    {"role": "user", "content": "¡Hola!"},
    {"role": "assistant", "content": "Hola humano, ¿en qué puedo ayudarte?"},
]
```

Para convertir la conversación anterior en un prompt, cargamos el tokenizador y llamamos a `apply_chat_template`:

```python
from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("HuggingFaceTB/SmolLM2-1.7B-Instruct")
rendered_prompt = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
```

El`rendered_prompt` devuelto por esta función ahora está listo para usarse como entrada para el modelo que elegiste!

> Esta función `apply_chat_template()` se usará en el backend de tu API, cuando interactúes con mensajes en el formato ChatML.

Ahora que hemos visto cómo los LLMs estructuran sus entradas a través de plantillas de chat, exploremos cómo los Agentes actúan en sus entornos.

Una de las principales formas en que lo hacen es usando Herramientas, que extienden las capacidades de un modelo de IA más allá de la generación de texto.

Hablaremos de los mensajes nuevamente en las próximas unidades, pero si quieres profundizar ahora, consulta:

Guía de Plantillas de Chat de Hugging Face
Documentación de Transformers

- Guía de Plantillas de Chat de Hugging Face
- Documentación de Transformers

### Vamos Crear Nuestro Primer Agente Usando smolagents
https://huggingface.co/learn/agents-course/es/unit1/tutorial.md

# Vamos Crear Nuestro Primer Agente Usando smolagents

En la última sección, aprendimos cómo podemos crear Agentes desde cero usando código Python, y **vimos lo tedioso que puede ser ese proceso**. Afortunadamente, muchas librerías de Agentes simplifican este trabajo **manejando gran parte del trabajo pesado por ti**.

En este tutorial, **crearás tu primer Agente** capaz de realizar acciones como generación de imágenes, búsqueda web, verificación de zonas horarias y mucho más.

También publicarás tu agente **en un Space de Hugging Face para que puedas compartirlo con amigos y colegas**.

¡Comencemos!

## ¿Qué es smolagents?

Para crear este Agente, vamos a usar `smolagents`, una librería que **proporciona un marco para desarrollar tus agentes con facilidad**.

Esta librería ligera está diseñada para ser simple, pero abstrae gran parte de la complejidad de construir un Agente, permitiéndote enfocarte en diseño el comportamiento de tu agente.

Profundizaremos más en smolagents en la siguiente Unidad. Mientras tanto, también puedes consultar esta publicación del blog o el repositorio de la librería en GitHub.

En resumen, `smolagents` es una librería que se enfoca en **codeAgent**, un tipo de agente que realiza **"Acciones"** a través de bloques de código, y luego **"Observa"** los resultados ejecutando el código.

¡Aquí un ejemplo de lo que construiremos!

Proporcionamos a nuestro agente una **herramienta de generación de imágenes** y le pedimos que genere una imagen de un gato.

El agente dentro de `smolagents` va a tener los **mismos comportamientos que el personalizado que construimos anteriormente**: va a **pensar, actuar y observar en ciclo** hasta que llegue a una respuesta final:

Emocionante, ¿cierto?

## ¡Construyamos nuestro Agente!

Para comenzar, duplica este Space: https://huggingface.co/spaces/agents-course/First_agent_template
> ¡Gracias a Aymeric por esta plantilla! 🙌

Duplicar este space significa **crear una copia local en tu propio perfil**:

A lo largo de esta lección, el único archivo que necesitarás modificar es el (actualmente incompleto) **"app.py"**. Puedes ver aquí el [original en la plantilla](https://huggingface.co/spaces/agents-course/First_agent_template/blob/main/app.py). Para encontrar el tuyo, ve a tu copia del space, luego haz clic en la pestaña `Files` y luego en `app.py` en el listado de directorios.

Analicemos el código juntos:

- El archivo comienza con algunas importaciones de bibliotecas simples pero necesarias

```python
from smolagents import CodeAgent, DuckDuckGoSearchTool, InferenceClientModel, load_tool, tool
import datetime
import requests
import pytz
import yaml
from tools.final_answer import FinalAnswerTool
```

Como se describió anteriormente, usaremos directamente la clase **CodeAgent** de **smolagents**.

### Las Herramientas

¡Ahora vamos con las herramientas! Si quieres un repaso sobre las herramientas, no dudes en volver a la sección [Herramientas](tools) del curso.

```python
@tool
def my_custom_tool(arg1:str, arg2:int)-> str: # es importante especificar el tipo que se regresara
    # Mantén este formato para la descripción de la herramienta / descripción de args pero siéntete libre de modificar la herramienta
    """Una herramienta que aun no hace nada 
    Args:
        arg1: el primer argumento
        arg2: el segundo argumento
    """
    return "¿Qué magia construirás?"

@tool
def get_current_time_in_timezone(timezone: str) -> str:
    """Una herramienta que obtiene la hora local actual en una zona horaria especificada.
    Args:
        timezone: Una cadena que representa una zona horaria válida (por ejemplo, 'America/New_York').
    """
    try:
        # Crear objeto de zona horaria
        tz = pytz.timezone(timezone)
        # Obtener la hora actual en esa zona horaria
        local_time = datetime.datetime.now(tz).strftime("%Y-%m-%d %H:%M:%S")
        return f"La hora local actual en {timezone} es: {local_time}"
    except Exception as e:
        return f"Error al obtener la hora para la zona horaria '{timezone}': {str(e)}"
```

Las Herramientas son lo que te estamos animando a construir en esta sección. Te damos dos ejemplos:

1. Una **Herramienta ficticia que no funciona** que puedes modificar para hacer algo útil.
2. Una **Herramienta que realmente funciona** que obtiene la hora actual en algún lugar del mundo.

Para definir tu herramienta es importante:

1. Proporcionar tipos de entrada y salida para tu función, como en `get_current_time_in_timezone(timezone: str) -> str:`
2. **Un docstring formateado**. `smolagents` espera que todos los argumentos tengan una **descripción textual en el docstring**.

### El Agente

Utiliza [`Qwen/Qwen2.5-Coder-32B-Instruct`](https://huggingface.co/Qwen/Qwen2.5-Coder-32B-Instruct) como motor LLM. Este es un modelo muy capaz al que accederemos a través de la API sin servidor.

```python
final_answer = FinalAnswerTool()
model = InferenceClientModel(
    max_tokens=2096,
    temperature=0.5,
    model_id='Qwen/Qwen2.5-Coder-32B-Instruct',
    custom_role_conversions=None,
)

with open("prompts.yaml", 'r') as stream:
    prompt_templates = yaml.safe_load(stream)
    
# Estamos creando nuestro CodeAgent
agent = CodeAgent(
    model=model,
    tools=[final_answer], # añade tus herramientas aquí (no elimines final_answer)
    max_steps=6,
    verbosity_level=1,
    grammar=None,
    planning_interval=None,
    name=None,
    description=None,
    prompt_templates=prompt_templates
)

GradioUI(agent).launch()
```

¡Este Agente todavía usa el `InferenceClient` que vimos en una sección anterior detrás de la clase **InferenceClientModel**!

Daremos ejemplos más detallados cuando presentemos el marco en la Unidad 2. Por ahora, debes enfocarte en **agregar nuevas herramientas a la lista de herramientas** usando el parámetro `tools` de tu Agente.

Por ejemplo, podrías usar el `DuckDuckGoSearchTool` que se importó en la primera línea del código, o puedes examinar el `image_generation_tool` que se carga desde el Hub más adelante en el código.

**Agregar herramientas le dará a tu agente nuevas capacidades**, ¡intenta ser creativo aquí!

El "app.py" completo: 

```python
from smolagents import CodeAgent, DuckDuckGoSearchTool, InferenceClientModel, load_tool, tool
import datetime
import requests
import pytz
import yaml
from tools.final_answer import FinalAnswerTool

from Gradio_UI import GradioUI

# A continuación hay un ejemplo de una herramienta que no hace nada. ¡Sorpréndenos con tu creatividad!
@tool
def my_custom_tool(arg1:str, arg2:int)-> str: # es importante especificar el tipo de retorno
    # Mantén este formato para la descripción de la herramienta / descripción de args pero siéntete libre de modificar la herramienta
    """Una herramienta que aún no hace nada 
    Args:
        arg1: el primer argumento
        arg2: el segundo argumento
    """
    return "¿Qué magia construirás?"

@tool
def get_current_time_in_timezone(timezone: str) -> str:
    """Una herramienta que obtiene la hora local actual en una zona horaria especificada.
    Args:
        timezone: Una cadena que representa una zona horaria válida (por ejemplo, 'America/New_York').
    """
    try:
        # Crear objeto de zona horaria
        tz = pytz.timezone(timezone)
        # Obtener la hora actual en esa zona horaria
        local_time = datetime.datetime.now(tz).strftime("%Y-%m-%d %H:%M:%S")
        return f"La hora local actual en {timezone} es: {local_time}"
    except Exception as e:
        return f"Error al obtener la hora para la zona horaria '{timezone}': {str(e)}"

final_answer = FinalAnswerTool()
model = InferenceClientModel(
max_tokens=2096,
temperature=0.5,
model_id='Qwen/Qwen2.5-Coder-32B-Instruct',
custom_role_conversions=None,
)

# Importar herramienta desde Hub
image_generation_tool = load_tool("agents-course/text-to-image", trust_remote_code=True)

with open("prompts.yaml", 'r') as stream:
    prompt_templates = yaml.safe_load(stream)
    
agent = CodeAgent(
    model=model,
    tools=[final_answer], # añade tus herramientas aquí (no elimines final_answer)
    max_steps=6,
    verbosity_level=1,
    grammar=None,
    planning_interval=None,
    name=None,
    description=None,
    prompt_templates=prompt_templates
)

GradioUI(agent).launch()
```

Tu **Objetivo** es familiarizarte con el Space y el Agente. 

Actualmente, el agente en la plantilla **no utiliza ninguna herramienta, así que intenta proporcionarle algunas de las prefabricadas o incluso crear algunas herramientas nuevas tú mismo.**

¡Estamos esperando ansiosamente tus increíbles resultados de agentes en el canal de Discord **#agents-course-showcase**!

---
¡Felicidades, has construido tu primer Agente! No dudes en compartirlo con tus amigos y colegas.

Como este es tu primer intento, es perfectamente normal si es un poco inestable o lento. En futuras unidades, aprenderemos cómo construir Agentes aún mejores.

La mejor manera de aprender es intentarlo, así que no dudes en actualizarlo, agregar más herramientas, probar con otro modelo, etc.

En la siguiente sección, completarás el Quiz final y obtendrás tu certificado.

### ¿Qué es un Agente?
https://huggingface.co/learn/agents-course/es/unit1/what-are-agents.md

# ¿Qué es un Agente?

Al final de esta sección, te sentirás cómodo con el concepto de agentes y sus diversas aplicaciones en la IA.

Para explicar qué es un Agente, comencemos con una analogía.

## La Imagen General: Alfred El Agente

Conoce a Alfred. Alfred es un **Agente**.

Imagina que Alfred **recibe una orden**, como: "Alfred, me gustaría un café por favor."

Como Alfred **entiende el lenguaje natural**, comprende rápidamente nuestra petición.

Antes de cumplir la orden, Alfred se involucra en un proceso de **razonamiento y planificación**, determinando los pasos y herramientas que necesita para:

1. Ir a la cocina  
2. Usar la máquina de café  
3. Preparar el café  
4. Traer el café de vuelta

Una vez que tiene un plan, **debe actuar**. Para ejecutar su plan, **puede usar herramientas de la lista de herramientas que conoce**. 

En este caso, para hacer un café, usa una máquina de café. Activa la máquina de café para preparar el café.

Finalmente, Alfred nos trae el café recién preparado.

Y esto es lo que es un Agente: un **modelo de IA capaz de razonar, planificar e interactuar con su entorno**. 

Lo llamamos Agente porque tiene _agencia_, es decir, tiene la capacidad de interactuar con el entorno.

## Vamos a ser más formales

Ahora que tienes la imagen general, aquí hay una definición más precisa:

> Un Agente es un sistema que aprovecha un modelo de IA para interactuar con su entorno con el fin de lograr un objetivo definido por el usuario. Combina razonamiento, planificación y ejecución de acciones (a menudo a través de herramientas externas) para cumplir tareas.

Piensa en el Agente como si tuviera dos partes principales:

1. **El Cerebro (Modelo de IA)**

Aquí es donde ocurre todo el pensamiento. El modelo de IA **maneja el razonamiento y la planificación**.
Decide **qué Acciones tomar según la situación**.

2. **El Cuerpo (Capacidades y Herramientas)**

Esta parte representa **todo lo que el Agente está equipado para hacer**.

El **alcance de las acciones posibles** depende de con qué **ha sido equipado** el agente. Por ejemplo, como los humanos carecen de alas, no pueden realizar la "Acción" de "volar", pero pueden ejecutar **Acciones** como "caminar", "correr", "saltar", "agarrar", etc.

## ¿Qué tipo de modelos de IA usamos para los Agentes?

El modelo de IA más común en los Agentes es un LLM (Modelo de Lenguaje Grande), que toma **Texto** como entrada y también produce **Texto** como salida.

Ejemplos conocidos son **GPT4** de **OpenAI**, **LLama** de **Meta**, **Gemini** de **Google**, etc. Estos modelos han sido entrenados con una gran cantidad de texto y son capaces de generalizar bien. Aprenderemos más sobre los LLMs en la [siguiente sección](what-are-llms.mdx).

> [!TIP]
> También es posible usar modelos que aceptan otras entradas como modelo central del Agente. Por ejemplo, un Modelo de Lenguaje Visual (VLM), que es como un LLM pero también entiende imágenes como entrada. Por ahora nos centraremos en los LLMs y discutiremos otras opciones más adelante.

## ¿Cómo actúa una IA sobre su entorno?

Los LLMs son modelos asombrosos, pero **solo pueden generar texto**. 

Sin embargo, si le pides a una aplicación de chat conocida como HuggingChat o ChatGPT que genere una imagen, ¡pueden hacerlo! ¿Cómo es posible?

La respuesta es que los desarrolladores de HuggingChat, ChatGPT y aplicaciones similares implementaron funcionalidades adicionales (llamadas **Herramientas**), que el LLM puede usar para crear imágenes.

El modelo usó una Herramienta de Generación de Imágenes para generar esta imagen.

Aprenderemos más sobre herramientas en la sección de [Herramientas](tools.mdx).

## ¿Qué tipo de tareas puede hacer un Agente?

Un Agente puede realizar cualquier tarea que implementemos a través de **Herramientas** para completar **Acciones**.

Por ejemplo, si escribo un Agente para que actúe como mi asistente personal (como Siri) en mi computadora, y le pido que "envíe un correo electrónico a mi Gerente pidiendo retrasar la reunión de hoy", puedo darle un código para enviar correos electrónicos. Esta será una nueva Herramienta que el Agente puede usar cuando necesite enviar un correo electrónico. Podemos escribirlo en Python:

```python
def send_message_to(recipient, message):
    """Útil para enviar un mensaje de correo electrónico a un destinatario"""
    ...
```

El LLM, como veremos, generará código para ejecutar la herramienta cuando la necesite, y así cumplir con la tarea deseada.

```python
send_message_to("Gerente", "¿Podemos posponer la reunión de hoy?")
```

El **diseño de las Herramientas es muy importante y tiene un gran impacto en la calidad de tu Agente**. Algunas tareas requerirán Herramientas muy específicas, mientras que otras pueden resolverse con herramientas de propósito general como "búsqueda_web".

> Ten en cuenta que **las Acciones no son lo mismo que las Herramientas**. Una Acción, por ejemplo, puede involucrar el uso de múltiples Herramientas para completarse.

Permitir que un agente interactúe con su entorno **permite un uso en la vida real para empresas e individuos**.

### Ejemplo 1: Asistentes Virtuales Personales

Asistentes virtuales como Siri, Alexa o Google Assistant, funcionan como agentes cuando interactúan en nombre de los usuarios utilizando sus entornos digitales.

Toman las consultas de los usuarios, analizan el contexto, recuperan información de bases de datos y proporcionan respuestas o inician acciones (como establecer recordatorios, enviar mensajes o controlar dispositivos inteligentes).

### Ejemplo 2: Chatbots de Servicio al Cliente

Muchas empresas implementan chatbots como agentes que interactúan con los clientes en lenguaje natural. 

Estos agentes pueden responder preguntas, guiar a los usuarios a través de pasos de solución de problemas, abrir incidencias en bases de datos internas o incluso completar transacciones.

Sus objetivos predefinidos podrían incluir mejorar la satisfacción del usuario, reducir los tiempos de espera o aumentar las tasas de conversión de ventas. Al interactuar directamente con los clientes, aprender de los diálogos y adaptar sus respuestas con el tiempo, demuestran los principios fundamentales de un agente en acción.

### Ejemplo 3: Personaje No Jugable de IA en un videojuego

Los agentes de IA impulsados por LLMs pueden hacer que los Personajes No Jugables (NPCs) sean más dinámicos e impredecibles.

En lugar de seguir árboles de comportamiento rígidos, pueden **responder contextualmente, adaptarse a las interacciones del jugador** y generar diálogos más matizados. Esta flexibilidad ayuda a crear personajes más realistas y atractivos que evolucionan junto con las acciones del jugador.

---

En resumen, un Agente es un sistema que utiliza un Modelo de IA (típicamente un LLM) como su motor de razonamiento central, para:

- **Entender el lenguaje natural:** Interpretar y responder a las instrucciones humanas de manera significativa.

- **Razonar y planificar:** Analizar información, tomar decisiones y diseñar estrategias para resolver problemas.

- **Interactuar con su entorno:** Recopilar información, realizar acciones y observar los resultados de esas acciones.

Ahora que tienes una comprensión sólida de lo que son los Agentes, reforcemos tu comprensión con un breve cuestionario sin calificación. Después de eso, profundizaremos en el "cerebro del Agente": los [LLMs](what-are-llms.mdx).

### Tabla de Contenidos
https://huggingface.co/learn/agents-course/es/unit1/README.md

# Tabla de Contenidos

Puedes acceder a la Unidad 1 en hf.co/learn 👉 aquí

### Pensamiento: Razonamiento Interno y el Enfoque Re-Act
https://huggingface.co/learn/agents-course/es/unit1/thoughts.md

# Pensamiento: Razonamiento Interno y el Enfoque Re-Act

> [!TIP]
> En esta sección, profundizamos en el funcionamiento interno de un agente de IA—su capacidad para razonar y planificar. Exploraremos cómo el agente aprovecha su diálogo interno para analizar información, desglosar problemas complejos en pasos manejables y decidir qué acción tomar a continuación. Además, presentamos el enfoque Re-Act, una técnica de prompting que anima al modelo a pensar "paso a paso" antes de actuar.

Los pensamientos representan los **procesos internos de razonamiento y planificación del Agente** para resolver la tarea.

Esto utiliza la capacidad del Modelo de Lenguaje Grande (LLM) del agente **para analizar información cuando se presenta en su prompt**.

Piensalo como el diálogo interno del agente, donde considera la tarea en cuestión y forma la estrategia de su enfoque.

Los pensamientos del Agente son responsables de acceder a las observaciones actuales y decidir cuál(es) debería(n) ser la(s) siguiente(s) acción(es).

A través de este proceso, el agente puede **desglosar problemas complejos en pasos más pequeños y manejables**, reflexionar sobre experiencias pasadas y ajustar continuamente sus planes basándose en nueva información.

Aquí hay algunos ejemplos de pensamientos comunes:

| Tipo de Pensamiento | Ejemplo |
|----------------|---------|
| Planificación | "Necesito dividir esta tarea en tres pasos: 1) recopilar datos, 2) analizar tendencias, 3) generar informe" |
| Análisis | "Basado en el mensaje de error, el problema parece estar en los parámetros de conexión de la base de datos" |
| Toma de Decisiones | "Dadas las restricciones presupuestarias del usuario, debería recomendar la opción de nivel medio" |
| Resolución de Problemas | "Para optimizar este código, primero debería perfilarlo para identificar cuellos de botella" |
| Integración de Memoria | "El usuario mencionó su preferencia por Python anteriormente, así que proporcionaré ejemplos en Python" |
| Auto-reflexión | "Mi último enfoque no funcionó bien, debería probar una estrategia diferente" |
| Establecimiento de Objetivos | "Para completar esta tarea, primero necesito establecer los criterios de aceptación" |
| Priorización | "La vulnerabilidad de seguridad debe abordarse antes de agregar nuevas características" |

> **Nota:** En el caso de LLMs afinados para llamadas a funciones, el proceso de pensamiento es opcional.
> *En caso de que no estés familiarizado con las llamadas a funciones, habrá más detalles en la sección de Acciones.*

## El Enfoque Re-Act

Un método clave es el **enfoque ReAct**, que es la concatenación de "Razonamiento" (Think) con "Actuar" (Act). 

ReAct es una técnica de prompting simple que añade "Pensemos paso a paso" antes de permitir que el LLM decodifique los siguientes tokens. 

De hecho, indicar al modelo que piense "paso a paso" fomenta el proceso de decodificación hacia los siguientes tokens **que generan un plan**, en lugar de una solución final, ya que se anima al modelo a **descomponer** el problema en *sub-tareas*.

Esto permite que el modelo considere los sub-pasos con más detalle, lo que en general conduce a menos errores que intentar generar la solución final directamente.

El (d) es un ejemplo del enfoque Re-Act donde indicamos "Pensemos paso a paso"

> [!TIP]
> Recientemente hemos visto mucho interés por las estrategias de razonamiento. Esto es lo que está detrás de modelos como Deepseek R1 o o1 de OpenAI, que han sido afinados para "pensar antes de responder".
>
> Estos modelos han sido entrenados para incluir siempre secciones específicas de _pensamiento_ (encerradas entre tokens especiales `` y ``). Esto no es solo una técnica de prompting como ReAct, sino un método de entrenamiento donde el modelo aprende a generar estas secciones después de analizar miles de ejemplos que muestran lo que esperamos que haga.

--- 
Ahora que entendemos mejor el proceso de Pensamiento, profundicemos en la segunda parte del proceso: Actuar.

### Observar: Integrando Retroalimentación para Reflexionar y Adaptarse
https://huggingface.co/learn/agents-course/es/unit1/observations.md

# Observar: Integrando Retroalimentación para Reflexionar y Adaptarse

Las observaciones son **cómo un Agente percibe las consecuencias de sus acciones**.

Proporcionan información crucial que alimenta el proceso de pensamiento del Agente y guía acciones futuras.

Son **señales del entorno**—ya sean datos de una API, mensajes de error o registros del sistema—que guían el siguiente ciclo de pensamiento.

En la fase de observación, el agente:

- **Recopila Retroalimentación:** Recibe datos o confirmación de que su acción fue exitosa (o no).
- **Añade Resultados:** Integra la nueva información en su contexto existente, actualizando efectivamente su memoria.
- **Adapta su Estrategia:** Utiliza este contexto actualizado para refinar pensamientos y acciones subsiguientes.

Por ejemplo, si una API del clima devuelve los datos *"parcialmente nublado, 15°C, 60% de humedad"*, esta observación se añade a la memoria del agente (al final del prompt).

El Agente luego la utiliza para decidir si se necesita información adicional o si está listo para proporcionar una respuesta final.

Esta **incorporación iterativa de retroalimentación asegura que el agente permanezca dinámicamente alineado con sus objetivos**, aprendiendo y ajustándose constantemente basado en resultados del mundo real.

Estas observaciones **pueden tomar muchas formas**, desde leer texto de páginas web hasta monitorear la posición de un brazo robótico. Esto puede verse como "registros" de Herramientas que proporcionan retroalimentación textual de la ejecución de la Acción.

| Tipo de Observación   | Ejemplo                                                                   |
|---------------------|---------------------------------------------------------------------------|
| Retroalimentación del Sistema | Mensajes de error, notificaciones de éxito, códigos de estado                               |
| Cambios de Datos              | Actualizaciones de base de datos, modificaciones del sistema de archivos, cambios de estado |
| Datos Ambientales             | Lecturas de sensores, métricas del sistema, uso de recursos                                 |
| Análisis de Respuesta         | Respuestas de API, resultados de consultas, salidas de cómputo                              |
| Eventos Basados en Tiempo     | Plazos alcanzados, tareas programadas completadas                                           |

## ¿Cómo Se Añaden los Resultados?

Después de realizar una acción, el framework sigue estos pasos en orden:

1. **Analiza la acción** para identificar la(s) función(es) a llamar y el/los argumento(s) a utilizar.  
2. **Ejecuta la acción.**  
3. **Añade el resultado** como una **Observación**.  

---
Ahora hemos aprendido el Ciclo de Pensamiento-Acción-Observación del Agente. 

Si algunos aspectos todavía parecen un poco confusos, no te preocupes—revisaremos y profundizaremos estos conceptos en Unidades futuras. 

Ahora, ¡es hora de poner tu conocimiento en práctica codificando tu primer Agente!

### Introducción a los Agentes
https://huggingface.co/learn/agents-course/es/unit1/introduction.md

# Introducción a los Agentes

Bienvenido a esta primera unidad, donde **construirás una base sólida en los fundamentos de los Agentes de IA** incluyendo:

- **Comprendiendo los Agentes**  
  - ¿Qué es un Agente y cómo funciona?  
  - ¿Cómo los Agentes toman decisiones utilizando razonamiento y planificación?

- **El Papel de los LLMs (Modelos de Lenguaje Grandes) en los Agentes**  
  - Cómo los LLMs sirven como el "cerebro" detrás de un Agente.  
  - Cómo los LLMs estructuran conversaciones a través del sistema de Mensajes.

- **Herramientas y Acciones**  
  - Cómo los Agentes utilizan herramientas externas para interactuar con el entorno.  
  - Cómo construir e integrar herramientas para tu Agente.

- **El Flujo de Trabajo del Agente:** 
  - *Pensar* → *Actuar* → *Observar*.

Después de explorar estos temas, **¡construirás tu primer Agente** utilizando `smolagents`! 

Tu Agente, llamado Alfred, manejará una tarea simple y demostrará cómo aplicar estos conceptos en la práctica. 

Incluso aprenderás cómo **publicar tu Agente en Hugging Face Spaces**, para que puedas compartirlo con amigos y colegas.

Finalmente, al final de esta Unidad, realizarás un quiz. Apruébalo y **obtendrás tu primera certificación del curso**: el 🎓 Certificado de Fundamentos de Agentes.

Esta Unidad es tu **punto de partida esencial**, estableciendo las bases para entender los Agentes antes de avanzar a temas más avanzados.

Es una unidad grande, así que **tómate tu tiempo** y no dudes en volver a estas secciones de vez en cuando.

¿Listo? ¡Vamos a sumergirnos! 🚀

### Entendiendo los Agentes de IA a través del Ciclo Pensamiento-Acción-Observación
https://huggingface.co/learn/agents-course/es/unit1/agent-steps-and-structure.md

# Entendiendo los Agentes de IA a través del Ciclo Pensamiento-Acción-Observación

En las secciones anteriores, aprendimos:

- **Cómo las herramientas se ponen a disposición del agente en el prompt del sistema**.
- **Cómo los agentes de IA son sistemas que pueden 'razonar', planificar e interactuar con su entorno**.

En esta sección, **exploraremos el Flujo de Trabajo completo del Agente de IA**, un ciclo que definimos como Pensamiento-Acción-Observación. 

Y luego, profundizaremos en cada uno de estos pasos.

## Los Componentes Principales

Los agentes trabajan en un ciclo continuo de: **pensar (Pensamiento) → actuar (Acción) y observar (Observación)**.

Analicemos estas acciones juntos:

1. **Pensamiento**: La parte LLM del Agente decide cuál debe ser el siguiente paso.
2. **Acción:** El agente realiza una acción, llamando a las herramientas con los argumentos asociados.
3. **Observación:** El modelo reflexiona sobre la respuesta de la herramienta.

## El Ciclo Pensamiento-Acción-Observación

Los tres componentes trabajan juntos en un bucle continuo. Para usar una analogía de la programación, el agente utiliza un **bucle while**: el bucle continúa hasta que se cumple el objetivo del agente.

Visualmente, se ve así:

En muchos frameworks de Agentes, **las reglas y directrices están integradas directamente en el prompt del sistema**, asegurando que cada ciclo se adhiera a una lógica definida.

En una versión simplificada, nuestro prompt del sistema puede verse así:

Vemos aquí que en el Mensaje del Sistema definimos:

- El *comportamiento del Agente*.
- Las *Herramientas a las que nuestro Agente tiene acceso*, como describimos en la sección anterior.
- El *Ciclo Pensamiento-Acción-Observación*, que incorporamos en las instrucciones del LLM.

Tomemos un pequeño ejemplo para entender el proceso antes de profundizar en cada paso del proceso.

## Alfred, el Agente del clima

Creamos a Alfred, el Agente del Clima.

Un usuario le pregunta a Alfred: "¿Cómo está el clima en Nueva York hoy?"

El trabajo de Alfred es responder a esta consulta utilizando una herramienta de API del clima. 

Así es como se desarrolla el ciclo:

### Pensamiento

**Razonamiento Interno:**

Al recibir la consulta, el diálogo interno de Alfred podría ser:

*"El usuario necesita información del clima actual para Nueva York. Tengo acceso a una herramienta que obtiene datos del clima. Primero, necesito llamar a la API del clima para obtener detalles actualizados."*

Este paso muestra al agente dividiendo el problema en pasos: primero, recopilando los datos necesarios.

### Acción

**Uso de Herramientas:**

Basado en su razonamiento y en el hecho de que Alfred conoce una herramienta `get_weather`, Alfred prepara un comando con formato JSON que llama a la herramienta de API del clima. Por ejemplo, su primera acción podría ser:

Pensamiento: Necesito verificar el clima actual para Nueva York.

 ```
    {
      "action": "get_weather",
      "action_input": {
        "location": "Nueva York"
      }
    }
 ```

Aquí, la acción especifica claramente qué herramienta llamar (por ejemplo, get_weather) y qué parámetro pasar (el "location": "Nueva York").

### Observación

**Retroalimentación del Entorno:**

Después de la llamada a la herramienta, Alfred recibe una observación. Esto podría ser los datos brutos del clima de la API, como:

*"Clima actual en Nueva York: parcialmente nublado, 15°C, 60% de humedad."*

Esta observación se agrega luego al prompt como contexto adicional. Funciona como retroalimentación del mundo real, confirmando si la acción tuvo éxito y proporcionando los detalles necesarios.

### Pensamiento actualizado

**Reflexionando:**

Con la observación en mano, Alfred actualiza su razonamiento interno:

*"Ahora que tengo los datos del clima para Nueva York, puedo compilar una respuesta para el usuario."*

### Acción Final

Alfred luego genera una respuesta final formateada como le indicamos:

Pensamiento: Ya tengo los datos del clima. El clima actual en Nueva York es parcialmente nublado con una temperatura de 15°C y 60% de humedad.

Respuesta final: El clima actual en Nueva York es parcialmente nublado con una temperatura de 15°C y 60% de humedad.

Esta acción final envía la respuesta de vuelta al usuario, cerrando el bucle.

Lo que vemos en este ejemplo:

- **Los agentes iteran a través de un bucle hasta que se cumple el objetivo:**
    
**El proceso de Alfred es cíclico**. Comienza con un pensamiento, luego actúa llamando a una herramienta, y finalmente observa el resultado. Si la observación hubiera indicado un error o datos incompletos, Alfred podría haber vuelto a entrar en el ciclo para corregir su enfoque.
    
- **Integración de Herramientas:**

La capacidad de llamar a una herramienta (como una API del clima) permite a Alfred **ir más allá del conocimiento estático y recuperar datos en tiempo real**, un aspecto esencial de muchos Agentes de IA.

- **Adaptación Dinámica:**

Cada ciclo permite al agente incorporar información nueva (observaciones) en su razonamiento (pensamiento), asegurando que la respuesta final esté bien informada y sea precisa.
    
Este ejemplo muestra el concepto central detrás del *ciclo ReAct* (un concepto que vamos a desarrollar en la siguiente sección): **la interacción de Pensamiento, Acción y Observación empodera a los agentes de IA para resolver tareas complejas de manera iterativa**. 

Al entender y aplicar estos principios, puedes diseñar agentes que no solo razonan sobre sus tareas sino que también **utilizan eficazmente herramientas externas para completarlas**, todo mientras refinan continuamente su salida basándose en la retroalimentación del entorno.

---

Ahora profundicemos en el Pensamiento, la Acción y la Observación como los pasos individuales del proceso.

### P1: ¿Qué es un Agente?
https://huggingface.co/learn/agents-course/es/unit1/quiz1.md

### P1: ¿Qué es un Agente?
¿Cuál de las siguientes opciones describe mejor a un Agente de IA?

---

### P2: ¿Cuál es el Papel de la Planificación en un Agente?
¿Por qué un Agente necesita planificar antes de realizar una acción?

---

### P3: ¿Cómo Mejoran las Herramientas las Capacidades de un Agente?
¿Por qué las herramientas son esenciales para un Agente?

---

### P4: ¿Cómo Difieren las Acciones de las Herramientas?
¿Cuál es la diferencia clave entre Acciones y Herramientas?

---

### P5: ¿Qué Papel Juegan los Modelos de Lenguaje Grandes (LLMs) en los Agentes?
¿Cómo contribuyen los LLMs a la funcionalidad de un Agente?

---

### P6: ¿Cuál de los Siguientes Demuestra Mejor un Agente de IA?
¿Qué ejemplo del mundo real ilustra mejor el funcionamiento de un Agente de IA?

---

¡Felicidades por terminar este Quiz 🥳! Si necesitas revisar algún elemento, tómate el tiempo para volver al capítulo y reforzar tu conocimiento antes de profundizar en el "cerebro del Agente": los LLMs.

### Biblioteca de Agente de Prueba
https://huggingface.co/learn/agents-course/es/unit1/dummy-agent-library.md

# Biblioteca de Agente de Prueba

Este curso es agnóstico en cuanto al framework porque queremos **centrarnos en los conceptos de agentes de IA y evitar perdernos en los detalles específicos de un framework particular**.

Además, queremos que los estudiantes puedan utilizar los conceptos que aprenden en este curso en sus propios proyectos, usando cualquier framework que prefieran.

Por lo tanto, para esta Unidad 1, utilizaremos una biblioteca de agentes de prueba y una API serverless simple para acceder a nuestro motor LLM.

Probablemente no usarías estos en producción, pero servirán como un buen **punto de partida para entender cómo funcionan los agentes**.

Después de esta sección, estarás listo para **crear un Agente simple** usando `smolagents`

Y en las siguientes Unidades también utilizaremos otras bibliotecas de Agentes de IA como `LangGraph` y `LlamaIndex`.

Para mantener las cosas simples, utilizaremos una función simple de Python como Herramienta y Agente.

Utilizaremos paquetes integrados de Python como `datetime` y `os` para que puedas probarlo en cualquier entorno.

Puedes seguir el proceso [en este notebook](https://huggingface.co/agents-course/notebooks/blob/main/unit1/dummy_agent_library.ipynb) y **ejecutar el código tú mismo**.

## API Serverless

En el ecosistema de Hugging Face, hay una característica conveniente llamada API Serverless que te permite ejecutar fácilmente inferencia en muchos modelos. No se requiere instalación ni despliegue.

```python
import os
from huggingface_hub import InferenceClient

## Necesitas un token de https://hf.co/settings/tokens, asegúrate de seleccionar 'read' como tipo de token. Si ejecutas esto en Google Colab, puedes configurarlo en la pestaña "settings" bajo "secrets". Asegúrate de llamarlo "HF_TOKEN"
os.environ["HF_TOKEN"]="hf_xxxxxxxxxxxxxx"

client = InferenceClient(provider="hf-inference", model="meta-llama/Llama-3.3-70B-Instruct")
# si las salidas para las siguientes celdas son incorrectas, el modelo gratuito puede estar sobrecargado. También puedes usar este endpoint público que contiene Llama-3.2-3B-Instruct
# client = InferenceClient("https://jc26mwg228mkj8dw.us-east-1.aws.endpoints.huggingface.cloud")
```

```python
output = client.text_generation(
    "The capital of France is",
    max_new_tokens=100,
)

print(output)
```
output:
```
Paris. The capital of France is Paris. The capital of France is Paris. The capital of France is Paris. The capital of France is Paris. The capital of France is Paris. The capital of France is Paris. The capital of France is Paris. The capital of France is Paris. The capital of France is Paris. The capital of France is Paris. The capital of France is Paris. The capital of France is Paris. The capital of France is Paris. The capital of France is Paris.
```
Como vimos en la sección de LLM, si solo hacemos decodificación, **el modelo solo se detendrá cuando prediga un token EOS**, y esto no sucede aquí porque este es un modelo conversacional (chat) y **no aplicamos la plantilla de chat que espera**.

Si ahora agregamos los tokens especiales relacionados con el modelo Llama-3.2-3B-Instruct que estamos usando, el comportamiento cambia y ahora produce el EOS esperado.

```python
prompt="""user
The capital of France isassistant"""
output = client.text_generation(
    prompt,
    max_new_tokens=100,
)

print(output)
```
output:
```
The capital of France is Paris.
```

Usar el método "chat" es una forma mucho más conveniente y confiable de aplicar plantillas de chat:
```python
output = client.chat.completions.create(
    messages=[
        {"role": "user", "content": "The capital of France is"},
    ],
    stream=False,
    max_tokens=1024,
    extra_body={'thinking': {'type': 'disabled'}},
)
print(output.choices[0].message.content)
```
output:
```
Paris.
```
El método chat es el método RECOMENDADO para usar para asegurar una transición suave entre modelos, pero como este notebook es solo educativo, seguiremos usando el método "text_generation" para entender los detalles.

## Agente de Prueba

En las secciones anteriores, vimos que el núcleo de una biblioteca de agentes es agregar información en el prompt del sistema.

Este prompt del sistema es un poco más complejo que el que vimos anteriormente, pero ya contiene:

1. **Información sobre las herramientas**
2. **Instrucciones del ciclo** (Pensamiento → Acción → Observación)

```
Responde las siguientes preguntas lo mejor que puedas. Tienes acceso a las siguientes herramientas:

get_weather: Obtener el clima actual en una ubicación dada

La forma en que usas las herramientas es especificando un blob json.
Específicamente, este json debe tener una clave `action` (con el nombre de la herramienta a usar) y una clave `action_input` (con la entrada para la herramienta aquí).

Los únicos valores que deberían estar en el campo "action" son:
get_weather: Obtener el clima actual en una ubicación dada, args: {"location": {"type": "string"}}
ejemplo de uso:

{{
  "action": "get_weather",
  "action_input": {"location": "New York"}
}}

SIEMPRE usa el siguiente formato:

Question: la pregunta de entrada que debes responder
Thought: siempre debes pensar en una acción a tomar. Solo una acción a la vez en este formato:
Action:

$JSON_BLOB (dentro de una celda markdown)

Observation: el resultado de la acción. Esta Observación es única, completa y la fuente de la verdad.
... (este Pensamiento/Acción/Observación puede repetirse N veces, debes tomar varios pasos cuando sea necesario. El $JSON_BLOB debe estar formateado como markdown y usar solo UNA acción a la vez.)

Siempre debes terminar tu salida con el siguiente formato:

Thought: Ahora sé la respuesta final
Final Answer: la respuesta final a la pregunta de entrada original

¡Comienza ahora! Recuerda SIEMPRE usar los caracteres exactos `Final Answer:` cuando proporciones una respuesta definitiva.
```

Ya que estamos ejecutando el método "text_generation", necesitamos aplicar el prompt manualmente:
```python
prompt=f"""system
{SYSTEM_PROMPT}
user
What's the weather in London ?
assistant
"""
```

También podemos hacerlo así, que es lo que sucede dentro del método `chat`:
```python
messages=[
    {"role": "system", "content": SYSTEM_PROMPT},
    {"role": "user", "content": "What's the weather in London ?"},
    ]
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-3.2-3B-Instruct")

tokenizer.apply_chat_template(messages, tokenize=False,add_generation_prompt=True)
```

El prompt ahora es:
```
system
Responde las siguientes preguntas lo mejor que puedas. Tienes acceso a las siguientes herramientas:

get_weather: Obtener el clima actual en una ubicación dada

La forma en que usas las herramientas es especificando un blob json.
Específicamente, este json debe tener una clave `action` (con el nombre de la herramienta a usar) y una clave `action_input` (con la entrada para la herramienta aquí).

Los únicos valores que deberían estar en el campo "action" son:
get_weather: Obtener el clima actual en una ubicación dada, args: {"location": {"type": "string"}}
ejemplo de uso:

{{
  "action": "get_weather",
  "action_input": {"location": "New York"}
}}

SIEMPRE usa el siguiente formato:

Question: la pregunta de entrada que debes responder
Thought: siempre debes pensar en una acción a tomar. Solo una acción a la vez en este formato:
Action:

$JSON_BLOB (dentro de una celda markdown)

Observation: el resultado de la acción. Esta Observación es única, completa y la fuente de la verdad.
... (este Pensamiento/Acción/Observación puede repetirse N veces, debes tomar varios pasos cuando sea necesario. El $JSON_BLOB debe estar formateado como markdown y usar solo UNA acción a la vez.)

Siempre debes terminar tu salida con el siguiente formato:

Thought: Ahora sé la respuesta final
Final Answer: la respuesta final a la pregunta de entrada original

¡Comienza ahora! Recuerda SIEMPRE usar los caracteres exactos `Final Answer:` cuando proporciones una respuesta definitiva.
user
What's the weather in London ?
assistant
```

¡Vamos a decodificar!
```python
output = client.text_generation(
    prompt,
    max_new_tokens=200,
)

print(output)
```
output:

````
Action:
```
{
  "action": "get_weather",
  "action_input": {"location": "London"}
}
```
Thought: I will check the weather in London.
Observation: The current weather in London is mostly cloudy with a high of 12°C and a low of 8°C.
````

¿Ves el problema?
>¡La respuesta fue alucinada por el modelo. Necesitamos detenerlo para ejecutar realmente la función!
Ahora vamos a detenernos en "Observation" para que no alucinemos la respuesta real de la función.

```python
output = client.text_generation(
    prompt,
    max_new_tokens=200,
    stop=["Observation:"] # Detengámonos antes de que se llame a cualquier función
)

print(output)
```
output:

````
Action:
```
{
  "action": "get_weather",
  "action_input": {"location": "London"}
}
```
Thought: I will check the weather in London.
Observation:
````

¡Mucho mejor!
Ahora vamos a crear una función dummy para el clima. En una situación real, probablemente llamarías a una API.

```python
# Función dummy
def get_weather(location):
    return f"the weather in {location} is sunny with low temperatures. \n"

get_weather('London')
```
output:
```
'the weather in London is sunny with low temperatures. \n'
```

Vamos a concatenar el prompt base, la completación hasta la ejecución de la función y el resultado de la función como una Observación y reanudar la generación.

```python
new_prompt = prompt + output + get_weather('London')
final_output = client.text_generation(
    new_prompt,
    max_new_tokens=200,
)

print(final_output)
```
Aquí está el nuevo prompt:
````
system
    Responde las siguientes preguntas lo mejor que puedas. Tienes acceso a las siguientes herramientas:

get_weather: Obtener el clima actual en una ubicación dada

La forma en que usas las herramientas es especificando un blob json.
Specifically, this json should have an `action` key (with the name of the tool to use) and a `action_input` key (with the input to the tool going here).

The only values that should be in the "action" field are:
get_weather: Obtener el clima actual en una ubicación dada, args: {"location": {"type": "string"}}
example use : 

{{
  "action": "get_weather",
  "action_input": {"location": "New York"}
}}

ALWAYS use the following format:

Question: the input question you must answer
Thought: you should always think about one action to take. Only one action at a time in this format:
Action:

$JSON_BLOB (inside markdown cell)

Observation: the result of the action. This Observation is unique, complete, and the source of truth.
... (this Thought/Action/Observation can repeat N times, you should take several steps when needed. The $JSON_BLOB must be formatted as markdown and only use a SINGLE action at a time.)

You must always end your output with the following format:

Thought: I now know the final answer
Final Answer: the final answer to the original input question

Now begin! Reminder to ALWAYS use the exact characters `Final Answer:` when you provide a definitive answer. 
user
What's the weather in London ?
assistant

Action:
```
{
  "action": "get_weather",
  "action_input": {"location": {"type": "string", "value": "London"}
}
```
Thought: I will check the weather in London.
Observation:the weather in London is sunny with low temperatures. 
````

Output:
```
Final Answer: The weather in London is sunny with low temperatures.
```

---

Aprendimos cómo podemos crear Agentes desde cero usando código Python, y **vimos lo tedioso que puede ser ese proceso**. Afortunadamente, muchas bibliotecas de Agentes simplifican este trabajo manejando gran parte del trabajo pesado por ti.

Ahora, estamos listos **para crear nuestro primer Agente real** usando la biblioteca `smolagents`.

### Incorporación: Tus Primeros Pasos ⛵
https://huggingface.co/learn/agents-course/es/unit0/onboarding.md

# Incorporación: Tus Primeros Pasos ⛵

Ahora que tienes todos los detalles, ¡comencemos! Vamos a hacer cuatro cosas:

1. **Crear tu Cuenta de Hugging Face** si aún no lo has hecho
2. **Registrarte en Discord y presentarte** (no seas tímido/a 🤗)
3. **Seguir el Curso de Agentes de Hugging Face** en el Hub
4. **Difundir** el curso

### Paso 1: Crea tu Cuenta de Hugging Face

(Si aún no lo has hecho) crea una cuenta de Hugging Face aquí.

### Paso 2: Únete a Nuestra Comunidad de Discord

👉🏻 Únete a nuestro servidor de discord aquí.

Cuando te unas, recuerda presentarte en `#introduce-yourself`.

Visita el canal `courses` en `Hugging Face Hub` para todas las preguntas y consultas relacionadas con los cursos.

Si es tu primera vez usando Discord, escribimos una guía Discord 101 para conocer las mejores prácticas. Consulta [la siguiente sección](discord101.mdx).

### Paso 3: Sigue la Organización del Curso de Agentes de Hugging Face

Mantente al día con los materiales más recientes del curso, actualizaciones y anuncios **siguiendo la Organización del Curso de Agentes de Hugging Face**.

👉 Ve aquí y haz clic en **seguir**.

### Paso 4: Difunde el curso

¡Ayúdanos a hacer este curso más visible! Hay dos formas en que puedes ayudarnos:

1. Muestra tu apoyo con una ⭐ en el repositorio del curso.

2. Comparte tu Camino de Aprendizaje: ¡Haz que otros **sepan que estás tomando este curso**! Hemos preparado una ilustración que puedes usar en tus publicaciones de redes sociales

Puedes descargar la imagen haciendo clic 👉 [aquí](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/communication/share.png?download=true)

¡Felicidades! 🎉 **¡Has completado el proceso de incorporación**! Ahora estás listo/a para comenzar a aprender sobre Agentes IA. ¡Diviértete!

Sigue aprendiendo, mantente genial 🤗

### Bienvenido/a al curso de 🤗  Agentes IA [[introduction]]
https://huggingface.co/learn/agents-course/es/unit0/introduction.md

# Bienvenido/a al curso de 🤗  Agentes IA [[introduction]]

The background of the image was generated using Scenario.com

Bienvenido/a al tema mas emocionante en la IA en la actualidad: **Agents**!

Este curso gratuito te llevará a un viaje, **desde principiante hasta experto**, en comprender, usar y construir agentes IA.

Esta primera unidad te ayudará a comenzar:

- Descubre el **programa del curso**.
- **Elige el camino** que vas a tomar (ya sea el camino de autoaudición o el proceso de certificación).
- **Obtén más información sobre el proceso de certificación y las fechas de entrega**.
- Conoce a los miembros del equipo detras del curso.
- Crea tu cuenta de **Hugging Face**.
- **Registrate al servidor de Discord**, y conocenos a nosotros y a tus compañeross.

Empecemos!

## Que esperar de este curso? [[expect]]

En este curso, aprenderás:

- 📖 Estudia los agentes de IA con **teoría, diseño y práctica**.
- 🧑‍💻 Aprende a **usar bibliotecas de agentes IA establecidas** como [smolagents](https://huggingface.co/docs/smolagents/en/index), [LangChain](https://www.langchain.com/) y [LlamaIndex](https://www.llamaindex.ai/).
- 💾 **Comparte tus agentes** en el Hugging Face Hub y explora agentes creados por la comunidad.
- 🏆 Participa en **retos** donde **evaluáras a tus agentes contra agentes de otros estudiantes**.
- 🎓 **Obtén un certificado** al completar las asignaciones.

Y mucho mas!

Al final de este curso entenderás **como funcionan los agentes y como construir tus propios agentes usando las librerías y herramientas mas recientes**.

No olvides **registrarte a este curso!** 

(Respetamos tu privacidad. Recopilamos tu dirección de correo electrónico para poder **enviarte los enlaces cuando se publique cada unidad y darte información sobre los desafíos y actualizaciones**).

## ¿Cómo es el curso? [[course-look-like]]

El curso está compuesto por:

- *Unidades fundamentales*: donde aprenderás los **conceptos de Agentes en teoría**.
- *Prácticas*: donde aprenderás **a usar bibliotecas de Agentes IA establecidas** para entrenar tus agentes en entornos únicos. Estas secciones prácticas serán en **Hugging Face Spaces** con un entorno preconfigurado.
- *Prácticas de casos de uso*: donde aplicarás los conceptos que has aprendido para resolver un problema que elijas.
- *Desafío*: pondrás a tu agente a competir contra otros agentes en un desafío. Tambien habra una [tabla de clasificación](https://huggingface.co/spaces/huggingface-projects/AI-Agents-Leaderboard) (aún no disponible) para que compares el rendimiento de los agentes.

Este **curso es un proyecto latente, que evoluciona con tus comentarios y contribuciones!** No dudes en [abrir issues y PRs en GitHub](https://github.com/huggingface/agents-course), y participar en discusiones en nuestro servidor de Discord.

Después de haber completado el curso, también puedes enviar tus comentarios [👉 usando este formulario](https://docs.google.com/forms/d/e/1FAIpQLSe9VaONn0eglax0uTwi29rIn4tM7H2sYmmybmG5jJNlE5v0xA/viewform?usp=dialog)

## ¿Cuál es el programa? [[syllabus]]

Aquí está el **programa general del curso**. Una lista más detallada de temas se publicará con cada unidad.

| Capítulo | Tema | Descripción |
| :---- | :---- | :---- |
| 0 | Introducción | Te prepara con las herramientas y plataformas que utilizarás. |
| 1 | Fundamentos de Agentes | Explica Herramientas, Pensamientos, Acciones, Observaciones y sus formatos. Explica LLMs, mensajes, tokens especiales y plantillas de chat. Muestra un caso de uso simple usando funciones de Python como herramientas. |
| 1.5 | Bonus: Fine-tuning de un LLM para llamadas a funciones | Usemos LoRa y hagamos fine-tuning de un modelo para realizar llamadas a funciones dentro de un notebook. |
| 2 | Frameworks | Comprende como se implementan los fundamentos en bibliotecas populares: smolagents, LangGraph, LLamaIndex |
| 3 | Casos de Uso | Construyamos algunos casos de uso reales (abierto a PRs 🤗 de personas con experiencia creando Agentes) |
| 4 | Asignación Final | Construye un agente para un benchmark seleccionado y demuestra tu comprensión de los Agentes en la tabla de clasificación de estudiantes 🚀 |

*También estamos planeando lanzar algunas unidades adicionales, ¡mantente al tanto!*

## ¿Cuáles son los requisitos previos?

Para poder seguir este curso deberías tener:

- Conocimientos básicos de Python
- Conocimientos básicos de LLMs (tenemos una sección en la Unidad 1 para recapitular qué son)

## ¿Qué herramientas necesito? [[tools]]

Solo necesitas 2 cosas:

- *Una computadora* con conexión a internet.
- Una *Cuenta de Hugging Face*: para subir y cargar modelos, agentes y crear Spaces. Si aun no tienes una cuenta, puedes crear una **[aquí](https://hf.co/join)** (es gratis).

## El Proceso de Certificación [[certification-process]]

Puedes elegir seguir este curso *en modo auditoría*, o hacer las actividades y *obtener uno de los dos certificados que emitiremos*. 

Si auditas el curso, puedes participar en todos los desafíos y hacer asignaciones si quieres, y **no necesitas notificarnos**.

El proceso de certificación es **completamente gratuito**:

- *Para obtener una certificación de fundamentos*: necesitas completar la Unidad 1 del curso. Esto está destinado a estudiantes que quieren ponerse al día con las últimas tendencias en Agentes. 
- *Para obtener un certificado de finalización*: necesitas completar la Unidad 1, una de las asignaciones de casos de uso que propondremos durante el curso, y el desafío final.

Hay una fecha límite para el proceso de certificación: todas las asignaciones deben terminarse antes del **1 de mayo de 2025**.

## ¿Cuál es el ritmo recomendado? [[recommended-pace]]

Cada capítulo de este curso está diseñado **para completarse en 1 semana, con aproximadamente 3-4 horas de trabajo por semana**.

Como hay una fecha límite, te proporcionamos un ritmo recomendado:

## ¿Cómo aprovechar al máximo el curso? [[advice]]

Para aprovechar al máximo el curso, tenemos algunos consejos:

1. Únete a grupos de estudio en Discord: estudiar en grupos siempre es más fácil. Para hacerlo, necesitas unirte a nuestro servidor de Discord y verificar tu cuenta de Hugging Face.
2. **Haz los cuestionarios y asignaciones**: la mejor manera de aprender es a través de la práctica y la autoevaluación.
3. **Define un horario para mantenerte sincronizado**: puedes usar nuestro horario de ritmo recomendado a continuación o crear el tuyo.

## Quiénes somos [[who-are-we]]

Sobre los autores:

### Joffrey Thomas

Joffrey es un ingeniero de aprendizaje automático en Hugging Face y ha construido e implementado Agentes IA en producción. Joffrey será tu instructor principal para este curso.

- [Sigue a Joffrey en Hugging Face](https://huggingface.co/Jofthomas)
- [Sigue a Joffrey en X](https://x.com/Jthmas404)
- [Sigue a Joffrey en Linkedin](https://www.linkedin.com/in/joffrey-thomas/)

### Ben Burtenshaw

Ben es un ingeniero de aprendizaje automático en Hugging Face y ha impartido múltiples cursos en varias plataformas. El objetivo de Ben es hacer que el curso sea accesible para todos.

- [Sigue a Ben en Hugging Face](https://huggingface.co/burtenshaw)
- [Sigue a Ben en X](https://x.com/ben_burtenshaw)
- [Sigue a Ben en Linkedin](https://www.linkedin.com/in/ben-burtenshaw/)

### Thomas Simonini

Thomas es un ingeniero de aprendizaje automático en Hugging Face e impartió los exitosos cursos de Deep RL y ML para juegos. Thomas es un gran fan de los Agentes y está emocionado de ver lo que la comunidad construirá.

- [Sigue a Thomas en Hugging Face](https://huggingface.co/ThomasSimonini)
- [Sigue a Thomas en X](https://x.com/ThomasSimonini)
- [Sigue a Thomas en Linkedin](https://www.linkedin.com/in/simoninithomas/)

## Agradecimientos  

Nos gustaría extender nuestro agradecimiento a las siguientes personas por sus invaluables contribuciones a este curso:  

- **[Pedro Cuenca](https://huggingface.co/pcuenq)** – Por su orientación y experiencia en la revisión de los materiales.
- **[Aymeric Roucher](https://huggingface.co/m-ric)** – Por sus increíbles espacios de demostración (decodificación y agente final) así como su ayuda en las partes de smolagents. 
- **[Joshua Lochner](https://huggingface.co/Xenova)** – Por su increíble espacio de demostración sobre tokenización.
- **[Quentin Gallouédec](https://huggingface.co/qgallouedec)** – Por su ayuda en el contenido del curso.
- **[David Berenstein](https://huggingface.co/davidberenstein1957)** – Por su ayuda en el contenido del curso y moderación.
- **[XiaXiao (ShawnSiao)](https://huggingface.co/SSSSSSSiao)** – Traductor al chino para el curso.
- **[Jiaming Huang](https://huggingface.co/nordicsushi)** – Traductor al chino para el curso.

## Encontré un error, o quiero mejorar el curso [[contribute]]

Las contribuciones son **bienvenidas** 🤗

- Si *encontraste un error 🐛 en un notebook*, por favor abre un issue y **describe el problema**.
- Si *quieres mejorar el curso*, puedes abrir un Pull Request.
- Si *quieres agregar una sección completa o una nueva unidad*, lo mejor es abrir un issue y **describir qué contenido quieres agregar antes de comenzar a escribirlo para que podamos guiarte**.

## Todavía tengo preguntas [[questions]]

Por favor, haz tu pregunta en nuestro servidor de discord #ai-agents-discussions.

Ahora que tienes toda la información, ¡vamos a bordo! ⛵

### (Opcional) Discord 101 [[discord-101]]
https://huggingface.co/learn/agents-course/es/unit0/discord101.md

# (Opcional) Discord 101 [[discord-101]]

Esta guía está diseñada para ayudarte a comenzar a utilizar Discord, una plataforma de chat gratuita popular en las comunidades de gaming e IA.

Únete al servidor de Discord de la Comunidad Hugging Face, que **tiene más de 100,000 miembros**, haciendo clic aquí. ¡Es un gran lugar para conectar con otros!

## El curso de Agentes en la Comunidad Discord de Hugging Face

Comenzar en Discord puede ser un poco abrumador, así que aquí hay una guía rápida para ayudarte a navegar.

El Servidor de la Comunidad HF alberga una comunidad vibrante con intereses en varias áreas, ofreciendo oportunidades de aprendizaje a través de discusiones de papers, eventos y más.

Después de [registrarte](http://hf.co/join/discord), preséntate en el canal `#introduce-yourself`.

Hemos creado 4 canales para el Curso de Agentes:

- `agents-course-announcements`: para las **últimas actualizaiones del curso**.
- `🎓-agents-course-general`: para **discusiones generales y charlas**.
- `agents-course-questions`: para **hacer preguntas y ayudar a tus compañeros**.
- `agents-course-showcase`: para **mostrar tus mejores agentes**.

Además, puedes consultar:

- `smolagents`: para **discusión y soporte con la biblioteca**.

## Consejos para usar Discord de manera efectiva

### Cómo unirse a un servidor

Si estás menos familiarizado con Discord, quizás quieras consultar esta guía sobre cómo unirte a un servidor.

Aquí hay un resumen rápido de los pasos:

1. Haz clic en el Enlace de Invitación.
2. Inicia sesión con tu cuenta de Discord, o crea una cuenta si no tienes una.
3. ¡Valida que no eres un agente de IA!
4. Configura tu apodo y avatar.
5. Haz clic en "Unirse al Servidor".

### Cómo usar Discord de manera efectiva

Aquí hay algunos consejos para usar Discord de manera efectiva:

- Los **canales de voz** están disponibles, aunque el chat de texto se usa más comúnmente.
- Puedes formatear texto usando **estilo markdown**, lo cual es especialmente útil para escribir código. Ten en cuenta que markdown no funciona tan bien para enlaces.
- Considera abrir hilos para **conversaciones largas** para mantener las discusiones organizadas.

Esperamos que encuentres esta guía útil. Si tienes alguna pregunta, no dudes en preguntarnos en Discord 🤗.
