Kaan Gümele

yazılar

İlk Text-to-SQL Asistanı — ve İlk Halüsinasyonu

Text-to-SQL · LLM · SQL · Halüsinasyon
Sunum slaytları →

Seri: Doğal Dille Veritabanına Soru Sormak — Sıfırdan Text-to-SQL Asistanı. Önceki yazı: Token ve Embedding

Eğitim slaytlarıDers 2 — İlk SQL

İlk yazıda modelin metni nasıl sayıya çevirdiğini gördük. Şimdi işin eğlenceli kısmına geçiyoruz: bir dil modeline veritabanımızın yapısını anlatıp, doğal dilden SQL ürettireceğiz. “En çok satan 3 ürünü göster” yaz, model sana SELECT ... ORDER BY ... LIMIT 3 versin.

Ama bu yazının asıl dersi ikinci yarıda: model bazen kendinden emin bir şekilde yanlış üretir. Buna halüsinasyon diyoruz ve neden olduğunu anlamak, güvenilir bir asistan kurmanın ilk şartı.

Şemayı Modele Anlatmak

Model bizim veritabanımızı bilmiyor — hangi tablolar var, hangi kolonlar var, hiçbir fikri yok. O yüzden ona şemayı (tabloların ve kolonların tanımını) metin olarak veriyoruz. Buna “context” (bağlam) diyoruz.

Örnek veritabanımız küçük ve deterministik: bir e-ticaret senaryosu — customers, products, orders, order_items tabloları.

SCHEMA = """
Tablolar:
- customers(id, name, city, registered_at)
- products(id, name, category, price)
- orders(id, customer_id, order_date)
- order_items(id, order_id, product_id, quantity)
"""

Modele verdiğimiz talimat (prompt) basit: “Sen bir SQL uzmanısın. Aşağıdaki şemaya göre kullanıcının sorusunu sadece DuckDB SQL sorgusuna çevir, açıklama yazma.”

from openai import OpenAI

client = OpenAI(api_key=API_KEY, base_url="https://api.deepseek.com")

def generate_sql(question, schema=SCHEMA):
    prompt = f"""Sen bir SQL uzmanısın. Şu şemaya göre soruyu SQL'e çevir.
Sadece SQL yaz, açıklama ekleme.

ŞEMA:
{schema}

SORU: {question}
SQL:"""
    resp = client.chat.completions.create(
        model="deepseek-chat",
        messages=[{"role": "user", "content": prompt}],
        temperature=0.0,   # kararlı, aynı soruya aynı cevap
    )
    return clean_sql(resp.choices[0].message.content)

temperature=0.0 önemli: modeli olabildiğince kararlı yapıyor, yani aynı soruya hep aynı SQL’i üretiyor. Yaratıcılık istemiyoruz; doğruluk istiyoruz.

Çalıştırıp DuckDB’de deneyelim:

import duckdb
db = duckdb.connect("store.duckdb", read_only=True)

sql = generate_sql("En çok satan 3 ürünü toplam adetleriyle göster.")
print(sql)
print(db.sql(sql).df())

Sonuç:

   name              toplam_adet
   Kablosuz Kulaklık     10
   Kahve Makinesi         7
   Su Şişesi              5

İşte bu — hiç SQL yazmadan, sadece Türkçe soruyla veritabanından cevap aldık. Birkaç soru daha:

  • “İstanbul’da kaç müşteri var?”4
  • “Her şehirde kaç müşteri var?” → şehir şehir dağılım
  • “En pahalı 3 ürün hangisi?” → doğru sıralama

Model şaşırtıcı derecede iyi. Peki nerede tökezliyor?

Ve İlk Halüsinasyon

Şimdi modele, şemada olmayan bir şey soralım:

sql = generate_sql("Ürünlerin tedarikçi firmasını ve iletişim bilgisini göster.")
print(sql)

Model gayet emin bir şekilde şunu üretir:

SELECT p.name, s.company_name, s.contact_info
FROM products p
JOIN suppliers s ON p.supplier_id = s.id;

Sorun şu: suppliers diye bir tablo yok. Şemamızda tedarikçi bilgisi hiç geçmiyor. Model, “tedarikçi firması” ifadesini duyunca “eh, o zaman bir suppliers tablosu vardır herhalde” diye uydurdu. Çalıştırınca DuckDB haklı olarak isyan ediyor:

Catalog Error: Table with name suppliers does not exist!

İşte buna halüsinasyon diyoruz: modelin, elinde olmayan bilgiyi varmış gibi, gayet kendinden emin üretmesi.

Neden Halüsinasyon Olur?

Dil modeli özünde bir “sıradaki en olası kelime” tahmin makinesidir. Eğitim verisinde binlerce e-ticaret şeması görmüştür ve o şemalarda “products” ile “suppliers” sık sık yan yana geçer. Bu yüzden model için suppliers tablosu istatistiksel olarak çok makul bir tahmindir — bizim veritabanımızda öyle bir tablo olup olmadığını kontrol edemez, çünkü sadece metin üretir, veritabanına bakmaz.

Bir detay daha: model çoğu zaman eksik bir kolonu sessizce görmezden gelir (örneğin “doğum tarihi” sorunca o kolonu atlar), ama eksik bir tabloyu cesurca uydurur. Halüsinasyonu güvenilir şekilde görmek istiyorsak, ona olmayan bir tablo sordurmak en net yol.

Bundan Nasıl Kurtulacağız?

Bu, kaçınılması gereken bir “hata” değil — dil modelinin doğasında olan bir durum. Çözüm modeli suçlamak değil, ona doğru bilgiyi doğru anda vermek ve ürettiğini kontrol etmek. Önümüzdeki iki yazının konusu tam olarak bu:

  • Ders 3 (RAG): Şemayı olduğu gibi tıkıştırmak yerine, soruyla ilgili tabloları akıllıca seçip vereceğiz. Doğru bağlam, daha az halüsinasyon.
  • Ders 4 (Agent): Modele SQL’i çalıştırma yeteneği vereceğiz; hata alırsa (mesela “suppliers yok” gibi) hatayı okuyup kendini düzeltecek.

Şimdilik akılda kalması gereken tek cümle: model metin üretir, gerçeği bilmez. Güven ama doğrula.


Seri:

  1. Token ve Embedding
  2. İlk SQL ve Halüsinasyon (bu yazı)
  3. RAG Aslında Ne? Doğru Tabloyu Bulmak
  4. Agent: Tool Calling ve Self-Correction
  5. Güvenlik, Multi-Agent ve Eval
  6. Deploy: API, Docker ve AWS