Все статьи
·6 мин

От текстового хаоса к ясности: RAG и LangChain на практике

Практический пример извлечения смысла из групп текстов с помощью RAG, LangChain, FAISS и моделей OpenAI.

RAGLangChainLLMNLPPython
От текстового хаоса к ясности: RAG и LangChain на практике

В современном мире, где решения всё чаще принимаются на основе данных, объём доступной информации поражает. Главная задача — пройти через этот массив и получить выводы, которые можно использовать на практике. Здесь на сцену выходят обработка естественного языка (NLP) и Retrieval-Augmented Generation (RAG).

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

Настройка окружения

Сначала подключим необходимые библиотеки и загрузим API-ключ OpenAI из файла окружения.

from operator import itemgetter
import openai
import os
import pandas as pd
from dotenv import load_dotenv, dotenv_values

load_dotenv()
load_openai_key_from_env()

def load_openai_key_from_env(filepath=".env"):
    config = dotenv_values(filepath)
    openai_api_key = config['open_ai_key']
    set_openai_key(openai_api_key)

def set_openai_key(api_key):
    openai.api_key = api_key
    os.environ['OPENAI_API_KEY'] = api_key

Предполагается, что файл .env находится в одной папке с остальными скриптами. Его содержимое:

open_ai_key=YOUR_API_KEY

Данные

Максимально упростим формат данных. Каждый столбец — Group_1, Group_2 и так далее — содержит серию публикаций, для которых нужно найти общий смысл. Здесь также можно загрузить CSV или получить данные из базы и преобразовать их в переменную data.

data = {
    'Group': [1, 2, 3],
    'Group_1': [
        "@JamesSmith James, great job on the project! Now imagine if everyone in the protest group, soldiers, pilots, and others were this efficient.",
        "@LauraMiles Laura, please apologize to the entire community rather than playing the hero.",
        'What about the "Brothers in Arms" group? Suddenly they agree on the new policy?'
    ],
    'Group_2': [
        "Hey everyone! Starting a new week with some exciting news. Keep an eye on our Instagram for an exclusive offer.",
        "BREAKING: The mock test is now LIVE. Download it from our blog or check our Instagram profile.",
        "Hello everyone! We have a new section in our Instagram stories: quizzes and shortcut challenges."
    ],
    'Group_3': [
        "Go team! #newproject #teamwork #worklife #colleagues #bestteam",
        "With renowned journalist and friend, David Foster. Presented a talk about the life of Beethoven.",
        "Study hard, play hard! #worklife #balance #goals #unilife"
    ]
}

df = pd.DataFrame(data)
df.head()

Где здесь RAG?

Прежде чем использовать RAG, разберёмся, что это такое и зачем он нужен. RAG — подход в NLP и информационном поиске, который объединяет два основных компонента: ретривер и генератор.

  • Гибридный подход. Обычные языковые модели хорошо генерируют естественный текст, но не всегда точны в фактах. RAG сначала находит подходящие фрагменты в корпусе, а затем передаёт их генератору, например GPT, для подготовки ответа.
  • Более надёжные ответы на фактические вопросы. Ответ опирается на найденные документы, а не только на знания, запомненные моделью.
  • Эффективность. Вместо обработки всего корпуса ретривер отбирает несколько релевантных фрагментов, и генератор работает уже с ними.
  • Данные не всегда актуальны. Если источник статичен, система не узнает о новых событиях без регулярного обновления корпуса или динамического поиска.
  • Не требуется исчерпывающий fine-tuning. RAG способен работать с вашими данными без полного дообучения модели на всём наборе документов.

Цепочка поиска и генерации

from typing import List, Callable

def setup_chain(selected_columns: List[str]) -> Callable:
    def get_context_from_columns() -> str:
        return " ".join(selected_columns)

    template = """Answer the question based only on the following context:
    {context}

    Question: {question}
    """

    prompt = ChatPromptTemplate.from_template(template)
    model = ChatOpenAI()  # генератор

    vectorstore = FAISS.from_texts(
        selected_columns,
        embedding=OpenAIEmbeddings()
    )
    retriever = vectorstore.as_retriever()

    chain = (
        {"context": retriever, "question": RunnablePassthrough()}
        | prompt
        | model
        | StrOutputParser()
    )

    return chain

Логика этой функции состоит из четырёх шагов:

  1. setup_chain создаёт цепочку, которая совмещает поиск по текстам через FAISS и ответы модели OpenAI.
  2. FAISS находит в selected_columns наиболее релевантные вопросу фрагменты.
  3. Шаблон промпта явно требует отвечать только на основании найденного контекста.
  4. Цепочка получает контекст, формирует запрос, отправляет его модели и преобразует результат в строку.

Как использовать код для подготовки сводок

В переменной question можно задать практически любой вопрос. Ниже — пример запуска цепочки для нескольких столбцов датафрейма.

def print_chain_results(df, question: str, start_col: int, end_col: int) -> None:
    for col in df.columns[start_col:end_col + 1]:
        output = summarize_all_posts(df, col).split("
")

        answer_chain = setup_chain(output)
        answer = answer_chain.invoke(question)

        print(f"Question: {question}")
        print(f"Number of group: {col}")
        print(f"Answer: {answer}
")

question = "What is most popular topic in text?"
start_col_index = 1
end_col_index = 3
print_chain_results(df, question, start_col_index, end_col_index)

Итоги

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

Полный код находится в репозитории GitHub. Полезна также документация LangChain по retrieval.

Источник и оригинал

Это русская версия авторской статьи Павла Стасиньски. Оригинал опубликован на Medium.

Читать оригинал на Medium