paint-brush
IA para Gestão do Conhecimento: Iterando em RAG com a Arquitetura QE-RAGpor@shanglun
4,222 leituras
4,222 leituras

IA para Gestão do Conhecimento: Iterando em RAG com a Arquitetura QE-RAG

por Shanglun Wang27m2023/09/12
Read on Terminal Reader

Muito longo; Para ler

Retrieval-Augmented Generation, ou RAG, é uma arquitetura popular para o desenvolvimento de aplicativos LLM poderosos. No entanto, a arquitetura atual tem algumas limitações reais. Percorreremos a construção de um aplicativo RAG e, em seguida, veremos como podemos melhorá-lo usando uma nova arquitetura chamada QE-RAG
featured image - IA para Gestão do Conhecimento: Iterando em RAG com a Arquitetura QE-RAG
Shanglun Wang HackerNoon profile picture


À medida que a revolução LLM começa a tomar forma, o entusiasmo dá lugar ao desenvolvimento comercial. À medida que a onda inicial de entusiasmo diminui, a IA generativa não é mais vista como uma caixa preta onisciente, mas mais como uma ferramenta constituinte, embora extremamente poderosa, do arsenal de um engenheiro. Como resultado, os empreendedores e tecnólogos têm agora um conjunto cada vez mais maduro de ferramentas e técnicas para desenvolver aplicações LLM.


Um dos casos de uso mais interessantes para LLMs tem sido na área de gestão do conhecimento. LLMs especializados, baseados na tecnologia GPT da OpenAI ou em modelos de código aberto como LLaMa 2 e Flan-T5, estão sendo usados de maneiras inteligentes para gerenciar grandes quantidades de dados. Onde anteriormente as organizações com grandes conjuntos de dados de texto dependiam de técnicas de pesquisa de texto, como correspondência difusa ou indexação de texto completo, agora elas têm acesso a um sistema poderoso que pode não apenas encontrar as informações, mas também resumi-las de maneira eficiente em termos de tempo e de fácil leitura. moda.


Dentro deste caso de uso, a arquitetura de geração aumentada de recuperação , ou RAG, emergiu como uma arquitetura de destaque com enorme flexibilidade e desempenho. Com essa arquitetura, as organizações podem indexar rapidamente um corpo de trabalho, realizar consultas semânticas nele e gerar respostas informativas e convincentes para consultas definidas pelo usuário com base no corpus. Várias empresas e serviços surgiram para apoiar implementações da arquitetura RAG, destacando o seu poder de permanência.


Por mais eficaz que o RAG possa ser, esta arquitetura também tem várias limitações reais. Neste artigo exploraremos a arquitetura RAG, identificaremos suas limitações e proporemos uma arquitetura aprimorada para resolver essas limitações.


Tal como acontece com todos os outros artigos, procuro me conectar com outros tecnólogos e entusiastas de IA. Se você tiver ideias sobre como essa arquitetura pode ser melhorada ou tiver ideias sobre IA que gostaria de discutir, não hesite em entrar em contato ! Você pode me encontrar no Github ou LinkedIn, os links estão no meu perfil e também no final deste artigo.


Visão geral do conteúdo

  • Arquitetura de geração aumentada de recuperação (RAG)
  • Limitações da Arquitetura RAG
  • Propondo QE-RAG, ou RAG aprimorado por perguntas
  • Conclusão


Arquitetura de geração aumentada de recuperação (RAG)

Com nomes como RAG, Flan e LLaMa, é provável que a comunidade de IA não ganhe prêmios por nomes futuristas e elegantes tão cedo. No entanto, a arquitetura RAG certamente merece um prêmio por sua combinação de duas técnicas extremamente poderosas disponibilizadas pelo desenvolvimento de LLMs – incorporação contextual de documentos e engenharia imediata.


Na sua forma mais simples, a arquitetura RAG é um sistema que usa pesquisa vetorial incorporada para encontrar a(s) parte(s) do corpus mais relevante(s) para uma pergunta, inserir a(s) parte(s) em um prompt e, em seguida, usar engenharia de prompt para garantir que o a resposta é baseada nos trechos fornecidos no prompt. Se tudo isso parece um pouco confuso, continue lendo porque explicarei cada componente por vez. Também incluirei código de exemplo para que você possa acompanhar.


O modelo de incorporação

Em primeiro lugar, um sistema RAG eficaz requer um modelo de incorporação poderoso. O modelo de incorporação transforma um documento de texto natural em uma série de números, ou um “vetor”, que representa aproximadamente o conteúdo semântico do documento. Supondo que o modelo de incorporação seja bom, você será capaz de comparar os valores semânticos de dois documentos diferentes e determinar se os dois documentos são semanticamente semelhantes usando aritmética vetorial.


