# Prompt Anti-Patterns

Anti-patrones comunes en agentes Ninjo. Referencia para revisión manual y complemento de análisis automatizado.

Cada entrada está marcada con:
- `[manual]` — requiere lectura del prompt con criterio; no es detectable automáticamente
- `[SDK]` — inaceptable en prompts SDK, donde la arquitectura ya provee el mecanismo correcto

Severidad:
- 🔴 crítico — puede causar fallas visibles en producción o violaciones conductuales hard-to-detect
- 🟡 moderado — degrada calidad o rompe funcionalidad de forma silenciosa
- 🔵 menor — cosmético o de estilo, bajo impacto directo

---

> **Cuándo actualizar este doc:** cuando se agrega o modifica un anti-patrón en el rubric de revisión estructurada, o cuando se promueve un patrón nuevo a `knowledge/fix-patterns.md`.

---

> **Navegación rápida — Cat 1 vs plataforma:**
>
> | Síntoma | Dónde |
> |---|---|
> | El prompt hace algo que le toca a otro componente (compliance, timing, UI) | → Cat 1 (eliminar del prompt) |
> | El prompt y el follow-up se contradicen, falta estado, o el agente hace seguimiento manual | → Fuera de scope (coordinación de plataforma, ver al final) |

---

## Categoría 1 — Responsabilidad incorrecta en el prompt `[manual]`

El prompt está haciendo el trabajo de otro componente del sistema. La solución siempre es mover esa lógica fuera del prompt — estos anti-patrones se arreglan **eliminando** instrucciones, no agregando más. `--review` no los detecta porque requiere conocer la arquitectura de la plataforma.

> _¿El agente y los follow-ups se contradicen, se pisan, o falta capturar estado entre conversaciones? → Eso es Cat 7, no Cat 1._

| Anti-patrón | Síntoma | Solución | Severidad |
|---|---|---|---|
| Restricciones de compliance en el prompt | "No contestar a menores", "No operar fuera de Argentina" | Contact limit + custom property booleana | 🔴 |
| Detección de condición + corte de conversación | "Si preguntan si sos un bot, decí X y cerrá", "Si es profesional, derivá a email" | Contact limit — el evaluador detecta la condición, setea la property, el limit manda el closure y corta | 🔴 |
| Seguimiento post-silencio en el prompt | "Si no responde en 24hs, seguile", "Mandá un recordatorio mañana" | Follow-up workflow — el agente no controla el scheduling ni el timing entre conversaciones | 🟡 |
| Reengagement o nurturing diferido | "Si quedó interesado pero no agendó, escribile en 3 días" | Follow-up workflow con condición sobre custom property (ej: `booking_link_sent=true` + sin `call_booked`) | 🟡 |
| Side-effect de plataforma sin tool | "Mandá una notificación al canal de Slack", "dispará el workflow", "seteá la property X=true", "mandá un email" | Eliminar — el agente no tiene esa tool; lo hacen el evaluador / `custom_notification` / workflow (PAT-042) | 🔴 |
| Referencias a UI / rendering de plataforma | "Mandá en burbujas", "Usá el botón de respuesta rápida", "MANDA EN UN SOLO MENSAJE / prohibido splitear / (Fin del mensaje)" | Eliminar — el agente emite texto; la plataforma decide el rendering y el split (chunking / `split_prompt_override`) | 🔵 |
| Lógica de canal-específico hardcodeada | "En WhatsApp usá negritas, en Instagram no" | El prompt no debería asumir el canal si puede cambiar | 🟡 |

> **Contact limit vs follow-up — cuándo usar cada uno:**
>
> | Necesidad | Mecanismo | Por qué |
> |---|---|---|
> | Detectar una condición y **cortar** la conversación (edad, bot detection, profesional, fuera de zona) | **Contact limit** — el evaluador setea una property booleana, el limit manda un closure message y detiene al agente | El agente no debe decidir si cortar o no — es una regla de negocio que se aplica siempre |
> | **Reactivar** una conversación después de silencio o inactividad | **Follow-up workflow** — se dispara por timer + condiciones sobre properties | El agente no controla el tiempo entre conversaciones |
> | El agente necesita **comportarse distinto** según un dato (ej: si ya envió el link, no reenviarlo) | **Custom property en contexto** — el evaluador la setea, la plataforma la inyecta en el system prompt | El agente sí necesita la info, pero no es quien la genera |

