Escreva SQL com Vanna e Milvus
Vanna é uma estrutura Python RAG (Retrieval-Augmented Generation) de código aberto para geração de SQL e funcionalidades relacionadas. O Milvus é a base de dados vetorial open-source mais avançada do mundo, criada para potenciar a pesquisa de semelhanças de incorporação e aplicações de IA.
O Vanna funciona em dois passos simples - treina um "modelo" RAG nos seus dados e, em seguida, faz perguntas que devolvem consultas SQL que podem ser configuradas para serem executadas na sua base de dados. Este guia demonstra como utilizar o Vanna para gerar e executar consultas SQL com base nos seus dados armazenados numa base de dados.
Pré-requisitos
Antes de executar este bloco de notas, certifique-se de que tem as seguintes dependências instaladas:
$ pip install "vanna[milvus,openai]"
Se estiver a utilizar o Google Colab, para ativar as dependências que acabou de instalar, poderá ter de reiniciar o tempo de execução (clique no menu "Tempo de execução" na parte superior do ecrã e selecione "Reiniciar sessão" no menu pendente).
E precisa de definir OPENAI_API_KEY
nas suas variáveis de ambiente. Pode obter a chave da API no OpenAI.
import os
os.environ["OPENAI_API_KEY"] = "sk-***********"
Preparação dos dados
Primeiro, precisamos de herdar as classes Milvus_VectorStore
e OpenAI_Chat
da Vanna e definir uma nova classe VannaMilvus
que combine as capacidades de ambas.
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)
Inicializamos a classe VannaMilvus
com os parâmetros de configuração necessários. Utilizamos uma instância milvus_client
para armazenar os embeddings e a model.DefaultEmbeddingFunction()
inicializada a partir de milvus_model para gerar embeddings.C
Quanto ao argumento de MilvusClient
:
- Definir o
uri
como um ficheiro local, por exemplo,./milvus.db
, é o método mais conveniente, pois utiliza automaticamente o Milvus Lite para armazenar todos os dados neste ficheiro. - Se tiver uma grande escala de dados, pode configurar um servidor Milvus mais eficiente em docker ou kubernetes. Nesta configuração, utilize o uri do servidor, por exemplo,
http://localhost:19530
, como o seuuri
. - Se pretender utilizar o Zilliz Cloud, o serviço de nuvem totalmente gerido para o Milvus, ajuste os endereços
uri
etoken
, que correspondem ao Public Endpoint e à chave Api no 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.
}
)
Este é um exemplo simples com apenas algumas amostras de dados, pelo que definimos n_results
para 2 para garantir que procuramos os 2 resultados mais semelhantes. Na prática, deve definir n_results
para um valor mais elevado quando lidar com um conjunto de dados de formação maior.
Utilizaremos uma base de dados SQLite de amostra com algumas tabelas que contêm alguns dados de amostra.
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)
Treinar com dados
Podemos treinar o modelo com os dados DDL da base de dados SQLite. Obtemos os dados DDL e introduzimo-los na função 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
)
Além de treinar com os dados DDL, também podemos treinar com a documentação e as consultas SQL da base de dados.
# 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'
Vamos dar uma olhadela aos dados de treino.
training_data = vn_milvus.get_training_data()
training_data
# | id | pergunta | conteúdo |
---|---|---|---|
0 | 595b185c-e6ad-47b0-98fd-0e93ef9b6a0a-sql | Quais são os detalhes do cliente chamado João... | SELECT * FROM Customer WHERE Name = 'John Doe' |
0 | 25f4956c-e370-4097-994f-996f22d145fa-ddl | Nenhum | CREATE TABLE Empresa (\n ID INTEGER PRIMARY... |
1 | b95ecc66-f65b-49dc-a9f1-c1842ad230ff-ddl | Nenhum | CREATE TABLE Cliente (\n ID INTEGER PRIMARY... |
2 | fcc73d15-30a5-4421-9d73-b8c3b0ed5305-ddl | Nenhum | CREATE TABLE sqlite_sequence(name,seq) |
3 | feae618c-5910-4f6f-8b4b-6cc3e03aec06-ddl | Nenhum | CREATE TABLE User (\n ID INTEGER PRIMARY KE... |
0 | 79a48db1-ba1f-4fd5-be99-74f2ca2eaeeb-doc | Nenhum | A XYZ Inc. é um líder mundial no fabrico e na... |
1 | 9f9df1b8-ae62-4823-ad28-d7e0f2d1f4c0-doc | Nenhum | A ABC Corp é especializada em tecnologia de ponta... |
Gerar SQLs e executá-los
Como treinámos com os dados DDL, a estrutura da tabela está agora disponível para gerar consultas SQL.
Vamos tentar uma pergunta simples.
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'
# | Telefone |
---|---|
0 | 123-456-7890 |
Aqui está uma pergunta mais complexa. As informações sobre o nome da empresa de produção estão nos dados do documento, que são informações de fundo. A consulta SQL gerada recuperará as informações do cliente com base no nome específico da empresa de fabrico.
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 | Nome da empresa | Empresa | Cidade | Telefone |
---|---|---|---|---|---|
0 | 2 | Jane Smith | XYZ Inc | São Paulo | 098-765-4321 |
Desconecte-se do SQLite e do Milvus e remova-os para liberar recursos.
sql_connect.close()
milvus_client.close()
os.remove(sqlite_path)
if os.path.exists(milvus_uri):
os.remove(milvus_uri)