Rethinking with Retrieval (RwR)

[object Object]

Rethinking with Retrieval (RwR, переосмысление через поиск) — техника, которая верифицирует каждый шаг цепочки рассуждений через поиск по внешним знаниям. Модель рассуждает пошагово как в обычном CoT, но после каждого шага система находит подтверждающее или опровергающее свидетельство из базы знаний.

В 2026 году RwR удобнее понимать как verification pattern поверх reasoning, а не как просто ещё один вид RAG. Его сильная сторона не в том, что он “добавляет контекст”, а в том, что он навязывает промежуточным шагам дисциплину проверки до того, как они начнут влиять на следующие выводы.

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

Суть в двух словах

Обычный Chain of Thought (CoT) генерирует всю цепочку рассуждений за раз. Модель опирается на внутренние знания, которые могут быть неточными — особенно в деталях: датах, числах, именах. Ошибка на раннем шаге каскадирует через все последующие.

Rethinking with Retrieval добавляет фактчекинг:

  1. Модель генерирует шаг рассуждения (CoT step)
  2. По этому шагу формируется поисковый запрос
  3. Из базы знаний (Wikipedia, документация, корпоративная БЗ) извлекается evidence
  4. Evidence подтверждает или опровергает шаг — если опровергает, шаг корректируется
  5. Только верифицированный шаг используется для дальнейшего рассуждения
ПромптClaude
Ответь на вопрос, рассуждая пошагово. После каждого шага проверяй свои утверждения через поиск.

Вопрос: Кто написал роман, по которому снят фильм «Бегущий по лезвию»? Какие ещё экранизации произведений этого автора существуют?

Шаг 1 (рассуждение): Фильм «Бегущий по лезвию» (Blade Runner, 1982) снят по роману.
[Поиск: 'Blade Runner фильм 1982 первоисточник роман']
[Найдено: Фильм основан на романе Филипа К. Дика «Мечтают ли андроиды об электроовцах?» (1968)]
Шаг 1 (верифицирован): Фильм снят по роману Филипа К. Дика «Мечтают ли андроиды об электроовцах?» (1968). Подтверждено.

Шаг 2 (рассуждение): Другие экранизации Дика — «Вспомнить всё» и «Особое мнение».
[Поиск: 'экранизации произведений Филип Дик список']
[Найдено: Более 10 экранизаций: Total Recall (1990, 2012), Minority Report (2002), A Scanner Darkly (2006), The Man in the High Castle (сериал, 2015-2019), The Adjustment Bureau (2011), Next (2007).]
Шаг 2 (уточнён): Экранизаций значительно больше двух. Помимо «Вспомнить всё» и «Особого мнения» — «Помутнение» (2006), «Человек в высоком замке» (2015-2019), «Меняющие реальность» (2011), «Пророк» (2007) и другие.

Итог: Роман написал Филип К. Дик. Его экранизации включают «Вспомнить всё», «Особое мнение», «Помутнение», «Человек в высоком замке» и ряд других фильмов и сериалов.
Ответ модели

RwR выявил неполноту на шаге 2: модель назвала только 2 экранизации из 10+. Поиск расширил ответ. Без RwR ответ был бы формально верным, но неполным — типичная проблема CoT на фактоёмких вопросах.

RwR (He et al., 2023) и RAT (Wang et al., 2024) решают схожую задачу — retrieval на каждом шаге CoT. Ключевое отличие: RwR фокусируется на верификации (подтвердить или опровергнуть шаг), а RAT — на ревизии (переписать шаг с учётом найденного). RwR ближе к фактчекингу, RAT — к итеративному уточнению.

Как работает RwR

Алгоритм по шагам

Обычный CoT vs RwR

