Ecrire du SQL avec Vanna et Milvus
Vanna est un framework Python RAG (Retrieval-Augmented Generation) open-source pour la génération SQL et les fonctionnalités associées. Milvus est la base de données vectorielles open-source la plus avancée au monde, conçue pour permettre la recherche de similarités dans l'embarqué et les applications d'intelligence artificielle.
Vanna fonctionne en deux étapes simples - former un "modèle" RAG sur vos données, puis poser des questions qui renverront des requêtes SQL qui peuvent être configurées pour être exécutées sur votre base de données. Ce guide montre comment utiliser Vanna pour générer et exécuter des requêtes SQL basées sur vos données stockées dans une base de données.
Conditions préalables
Avant d'exécuter ce notebook, assurez-vous que les dépendances suivantes sont installées :
$ pip install "vanna[milvus,openai]"
Si vous utilisez Google Colab, pour activer les dépendances qui viennent d'être installées, vous devrez peut-être redémarrer le runtime (Cliquez sur le menu "Runtime" en haut de l'écran, et sélectionnez "Restart session" dans le menu déroulant).
Vous devez également définir l'adresse OPENAI_API_KEY
dans vos variables d'environnement. Vous pouvez obtenir la clé API auprès d'OpenAI.
import os
os.environ["OPENAI_API_KEY"] = "sk-***********"
Préparation des données
Tout d'abord, nous devons hériter des classes Milvus_VectorStore
et OpenAI_Chat
de Vanna et définir une nouvelle classe VannaMilvus
qui combine les capacités des deux.
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)
Nous initialisons la classe VannaMilvus
avec les paramètres de configuration nécessaires. Nous utilisons une instance milvus_client
pour stocker les enregistrements et la classe model.DefaultEmbeddingFunction()
initialisée à partir de milvus_model pour générer les enregistrements.
Comme pour l'argument de MilvusClient
:
- Définir
uri
comme un fichier local, par exemple./milvus.db
, est la méthode la plus pratique, car elle utilise automatiquement Milvus Lite pour stocker toutes les données dans ce fichier. - Si vous avez des données à grande échelle, vous pouvez configurer un serveur Milvus plus performant sur docker ou kubernetes. Dans cette configuration, veuillez utiliser l'uri du serveur, par exemple
http://localhost:19530
, comme votreuri
. - Si vous souhaitez utiliser Zilliz Cloud, le service cloud entièrement géré pour Milvus, ajustez les adresses
uri
ettoken
, qui correspondent au point final public et à la clé Api dans Zilliz Cloud.
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.
}
)
Il s'agit d'un exemple simple avec seulement quelques échantillons de données, nous avons donc fixé n_results
à 2 pour nous assurer que nous recherchons les 2 premiers résultats les plus similaires. En pratique, vous devriez fixer n_results
à une valeur plus élevée lorsque vous avez affaire à un ensemble de données d'entraînement plus important.
Nous utiliserons un exemple de base de données SQLite avec quelques tables contenant des échantillons de données.
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)
Entraînement avec les données
Nous pouvons entraîner le modèle sur les données DDL de la base de données SQLite. Nous obtenons les données DDL et les transmettons à la fonction 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
)
Outre l'entraînement sur les données DDL, nous pouvons également l'effectuer sur la documentation et les requêtes SQL de la base de données.
# 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'
Jetons un coup d'œil aux données d'entraînement.
training_data = vn_milvus.get_training_data()
training_data
# | id | question | contenu |
---|---|---|---|
0 | 595b185c-e6ad-47b0-98fd-0e93ef9b6a0a-sql | Quels sont les détails du client nommé Joh... | SELECT * FROM Customer WHERE Name = 'John Doe' (nom du client) |
0 | 25f4956c-e370-4097-994f-996f22d145fa-ddl | Aucun | CREATE TABLE Company (\n ID INTEGER PRIMARY... |
1 | b95ecc66-f65b-49dc-a9f1-c1842ad230ff-ddl | Aucun | CREATE TABLE Customer (\n ID INTEGER PRIMAR... |
2 | fcc73d15-30a5-4421-9d73-b8c3b0ed5305-ddl | Aucun | CREATE TABLE sqlite_sequence(name,seq) |
3 | feae618c-5910-4f6f-8b4b-6cc3e03aec06-ddl | Aucun | CREATE TABLE User (\n ID INTEGER PRIMARY KE... |
0 | 79a48db1-ba1f-4fd5-be99-74f2ca2eaeeb-doc | Aucun | XYZ Inc est un leader mondial de la fabrication et de la... |
1 | 9f9df1b8-ae62-4823-ad28-d7e0f2d1f4c0-doc | Aucun | ABC Corp se spécialise dans les technologies de pointe... |
Générer des SQL et les exécuter
Comme nous nous sommes entraînés avec les données DDL, la structure de la table est maintenant disponible pour générer des requêtes SQL.
Essayons une question simple.
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'
# | Téléphone |
---|---|
0 | 123-456-7890 |
Voici une question plus complexe. Les informations sur le nom de la société de fabrication se trouvent dans les données du document, qui sont des informations de base. La requête SQL générée récupérera les informations sur le client en fonction du nom spécifique de l'entreprise de fabrication.
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 | Nom de l'entreprise | Société | Ville | Téléphone |
---|---|---|---|---|---|
0 | 2 | Jane Smith | XYZ Inc | Los Angeles | 098-765-4321 |
Déconnectez-vous de SQLite et de Milvus et supprimez-les pour libérer des ressources.
sql_connect.close()
milvus_client.close()
os.remove(sqlite_path)
if os.path.exists(milvus_uri):
os.remove(milvus_uri)