ИИ для управления знаниями: итерация RAG с архитектурой QE-RAG

ИИ для управления знаниями: итерация RAG с архитектурой QE-RAG

12 сентября 2023 г.

Когда революция LLM начинает обретать форму, шумиха уступила место коммерческому развитию. По мере того как первоначальная волна энтузиазма утихает, генеративный ИИ больше не рассматривается как всеведущий черный ящик, а скорее как составной, хотя и чрезвычайно мощный инструмент в арсенале инженера. В результате предприниматели и технологи теперь имеют все более совершенный набор инструментов и методов для разработки приложений LLM.

Один из наиболее интересных вариантов использования LLM связан с управлением знаниями. Специализированные LLM, основанные либо на технологии OpenAI GPT, либо на моделях с открытым исходным кодом, таких как LLaMa 2 и Flan-T5 используются умными способами для управления большими объемами данных. Если раньше организациям с большими наборами текстовых данных приходилось полагаться на методы текстового поиска, такие как нечеткое сопоставление или полнотекстовое индексирование, теперь у них есть доступ к мощной системе, которая может не только найти информацию, но и обобщить ее в экономичном и удобном для чтения виде. мода.

В этом варианте использования используется архитектура генерации с расширенным поиском , или RAG, стала выдающейся архитектурой с огромной гибкостью и производительностью. Благодаря этой архитектуре организации могут быстро индексировать объем работы, выполнять к нему семантические запросы и генерировать информативные и убедительные ответы на определяемые пользователем запросы на основе корпуса. Несколько компаний и служб начали поддерживать реализацию архитектуры RAG, что подчеркивает ее долговечность.

Какой бы эффективной ни была RAG, эта архитектура также имеет несколько реальных ограничений. В этой статье мы рассмотрим архитектуру RAG, определим ее ограничения и предложим улучшенную архитектуру для устранения этих ограничений.

Как и в случае со всеми остальными статьями, я хочу пообщаться с другими технологами и энтузиастами искусственного интеллекта. Если у вас есть мысли о том, как можно улучшить эту архитектуру, или идеи об искусственном интеллекте, которые вы хотели бы обсудить, не стесняйтесь обращаться к нам! Вы можете найти меня на Github или LinkedIn, ссылки есть в моем профиле, а также внизу этой статьи.

Обзор контента

  • Архитектура извлечения дополненной генерации (RAG)
  • Ограничения архитектуры RAG
  • Предложение QE-RAG или RAG с расширенными вопросами
  • Заключение

Архитектура извлечения дополненной генерации (RAG)

С такими именами, как RAG, Flan и LLaMa, сообщество искусственного интеллекта вряд ли в ближайшее время получит награды за футуристические и стильные имена. Тем не менее, архитектура RAG, безусловно, заслуживает награды за сочетание двух чрезвычайно мощных технологий, ставших доступными благодаря развитию LLM: встраивание контекстных документов и оперативное проектирование.

В самом простом виде архитектура RAG — это система, которая использует векторный поиск по внедрению, чтобы найти часть(и) корпуса, наиболее соответствующую вопросу, вставить эту(ые) часть(и) в подсказку, а затем использовать разработку подсказок, чтобы гарантировать, что ответ основан на выдержках, приведенных в подсказке. Если все это звучит немного запутанно, читайте дальше, потому что я объясню каждый компонент по очереди. Я также приложу пример кода, чтобы вы могли следовать инструкциям.

Модель внедрения

Прежде всего, эффективная система RAG требует мощной модели внедрения. Модель внедрения преобразует обычный текстовый документ в последовательность чисел или «вектор», который примерно представляет семантическое содержание документа. Предполагая, что модель внедрения хорошая, вы сможете чтобы сравнить семантические значения двух разных документов и определить, являются ли эти два документа семантически похожими, используя векторную арифметику.

Чтобы увидеть это в действии, вставьте следующий код в файл Python и запустите его:

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

