A combinação de geração aumentada por recuperação (RAG) e modelos de IA generativos trouxe mudanças ao processamento de linguagem natural ao melhorar as respostas às consultas. No âmbito do RAG Agente, este método convencional de depender de um modelo monolítico para tarefas foi aprimorado pela introdução de modularidade e autonomia. Ao decompor o processo de resolução de problemas em ferramentas integradas dentro de um agente, o RAG Agente fornece benefícios como precisão, transparência, escalabilidade e capacidades de depuração.
A Visão por Trás do RAG Agente para Texto-para-SQL
Sistemas tradicionais de RAG 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, esta abordagem pode não ser a mais eficaz. É aqui que podemos aproveitar o poder do framework RAG Agente, 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
- Aprimoramos 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 de cada componente 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 Indução de Poucas Etapas → Ferramenta de Pesquisa Híbrida → Ferramenta de Reordenação → Ferramenta de Recuperação de Tabela → Ferramenta de Construção de Prompt → Ferramenta de Execução LLM → Ferramenta de Execução 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.
Melhorias
- Lidar com 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 abreviaturas 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 Indução de Poucas Etapas
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 Trabalho 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 SQL; SELECT região, SUM(vendas) …
- “Mostrar vendas totais por produto.” → Exemplo de SQL; SELECIONE nome_do_produto, SOMA(vendas) …
3. Pergunta mais semelhante: “Mostrar vendas totais por produto.”
4. Exemplo de saída em SQL: SELECIONE nome_do_produto, SOMA(vendas) DE …
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, essa ferramenta combina busca semântica, busca por palavra-chave baseada no BM25 e mapeamento baseado em palavras-chave. Os resultados da busca desses métodos são combinados 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 selecionada.
- A presença de “produto” resulta na tabela de produtos sendo selecionada.
Mapeamento de Sobreposição de Palavras-chave (BM25)
Este é um método de busca baseado em sobreposição de palavras-chave que 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 do Termo-Inverso da Frequência do Documento).
A Frequência do Termo (TF) ajuda a medir a frequência de um termo em um determinado documento. A abordagem de Frequência do Documento Inversa (IDF) destaca palavras que aparecem em todos os documentos, diminuindo a importância.
A normalização leva em consideração o comprimento do documento para evitar qualquer viés em direçã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.”
- finanças: 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.”
- finanças: Não relevante.
Saída:
Lista classificada: [produtos, sales_data, pedidos, finanças]
Busca Semântica
Neste método de busca, como o nome sugere, encontramos tabelas semanticamente similares utilizando incorporações vetoriais. Alcançamos isso calculando um escore de similaridade, como similaridade de cosseno, entre o documento (vetores de tabela) e vetores de consulta do usuário.
Fusão de Rank Recíproco
Combina os resultados de pesquisa BM25 e semântica usando a estratégia de fusão de classificação recíproca, que é explicada um pouco mais detalhadamente abaixo:
Fusão de Reciprocidade de Classificação (RRF) combinando BM25 e pesquisa semântica:
RRF é um método para combinar resultados de vários algoritmos de classificação (por exemplo, BM25 e pesquisa semântica). Ele atribui uma pontuação a cada documento com base em sua posição nos métodos individuais, dando pontuações mais altas aos 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 pesquisa)
- k é uma constante (tipicamente 60)
- r(d) é a classificação do documento d no método de pesquisa 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 pesquisa 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, Classificação Semântica = Não Classificado
- Pontuação RRF = (1/60+3)= 0.01587
4. financeiro
- BM25 Rank = Não Classificado, Classificação Semântica = 2
- Pontuação RRF = (1/60+2)=0.01613
5. Ordenar por pontuação RRF
- sales_data (maior pontuação devido à classificação superior na pesquisa semântica).
- produtos (pontuação alta do BM25).
- pedidos (relevância inferior no geral).
- financeiro (sobreposição limitada).
Resultado final: [‘sales_data’, ‘produtos,’ ‘financeiro,’ ‘pedidos’]
Tabelas recuperadas usando mapeamento da Tabela de Palavras-chave 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: [“sales_data,” “produtos,” “financeiro”]
- Lógica de reclassificação
- Para cada tabela, calcular uma pontuação de relevância concatenando a consulta e a descrição da tabela.
- Ordenar por pontuação de relevância.
- Resultado: [“sales_data,” “produtos”]
Um pouco mais sobre a lógica de reclassificação: O
codificador cross–calcula um escore de relevância analisando a consulta concatenada e a descrição da tabela como um par de entrada único. Esse processo envolve:
- Entrada de 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 Pouco Supervisionado.
Suponha que você seja alguém que é proficiente em gerar consultas SQL. Gere uma consulta SQL para: Recuperar vendas totais agrupadas por produto nos últimos 7 dias.
Tabelas relevantes:
- sales_data: Contém colunas [vendas, data, id_produto].
- produtos: Contém colunas [id_produto, nome_produto].
Exemplo de 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 único agente com várias ferramentas para simplificar a modularidade e reduzir a complexidade, um framework de múltiplos agentes poderia ser explorado no futuro. Poderíamos possivelmente explorar o seguinte:
- Agentes dedicados para recuperação de contexto. Agentes separados para buscas semânticas e por palavras-chave.
- Agentes específicos de tarefa. Agentes especializados em validação ou otimização de SQL.
- Colaboração entre agentes. Utilizando um agente de coordenação para gerenciar a delegação de tarefas.
Essa abordagem poderia melhorar a escalabilidade e permitir fluxos de trabalho mais sofisticados, especialmente em implementações de nível empresarial.
Conclusão
O RAG Agentic para aplicações de texto-para-SQL oferece uma abordagem escalável e modular para resolver tarefas de consulta estruturada. Ao incorporar pesquisa híbrida, reclassificação, solicitação de poucas amostras e construção dinâmica de prompts dentro de um framework de agente único, esse sistema garante precisão, transparência e extensibilidade. Esse 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