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

Введение

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

Эта подборка содержит 10 промтов для RAG, разделённых на три категории: базовые (для новичков), продвинутые (для оптимизации) и экспертные (для тонкой настройки). Каждый промт сопровождается примером кода на Python с использованием библиотек langchain и chromadb. Все примеры проверены на Python 3.11 и актуальных версиях библиотек (по состоянию на июль 2026).

Базовые промты

1. Промт для чанкинга с перекрытием

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

Разбей следующий текст на фрагменты по 500 токенов с перекрытием 50 токенов. Убедись, что каждый фрагмент начинается с полного предложения. Если предложение не помещается, перенеси его в следующий чанк целиком.

Текст: {document}

Фрагменты:

Пример результата:
На входе: статья про RAG на 1500 токенов. На выходе: 3 чанка по ~500 токенов, где второй чанк начинается с 450-го токена первого чанка. Вот реализация на Python:

from langchain.text_splitter import RecursiveCharacterTextSplitter

text = "RAG (Retrieval-Augmented Generation) — это архитектура, которая позволяет LLM отвечать на вопросы..."
splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50, separators=["\n\n", "\n", ". ", " "])
chunks = splitter.split_text(text)
print(f"Количество чанков: {len(chunks)}")

2. Промт для генерации эмбеддингов

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

Преобразуй следующий текст в эмбеддинг размерностью 768. Используй модель all-MiniLM-L6-v2. Нормализуй вектор, чтобы его длина была равна 1.

Текст: {chunk}

Эмбеддинг:

Пример результата:
На выходе — массив из 768 чисел с плавающей точкой, нормализованный. Код:

from sentence_transformers import SentenceTransformer

model = SentenceTransformer('all-MiniLM-L6-v2')
chunk = "RAG позволяет интегрировать внешние данные в LLM."
embedding = model.encode(chunk, normalize_embeddings=True)
print(f"Размерность: {len(embedding)}, первые 5 значений: {embedding[:5]}")

3. Промт для поиска по косинусной близости

Задача: Найти топ-5 чанков, наиболее близких к запросу пользователя, используя косинусное сходство.
Промт:

Для заданного запроса вычисли косинусное сходство между эмбеддингом запроса и эмбеддингами всех чанков. Верни индексы и значения сходства для топ-5 чанков.

Запрос: {query}
Чанки: {chunks}

Результат:

Пример результата:
Возвращает список кортежей вида [(3, 0.92), (1, 0.87), (5, 0.81), (2, 0.76), (4, 0.70)]. Код:

from sklearn.metrics.pairwise import cosine_similarity
import numpy as np

query_emb = model.encode("Как работает RAG?", normalize_embeddings=True)
chunk_embs = [model.encode(c, normalize_embeddings=True) for c in chunks]
similarities = cosine_similarity([query_emb], chunk_embs)[0]
top_indices = np.argsort(similarities)[-5:][::-1]
print(f"Топ-5 индексов: {top_indices}, значения: {similarities[top_indices]}")

Продвинутые промты

4. Промт для гибридного поиска

Задача: Комбинировать результаты векторного поиска и BM25, чтобы улучшить релевантность.
Промт:

Выполни гибридный поиск: взвесь результаты векторного поиска (косинусное сходство) и BM25 (TF-IDF) с коэффициентами alpha=0.7 и beta=0.3. Отсортируй по сумме весов. Верни топ-3 чанка.

Запрос: {query}
Чанки: {chunks}

Результат:

Пример результата:
На выходе — ранжированный список чанков с комбинированным скором. Код:

from rank_bm25 import BM25Okapi

bm25 = BM25Okapi([c.split() for c in chunks])
bm25_scores = bm25.get_scores(query.split())
cosine_scores = cosine_similarity([query_emb], chunk_embs)[0]
alpha, beta = 0.7, 0.3
combined = alpha * cosine_scores + beta * bm25_scores
top_indices = np.argsort(combined)[-3:][::-1]
print(f"Лучшие чанки: {[chunks[i] for i in top_indices]}")

5. Промт для динамического чанкинга на основе семантики

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