Обычный CoT
Шаг 1: Кофе изобрели в Бразилии в XVII веке. Шаг 2: Бразилия стала крупнейшим экспортёром. Шаг 3: Поэтому бразильский кофе считается эталоном. Итог: Содержит галлюцинацию — кофе открыт в Эфиопии, не в Бразилии. Все выводы построены на ложной предпосылке.
Rethinking with Retrieval
Шаг 1: Кофе изобрели в Бразилии. [Evidence: Кофе обнаружен в Эфиопии (IX-XV вв.), привезён в Бразилию в 1727 г.] Шаг 1 (исправлен): Кофе открыт в Эфиопии, привезён в Бразилию в XVIII в. Шаг 2: Бразилия стала крупнейшим производителем. [Evidence: С XIX века — да, крупнейший в мире.] Итог: Факты верны, каждый шаг подтверждён источником.

Плюсы и минусы

Плюсы

  • Снижает галлюцинации — каждый шаг подкреплён evidence из внешних источников
  • Faithful reasoning — рассуждения основаны на фактах, а не на внутренних знаниях модели
  • Работает с любой базой знаний: Wikipedia, документация, корпоративные данные
  • Прозрачность: для каждого утверждения можно показать источник
  • Совместим с существующими RAG-пайплайнами — расширяет, а не заменяет

Минусы

  • Дороже обычного CoT: каждый шаг = retrieval + верификация = больше вызовов LLM
  • Медленнее: последовательная обработка, нельзя параллелить зависимые шаги
  • Качество зависит от retriever: если база знаний неполная — верификация неэффективна
  • Overkill для задач без фактологии: математика, логика, творчество

Результаты бенчмарков

Точность RwR vs базовых методов на фактоёмких задачах (%)
RwR — CommonsenseQA81%
CoT — CommonsenseQA73%
RwR — StrategyQA72%
CoT — StrategyQA65%
RwR — TempQA68%
CoT — TempQA57%

RwR показывает наибольший прирост на задачах, где:

  • Ответ зависит от конкретных фактов, которые модель может не знать или путать
  • Требуется multi-hop reasoning — связывание нескольких фактов в цепочку
  • Ошибка в одном факте каскадирует и портит весь вывод
ПромптChatGPT
Используй Rethinking with Retrieval для ответа:

Вопрос: Верно ли, что автор «Мастера и Маргариты» родился в Киеве, а умер в Москве?

Шаг 1 (рассуждение): Автор «Мастера и Маргариты» — Михаил Булгаков.
[Поиск: 'автор Мастер и Маргарита']
[Evidence: Роман написан Михаилом Афанасьевичем Булгаковым, работа над ним велась в 1928-1940 гг.]
Шаг 1: Подтверждено. Автор — Булгаков.

Шаг 2 (рассуждение): Булгаков родился в Киеве.
[Поиск: 'Михаил Булгаков место рождения']
[Evidence: Булгаков родился 3 (15) мая 1891 года в Киеве.]
Шаг 2: Подтверждено. Родился в Киеве.

Шаг 3 (рассуждение): Булгаков умер в Москве.
[Поиск: 'Михаил Булгаков дата и место смерти']
[Evidence: Умер 10 марта 1940 года в Москве от гипертонического нефросклероза.]
Шаг 3: Подтверждено. Умер в Москве.

Итог: Да, утверждение верно. Булгаков родился в Киеве (1891), умер в Москве (1940). Все три факта подтверждены через поиск.
Ответ модели

Каждый факт проверен отдельно: авторство, место рождения, место смерти. Модель не полагалась на внутренние знания — каждое утверждение подкреплено evidence. Даже на «простом» вопросе это гарантирует точность.

  • Задачи без фактологии: математические вычисления, формальная логика, генерация кода — retrieval не даст полезного evidence.
  • Отсутствие базы знаний: если информация нигде не задокументирована, верифицировать нечем.
  • Субъективные вопросы: «Какой язык программирования лучший?» — нет однозначного evidence.
  • Жёсткие ограничения latency: каждый шаг — это дополнительный retrieval + LLM-вызов.

Как понимать RwR в 2026

