Saltar al contenido principal

Tunear prompts para tu dominio

GRAIL funciona bien out-of-the-box porque sus prompts son generalistas: cubren texto narrativo, papers científicos, código, contratos legales — todo razonablemente. Pero la diferencia entre razonable y excelente vive en los prompts.

Si te tomas una tarde para tunear los dos o tres prompts críticos para tu dominio, vas a ver mejoras compuestas en cada capa.

Por qué importa (las ganancias se componen)​

Los prompts afectan cuatro capas en cascada:

prompts de extracción → mejor grafo
↓
prompts de reportes → mejores reportes de comunidad
↓
prompts de búsqueda → mejor contexto al LLM
↓
prompts de síntesis → mejores respuestas finales

Una mejora del 20% en extracción no produce un 20% mejor en respuestas — produce algo más cercano al 60%, porque cada capa amplifica la anterior.

Lo que puedes tunear​

GRAIL trae 10 prompts override-ables. Agrúpalos mentalmente en tres familias:

Familia 1 — Construcción del grafo (impacto altísimo)​

PromptQué haceCuándo conviene tunear
entity_relationExtracción en una pasada — entidades, relaciones, descripciones, queries anticipadasCasi siempre si tu corpus no es genérico
community_reportResumen narrativo por comunidad — base del modo globalCuando los reportes globales se sienten vagos
summarize_descriptionConsolida descripciones cuando una entidad aparece muchas vecesCuando ves descripciones contradictorias
entity_dedupDetecta entidades duplicadas para mergearSi tu corpus tiene mucho ruido en nombres
create_custom_entitiesDescubre tipos de entidad nuevos desde una muestraCuando empiezas en un dominio nuevo

Familia 2 — Síntesis de respuestas​

PromptQué haceCuándo conviene tunear
local_searchEnsambla el contexto local y respondePara fijar voz, estructura, citas obligatorias
global_mapScoring de relevancia de reportes de comunidadAvanzado, raramente necesario
global_reduceSíntesis final del modo globalPara controlar formato del resumen temático

Familia 3 — Apoyo (raramente tunear)​

PromptQué haceCuándo conviene tunear
json_correctionRepara JSON malformado (fallback)Casi nunca
claim_extractionExtracción de claims/covariates (opcional)Solo si activaste covariates

Cómo funciona (1 minuto de arquitectura)​

Cada prompt en GRAIL es un módulo Python con tres exports:

NAME = "entity_relation" # nombre lógico (debe matchear filename)
REQUIRED_PARAMS = ["entity_types", "input_text"] # validados antes de llamar

def build_messages(**params) -> list[dict]:
return [
{"role": "system", "content": "..."},
{"role": "user", "content": "..."},
]

El PromptRegistry resuelve por nombre, en este orden:

custom_paths (en el orden que los listas) → builtin → KeyError

Si tu directorio custom no tiene entity_relation.py, se usa el builtin. Si lo tiene, gana el tuyo.

Setup en 3 minutos​

1. Crea el directorio de prompts​

mkdir -p mi-proyecto/my_prompts

2. Copia el builtin que quieres tunear​

cp grail/prompts/builtin/entity_relation.py mi-proyecto/my_prompts/entity_relation.py

(O ve el código fuente del builtin con grail prompt show entity_relation.)

3. Apúntalo en grail.yaml​

prompts:
custom_paths:
- ./my_prompts
strict: false # true = exige que provean LOS 10 prompts

4. Edita el prompt y re-indexa​

grail index ./mi-proyecto

Listo. GRAIL ahora usa tu versión para entity_relation y los builtins para el resto.

Estrategias de tuneo (las que más pagan)​

Estrategia A — Cambia los ejemplos del prompt​

El builtin de entity_relation trae tres ejemplos: ficción narrativa, paper científico, y código. Estos ejemplos sesgan al LLM.

Si tu corpus es médico, cambia los ejemplos por consultas reales con sus extracciones esperadas. El LLM va a imitar el patrón.

EXAMPLES = """
Texto de ejemplo: "El paciente con glioblastoma multiforme grado IV recibió
temozolomida + radioterapia adyuvante según protocolo Stupp."

Salida esperada:
<extracted_data>
("entity"<|>GLIOBLASTOMA MULTIFORME<|>DISEASE<|>Tumor cerebral primario...)##
("entity"<|>TEMOZOLOMIDA<|>DRUG<|>Agente alquilante oral...)##
("entity"<|>PROTOCOLO STUPP<|>GUIDELINE<|>Régimen estándar...)##
("relationship"<|>TEMOZOLOMIDA<|>GLIOBLASTOMA MULTIFORME<|>tratamiento primario<|>9)##
</extracted_data>
"""

Estrategia B — Restringe los tipos de relación​

GRAIL puede classificar relaciones con un vocabulario controlado:

indexing:
extract_relationship_types: true
relationship_types:
- TREATS
- CONTRAINDICATES
- INTERACTS_WITH
- METABOLIZES