Para ver isso em ação, cole o seguinte código em um arquivo Python e execute-o:


 import openai from openai.embeddings_utils import cosine_similarity openai.api_key = [YOUR KEY] EMBEDDING_MODEL = "text-embedding-ada-002" def get_cos_sim(input_1, input_2): embeds = openai.Embedding.create(model=EMBEDDING_MODEL, input=[input_1, input_2]) return cosine_similarity(embeds['data'][0]['embedding'], embeds['data'][1]['embedding']) print(get_cos_sim('Driving a car', 'William Shakespeare')) print(get_cos_sim('Driving a car', 'Riding a horse'))



O código acima gera os embeddings para as frases “Driving a car”, “William Shakespeare” e “Riding a horse” antes de compará-las entre si usando o algoritmo de similaridade de cosseno. Esperaríamos que a similaridade do cosseno fosse maior quando as frases fossem semanticamente semelhantes, então “Driving a car” e “Riding a horse” deveriam ser muito mais próximas, enquanto “Driving a car” e “William Shakespeare” deveriam ser diferentes.


Você verá que, de acordo com o modelo de incorporação da OpenAI, ada-002, a frase “dirigindo um carro” é 88% semelhante à frase “andar a cavalo” e 76% semelhante à frase “William Shakespeare”. Isso significa que o modelo de incorporação está funcionando conforme o esperado. Esta determinação de similaridade semântica é a base do sistema RAG.


A ideia de similaridade de cossenos é notavelmente robusta quando estendida a comparações de documentos muito maiores. Por exemplo, tomemos o poderoso monólogo de Macbeth, de Shakespeare, “ Amanhã, e amanhã, e amanhã ”:


 monologue = '''Tomorrow, and tomorrow, and tomorrow, Creeps in this petty pace from day to day, To the last syllable of recorded time; And all our yesterdays have lighted fools The way to dusty death. Out, out, brief candle! Life's but a walking shadow, a poor player, That struts and frets his hour upon the stage, And then is heard no more. It is a tale Told by an idiot, full of sound and fury, Signifying nothing.''' print(get_cos_sim(monologue, 'Riding a car')) print(get_cos_sim(monologue, 'The contemplation of mortality'))


Você verá que o monólogo é apenas 75% semelhante à ideia de “andar de carro” e 82% semelhante à ideia de “A contemplação da mortalidade”.


Mas não precisamos apenas comparar monólogos com ideias, podemos realmente comparar monólogos com perguntas. Por exemplo:


 get_cos_sim('''Tomorrow, and tomorrow, and tomorrow, Creeps in this petty pace from day to day, To the last syllable of recorded time; And all our yesterdays have lighted fools The way to dusty death. Out, out, brief candle! Life's but a walking shadow, a poor player, That struts and frets his hour upon the stage, And then is heard no more. It is a tale Told by an idiot, full of sound and fury, Signifying nothing.''', 'Which Shakespearean monologue contemplates mortality?') get_cos_sim('''Full of vexation come I, with complaint Against my child, my daughter Hermia. Stand forth, Demetrius. My noble lord, This man hath my consent to marry her. Stand forth, Lysander. And my gracious Duke, This man hath bewitch'd the bosom of my child. Thou, thou, Lysander, thou hast given her rhymes, And interchanged love-tokens with my child: Thou hast by moonlight at her window sung With feigning voice verses of feigning love, And stol'n the impression of her fantasy With bracelets of thy hair, rings, gauds, conceits, Knacks, trifles, nosegays, sweetmeats (messengers Of strong prevailment in unharden'd youth): With cunning hast thou filch'd my daughter's heart, Turn'd her obedience, which is due to me, To stubborn harshness. And, my gracious Duke, Be it so she will not here, before your Grace, Consent to marry with Demetrius, I beg the ancient privilege of Athens: As she is mine, I may dispose of her; Which shall be either to this gentleman, Or to her death, according to our law Immediately provided in that case.''', 'Which Shakespearean monologue contemplates mortality?')



Você deve ver que a incorporação mostra que o monólogo de Macbeth está muito mais próximo, contextualmente, da pergunta “Qual monólogo de Shakespeare contempla a mortalidade?” do que o monólogo de Egeu, que menciona a morte, mas não aborda diretamente o conceito de mortalidade.


A pesquisa vetorial

Agora que temos a incorporação, como podemos utilizá-la em nosso sistema RAG? Bem, suponha que quiséssemos dar ao nosso sistema RAG o conhecimento de todos os monólogos de Shakespeare para que ele possa responder a perguntas sobre Shakespeare. Nesse caso, baixaríamos todos os monólogos de Shakespeare e geraríamos os embeddings para eles. Se você estiver acompanhando, poderá gerar a incorporação assim:



 embedding = openai.Embedding.create(model=EMBEDDING_MODEL, input=[monologue])['data'][0]['embedding']