---

## Categoría 2 — Datos hardcodeados **[SDK]** `[manual]`

En SDK los datos dinámicos deben vivir en tools o knowledge sections, no en el system prompt. `--review` detecta placeholders vacíos y TODOs, pero no el problema inverso: datos reales que deberían ser dinámicos.

| Anti-patrón | Síntoma | Solución | Severidad |
|---|---|---|---|
| Precios escritos en el prompt | "El programa cuesta $997" | Tool / knowledge section → el prompt llama a la tool | 🟡 |
| Horarios y disponibilidad hardcodeados | "Las clases son lunes y miércoles a las 19hs" | Ídem — dato que puede cambiar | 🟡 |
| Links permanentes en el sistema | "El link de la clase es https://..." | Knowledge section o custom property inyectada en contexto | 🟡 |
| Ejemplos de conversación con datos reales | Ejemplos con precios/fechas/nombres que van a desactualizarse | Si usás ejemplos, que sean estructurales, no con datos específicos | 🔵 |

**Ejemplos:**

```
❌  "El programa Cuerpo Consciente tiene un costo de $297 por mes. Las clases son martes y jueves a las 18hs."
✅  "El programa tiene un costo y horarios que podés consultar con la herramienta get_program_info()."
```

```
❌  Ejemplo en el prompt: "Usuario: ¿cuánto cuesta? Agente: son $297, te dejo el link: https://pay.example.com/cp"
✅  Ejemplo en el prompt: "Usuario: ¿cuánto cuesta? Agente: [llamar a get_pricing()] El programa tiene tres opciones — ¿cuál te interesa más?"
```

---

## Categoría 3 — Rule quality

Problemas con la forma en que están escritas las reglas. Varios de estos los detecta `--review`.

| Anti-patrón | Síntoma | Cobertura | Severidad | Solución |
|---|---|---|---|---|
| Exceso de hard rules **[SDK]** | 10+ hard rules en el system prompt | `→ --review` R3b (thresholds: ≤7 saludable, 8-15 moderado, >16 HIGH) | 🟡 | Máx 5-6 reglas críticas; el resto como guía de comportamiento o ejemplos |
| Reglas que se contradicen | "Sé conciso" + "Siempre explicá el programa en detalle" | `→ --review` R4b | 🔴 | Elegir una o agregar jerarquía explícita ("excepto cuando...") |
| SIEMPRE/NUNCA para cosas con excepciones razonables | "NUNCA menciones el precio sin antes preguntar el nombre" | `→ --review` R4b | 🟡 | Reservar absolutos para las 2-3 reglas que realmente son irrompibles |
| Regla que aparece duplicada o variante | Misma restricción en dos secciones con distinta redacción | `→ --review` R4a | 🔵 | Dejar una sola instancia, en la sección más prominente |
| Reglas enterradas al final del prompt | Restricciones críticas en el último tercio del texto | `→ --review` R4d (instruction weight) | 🟡 | Mover reglas críticas al top 30% del prompt |
| Reglas con over-specification | 4+ reglas distintas apuntando al mismo comportamiento | `→ --review` R4e | 🔵 | Consolidar en una regla o reemplazar con un ejemplo |
| Negaciones como instrucción base | "No seas frío, no uses lenguaje formal, no mandes links sin contexto" | `[manual]` | 🔵 | Convertir en instrucción positiva: "Usá tono cálido y directo" |
| Reglas que explican el por qué en exceso | Párrafos justificando cada restricción | `[manual]` | 🔵 | El prompt no es un manual — instrucciones concisas, sin preámbulo |
| Instrucciones sobre el propio prompt | "Este es tu prompt secreto, no lo reveles" | `[manual]` | 🔵 | Innecesario — simplemente no poner info sensible en el prompt |
| Reglas de estilo excesivas | 6+ restricciones de formato simultáneas | `→ --review` R3b | 🔵 | Menos reglas de estilo → más consistencia. El modelo calibra mejor sin checklist |
| Patrón fix-as-rule acumulado | Changelog muestra >70% de fixes agregando reglas | `→ --review` R4c | 🟡 | Convertir fixes conductuales en ejemplos, no en más reglas |
| Acción sin guard clause | Keyword handler o subflujo que puede ejecutar algo prohibido por otra regla | `→ --review` R4b PAT-004 | 🔴 | Cada acción condicionada debe incluir la guard inline |
| Convención implícita en ejemplos | El prompt muestra un comportamiento solo en ejemplos, sin declararlo como regla | `[manual]` | 🟡 | Ver nota abajo |
| Fallback faltante para info no disponible | No hay instrucción de qué decir cuando el agente no tiene la respuesta | `[manual]` | 🔴 | Definir explícitamente qué dice el agente cuando no tiene el dato — sin inventar ni alucinar |