Esto convierte las aristas de RELATED (genérico) a relaciones tipadas, lo que hace queries estructurales mucho más útiles.

Estrategia C — Fija la voz del modo local​

Por default, local_search responde en estilo asistente neutral. Si tu producto necesita voz específica (formal-legal, técnica-médica, conversacional), edita el system prompt:

def build_messages(context_data, user_query, **kwargs):
return [
{"role": "system", "content":
"Eres un asistente legal especializado en derecho chileno. "
"Citas siempre el artículo y la ley específica. "
"No respondes con generalidades — solo con texto verificable "
"del corpus. Si no encuentras la respuesta, dilo explícitamente."
},
{"role": "user", "content":
f"Contexto:\n{context_data}\n\nPregunta: {user_query}"
},
]

Estrategia D — Pack multilingual completo​

Para una experiencia 100% en español (no solo traducir queries), crea un pack que reemplace todos los prompts con versiones en español:

prompts:
custom_paths:
- ./prompts_es
strict: true # error si falta alguno → fuerza un pack completo

Recomendamos strict: true solo cuando estás seguro de tener los 10 archivos — falla rápido en el startup en lugar de mezclar idiomas.

Workflow de desarrollo iterativo​

Tunear prompts es iterar. GRAIL trae herramientas para que el ciclo sea barato y reproducible:

1. Habilita el cache de LLM​

llm:
cache_enabled: true

Si re-corres con el mismo prompt + mismo input, no se vuelve a pagar al proveedor. Esto convierte el ciclo "ajusta → indexa → revisa" en algo gratis después de la primera corrida.

2. Indexa un sample, no el corpus completo​

mkdir mi-proyecto-sample/input
cp mi-proyecto/input/{0,1,2}*.pdf mi-proyecto-sample/input/
grail index ./mi-proyecto-sample

3 PDFs te dan suficiente feedback para iterar. Solo cuando estés contento con el prompt, indexa el corpus completo.

3. Inspecciona los prompts efectivos​

grail prompt list # lista todos los prompts registrados
grail prompt show entity_relation # imprime el prompt completo
grail prompt show entity_relation --project ./mi-proyecto # usa tu pack custom

4. Traza una consulta para ver el prompt en vivo​

grail query ./mi-proyecto-sample "..." --mode local --trace ./traces
cat ./traces/*.json | jq '.llm_calls[0].messages'

Ver Trazar consultas para el detalle.

Pitfalls comunes​

1. Cambiaste los delimitadores del entity_relation y ahora extrae 0 entidades​

El parser en grail.indexing.entities_relationships lee DEFAULT_DELIMITERS del módulo del prompt. Si cambias los delimitadores en el contenido del prompt pero no exportas DEFAULT_DELIMITERS, el parser usa los viejos y todo se rompe en silencio.

Fix: exporta los nuevos en tu módulo:

DEFAULT_DELIMITERS = {
"tuple_delimiter": "||",
"record_delimiter": "@@",
"start_delimiter": "<data>",
"completion_delimiter": "</data>",
}

2. Modelo de razonamiento (Qwen3.6 thinking, etc.) usa todo max_tokens en <think> y la respuesta sale truncada​

Sube los límites:

indexing:
extraction_max_tokens: 16384
community:
max_report_length: 16384
search:
response_max_tokens: 16384

3. strict: true y olvidaste un prompt​

GRAIL falla en el startup con un mensaje claro listando qué falta. Soluciones:

  • Lista todos los 10 en tu directorio custom, o
  • Cambia a strict: false para mergear con builtins

4. Tu prompt no se está usando​

Verifica el orden en custom_paths:

prompts:
custom_paths:
- ./prompts_v2 # tiene entity_relation.py
- ./prompts_v1 # también tiene entity_relation.py

Gana el primero en la lista. Para revertir a v1, swapea el orden.

Cuándo NO tunear prompts​

  • Tu corpus es realmente genérico (Wikipedia, libros de ficción, código mixto sin un patrón). Los builtins van a competir bien.
  • No tienes tiempo de iterar. Tunear sin iterar suele empeorar las cosas — el dominio expert correcto en tu cabeza no es lo mismo que el prompt correcto.
  • Estás validando si GRAIL te sirve. Primero corre el quickstart con builtins. Solo invierte en prompts cuando confirmes que el resto del sistema funciona.

Referencia interna​

Para la inmersión técnica completa — incluyendo el contrato exacto del parser de entity_relation, todos los parámetros de cada prompt, y workflows avanzados (ej. prompts adaptativos por tipo de chunk) — ver el doc interno docs/prompt_customization.md en el repo.

Siguiente paso​

  • Trazar consultas — verifica qué prompts vio el LLM en cada respuesta.
  • Optimizar costos — el cache de LLM hace que iterar prompts sea gratis.
  • Modelo de memoria — en modo memoria los prompts cuentan menos (el agente declara directamente), pero community_report y local_search igual aplican a las consultas.