paint-brush
IA pour la gestion des connaissances : itérer sur RAG avec l'architecture QE-RAGpar@shanglun
4,226 lectures
4,226 lectures

IA pour la gestion des connaissances : itérer sur RAG avec l'architecture QE-RAG

par Shanglun Wang27m2023/09/12
Read on Terminal Reader

Trop long; Pour lire

La génération augmentée par récupération, ou RAG, est une architecture populaire pour développer de puissantes applications LLM. Cependant, l’architecture actuelle présente de réelles limites. Nous passons en revue la création d'une application RAG, puis examinons comment nous pouvons l'améliorer à l'aide d'une nouvelle architecture appelée QE-RAG.
featured image - IA pour la gestion des connaissances : itérer sur RAG avec l'architecture QE-RAG
Shanglun Wang HackerNoon profile picture


Alors que la révolution LLM commence à prendre forme, le battage médiatique a cédé la place au développement commercial. Alors que la vague initiale d’enthousiasme s’apaise, l’IA générative n’est plus considérée comme une boîte noire omnisciente, mais plutôt comme un outil constitutif, bien qu’extrêmement puissant, de l’arsenal d’un ingénieur. En conséquence, les entrepreneurs et les technologues disposent désormais d’un ensemble d’outils et de techniques de plus en plus matures avec lesquels développer des applications LLM.


L’un des cas d’utilisation les plus intéressants des LLM concerne le domaine de la gestion des connaissances. Les LLM spécialisés, basés soit sur la technologie GPT d'OpenAI, soit sur des modèles open source comme LLaMa 2 et Flan-T5, sont utilisés de manière intelligente pour gérer de grandes quantités de données. Là où auparavant les organisations disposant de grands ensembles de données textuelles devaient s'appuyer sur des techniques de recherche de texte telles que la correspondance floue ou l'indexation de texte intégral, elles ont désormais accès à un système puissant qui peut non seulement trouver l'information, mais aussi la résumer de manière rapide et conviviale. mode.


Dans ce cas d'utilisation, l'architecture de génération augmentée par récupération , ou RAG, est apparue comme une architecture remarquable dotée d'une flexibilité et de performances énormes. Grâce à cette architecture, les organisations peuvent rapidement indexer un ensemble de travaux, y effectuer des requêtes sémantiques et générer des réponses informatives et convaincantes aux requêtes définies par l'utilisateur basées sur le corpus. Plusieurs entreprises et services ont vu le jour pour soutenir la mise en œuvre de l'architecture RAG, soulignant sa pérennité.


Aussi efficace que puisse être RAG, cette architecture présente également plusieurs limites réelles. Dans cet article, nous explorerons l'architecture RAG, identifierons ses limites et proposerons une architecture améliorée pour résoudre ces limitations.


Comme pour tous les autres articles, je cherche à entrer en contact avec d’autres technologues et passionnés d’IA. Si vous avez des idées sur la façon dont cette architecture peut être améliorée, ou si vous avez des idées sur l'IA dont vous aimeriez discuter, n'hésitez pas à nous contacter ! Vous pouvez me trouver sur Github ou LinkedIn, les liens sont dans mon profil ainsi qu'en bas de cet article.


Aperçu du contenu

  • Architecture de génération augmentée de récupération (RAG)
  • Limites de l'architecture RAG
  • Proposer le QE-RAG, ou Question-Enhanced RAG
  • Conclusion


Architecture de génération augmentée de récupération (RAG)

Avec des noms comme RAG, Flan et LLaMa, la communauté de l’IA ne remportera probablement pas de sitôt des prix pour des noms futuristes et élégants. Cependant, l'architecture RAG mérite certainement une récompense pour sa combinaison de deux techniques extrêmement puissantes rendues disponibles par le développement des LLM : l'intégration de documents contextuels et l'ingénierie rapide.


Dans sa forme la plus simple, l'architecture RAG est un système qui utilise la recherche vectorielle intégrée pour trouver la ou les parties du corpus les plus pertinentes pour une question, insère la ou les parties dans une invite, puis utilise l'ingénierie des invites pour garantir que la ou les parties sont les plus pertinentes. la réponse est basée sur les extraits donnés dans l’invite. Si tout cela vous semble un peu déroutant, continuez à lire car je vais expliquer chaque composant tour à tour. J'inclurai également un exemple de code afin que vous puissiez suivre.


Le modèle d'intégration