Assim que tivermos os embeddings, desejaremos armazená-los de uma forma que nos permita consultá-los e compará-los com um novo embedding. Normalmente nós os colocaríamos no que chamamos de Banco de Dados de Vetores , que é um armazenamento de dados especializado que permite comparações rápidas de dois vetores. No entanto, a menos que seu corpus seja extremamente grande, as comparações de força bruta são surpreendentemente toleráveis para a maioria dos casos de uso experimentais que não são de produção, onde o desempenho não é crítico.


Independentemente de você optar por usar um banco de dados ou não, você desejará construir um sistema que possa encontrar itens em seu corpus que melhor se adaptem à questão. Em nosso exemplo, desejaremos ser capazes de encontrar o monólogo mais relevante para a pergunta do usuário em questão. Você pode querer fazer algo como:



 monologues_embeddings = [ ['Tomorrow, and tomorrow, and tomorrow...', [...]], # text in the left position, embedding in the right position ['Full of vexation come I...', [...]], … # More monologues and their embeddings as you see fit. ] def lookup_most_relevant(question): embed = openai.Embedding.create(model=EMBEDDING_MODEL, input=[question])['data'][0]['embedding'] top_monologue = sorted(monologues_embeddings, key=lambda x: cosine_similarity(embed, x[1]), reverse=True)[0] return top_monologue lookup_most_relevant(“How does Macbeth evaluate his life when he is confronted with his mortality?“)


Se você executar este exemplo, deverá ver o monólogo de Macbeth sendo selecionado, com aproximadamente 82% de semelhança com a pergunta.


A Engenharia Prompt

A última etapa do modelo RAG é a engenharia imediata. No nosso caso, não é muito difícil. Agora que temos o monólogo em mãos, podemos construir um prompt simplesmente incorporando o monólogo em nossa consulta e fazendo a pergunta:


 completion = openai.ChatCompletion.create( model="gpt-4", messages=[ {"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": """ Consider the following monologue: Tomorrow, and tomorrow, and tomorrow, Creeps in this petty pace from day to day, To the last syllable of recorded time; And all our yesterdays have lighted fools The way to dusty death. Out, out, brief candle! Life's but a walking shadow, a poor player, That struts and frets his hour upon the stage, And then is heard no more. It is a tale Told by an idiot, full of sound and fury, Signifying nothing. How does Macbeth evaluate his life when he is confronted with his mortality? """}, ] )


Você deverá ver uma resposta como:


 In the well-known "Tomorrow and tomorrow and tomorrow" monologue from Shakespeare's Macbeth, the titular character plunges into existential despair when confronted with his impending death. Macbeth evaluates his life as hollow, futile, and meaningless when facing his mortality. He sees it as a "walking shadow" and himself as a "poor player," highlighting his view of life as a fleeting, contrived performance with no lasting substance or impact. The "brief candle" is a metaphor for his own mortality, suggesting that life's flame can be extinguished suddenly and unceremoniously. By stating "It is a tale told by an idiot, full of sound and fury, signifying nothing," Macbeth is expressing his belief that life, despite all its noise and action, is ultimately empty, absurd, and void of meaning. Overall, Macbeth's examination of his life is profoundly pessimistic, revealing his deep despair and cynicism.


É claro que este exemplo específico não é a demonstração mais poderosa da arquitetura RAG, uma vez que a maioria dos modelos GPT já conhece os monólogos de Shakespeare e foi treinado no grande conjunto de análises de Shakespeare publicamente na Internet. Na verdade, se você fizer esta mesma pergunta ao GPT-4 sem o monólogo incorporado, provavelmente obterá uma resposta muito boa, embora provavelmente não fará tantas referências de citação ao solilóquio. No entanto, deve ficar claro que, num ambiente comercial, esta técnica pode ser aplicada de forma cruzada a conjuntos de dados proprietários ou esotéricos que não são acessíveis às implementações GPT existentes.


Na verdade, os leitores familiarizados com meu artigo anterior, Construindo um Analisador de Documentos com ChatGPT, Google Cloud e Python , podem reconhecer que a última parte da técnica é muito semelhante à engenharia imediata que fiz naquele artigo. Partindo dessa ideia, podemos facilmente imaginar um sistema RAG construído com base em publicações do governo japonês (os dados de amostra desse artigo), que permitiria aos utilizadores pesquisar e fazer perguntas sobre a política económica japonesa. O sistema recuperaria rapidamente os documentos mais relevantes, resumi-los-ia e produziria uma resposta com base no profundo conhecimento específico do domínio, não disponível nos modelos básicos da GPT. Esse poder e simplicidade são exatamente os motivos pelos quais a arquitetura RAG está ganhando tanta força entre os desenvolvedores de LLM.


Agora que examinamos a arquitetura RAG, vamos explorar algumas das deficiências dessa arquitetura.


Limitações da Arquitetura RAG

Incorporando capacidade de depuração

Como muitos sistemas RAG dependem da incorporação de documentos e da pesquisa vetorial para conectar a pergunta e os documentos relevantes, todo o sistema costuma ser tão bom quanto o modelo de incorporação usado. O modelo de incorporação OpenAI é incrivelmente flexível e existem muitas técnicas para ajustar as incorporações. LLaMa, concorrente de código aberto da Meta para GPT, oferece modelos de incorporação com ajuste fino. No entanto, há um aspecto de caixa preta inevitável no modelo de incorporação. Isso é um tanto administrável ao comparar sequências de texto curtas quando se trata de comparar sequências curtas com documentos muito mais longos. Em nosso exemplo anterior, temos que ter um pouco de fé de que a pesquisa de incorporação é capaz de conectar “mortalidade” ao monólogo “amanhã, e amanhã, e amanhã”. Isto pode ser bastante desconfortável para cargas de trabalho onde a transparência e a capacidade de depuração são críticas.

Sobrecarga de contexto

Outra limitação do modelo RAG é a quantidade relativamente limitada de contexto que pode ser transmitida a ele. Como o modelo de incorporação requer contexto em nível de documento para funcionar bem, precisamos ter cuidado ao dividir o corpus para incorporação. O monólogo de Macbeth pode ter uma semelhança de 82% com a questão sobre a mortalidade, mas esse número cai para 78% quando você compara a questão com a incorporação das duas primeiras linhas do monólogo, ou seja, “Amanhã, e amanhã, e amanhã. Rasteja neste ritmo mesquinho dia a dia, Até a última sílaba do tempo registrado.”.


Como resultado, o contexto passado para o prompt RAG precisa ser bastante grande. Atualmente, os modelos GPT de maior contexto ainda estão limitados a 16.000 tokens, o que é bastante texto, mas quando você trabalha com longas transcrições de entrevistas ou artigos ricos em contexto, ficará limitado na quantidade de contexto que pode fornecer. no prompt de geração final.

Terminologia Nova

A limitação final do modelo RAG é a sua incapacidade de trabalhar com terminologia nova. Pessoas que trabalham em áreas específicas tendem a desenvolver terminologias e formas de falar exclusivas dessa área. Quando essas terminologias não estão presentes nos dados de treinamento do modelo de incorporação, o processo de pesquisa será prejudicado.


Por exemplo, o modelo de incorporação ada-002 pode não saber que a “Rust Programming Language” está relacionada ao “LLVM”. Na verdade, ele retorna uma similaridade de cosseno relativamente baixa de 78%. Isso significa que documentos que falam sobre LLVM podem não mostrar uma forte semelhança em uma consulta sobre Rust, mesmo que as duas ideias estejam intimamente relacionadas na vida real.


Normalmente, o problema da nova terminologia pode ser superado com alguma engenharia imediata, mas no contexto de uma pesquisa de incorporação, isso é relativamente difícil de fazer. O ajuste fino de um modelo de incorporação é, como mencionado anteriormente, possível, mas ensinar ao modelo de incorporação a nova terminologia em todos os contextos pode ser propenso a erros e demorado.

Propondo QE-RAG, ou RAG aprimorado por perguntas

Dadas estas limitações, gostaria de propor uma arquitetura modificada que permitiria a implementação de uma nova classe de sistemas RAG que contornasse muitas das limitações descritas acima. A ideia se baseia em fazer pesquisas vetoriais sobre perguntas frequentes, além do corpus, e utilizar um LLM para pré-processar o corpus no contexto das perguntas. Se esse processo parecer complicado, não se preocupe, abordaremos os detalhes da implementação nesta seção junto com exemplos de código que você pode usar para acompanhar.


Uma coisa a notar é que o QE-RAG deve ser executado junto com uma implementação básica do RAG para que possa recorrer a outra implementação, se necessário. À medida que a implementação amadurece, ela deverá precisar cada vez menos do substituto, mas o QE-RAG ainda pretende ser um aprimoramento, em vez de uma substituição, da arquitetura vanilla RAG.


A arquitetura

Os traços gerais da arquitetura QE-RAG são os seguintes:

  1. Crie um banco de dados vetorial de perguntas que podem ou provavelmente serão feitas sobre o corpus.
  2. Pré-processe e resuma o corpus em relação às questões no banco de dados vetorial.
  3. Quando uma consulta do usuário chegar, compare-a com as perguntas no banco de dados vetorial.
  4. Se uma pergunta no banco de dados for muito semelhante à consulta do usuário, recupere a versão do corpus que está resumida para responder à pergunta.
  5. Use o corpus resumido para responder à pergunta do usuário.
  6. Se nenhuma pergunta no banco de dados for altamente semelhante à consulta do usuário, volte para uma implementação RAG simples.


Vamos examinar cada parte por vez.


Incorporações de perguntas

A arquitetura começa, assim como o vanilla RAG, com uma incorporação e um banco de dados vetorial. Porém, em vez de incorporar os documentos, iremos incorporar uma série de perguntas.

Para ilustrar isso, suponha que estejamos tentando construir um LLM especialista em Shakespeare. Podemos querer que ele responda a perguntas como:


 questions = [ "How does political power shape the way characters interact in Shakespeare's plays?", "How does Shakespeare use supernatural elements in his plays?", "How does Shakespeare explore the ideas of death and mortality in his plays?", "How does Shakespeare explore the idea of free will in his plays?" ]


Queremos criar uma incorporação para eles assim, e salvá-los ou usá-los posteriormente:


 questions_embed = openai.Embedding.create(model=EMBEDDING_MODEL, input=questions)


Pré-processamento e resumo

Agora que temos as perguntas, vamos fazer o download e resumir o corpus. Para este exemplo, baixaremos as versões HTML de Macbeth e Hamlet:


 import openai import os import requests from bs4 import BeautifulSoup plays = { 'shakespeare_macbeth': 'https://www.gutenberg.org/cache/epub/1533/pg1533-images.html', 'shakespeare_hamlet': 'https://www.gutenberg.org/cache/epub/1524/pg1524-images.html', } if not os.path.exists('training_plays'): os.mkdir('training_plays') for name, url in plays.items(): print(name) file_path = os.path.join('training_plays', '%s.txt' % name) if not os.path.exists(file_path): res = requests.get(url) with open(file_path, 'w') as fp_write: fp_write.write(res.text)


Depois processamos as peças em cenas, usando as tags HTML como guia:


 with open(os.path.join('training_plays', 'shakespeare_hamlet.txt')) as fp_file: soup = BeautifulSoup(''.join(fp_file.readlines())) headers = soup.find_all('div', {'class': 'chapter'})[1:] scenes = [] for header in headers: cur_act = None cur_scene = None lines = [] for i in header.find_all('h2')[0].parent.find_all(): if i.name == 'h2': print(i.text) cur_act = i.text elif i.name == 'h3': print('\t', i.text.replace('\n', ' ')) if cur_scene is not None: scenes.append({ 'act': cur_act, 'scene': cur_scene, 'lines': lines }) lines = [] cur_scene = i.text elif (i.text != '' and not i.text.strip('\n').startswith('ACT') and not i.text.strip('\n').startswith('SCENE') ): lines.append(i.text)


E aqui está a parte que torna o QE-RAG único, em vez de criar embeddings para as cenas específicas, criamos resumos para elas, direcionados para cada uma das questões:


 def summarize_for_question(text, question, location): completion = openai.ChatCompletion.create( model="gpt-3.5-turbo-16k", messages=[ {"role": "system", "content": "You are a literature assistant that provides helpful summaries."}, {"role": "user", "content": """Is the following excerpt from %s relevant to the following question? %s === %s === If so, summarize the sections that are relevant. Include references to specific passages that would be useful. If not, simply say: \"nothing is relevant\" without additional explanation""" % ( location, question, text )}, ] ) return completion


Esta função pede ao ChatGPT para fazer 2 coisas: 1) identificar se a passagem é realmente útil para responder à pergunta em questão e 2) resumir as partes da cena que são úteis para responder à pergunta.