Если RAT больше похож на iterative revision, то RwR ближе к reasoning quality control. В современных системах его имеет смысл использовать как слой промежуточной верификации:

  • до финального ответа
  • до tool execution
  • до передачи шага следующему агенту или маршруту

То есть RwR хорошо встраивается в trace-first пайплайны, где важно не просто получить ответ, а понимать, какие промежуточные утверждения были проверены и где система изменила курс.

Где RwR реально полезен

Техника окупается сильнее всего там, где:

  • нужно не просто найти факты, а проверить уже сформулированный шаг
  • регуляторные, policy или compliance-вопросы требуют evidence-backed reasoning
  • downstream action зависит от корректности промежуточного вывода
  • вам нужен audit trail: какое утверждение было проверено, чем и с каким вердиктом

В этих сценариях RwR часто полезнее, чем “более умная модель”, потому что проблема не в силе reasoning как таковой, а в отсутствии дисциплины промежуточной проверки.

Ограничения и stop rules

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

  • шаг не формулируется как проверяемый claim
  • evidence регулярно не находит ничего лучше, чем исходная догадка модели
  • latency budget меньше, чем цена нескольких retrieval-итераций

Практическое правило простое: если шаг можно однозначно проверить через источник, RwR полезен. Если шаг в основном интерпретационный или генеративный, verification layer быстро превращается в дорогую формальность.

Техническая реализация

Базовый пайплайн RwR на Python

from openai import OpenAI
from dataclasses import dataclass

client = OpenAI()


@dataclass
class VerificationResult:
    """Результат верификации одного шага."""
    original_step: str
    evidence: list[str]
    verdict: str  # "confirmed", "corrected", "unverified"
    corrected_step: str


COT_PROMPT = """Разбей ответ на вопрос на отдельные шаги рассуждения.
Каждый шаг должен содержать одно конкретное, проверяемое утверждение.

Вопрос: {question}

Формат:
Step 1: ...
Step 2: ...
Step N: ..."""

QUERY_PROMPT = """Сформулируй поисковый запрос для проверки этого утверждения.
Запрос должен быть коротким, конкретным и содержать ключевые сущности.

Утверждение: {step}

Верни только поисковый запрос, ничего больше."""

VERIFY_PROMPT = """Сопоставь утверждение с найденным evidence.

Утверждение: {step}

Evidence:
{evidence}

Предыдущие верифицированные шаги:
{previous_steps}

Определи:
1. Утверждение ПОДТВЕРЖДЕНО, ОПРОВЕРГНУТО или НЕЛЬЗЯ ПРОВЕРИТЬ?
2. Если опровергнуто — дай исправленную версию.

Формат ответа:
VERDICT: confirmed | corrected | unverified
CORRECTED: <исправленное утверждение или "N/A">"""

FINAL_PROMPT = """Сформулируй финальный ответ на основе верифицированных шагов.

Вопрос: {question}

Верифицированные шаги:
{steps}

Дай точный, фактологически верный ответ."""


def generate_cot_steps(question: str) -> list[str]:
    """Шаг 1: генерация CoT."""
    response = client.chat.completions.create(
        model="gpt-4o",
        messages=[{"role": "user", "content": COT_PROMPT.format(
            question=question
        )}],
        temperature=0,
    )
    lines = response.choices[0].message.content.strip().split("\n")
    steps = []
    for line in lines:
        line = line.strip()
        if line.lower().startswith("step"):
            step = line.split(":", 1)[1].strip() if ":" in line else line
            steps.append(step)
    return steps


def formulate_query(step: str) -> str:
    """Шаг 2: формирование поискового запроса."""
    response = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[{"role": "user", "content": QUERY_PROMPT.format(
            step=step
        )}],
        temperature=0,
    )
    return response.choices[0].message.content.strip()


