Lompat ke konten
dokumentasi.id
Commit a3048a3
v1
Star929

11

Text-to-SQL dan Analitik Data

Tiga berkas di repository ini menjawab pertanyaan analitik atas data industri di bursa saham dengan tiga bentuk sumber data. 15_sql.py memuat industry-leaders-full.csv ke SQLite lalu membiarkan LLM menulis SQL. 16_repl.py melewati SQL dan menyerahkan sebuah DataFrame pandas agar model mengeksekusi kode Python. workshop/ai-search-engine.py membuang CSV sama sekali dan mengambil data langsung dari API Sectors. Ketiganya dirujuk di README sebagai materi kursus "Building search engines in the age of AI". README.md:57-60

Dua jalur dari satu CSV

graph LR
  Q["Pertanyaan bahasa alami"] --> C{"Butuh agregasi SQL?"}
  C -->|"ya"| A["create_sql_agent"]
  C -->|"tidak"| B["PythonAstREPLTool"]
  CSV["industry-leaders-full.csv"] --> L["pandas.read_csv"]
  L --> DB[("industry.db · tabel industry")]
  L --> DF["industry = df.loc[:, 4 kolom]"]
  DB --> A
  DF --> B
  A --> R1["Hasil query SQL"]
  B --> R2["stdout eksekusi pandas"]
  style CSV fill:#8b5cf6,color:#fff
  style L fill:#8b5cf6,color:#fff
  style DF fill:#8b5cf6,color:#fff
  style DB fill:#336791,color:#fff
  style C fill:#f59e0b,color:#000
  style A fill:#f59e0b,color:#000
  style B fill:#f59e0b,color:#000
  style R1 fill:#2d7d46,color:#fff
  style R2 fill:#2d7d46,color:#fff
%% fig: Dua jalur dari satu dataset ke jawaban
%% desc: CSV yang sama masuk ke SQLite untuk jalur SQL, atau ke DataFrame pandas untuk jalur eksekusi kode; keduanya berakhir di hasil berbentuk tabel.
Fig. 1: Dua jalur dari satu dataset ke jawabanCSV yang sama masuk ke SQLite untuk jalur SQL, atau ke DataFrame pandas untuk jalur eksekusi kode; keduanya berakhir di hasil berbentuk tabel.

15_sql.py membangun industry.db hanya kalau berkas itu belum ada: CSV dibaca, engine sqlite:///industry.db disiapkan, lalu seluruh baris ditulis ke tabel industry dengan if_exists='replace'. Kalau database sudah ada, skrip hanya membuka koneksi dan tidak menimpa isinya. 15_sql.py:17-25

⚠️ Kedua skrip membaca ./datasets/industry-leaders-full.csv, padahal repository tidak punya direktori datasets; berkas yang tersedia ada di workshop/industry-leaders-full.csv. Tanpa penyesuaian path, pd.read_csv gagal sebelum agent sempat dipanggil. 15_sql.py:19 workshop/industry-leaders-full.csv:1

Artefak industry.db juga tidak ikut ter-commit karena .gitignore memblokir *.db, jadi setiap mesin membangun ulang database dari CSV pada eksekusi pertama. .gitignore:6-6

Jalur SQL dengan create_sql_agent

SQLDatabase(engine=engine) membungkus engine tadi, dan db.get_usable_table_names() mencetak daftar tabel yang boleh dipakai agent sebagai konteks skema. 15_sql.py:27-30

Sebelum LLM dilibatkan, skrip sudah lebih dulu menjalankan SQL tulis tangan, SELECT * FROM industry WHERE total_market_cap > 1e14, lewat db.run. Baris ini berfungsi sebagai baseline: db.run menerima string SQL apa pun dan mengeksekusinya langsung ke SQLite tanpa pemeriksaan tambahan. 15_sql.py:33-34

Agent-nya dibuat dengan agent_type="tool-calling" dan verbose=True, memakai ChatGroq bermodel llama-3.3-70b-versatile. 15_sql.py:36-44 Satu pemanggilan invoke kemudian mengirim pertanyaan bisnis dan meminta hasil berupa tabel markdown. 15_sql.py:45-47

Parameter di create_sql_agentNilai pada 15_sql.py
llmChatGroq(model_name="llama-3.3-70b-versatile") 15_sql.py:36-38
dbSQLDatabase(engine=engine) atas industry.db 15_sql.py:27
agent_type"tool-calling" 15_sql.py:44
verboseTrue, sehingga langkah antara SQL yang dihasilkan ikut tercetak 15_sql.py:44
tabel targetindustry 15_sql.py:17-21

