10 промтов для RAG систем: индексация, поиск, генерация

Введение

Retrieval-Augmented Generation (RAG) — одна из самых востребованных архитектур в NLP. Она позволяет LLM отвечать на вопросы, опираясь на внешние источники знаний, а не только на свои веса. Однако качество RAG напрямую зависит от того, как вы строите пайплайн: от чанкинга документов до выбора стратегии поиска и генерации ответов. Промты — это не просто текст для LLM, а инструмент управления поведением системы. В этой подборке — 10 промтов, которые помогут вам улучшить индексацию, гибридный поиск и качество генерации. Каждый промт сопровождается примером использования и фрагментом кода на Python.

1. Чанкинг с семантическими границами

Задача: Разбить длинный документ на фрагменты, сохраняя смысловую целостность (например, не разрывая абзац или список).

Промт:

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

Текст: {document_text}

Пример результата:

[
  {
    "chunk_id": 1,
    "text": "RAG (Retrieval-Augmented Generation) — это метод, который комбинирует поиск информации и генерацию текста. Он позволяет модели отвечать на вопросы, используя внешние источники знаний.",
    "start_char": 0,
    "end_char": 150
  },
  {
    "chunk_id": 2,
    "text": "Основные компоненты RAG: индексатор, ретривер и генератор. Индексатор преобразует документы в эмбеддинги, ретривер находит релевантные фрагменты, а генератор формирует ответ.",
    "start_char": 151,
    "end_char": 350
  }
]

Код (Python с LangChain):

from langchain.text_splitter import RecursiveCharacterTextSplitter

text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=500,
    chunk_overlap=50,
    separators=["\n\n", "\n", ". ", " ", ""]
)
chunks = text_splitter.split_text(document_text)

2. Генерация эмбеддингов с учётом контекста

Задача: Создать эмбеддинг для фрагмента, который учитывает не только сам текст, но и его метаданные (например, название документа или дату).

Промт:

Сгенерируй эмбеддинг для следующего текста, включив в него контекст из метаданных. Метаданные: {metadata}. Текст: {chunk_text}. Верни вектор чисел с плавающей точкой размерностью 768.

Пример метаданных:

metadata = {
    "source": "report_2025.pdf",
    "section": "Introduction",
    "date": "2025-06-01"
}

Примечание: На практике эмбеддинги генерируются моделями (например, text-embedding-ada-002), но промт может использоваться для предобработки — например, для создания enriched text, где метаданные добавляются в начало фрагмента: [Source: report_2025.pdf] Текст.... Это улучшает результаты поиска (см. эксперименты от Pinecone, 2024).

3. Гибридный поиск: объединение BM25 и векторного поиска

Задача: Найти релевантные фрагменты, используя как лексическое совпадение (BM25), так и семантическую близость (эмбеддинги).

Промт:

Выполни гибридный поиск по запросу "{query}". Для каждого фрагмента из коллекции рассчитай два score: BM25_score и cosine_similarity_score. Итоговый score = 0.3 * BM25_score + 0.7 * cosine_similarity_score. Верни топ-5 фрагментов с наибольшим итоговым score.

Пример кода (Python, библиотека rank_bm25 и sentence-transformers):

from rank_bm25 import BM25Okapi
from sentence_transformers import SentenceTransformer
from sklearn.metrics.pairwise import cosine_similarity
import numpy as np

model = SentenceTransformer('all-MiniLM-L6-v2')
tokenized_corpus = [doc.split() for doc in corpus]
bm25 = BM25Okapi(tokenized_corpus)

# Векторный поиск
query_emb = model.encode([query])
corpus_emb = model.encode(corpus)
cos_scores = cosine_similarity(query_emb, corpus_emb)[0]

# BM25
bm25_scores = bm25.get_scores(query.split())

# Гибрид
hybrid_scores = 0.3 * np.array(bm25_scores) + 0.7 * cos_scores
top_indices = np.argsort(hybrid_scores)[-5:][::-1]

4. Денормализация BM25 для точного совпадения

Задача: Улучшить BM25 для поиска точных совпадений (например, номеров заказов или дат).

Промт:

Для запроса "{query}" найди фрагменты, где есть точное совпадение по ключевым словам. Используй TF-IDF с усилением для точных совпадений: если слово встречается в запросе и фрагменте, его вес умножается на 2. Верни топ-3 фрагмента.

Реализация: Модифицируйте BM25, задав параметр k1=1.2 и b=0.75, но для ключевых слов (например, из списка стоп-слов) увеличьте вес. Это описано в документации Elasticsearch (BM25 similarity).

5. Поиск с учётом временной релевантности

Задача: Отдать приоритет более свежим документам при одинаковой семантической близости.

Промт:

Для запроса "{query}" найди топ-5 фрагментов с учётом даты. Используй формулу: score = cosine_similarity * (1 + decay_factor / (days_since_publication + 1)), где decay_factor = 0.1. Верни фрагменты с датами и score.

Пример:

Фрагмент Дата Cosine Score Итоговый Score
RAG overview 2026-07-01 0.85 0.85 * (1 + 0.1/16) = 0.856
RAG overview 2025-01-01 0.85 0.85 * (1 + 0.1/562) = 0.850

6. Ранжирование с помощью кросс-энкодера

Задача: Переранжировать результаты поиска, используя более точную модель (кросс-энкодер).

Промт:

Для каждого фрагмента из списка оцени релевантность к запросу "{query}" по шкале от 0 до 1. Используй модель cross-encoder/ms-marco-MiniLM-L-6-v2. Верни топ-3 фрагмента с оценками.

Код:

from sentence_transformers import CrossEncoder
model = CrossEncoder('cross-encoder/ms-marco-MiniLM-L-6-v2')
scores = model.predict([(query, doc) for doc in candidates])

7. Генерация ответа с цитированием источников

Задача: Сформировать ответ на вопрос, обязательно указав, из каких фрагментов взята информация.

Промт:

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

Фрагменты:
[1] {chunk_1}
[2] {chunk_2}

Вопрос: {question}

Пример ответа:
«Согласно [1], RAG улучшает точность ответов на 30% по сравнению с базовой LLM. Однако в [2] указано, что прирост может быть ниже для узкоспециализированных тем.»

8. Обработка «не знаю» для RAG

Задача: Заставить модель признать, что ответа нет в документах, вместо галлюцинаций.

Промт:

Если ни один из фрагментов не содержит информации для ответа на вопрос, напиши: «Извините, в предоставленных документах нет информации по этому вопросу.» Не пытайся додумать ответ. Фрагменты: {chunks}

Пример:
Вопрос: «Какая погода в Москве?»
Фрагменты: содержат только документацию по API.
Ответ модели: «Извините, в предоставленных документах нет информации по этому вопросу.»

9. Аугментация запроса через HyDE

Задача: Улучшить поиск, сгенерировав гипотетический ответ на запрос и используя его для поиска (HyDE — Hypothetical Document Embeddings).

Промт:

Сгенерируй гипотетический документ, который был бы идеальным ответом на запрос "{query}". Затем используй эмбеддинг этого документа для поиска в коллекции. Верни топ-3 фрагмента.

Реализация (из статьи Gao et al., 2022):

hyde_prompt = f"Write a hypothetical answer to the question: {query}"
hyde_doc = llm.generate(hyde_prompt)
hyde_emb = model.encode([hyde_doc])
# Далее поиск как обычно

10. Мульти-запросный поиск

Задача: Сгенерировать несколько вариантов запроса, чтобы охватить разные аспекты вопроса.

Промт:

Сгенерируй 3 разных формулировки запроса по теме "{query}". Каждая должна отражать разный аспект. Затем выполни поиск по каждому и объедини результаты (удали дубликаты).

Пример:
Запрос: «Как работает RAG?»
Варианты:
1. «Архитектура RAG: ретривер и генератор»
2. «Преимущества RAG перед fine-tuning»
3. «Примеры реализации RAG на Python»

Заключение

Эти 10 промтов покрывают ключевые этапы RAG: от подготовки данных (чанкинг, эмбеддинги) до поиска (гибридный, временной, HyDE) и генерации (цитирование, обработка незнания). Экспериментируйте с весами и параметрами — оптимальные значения зависят от вашего датасета. Начните с простого пайплайна и постепенно внедряйте продвинутые техники. Удачи в построении RAG-систем!

← Все статьи

Комментарии