Capítulo 1: El Manifiesto Cero Magia
Si has intentado construir una aplicación de IA en los últimos dos años, probablemente hayas sentido la “Fatiga de Frameworks.”
Instalas una biblioteca popular. Importas un ReasoningEngine. Llamas a .run(). Funciona como por arte de magia en el ejemplo “Hello World”. Pero en el momento en que intentas hacer algo real —como editar una línea específica en un archivo Python sin eliminar las importaciones— se rompe.
Y como usaste un framework, no puedes arreglarlo. Te quedas atascado excavando a través de capas de clases abstractas, patrones factory y “Chains” tratando de encontrar el único prompt que está causando la alucinación.
No vamos a hacer eso aquí.
Este libro es una rebelión contra la “Magia”. Vamos a tomar el enfoque “Cero Magia”: construir un agente de programación de nivel producción llamado Nanocode en Python puro. Sin LangChain, sin AutoGPT, sin Pydantic.
¿Por qué? Porque un agente autónomo no es magia. Es solo un bucle while.
¿Qué es realmente un Agente?
Si dejamos de lado el marketing de capital de riesgo, un “Agente” es simplemente un termostato.
Un termostato lee la temperatura (entrada), la compara con el objetivo (decisión) y enciende el calentador (acción). Luego espera y repite. Eso es todo. Un agente de IA hace lo mismo, solo que con texto en lugar de temperatura.