def retrieve_evidence(query: str, retriever) -> list[str]:
    """Шаг 3: retrieval из базы знаний.

    retriever — любая функция str -> list[str]:
    - BM25 по Wikipedia
    - Vector search (Qdrant, Chroma)
    - Web search API (Tavily, Exa)
    """
    return retriever(query)


def verify_step(
    step: str,
    evidence: list[str],
    previous_steps: list[str]
) -> VerificationResult:
    """Шаг 4: верификация с учётом evidence."""
    evidence_text = "\n---\n".join(evidence) if evidence else "(нет evidence)"
    prev_text = "\n".join(
        f"  {i+1}. {s}" for i, s in enumerate(previous_steps)
    ) or "(нет)"

    response = client.chat.completions.create(
        model="gpt-4o",
        messages=[{"role": "user", "content": VERIFY_PROMPT.format(
            step=step,
            evidence=evidence_text,
            previous_steps=prev_text,
        )}],
        temperature=0,
    )

    content = response.choices[0].message.content.strip()

    # Парсинг ответа
    verdict = "unverified"
    corrected = step
    for line in content.split("\n"):
        line = line.strip()
        if line.startswith("VERDICT:"):
            v = line.split(":", 1)[1].strip().lower()
            if v in ("confirmed", "corrected", "unverified"):
                verdict = v
        elif line.startswith("CORRECTED:"):
            c = line.split(":", 1)[1].strip()
            if c != "N/A":
                corrected = c

    return VerificationResult(
        original_step=step,
        evidence=evidence,
        verdict=verdict,
        corrected_step=corrected if verdict == "corrected" else step,
    )


def rethinking_with_retrieval(
    question: str,
    retriever,  # Callable[[str], list[str]]
    top_k: int = 3,
) -> dict:
    """Полный пайплайн Rethinking with Retrieval."""

    # 1. Генерация CoT
    steps = generate_cot_steps(question)

    # 2-4. Для каждого шага: query -> retrieve -> verify
    verified_steps: list[str] = []
    verification_log: list[VerificationResult] = []

    for step in steps:
        query = formulate_query(step)
        evidence = retrieve_evidence(query, retriever)[:top_k]
        result = verify_step(step, evidence, verified_steps)
        verification_log.append(result)
        verified_steps.append(result.corrected_step)

    # 5. Финальный ответ
    steps_text = "\n".join(
        f"  {i+1}. [{v.verdict}] {v.corrected_step}"
        for i, v in enumerate(verification_log)
    )
    final = client.chat.completions.create(
        model="gpt-4o",
        messages=[{"role": "user", "content": FINAL_PROMPT.format(
            question=question,
            steps=steps_text,
        )}],
        temperature=0,
    )

    return {
        "answer": final.choices[0].message.content.strip(),
        "steps": [
            {
                "original": v.original_step,
                "corrected": v.corrected_step,
                "verdict": v.verdict,
                "evidence_count": len(v.evidence),
            }
            for v in verification_log
        ],
        "total_steps": len(steps),
        "corrected_count": sum(
            1 for v in verification_log if v.verdict == "corrected"
        ),
    }


# — Пример с BM25 retriever ---
# pip install rank-bm25

from rank_bm25 import BM25Okapi


def build_bm25_retriever(corpus: list[str]):
    """Простой BM25 retriever по корпусу текстов."""
    tokenized = [doc.lower().split() for doc in corpus]
    bm25 = BM25Okapi(tokenized)

    def search(query: str, k: int = 3) -> list[str]:
        scores = bm25.get_scores(query.lower().split())
        top_indices = sorted(
            range(len(scores)), key=lambda i: scores[i], reverse=True
        )[:k]
        return [corpus[i] for i in top_indices if scores[i] > 0]

    return search


# Пример использования
knowledge_base = [
    "Санкт-Петербург основан 27 мая 1703 года Петром I.",
    "Санкт-Петербург был столицей Российской империи с 1712 по 1918 год.",
    "Население Санкт-Петербурга — около 5.6 млн человек (2024).",
    "Эрмитаж — один из крупнейших музеев мира, основан в 1764 году.",
    "Московский Кремль построен в XV веке при Иване III.",
]