Avant tout, un système RAG efficace nécessite un modèle d’intégration puissant. Le modèle d'intégration transforme un document texte naturel en une série de nombres, ou un « vecteur », qui représente grossièrement le contenu sémantique du document. En supposant que le modèle d'intégration soit bon, vous pourrez comparer les valeurs sémantiques de deux documents différents et déterminer si les deux documents sont sémantiquement similaires à l'aide de l'arithmétique vectorielle.


Pour voir cela en action, collez le code suivant dans un fichier Python et exécutez-le :


 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'))



Le code ci-dessus génère les intégrations pour les expressions « Conduire une voiture », « William Shakespeare » et « Monter à cheval » avant de les comparer les unes aux autres à l'aide de l'algorithme de similarité cosinus. Nous nous attendrions à ce que la similarité cosinus soit plus élevée lorsque les phrases sont sémantiquement similaires, donc « Conduire une voiture » et « Monter à cheval » devraient être beaucoup plus proches, tandis que « Conduire une voiture » et « William Shakespeare » devraient être différents.


Vous devriez voir que, selon le modèle d'intégration d'OpenAI, ada-002, l'expression « conduire une voiture » est similaire à 88 % à l'expression « monter à cheval » et à 76 % à l'expression « William Shakespeare ». Cela signifie que le modèle d'intégration fonctionne comme prévu. Cette détermination de similarité sémantique est le fondement du système RAG.


L’idée de similarité cosinusoïdale est remarquablement robuste lorsque vous l’étendez à des comparaisons de documents beaucoup plus volumineux. Par exemple, prenons le puissant monologue de Macbeth de Shakespeare, « Demain, et demain, et demain » :


 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'))


Vous devriez voir que le monologue n'est similaire qu'à 75 % à l'idée de « conduire une voiture » et à 82 % à l'idée de « La contemplation de la mortalité ».


Mais nous ne devons pas seulement comparer les monologues avec des idées, nous pouvons réellement comparer les monologues avec des questions. Par exemple:


 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?')



Vous devriez voir que l'intégration montre que le monologue de Macbeth est beaucoup plus proche, contextuellement, de la question « Quel monologue shakespearien envisage la mortalité ? » que le monologue d'Egeus, qui mentionne la mort mais ne s'attaque pas directement au concept de mortalité.


La recherche de vecteurs

Maintenant que nous avons l'intégration, comment pouvons-nous l'utiliser dans notre système RAG ? Eh bien, supposons que nous voulions donner à notre système RAG la connaissance de tous les monologues de Shakespeare afin qu'il puisse répondre aux questions sur Shakespeare. Dans ce cas, nous téléchargerions tous les monologues de Shakespeare et générerions les intégrations correspondantes. Si vous suivez, vous pouvez générer l'intégration comme ceci :



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


Une fois que nous aurons les intégrations, nous souhaiterons les stocker de manière à nous permettre de les interroger et de les comparer avec une nouvelle intégration. Normalement, nous les placerions dans ce qu'on appelle une base de données vectorielles , qui est un magasin de données spécialisé qui permet des comparaisons rapides de deux vecteurs. Cependant, à moins que votre corpus ne soit extrêmement volumineux, les comparaisons par force brute sont étonnamment tolérables pour la plupart des cas d'utilisation expérimentaux hors production où les performances ne sont pas critiques.


Que vous choisissiez ou non d'utiliser une base de données, vous souhaiterez créer un système capable de trouver les éléments de votre corpus qui correspondent le mieux à la question. Dans notre exemple, nous voudrons avoir la possibilité de trouver le monologue le plus pertinent par rapport à la question de l'utilisateur en question. Vous voudrez peut-être faire quelque chose comme :



 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?“)


Si vous exécutez cet exemple, vous devriez voir le monologue de Macbeth sélectionné, avec une similarité d'environ 82 % avec la question.


L'ingénierie rapide

