El prompt del sistema de Claude Code: lecciones de ingeniería para quienes crean agentes
El mes pasado vi cómo un agente de programación interno eliminaba el archivo generado equivocado, «corregía» el fallo de una prueba que había provocado y luego informaba con seguridad de que todo había salido bien. El problema de fondo no era la calidad del modelo. Era un límite mal definido: el agente tenía permiso para editar demasiadas cosas, reglas vagas sobre los artefactos generados y ninguna obligación de pedir confirmación antes de realizar acciones destructivas.
Por eso resulta útil el reciente debate público sobre los prompts del sistema de Claude y Claude Code, aunque no conviene tratar los prompts filtrados o recopilados como verdades absolutas. Las recopilaciones de prompts suelen ser extraoficiales, incompletas, obsoletas o extraídas de un estado del producto que ya no existe. El propio Claude Code evoluciona rápidamente, y la gama actual de Claude —Opus 4.8, Sonnet 4.6, Haiku 4.5 y Fable 5 con un contexto de 1M— ofrece a los creadores de agentes un equilibrio de ventajas y limitaciones muy distinto al de las generaciones anteriores.
Pero la lección interesante no es «copia este prompt». La lección útil es arquitectónica: los agentes de programación maduros incorporan límites operativos, prioridades de instrucciones, reglas de confirmación, disciplina de acceso a archivos y comportamiento ante fallos en el sistema que rodea al modelo.
Esta publicación trata sobre esas lecciones.
Lecciones del prompt del sistema de Claude Code sin copiar el prompt
La expresión «prompt del sistema de Claude Code» se ha convertido en una forma abreviada de preguntar «¿cómo le indica al modelo un agente de programación serio que debe comportarse?». Es la pregunta equivocada si conduce a la arqueología de prompts, pero la correcta si conduce a un mejor diseño de agentes.
En la práctica, el prompt del sistema es solo una capa. Un agente apto para producción suele tener al menos cinco mecanismos de control que cooperan entre sí:
| Capa | Qué controla | Ejemplo |
|---|---|---|
| Instrucciones del sistema | Contrato de comportamiento estable | «Prioriza los cambios mínimos; no hagas commits salvo que te lo pidan». |
| Política del desarrollador | Reglas específicas del producto | «Usa apply_patch; nunca ejecutes comandos destructivos sin aprobación». |
| Esquema de herramientas | Lo que el modelo puede hacer realmente | read_file, edit_file, run_shell, update_plan |
| Permisos de ejecución | A qué puede acceder el proceso | Entorno aislado, directorio de trabajo, política de red |
| Instrucciones del usuario | Objetivo específico de la tarea | «Corrige la prueba de autenticación que falla». |
El error que veo en muchos agentes desarrollados internamente es agrupar todo esto en un único prompt extenso. Eso funciona en las demostraciones. Falla bajo presión.
Un enfoque mejor consiste en usar el prompt para explicar la intención y el entorno de ejecución para hacer cumplir los límites de capacidad.
Por ejemplo, no te limites a indicar en el prompt:
Do not delete files unless the user asks.
Haz que la eliminación sea una herramienta independiente que requiera confirmación:
{
"name": "delete_file",
"description": "Delete a file from the workspace.",
"input_schema": {
"type": "object",
"properties": {
"path": { "type": "string" },
"reason": { "type": "string" },
"user_confirmed": { "type": "boolean" }
},
"required": ["path", "reason", "user_confirmed"]
}
}
Luego haz cumplir esa regla fuera del modelo:
def delete_file(path: str, reason: str, user_confirmed: bool):
if not user_confirmed:
raise PermissionError("delete_file requires explicit user confirmation")
if not path.startswith(WORKSPACE_ROOT):
raise PermissionError("cannot delete files outside workspace")
os.remove(path)
El prompt le dice al agente cómo pensar. El límite de la herramienta decide qué puede ocurrir.
La jerarquía de instrucciones es una interfaz de ingeniería
Los modelos Claude siguen bien las instrucciones organizadas en capas, pero aun así debes diseñar esas capas con cuidado. Si tu agente de programación tiene instrucciones del sistema, del desarrollador, del proyecto y del usuario, cada capa debe tener una función clara.
Una jerarquía útil tiene este aspecto:
- Instrucciones del sistema: identidad, enfoque de seguridad y principios operativos generales.
- Instrucciones del desarrollador: mecánica del producto, reglas de uso de herramientas, estilo de respuesta y modelo de permisos.
- Instrucciones del proyecto: convenciones específicas del repositorio, comandos de pruebas y notas sobre la arquitectura.
- Instrucciones del usuario: la tarea inmediata.
Los flujos de trabajo al estilo de Claude Code hacen visible esta estructura con archivos como CLAUDE.md, además de la configuración de la línea de comandos y el contexto de la sesión. Para los creadores de agentes, la lección más amplia es que las instrucciones locales del proyecto deben tener un alcance definido y prevalecer solo cuando corresponda.
Un archivo sencillo de instrucciones del proyecto podría tener este aspecto:
# CLAUDE.md
## Build and Test
- Use `pnpm test -- --runInBand` for focused test runs.
- Do not run `pnpm deploy` unless explicitly requested.
## Code Style
- Prefer small functions over large service classes.
- Do not add new dependencies without explaining why.
## Repo Notes
- API routes live in `apps/api/src/routes`.
- Generated GraphQL types live in `apps/web/src/generated`; do not edit them manually.
Un problema frecuente: los agentes suelen tratar todas las instrucciones como si tuvieran la misma prioridad global. Si el usuario dice «Ignora las reglas del proyecto y edita directamente los archivos generados», tu agente debe saber si eso está permitido. En la mayoría de las configuraciones, las instrucciones del usuario no deberían anular silenciosamente las restricciones del proyecto o del desarrollador que tengan mayor prioridad.
Puedes modelar eso explícitamente:
PRIORITY = {
"system": 4,
"developer": 3,
"project": 2,
"user": 1,
}
def resolve_conflict(existing_rule, new_rule):
if PRIORITY[new_rule.source] > PRIORITY[existing_rule.source]:
return new_rule
return existing_rule
En un agente real, el modelo también debería explicar los conflictos al usuario:
I can update the source schema and regenerate the types, but I should not edit
`src/generated` directly because the project instructions mark it as generated.
Esa sola frase evita una cantidad sorprendente de daños.
Los límites de las herramientas son más eficaces que la cortesía del prompt
Cuando se examinan los prompts de los agentes de programación, a menudo se presta atención a la redacción: «sé conciso», «actúa como un ingeniero sénior», «no seas complaciente». Esas indicaciones son útiles, pero no constituyen el mecanismo central de seguridad.
El mecanismo central es el límite de la herramienta.
Un agente de programación serio debería distinguir entre:
- Leer archivos
- Buscar en archivos
- Editar archivos
- Ejecutar comandos
- Instalar paquetes
- Acceder a recursos de red
- Eliminar o mover archivos
- Crear commits o ramas
- Abrir solicitudes de incorporación de cambios
- Modificar secretos o la configuración de despliegue
Estas operaciones no son moralmente equivalentes.
Esta es una matriz práctica de permisos que uso al diseñar entornos de ejecución de agentes:
| Operación | Política predeterminada | Por qué |
|---|---|---|
| Leer archivos del espacio de trabajo | Permitir | Necesario para recopilar contexto |
Buscar con rg | Permitir | Riesgo bajo y gran utilidad |
| Editar archivos fuente bajo control de versiones | Permitir con los cambios visibles | Flujo de trabajo principal de programación |
| Ejecutar pruebas específicas | Permitir normalmente | Verifica los cambios |
| Instalar dependencias | Pedir aprobación | Modifica el entorno y los archivos de bloqueo de dependencias |
| Eliminar archivos | Pedir aprobación | Puede causar fácilmente pérdidas irreversibles |
| Acceder a la red | Pedir aprobación o denegar | Puede filtrar datos o introducir un comportamiento no determinista |
| Editar secretos | Denegar de forma predeterminada | Gran alcance de los posibles daños |
| Crear commits de los cambios | Pedir aprobación | El historial del repositorio pertenece al usuario |
| Desplegar | Denegar salvo que se solicite explícitamente | Impacto en producción |
Aquí también importa la elección del modelo. Me gustan los modelos de la categoría Sonnet para la mayoría de los ciclos de los agentes de programación porque ofrecen un buen equilibrio entre latencia, coste y razonamiento. Opus 4.8 es el modelo al que recurriría cuando el agente necesita una síntesis arquitectónica más profunda o una depuración compleja. Haiku 4.5 encaja con la clasificación rápida y la exploración ligera de repositorios. El contexto de 1M de Fable 5 resulta interesante para comprender grandes bases de código, pero un contexto amplio no elimina la necesidad de una buena recuperación de información, lecturas con un alcance definido y disciplina en el uso de herramientas.
Un millón de tokens de contexto puede seguir conteniendo el archivo equivocado.
La confirmación del usuario debe ser específica, no un mero trámite
Una mala experiencia de confirmación se ve así:
May I proceed?
¿Continuar con qué? ¿Editar un archivo? ¿Ejecutar migraciones? ¿Eliminar un directorio?
Una buena confirmación es concreta:
I need confirmation before running:
`rm -rf apps/web/.next`
Reason: remove a stale Next.js build cache that may be causing the local
runtime error. This deletes only generated build output and does not touch
source files.
Para los agentes similares a Claude Code, esto importa porque el modelo puede inferir correctamente el siguiente paso y aun así necesitar autorización humana. Si cada solicitud pide una aprobación vaga, los usuarios se cansan de aprobar y aceptan sin prestar atención. Si solo las operaciones de alto riesgo requieren una confirmación precisa, los usuarios mantienen la atención.
Recomiendo representar la confirmación como un estado estructurado, no como texto:
{
"action": "run_shell",
"command": "rm -rf apps/web/.next",
"risk": "destructive_generated_artifact",
"requires_confirmation": true,
"confirmation_prompt": "Delete generated Next.js cache at apps/web/.next?"
}
Así, el ejecutor de comandos de shell puede hacerla cumplir:
def run_shell(command, confirmed=False):
risk = classify_command(command)
if risk.requires_confirmation and not confirmed:
return {
"status": "needs_confirmation",
"message": risk.prompt
}
return subprocess.run(command, shell=True, cwd=WORKSPACE_ROOT)
En la práctica, la clasificación de comandos nunca será perfecta. No pasa nada. Usa valores predeterminados conservadores. Si el clasificador no tiene claro si un comando es destructivo, pregunta.
Comandos que conviene tratar con cuidado
Estos requieren una política explícita:
rm -rf path
git reset --hard
git clean -fd
git push --force
npm install some-package
pip install -r requirements.txt
docker compose down -v
kubectl delete ...
terraform apply
Algunos son inofensivos en el contexto adecuado. Otros son catastróficos en el contexto equivocado. El agente debe saber que la diferencia depende del contexto y preguntar cuando el alcance del impacto no esté claro.
El acceso a archivos es ingeniería de contexto
La parte más infravalorada del diseño de agentes de programación es el acceso a archivos. El prompt puede decir «inspecciona primero los archivos relevantes», pero el entorno de ejecución debe facilitar ese comportamiento.
Como mínimo, proporciona al modelo herramientas para:
- Buscar rápidamente nombres de archivo
- Buscar texto
- Leer rangos acotados de archivos
- Ver diferencias de git
- Aplicar parches de forma atómica
Un conjunto mínimo de herramientas podría ser así:
[
{
"name": "search_files",
"input_schema": {
"type": "object",
"properties": {
"query": { "type": "string" },
"glob": { "type": "string" }
},
"required": ["query"]
}
},
{
"name": "read_file",
"input_schema": {
"type": "object",
"properties": {
"path": { "type": "string" },
"start_line": { "type": "integer" },
"limit": { "type": "integer" }
},
"required": ["path"]
}
},
{
"name": "apply_patch",
"input_schema": {
"type": "object",
"properties": {
"patch": { "type": "string" }
},
"required": ["patch"]
}
}
]
Las lecturas acotadas importan. Si el agente lee todos los archivos de un monorepositorio, desperdicia contexto y suele rendir peor. Si lee demasiado poco, hace suposiciones. El punto óptimo es adquirir contexto de forma iterativa:
rg "createSession|verifySession|SessionStore" apps packages
sed -n '1,220p' apps/api/src/auth/session.ts
sed -n '1,180p' apps/api/src/routes/login.ts
git diff -- apps/api/src/auth/session.ts
Los usuarios de Claude Code reconocerán este ritmo: buscar, inspeccionar, editar, verificar. Quienes desarrollan agentes deberían incorporar ese ciclo al comportamiento predeterminado.
Un problema frecuente: las herramientas de archivos deben preservar los finales de línea, los permisos y la codificación esperada. He visto agentes reescribir scripts de shell «con éxito» y eliminar los permisos de ejecución, o normalizar archivos de formas que generan ruido en las diferencias. La capa de edición debe ser predecible y conservadora.
Modos de fallo: qué ocurre realmente cuando los agentes fallan
La mayoría de los agentes de programación no fallan de forma espectacular. Fallan de formas que parecen razonables.
Estos son los modos de fallo que veo con más frecuencia.
1. El agente resuelve el síntoma
La prueba falla porque una API devuelve 401. El agente cambia la prueba para que espere 401. La compilación pasa, pero el producto está roto.
Mitigación:
- Exige que el agente explique la cadena causal.
- Pide pruebas específicas antes de hacer reescrituras amplias.
- Da prioridad a corregir el código fuente frente a actualizar las instantáneas, salvo que el usuario haya solicitado un cambio de comportamiento.
2. El agente edita demasiado
El usuario pide corregir un pequeño error. El agente reformatea cinco archivos, cambia nombres de variables y modifica API públicas.
Mitigación:
Make the minimal change that fixes the issue. Do not perform opportunistic
refactors unless they are necessary for correctness.
Pero también exige revisar las diferencias. Muestra los archivos modificados y el recuento de líneas antes de finalizar:
git diff --stat
git diff -- apps/api/src/auth/session.ts
3. El agente confía en contexto desactualizado
Las sesiones largas acumulan suposiciones desactualizadas. El usuario modifica un archivo por fuera, las pruebas ahora fallan de otra manera y el agente sigue trabajando con el modelo mental anterior.
Mitigación:
- Vuelve a leer los archivos antes de editarlos si pueden haber cambiado.
- Comprueba
git diffantes de aplicar parches. - Trata la salida de las herramientas como información más reciente que la memoria de la conversación.
4. El agente oculta el fallo de una herramienta
Un comando de shell falla, pero el agente continúa como si hubiera funcionado.
Mitigación: los resultados de las herramientas deben ser estructurados y difíciles de ignorar.
{
"tool": "run_shell",
"command": "pnpm test auth",
"exit_code": 1,
"stdout": "...",
"stderr": "Cannot find module '@repo/config'",
"status": "failed"
}
Después, indica al agente:
If a tool call fails, do not claim success. Diagnose the failure or clearly
report the blocker.
5. El agente introduce secretos en el contexto
Si el agente puede leer .env, volcados de errores o configuraciones de producción, puede incluir secretos accidentalmente en resúmenes, registros o prompts.
Mitigación:
- Bloquea por defecto los archivos conocidos que contengan secretos.
- Oculta los valores que parezcan tokens en la salida de las herramientas.
- Exige la aprobación explícita del usuario para acceder a rutas que contengan secretos.
Ejemplo de política de rutas:
DENY_PATTERNS = [
".env",
".env.*",
"**/secrets/**",
"**/*.pem",
"**/*.key",
]
def can_read(path):
return not any(fnmatch(path, pattern) for pattern in DENY_PATTERNS)
Indicar al modelo que no filtre secretos está bien. Evitar que vea secretos innecesarios es mejor.
Diseñar un ciclo de agente con la API de Claude
Si desarrollas directamente sobre la API de Claude, el ciclo útil más sencillo es:
- Envía las instrucciones de sistema y de desarrollador.
- Proporciona la tarea del usuario.
- Deja que Claude solicite herramientas.
- Ejecuta las herramientas en tu entorno de ejecución.
- Devuelve los resultados de las herramientas.
- Repite hasta que el modelo genere una respuesta final.
Una estructura simplificada de solicitud se ve así:
{
"model": "claude-sonnet-4-6",
"max_tokens": 4096,
"system": "You are a coding agent. Make minimal, correct changes...",
"messages": [
{
"role": "user",
"content": "Fix the auth timeout bug and add a regression test."
}
],
"tools": [
{
"name": "read_file",
"description": "Read a bounded section of a workspace file.",
"input_schema": {
"type": "object",
"properties": {
"path": { "type": "string" },
"start_line": { "type": "integer" },
"limit": { "type": "integer" }
},
"required": ["path"]
}
}
]
}
Conceptualmente, una respuesta de uso de herramientas se ve así:
{
"role": "assistant",
"content": [
{
"type": "text",
"text": "I’ll inspect the auth timeout path first."
},
{
"type": "tool_use",
"name": "read_file",
"id": "toolu_123",
"input": {
"path": "apps/api/src/auth/session.ts",
"start_line": 1,
"limit": 220
}
}
]
}
Después, tu entorno de ejecución devuelve el resultado:
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "toolu_123",
"content": "export function createSession(...) { ... }"
}
]
}
La sintaxis exacta del SDK depende de la versión del cliente, pero el principio de diseño se mantiene: Claude propone acciones, tu entorno de ejecución las ejecuta o rechaza, y el siguiente turno del modelo recibe el resultado.
Esa separación es lo que mantiene el sistema comprensible.
Comandos y ajustes de Claude Code que conviene estudiar
Aunque no uses Claude Code directamente, su flujo de trabajo ofrece patrones de producto útiles.
Inicialización típica de un proyecto:
claude
/init
Eso crea o actualiza las directrices del proyecto para que las sesiones futuras comprendan las convenciones específicas del repositorio.
Una sesión práctica suele ser así:
claude "Find why the billing webhook test is flaky. Do not edit files yet."
claude "Patch the smallest fix and run the focused test."
claude "Show me the diff and summarize remaining risks."
Me gusta este estilo porque separa la investigación, la modificación y la verificación. Quienes desarrollan agentes pueden reflejarlo mediante modos:
{
"mode": "investigate",
"can_edit": false,
"can_run_tests": true,
"requires_confirmation_for": ["network", "delete", "install"]
}
Después, cambia de modo explícitamente:
{
"mode": "implement",
"can_edit": true,
"can_run_tests": true,
"requires_confirmation_for": ["network", "delete", "install", "commit"]
}
Lo importante no es el comando específico. Es el contrato de interacción. Los usuarios deben saber cuándo el agente solo está leyendo, cuándo está modificando archivos y cuándo está a punto de hacer algo con efectos secundarios.
Por qué los volcados de prompts son frágiles
El repositorio de GitHub que recopilaba prompts de sistema y configuraciones de modelos se hizo popular porque los ingenieros sienten una curiosidad natural por cómo se dan instrucciones a las herramientas reales. Entiendo su atractivo. Leer prompts puede revelar el criterio de producto: qué riesgos preocupan a sus creadores, qué flujos de trabajo optimizan y cómo se comunican con el modelo.
Pero los prompts copiados son frágiles por varias razones:
- Pueden no ser oficiales. Puede que no sepas cómo se extrajeron o si están completos.
- Pueden estar desactualizados. Los prompts de los agentes cambian a medida que cambian los modelos, las herramientas y las políticas del producto.
- Dependen de un comportamiento oculto del entorno de ejecución. Un prompt que menciona una herramienta es inútil sin la implementación de esa herramienta.
- Incorporan decisiones de compromiso específicas del producto. Las restricciones de Claude Code pueden no coincidir con el modelo de amenazas de tu agente.
- Pueden generar una falsa sensación de confianza. Un prompt bien elaborado no hace cumplir los permisos.
Usa estos materiales como inspiración para el diseño, no como dependencias.
Un ejercicio mejor es preguntarse: ¿qué problema resuelve esta instrucción? Después, resuelve ese problema en tu propia pila tecnológica con apoyo tanto del prompt como del entorno de ejecución.
Conclusiones prácticas
- Trata la
Claude Code system promptconversación como un debate sobre arquitectura, no como una oportunidad para copiar y pegar. - Coloca el comportamiento estable en las instrucciones de sistema y de desarrollador, las convenciones del repositorio en los archivos del proyecto y los objetivos de la tarea en los mensajes del usuario.
- Haz cumplir los límites de alto riesgo mediante las herramientas y los permisos del entorno de ejecución, no solo mediante texto.
- Exige una confirmación específica para las acciones destructivas, que usen la red, que modifiquen dependencias o que afecten a producción.
- Proporciona al agente acceso acotado a archivos, búsqueda rápida, aplicación atómica de parches y fallos de herramientas estructurados.
- Optimiza para el ciclo que funciona en la práctica: inspeccionar, planificar, editar lo mínimo, verificar y resumir los riesgos.
- Asume que los prompts copiados pueden estar desactualizados o incompletos; construye un comportamiento duradero para el agente basado en capacidades explícitas y una gestión clara de los fallos.
Una sola clave de API para Claude Opus 5.5, Sonnet 5, Haiku 4.5 y Fable 5.1, además de GPT-6 modelos. Pago por uso, sin suscripción.
Obtén tu clave de API →