Slik fungerer NL-to-SQL-agenter
Skjemainjisering, spørringsgenerering, kjøring og formatering av resultater.
Slik fungerer NL-to-SQL-agenter er en gratis leksjon i AI-agenter på CoddyKit. Dette er leksjon 1 av 4. Du kan lese hele leksjonen gratis nedenfor – og deretter øve praktisk i nettleseren med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Den er en del av læringsløpet i AI-agenter, og fremdriften din synkroniseres mellom nettet og CoddyKit-appen. Kurset i AI-agenter inneholder totalt 4 leksjoner.
Hva er en NL-to-SQL-agent
En agent for naturlig språk til SQL oversetter spørsmål på vanlig norsk til SQL-spørringer, kjører dem mot en database og returnerer svar som er lette å forstå.
I stedet for å skrive SELECT COUNT(*) FROM orders WHERE status='pending' kan brukere ganske enkelt spørre: "Hvor mange ventende bestillinger har vi?"
Kjernearkitekturen
Alle NL-to-SQL-agenter følger den samme prosessen:
- Skjemainjisering — sett inn databasestrukturen i prompten
- LLM genererer SQL — modellen produserer en spørring
- Kjør — kjør spørringen mot databasen
- Formater resultater — gjør rader om til lesbar tekst
- Returner svar — svar brukeren
# High-level pipeline
def nl_to_sql_agent(user_question, db_connection):
schema = get_schema(db_connection)
sql = llm_generate_sql(user_question, schema)
rows = execute_query(db_connection, sql)
answer = format_results(rows, user_question)
return answerSkjemainjisering forklart
LLM-en kjenner ikke strukturen i databasen din. Du må sette inn skjemaet i hver prompt, slik at modellen vet hvilke tabeller og kolonner som finnes.
En kortfattet skjemabeskrivelse forteller modellen: "Tabellen orders har kolonnene: id, user_id, status, total, created_at."
def build_schema_prompt(schema_info):
lines = []
for table in schema_info:
cols = ', '.join(
f"{c['name']} ({c['type']})"
for c in table['columns']
)
lines.append(f"Table {table['name']}: {cols}")
return '\n'.join(lines)
# Output:
# Table users: id (INT), email (VARCHAR), created_at (TIMESTAMP)
# Table orders: id (INT), user_id (INT), status (VARCHAR), total (FLOAT)
if __name__ == '__main__':
demo_schema = [
{'name': 'users', 'columns': [{'name': 'id', 'type': 'INT'}, {'name': 'email', 'type': 'VARCHAR'}]},
{'name': 'orders', 'columns': [{'name': 'id', 'type': 'INT'}, {'name': 'user_id', 'type': 'INT'}]},
]
print(build_schema_prompt(demo_schema))
Prompt for SQL-generering med LLM
Prompten må gi LLM-en tre ting: skjemaet, spørsmålet og tydelige instruksjoner om å returnere bare gyldig SQL.
Det er avgjørende for sikkerhet og korrekthet å presisere bare SELECT og hvilken SQL-dialekt som skal brukes (PostgreSQL, MySQL, SQLite).
SYSTEM_PROMPT = '''You are a SQL expert. Given a database schema and a question,
generate a valid {dialect} SELECT query. Return ONLY the SQL query, no explanation.
Do not use INSERT, UPDATE, DELETE, or DROP.
Schema:
{schema}
'''
def llm_generate_sql(question, schema, dialect='PostgreSQL'):
prompt = SYSTEM_PROMPT.format(schema=schema, dialect=dialect)
response = client.chat.completions.create(
model='gpt-4o',
messages=[
{'role': 'system', 'content': prompt},
{'role': 'user', 'content': question}
]
)
return response.choices[0].message.content.strip()Kjøre den genererte SQL-en
Etter at LLM-en returnerer SQL, kjører du den mot den faktiske databasen. Bruk parametriserte spørringer når det er mulig, og fang alltid opp unntak — LLM-en kan generere ugyldig SQL.
Ved å pakke kjøringen inn i try/except kan du prøve på nytt med et feiltips sendt tilbake til LLM-en.
import psycopg2
def execute_query(conn, sql):
try:
with conn.cursor() as cur:
cur.execute(sql)
columns = [desc[0] for desc in cur.description]
rows = cur.fetchmany(100) # limit rows
return {'columns': columns, 'rows': rows}
except psycopg2.Error as e:
return {'error': str(e), 'sql': sql}Formatere resultater for brukeren
Rå databaserader er ikke brukervennlige. Agenten må gjøre dem om til et svar på naturlig språk.
For små resultatsett kan du sende radene tilbake til LLM-en for tolkning. For store resultatsett bør du først beregne sammendragsstatistikk.
def format_results(result, original_question):
if 'error' in result:
return f'Query failed: {result["error"]}'
rows = result['rows']
columns = result['columns']
if not rows:
return 'No results found.'
# For simple counts/aggregates — just return the value
if len(columns) == 1 and len(rows) == 1:
return f'Result: {rows[0][0]}'
# For multi-row results — summarize
summary = f'Found {len(rows)} rows.\n'
for row in rows[:5]: # show first 5
summary += ', '.join(f'{columns[i]}: {row[i]}' for i in range(len(columns))) + '\n'
return summary
if __name__ == '__main__':
demo_result = {'rows': [[42]], 'columns': ['count']}
print(format_results(demo_result, 'How many users signed up?'))
demo_result2 = {'rows': [], 'columns': ['id']}
print(format_results(demo_result2, 'Any orders today?'))
Hvorfor NL-to-SQL er vanskelig: Tvetydighet
Tvetydighet er den største utfordringen. Tenk på spørsmålet: "Vis meg de beste kundene."
- Best etter omsetning? Etter antall bestillinger? Etter hvor nylig de handlet?
- Siste måned? Hele perioden?
- De 10 beste? De 100 beste?
Mennesker forstår kontekst; LLM-er gjør antakelser. Agenter trenger strategier for å håndtere eller avklare tvetydige spørsmål.
AMBIGUITY_PROMPT = '''If the question is ambiguous, respond with JSON:
{"needs_clarification": true, "question": "your clarifying question"}
If clear, respond with the SQL query directly.
User question: {question}
'''
def generate_or_clarify(question, schema):
response = llm_call(AMBIGUITY_PROMPT.format(
question=question, schema=schema
))
if '"needs_clarification"' in response:
import json
return json.loads(response)
return {'sql': response}Hvorfor NL-to-SQL er vanskelig: Skjemastørrelse
Virksomhetsdatabaser kan ha hundrevis av tabeller og tusenvis av kolonner. Hvis du setter inn hele skjemaet, vil det overskride kontekstvinduet til LLM-en.
Løsninger omfatter: skjemasøk (lag innbygginger av tabellbeskrivelser og hent de relevante), tabellfiltrering (spør LLM-en hvilke tabeller som trengs først) og skjemakomprimering (utelat indeks- og revisjonskolonner).
# Two-phase approach for large schemas
def get_relevant_tables(question, all_tables):
prompt = f'''Given these tables: {all_tables}
Which 3-5 tables are most relevant to answer: "{question}"?
Return a JSON list of table names only.'''
response = llm_call(prompt)
import json
return json.loads(response)
def nl_to_sql_large_db(question, conn):
all_tables = list_all_tables(conn) # just names
relevant = get_relevant_tables(question, all_tables)
schema = get_schema_for_tables(conn, relevant)
return llm_generate_sql(question, schema)Hvorfor NL-to-SQL er vanskelig: Forskjeller mellom SQL-dialekter
SQL er ikke universelt. LIMIT i PostgreSQL/MySQL blir til TOP i SQL Server. Datofunksjoner varierer mellom databaser. LLM-en må vite hvilken dialekt som skal brukes.
Ta alltid med måldialekten i systemprompten, og vurder å legge til dialektspesifikke eksempler i few-shot-prompting.
DIALECT_EXAMPLES = {
'postgresql': 'Use LIMIT for row limits. Use NOW() for current time.',
'mysql': 'Use LIMIT for row limits. Use NOW() for current time.',
'sqlite': 'Use LIMIT. Use datetime("now") for current time.',
'mssql': 'Use TOP N for row limits. Use GETDATE() for current time.',
'bigquery': 'Use LIMIT. Use CURRENT_TIMESTAMP() for current time. Use backtick for table names.'
}
def get_dialect_hint(dialect):
return DIALECT_EXAMPLES.get(dialect.lower(), '')
if __name__ == '__main__':
for dialect in ['postgresql', 'sqlite', 'mssql']:
print(f'{dialect}: {get_dialect_hint(dialect)}')
Løkke for feilgjenoppretting
Generert SQL mislykkes ofte på første forsøk. En robust agent implementerer en løkke for feilgjenoppretting: send den mislykkede SQL-en og feilmeldingen tilbake til LLM-en, og be den rette spørringen.
Begrens antall nye forsøk til 2–3 for å unngå uendelige løkker for spørringer som ikke kan rettes.
def nl_to_sql_with_retry(question, schema, conn, max_retries=3):
sql = llm_generate_sql(question, schema)
for attempt in range(max_retries):
result = execute_query(conn, sql)
if 'error' not in result:
return format_results(result, question)
# Ask LLM to fix the error
fix_prompt = f'The SQL query failed with error: {result["error"]}\n'\
f'Original SQL: {sql}\n'\
f'Please fix the SQL query.'
sql = llm_call(fix_prompt)
print(f'Retry {attempt + 1} with fixed SQL')
return 'Could not generate a valid query after retries.'Sette alt sammen
En NL-to-SQL-agent i produksjon kombinerer alle delene: skjemahenting, promptkonstruksjon, SQL-generering, validering, kjøring, feilgjenoppretting og resultatformattering.
Ved å legge til hurtigbufring av spørringer (samme spørsmål → samme SQL) reduserer du ventetid og LLM-kostnader betydelig for gjentatte spørringer.
import hashlib
query_cache = {}
def cached_nl_to_sql(question, schema_hash, conn):
cache_key = hashlib.md5((question + schema_hash).encode()).hexdigest()
if cache_key in query_cache:
print('Cache hit!')
sql = query_cache[cache_key]
else:
schema = get_schema(conn)
sql = llm_generate_sql(question, schema)
query_cache[cache_key] = sql
result = execute_query(conn, sql)
return format_results(result, question)Kunnskapssjekk
Hva er riktig rekkefølge på trinnene i prosessen til en NL-to-SQL-agent?
Oppsummering: NL-to-SQL-arkitektur
NL-to-SQL-agenter gjør spørsmål på naturlig språk om til kjørbare SQL-spørringer gjennom en strukturert prosess: sett inn skjema → generer SQL → kjør → formater → returner.
De viktigste utfordringene er tvetydige bruker spørsmål, store skjemaer som overskrider kontekstvinduer, og forskjeller mellom SQL-dialekter på tvers av databaser. Løkker for feilgjenoppretting håndterer SQL generert av LLM-en som mislykkes ved første kjøring.
Lær deg AI-agenter med en AI-veileder – gratis
Skriv og kjør ekte kode i nettleseren, få umiddelbar hjelp fra en AI-veileder som er tilgjengelig døgnet rundt, og fortsett der du slapp – på nettet eller i appen.
- Kurs
- 60
- Leksjoner
- 239
Ofte stilte spørsmål
Er leksjonen «Slik fungerer NL-to-SQL-agenter» gratis?
Ja – hele teksten i «Slik fungerer NL-to-SQL-agenter» er gratis å lese her på nettet. For å øve interaktivt med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt, og for å låse opp resten av AI-agenter-kurset, kan du oppgradere til CoddyKit PRO. Kurset i AI-agenter inneholder totalt 4 leksjoner.
Hva lærer jeg i «Slik fungerer NL-to-SQL-agenter»?
Skjemainjisering, spørringsgenerering, kjøring og formatering av resultater. Du øver på AI-agenter med praktisk kode som du kjører direkte i nettleseren, mens en AI-veileder som er tilgjengelig døgnet rundt, svarer på spørsmålene dine mens du jobber deg gjennom leksjonen.
Trenger jeg erfaring for å begynne med AI-agenter?
Ingen tidligere erfaring er nødvendig. AI-agenter på CoddyKit er lagt opp for både nybegynnere og viderekomne, så De kan begynne her eller helt fra start og lære i Deres eget tempo. Dette er leksjon 1 av 4.
Hvor lang tid tar leksjonen «Slik fungerer NL-to-SQL-agenter»?
De fleste CoddyKit-leksjoner tar omtrent 5–10 minutter. Hver leksjon er kort og interaktiv, slik at De gjør jevne fremskritt og kan fortsette akkurat der De slapp – både på nettet og i appen.
Kan jeg skrive og kjøre kode i denne AI-agenter-leksjonen?
Ja. Alle AI-agenter-leksjoner har en innebygd kodeeditor, slik at De kan skrive og kjøre ekte kode direkte i nettleseren og få umiddelbar tilbakemelding fra AI – uten lokal konfigurering.
Alle leksjonene i dette kurset
- Slik fungerer NL-to-SQL-agenter
- Skjemaforståelse og injisering
- Generere og validere SQL-spørringer
- Håndtere tvetydige databasespørsmål