Agentikas MVP — 7 agentes para codificar tu experiencia de senior
Una arquitectura agéntica de desarrollo software donde la experiencia humana se codifica en skills, schemas y nombres. Funciona — pero sin senior detrás, es la receta perfecta para deuda técnica a velocidad de IA. El MVP, los 5 agentes que ya viven, los 2 que vienen, y por qué la herramienta no reemplaza al criterio.
La pregunta que me llevó a construir esto: ¿cuánto de lo que hago como senior engineer no es escribir código sino decidir cómo se escribe?
Llevo años desarrollando software. Pasé por todas las fases: junior aprendiendo a no romper cosas, mid leyendo código de otros, senior tomando decisiones arquitectónicas sin pensarlo, lead diseñando sistemas que sobrevivan más allá de mi cabeza. Y un día, hace unos meses, una observación honesta:
El 80% de mi trabajo de senior no era escribir código. Era decidir qué se escribe, dónde, siguiendo qué patrón, con qué tests, bajo qué restricciones.
Las herramientas IA modernas (Claude, Cursor, Copilot, los SDKs de Anthropic y OpenAI con tool use, MCP, agentic loops) cambian las reglas del juego: el escribir es cada vez más barato. Pero el decidir — esa parte humana — sigue ahí.
Agentikas es mi intento de codificar el decidir. No el código, sino las decisiones que hacen que el código no apeste. Y este MVP es la primera versión funcional de esa idea: siete agentes especializados, cada uno con una skill bien definida, orquestados vía A2A, leyendo tu repo vía MCP, y encadenados en un pipeline que produce código revisable.
Este post describe el MVP en detalle, las decisiones técnicas detrás, y — honesto desde el día 1 — los riesgos. Porque si esta herramienta cae en manos sin experiencia, el resultado no es un acelerador. Es deuda técnica a velocidad de IA.
La tesis en una imagen
┌──────────────────────────────────────┐
│ Orchestrator │
│ conversacional, gestiona el flujo │
└─────────────────┬────────────────────┘
│ A2A (HTTP/JSON-RPC)
┌──────────┬─────────────────┼──────────────┬──────────────┐
▼ ▼ ▼ ▼ ▼
┌────────┐ ┌────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────┐
│Project │ │Investi-│ │ Project │ │ Feature │ │ Notifier │
│Mapper │ │gator │ │ Explorer │ │ Developer │ │ │
│ :8003 │ │ :8001 │ │ :8002 │ │ :8004 │ │ :8005 │
└────┬───┘ └────────┘ └──────────────┘ └──────┬───────┘ └──────────┘
│ MCP │ persist
▼ ▼
┌──────────────┐ ┌────────────────┐
│ Codebase MCP │ │ Cloudflare R2 │
│ :9000 │ │ agentic- │
└──────────────┘ │ artifacts │
└────────────────┘
▲ ▲
│ ro mount │ S3 SDK
│ │
target repo persisted patches
Y dos agentes más en TODO list:
- QA Tester (
:8006) — testea, itera con el Feature Developer hasta pasar todos los tests, abre PR. - Project Tech Librarian (
:8007) — documenta ADRs, errores recurrentes, best practices que emergen, y mejora las skills con el tiempo.
Total: 7 agentes + un servidor MCP + un orquestador conversacional. Cada uno con su rol activo (no descriptivo), cada uno con su skill markdown intercambiable, cada uno schema-validated con Zod.
Los 5 agentes del MVP actual
1. Project Mapper
Lee el repo objetivo a través de un Codebase MCP server (Model
Context Protocol). Devuelve un ProjectMap estructurado:
stack_summary (lo que necesita el Investigator) +
project_map (lo que necesita el Tech Lead).
El detalle elegante: usa la integración nativa de MCP en el SDK de Anthropic. Cero líneas de cliente MCP escritas — el SDK descubre tools del server, los expone a Claude, gestiona el agentic loop entero.
const response = await claude.beta.messages.create(
{
model: "claude-sonnet-4-6",
max_tokens: 4000,
system: skillContent,
mcp_servers: [
{ type: "url", url: "http://codebase-mcp:9000/mcp", name: "codebase" },
],
messages: [{ role: "user", content: "Map this repo..." }],
},
{ headers: { "anthropic-beta": "mcp-client-2025-04-04" } },
);
// Claude usa get_repo_info, list_directory, read_file dinámicamente.
// Tú solo recibes el resultado final sintetizado.
2. Investigator
Investigación externa: dado el feature + stack del proyecto, devuelve patrones canónicos, librerías estándar, pitfalls conocidos, edge cases que deben testearse.
Skill intercambiable según el flow: feature-research.md
para features nuevas, upgrade-research.md para upgrades de
versión, security-research.md cuando lleguen flujos de
seguridad. Mismo agente, distinta misión.
3. Project Explorer (Tech Lead)
El que decide. Recibe research + project_map y produce un
DevelopmentBrief: file paths concretos,
follow_pattern apuntando a un archivo del repo que sirva de
referencia, test_scenarios numerados, constraints (max_files, forbidden
patterns), acceptance_criteria, out_of_scope.
Es el agente más cargado de "experiencia senior codificada". Su prompt no es "audita el código" — es "tú eres tech lead; produce un brief que un junior pueda ejecutar sin pedir aclaraciones". El cambio de framing del nombre ("Auditor" → "Tech Lead") cambia los outputs del modelo más que cualquier prompt elaborado.
{
"feature_summary": "Magic-link auth: email → token → session",
"files_to_create": [
{ "path": "src/auth/magic-link.ts", "purpose": "core token logic" }
],
"follow_pattern": {
"reference_file": "src/auth/oauth.ts",
"why": "imitate error handling, naming, session integration"
},
"test_scenarios": [
{ "id": "t1", "name": "valid email generates token", "type": "unit" },
{ "id": "t2", "name": "expired token rejected", "type": "unit" }
],
"constraints": {
"max_files_changed": 8,
"test_framework": "vitest",
"forbidden_patterns": ["any", "ts-ignore"]
},
"out_of_scope": ["Refactoring legacy auth code"]
}
4. Feature Developer
Ejecuta el brief con TDD: tests primero, código después. Output es un patch estructurado (lista de archivos con contenido) — no escribe a disco.
¿Por qué no escribe? Por sandboxing. La escritura real es trabajo de
un componente futuro (Applier) con permisos restringidos. Por ahora, el
output viaja como JSON y se persiste en Cloudflare R2
(bucket agentic-artifacts) para que un humano o el QA
Tester futuro lo aplique.
5. Notifier
El más simple. Sin LLM. Recibe
subject + body + recipients y entrega vía
email/Slack/webhook (en walking skeleton: log a stdout). Existe porque
separar entrega de razonamiento mantiene a los agentes
cognitivos limpios. Cuando un día queramos cambiar de SMTP a SendGrid o
añadir Slack, tocamos un solo agente, no cinco.
+ Codebase MCP server
No es un agente — es infraestructura. Expone el repo objetivo como
Model Context Protocol server con 4 tools:
list_directory, read_file, search
(ripgrep), get_repo_info. Sandboxed (todo path verificado
contra REPO_ROOT), execFile (no shell —
anti-inyección), StreamableHTTP transport.
Lo crítico: no sabe que existe A2A. Es un MCP server estándar que cualquier cliente MCP puede consumir (Claude Desktop, otro IDE, otro agente). El Project Mapper lo usa internamente sin que el resto del sistema lo sepa. A2A coordina; MCP da capacidades. Composición ortogonal.
Los 2 agentes pendientes
QA Tester (próximo paso)
El que cierra el loop de calidad. Recibe el patch del Feature Developer y:
- Recompone el filesystem en un sandbox aislado
- Ejecuta: type-check, lint, suite existente + tests nuevos del patch
- Si todo pasa: hace
git checkout -b feat/<slug>, commit, push, abre PR vía GitHub API - Si algo falla: devuelve
state: input-requiredal Feature Developer con un payload estructurado:
{
"iteration": 2,
"failures": [
{
"test_id": "t3",
"error": "Expected 'expired' but got 'invalid'",
"file": "src/auth/__tests__/magic-link.test.ts:45"
}
],
"suggestions": [
"Token expiration check at line 23 should compare with Date.now() in ms"
]
}
El Feature Developer recibe esto, ajusta su patch, devuelve nuevo
intento. Loop limitado a 3-5 iteraciones. Después →
state: input-required al humano. Sin límite, los agentes
pueden iterar indefinidamente con micro-fixes que nunca convergen.
Esto requiere:
- Sandbox real (Docker-in-Docker o runner dedicado)
- Token Git con permisos
repo - GitHub API SDK (
@octokit/rest)
Project Tech Librarian
El más interesante a largo plazo. Su trabajo:
- Lee históricos de R2 (todos los briefs, patches, test reports pasados)
- Detecta patrones recurrentes: errores que el QA Tester rechaza repetidamente, decisiones arquitectónicas similares en briefs distintos, best practices que el Tech Lead aplica consistentemente
- Genera ADRs (Architecture Decision Records) cuando una decisión se repite N veces
- Documenta antipatrones cuando un mismo bug aparece en M tasks
- Propone mejoras a los markdown de skills cuando una skill produce outputs mediocres reiteradamente
Es el agente que convierte el sistema de estático a adaptativo. Sin Librarian, las skills son código humano que hay que mantener manualmente. Con Librarian, el sistema mejora con cada ciclo — cada PR mergeado es un entrenamiento para el siguiente.
Schema-as-policy: el LibrarianReport exige
evidence_artifacts: string[] con mínimo 2 entradas.
No genera docs sin evidencia real de tasks pasados que respalden
el aprendizaje. Eso evita que invente "best practices" que
suenan bien pero no están validadas por el uso real del sistema.
La ingeniería detrás: monorepo, workspaces, Docker
Estructura del repo
agentic-architecture/
├── package.json ← root con workspaces declarados
├── package-lock.json ← lockfile único
├── Dockerfile.workspace ← UN dockerfile que sirve a todos los services
├── docker-compose.yml
├── .dockerignore
│
├── shared/ ← package "@agentikas/shared"
│ ├── package.json
│ ├── a2a-client.ts ← cliente A2A reutilizable
│ ├── artifact-store.ts ← LocalStore + R2Store
│ └── local-llm.ts ← wrapper Ollama OpenAI-compat
│
├── orchestrator/ ← package
│ ├── package.json ← depende de "@agentikas/shared"
│ ├── tsconfig.json
│ └── src/server.ts
│
├── agents/
│ ├── investigator/ ← package
│ ├── notifier/
│ ├── project-explorer/
│ ├── project-mapper/
│ └── feature-developer/
│
├── services/
│ └── codebase-mcp/ ← MCP server, no es agente
│
├── skills/ ← markdown, montados read-only en containers
│ ├── feature-research.md
│ ├── feature-briefing.md
│ ├── feature-development.md
│ ├── project-mapping.md
│ └── upgrade-research.md
│
├── workspace/ ← LocalStore (gitignored)
└── blog/ ← markdown de los posts (este incluido)
npm workspaces: hoisting + symlinks
El package.json raíz declara:
{
"name": "agentic-architecture",
"workspaces": ["shared", "orchestrator", "agents/*", "services/*"]
}
npm install en raíz:
- Instala TODAS las deps de TODOS los packages en
/node_modulesraíz (hoisting) - Symlinkea cada workspace package en el
node_modulesde cada consumidor - Resultado: un solo lockfile, deps deduplicadas, agentes importan
from "@agentikas/shared/local-llm"en lugar defrom "../../../shared/local-llm.js"
Sin workspaces (lo que tenía antes): cada agente con
su propio package.json + node_modules,
shared/ copiado como código fuente sin deps propias. Cuando
metí R2Store y necesitó @aws-sdk/client-s3 desde
shared/, rompió todo: Node ESM resolution
buscaba la dep en /app/shared/node_modules que no existía.
Workspaces lo arregla automáticamente — la dep se hoistea a la raíz
visible para todos.
Un solo Dockerfile parametrizado
El truco que más limpia el repo:
# Dockerfile.workspace — uno solo, sirve a 7 servicios
FROM node:22-alpine
WORKDIR /app
RUN apk add --no-cache ripgrep git
# Layer cacheable: package.json de todos los workspaces
COPY package.json package-lock.json* ./
COPY shared/package.json ./shared/
COPY orchestrator/package.json ./orchestrator/
COPY agents/investigator/package.json ./agents/investigator/
# … resto de workspaces
RUN npm install --include=dev # hoistea a /app/node_modules
COPY . . # source code
ARG SERVICE_PATH # parametrizable
WORKDIR /app/${SERVICE_PATH}
CMD ["npm", "start"]
Y en docker-compose.yml cada servicio pasa su
SERVICE_PATH:
investigator:
build:
context: .
dockerfile: Dockerfile.workspace
args:
SERVICE_PATH: agents/investigator
feature-developer:
build:
context: .
dockerfile: Dockerfile.workspace
args:
SERVICE_PATH: agents/feature-developer
Una sola Dockerfile mantenida en lugar de siete. Añadir un nuevo agente (QA Tester mañana, Tech Librarian la semana que viene) es una entrada en docker-compose, cero ficheros nuevos en el directorio del agente más allá del código.
Git: dónde acaba el código que producen los agentes
Hoy:
- Skills (
skills/*.md) → en este repo (agentic-architecture), versionado normalmente - Outputs cognitivos (briefs, patches, reports) →
Cloudflare R2, bucket
agentic-artifacts, keytasks/<task_id>.json - Código generado por Feature Developer → cuando el
QA Tester esté listo, irá a
feat/<slug>en el repo target del cliente vía GitHub API
El repo target es separado del repo de la arquitectura. El sistema agéntico no escribe en su propio código — escribe en el código del proyecto que está ayudando a desarrollar. El Codebase MCP server lo monta read-only:
codebase-mcp:
volumes:
- ${TARGET_REPO:-./}:/repo:ro
TARGET_REPO es env var: por defecto el propio repo
agentic-architecture (útil para dogfooding y demos), pero apuntar a
cualquier ruta en disco lo cambia. Cuando despleguemos a Cloudflare,
será un volumen montado desde un clone fresco del repo del cliente.
Los tres MODEs por agente con LLM
Cada agente cognitivo tiene tres modos de operación, controlados por env var:
INVESTIGATOR_MODE=mock # canned response, $0, instantáneo
INVESTIGATOR_MODE=local # Ollama + Qwen Coder 32B, $0, ~30s
INVESTIGATOR_MODE=live # Anthropic Claude Sonnet 4.6, ~$0.01, ~3s
mock para CI y tests de integración. local
para desarrollo serio sin gastar API ni filtrar código (privacidad: todo
se procesa en tu Mac). live para calidad máxima en
producción.
El truco arquitectónico: el if/else
está en el agente, no en una clase distinta por modo.
Misma firma, distinto motor cognitivo.
if (MODE === "mock") {
textContent = mockReport(topic);
} else if (MODE === "local") {
const r = await callLocalLLM({ system, user, maxTokens: 2000 });
textContent = r.text;
} else {
const response = await claude.messages.create({ ... });
textContent = extractText(response);
}
Esa función callLocalLLM vive en
shared/local-llm.ts — una sola implementación reutilizada
por los 4 agentes con LLM. El día que metas otro modelo local (vLLM,
llama.cpp server, otra distro), tocas un archivo, no cuatro.
Cómo se codifica la experiencia humana
Esta sección es la que más me importa, porque es la tesis del proyecto.
1. Nombres → prompts
Los modelos LLM son extraordinariamente sensibles a la identidad declarada. Un agente llamado "Auditor" produce listas de problemas. Uno llamado "Tech Lead" produce specs ejecutables. Mismo input, mismo prompt literal — distinto comportamiento por arrastre del nombre.
Cuando diseñas la red, nombrar bien tus agentes es prompt engineering implícito. "Project Mapper" sugiere mapear; "Codebase Auditor" sugeriría encontrar problemas; "Stack Detector" sugeriría minimalismo. Eligiendo el nombre correcto, sesgas el output del modelo en la dirección que quieres.
2. Skills como markdown intercambiable
Cada agente carga su misión desde un archivo .md que
recibe en el payload:
const skillContent = readFileSync(
resolve(SKILLS_DIR, payload.skill_uri.replace(/^skills\//, "")),
"utf-8",
);
// Y se lo pasa a Claude como system prompt:
const response = await claude.messages.create({
system: skillContent, // ← la "misión"
messages: [{ role: "user", content: userPrompt }],
});
Los markdown se montan read-only en los containers como volumen Docker:
investigator:
volumes:
- ./skills:/app/skills:ro
Editar una skill no requiere rebuild. Cambias el markdown, el siguiente task la usa. Eso convierte la mejora del sistema en algo que un product manager con buen criterio puede hacer sin tocar TypeScript.
3. Constraints en schemas (no en wikis)
El brief del Tech Lead define
forbidden_patterns: ["any", "ts-ignore"]. El Feature
Developer no puede devolver un patch que contenga esos
patterns sin que el schema lo rechace. Es
schema-as-policy: la regla de negocio viaja con los
datos, no con la documentación.
const DevelopmentResult = z.object({
patches: z.array(...),
tests_written: z.number().min(1), // ≥1, no opcional
constraints_violated: z.array(z.string()) // si no vacío → state: failed
});
4. follow_pattern: analogía sobre imaginación
El campo más importante del brief no es la lista de archivos — es
follow_pattern.reference_file. Apuntar a un archivo real
del repo donde ya existe un patrón similar reduce alucinaciones
masivamente. El LLM trabaja por analogía con código
real, no por imaginación abstracta.
Esto refleja exactamente cómo enseña un senior a un junior: "mira cómo lo hicimos en el módulo de auth, hazlo igual aquí". No le explicamos los patrones desde cero. Apuntamos al ejemplo y pedimos que lo imite.
Ventajas reales del enfoque agéntico
Codifica experiencia que de otro modo se pierde: cuando el senior se va, sus criterios se van con él. Si están en skills/markdowns, no.
Escala paralelizable: 5 features distintas en flight simultáneamente = 5 pipelines independientes. Un equipo humano no escala así sin perder coherencia.
Coste ~$0.10-0.15 por feature (en live mode con Sonnet). Vs. ~150€ de un developer junior por una mañana de trabajo. No estamos reemplazando seniors — estamos paralelizando ejecución.
Resiliencia de proveedor: tres MODEs (mock/local/live) significa que un fallo de Anthropic no bloquea el desarrollo. Caí en esto la semana pasada cuando billing se atascó 6 horas.
Auditabilidad: cada brief, cada patch, cada test report queda persistido en R2 con su
task_id. Tres meses después puedes responder "¿por qué este código se diseñó así?" — está literalmente todo guardado.Velocidad de iteración del sistema mismo: editar una skill markdown y ver cómo cambia el comportamiento del agente en el siguiente task es un feedback loop que no existe en equipos humanos.
Desventajas honestas
Calidad cognitiva por debajo del frontier: Qwen Coder 32B local va muy bien para iterar, pero no llega a Sonnet 4.6 en código complejo. Sonnet 4.6 a su vez no llega a un senior humano experimentado en decisiones arquitectónicas no triviales.
Latencia: pipeline 5-pasos en
localmode tarda ~6 minutos. Enlive~45 segundos. Compáralo con un senior humano que decide en 30 segundos lo más obvio. No es feedback en tiempo real todavía.Sandboxing complejo: el Feature Developer hoy no escribe a disco. El QA Tester futuro requerirá Docker-in-Docker o un runner agent con permisos chequeados. Es trabajo serio de infra de seguridad.
Cadena de dependencias: si el Project Mapper falla, todo lo downstream falla. Hay que diseñar fallbacks (mock por agente) y gracefulness (state: failed estructurado), cosa que añade complejidad.
Coste cognitivo de mantener prompts: cuando los modelos LLM cambian (nueva versión, otra familia), las skills pueden necesitar rework.
El riesgo que casi nadie dice: sin senior, no funciona
Aquí va la parte incómoda.
Los agentes IA democratizan la ejecución pero no el criterio. Tener 7 agentes corriendo no convierte a un junior en senior. Convierte a un senior en alguien con superpoderes — y a un junior en alguien produciendo deuda técnica más rápido de lo que puede revisar.
¿Qué pasa cuando un equipo sin experiencia adopta esto?
1. No detectan briefs malos. El Tech Lead puede
producir un brief que suene plausible pero apunte a
src/features/oauth/ cuando ese path no existe. Un senior lo
cazaría leyendo el brief en 10 segundos. Un junior acepta, ejecuta,
descubre el problema 2 horas después con el código a medio escribir.
2. No detectan código malo que pasa tests. Los tests miden lo que les pides medir. Si el brief no incluyó el escenario edge case correcto, los tests pasan y el código tiene un bug latente. Un senior huele "esto pasa los tests pero algo huele raro". Un junior celebra el verde y mergea.
3. Confían en el output que parece bueno. Los LLMs producen prosa plausible y código sintácticamente correcto. La diferencia entre "código correcto" y "código plausible" es la línea que separa software que sobrevive 2 años de software que crashea en producción a los 6 meses. Esa línea solo la ve quien ha vivido los crashes anteriores.
4. No saben cuándo parar el loop. El QA Tester va a iterar con el Feature Developer hasta que pasen los tests. Pero si el problema es de diseño (el brief estaba mal), iterar no resuelve — solo introduce parches. Un senior detecta "esto no se arregla con más loops, hay que rediseñar". Un junior lo deja iterar hasta que aparenta funcionar.
5. Producen tech debt a velocidad de IA. Esto es lo más serio. Un junior con un equipo agéntico produce más código que un senior solo. Pero la fracción de ese código que está bien diseñado es menor. El ratio de deuda técnica generada vs. valor entregado se invierte. Y al contrario que un junior humano que aprende de sus errores, el junior con agentes los repite a escala porque no los reconoce.
El rol del senior en un equipo agéntico
No desaparece. Cambia:
- Diseña las skills: el contenido del markdown es
donde reside la experiencia. Editar
feature-briefing.mdpara añadir un constraint nuevo es como impartir formación al Tech Lead. - Revisa outputs contra intuición: cuando un brief huele raro, lo detecta. Cuando un patch resuelve los tests pero deja deuda, lo detecta.
- Caza failure modes: las primeras semanas con el sistema, cualquier caso raro que aparece va a la documentación (futuro Project Tech Librarian) o a un nuevo constraint en las skills.
- Itera la arquitectura del meta-sistema: ¿hace falta un nuevo agente? ¿hay que separar dos roles que están mezclados? ¿la longitud del brief es óptima? Esas decisiones siguen siendo del senior.
El senior ya no escribe la mayoría del código. Diseña el sistema que escribe el código. Y revisa lo que escribe.
Si tu organización está pensando en adoptar arquitecturas agénticas, la pregunta correcta no es "¿cuántos juniors puedo reemplazar?". Es "¿cuántos seniors necesito para vigilar este sistema?". La respuesta es siempre mayor que cero. Y probablemente más de los que pensabas.
Lo que viene en esta serie
Este post es el manifiesto. La serie técnica que sigue:
- Día 1 — Walking skeleton: primer ping A2A funcionando
- Día 2 — De Auditor a Tech Lead: por qué los nombres son prompts
- Día 3 — A2A + MCP juntos: exponer un repo como Model Context Protocol server
- Día 4 — El bloqueo de Anthropic billing y el patrón de tres MODEs con Ollama
- Día 5 (próximo) — Comparativa side-by-side Qwen Coder 32B local vs Sonnet 4.6 sobre el mismo brief
- Día 6 — Construyendo el QA Tester con loop de iteración
- Día 7 — El Project Tech Librarian que hace al sistema aprender
- Día N — Despliegue a Cloudflare Workers, sandbox real, multi-tenant
Si te interesa el cómo sin perder de vista el por qué, suscríbete. Cada post mantiene la honestidad de los obstáculos reales — incluyendo los bugs que pisé, los reframes que hice a media implementación, y los momentos donde paré a preguntarme si estaba construyendo algo útil o solo sofisticado.
Este sistema funciona porque está diseñado por alguien que ha visto demasiadas veces lo que pasa cuando no funciona. Si lo replicas, asegúrate de tener la misma carga de cicatrices. Si no las tienes, asegúrate de tener un senior que sí.
Agentikas MVP cierra hoy con: 5 agentes operativos, 1 servidor MCP, 2 agentes pendientes (QA Tester, Tech Librarian), 3 modos por agente (mock/local/live), persistencia en Cloudflare R2, monorepo npm workspaces, y un solo Dockerfile parametrizado para 7 servicios. El código está en el repo. Las cicatrices están en los blog posts que vienen.
Comentarios
Cargando comentarios…
Inicia sesión en tu dashboard para participar.