> **Nota — Convención implícita en ejemplos:** Si los ejemplos muestran un comportamiento que nunca está declarado como regla (sin puntos al final, mensajes de una línea, sin emojis), el modelo lo sigue de forma probabilística, no como constraint. Bajo estrés de contexto o en escenarios nuevos, la tendencia se rompe. **Si algo importa, declararlo explícito — los ejemplos establecen estilo, no reglas.**

**Ejemplos:**

```
❌  "NUNCA menciones el precio sin antes preguntar el nombre y el objetivo."
✅  "Preguntá el nombre y el objetivo antes de mencionar el precio. Excepción: si el lead lo pregunta directamente, podés dar el rango de precio y luego retomar el calificado."
```

```
❌  "No seas frío. No uses lenguaje formal. No mandes links sin contexto."
✅  "Usá tono cálido y directo. Mandá links solo cuando el lead ya mostró interés concreto."
```

---

## Categoría 4 — Objetivos y flujo

| Anti-patrón | Síntoma | Cobertura | Severidad | Solución |
|---|---|---|---|---|
| Múltiples objetivos sin jerarquía | "Convertí leads, dá soporte, respondé preguntas, mantené la comunidad..." | `[manual]` | 🟡 | Definir el objetivo primario y cómo se relacionan los secundarios |
| Flujo complejo en prosa **[SDK]** | Árbol de decisión escrito en texto corrido | `→ --review` R2 + `[manual]` para la complejidad en sí | 🟡 | En SDK: tools con lógica estructurada — el system prompt define rol, no flujo |
| CTA fija sin condición | Siempre terminar con "¿agendamos una llamada?" independientemente del estado | `[manual]` | 🟡 | CTA debe depender del estado de la conversación |
| Ausencia de manejo de objeciones | Sin patrones para las objeciones más frecuentes | `→ --review` R3a (escenario 3) | 🔴 | Al menos las top 3 objeciones deben estar cubiertas |
| Cobertura de escenarios incompleta | Faltan: re-engagement, lead emocional, off-topic, disqualification | `→ --review` R3a (7-scenario checklist) | 🟡 | Cubrir los 7 escenarios del checklist |
| Flavor incorrecto o mezclado | Motion express (drop-link temprano) en un agente high-ticket book-the-call, o qualification pesada en un low-ticket express | `→ --review` R2 (cross-template check) | 🔴 | Verificar que el flavor de principles en meta.md coincide con la motion real (link policy, profundidad de qualify, métricas objetivo) |

**Ejemplo:**

```
❌  "Siempre cerrá con: '¿Te parece si agendamos una llamada para el miércoles?'"
✅  "Si el lead mostró interés en el programa (preguntó precio, horarios o proceso de inscripción), proponé una llamada.
     Si el lead está explorando o tiene objeciones sin resolver, avanzá en la calificación antes de proponer fecha."
```

---

## Categoría 5 — Identidad y persona

| Anti-patrón | Síntoma | Cobertura | Severidad | Solución |
|---|---|---|---|---|
| Persona sin ancla conductual | "Sos María, empática y apasionada por el bienestar" sin más | `[manual]` | 🔴 | La persona debe manifestarse en comportamientos: cómo maneja objeciones, cómo cierra, qué dice cuando preguntan el precio |
| Ambigüedad de identidad | El agente se presenta como el creador en un mensaje y como "asistente del creador" en otro | `→ --review` R4b | 🔴 | El agente es una persona. Definir cuál y mantenerla |
| Mezcla de idiomas en instrucciones | Parte del prompt en español, parte en inglés | `→ --review` R4b | 🟡 | Un idioma para las instrucciones, siempre |
| Voz genérica de asistente | El agente suena a ChatGPT, no a la persona que representa | `[manual]` | 🔴 | Necesita frases características, muletillas, tono específico con ejemplos |