Se você tentar esta função com algumas cenas cruciais de Macbeth ou Hamlet, verá que GPT3.5 é muito bom para identificar se uma cena é relevante para a questão, e o resumo será um pouco mais curto do que a cena em si. Isso torna muito mais fácil incorporar posteriormente na etapa imediata de engenharia.


Agora podemos fazer isso para todas as cenas.


 for scene in scenes: scene_text = ''.join(scene['lines']) question_summaries = {} for question in questions: completion = summarize_for_question(''.join(scene['lines']), question, "Shakespeare's Hamlet") question_summaries[question] = completion.choices[0].message['content'] scene['question_summaries'] = question_summaries


Nas cargas de trabalho de produção, colocaríamos os resumos em um banco de dados, mas, em nosso caso, apenas os gravaremos como um arquivo JSON no disco.


Pesquisa vetorial em duas etapas

Agora, suponha que recebamos uma pergunta do usuário como a abaixo:


 user_question = "How do Shakespearean characters deal with the concept of death?"


Como no vanilla RAG, desejaremos criar uma incorporação para a pergunta:


 uq_embed = openai.Embedding.create(model=EMBEDDING_MODEL, input=[user_question])['data'][0]['embedding']


Em um RAG vanilla, compararíamos a incorporação da pergunta do usuário com a incorporação das cenas de Shakespeare, mas no QE-RAG, comparamos com a incorporação das perguntas:


 print([cosine_similarity(uq_embed, q) for q in question_embed])


