Comprensión e inyección de esquemas
Extraiga y dé formato al esquema de la base de datos para el contexto del LLM: tablas, columnas y relaciones.
Comprensión e inyección de esquemas es una lección gratuita de AI Agents en CoddyKit. Esta es la lección 2 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de AI Agents, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de AI Agents incluye 4 lecciones en total.
Por qué es importante el contexto del esquema
El LLM conoce la sintaxis de SQL, pero no sabe nada sobre su base de datos. Sin contexto del esquema, inventará nombres de tablas y columnas.
La inyección del esquema consiste en extraer mediante programación la estructura de su BD e incluirla en cada prompt, de modo que el LLM conozca sus tablas, columnas y tipos exactos.
Consulta de INFORMATION_SCHEMA
Las principales bases de datos relacionales exponen metadatos mediante INFORMATION_SCHEMA. Puede consultarlo para obtener todas las tablas, los nombres de las columnas y los tipos de datos sin tocar el código de la aplicación.
Esto funciona en PostgreSQL, MySQL, SQL Server y SQLite, con pequeñas diferencias.
import psycopg2
def get_schema(conn):
query = '''
SELECT table_name, column_name, data_type
FROM information_schema.columns
WHERE table_schema = 'public'
ORDER BY table_name, ordinal_position
'''
with conn.cursor() as cur:
cur.execute(query)
return cur.fetchall()Agrupación de columnas por tabla
El resultado sin procesar de INFORMATION_SCHEMA es una lista plana de filas. Agrúpelas por nombre de tabla para crear una representación estructurada que sea más fácil de incluir en un prompt.
from collections import defaultdict
def build_schema_dict(conn):
rows = get_schema(conn)
schema = defaultdict(list)
for table_name, column_name, data_type in rows:
schema[table_name].append({
'name': column_name,
'type': data_type
})
return dict(schema)
# Result:
# {
# 'users': [{'name': 'id', 'type': 'integer'}, {'name': 'email', 'type': 'character varying'}],
# 'orders': [{'name': 'id', 'type': 'integer'}, {'name': 'user_id', 'type': 'integer'}]
# }Formateo del esquema para prompts del LLM
El LLM lee el esquema como texto sin formato. Use un formato conciso y legible: una tabla por línea, con los nombres de las columnas y sus tipos entre paréntesis.
Incluir las claves principales (PK) y las claves externas (FK) ayuda al LLM a escribir instrucciones JOIN correctas.
def format_schema_for_prompt(schema_dict, pk_info=None, fk_info=None):
lines = []
for table, columns in schema_dict.items():
col_parts = []
for col in columns:
label = col['name']
if pk_info and (table, col['name']) in pk_info:
label += ' PK'
if fk_info and (table, col['name']) in fk_info:
label += f' FK->{fk_info[(table, col["name"])]}'
col_parts.append(f"{label} ({col['type']})")
lines.append(f"Table {table}: {', '.join(col_parts)}")
return '\n'.join(lines)
# Output:
# Table users: id PK (integer), email (varchar), created_at (timestamp)
# Table orders: id PK (integer), user_id FK->users.id (integer), total (float)
if __name__ == '__main__':
demo_schema = {'users': [{'name': 'id', 'type': 'integer'}, {'name': 'email', 'type': 'varchar'}]}
demo_pk = {('users', 'id')}
print(format_schema_for_prompt(demo_schema, pk_info=demo_pk))
Inclusión de claves principales y externas
Las relaciones entre claves externas son la parte más importante del contexto del esquema, ya que indican al LLM cómo escribir los JOIN. Consulte information_schema.table_constraints y key_column_usage para extraerlas.
def get_foreign_keys(conn):
query = '''
SELECT
kcu.table_name,
kcu.column_name,
ccu.table_name AS foreign_table,
ccu.column_name AS foreign_column
FROM information_schema.table_constraints AS tc
JOIN information_schema.key_column_usage AS kcu
ON tc.constraint_name = kcu.constraint_name
JOIN information_schema.constraint_column_usage AS ccu
ON ccu.constraint_name = tc.constraint_name
WHERE tc.constraint_type = 'FOREIGN KEY'
'''
with conn.cursor() as cur:
cur.execute(query)
return {
(row[0], row[1]): f'{row[2]}.{row[3]}'
for row in cur.fetchall()
}
if __name__ == '__main__':
class FakeCursor:
def __enter__(self): return self
def __exit__(self, *a): return False
def execute(self, query): pass
def fetchall(self):
return [('orders', 'user_id', 'users', 'id')]
class FakeConn:
def cursor(self): return FakeCursor()
fks = get_foreign_keys(FakeConn())
print('Foreign keys found:')
for (table, col), ref in fks.items():
print(f' {table}.{col} -> {ref}')
Compresión del esquema: el problema
Una base de datos empresarial real puede tener más de 200 tablas. Si inyecta el esquema completo, superará la ventana de contexto de GPT-4 y gastará dinero innecesariamente en tokens.
Un esquema de 200 tablas con 20 columnas cada una ocupa aproximadamente 40.000+ tokens, un coste demasiado alto para enviarlo en cada consulta.
def estimate_schema_tokens(schema_dict):
text = format_schema_for_prompt(schema_dict)
# Rough estimate: 1 token per 4 characters
estimated_tokens = len(text) // 4
print(f'Tables: {len(schema_dict)}')
print(f'Estimated schema tokens: {estimated_tokens}')
return estimated_tokens
# 200 tables * 15 columns * 25 chars/col = 75,000 chars = ~18,750 tokens
# Plus user question + system prompt = easily over context limitCompresión del esquema: inyección selectiva
La estrategia de compresión más eficaz consiste en inyectar únicamente las tablas relevantes para la pregunta. Use un enfoque en dos fases: primero pregunte al LLM qué tablas necesita y, después, inyecte únicamente los esquemas de esas tablas.
def select_relevant_tables(question, all_table_names, n=5):
table_list = ', '.join(all_table_names)
prompt = f'''Database tables: {table_list}
Question: {question}
List the {n} most relevant table names as a JSON array.
Example: ["users", "orders", "products"]'''
response = llm_call(prompt)
import json
return json.loads(response)
def compressed_schema(question, conn):
all_tables = list(build_schema_dict(conn).keys())
relevant = select_relevant_tables(question, all_tables)
full_schema = build_schema_dict(conn)
return {t: full_schema[t] for t in relevant if t in full_schema}Compresión del esquema: exclusión de columnas irrelevantes
Muchas tablas tienen columnas de auditoría como created_at, updated_at, deleted_at, version y created_by, que rara vez son relevantes para las consultas de negocio. Elimínelas para reducir el número de tokens.
AUDIT_COLUMNS = {
'created_at', 'updated_at', 'deleted_at', 'created_by',
'updated_by', 'version', 'is_deleted', 'modified_at'
}
def compress_schema(schema_dict, exclude_audit=True):
compressed = {}
for table, columns in schema_dict.items():
# Skip internal/system tables
if table.startswith('_') or table.startswith('pg_'):
continue
if exclude_audit:
columns = [c for c in columns if c['name'] not in AUDIT_COLUMNS]
if columns: # only include if columns remain
compressed[table] = columns
return compressed
if __name__ == '__main__':
demo_schema = {
'users': [{'name': 'id', 'type': 'INT'}, {'name': 'email', 'type': 'VARCHAR'}, {'name': 'created_at', 'type': 'TIMESTAMP'}],
'pg_stat': [{'name': 'x', 'type': 'INT'}],
}
compressed = compress_schema(demo_schema)
print('Tables kept:', list(compressed.keys()))
print('users columns after compression:', [c['name'] for c in compressed['users']])
Adición de descripciones de tablas
Los nombres de las columnas no siempre se explican por sí solos. Añadir descripciones en lenguaje natural de lo que representa cada tabla mejora considerablemente la calidad de la generación de SQL.
Almacene las descripciones en un archivo de configuración o como comentarios de tablas de PostgreSQL.
TABLE_DESCRIPTIONS = {
'users': 'Registered app users with authentication info',
'orders': 'Customer purchase orders',
'order_items': 'Individual line items within an order',
'products': 'Product catalog with pricing',
'payments': 'Payment transactions linked to orders'
}
def format_schema_with_descriptions(schema_dict):
lines = []
for table, columns in schema_dict.items():
desc = TABLE_DESCRIPTIONS.get(table, '')
col_str = ', '.join(f"{c['name']} ({c['type']})" for c in columns)
if desc:
lines.append(f"Table {table} ({desc}): {col_str}")
else:
lines.append(f"Table {table}: {col_str}")
return '\n'.join(lines)
if __name__ == '__main__':
demo_schema = {'users': [{'name': 'id', 'type': 'INT'}], 'orders': [{'name': 'id', 'type': 'INT'}]}
print(format_schema_with_descriptions(demo_schema))
Almacenamiento en caché del esquema
Los esquemas de bases de datos rara vez cambian. Consultar INFORMATION_SCHEMA en cada consulta añade latencia y carga. Almacene en caché la cadena de texto con formato del esquema e invalídela cuando se produzcan eventos de cambio del esquema o cuando venza un TTL basado en tiempo.
import time
class SchemaCache:
def __init__(self, ttl_seconds=300):
self._cache = None
self._timestamp = 0
self.ttl = ttl_seconds
def get(self, conn):
now = time.time()
if self._cache is None or (now - self._timestamp) > self.ttl:
print('Refreshing schema cache...')
schema_dict = build_schema_dict(conn)
fk_info = get_foreign_keys(conn)
self._cache = format_schema_for_prompt(schema_dict, fk_info=fk_info)
self._timestamp = now
return self._cache
schema_cache = SchemaCache(ttl_seconds=300)Flujo completo de inyección del esquema
Combine todas las técnicas: almacene en caché el esquema comprimido, inyéctelo en el prompt del sistema y use un filtrado selectivo de tablas para bases de datos grandes.
def build_sql_agent_prompt(question, conn, large_db=False):
if large_db:
schema = compressed_schema(question, conn)
schema_text = format_schema_with_descriptions(schema)
else:
schema_text = schema_cache.get(conn)
system = f'''You are a PostgreSQL expert.
Return ONLY a valid SELECT query based on this schema:
{schema_text}
Rules:
- Use only SELECT statements
- Use table aliases for clarity
- Limit results to 100 rows unless asked for all
'''
return systemComprobación de conocimientos
¿Cuándo debe usar la inyección selectiva de tablas en lugar de inyectar el esquema completo?
Resumen: comprensión e inyección del esquema
Una inyección eficaz del esquema es la base de unos agentes NL-to-SQL fiables. Extraiga la estructura de INFORMATION_SCHEMA, incluya las relaciones entre claves primarias y foráneas, y dele formato como texto compacto para el LLM.
Para bases de datos grandes: almacene el esquema en caché, elimine las columnas de auditoría y use la inyección selectiva para enviar únicamente las tablas relevantes para cada pregunta. Las descripciones de las tablas en lenguaje natural mejoran aún más la calidad de las consultas.
Preguntas frecuentes
¿La lección «Comprensión e inyección de esquemas» es gratis?
Sí — el texto completo de «Comprensión e inyección de esquemas» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de AI Agents, actualiza a CoddyKit PRO. El curso de AI Agents incluye 4 lecciones en total.
¿Qué aprenderé en «Comprensión e inyección de esquemas»?
Extraiga y dé formato al esquema de la base de datos para el contexto del LLM: tablas, columnas y relaciones. Practicas AI Agents con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.
¿Necesito experiencia previa para empezar AI Agents?
No se requiere experiencia previa. AI Agents en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 2 de 4.
¿Cuánto tiempo toma la lección «Comprensión e inyección de esquemas»?
La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.
¿Puedo escribir y ejecutar código en esta lección de AI Agents?
Sí. Cada lección de AI Agents incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.
Todas las lecciones de este curso
- Cómo funcionan los agentes NL-to-SQL
- Comprensión e inyección de esquemas
- Generación y validación de consultas SQL
- Gestión de preguntas ambiguas sobre bases de datos