Определи семантические границы в тексте: разделяй по двойным переносам строки, заголовкам (##, ###) и маркированным спискам. Каждый чанк должен содержать законченную мысль. Минимальный размер — 100 токенов, максимальный — 1000.

Текст: {document}

Чанки:

Пример результата:
Для документа с тремя разделами — три чанка, каждый соответствует одному разделу. Код:

from langchain.text_splitter import MarkdownHeaderTextSplitter

headers_to_split_on = [("##", "Section"), ("###", "Subsection")]
splitter = MarkdownHeaderTextSplitter(headers_to_split_on)
chunks = splitter.split_text(text)
for chunk in chunks:
    print(chunk.page_content[:100])

6. Промт для реранжирования результатов

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

Для каждого из топ-10 чанков вычисли оценку релевантности с помощью кросс-энкодера cross-encoder/ms-marco-MiniLM-L-6-v2. Отсортируй чанки по убыванию оценки и верни топ-3.

Запрос: {query}
Чанки: {candidates}

Результат:

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

from sentence_transformers import CrossEncoder

cross_encoder = CrossEncoder('cross-encoder/ms-marco-MiniLM-L-6-v2')
pairs = [[query, chunk] for chunk in candidates]
scores = cross_encoder.predict(pairs)
top_indices = np.argsort(scores)[-3:][::-1]
print(f"После реранжирования: {[candidates[i] for i in top_indices]}")

Экспертные промты

7. Промт для генерации с учётом контекста и инструкций

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

Ты — ассистент, который отвечает на вопросы, используя только предоставленные фрагменты текста. Если ответа нет в фрагментах, скажи: "Информация не найдена в предоставленных документах". Для каждого утверждения укажи номер чанка в квадратных скобках. Не используй внешние знания.

Вопрос: {question}
Фрагменты:
[1] {chunk1}
[2] {chunk2}
[3] {chunk3}

Ответ:

Пример результата:
На вопрос "Что такое RAG?" ответ: "RAG (Retrieval-Augmented Generation) — это архитектура, которая позволяет LLM отвечать на вопросы, опираясь на внешние источники [1]. Она состоит из этапов индексации, поиска и генерации [2]."

8. Промт для мульти-запросного расширения

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

Создай 5 альтернативных формулировок для заданного вопроса. Варианты должны быть семантически близки, но использовать разные слова и синтаксис. Верни их в виде списка.

Исходный вопрос: {question}

Варианты:

Пример результата:
Для вопроса "Как работает RAG?" варианты: (1) "Опишите принцип работы RAG", (2) "Какие шаги включает RAG-пайплайн?", (3) "Как RAG использует внешние данные?", (4) "Объясните механизм retrieval в RAG", (5) "Что такое retrieval-augmented generation?". Код:

from langchain.prompts import PromptTemplate
from langchain.llms import OpenAI

prompt = PromptTemplate.from_template("Создай 5 альтернативных формулировок для вопроса: {question}")
llm = OpenAI(model="gpt-4", temperature=0.7)
queries = llm.invoke(prompt.format(question="Как работает RAG?"))
print(queries)

9. Промт для фильтрации по метаданным

Задача: Ограничить поиск только теми чанками, которые соответствуют определённым метаданным (дата, автор, категория).
Промт:

Выполни поиск по векторной базе данных, применив фильтр: дата публикации позже 2024-01-01, категория = 'AI'. Используй метаданные, сохранённые в коллекции. Верни топ-3 чанка с их метаданными.

Запрос: {query}
Фильтр: {filter}

Результат:

Пример результата:
Возвращает только те чанки, которые прошли фильтр. Код:

import chromadb

client = chromadb.Client()
collection = client.get_or_create_collection("docs")
results = collection.query(
    query_texts=[query],
    n_results=3,
    where={"date": {"$gte": "2024-01-01"}, "category": "AI"}
)
print(results['documents'])

10. Промт для оценки качества ответа

Задача: Проверить, насколько ответ соответствует предоставленным чанкам, и выставить оценку от 0 до 1.
Промт:

Оцени ответ по критериям: (1) релевантность — использует ли ответ только факты из чанков? (2) полнота — охватывает ли ответ все аспекты вопроса? (3) точность цитирования — верно ли указаны номера чанков? Верни оценку от 0 до 1 и пояснение.

Вопрос: {question}
Чанки: {chunks}
Ответ: {answer}

Оценка:

Пример результата:
Оценка 0.85 с пояснением: "Ответ корректен, но не упомянут аспект из чанка [3]." Код:

from langchain.evaluation import load_evaluator

evaluator = load_evaluator("labeled_criteria", criteria="relevance")
result = evaluator.evaluate_strings(
    prediction=answer,
    reference=chunks,
    input=question
)
print(result)

Заключение

Эти 10 промтов покрывают ключевые этапы работы RAG-системы: от разбиения документов до генерации ответа и оценки его качества. Начните с базовых промтов для чанкинга и эмбеддингов, затем переходите к гибридному поиску и реранжированию, а для продакшена используйте экспертные промты с мульти-запросами и фильтрацией. Помните, что промты — это не статичные шаблоны: их нужно адаптировать под вашу предметную область, тип документов и модель LLM. Экспериментируйте с параметрами (размер чанка, коэффициенты гибридного поиска) и используйте метрики вроде Precision@k и Recall@k для объективной оценки.

Готовы попробовать? Возьмите любой документ, примените промт для чанкинга, загрузите в ChromaDB и запустите поиск. Улучшение качества ответов не заставит себя ждать.

← Все статьи

Комментарии