Vemos que a pesquisa vetorial identificou (corretamente) a questão 3 como a questão mais relevante. Agora, recuperamos os dados resumidos da pergunta 3:


 relevant_texts = [] for scene in hamlet + macbeth: # hamlet and macbeth are the scene lists from the above code if "NOTHING IS RELEVANT" not in scene['question_summaries'][questions[2]].upper() and \ "NOTHING IN THIS EXCERPT" not in scene['question_summaries'][questions[2]].upper() and \ 'NOTHING FROM THIS EXCERPT' not in scene['question_summaries'][questions[2]].upper() and \ "NOT DIRECTLY ADDRESSED" not in scene['question_summaries'][questions[2]].upper(): relevant_texts.append(scene['question_summaries'][questions[2]])


Observe que, como o resumo GPT não é determinístico, você pode obter diversas sequências diferentes para indicar que uma cena não é relevante para a questão em questão. A chave é colocar apenas os trechos relevantes na lista de resumos relevantes.


Neste estágio, podemos fazer uma pesquisa vetorial de segundo nível para incluir apenas os resumos mais relevantes em nosso prompt, mas dado o tamanho do nosso corpus, podemos simplesmente usar toda a lista relevant_texts em nosso prompt.


Engenharia imediata

Agora que temos todos os trechos, podemos construir o prompt assim:

 completion = openai.ChatCompletion.create( model="gpt-3.5-turbo-16k", messages=[ {"role": "system", "content": "You are a literature assistant that answers questions based on excerpts."}, {"role": "user", "content": """Consider the following passages: %s ==== Answer this question: %s Be detailed and complete in your analysis. Make references to specific passages where helpful. Answer only based on passages provided. """ % ( '\n===\n'.join(relevant_texts), user_question )}, ] )