La dernière étape du modèle RAG est l’ingénierie rapide. Dans notre cas, ce n'est pas trop difficile. Maintenant que nous avons le monologue à portée de main, nous pouvons construire une invite en intégrant simplement le monologue dans notre requête, puis en posant la question :


 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? """}, ] )


Vous devriez voir une réponse comme :


 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.


Bien entendu, cet exemple particulier n'est pas la démonstration la plus puissante de l'architecture RAG, puisque la plupart des modèles GPT connaissent déjà les monologues de Shakespeare et ont été formés sur le vaste corpus d'analyses de Shakespeare publiées publiquement sur Internet. En fait, si vous posez cette question exacte à GPT-4 sans le monologue intégré, vous obtiendrez probablement une très bonne réponse, même si elle ne fera probablement pas autant de références aux citations du monologue. Cependant, il devrait être évident que, dans un contexte commercial, cette technique peut être appliquée de manière croisée à des ensembles de données propriétaires ou ésotériques qui ne sont pas accessibles aux implémentations GPT existantes.


En fait, les lecteurs qui connaissent mon article précédent, Créer un analyseur de documents avec ChatGPT, Google Cloud et Python , reconnaîtront peut-être que la dernière partie de la technique est très similaire à l'ingénierie d'invite que j'ai réalisée dans cet article. En partant de cette idée, nous pouvons très facilement imaginer un système RAG construit sur les publications du gouvernement japonais (les exemples de données de cet article), qui permettrait aux utilisateurs de rechercher et de poser des questions sur la politique économique japonaise. Le système récupérerait rapidement les documents les plus pertinents, les résumerait et produirait une réponse basée sur des connaissances approfondies spécifiques au domaine non disponibles pour les modèles GPT de base. Cette puissance et cette simplicité sont précisément la raison pour laquelle l'architecture RAG suscite beaucoup d'intérêt parmi les développeurs LLM.


Maintenant que nous avons passé en revue l'architecture RAG, explorons certaines des lacunes de cette architecture.


Limites de l'architecture RAG

Intégration de la débogage

Étant donné que de nombreux systèmes RAG s'appuient sur l'intégration de documents et la recherche vectorielle pour relier la question et les documents pertinents, l'ensemble du système est souvent aussi performant que le modèle d'intégration utilisé. Le modèle d'intégration OpenAI est incroyablement flexible et il existe de nombreuses techniques pour ajuster les intégrations. LLaMa, le concurrent open source de Meta à GPT, propose des modèles d'intégration affinables. Cependant, il existe un aspect inévitable de boîte noire dans le modèle d’intégration. Ceci est quelque peu gérable lors de la comparaison de chaînes de texte courtes lorsqu'il s'agit de comparer des chaînes courtes avec des documents beaucoup plus longs. Dans notre exemple précédent, nous devons faire un léger acte de foi sur le fait que la recherche intégrée est capable de relier la « mortalité » au monologue « demain, et demain, et demain ». Cela peut être assez inconfortable pour les charges de travail où la transparence et la possibilité de débogage sont essentielles.

Surcharge de contexte

Une autre limitation du modèle RAG est la quantité relativement limitée de contexte qui peut lui être transmis. Étant donné que le modèle d'intégration nécessite un contexte au niveau du document pour fonctionner correctement, nous devons être prudents lorsque nous découpons le corpus à intégrer. Le monologue de Macbeth peut avoir une similarité de 82 % avec la question sur la mortalité, mais ce chiffre descend à 78 % si l'on compare la question à l'intégration des deux premières lignes du monologue, c'est-à-dire « Demain, et demain, et demain. Se glisse dans ce petit rythme de jour en jour, Jusqu'à la dernière syllabe du temps enregistré.".


Par conséquent, le contexte transmis à l’invite RAG doit être plutôt volumineux. Actuellement, les modèles GPT les plus contextuels sont encore limités à 16 000 jetons, ce qui représente beaucoup de texte, mais lorsque vous travaillez avec de longues transcriptions d'entretiens ou des articles riches en contexte, vous serez limité dans la quantité de contexte que vous pouvez donner. dans l'invite de génération finale.

Terminologie nouvelle

La dernière limite du modèle RAG est son incapacité à travailler avec une nouvelle terminologie. Les personnes travaillant dans des domaines spécifiques ont tendance à développer des terminologies et des façons de parler propres à ce domaine. Lorsque ces terminologies ne sont pas présentes dans les données de formation du modèle d'intégration, le processus de recherche en souffrira.


Par exemple, le modèle d'intégration ada-002 peut ne pas savoir que le « langage de programmation Rust » est lié à « LLVM ». En fait, il renvoie une similarité cosinus relativement faible de 78 %. Cela signifie que les documents parlant de LLVM peuvent ne pas montrer une forte similitude dans une requête sur Rust, même si les deux idées sont étroitement liées dans la vie réelle.


Habituellement, le problème de la nouvelle terminologie peut être surmonté grâce à une ingénierie rapide, mais dans le contexte d'une recherche intégrée, cela est relativement difficile à faire. Comme mentionné précédemment, il est possible d'affiner un modèle d'intégration, mais enseigner au modèle d'intégration la nouvelle terminologie dans tous les contextes peut être sujet aux erreurs et prendre beaucoup de temps.

Proposer le QE-RAG, ou Question-Enhanced RAG

Compte tenu de ces limitations, j'aimerais proposer une architecture modifiée qui permettrait la mise en œuvre d'une nouvelle classe de systèmes RAG qui contourne bon nombre des limitations décrites ci-dessus. L'idée est basée sur la réalisation de recherches vectorielles sur des questions fréquemment posées, en plus du corpus, et sur l'utilisation d'un LLM pour prétraiter le corpus dans le contexte des questions. Si ce processus semble compliqué, ne vous inquiétez pas, nous passerons en revue les détails d'implémentation dans cette section ainsi que des exemples de code que vous pouvez utiliser pour suivre.


Une chose à noter est que QE-RAG doit être exécuté parallèlement à une implémentation Vanilla RAG afin qu'il puisse s'appuyer sur une autre implémentation si nécessaire. À mesure que la mise en œuvre mûrit, elle devrait de moins en moins avoir besoin d'une solution de repli, mais QE-RAG est toujours destiné à être une amélioration plutôt qu'un remplacement de l'architecture RAG vanille.


L'architecture

Les grandes lignes de l’architecture QE-RAG sont les suivantes :

  1. Créez une base de données vectorielle de questions qui peuvent être ou seront probablement posées sur le corpus.
  2. Prétraitez et résumez le corpus par rapport aux questions de la base de données vectorielles.
  3. Lorsqu'une requête utilisateur arrive, comparez-la avec les questions de la base de données vectorielle.
  4. Si une question de la base de données est très similaire à la requête de l'utilisateur, récupérez la version du corpus résumée pour répondre à la question.
  5. Utilisez le corpus résumé pour répondre à la question de l'utilisateur.
  6. Si aucune question dans la base de données n'est très similaire à la requête de l'utilisateur, revenez à une implémentation Vanilla RAG.


Passons en revue chaque partie tour à tour.


Intégrations de questions

L'architecture commence, tout comme le RAG vanille, par une intégration et une base de données vectorielle. Cependant, au lieu d’intégrer les documents, nous intégrerons une série de questions.

Pour illustrer cela, supposons que nous essayions de construire un LLM expert sur Shakespeare. Nous pourrions vouloir qu'il réponde à des questions telles que :


 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?" ]


Nous voudrons créer une intégration pour eux comme ceci, et les enregistrer ou les utiliser ultérieurement :


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


Prétraitement et résumé

Maintenant que nous avons les questions, nous allons vouloir télécharger et résumer le corpus. Pour cet exemple, nous allons télécharger les versions HTML de Macbeth et 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)


Ensuite, nous traitons les pièces en scènes, en utilisant les balises HTML comme guide :


 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)


Et voici ce qui rend QE-RAG unique : au lieu de créer des intégrations pour des scènes spécifiques, nous créons des résumés pour celles-ci, ciblés sur chacune des questions :


 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


Cette fonction demande à ChatGPT de faire 2 choses : 1) identifier si le passage est réellement utile pour répondre à la question posée, et 2) résumer les parties de la scène qui sont utiles pour répondre à la question.


Si vous essayez cette fonction avec quelques scènes charnières de Macbeth ou Hamlet, vous verrez que GPT3.5 est assez efficace pour identifier si une scène est pertinente par rapport à la question, et le résumé sera un peu plus court que la scène elle-même. Cela rend beaucoup plus facile l’intégration ultérieure lors de l’étape d’ingénierie rapide.


Nous pouvons désormais faire cela pour toutes les scènes.


 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


Dans les charges de travail de production, nous placerions les résumés dans une base de données, mais dans notre cas, nous les écrirons simplement sous forme de fichier JSON sur le disque.


Recherche de vecteurs en deux étapes

Supposons maintenant que nous recevions une question utilisateur comme celle-ci :


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


Comme dans Vanilla RAG, nous souhaiterons créer un plongement pour la question :


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


Dans un RAG vanille, nous comparerions l'intégration des questions de l'utilisateur avec les intégrations des scènes de Shakespeare, mais dans QE-RAG, nous comparons les intégrations avec les questions :


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


On voit que la recherche vectorielle a (correctement) identifié la question 3 comme la question la plus pertinente. Maintenant, nous récupérons les données récapitulatives pour la question 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]])


Veuillez noter que, comme le résumé GPT n'est pas déterministe, vous pouvez obtenir plusieurs chaînes différentes pour indiquer qu'une scène n'est pas pertinente pour la question posée. La clé est de placer uniquement les extraits pertinents dans la liste des résumés pertinents.


À ce stade, nous pouvons effectuer une recherche vectorielle de deuxième niveau pour inclure uniquement les résumés les plus pertinents dans notre invite, mais étant donné la taille de notre corpus, nous pouvons simplement utiliser l'intégralité de la liste relevant_texts dans notre invite.


Ingénierie rapide

Maintenant que nous avons tous les extraits, nous pouvons créer l'invite comme ceci :

 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 )}, ] )


Laissez l'appel API s'exécuter et vous devriez voir quelque chose comme ci-dessous :


 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.


C'est un très bon résumé qui s'inspire de nombreuses parties différentes de Hamlet et Macbeth.

À titre de comparaison, j'ai créé des intégrations pour des scènes entières et effectué une recherche RAG vanille en utilisant la même invite, trouvant les N scènes les plus pertinentes jusqu'à ce que j'atteigne 10 000 jetons.


 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']))


Si vous envoyez ceci à l'invite, vous devriez voir quelque chose comme ci-dessous :


 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.


Il s’agit d’une analyse très convaincante, mais qui ne s’intéresse pas à bon nombre des passages les plus importants de Hamlet et Macbeth. Comme vous pouvez le constater, QE-RAG présente l'avantage distinct de pouvoir intégrer un contexte plus pertinent qu'un système RAG standard.


L’exemple ci-dessus ne démontre cependant pas un autre avantage du QE-RAG, à savoir la capacité de donner au développeur un meilleur contrôle du processus d’intégration. Pour voir comment QE-RAG y parvient, examinons une extension de ce problème : en traitant d'une nouvelle terminologie.


Étendre QE-RAG à une nouvelle terminologie

Là où QE-RAG brille vraiment, c’est lorsque vous introduisez une nouvelle terminologie. Par exemple, supposons que vous introduisiez un nouveau concept, comme le mot japonais « zetsubou », qui est un terme qui se situe entre le désespoir et le désespoir, exprimant spécifiquement un abandon face aux circonstances. Il ne s’agit pas d’une catastrophe aussi immédiate que le concept anglais de désespoir, mais plutôt d’un acquiescement à des choses désagréables qui se produisent.


Supposons que nous voulions répondre à une question du type :


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


Avec Vanilla RAG, nous effectuerions une recherche d'intégrations, puis ajouterions un explicatif lors de la dernière étape d'ingénierie des invites :


 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 )}, ] )


Le résultat est une réponse très bien écrite et convaincante mais légèrement excessive, axée sur quelques scènes de Hamlet. Macbeth n'est pas du tout mentionné dans cette réponse, car aucune des scènes n'a réussi la recherche d'intégration. En regardant les intégrations, il est très clair que la signification sémantique de « zetsubou » n’a pas été capturée correctement et que les textes pertinents n’ont donc pas pu en être extraits.


Dans QE-RAG, nous pouvons injecter la définition du nouveau terme au stade de la synthèse, améliorant considérablement la qualité du texte accessible par le système :


 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." )


Utilisez cette invite de résumé avec les passages et vous verrez des résumés extrêmement précis et contextuels. Ces résumés peuvent ensuite être utilisés dans le QE-RAG ultérieur.


De plus, l'intégration des questions sera extrêmement similaire, car même si ada-002 ne connaît pas la signification précise de « zetsubou », la présence de ce mot dans les deux questions rend les intégrations de questions extrêmement similaires.


Par conséquent, en utilisant QE-RAG pour répondre à la question initiale sur Zetsubou, nous obtenons la réponse suivante :


 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


Le résultat a encore un peu de mal avec ce concept, mais il devrait être évident que QE-RAG est beaucoup plus à même d’aborder le fond de la question et de s’appuyer sur diverses sources.

Et avec cela, nous avons implémenté QE-RAG et démontré son cas d’utilisation !


Conclusion

Dans l'article d'aujourd'hui, nous avons examiné l'architecture RAG de plus en plus populaire et ses limites. Nous avons ensuite étendu l'architecture RAG avec une nouvelle architecture appelée QE-RAG, qui cherche à utiliser plus pleinement les capacités des grands modèles de langage. En plus d'améliorer la précision et l'accès contextuel, QE-RAG permet à l'ensemble du système de se développer à mesure qu'il interagit avec les utilisateurs et se familiarise avec les types de questions posées, permettant ainsi aux entreprises de développer une propriété intellectuelle unique en plus de l'open source. ou des LLM disponibles dans le commerce .

Bien entendu, en tant qu’idée expérimentale, QE-RAG n’est pas parfait. Si vous avez des idées sur la façon dont cette architecture peut être améliorée, ou si vous souhaitez simplement discuter des technologies LLM, n'hésitez pas à m'écrire via mon Github ou LinkedIn .