Jalur pandas dengan PythonAstREPLTool

16_repl.py tidak membuat database. CSV dibaca, empat kolom dipilih ke variabel industry, lalu tool dibuat dengan locals={"df": industry}. Perhatikan penamaannya: kunci lokalnya df, tetapi isinya DataFrame hasil seleksi kolom, bukan CSV mentah. 16_repl.py:30-34

Kolom di industry-leaders-full.csvMasuk ke tool
industryya 16_repl.py:32
sub_industryya 16_repl.py:32
total_market_capya 16_repl.py:32
num_of_companiesya 16_repl.py:32
companiestidak; berisi JSON daftar emiten workshop/industry-leaders-full.csv:1
top_mcap_gainerstidak workshop/industry-leaders-full.csv:1
top_price_changetidak workshop/industry-leaders-full.csv:1

System prompt menyisipkan industry.head().to_markdown() sehingga model melihat nama kolom dan bentuk nilai tanpa perlu menebak skema. Prompt itu juga menegaskan arti top_mcap_gainers dan meminta keluaran tabular. 16_repl.py:40-45

Berkas ini tidak memakai agent executor. Ia merangkai LCEL: prompt, llm.bind_tools(tools, tool_choice=python_repl.name), lalu JsonOutputKeyToolsParser(key_name=python_repl.name, first_tool_only=True) yang hanya mengambil panggilan tool pertama. 16_repl.py:54-59 Rantai penuh menambahkan python_repl di ujungnya sehingga kode dari model langsung dieksekusi dan stdout-nya dikembalikan sebagai jawaban. 16_repl.py:61-68

sequenceDiagram
  participant U as Pengguna
  participant P as ChatPromptTemplate
  participant M as ChatGroq
  participant J as JsonOutputKeyToolsParser
  participant T as PythonAstREPLTool
  U->>P: query_string dari input
  P->>M: system prompt + industry.head().to_markdown
  rect rgb(245,158,11)
    M->>J: tool_call bernama python_repl
  end
  rect rgb(45,125,70)
    J->>T: kode Python, first_tool_only=True
    T-->>U: stdout berisi hasil pandas
  end
  rect rgb(220,38,38)
    J--xU: rantai dipotong sebelum tool, kode tidak dieksekusi
  end
%% fig: Siklus satu pertanyaan pada rantai REPL
%% desc: Model hanya menghasilkan panggilan tool; eksekusi terjadi di PythonAstREPLTool dan rantai bisa dipotong tepat sebelum langkah itu untuk sekadar membaca kode.
Fig. 2: Siklus satu pertanyaan pada rantai REPLModel hanya menghasilkan panggilan tool; eksekusi terjadi di PythonAstREPLTool dan rantai bisa dipotong tepat sebelum langkah itu untuk sekadar membaca kode.

Komentar di berkas menyatakan cara aman mencoba prompt: buang python_repl dari ujung chain untuk melihat kode yang akan dijalankan tanpa benar-benar menjalankannya. 16_repl.py:63-66

⚠️ PythonAstREPLTool di 16_repl.py dibuat tanpa allow_dangerous_code=True, sedangkan 04_csv.py menambahkan flag itu karena LangChain 1.x mewajibkannya untuk eksekusi kode. langchain-experimental==0.4.1 yang terpasang membuat tool menolak dipakai sampai flag tersebut diberikan, dan sekali diberikan, kode dari model berjalan di proses Python Anda tanpa sandbox. 16_repl.py:34 04_csv.py:16 requirements.txt:63-64

Aspek15_sql.py16_repl.py
Sumber datatabel industry di industry.db 15_sql.py:17-21CSV langsung ke pandas 16_repl.py:30
Alatcreate_sql_agent(agent_type="tool-calling") 15_sql.py:44PythonAstREPLTool(locals=...) 16_repl.py:34
Bahasa eksekusiSQLPython dan pandas
Modelllama-3.3-70b-versatile 15_sql.py:37llama-3.3-70b-versatile 16_repl.py:37
Bentuk keluaranhasil db.run dari SQL yang dihasilkan 15_sql.py:33-34stdout hasil print 16_repl.py:68
Titik gagal utamapath CSV dan skema tabelskema kolom di prompt dan izin eksekusi kode