Más específicamente, un agente tiene cuatro partes. El Cerebro es el LLM —una función sin estado donde envías texto y devuelve texto. Llama a las Herramientas —funciones como Leer Archivo y Ejecutar Comando— para interactuar con el mundo exterior. Todo esto se encuentra dentro de un Bucle (un while True) que sigue ciclando hasta que la tarea está terminada, con la Memoria —solo una lista de Python— acumulando el historial de conversación en el camino. (La lista muere cuando el programa termina; añadiremos almacenamiento persistente en el Capítulo 6.)
Si puedes escribir un bucle while, puedes construir un agente.
Al construirlo desde cero, tendrás algo que los usuarios de frameworks no tienen: control. Cuando nuestro agente se quede atascado en un bucle, sabrás exactamente qué línea de código lo causó. Cuando la factura de la API se vuelva demasiado alta, verás exactamente dónde se están fugando los tokens.
Qué Vamos a Construir
Nanocode es una herramienta CLI que se ejecuta en tu terminal. Hablas con ella como si fuera un colega. Lee tus archivos. Ejecuta tus comandos. Edita tu código.
Al final de este libro, la habrás conectado a Claude Sonnet 4.6 (o DeepSeek, o un modelo local a través de Ollama). Le darás manos —herramientas para leer archivos, escribir archivos y ejecutar comandos de shell— y ojos para buscar en tu base de código. Y construirás un arnés de seguridad para que no pueda ejecutar accidentalmente rm -rf /.
Configuración del Proyecto
1. Inicializar el Proyecto
1 mkdir nanocode
2 cd nanocode
3 git init
2. Crear un Entorno Virtual
Nunca instales herramientas de IA de forma global. Entran en conflicto con los paquetes del sistema.
1 # Mac/Linux
2 python3 -m venv venv
3 source venv/bin/activate
4
5 # Windows
6 python -m venv venv
7 venv\Scripts\activate
3. Instalar Dependencias
Solo necesitamos tres bibliotecas:
requests— Para comunicarse con las APIs de LLM.python-dotenv— Para cargar las claves API desde un archivo.env.pytest— Para probar nuestro código sin realizar llamadas API.
Crea requirements.txt:
1 requests
2 python-dotenv
3 pytest
Instalación:
1 pip install -r requirements.txt
4. Asegura Tus Claves
![]() |
Advertencia: Si subes tu clave de API a GitHub, los bots la detectarán y vaciarán tu cuenta en cuestión de minutos. |
Crea .gitignore:
1 .env
2 __pycache__/
3 venv/
4 .DS_Store
5 .nanocode/
La Excepción AgentStop
Antes de escribir el bucle de eventos, necesitamos un mecanismo de salida limpia. Una excepción es mejor que tener sentencias break dispersas por todo el código.
El Contexto: Las excepciones no son solo para errores; también son un mecanismo de control de flujo. Cuando el usuario escribe /q, lanzamos AgentStop. El bucle principal la captura y sale limpiamente.
El Código:
1 # --- Exceptions ---
2
3 class AgentStop(Exception):
4 """Raised when the agent should stop processing."""
5 pass
Esto va en la parte superior de nanocode.py. Es una excepción marcadora—sin lógica, solo una señal.
La Clase Agent
Ahora la abstracción fundamental: la clase Agent. Mantiene el estado y la lógica en un solo lugar, lo que facilita las pruebas.
El Contexto: Podríamos poner toda la lógica en main(). Pero entonces tendríamos que simular input() y print() para probarla. Al extraer la lógica en Agent.handle_input(), podemos probarla directamente.
El Código:
10 class Agent:
11 """A coding agent that processes user input."""
12
13 def __init__(self):
14 pass
15
16 def handle_input(self, user_input):
17 """Handle user input. Returns output string, raises AgentStop to quit."""
18 if user_input.strip() == "/q":
19 raise AgentStop()
20
21 if not user_input.strip():
22 return ""
23
24 return f"You said: {user_input}\n(Agent not yet connected)"
La explicación detallada:
- Líneas 13-14: Constructor vacío por ahora. Añadiremos
brainytoolsen capítulos posteriores. - Líneas 18-19: Verifica el comando de salida
/q. LanzaAgentStopen lugar de devolver un valor especial. - Líneas 21-22: Omite la entrada vacía. Devuelve una cadena vacía (sin salida para mostrar).
- Línea 24: Devuelve la entrada como eco. Esto es provisional—más adelante, enviaremos esto al Brain.
Definiendo el éxito con pruebas
Antes de escribir el bucle principal, necesitamos pruebas.
Crea test_nanocode.py:
1 import pytest
2 from nanocode import Agent, AgentStop
3
4
5 def test_handle_input_returns_string():
6 """Verify handle_input returns a string for normal input."""
7 agent = Agent()
8 result = agent.handle_input("hello")
9 assert isinstance(result, str)
10 assert "hello" in result
11
12
13 def test_empty_input_returns_empty_string():
14 """Verify empty/whitespace input returns empty string."""
15 agent = Agent()
16 assert agent.handle_input("") == ""
17 assert agent.handle_input(" ") == ""
18 assert agent.handle_input("\n") == ""
19
20
21 def test_quit_command_raises_agent_stop():
22 """Verify /q raises AgentStop exception."""
23 agent = Agent()
24 with pytest.raises(AgentStop):
25 agent.handle_input("/q")
26
27
28 def test_quit_command_with_whitespace():
29 """Verify /q works with surrounding whitespace."""
30 agent = Agent()
31 with pytest.raises(AgentStop):
32 agent.handle_input(" /q ")
Ejecute las pruebas:
1 pytest test_nanocode.py -v
1 test_nanocode.py::test_handle_input_returns_string PASSED
2 test_nanocode.py::test_empty_input_returns_empty_string PASSED
3 test_nanocode.py::test_quit_command_raises_agent_stop PASSED
4 test_nanocode.py::test_quit_command_with_whitespace PASSED
Todo verde. Nuestro agente maneja los casos básicos correctamente.
![]() |
Aparte: ¿Por qué pytest? Detecta las funciones que comienzan con |
El Bucle Principal
Ahora el envoltorio delgado de I/O que conecta el agente con la terminal:
29 def main():
30 agent = Agent()
31 print("⚡ Nanocode v0.1 initialized.")
32 print("Type '/q' to quit.")
33
34 while True:
35 try:
36 user_input = input("\n❯ ")
37 output = agent.handle_input(user_input)
38 if output:
39 print(output)
40
41 except (AgentStop, KeyboardInterrupt):
42 print("\nExiting...")
43 break
44
45
46 if __name__ == "__main__":
47 main()
La Explicación Detallada:
- Líneas 30-32: Crea el agente e imprime los mensajes de inicio.
- Línea 36:
input()se bloquea y espera a que el usuario escriba algo. - Líneas 37-39: Llama a
handle_input()e imprime cualquier salida. - Líneas 41-43: Captura
AgentStop(desde/q) oKeyboardInterrupt(desde Ctrl+C) y sale limpiamente.
![]() |
Nota adicional: La función |
Observa la separación: Agent.handle_input() contiene toda la lógica. main() es solo el pegamento de E/S. Esto hace que el agente sea comprobable sin necesidad de simular stdin/stdout.
Ejecútalo
1 python nanocode.py
Debería ver:
1 ⚡ Nanocode v0.1 initialized.
2 Type '/q' to quit.
3
4 ❯ hello
5 You said: hello
6 (Agent not yet connected)
7
8 ❯ /q
9
10 Exiting...
Este es el chasis. El motor viene después.
Conclusión
Eso es el chasis: una clase Agent, un método handle_input(), un bucle while True. Todavía no hace nada útil, pero todo lo que construiremos a partir de aquí se conecta a este esqueleto. Las pruebas aseguran que no rompamos lo que ya funciona mientras avanzamos.