Deixe a chamada da API ser executada e você verá algo como abaixo:


 Based on the passages provided, Shakespearean characters deal with the concept of death in various ways. In Hamlet, for example, the characters contemplate the nature of death and its implications. Hamlet himself reflects on the brevity of life and the corrupting influence of vices. He questions the purpose of existence and contemplates suicide as a release from the burdens of life. In the passage, Hamlet says, "To die, to sleep— / To sleep, perchance to dream. Ay, there's the rub, / For in that sleep of death what dreams may come" (Lines 65-67). This shows his contemplation of the afterlife and the uncertainty surrounding it. In Macbeth, characters are confronted with the consequences of their actions and the fragility of life. Lady Macbeth, for instance, experiences guilt and remorse for her involvement in the murder of King Duncan. She is haunted by visions of blood on her hands and expresses the desire to escape the consequences of her actions. In the passage, Lady Macbeth says, "Out, damned spot! Out, I say!" (Line 30). This demonstrates her struggle to cope with the guilt and the psychological toll it takes on her. Furthermore, both Macbeth and Lady Macbeth reflect on the inevitability and the consequences of their actions. In the passage, Macbeth laments the decay of life and the loss of purpose and meaning. He reflects on the transitory nature of existence and the insignificance of worldly achievements. Macbeth states, "Life's but a walking shadow, a poor player / That struts and frets his hour upon the stage" (Lines 217-218). This shows his acceptance of mortality and his realization that material possessions and power do not bring true fulfillment. In summary, Shakespearean characters deal with the concept of death in various ways. They contemplate the nature of life and the afterlife, struggle with guilt and remorse, and question the meaning and purpose of their existence. Shakespeare's exploration of death and mortality through his characters provides insight into the complexities of human nature and the universal human experience of confronting one's mortality.


O que é um resumo muito bom, inspirado em muitas partes diferentes de Hamlet e Macbeth.

Para efeito de comparação, criei embeddings para cenas inteiras e executei uma pesquisa RAG simples usando o mesmo prompt, encontrando as N cenas mais relevantes até atingir 10.000 tokens


 relevant_texts = [] for i in range(10): if len(encoding.encode('\n===\n'.join(relevant_texts))) > 10000: break relevant_texts.append(''.join(macbeth[sorted( [(idx, cosine_similarity(uq_embed, q)) for idx, q in enumerate(macbeth_embed)], key=lambda x: x[1], reverse=True )[i][0]]['lines'])) relevant_texts.append(''.join(hamlet[sorted( [(idx, cosine_similarity(uq_embed, q)) for idx, q in enumerate(hamlet_embed)], key=lambda x: x[1], reverse=True )[i][0]]['lines']))