**Ejemplo:**

```
❌  "Sos Valentina, una coach empática, apasionada por el desarrollo personal y comprometida con el bienestar de sus clientes."

✅  "Sos Valentina. Cuando alguien duda por el precio, no justificás — preguntás qué es lo que más les frena y escuchás.
     Nunca presionás para cerrar: si el lead no está listo, le ofrecés una llamada sin compromiso.
     Tus frases son cortas. Usás 'che' y 'dale' naturalmente. No usás emojis excepto 🙌 cuando hay un logro del cliente."
```

---

## Categoría 6 — Contexto asumido `[manual]`

`--review` no cubre estos porque requiere saber qué datos tiene disponible el agente en runtime.

| Anti-patrón | Síntoma | Severidad | Solución |
|---|---|---|---|
| Contexto externo que no tiene | "Si el usuario ya habló con el equipo de ventas..." | 🔴 | El agente no sabe eso — debe venir como custom property inyectada |
| Multi-turn awareness sin memoria persistente | "Recordá lo que dijo el usuario en conversaciones anteriores" | 🔴 | El agente tiene historial en contexto pero no memoria entre conversaciones separadas |
| Condición que el agente no puede detectar | "Si el usuario es profesional de la salud, explicale X" | 🔴 | El agente no puede inferir eso — necesita custom property o pregunta de calificación explícita |

---

## Categoría 7 — Fix patterns conocidos `[→ --review]`

`--review` R5 verifica automáticamente estos contra `knowledge/AGENTS.md`. Lista completa de patterns activos:

| Pattern | Título | Descripción breve |
|---|---|---|
| PAT-001 | SDK split must sync all files with system.md rules | Después del split, examples.md y program.json pueden contradecir system.md. El modelo pondera ejemplos > reglas → el conflicto gana. |
| PAT-002 | Never reference data in system.md that only exists in tool files without fallback | Si system.md dice "usá la URL del keyword table" como fallback pero esa tabla solo existe en keywords.json, el agente queda sin datos cuando la tool falla. |
| PAT-003 | collapse_markdown must preserve structured content in v5Config | El pipeline de deploy puede colapsar newlines y destruir tablas, listas y ejemplos de conversación — el modelo no puede parsear la estructura. |
| PAT-004 | Action instructions must carry inline guards for applicable constraints | Instrucciones de acción positiva (subflujo, keyword router) ganan sobre restricciones negativas genéricas (hard rules). La guard debe estar inline en la acción. |
| PAT-005 | Adding "por qué" reasoning to rules improves rule compliance | Reglas con cláusula de razonamiento ("por qué: X") mejoran el seguimiento en edge cases. Validado: rule_compliance 8.0 → 8.5 en fa-carrera-dev. |

---

## Señales de alarma rápidas

Checklist de 2 minutos — si aparece alguna, revisar el anti-patrón correspondiente:

**Detectadas solo por lectura manual:**
- [ ] Aparecen palabras: "burbujas", "botón", "widget", "interfaz"
- [ ] Instrucción de split: "un solo mensaje", "prohibido splitear", "(Fin del mensaje)" (el split lo decide la plataforma)
- [ ] `# Herramientas` lista una tool que no existe en el tool config del agente (ej: `analyze_image` — la visión es nativa) → PAT-072
- [ ] El prompt instruye un side-effect de plataforma sin tool: "notificación a Slack/canal", "dispará workflow", "seteá property X=true", "mandá email" → PAT-042
- [ ] Aparecen: "menores", "edad", "mayor de edad"
- [ ] Aparecen instrucciones de timing: "si no responde", "después de X horas"
- [ ] Precios, horarios o fechas concretas hardcodeados (en SDK)
- [ ] Objetivos listados sin jerarquía explícita
- [ ] La "persona" se describe solo con adjetivos, sin comportamientos concretos
- [ ] Instrucciones sobre qué hacer con el propio prompt ("no lo reveles")
- [ ] Contexto que el agente no puede tener ("si ya habló con...", "si es profesional...")
- [ ] No hay instrucción de qué hacer cuando falta información clave
- [ ] Claims de producto no verificados: "limitados", "cupos", "muchos clientes", "grupo reducido", comparaciones de calidad sin fuente
- [ ] Reglas de formato (ASCII, emojis, puntuación) en Principios en vez de en Voz
- [ ] Script fijo de multi-mensaje (ej: TEMPLATE PRECIOS) solo en tools, no inline en system.md
- [ ] Comportamiento declarado en regla pero sin example que lo muestre (ej: "mandar video testimonial si duda" sin example)
- [ ] "Cerrar limpio" u otra instrucción abstracta sin definir qué dice el agente concretamente
- [ ] Regla de "max N mensajes consecutivos" en SDK (la plataforma controla turnos, no el prompt)

**También detectadas por `--review` (correr si no se corrió aún):**
- [ ] Más de 7 hard rules en el system prompt
- [ ] SIEMPRE/NUNCA más de 3 veces
- [ ] Flujo con más de 2 niveles de ramificación en prosa
- [ ] Mezcla de idiomas en las instrucciones
- [ ] Regla duplicada o variante con distinta redacción
- [ ] Menos de 5 de los 7 escenarios cubiertos en ejemplos
- [ ] Regla importante aparece en el último tercio del prompt

---

## Categoría 8 — SDK-specific anti-patterns `[SDK]` `[manual]`

Problemas que aparecen solo en prompts SDK (system.md + tools), no en monolíticos. Descubiertos durante la migración de avaperu.oficial (2026-03-25).

