Comprendre et injecter un schéma
Extrayez et mettez en forme le schéma de DB pour le contexte du LLM : tables, colonnes et relations.
Comprendre et injecter un schéma est une leçon AI Agents gratuite sur CoddyKit. Ceci est la leçon 2 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage AI Agents, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours AI Agents comprend 4 leçons au total.
Pourquoi le contexte du schéma est important
Le LLM connaît la syntaxe SQL, mais ne sait rien de votre base de données. Sans contexte de schéma, il inventera des noms de tables et de colonnes.
L’injection du schéma consiste à extraire automatiquement la structure de votre base de données et à l’inclure dans chaque consigne, afin de permettre au LLM de connaître précisément vos tables, vos colonnes et leurs types.
Interroger INFORMATION_SCHEMA
Toutes les grandes bases de données relationnelles exposent leurs métadonnées par l’intermédiaire de INFORMATION_SCHEMA. Vous pouvez l’interroger pour obtenir chaque table, chaque nom de colonne et chaque type de données sans toucher au code de l’application.
Cette méthode fonctionne avec PostgreSQL, MySQL, SQL Server et SQLite, avec quelques différences mineures.
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()Regrouper les colonnes par table
Le résultat brut d’INFORMATION_SCHEMA est une liste plate de lignes. Regroupez-les par nom de table pour construire une représentation structurée, plus facile à mettre en forme dans une consigne.
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'}]
# }Mettre le schéma en forme pour les consignes du LLM
Le LLM lit le schéma sous forme de texte brut. Utilisez un format concis et lisible : une table par ligne, avec les noms et les types des colonnes entre parenthèses.
Inclure les clés primaires (PK) et les clés étrangères (FK) aide le LLM à rédiger des instructions JOIN correctes.
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))
Inclure les clés primaires et étrangères
Les relations entre clés étrangères constituent la partie la plus importante du contexte du schéma : elles indiquent au LLM comment rédiger des JOIN. Interrogez information_schema.table_constraints et key_column_usage pour les extraire.
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}')
Compression du schéma : le problème
Une base de données d’entreprise réelle peut contenir plus de 200 tables. Si vous injectez le schéma complet, vous dépasserez la fenêtre de contexte de GPT-4 et dépenserez inutilement de l’argent en jetons.
Un schéma de 200 tables comportant chacune 20 colonnes représente environ 40 000 jetons ou plus : il est trop coûteux de l’envoyer pour chaque requête.
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 limitCompression du schéma : injection sélective
La stratégie de compression la plus efficace consiste à n’injecter que les tables pertinentes pour la question. Utilisez une approche en deux phases : demandez d’abord au LLM de quelles tables il a besoin, puis injectez uniquement les schémas correspondants.
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}Compression du schéma : exclure les colonnes parasites
De nombreuses tables possèdent des colonnes d’audit telles que created_at, updated_at, deleted_at, version et created_by, qui sont rarement pertinentes pour les requêtes métier. Supprimez-les afin de réduire le nombre de jetons.
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']])
Ajouter des descriptions de tables
Les noms de colonnes ne sont pas toujours explicites. Ajouter des descriptions en langage naturel de ce que représente chaque table améliore considérablement la qualité de la génération SQL.
Stockez les descriptions dans un fichier de configuration ou sous forme de commentaires de tables 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))
Mise en cache du schéma
Les schémas de base de données changent rarement. Interroger INFORMATION_SCHEMA à chaque requête ajoute de la latence et de la charge. Mettez en cache la chaîne de caractères du schéma mise en forme et invalidez-la lors des événements de modification du schéma ou selon une durée TTL.
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)Flux complet d'injection du schéma
En combinant toutes les techniques : mettez en cache le schéma compressé, injectez-le dans l'invite système et utilisez un filtrage sélectif des tables pour les grandes bases de données.
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 systemVérification des connaissances
Quand devez-vous utiliser l'injection sélective des tables plutôt que d'injecter le schéma complet ?
Récapitulatif : compréhension et injection du schéma
Une injection efficace du schéma est la base d'agents NL vers SQL fiables. Extrayez la structure de INFORMATION_SCHEMA, incluez les relations entre clés primaires et étrangères, puis mettez-la en forme sous forme de texte compact pour le LLM.
Pour les grandes bases de données : mettez le schéma en cache, supprimez les colonnes d'audit et utilisez l'injection sélective pour n'envoyer que les tables pertinentes pour chaque question. Les descriptions des tables en langage naturel améliorent encore la qualité des requêtes.
Questions Fréquemment Posées
La leçon « Comprendre et injecter un schéma » est-elle gratuite ?
Oui — le texte complet de « Comprendre et injecter un schéma » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours AI Agents, passe à CoddyKit PRO. Le cours AI Agents comprend 4 leçons au total.
Qu'est-ce que j'apprendrai dans « Comprendre et injecter un schéma » ?
Extrayez et mettez en forme le schéma de DB pour le contexte du LLM : tables, colonnes et relations. Tu pratiques AI Agents avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.
Dois-je avoir de l'expérience pour commencer AI Agents ?
Aucune expérience préalable n'est requise. AI Agents sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 2 sur 4.
Combien de temps prend la leçon « Comprendre et injecter un schéma » ?
La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.
Peux-tu écrire et exécuter du code dans cette leçon AI Agents ?
Oui. Chaque leçon AI Agents inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.
Toutes les leçons de ce cours
- Fonctionnement des agents NL-to-SQL
- Comprendre et injecter un schéma
- Générer et valider des requêtes SQL
- Gérer les questions ambiguës sur les bases de données