Se você inserir isso no prompt, deverá ver algo como abaixo:


 Based on the provided passages, Shakespearean characters deal with the concept of death in various ways. In Hamlet, the characters reflect on death and mortality extensively. Hamlet himself contemplates suicide, expressing his desire to end his suffering and questioning the purpose of life. He says, "To be, or not to be: that is the question" (Hamlet, Act III, Scene I, Lines 56). This soliloquy reveals his struggle with the concept of death and his contemplation of the consequences of choosing either life or death. Additionally, in the same play, the appearance of the Ghost prompts discussions about death. The characters question the meaning and significance of encountering the dead, and Hamlet grapples with the idea of mortality in relation to his father's death. He reflects, "Oh God! a beast that wants discourse of reason / Would have mourn'd longer" (Hamlet, Act I, Scene II, Lines 152-153). This passage highlights Hamlet's grief and his pondering of the brevity of life and the transitory nature of existence. In Macbeth, the characters also confront death and its consequences. Macbeth, after killing King Duncan, reflects on the significance of the act and its implications. He says, "Will all great Neptune's ocean wash this blood Clean from my hand? No, this my hand will rather The multitudinous seas incarnadine" (Macbeth, Act II, Scene II, Lines 77-79). This passage showcases Macbeth's awareness of the guilt associated with murder and his realization that the blood on his hands cannot be cleansed. Lady Macbeth, on the other hand, shows a different perspective on death. She suppresses her guilt and tries to detach herself from the consequences of their actions. She states, "A little water clears us of this deed" (Macbeth, Act II, Scene II, Line 69). This passage reveals her denial and determination to move forward without remorse. In conclusion, Shakespearean characters deal with the concept of death in various ways. Some contemplate the meaning of life and question the purpose of existence, while others grapple with the guilt and consequences that come with taking a life. Shakespeare explores the complexities of death and mortality through his characters' introspection and their different responses to the inevitability of death.


Esta é uma análise muito convincente, mas não aborda muitas das passagens mais importantes de Hamlet e Macbeth. Como você pode ver, o QE-RAG tem uma vantagem distinta por ser capaz de incorporar um contexto mais relevante do que um sistema RAG padrão.


O exemplo acima, entretanto, não demonstra outra vantagem do QE-RAG, que é a capacidade de dar ao desenvolvedor melhor controle do processo de incorporação. Para ver como o QE-RAG consegue isto, vejamos uma extensão deste problema – lidar com uma nova terminologia.


Estendendo QE-RAG para nova terminologia

Onde o QE-RAG realmente brilha é quando você introduz uma nova terminologia. Por exemplo, suponha que você esteja introduzindo um novo conceito, como a palavra japonesa “zetsubou”, que é um termo que fica entre o desespero e a desesperança, transmitindo especificamente uma rendição às circunstâncias. Não é tão imediatamente catastrófico como o conceito inglês de desespero, mas muito mais sobre a aquiescência às coisas desagradáveis que estão acontecendo.


Suponha que queiramos responder a uma pergunta como:


user_question = "How do Shakespearean characters cope with Zetsubou?"


Com o vanilla RAG, faríamos uma pesquisa de embeddings e, em seguida, adicionaríamos um explicador na etapa final de engenharia imediata:


 relevant_texts = [] for i in range(10): if len(encoding.encode('\n===\n'.join(relevant_texts))) > 10000: break relevant_texts.append(''.join(macbeth[sorted( [(idx, cosine_similarity(uq_embed, q)) for idx, q in enumerate(macbeth_embed)], key=lambda x: x[1], reverse=True )[i][0]]['lines'])) relevant_texts.append(''.join(hamlet[sorted( [(idx, cosine_similarity(uq_embed, q)) for idx, q in enumerate(hamlet_embed)], key=lambda x: x[1], reverse=True )[i][0]]['lines'])) completion = openai.ChatCompletion.create( model="gpt-3.5-turbo-16k", messages=[ {"role": "system", "content": "You are a literature assistant that answers questions based on excerpts."}, {"role": "user", "content": """Zetsubou is the concept of hopelessness and despair, combined with a surrender to whim of one's circumstances. Consider the following passages: %s ==== Answer this question: %s Be detailed and complete in your analysis. Make references to specific passages where helpful. Answer only based on passages provided. """ % ( '\n===\n'.join(relevant_texts), user_question )}, ] )


O resultado é uma resposta muito bem escrita e convincente, mas um pouco exagerada, focada em algumas cenas de Hamlet. Macbeth não é mencionado nesta resposta, porque nenhuma das cenas passou na pesquisa de incorporação. Ao observar os embeddings, fica muito claro que o significado semântico de “zetsubou” não foi capturado adequadamente e, portanto, textos relevantes não puderam ser recuperados dele.


