A combinação da geração aumentada por recuperação (RAG) e modelos de IA generativa trouxe mudanças para o processamento de linguagem natural ao melhorar as respostas às consultas. No âmbito do RAG Agentic, esse método convencional de depender de um modelo monolítico para tarefas foi aprimorado pela introdução de modularidade e autonomia. Ao dividir o processo de resolução de problemas em ferramentas integradas dentro de um agente, o RAG Agentic oferece benefícios como precisão, transparência, escalabilidade e capacidades de depuração.
A Visão por Trás do RAG Agentic para Texto-para-SQL
Sistemas RAG tradicionais frequentemente recuperam documentos relevantes e dependem de um único modelo monolítico para gerar respostas. Embora este seja um método eficaz em alguns casos, quando se trata de saídas estruturais como no caso da geração de SQL, essa abordagem pode não ser a mais eficaz. É aqui que podemos aproveitar o poder do framework RAG Agentic, onde nós:
- Dividimos as tarefas em ferramentas menores e mais gerenciáveis dentro de um agente
- Melhoramos a precisão atribuindo tarefas a ferramentas especializadas
- Incrementamos a transparência rastreando o raciocínio e o fluxo de trabalho de cada ferramenta
- Simplificamos a escalabilidade e a depuração por meio do design modular
Vamos falar sobre como essa ferramenta funciona e o papel que cada componente desempenha na transformação de perguntas do usuário em consultas SQL precisas.
Visão Geral da Arquitetura
A estrutura compreende um agente utilizando ferramentas dentro do fluxo de trabalho de texto-para-SQL. O processo pode ser resumido da seguinte forma:
Consulta do Usuário → Ferramenta de Transformação de Consulta → Ferramenta de Instrução Rápida → Ferramenta de Busca Híbrida → Ferramenta de Reordenação → Ferramenta de Recuperação de Tabela → Ferramenta de Construção de Prompt → Ferramenta de Execução de LLM → Ferramenta de Execução de SQL → Saída Final
1. Ferramenta de Transformação de Consulta do Usuário
Esta ferramenta envolve o processamento da consulta do usuário para uma melhor compreensão do LLM. Ela aborda ambiguidades, reformula perguntas do usuário, traduz abreviações para suas formas e fornece contexto quando necessário.
Aprimoramentos
- Tratar referências temporais. Mapear termos como “até hoje” ou “até agora” para datas explícitas.
- Substituir palavras ambíguas. Por exemplo, “recente” poderia ser substituído por “últimos 7 dias.”
- Conectar abreviações ou siglas aos seus nomes.
Exemplo
Entrada: “Mostrar vendas recentes MTD.”
Consulta transformada: “Recuperar dados de vendas dos últimos 7 dias (Mês até a Data).”
from datetime import date, timedelta
def transform_query(user_query):
# Lidar com referências temporais em aberto
today = date.today()
transformations = {
"as of today": f"up to {today}",
"till now": f"up to {today}",
"recent": "last 7 days",
"last week": f"from {today - timedelta(days=7)} to {today}",
}
for key, value in transformations.items():
user_query = user_query.replace(key, value)
# Mapear abreviações comuns
abbreviations = {
"MTD": "Month to Date",
"YTD": "Year to Date",
}
for abbr, full_form in abbreviations.items():
user_query = user_query.replace(abbr, full_form)
return user_query
query_transform_tool = Tool(
name="Query Transformer",
func=transform_query,
description="Refines user queries for clarity and specificity, handles abbreviations and open-ended terms."
)
2. Ferramenta de Instrução Rápida
Esta ferramenta faz uma chamada ao LLM para identificar a pergunta de um tipo a partir de um conjunto (também podemos dizer que corresponde ao modelo). A pergunta correspondente aprimora o prompt com uma consulta SQL de exemplo.
Fluxo de Exemplo
1. Pergunta de entrada: “Mostre-me as vendas totais por produto nos últimos 7 dias.”
2. Modelos predefinidos:
- “Mostrar vendas agrupadas por região.” → Exemplo de SQL; SELECT região, SUM(vendas) …
- “Exibir vendas totais por produto.” → Exemplo de SQL; SELECT product_name, SUM(sales) …
3. Pergunta mais semelhante: “Exibir vendas totais por produto.”
4. Exemplo de saída SQL: SELECT product_name, SUM(sales) FROM …
from langchain.chat_models import ChatOpenAI
llm = ChatOpenAI(model="gpt-4")
predefined_examples = {
"Show sales grouped by region": "SELECT region, SUM(sales) FROM sales_data GROUP BY region;",
"Show total sales by product": "SELECT product_name, SUM(sales) FROM sales_data GROUP BY product_name;",
}
def find_similar_question(user_query):
prompt = "Find the most similar question type for the following user query:\n"
prompt += f"User Query: {user_query}\n\nOptions:\n"
for example in predefined_examples.keys():
prompt += f"- {example}\n"
prompt += "\nRespond with the closest match."
response = llm.call_as_function(prompt)
most_similar = response['content']
return predefined_examples.get(most_similar, "")
few_shot_tool = Tool(
name="Few-Shot Prompting",
func=find_similar_question,
description="Finds the most similar question type using an additional LLM call and retrieves the corresponding example SQL."
)
3. Ferramenta de Busca Híbrida
Para uma recuperação robusta, esta ferramenta combina busca semântica, busca por palavras-chave baseada em BM25 e mapeamento baseado em palavras-chave. Os resultados da busca desses métodos são reunidos usando fusão de classificação recíproca.
Como tudo se junta?
Mapeamento de Tabela de Palavras-Chave
Esta abordagem mapeia as tabelas para as palavras-chave contidas na consulta. Por exemplo:
- A presença de “vendas” resulta na tabela de vendas sendo pré-selecionada.
- A presença de “produto” resulta na tabela de produtos sendo pré-selecionada.
Mapeamento de Sobreposição de Palavras-Chave (BM25)
Este é um método de busca baseado na sobreposição de palavras-chave que pré-seleciona tabelas de acordo com a relevância. Para isso, aplicaremos a técnica BM25. Isso classifica os documentos em ordem de relevância para uma busca do usuário. Essa técnica de busca considera a saturação de termos em vista, bem como TF-IDF (Frequência de Termo – Frequência Inversa de Documento).
A Frequência de Termo (TF) ajuda a medir a frequência de um termo em um determinado documento. A abordagem de Frequência Inversa de Documento (IDF) destaca palavras que aparecem em todos os documentos, diminuindo a importância.
A normalização leva em conta o comprimento do documento para evitar qualquer viés em relação a documentos mais longos.
Dado:
- sales_data: Contém termos como “vendas,” “data,” “produto.”
- produtos: Contém termos como “produto,” “categoria.”
- pedidos: Contém termos como “pedido,” “data,” “cliente.”
- financeiro: Contém termos como “receita,” “lucro,” “despesa.”
Consulta do usuário: “Mostrar vendas totais por produto.”
- Identificar termos na consulta do usuário: [“vendas,” “produto”].
- Classificar cada documento (com base na frequência e relevância desses termos) na Tabela de Banco de Dados.
Relevância dos documentos:
- vendas: Alta relevância devido a “vendas” e “produto”
- produtos: Alta relevância devido a “produto.”
- pedidos: Baixa relevância devido à presença apenas de “vendas.”
- financeiro: Não relevante.
Saída:
Lista classificada: [produtos, sales_data, pedidos, financeiro]
Busca Semântica
Neste método de busca, como o próprio nome sugere, encontramos tabelas semanticamente similares utilizando incorporações vetoriais. Conseguimos isso calculando uma pontuação de similaridade, como a similaridade de cosseno, entre os vetores de documentos (tabelas) e vetores de consulta do usuário.
Fusão de Classificação Recíproca
Combina os resultados BM25 e de busca semântica usando a estratégia de fusão de classificação recíproca, que é explicada um pouco mais detalhadamente abaixo:
Fusão de Classificação Recíproca (RRF) combinando BM25 e busca semântica:
RRF é um método para combinar resultados de múltiplos algoritmos de classificação (por exemplo, BM25 e busca semântica). Ele atribui uma pontuação a cada documento com base em sua classificação nos métodos individuais, dando pontuações mais altas a documentos classificados mais alto em vários métodos.
Fórmula RRF:
RRF(d) = Σ(r ∈ R) 1 / (k + r(d))
Onde:
- d é um documento
- R é o conjunto de classificadores (métodos de busca)
- k é uma constante (tipicamente 60)
- r(d) é a classificação do documento d no método de busca r
Exemplo Passo a Passo
Dados de entrada.
1. Resultados de classificação BM25:
- produtos (Classificação 1)
- dados_de_vendas (Classificação 2)
- pedidos (Classificação 3)
2. Resultados de classificação de busca semântica:
- dados_de_vendas (Classificação 1)
- financeiro (Classificação 2)
- produtos (Classificação 3)
Fusão Passo a Passo
Para cada tabela, calcule a pontuação:
1. dados_de_vendas
- Classificação BM25 = 2, Classificação Semântica = 1
- Pontuação RRF = (1/60+2 ) + (1/60+1) = 0.03252
2. produtos
- Classificação BM25 = 1, Classificação Semântica = 3
- Pontuação RRF = (1/60+1) + (1/60+3)= .03226
3. pedidos
- BM25 Rank = 3, Semantic Rank = Not Ranked
- RRF Score = (1/60+3)= 0.01587
4. financeiro
- BM25 Rank = Not Ranked, Semantic Rank = 2
- RRF Score = (1/60+2)=0.01613
5. Classificar por pontuação RRF
- dados_de_vendas (pontuação mais alta devido ao topo na pesquisa semântica).
- produtos (pontuação alta do BM25).
- pedidos (relevância mais baixa no geral).
- financeiro (sobreposição limitada).
Resultado final: [‘dados_de_vendas’, ‘produtos,’ ‘financeiro,’ ‘pedidos’]
Tabelas recuperadas usando a Tabela de Palavras-chave mapeamento são sempre incluídas.
from rank_bm25 import BM25Okapi
def hybrid_search(query):
# Mapeamento baseado em palavras-chave
keyword_to_table = {
"sales": "sales_data",
"product": "products",
}
keyword_results = [table for keyword, table in keyword_to_table.items() if keyword in query.lower()]
# Pesquisa BM25
bm25 = BM25Okapi(["sales_data", "products", "orders", "financials"])
bm25_results = bm25.get_top_n(query.split(), bm25.corpus, n=5)
# Pesquisa Semântica
semantic_results = vector_store.similarity_search(query, k=5)
# Fusão de Reciprocal Rank
def reciprocal_rank_fusion(results):
rank_map = {}
for rank, table in enumerate(results):
rank_map[table] = rank_map.get(table, 0) + 1 / (1 + rank)
return sorted(rank_map, key=rank_map.get, reverse=True)
combined_results = reciprocal_rank_fusion(bm25_results + semantic_results)
return list(set(keyword_results + combined_results))
hybrid_search_tool = Tool(
name="Hybrid Search",
func=hybrid_search,
description="Combines keyword mapping, BM25, and semantic search with RRF for table retrieval."
)
4. Ferramenta de Re-Classificação
Esta ferramenta garante que as tabelas mais relevantes sejam priorizadas.
Exemplo
- Tabelas de entrada: [“dados_de_vendas,” “produtos,” “financeiro”]
- Logica de reclassificação
- Para cada tabela, calcular uma pontuação de relevância concatenando a consulta e a descrição da tabela.
- Classificar por pontuação de relevância.
- Saída: [“dados_de_vendas,” “produtos”]
Um pouco mais sobre a lógica de Re-rank:
O codificador–cruzado calcula uma pontuação de relevância analisando a consulta concatenada e a descrição da tabela como um par de entrada único. Esse processo envolve:
- Entrada em pares. A consulta e cada descrição da tabela são emparelhadas e passadas como entrada para o codificador cruzado.
- Codificação conjunta. Ao contrário dos codificadores separados (por exemplo, bi-codificadores), o codificador cruzado codifica conjuntamente o par, permitindo capturar melhor o contexto e as dependências entre a consulta e a descrição da tabela.
- Pontuação. O modelo gera uma pontuação de relevância para cada par, indicando o quão bem a tabela corresponde à consulta.
from transformers import pipeline
reranker = pipeline("text-classification", model="cross-encoder/ms-marco-TinyBERT-L-2")
def re_rank_context(query, results):
scores = [(doc, reranker(query + " " + doc)[0]['score']) for doc in results]
return [doc for doc, score in sorted(scores, key=lambda x: x[1], reverse=True)]
re_rank_tool = Tool(
name="Re-Ranker",
func=re_rank_context,
description="Re-ranks the retrieved tables based on relevance to the query."
)
5. Ferramenta de Construção de Prompt
Esta ferramenta constrói um prompt detalhado para o modelo de linguagem, incorporando a consulta refinada do usuário, o esquema recuperado e exemplos da Ferramenta de Prompting Few-Shot.
Suponha que você seja alguém capaz de gerar consultas SQL. Gere uma consulta SQL para: Recuperar as vendas totais agrupadas por produto nos últimos 7 dias.
Tabelas relevantes:
- dados_de_vendas: Contém colunas [vendas, data, id_do_produto].
- produtos: Contém colunas [id_do_produto, nome_do_produto].
Exemplo SQL:
SELECT product_name, SUM(sales) FROM sales_data JOIN products ON sales_data.product_id = products.product_id GROUP BY product_name;
Escopo Futuro
Embora este sistema utilize um agente único com múltiplas ferramentas para simplificar a modularidade e reduzir a complexidade, um framework de vários agentes poderia ser explorado no futuro. Poderíamos possivelmente explorar o seguinte:
- Agentes dedicados para recuperação de contexto. Agentes separados para pesquisas semânticas e por palavras-chave.
- Agentes específicos para tarefas. Agentes especializados em validação ou otimização de SQL.
- Colaboração entre agentes. Usando um agente de coordenação para gerenciar a delegação de tarefas.
Essa abordagem pode aumentar a escalabilidade e permitir fluxos de trabalho mais sofisticados, especialmente em implementações em nível empresarial.
Conclusão
RAG Agente para aplicações de texto para SQL oferece uma abordagem escalável e modular para resolver tarefas de consulta estruturadas. Ao incorporar pesquisa híbrida, reclassificação, sugestões de poucos exemplos e construção dinâmica de prompts dentro de uma estrutura de agente único, este sistema garante precisão, transparência e extensibilidade. Este fluxo de trabalho aprimorado demonstra um poderoso modelo para transformar perguntas em linguagem natural em consultas SQL acionáveis.
Source:
https://dzone.com/articles/creating-an-agentic-rag-for-text-to-sql-applications