SQL schreiben mit Vanna und Milvus
Vanna ist ein Open-Source-Python-RAG-Framework (Retrieval-Augmented Generation) für die SQL-Generierung und verwandte Funktionen. Milvus ist die fortschrittlichste Open-Source-Vektordatenbank der Welt, die für die Einbettung von Ähnlichkeitssuche und KI-Anwendungen entwickelt wurde.
Vanna funktioniert in zwei einfachen Schritten: Trainieren Sie ein RAG-"Modell" auf Ihren Daten, und stellen Sie dann Fragen, die SQL-Abfragen zurückgeben, die in Ihrer Datenbank ausgeführt werden können. Dieser Leitfaden zeigt Ihnen, wie Sie mit Vanna SQL-Abfragen auf der Grundlage Ihrer in einer Datenbank gespeicherten Daten erstellen und ausführen können.
Voraussetzungen
Vergewissern Sie sich, dass Sie die folgenden Abhängigkeiten installiert haben, bevor Sie dieses Notizbuch ausführen:
$ pip install "vanna[milvus,openai]"
Wenn Sie Google Colab verwenden, müssen Sie möglicherweise die Runtime neu starten, um die soeben installierten Abhängigkeiten zu aktivieren (klicken Sie auf das Menü "Runtime" am oberen Rand des Bildschirms und wählen Sie "Sitzung neu starten" aus dem Dropdown-Menü).
Außerdem müssen Sie die OPENAI_API_KEY
in Ihren Umgebungsvariablen festlegen. Sie können den API-Schlüssel von OpenAI erhalten.
import os
os.environ["OPENAI_API_KEY"] = "sk-***********"
Vorbereitung der Daten
Zunächst müssen wir von den Klassen Milvus_VectorStore
und OpenAI_Chat
von Vanna erben und eine neue Klasse VannaMilvus
definieren, die die Fähigkeiten beider Klassen kombiniert.
from pymilvus import MilvusClient, model
from vanna.milvus import Milvus_VectorStore
from vanna.openai import OpenAI_Chat
class VannaMilvus(Milvus_VectorStore, OpenAI_Chat):
def __init__(self, config=None):
Milvus_VectorStore.__init__(self, config=config)
OpenAI_Chat.__init__(self, config=config)
Wir initialisieren die Klasse VannaMilvus
mit den notwendigen Konfigurationsparametern. Wir verwenden eine milvus_client
Instanz, um Einbettungen zu speichern und die model.DefaultEmbeddingFunction()
, die von milvus_model initialisiert wurde, um Einbettungen zu generieren.C
Was das Argument von MilvusClient
betrifft:
- Die Einstellung von
uri
als lokale Datei, z.B../milvus.db
, ist die bequemste Methode, da sie automatisch Milvus Lite nutzt, um alle Daten in dieser Datei zu speichern. - Wenn Sie große Datenmengen haben, können Sie einen leistungsfähigeren Milvus-Server auf Docker oder Kubernetes einrichten. Bei dieser Einrichtung verwenden Sie bitte die Server-Uri, z. B.
http://localhost:19530
, alsuri
. - Wenn Sie Zilliz Cloud, den vollständig verwalteten Cloud-Service für Milvus, verwenden möchten, passen Sie
uri
undtoken
an, die dem öffentlichen Endpunkt und dem Api-Schlüssel in Zilliz Cloud entsprechen.
milvus_uri = "./milvus_vanna.db"
milvus_client = MilvusClient(uri=milvus_uri)
vn_milvus = VannaMilvus(
config={
"api_key": os.getenv("OPENAI_API_KEY"),
"model": "gpt-3.5-turbo",
"milvus_client": milvus_client,
"embedding_function": model.DefaultEmbeddingFunction(),
"n_results": 2, # The number of results to return from Milvus semantic search.
}
)
Da es sich um ein einfaches Beispiel mit nur wenigen Daten handelt, setzen wir n_results
auf 2, um sicherzustellen, dass wir nach den zwei ähnlichsten Ergebnissen suchen. In der Praxis sollten Sie n_results
auf einen höheren Wert setzen, wenn Sie mit größeren Trainingsdaten arbeiten.
Wir werden eine SQLite-Beispieldatenbank mit einigen Tabellen verwenden, die einige Beispieldaten enthalten.
import sqlite3
sqlite_path = "./my-database.sqlite"
sql_connect = sqlite3.connect(sqlite_path)
c = sql_connect.cursor()
init_sqls = """
CREATE TABLE IF NOT EXISTS Customer (
ID INTEGER PRIMARY KEY AUTOINCREMENT,
Name TEXT NOT NULL,
Company TEXT NOT NULL,
City TEXT NOT NULL,
Phone TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS Company (
ID INTEGER PRIMARY KEY AUTOINCREMENT,
Name TEXT NOT NULL,
Industry TEXT NOT NULL,
Location TEXT NOT NULL,
EmployeeCount INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS User (
ID INTEGER PRIMARY KEY AUTOINCREMENT,
Username TEXT NOT NULL UNIQUE,
Email TEXT NOT NULL UNIQUE
);
INSERT INTO Customer (Name, Company, City, Phone)
VALUES ('John Doe', 'ABC Corp', 'New York', '123-456-7890');
INSERT INTO Customer (Name, Company, City, Phone)
VALUES ('Jane Smith', 'XYZ Inc', 'Los Angeles', '098-765-4321');
INSERT INTO Company (Name, Industry, Location, EmployeeCount)
VALUES ('ABC Corp', 'cutting-edge technology', 'New York', 100);
INSERT INTO User (Username, Email)
VALUES ('johndoe123', 'johndoe123@example.com');
"""
for sql in init_sqls.split(";"):
c.execute(sql)
sql_connect.commit()
# Connect to the SQLite database
vn_milvus.connect_to_sqlite(sqlite_path)
Trainieren mit Daten
Wir können das Modell mit den DDL-Daten der SQLite-Datenbank trainieren. Wir holen uns die DDL-Daten und füttern damit die Funktion train
.
# If there exists training data, we should remove it before training.
existing_training_data = vn_milvus.get_training_data()
if len(existing_training_data) > 0:
for _, training_data in existing_training_data.iterrows():
vn_milvus.remove_training_data(training_data["id"])
# Get the DDL of the SQLite database
df_ddl = vn_milvus.run_sql("SELECT type, sql FROM sqlite_master WHERE sql is not null")
# Train the model on the DDL data
for ddl in df_ddl["sql"].to_list():
vn_milvus.train(ddl=ddl)
Adding ddl: CREATE TABLE Customer (
ID INTEGER PRIMARY KEY AUTOINCREMENT,
Name TEXT NOT NULL,
Company TEXT NOT NULL,
City TEXT NOT NULL,
Phone TEXT NOT NULL
)
Adding ddl: CREATE TABLE sqlite_sequence(name,seq)
Adding ddl: CREATE TABLE Company (
ID INTEGER PRIMARY KEY AUTOINCREMENT,
Name TEXT NOT NULL,
Industry TEXT NOT NULL,
Location TEXT NOT NULL,
EmployeeCount INTEGER NOT NULL
)
Adding ddl: CREATE TABLE User (
ID INTEGER PRIMARY KEY AUTOINCREMENT,
Username TEXT NOT NULL UNIQUE,
Email TEXT NOT NULL UNIQUE
)
Neben dem Training mit den DDL-Daten können wir auch mit der Dokumentation und den SQL-Abfragen der Datenbank trainieren.
# Add documentation about your business terminology or definitions.
vn_milvus.train(
documentation="ABC Corp specializes in cutting-edge technology solutions and innovation."
)
vn_milvus.train(
documentation="XYZ Inc is a global leader in manufacturing and supply chain management."
)
# You can also add SQL queries to your training data.
vn_milvus.train(sql="SELECT * FROM Customer WHERE Name = 'John Doe'")
Adding documentation....
Adding documentation....
Using model gpt-3.5-turbo for 65.0 tokens (approx)
Question generated with sql: What are the details of the customer named John Doe?
Adding SQL...
'595b185c-e6ad-47b0-98fd-0e93ef9b6a0a-sql'
Werfen wir einen Blick auf die Trainingsdaten.
training_data = vn_milvus.get_training_data()
training_data
# | id | Frage | Inhalt |
---|---|---|---|
0 | 595b185c-e6ad-47b0-98fd-0e93ef9b6a0a-sql | Wie lauten die Details des Kunden namens Joh... | SELECT * FROM Kunde WHERE Name = 'John Doe' |
0 | 25f4956c-e370-4097-994f-996f22d145fa-ddl | Keine | CREATE TABLE Unternehmen (\n ID INTEGER PRIMARY... |
1 | b95ecc66-f65b-49dc-a9f1-c1842ad230ff-ddl | Keine | CREATE TABLE Kunde (\n ID INTEGER PRIMAR... |
2 | fcc73d15-30a5-4421-9d73-b8c3b0ed5305-ddl | Keine | CREATE TABLE sqlite_sequence(name,seq) |
3 | feae618c-5910-4f6f-8b4b-6cc3e03aec06-ddl | Keine | CREATE TABLE Benutzer (\n ID INTEGER PRIMARY KE... |
0 | 79a48db1-ba1f-4fd5-be99-74f2ca2eaeeb-doc | Keine | XYZ Inc ist ein weltweit führendes Unternehmen in der Herstellung und... |
1 | 9f9df1b8-ae62-4823-ad28-d7e0f2d1f4c0-doc | Keine | ABC Corp ist spezialisiert auf Spitzentechnologie... |
SQLs generieren und ausführen
Da wir mit den DDL-Daten geübt haben, ist die Tabellenstruktur nun für die Generierung von SQL-Abfragen verfügbar.
Versuchen wir es mit einer einfachen Frage.
sql = vn_milvus.generate_sql("what is the phone number of John Doe?")
vn_milvus.run_sql(sql)
SQL Prompt: [{'role': 'system', 'content': "You are a SQLite expert. Please help to generate a SQL query to answer the question. Your response should ONLY be based on the given context and follow the response guidelines and format instructions. \n===Tables \nCREATE TABLE Customer (\n ID INTEGER PRIMARY KEY AUTOINCREMENT,\n Name TEXT NOT NULL,\n Company TEXT NOT NULL,\n City TEXT NOT NULL,\n Phone TEXT NOT NULL\n)\n\nCREATE TABLE User (\n ID INTEGER PRIMARY KEY AUTOINCREMENT,\n Username TEXT NOT NULL UNIQUE,\n Email TEXT NOT NULL UNIQUE\n)\n\n\n===Additional Context \n\nABC Corp specializes in cutting-edge technology solutions and innovation.\n\nXYZ Inc is a global leader in manufacturing and supply chain management.\n\n===Response Guidelines \n1. If the provided context is sufficient, please generate a valid SQL query without any explanations for the question. \n2. If the provided context is almost sufficient but requires knowledge of a specific string in a particular column, please generate an intermediate SQL query to find the distinct strings in that column. Prepend the query with a comment saying intermediate_sql \n3. If the provided context is insufficient, please explain why it can't be generated. \n4. Please use the most relevant table(s). \n5. If the question has been asked and answered before, please repeat the answer exactly as it was given before. \n"}, {'role': 'user', 'content': 'What are the details of the customer named John Doe?'}, {'role': 'assistant', 'content': "SELECT * FROM Customer WHERE Name = 'John Doe'"}, {'role': 'user', 'content': 'what is the phone number of John Doe?'}]
Using model gpt-3.5-turbo for 367.25 tokens (approx)
LLM Response: SELECT Phone FROM Customer WHERE Name = 'John Doe'
# | Telefon |
---|---|
0 | 123-456-7890 |
Hier ist eine komplexere Frage. Die Informationen über den Namen des Fertigungsunternehmens befinden sich in den Dokumentdaten, bei denen es sich um Hintergrundinformationen handelt. Die generierte SQL-Abfrage ruft die Kundeninformationen auf der Grundlage des spezifischen Namens des Fertigungsunternehmens ab.
sql = vn_milvus.generate_sql("which customer works for a manufacturing corporation?")
vn_milvus.run_sql(sql)
SQL Prompt: [{'role': 'system', 'content': "You are a SQLite expert. Please help to generate a SQL query to answer the question. Your response should ONLY be based on the given context and follow the response guidelines and format instructions. \n===Tables \nCREATE TABLE Company (\n ID INTEGER PRIMARY KEY AUTOINCREMENT,\n Name TEXT NOT NULL,\n Industry TEXT NOT NULL,\n Location TEXT NOT NULL,\n EmployeeCount INTEGER NOT NULL\n)\n\nCREATE TABLE Customer (\n ID INTEGER PRIMARY KEY AUTOINCREMENT,\n Name TEXT NOT NULL,\n Company TEXT NOT NULL,\n City TEXT NOT NULL,\n Phone TEXT NOT NULL\n)\n\n\n===Additional Context \n\nXYZ Inc is a global leader in manufacturing and supply chain management.\n\nABC Corp specializes in cutting-edge technology solutions and innovation.\n\n===Response Guidelines \n1. If the provided context is sufficient, please generate a valid SQL query without any explanations for the question. \n2. If the provided context is almost sufficient but requires knowledge of a specific string in a particular column, please generate an intermediate SQL query to find the distinct strings in that column. Prepend the query with a comment saying intermediate_sql \n3. If the provided context is insufficient, please explain why it can't be generated. \n4. Please use the most relevant table(s). \n5. If the question has been asked and answered before, please repeat the answer exactly as it was given before. \n"}, {'role': 'user', 'content': 'What are the details of the customer named John Doe?'}, {'role': 'assistant', 'content': "SELECT * FROM Customer WHERE Name = 'John Doe'"}, {'role': 'user', 'content': 'which customer works for a manufacturing corporation?'}]
Using model gpt-3.5-turbo for 384.25 tokens (approx)
LLM Response: SELECT *
FROM Customer
WHERE Company = 'XYZ Inc'
# | ID | Name | Unternehmen | Stadt | Telefon |
---|---|---|---|---|---|
0 | 2 | Jane Smith | XYZ Inc | Los Angeles | 098-765-4321 |
Trennen Sie die Verbindung zu SQLite und Milvus und entfernen Sie sie, um Ressourcen freizugeben.
sql_connect.close()
milvus_client.close()
os.remove(sqlite_path)
if os.path.exists(milvus_uri):
os.remove(milvus_uri)