Приведенный выше код генерирует вложения для фраз «Вождение автомобиля», «Уильям Шекспир» и «Верховая езда на лошади» перед их сравнением друг с другом с использованием алгоритма косинусного подобия. Мы могли бы ожидать, что косинусное сходство будет выше, когда фразы схожи семантически, поэтому «Вождение автомобиля» и «Езда на лошади» должны быть намного ближе, тогда как «Вождение автомобиля» и «Уильям Шекспир» должны быть разными.

Вы должны увидеть, что согласно модели встраивания OpenAI, ada-002, фраза «вождение автомобиля» на 88% похожа на фразу «верховая езда» и на 76% похожа на фразу «Уильям Шекспир». Это означает, что модель внедрения работает так, как мы ожидаем. Это определение семантического сходства лежит в основе системы RAG.

Идея косинусного подобия оказывается чрезвычайно устойчивой, если распространить ее на сравнения гораздо более крупных документов. Например, возьмем мощный монолог из шекспировского «Макбета» «Завтра, и завтра, и завтра»:

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

Вы должны видеть, что монолог лишь на 75% похож на идею «Езды на машине» и на 82% — на идею «Созерцания смертности».

Но нам не просто нужно сравнивать монологи с идеями, мы действительно можем сравнивать монологи с вопросами. Например:

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

Вы должны увидеть, что вставка показывает, что монолог Макбета по контексту гораздо ближе к вопросу «Какой шекспировский монолог предполагает смертность?» чем монолог Эгея, в котором упоминается смерть, но не затрагивается непосредственно концепция смертности.

Поиск векторов

Теперь, когда у нас есть встраивание, как нам использовать его в нашей системе RAG? Предположим, мы хотим предоставить нашей RAG-системе знания обо всех монологах Шекспира, чтобы она могла отвечать на вопросы о Шекспире. В этом случае мы бы скачали все монологи Шекспира и сгенерировали для них вложения. Если вы следуете инструкциям, вы можете создать встраивание следующим образом:

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

Как только мы получим внедрения, мы захотим сохранить их таким образом, чтобы мы могли запрашивать и сравнивать их с новым внедрением. Обычно мы помещаем их в так называемую базу данных векторов, которая представляет собой специализированное хранилище данных, позволяющее быстро сравнивать данные. из двух векторов. Однако, если ваш корпус не очень велик, сравнение методом грубой силы на удивление терпимо для большинства непроизводственных экспериментальных случаев использования, когда производительность не имеет решающего значения.

Независимо от того, решите ли вы использовать базу данных или нет, вам захочется создать систему, которая сможет найти в вашем корпусе элементы, которые лучше всего соответствуют вопросу. В нашем примере нам понадобится возможность найти монолог, который наиболее соответствует заданному вопросу пользователя. Возможно, вы захотите сделать что-то вроде:

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

Если вы запустите этот пример, вы увидите выбранный монолог Макбета, сходство которого с вопросом составляет примерно 82%.

Быстрое проектирование