retriever = build_bm25_retriever(knowledge_base)
result = rethinking_with_retrieval(
    question="Когда был основан Санкт-Петербург и кем?",
    retriever=retriever,
)
print(result["answer"])
print(f"Исправлено шагов: {result['corrected_count']}/{result['total_steps']}")

Пайплайн с vector search (Qdrant)

from qdrant_client import QdrantClient
from qdrant_client.models import Distance, VectorParams, PointStruct

qdrant = QdrantClient(":memory:")  # или url="http://localhost:6333"


def build_vector_retriever(
    documents: list[str],
    collection_name: str = "knowledge",
):
    """Vector retriever через Qdrant + OpenAI embeddings."""

    # Создание эмбеддингов
    embed_response = client.embeddings.create(
        model="text-embedding-3-small",
        input=documents,
    )
    vectors = [e.embedding for e in embed_response.data]
    dim = len(vectors[0])

    # Создание коллекции
    qdrant.create_collection(
        collection_name=collection_name,
        vectors_config=VectorParams(size=dim, distance=Distance.COSINE),
    )

    # Загрузка документов
    points = [
        PointStruct(id=i, vector=v, payload={"text": doc})
        for i, (v, doc) in enumerate(zip(vectors, documents))
    ]
    qdrant.upsert(collection_name=collection_name, points=points)

    def search(query: str, k: int = 3) -> list[str]:
        q_embed = client.embeddings.create(
            model="text-embedding-3-small",
            input=[query],
        ).data[0].embedding
        results = qdrant.search(
            collection_name=collection_name,
            query_vector=q_embed,
            limit=k,
        )
        return [hit.payload["text"] for hit in results]

    return search

Выбор retriever

RetrieverКогда использоватьLatencyСтоимость
BM25 (rank-bm25, Elasticsearch)Точное совпадение ключевых слов, имена, даты1-10 мсБесплатно
Vector search (Qdrant, Chroma)Семантический поиск, парафразы, смысловая близость10-50 мсНизкая
Hybrid (BM25 + vector)Лучшее из обоих миров: точность + семантика20-60 мсНизкая
Web Search (Tavily, Exa)Актуальные факты, новости, данные не в вашей БЗ200-500 мсСредняя

Стоимость и latency

МетодLLM-вызовыRetrieval-вызовыПримерная стоимость (GPT-4o, 5 шагов)
CoT10~$0.01
RAG11~$0.02
RwRN + 2N~$0.05-0.12
RwR selectiveN + 2 - SN - S~$0.03-0.08

Где N — число шагов, S — число пропущенных (модель уверена в шаге). На практике 30-40% шагов не требуют верификации, если модель обладает надёжными знаниями в предметной области.

  1. Слишком общие поисковые запросы — шаг «анализируем контекст» порождает бесполезный поиск. Решение: добавьте промпт для переформулирования шага в конкретный запрос с ключевыми сущностями.
  2. Слепое доверие evidence — не все найденные документы корректны. Добавьте scoring релевантности: если top-1 результат имеет низкий score, лучше отметить шаг как unverified, чем «подтверждать» нерелевантным текстом.
  3. Retriever не покрывает предметную область — если в базе знаний нет информации о теме вопроса, RwR деградирует до обычного CoT. Решение: используйте web search как fallback, когда vector search не даёт результатов с высоким score.
  4. Излишняя гранулярность шагов — если CoT разбит на 15 микрошагов, retrieval на каждом становится избыточным. Оптимально: 3-7 шагов, каждый содержит одно проверяемое утверждение.

Проверьте себя

Проверьте себя

1. В чём ключевая идея Rethinking with Retrieval?

2. Когда RwR даёт наибольший выигрыш по сравнению с обычным CoT?

3. Какой retriever лучше всего подходит для RwR при работе с корпоративной документацией?