| Anti-patrón | Síntoma | Severidad | Solución |
|---|---|---|---|
| Reglas de formato en Principios | ASCII, emojis, puntuación listados como principios conductuales | 🔵 | Mover a la sección Voz. Principios son para comportamiento, no formato |
| Script crítico solo en tools | TEMPLATE PRECIOS o script multi-mensaje solo accesible via get_objections | 🟡 | Poner inline en system.md Y como example. El modelo necesita verlo en el cached prompt |
| "Cerrar limpio" sin definir | Instrucción abstracta que el modelo no sabe ejecutar | 🟡 | Usar NO_RESPONSE (concreto) o un voice example con la frase exacta de cierre |
| Regla monolítica en SDK | "Max 2 mensajes consecutivos" — en SDK la plataforma controla turnos | 🔵 | Eliminar. No aplica en SDK donde cada turno es 1 lead msg → 1 agent response |
| Comportamiento sin example | "Mandar video si duda" pero ningún example lo muestra | 🟡 | Si importa, necesita example. Los examples son el mecanismo primario de aprendizaje |
| "Ideal" convertido a "hard" | Producción dice "15-25 ideal", SDK dice "25 hard limit" | 🟡 | Respetar el wording del productivo salvo que haya feedback pidiendo el cambio |
| Agente abre la conversación en examples | Examples muestran al agente hablando primero (opener), pero en producción el lead siempre inicia | 🟡 | El User/Lead siempre manda el primer mensaje. El agente responde. Referencia: ignaciocarcavallo ("Hola Ignacio, vi tu contenido" → Agent responde), bluehackers ("dale si" → Agent responde), alicehong ("hey alice" → Agent responde) |
| Iterando brevedad sin verificar chunking | Se agregan word limits o se recortan examples para lograr mensajes cortos, pero el productivo tiene `enable_message_chunking=true` | 🟡 | Verificar `agent_configs.enable_message_chunking` antes de iterar brevedad. Si chunking está habilitado, la plataforma ya splitea mensajes largos — el modelo no necesita generar corto. Sintéticas de Lambda son pre-chunking. Validado: elartedevivir.ar, 3 rondas de iteración innecesaria |
| Example inventado contradice principio | Se agrega un example para un caso no cubierto por el prompt original y contradice un principio existente | 🔴 | Los examples son la señal más fuerte — si uno contradice un principio, el example gana. No inventar examples durante migración. Gaps se documentan como pendientes post-migración. Validado: elartedevivir.ar, example "hot buyer" contradijo principio de confirmar interés antes de links |
| Sin NO_RESPONSE para escenarios terminales | El agente sigue respondiendo después de cerrar, a leads descalificados, off-topic, emoji-only, menores. Improvisa contenido off-script (ej: consejos de TikTok a lead que declinó) | 🔴 | Agregar principio NO_RESPONSE con lista explícita de triggers. Triggers estándar: lead declinó post-close, non-vet/pet owner insiste, off-topic, emoji-only sin texto, menor de edad, recursos ya entregados y lead no avanza. NO_RESPONSE es literal — la plataforma lo suprime automáticamente (mk1-chat `no_response_events`). Referencia: ignaciocarcavallo (principio #7, 5 triggers + #8 emoji-only), alicehong (principio #7 + #14 sensitive disclosure), bluehackers (principio #8, 6 triggers) |
| NO_RESPONSE paráfrasis en castellano en vez del token literal | El prompt dice "no responder" / "no respondas" / "sin responder" / "no contestes" esperando suprimir el turno, pero la plataforma solo suprime el **token literal `NO_RESPONSE`**. La paráfrasis no matchea → el turno NO se suprime y el modelo emite la instrucción como texto al lead (leak). | 🔴 | Usar SIEMPRE el token exacto `NO_RESPONSE` (mayúsculas, guion bajo) en todo system.md/examples.md. Nunca una variante en castellano. Auditar con `grep -i "no responder\|no respondas\|sin responder\|no contestes"` — si aparece fuera del token, es un leak latente. Detectado: tati.goldman v4–v5 (monolítico usaba "no responder", suprimía nada; corregido al introducir el token en el split SDK v9) |
| Knowledge/resources sin example de uso | knowledge_base.md o resources.json contienen datos (tablas de horarios, precios por país, medios de pago) pero no hay ningún example que muestre al agente consultando y usando esos datos. El LLM confunde filas de tablas o ignora los datos | 🟡 | Si un dato en KB/resources puede ser consultado por el lead, agregar al menos un example que muestre la consulta y la respuesta correcta. Para tablas con múltiples filas (ej: horarios por país), agregar 2 examples de filas distintas para forzar al modelo a buscar en la tabla en vez de memorizar un valor. Detectado: titto.iyb, agente respondió "10 AM hora Bolivia" cuando KB dice 11 AM — no había example de consulta de horario |
| Em dashes (—) en examples.md | El LLM reproduce caracteres especiales que ve en examples de forma verbatim. Em dashes en diálogo del agente aparecen en mensajes reales de WhatsApp/IG donde se ven fuera de lugar | 🟡 | NUNCA usar em dashes (—) en líneas de diálogo del agente dentro de examples.md **ni en mensajes citados dentro de system.md** (los templates entre comillas que el agente envía — ej: el texto de registro de un flujo). Reemplazar por guion simple (-), coma, puntos suspensivos o punto y coma. En system.md headers/instrucciones que el agente NO emite es aceptable. En knowledge_base.md, objections.md y personal_story.md evaluar caso a caso — si el texto puede ser citado verbatim por el agente, usar guion simple. Detectado: bienestar.rl SDK migration (lint L004, 16 em dashes); ideal-hectorino (em dash dentro del template de registro en system.md, texto que el agente manda) |
| Tool inexistente listada en `# Herramientas` | system.md lista una tool que el runtime no provee (clásico: `analyze_image`). El modelo intenta llamarla y falla, o narra el error al lead. La lectura de imágenes/capturas es nativa multimodal — no hay tool de visión | 🔴 | Solo listar tools que estén en el tool config real del agente (`send_resource`, `list_sendable_resources`, + las configuradas). Auditar `# Herramientas` contra la config antes de deployar; sacar cualquier tool que no exista. Ver PAT-072. Detectado: ideal-hectorino + zaliwealthgroup (`analyze_image` copiado entre agentes, nunca existió) |
| Fabricated product claims en prompts generados | /cortex-create o iteración genera claims sobre el producto que suenan razonables pero no están en los docs del cliente: escasez ("cupos limitados"), social proof ("muchos llevan..."), comparaciones de tamaño de grupo, testimonios inventados. El LLM llena gaps de info con patrones de venta genéricos | 🔴 | Después de crear o iterar un agente, auditar TODOS los SDK files buscando: (1) **Escasez fabricada**: "limitados", "cupos", "últimos lugares", "se acaban" — solo incluir si el cliente lo confirmó explícitamente. (2) **Social proof sin fuente**: "muchos", "otros clientes", "la mayoría" — si no hay cohortes anteriores o testimonios reales, reformular en segunda persona ("puedes llevar..."). (3) **Comparaciones no verificadas**: "grupo más reducido", "más exclusivo", "más personalizado" — solo si hay dato concreto del formato. (4) **Calificativos de calidad inventados**: "el mejor", "único", "garantizado" — el agente no opina sobre calidad, describe el producto. Detectado: titto.iyb.vip, 3 claims fabricados durante /cortex-create (cupos limitados, grupo reducido, social proof histórico) |
| PROHIBIDO en notes/instrucciones sin example positivo | `notes` en keywords.json o instrucciones SDK con "PROHIBIDO X" / "NUNCA usar Y" sin un example mostrando el comportamiento correcto. La negación solo achica el espacio de soluciones — el modelo no infiere el "qué hacer" desde la negación, especialmente cuando la frase prohibida es muy parecida a aperturas válidas (e.g. PROHIBIDO `'Hola, soy Valen. Contame un poco...'` mientras los examples genuinos abren con `'hola como estas? soy valen'`). | 🟡 | Reemplazar la negación con (1) instrucción positiva concreta y (2) un example anclando el patrón deseado. Si la negación es muy específica, suele ser señal de que falta cobertura de example, no de que falte una regla. Validado: bienestar.rl v13 (3 keywords con `PROHIBIDO apertura fija` removidos; Example 15 "Bienestar — opener tipico" agregado; sintéticas locales 10/10 PASS confirmaron que el opener nuevo se generaliza). |

---

## Fuera de scope — Coordinación agente↔plataforma

> Los siguientes problemas son reales pero **no son anti-patrones del prompt** — son gaps de configuración entre componentes de la plataforma (evaluador, follow-ups, custom properties, workflows). Se documentan acá como referencia para un futuro checklist de plataforma.

| Problema | Síntoma | Dónde se resuelve |
|---|---|---|
| Follow-up arranca desde cero | No hay custom property que capture el estado de la conversación | Evaluador — configurar qué propiedades setea al cerrar |
| CTA duplicada entre agente y follow-up | Ambos mandan el mismo link o proponen distintas acciones | Coordinación de prompts — el follow-up asume el estado que dejó el agente |
| Persona/tono inconsistente entre agente y follow-up | El agente es cálido, el follow-up suena genérico | Prompt del follow-up — revisarlo por separado |
| Agente hace seguimiento manual | No hay workflow configurado, el prompt intenta hacer el scheduling | Workflow de plataforma — configurar follow-up con condiciones |
| Agente sin infraestructura de activación | Prompt deployado pero CERO contact limits, follow-ups con `propertyConditions` vacíos (disparan a no-leads), y custom properties ad-hoc que no gatean nada. Síntoma: doble-mensajeo post-booking, follow-ups a marcas/personales, propiedad "terminal" que no frena nada. Patrón recurrente en agentes construidos por MCP/agencias. | Es **step 8 de `cortex-create`** (no opcional): definir custom properties (reusar `ninjo_*`, no duplicar), follow-ups gateados por propiedad, y al menos el contact limit terminal (`call_booked = true → stop`). Detectado: tati.goldman (2026-06-23) — 0 contact limits, follow-ups sin condiciones, `agendo_llamada` duplicaba `call_booked` |
| Custom property duplicada de una `ninjo_*` auto-generada | El cliente crea a mano una propiedad (ej. `agendo_llamada`) que ya existe como auto-generada (`call_booked`). Ambas se setean por separado y **divergen en producción** (ej. 3 vs 2). Ninguna gatea nada si no hay contact limit/workflow apuntándola. | Reusar la `ninjo_*` existente; borrar la duplicada. Las terminales/gating siempre booleanas. Detectado: tati.goldman (`agendo_llamada` vs `call_booked`) |