No QE-RAG, podemos injetar a definição do novo termo na fase de resumo, melhorando drasticamente a qualidade do texto acessível pelo sistema:


 def summarize_for_question(text, question, location, context=''): completion = openai.ChatCompletion.create( model="gpt-3.5-turbo-16k", messages=[ {"role": "system", "content": "You are a literature assistant that provides helpful summaries."}, {"role": "user", "content": """%s Is the following excerpt from %s relevant to the following question? %s === %s === If so, summarize the sections that are relevant. Include references to specific passages that would be useful. If not, simply say: \"nothing is relevant\" without additional explanation""" % ( context, location, question, text )}, ] ) return completion questions = [ "How do characters deal with Zetsubou in Shakespearean plays?" ] summarize_for_question(''.join(scene['lines']), questions[0], "Shakespeare's Macbeth", "Zetsubou is the concept of hopelessness and despair, combined with a surrender to whim of one's circumstances." )


Use este prompt de resumo com as passagens e você verá resumos extremamente precisos e contextuais. Esses resumos podem então ser usados no QE-RAG subsequente.


Além disso, a incorporação da questão será extremamente semelhante, pois embora ada-002 não saiba o significado preciso de “zetsubou”, a presença desta palavra em ambas as questões torna a incorporação da questão extremamente semelhante.


Portanto, usando o QE-RAG para responder à pergunta original sobre Zetsubou, obtemos a seguinte resposta:


 Based on the passages provided, Shakespearean characters cope with Zetsubou, or hopelessness and despair, in different ways. In "Macbeth," Lady Macbeth copes with Zetsubou through guilt and anguish. She is consumed by the guilt of her and her husband's actions, and this guilt manifests in her sleepwalking and attempts to wash her hands of imagined blood stains. This can be seen in her utterances of "Out, damned spot!" and her preoccupation with the blood on her hands. Lady Macbeth's coping mechanism is to face the consequences of her actions head-on, even if it means descending into madness. The Doctor and Gentlewoman observe her behavior and acknowledge that her condition requires divine intervention. In "Hamlet," the excerpt does not provide specific passages or examples of characters coping with Zetsubou. However, it does address Hamlet's inner turmoil and struggle to cope with his circumstances. Hamlet's state of mind, confusion, and madness are a reflection of despair. The characters in this scene are attempting to understand Hamlet's condition and find ways to deal with it, but the excerpt does not provide details on how they cope with Zetsubou. In "Macbeth," Macbeth himself copes with Zetsubou by surrendering to his dire circumstances. He becomes desensitized to fear and horror, indicating a kind of resignation to his grim circumstances. He acknowledges that there is no escape from his fate and chooses to face it head-on, even though he knows it will lead to his downfall. Macbeth's reaction to the news of his wife's death and his subsequent soliloquy reflect a deep despair and hopelessness. He sees life as meaningless, a "tale Told by an idiot, full of sound and fury, Signifying nothing." Macbeth's surrender to his circumstances and his belief in the futility of life demonstrate his coping mechanism for Zetsubou. In "Macbeth," Lady Macduff copes with Zetsubou by expressing her feelings of despair and hopelessness. She feels betrayed and abandoned by her husband, who has fled and left their family behind. She questions his actions and believes that they show a lack of love and wisdom. Lady Macduff experiences a sense of abandonment, which aligns with the concept of Zetsubou. She tries to make her son understand the severity of their circumstances, but he questions the definition of a traitor. Lady Macduff ultimately meets a tragic end, indicating that her coping mechanism for Zetsubou is not successful. In summary, Shakespearean characters cope with Zetsubou in different ways. Lady Macbeth copes through guilt and faces the consequences of her actions, even if it means descending into madness. Macbeth copes through surrendering to his dire circumstances and embracing his fate. Lady Macduff copes through expressing her feelings of despair and hopelessness but ultimately meets a tragic end. These characters' coping mechanisms reflect different responses to Zetsubou in Shakespearean plays


Os resultados ainda apresentam algumas dificuldades com este conceito, mas deve ser evidente que o QE-RAG é muito mais capaz de se envolver com a essência da questão e recorrer a uma variedade de fontes.

E com isso implementamos o QE-RAG e demonstramos seu caso de uso!


Conclusão

No artigo de hoje, examinamos a cada vez mais popular arquitetura RAG e suas limitações. Em seguida, estendemos a arquitetura RAG com uma nova arquitetura chamada QE-RAG, que busca utilizar mais plenamente os recursos de grandes modelos de linguagem. Além de maior precisão e acesso contextual, o QE-RAG permite que todo o sistema cresça à medida que interage com os usuários e se torna mais familiarizado com os tipos de perguntas que estão sendo feitas, permitindo que as empresas desenvolvam propriedade intelectual exclusiva com base em código aberto. ou LLMs disponíveis comercialmente .

É claro que, como ideia experimental, o QE-RAG não é perfeito. Se você tiver ideias sobre como essa arquitetura pode ser melhorada ou simplesmente quiser discutir sobre tecnologias LLM, não hesite em me enviar uma mensagem através do meu Github ou LinkedIn .