Automatizando mis proyectos con agentes de IA: cómo construí un sistema que trabaja 24/7 por mí
La idea nació de una frustración concreta: tenía varios proyectos personales que quería avanzar en paralelo, pero el tiempo que podía dedicarles era fragmentado — una hora aquí, media hora allá. El contexto que perdía entre sesiones era enorme.
¿Y si en lugar de ser yo quien escribe el código, simplemente le digo a un agente qué hacer y reviso el resultado?
Eso es exactamente lo que construí.
La visión
El objetivo es simple pero ambicioso: que yo solo toque el teclado para revisar Pull Requests y dar nuevas instrucciones. Todo lo demás — clonar el repo, crear el branch, escribir el código, correr los tests, abrir el PR — lo hace un agente de IA de forma autónoma.
El flujo completo se ve así:
Yo (Telegram)
│
▼
/task mi-api Implementa autenticación JWT
│
▼
Task Queue (Celery + Redis)
│
▼
Agent Router → asigna agente según el proyecto
│
▼
Claude Agent
├── clona el repo
├── crea branch: feat/task-42-jwt-auth
├── lee el código existente
├── implementa los cambios
├── corre pytest
└── abre PR en GitHub con descripción completa
│
▼
Telegram: "✅ Task #42 completada — PR listo para revisar"
Link directo al PR
│
▼
Yo reviso, apruebo, mergeo. Listo.
La arquitectura
El sistema tiene cuatro capas bien separadas.
1. El canal de instrucciones
Elegí Telegram Bot como canal principal. Es gratis, tiene una API excelente, funciona desde el celular y — lo más importante para un sistema personal — el Chat ID propio garantiza que solo yo puedo enviar instrucciones. No necesito workspace de empresa ni configuraciones complejas.
Los comandos que soporta el bot:
/task <proyecto> <descripción>
/task mi-api Implementa JWT auth --priority high
/task frontend Arregla el modal en mobile --agent claude-sonnet-4
/status
/projects
El bot parsea la instrucción, crea un registro en la base de datos con status QUEUED y encola la tarea en Celery.
2. El orquestador
El backend es FastAPI + Celery + Redis. Celery maneja la cola de tareas con soporte nativo para prioridades y reintentos. Redis actúa como broker.
El componente más interesante es el router de agentes — la pieza que decide qué agente ejecuta cada tarea. La lógica actual es simple: usa el agente configurado en el proyecto (claude-opus-4 para tareas complejas, claude-sonnet-4 para tareas simples). Si el agente principal falla, hace fallback automático al agente alternativo.
async def route_task(task: Task, project: Project) -> AgentResult:
agent = _build_agent(task.agent or project.default_agent)
# Clonar o actualizar el repo antes de empezar
clone_result = clone_or_update_repo(project.repo_url, repo_path)
result = await agent.execute(agent_task)
# Si falló, reintenta con el agente de fallback
if not result.success and project.fallback_agent:
fallback = _build_agent(project.fallback_agent)
result = await fallback.execute(agent_task)
return result
3. El agente Claude
Este es el núcleo del sistema. El agente usa Claude API con tool use — en lugar de un script que llama a Claude para generar un diff y aplicarlo, el agente opera en un loop: recibe la tarea, decide qué herramienta usar, ejecuta la herramienta, recibe el resultado, y sigue hasta terminar.
Las herramientas disponibles son cuatro:
File tools — leer, escribir y listar archivos del repositorio. El agente puede leer el código existente antes de modificarlo.
Bash tool — ejecutar comandos shell. Corre los tests, el linter, o cualquier comando configurado en el proyecto.
Git tools — el ciclo completo de Git: crear branch, hacer commits atómicos, push al remoto y abrir el PR via GitHub API.
Search tool — buscar patrones en el codebase usando ripgrep. Útil para encontrar dónde están definidas las cosas antes de modificarlas.
El loop del agente se ve así en código simplificado:
while iterations < MAX_ITERATIONS:
response = client.messages.create(
model=self.model,
tools=ALL_TOOLS,
messages=messages,
)
if response.stop_reason == "end_turn":
return self._parse_final_response(response)
# Ejecutar cada tool que el agente pidió
tool_results = []
for block in response.content:
if block.type == "tool_use":
result = self._execute_tool(block.name, block.input, task)
tool_results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": json.dumps(result),
})
messages.append({"role": "user", "content": tool_results})
El system prompt es la parte más crítica — define exactamente qué debe hacer el agente y en qué orden: leer la estructura del proyecto, crear el branch, implementar los cambios, hacer commits incrementales, correr tests, hacer push.
4. La capa de Git y GitHub
Uno de los requisitos más importantes del sistema es que los agentes nunca hagan push directo a main. Cada tarea vive en su propio branch con un nombre descriptivo: feat/task-42-jwt-auth.
Al terminar, el agente hace push del branch y el worker abre el PR automáticamente via GitHub API con un título y descripción generada por el propio agente — qué hizo, por qué, cómo probarlo.
pr_result = open_pull_request(
repo_url=project.repo_url,
branch_name=result.branch_name,
title=f"[Task #{task.id}] {task.description[:80]}",
body=result.log, # generado por el agente
base_branch=project.base_branch,
)
Configuración por proyecto
Cada proyecto tiene su propia carpeta con dos archivos:
config.yml — define el repo, el agente preferido y los comandos:
name: mi-api
repo_url: https://github.com/adolfo/mi-api
default_agent: claude-opus-4
fallback_agent: claude-sonnet-4
base_branch: main
test_command: pytest
lint_command: ruff check .
context.md — descripción del proyecto que el agente lee al inicio de cada tarea. Incluye el stack, las convenciones del proyecto y cualquier cosa que el agente deba saber antes de tocar el código.
Esta separación es intencional: el agente tiene contexto del proyecto sin necesidad de leer todo el codebase desde cero en cada tarea.
Las reglas que siguen los agentes
Definí un conjunto de reglas explícitas en el CLAUDE.md del repositorio. Cualquier agente que opere en este sistema debe seguirlas:
- Nunca push directo a
main— siempre branches propios - Commits atómicos — un commit por cambio lógico, no un commit gigante al final
- Conventional Commits —
feat(auth): add JWT token generation - No romper tests existentes — si hay que cambiar tests, explicarlo en el PR
- No introducir dependencias nuevas sin mencionarlo explícitamente
- Nunca hardcodear secrets — variables de entorno siempre
- Ante la duda, hacer menos y documentar la duda en el PR
Este archivo actúa también como memoria persistente entre sesiones — cualquier agente nuevo puede leerlo y entender exactamente cómo operar.
El stack completo
| Capa | Tecnología |
|---|---|
| Backend | Python 3.12 + FastAPI |
| Cola de tareas | Celery + Redis |
| Base de datos | SQLite (dev) / PostgreSQL (prod) |
| Agente principal | Claude API con tool use |
| Multi-LLM (futuro) | LiteLLM |
| Canal de instrucciones | Telegram Bot |
| Operaciones Git | GitHub API + GitPython |
| Infraestructura | Railway |
| Containers | Docker + Docker Compose |
Lo que aprendí construyendo esto
El tool use es más poderoso que generar diffs. Un agente que puede leer archivos, ejecutar comandos y hacer commits de forma iterativa toma mejores decisiones que uno que genera un parche ciego sin ver el resultado de sus acciones.
El contexto por proyecto es fundamental. Sin un context.md que describa las convenciones del proyecto, el agente toma decisiones genéricas que a veces no encajan con el estilo del codebase. Con contexto, las decisiones son mucho más acertadas.
Los archivos de memoria persisten más que las sesiones. El mayor riesgo de un sistema así es perder el hilo entre sesiones. Resolví esto con archivos de contexto versionados en el propio repositorio — PROJECT_STATE.md que refleja qué está hecho y qué sigue, y DECISIONS.md con los ADRs del proyecto.
La regla del branch obligatorio es innegociable. Que el agente nunca pueda tocar main directamente no es solo una buena práctica — es lo que hace que el sistema sea revisable y seguro. Si el agente se equivoca, el PR simplemente no se aprueba.
El estado actual y lo que viene
El MVP está completo: un agente Claude que recibe instrucciones por Telegram, trabaja en repositorios reales y abre PRs automáticamente. El siguiente paso es la prueba end-to-end con un proyecto real.
El roadmap después de eso:
Fase 2 — cola con múltiples proyectos en paralelo, contexto persistente entre tareas, comandos /status más detallados.
Fase 3 — soporte multi-agente via LiteLLM: asignar GPT-4 o Gemini según el tipo de tarea o el costo. Un router inteligente que elija el modelo correcto automáticamente.
Fase 4 — dashboard web para ver el estado de todas las tareas, reintentos automáticos, métricas de costo por tarea y tasa de éxito por agente.
El código está en GitHub si quieres explorar la implementación completa.