Последний шаг в модели RAG — оперативное проектирование. В нашем случае это не так уж сложно. Теперь, когда у нас есть монолог, мы можем создать подсказку, просто встроив монолог в наш запрос, а затем задав вопрос:

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?

         """},
    ]
)

Вы должны увидеть ответ типа:

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.

Конечно, этот конкретный пример не является самой мощной демонстрацией архитектуры RAG, поскольку большинство моделей GPT уже знают о монологах Шекспира и прошли обучение на большом объеме анализа Шекспира, публично опубликованном в Интернете. Фактически, если вы зададите GPT-4 именно этот вопрос без встроенного монолога, вы, скорее всего, получите очень хороший ответ, хотя в нем, скорее всего, не будет так много ссылок на цитаты из монолога. Однако должно быть очевидно, что в коммерческих целях этот метод можно перекрестно применять к частным или экзотическим наборам данных, которые недоступны для существующих реализаций GPT.

Фактически, читатели, знакомые с моей предыдущей статьей, Создание анализатора документов с ChatGPT, Google Cloud и Python, возможно, поймете, что последняя часть метода очень похожа на быстрое проектирование, которое я делал в этой статье. Основываясь на этой идее, мы можем очень легко представить систему RAG, построенную на основе публикаций правительства Японии (пример данных из этой статьи), которая позволила бы пользователям искать и задавать вопросы об экономической политике Японии. Система быстро извлекала наиболее релевантные документы, суммировала их и давала ответ, основанный на глубоких знаниях по конкретной предметной области, недоступных базовым моделям GPT. Именно именно благодаря этой мощности и простоте архитектура RAG пользуется большой популярностью среди разработчиков LLM.

Теперь, когда мы рассмотрели архитектуру RAG, давайте рассмотрим некоторые недостатки этой архитектуры.

Ограничения архитектуры RAG

Внедрение возможностей отладки

Поскольку многие системы RAG используют встраивание документов и векторный поиск для связи вопроса и соответствующих документов, качество всей системы часто не уступает используемой модели внедрения. Модель внедрения OpenAI невероятно гибка, и существует множество методов настройки для внедрения. LLaMa, конкурент GPT Meta с открытым исходным кодом, предлагает тонко настраиваемые модели внедрения. Однако в модели внедрения есть неизбежный аспект черного ящика. Это в некоторой степени осуществимо при сравнении коротких текстовых строк, когда дело доходит до сравнения коротких строк с гораздо более длинными документами. В нашем предыдущем примере нам нужно сделать небольшой шаг веры в то, что поиск по внедрению способен связать «смертность» с монологом «завтра, и завтра, и завтра». Это может быть весьма неудобно для рабочих нагрузок, где прозрачность и возможность отладки имеют решающее значение.

Перегрузка контекста

Еще одним ограничением модели RAG является относительно ограниченный объем передаваемого ей контекста. Поскольку для хорошей работы модели внедрения требуется контекст уровня документа, нам нужно быть осторожными при разделении корпуса для внедрения. Монолог Макбета может иметь 82% сходства с вопросом о смертности, но это число снижается до 78%, если сравнить этот вопрос со вставкой первых двух строк монолога, то есть «Завтра, и завтра, и завтра. Ползает в этом мелком темпе изо дня в день, До последнего слога записанного времени».

В результате контекст, передаваемый в приглашение RAG, должен быть довольно большим. В настоящее время самые высококонтекстные модели GPT по-прежнему ограничены 16 000 токенами, что довольно много текста, но когда вы работаете с длинными стенограммами интервью или статьями с богатым контекстом, вы будете ограничены в том, сколько контекста вы можете предоставить. в приглашении окончательного создания.

Новая терминология

Последним ограничением модели RAG является ее неспособность работать с новой терминологией. Люди, работающие в определенных областях, склонны разрабатывать терминологию и манеру разговора, уникальные для этой области. Если эти термины отсутствуют в обучающих данных модели внедрения, процесс поиска пострадает.

Например, модель внедрения ada-002 может не знать, что «язык программирования Rust» связан с «LLVM». Фактически, он возвращает относительно низкое косинусное сходство — 78%. Это означает, что документы, в которых говорится о LLVM, могут не обнаруживать сильного сходства в запросе о Rust, хотя в реальной жизни эти две идеи тесно связаны.

Обычно проблему новой терминологии можно решить с помощью быстрого проектирования, но в контексте встроенного поиска это относительно сложно сделать. Как упоминалось ранее, точная настройка модели внедрения возможна, но обучение модели внедрения новой терминологии во всех контекстах может привести к ошибкам и отнять много времени.

Предложение QE-RAG или RAG, расширенного вопросами

Учитывая эти ограничения, я хотел бы предложить модифицированную архитектуру, которая позволила бы реализовать новый класс систем RAG, который обходит многие из ограничений, описанных выше. Идея основана на векторном поиске по часто задаваемым вопросам в дополнение к корпусу и использовании LLM для предварительной обработки корпуса в контексте вопросов. Если этот процесс кажется сложным, не волнуйтесь: в этом разделе мы рассмотрим детали реализации вместе с примерами кода, которые вы можете использовать в дальнейшем.

Следует отметить, что QE-RAG следует запускать вместе с стандартной реализацией RAG, чтобы при необходимости можно было использовать другую реализацию. По мере развития реализации резервный вариант будет требоваться все меньше и меньше, но QE-RAG по-прежнему предназначен для улучшения, а не замены стандартной архитектуры RAG.

Архитектура

В общих чертах архитектура QE-RAG заключается в следующем:

  1. Создайте векторную базу данных вопросов, которые могут или могут быть заданы по корпусу.
  2. Предварительная обработка и суммирование корпуса по вопросам в векторной базе данных.
  3. Когда поступает пользовательский запрос, сравните его с вопросами в базе данных векторов.
  4. Если вопрос в базе данных очень похож на пользовательский запрос, получите версию корпуса, обобщенную для ответа на вопрос.
  5. Используйте сводный корпус, чтобы ответить на вопрос пользователя.
  6. Если ни один вопрос в БД не очень похож на пользовательский запрос, вернитесь к стандартной реализации RAG.
  7. Давайте рассмотрим каждую часть по очереди.

    Внедрение вопросов

    Архитектура начинается, как и в обычной RAG, со встраивания и векторной базы данных. Однако вместо встраивания документов мы встроим ряд вопросов.

    Чтобы проиллюстрировать это, предположим, что мы пытаемся создать магистратуры, которая является экспертом по Шекспиру. Возможно, мы захотим, чтобы он отвечал на такие вопросы, как:

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

    Мы захотим создать для них встраивание вот так и сохранить их или использовать позже:

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

    Предварительная обработка и суммирование

    Теперь, когда у нас есть вопросы, нам нужно загрузить и обобщить корпус. В этом примере мы загрузим HTML-версии «Макбета» и «Гамлета»:

    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)
    

    Затем мы преобразуем пьесы в сцены, используя в качестве руководства HTML-теги:

    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)
    

    И вот что делает QE-RAG уникальным: вместо создания вложений для конкретных сцен мы создаем для них резюме, ориентированное на каждый из вопросов:

    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
    

    Эта функция просит ChatGPT сделать две вещи: 1) определить, действительно ли отрывок полезен для ответа на поставленный вопрос, и 2) суммировать части сцены, которые полезны для ответа на вопрос.

    Если вы попробуете эту функцию с несколькими ключевыми сценами из «Макбета» или «Гамлета», вы увидите, что GPT3.5 довольно хорошо определяет, имеет ли сцена отношение к вопросу, и резюме будет немного короче, чем сама сцена. Это значительно упрощает последующее встраивание на этапе быстрого проектирования.

    Теперь мы можем сделать это для всех сцен.

    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
    

    При производственных нагрузках мы помещаем сводные данные в базу данных, но в нашем случае мы просто записываем их на диск в виде файла JSON.

    Двухэтапный векторный поиск

    Теперь предположим, что мы получили вопрос пользователя, подобный приведенному ниже:

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

    Как и в оригинальной RAG, нам нужно создать вложение для вопроса:

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

    В стандартном RAG мы сравнивали бы встраивание вопросов пользователя с встраиванием сцен из Шекспира, но в QE-RAG мы сравнивали бы встраивание вопросов:

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

    Мы видим, что векторный поиск (правильно) определил вопрос 3 как наиболее релевантный вопрос. Теперь мы получаем сводные данные для вопроса 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]])
    

    Обратите внимание: поскольку обобщение GPT не является детерминированным, вы можете получить несколько разных строк, указывающих на то, что сцена не имеет отношения к рассматриваемому вопросу. Ключевым моментом является добавление в список релевантных резюме только соответствующих выдержек.

    На этом этапе мы можем выполнить векторный поиск второго уровня, чтобы включить в нашу подсказку только наиболее релевантные сводки, но, учитывая размер нашего корпуса, мы можем просто использовать весь список relevant_texts в нашей подсказке. .

    Быстрое проектирование

    Теперь у нас есть все выдержки, и мы можем построить приглашение следующим образом:

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

    Позвольте вызову API выполниться, и вы должны увидеть что-то вроде следующего:

    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.
    

    Это очень хорошее изложение, основанное на различных частях «Гамлета» и «Макбета».

    Для сравнения я создал встраивания для целых сцен и запустил стандартный поиск RAG, используя ту же подсказку, находя первые N наиболее релевантных сцен, пока не достиг 10 000 токенов.

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

    Если вы добавите это в командную строку, вы должны увидеть что-то вроде следующего:

    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 Neptunes 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.
    

    Это очень убедительный анализ, но он не затрагивает многие наиболее важные отрывки из Гамлета и Макбета. Как видите, QE-RAG имеет явное преимущество, поскольку позволяет встроить более релевантный контекст, чем стандартная система RAG.

    Однако приведенный выше пример не демонстрирует еще одно преимущество QE-RAG, а именно возможность дать разработчику лучший контроль над процессом внедрения. Чтобы увидеть, как QE-RAG достигает этого, давайте посмотрим на расширение этой проблемы – на новую терминологию.

    Расширение QE-RAG на новую терминологию

    Где QE-RAG действительно проявляет себя, так это когда вы вводите новую терминологию. Например, предположим, что вы вводите новое понятие, такое как японское слово «дзэцубо», которое представляет собой термин, находящийся между отчаянием и безнадежностью, конкретно обозначающий капитуляцию перед обстоятельствами. Оно не столь катастрофично, как английское понятие отчаяния, а гораздо больше связано с молчаливым согласием с происходящими неприятными вещами.

    Предположим, мы хотим ответить на такой вопрос:

    user_question = "Как персонажи Шекспира справляются с Зецубо?"

    В стандартном RAG мы выполняли поиск вложений, а затем на последнем этапе разработки подсказки добавляли объяснение:

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

    В результате получился очень хорошо написанный и убедительный, но слегка растянутый ответ, сосредоточенный на нескольких сценах из «Гамлета». Макбет вообще не упоминается в этом ответе, потому что ни одна из сцен не прошла поиск по встраиванию. При взгляде на вложения становится ясно, что семантическое значение «зецубо» не было передано должным образом, и поэтому из него невозможно извлечь соответствующие тексты.

    В QE-RAG мы можем ввести определение нового термина на этапе реферирования, что значительно улучшит качество текста, доступного системе:

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

    Используйте эту подсказку для резюмирования отрывков, и вы увидите чрезвычайно точные и контекстуальные рефераты. Эти резюме затем могут быть использованы в ходе последующего QE-RAG.

    Кроме того, встраивание вопросов будет очень похожим, потому что, хотя ada-002 не знает точного значения слова «зецубо», присутствие этого слова в обоих вопросах делает встраивание вопросов чрезвычайно похожим.

    Поэтому, используя QE-RAG для ответа на исходный вопрос о Зецубо, мы получаем следующий ответ:

    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
    

    Результаты все еще немного не соответствуют этой концепции, но должно быть очевидно, что QE-RAG гораздо лучше способен разобраться в сути вопроса и извлечь информацию из различных источников.

    И благодаря этому мы внедрили QE-RAG и продемонстрировали вариант его использования!

    Заключение

    В сегодняшней статье мы рассмотрели набирающую популярность архитектуру RAG и ее ограничения. Затем мы расширили архитектуру RAG новой архитектурой под названием QE-RAG, которая призвана более полно использовать возможности больших языковых моделей. Помимо повышения точности и контекстного доступа, QE-RAG позволяет всей системе развиваться по мере взаимодействия с пользователями и лучше знакомиться с типами задаваемых вопросов, что позволяет компаниям разрабатывать уникальную интеллектуальную собственность поверх LLM с открытым исходным кодом или коммерчески доступные.

    Конечно, как экспериментальная идея QE-RAG не идеальна. Если у вас есть идеи о том, как можно улучшить эту архитектуру, или вы просто хотите обсудить технологии LLM, не стесняйтесь, напишите мне через мой Github или LinkedIn.


    Оригинал
PREVIOUS ARTICLE
NEXT ARTICLE