AI-agenter · leksjon

Slik fungerer NL-to-SQL-agenter

Skjemainjisering, spørringsgenerering, kjøring og formatering av resultater.

Leksjon 1 av 413 trinn

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:

  1. Skjemainjisering — sett inn databasestrukturen i prompten
  2. LLM genererer SQL — modellen produserer en spørring
  3. Kjør — kjør spørringen mot databasen
  4. Formater resultater — gjør rader om til lesbar tekst
  5. 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 answer

Skjemainjisering 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.

Gratis å komme i gang

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

  1. Slik fungerer NL-to-SQL-agenter
  2. Skjemaforståelse og injisering
  3. Generere og validere SQL-spørringer
  4. Håndtere tvetydige databasespørsmål
← Tilbake til AI-agenter