Jalur API: workshop/ai-search-engine.py

Versi workshop mengganti CSV dengan endpoint Sectors. retrieve_from_endpoint menyusun header Authorization dari SECTORS_API_KEY, memanggil requests.get, memeriksa raise_for_status, dan menghentikan program lewat SystemExit kalau terjadi HTTPError. 18-27 workshop/ai-search-engine.py:18-27

Dua fungsi diberi dekorator @tool sehingga bisa dipanggil model: get_company_overview(stock) dan get_sector_overview(sector). Keduanya hanya menyusun URL lalu menyerahkan pekerjaan ke retrieve_from_endpoint. 29-47 workshop/ai-search-engine.py:29-47

ToolEndpoint yang dipanggilArgumen
get_company_overviewhttps://api.sectors.app/v1/company/report/{stock}/?sections=overviewkode saham, misalnya BBRI, TLKM workshop/ai-search-engine.py:29-37
get_sector_overviewhttps://api.sectors.app/v1/subsector/report/{sector}/nama subsector, misalnya banks workshop/ai-search-engine.py:39-47

Perakitan agent memakai create_react_agent dari LangGraph dengan system_message khusus analis data finansial dan checkpointer=MemorySaver(), lalu chat() mengirim pesan dengan session_id dan config berisi thread_id tetap bernilai "supertype". 74-87 workshop/ai-search-engine.py:74-87 90-102 workshop/ai-search-engine.py:90-102

graph LR
  UI["user_input"] --> AG["create_react_agent"]
  AG --> T1["get_company_overview"]
  AG --> T2["get_sector_overview"]
  T1 --> RT["retrieve_from_endpoint"]
  T2 --> RT
  RT --> API["api.sectors.app"]
  AG --> MEM[("MemorySaver")]
  style RT fill:#8b5cf6,color:#fff
  style API fill:#2d7d46,color:#fff
  style AG fill:#f59e0b,color:#000
  style MEM fill:#336791,color:#fff
%% fig: Loop tool-use ke API Sectors
%% desc: Agent memilih satu dari dua tool, keduanya lewat retrieve_from_endpoint mentah ke api.sectors.app, sementara MemorySaver menyimpan riwayat percakapan per thread.
Fig. 3: Loop tool-use ke API SectorsAgent memilih satu dari dua tool, keduanya lewat retrieve_from_endpoint mentah ke api.sectors.app, sementara MemorySaver menyimpan riwayat percakapan per thread.

⚠️ Jalur pencocokan nama di workshop belum jalan. match_input_to_valid_subsector_slug mengembalikan good_approximation yang masih list kosong, fuzz baru diimpor di dalam fungsi, dan __main__ menyisakan dua penanda Challenge (1) serta Challenge (2) untuk bagian ini. Tanpa implementasi itu, input pengguna tidak pernah dipetakan ke slug subsector yang valid. workshop/ai-search-engine.py:58-71 workshop/ai-search-engine.py:104-113

⚠️ Model di berkas workshop masih llama3-8b-8192, sedangkan 15_sql.py dan 16_repl.py sudah dimigrasi ke llama-3.3-70b-versatile karena model lama dihentikan. Jalankan ulang ketiga skrip dengan model yang masih aktif. workshop/ai-search-engine.py:16 CHANGES.md:129-133

Yang perlu disiapkan sebelum menjalankan

langchain-groq dan langchain-experimental sama-sama dibutuhkan: yang pertama untuk ChatGroq di 15_sql.py dan 16_repl.py, yang kedua untuk PythonAstREPLTool. requirements.txt:63-64 Kunci API sendiri dibaca lewat load_dotenv(); 16_repl.py bahkan mengambil SECTORS_API_KEY walaupun jalur REPL tidak memakainya. 16_repl.py:27-28 Tata cara mengisi .env dan variabel mana yang wajib dibahas di Persiapan Lingkungan dan Konfigurasi API Key.

Pilihan antara dua jalur pertama sederhana: pakai 15_sql.py ketika pertanyaan berbentuk agregasi atas kolom skalar dan SQL lebih mudah diaudit, dan pakai 16_repl.py ketika jawabannya butuh manipulasi bentuk DataFrame. Pola agent yang lebih panjang, termasuk pembagian peran antar agent, dibahas di Pola Multi-Agent dengan OpenAI Agent SDK.