> Nivel 1 de Agent Harness Patterns, un recorrido de patrones sobre cómo funcionan los agentes de IA. Versión web: https://harnesspatterns.dev/es/patterns/what-is-an-agent · Todos los patrones (en inglés): https://harnesspatterns.dev/llms.txt

# ¿Qué es un agente?

"Agente" se usa para casi cualquier cosa que tenga un modelo de lenguaje adentro. La definición útil es mucho más estrecha, y se reduce a una pregunta: ¿quién decide el siguiente paso? En un agente, lo decide el modelo.

## El problema

Un chatbot, un script que llama dos veces a un modelo y un sistema que arregla bugs por su cuenta: a todos se los llama agentes. Eso hace difícil saber qué estás construyendo, y más difícil aún elegir la herramienta correcta para el trabajo.

La animación es un pequeño juego de aventuras. Un aldeano pregunta: "Perdí la llave del cofre del pueblo. ¿Dónde está?". El juego tiene dos funciones que pueden ayudar: `ask_villager` le pregunta a alguien qué vio, y `search_area` busca en un punto del mapa. Fíjate en quién decide cuál de las dos se ejecuta, y cuándo.

## Qué lo convierte en un agente

Un agente no es "un LLM con herramientas" ni "un workflow inteligente". Es un sistema donde **el modelo elige el flujo de control**: qué herramienta llamar, en qué orden y cuándo parar. Tu código nunca dice "pregúntale al pescador y después busca en el roble". El modelo lee cada resultado y decide el siguiente paso.

El bucle que lo hace posible es el próximo nivel.

## En la animación

- **El aldeano** (tu app): La pregunta sale de una casa del pueblo, y la respuesta vuelve ahí.
- **Astor** (el bucle): El bucle del agente, con el bandoneón de mensajes a la espalda. Va adonde lo mandan las notas del Oráculo, y a ningún otro lado.
- **El Oráculo** (LLM): El modelo, en una cueva entre dos fogatas. Nunca sale: solo sabe lo que hay en el bandoneón. Sin herramientas solo puede hablar, como Petrus el Inerte.
- **El muelle y el bosque** (herramientas): `ask_villager` y `search_area`: tus propias funciones. Una pantalla queda a oscuras hasta que un camino lleva a ella.
- **Las pantallas iluminadas** (flujo de control): Todo el patrón en una imagen, también en el minimapa. Cada camino aparece solo cuando el Oráculo pide esa herramienta, después de leer el último resultado. En un workflow, tu código los habría dibujado todos antes de que nadie preguntara. La montaña sigue a oscuras: el modelo nunca la necesitó.
- **Rupias y corazones** (tokens, maxTurns): Cada visita al Oráculo cuesta rupias, porque se vuelve a leer todo el bandoneón, y un corazón del presupuesto de turnos. Los números son ilustrativos.

## El código

**Con astorlm:** Envuelve tus propias funciones con `tool()` y dáselas al agente. El bucle ya viene incluido: el modelo elige qué herramientas llamar, en qué orden y cuándo tiene suficiente para responder.

**Desde cero:** Las funciones de tu juego, entregadas al modelo como herramientas. El bucle en sí es el del nivel 2; lo único nuevo es qué herramientas recibe.

**Con astorlm**

```ts
import { OpenAIProvider, createLocalAgent, tool } from 'astorlm'
import { z } from 'zod'

// Any OpenAI-compatible endpoint: OpenAI, Ollama, LM Studio, vLLM, a proxy…
const provider = new OpenAIProvider({
  baseURL: 'http://localhost:11434/v1', // e.g. Ollama's default address
  model: 'your-model', // e.g. 'llama3.1', 'gpt-4o-mini'
  apiKey: 'YOUR_API_KEY', // local servers usually ignore it
})

// Your own game functions, wrapped as tools: a name, a description and an input schema.
const askVillager = tool({
  name: 'ask_villager',
  description: 'Ask someone in the village what they saw. Returns what they say.',
  schema: z.object({ name: z.string().describe('Who to ask, e.g. "fisher" or "baker"') }),
  execute: async ({ name }) => world.villager(name).say(),
})

const searchArea = tool({
  name: 'search_area',
  description: 'Search one spot on the map. Returns what is found there, if anything.',
  schema: z.object({ area: z.string().describe('A named spot, e.g. "old oak" or "bridge"') }),
  execute: async ({ area }) => world.search(area),
})

// The model decides which tools to call, in what order, and when to stop.
const agent = await createLocalAgent({
  provider,
  tools: [askVillager, searchArea],
  maxTurns: 5, // a cap on the laps, in case it never settles
})

await agent.run('I lost the key to the village chest. Where is it?') // "In the crow's nest on the old oak"
```

**TypeScript**

```ts
// An agent that finds a villager's lost key. Plain TypeScript, no SDK.

// Your game. In a real one these read the world state and the characters' dialogue.
async function askVillager(name: string) {
  const seen: Record<string, string> = {
    fisher: 'A crow flew off with something shiny, toward the old oak in the woods.',
  }
  return seen[name] ?? `The ${name} saw nothing.`
}
async function searchArea(area: string) {
  return area === 'old oak' ? "In the crow's nest: a small brass key." : `Nothing at the ${area}.`
}

// The same functions, as tools the model can ask for. Each one returns text.
const tools = {
  ask_villager: ({ name }: { name: string }) => askVillager(name),
  search_area: ({ area }: { area: string }) => searchArea(area),
}

// runAgent is the loop from level 2, with the tools passed in. Your code never says
// "ask the fisher, then search the oak": the model picks each step after reading the last result.
export async function agent(question: string): Promise<string> {
  return runAgent(question, tools)
}
```

**Python**

```python
# An agent that finds a villager's lost key. Standard library only, no SDK.

# Your game. In a real one these read the world state and the characters' dialogue.
def ask_villager(name):
    seen = {"fisher": "A crow flew off with something shiny, toward the old oak in the woods."}
    return seen.get(name, f"The {name} saw nothing.")

def search_area(area):
    return "In the crow's nest: a small brass key." if area == "old oak" else f"Nothing at the {area}."

# The same functions, as tools the model can ask for. Each one returns text.
TOOLS = {
    "ask_villager": lambda args: ask_villager(args["name"]),
    "search_area": lambda args: search_area(args["area"]),
}

# run_agent is the loop from level 2, with the tools passed in. Your code never says
# "ask the fisher, then search the oak": the model picks each step after reading the last result.
def agent(question):
    return run_agent(question, TOOLS)
```

## Cuándo usar uno, y cuánto cuesta

- **Úsalo cuando no puedes escribir los pasos de antemano.** La llave podría estar en un nido, debajo de un puente o ya vendida en la tienda: en código, cada caso nuevo es otra rama. El agente los resuelve con el mismo bucle, siempre que tenga las herramientas.
- **Cada paso es una llamada al modelo.** Dos herramientas significaron tres llamadas aquí. Más pasos implican más latencia y más tokens, así que limita las vueltas con `maxTurns`.
- **La misma pregunta puede tomar otro camino.** Registra el camino que eligió el modelo, para poder ver por qué una respuesta salió como salió.
- **Las herramientas son el límite.** El modelo solo puede hacer lo que tus herramientas permiten. Lo que le das es lo que puede romper.

## Patrones relacionados

- [2 · El bucle del agente](https://harnesspatterns.dev/es/patterns/agent-loop.md)
- [3 · Diseñar una herramienta](https://harnesspatterns.dev/es/patterns/designing-a-tool.md)
- [12 · Planificar y reflexionar](https://harnesspatterns.dev/es/patterns/plan-and-reflect.md)
