Contrastive Chain-of-Thought

[object Object]

Contrastive Chain-of-Thought (Contrastive CoT) — расширение классического Chain of Thought, где помимо правильных примеров модели показывают и ошибочные reasoning traces с пометкой, где именно произошёл сбой. В 2026 это полезно понимать как error-aware few-shot pattern: техника хороша не везде, а именно там, где модель систематически попадает в повторяющиеся логические ловушки.

Представьте урок математики. Учитель не только показывает правильное решение, но и разбирает типичную ошибку: «Вот так решили — и это неправильно, потому что забыли учесть скобки». Вы запоминаете, на что обратить внимание. Contrastive CoT работает так же — показывает модели и правильный, и ошибочный путь, чтобы она не повторяла чужих промахов.

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

Contrastive CoT — простая модификация Few-shot CoT. К каждому примеру с правильным рассуждением добавляется пример с ошибочным рассуждением на ту же задачу. Модель учится не только «как правильно», но и «как неправильно» — и в результате реже допускает типичные ошибки.

Формат: для каждого демонстрационного вопроса вы показываете два варианта решения — правильный и неправильный. В неправильном явно указана ошибка.

ПромптChatGPT
Вопрос: В магазине яблоки стоят 80 руб/кг. Маша купила 2.5 кг и дала 500 руб. Сколько сдачи?

[Правильное рассуждение]
1. Стоимость покупки: 2.5 × 80 = 200 руб.
2. Сдача: 500 − 200 = 300 руб.
Ответ: 300 руб. ✓

[Неправильное рассуждение]
1. Стоимость покупки: 2 × 80 = 160 руб. ← Ошибка: проигнорировали 0.5 кг
2. Сдача: 500 − 160 = 340 руб.
Ответ: 340 руб. ✗

Теперь реши:
Вопрос: Бензин стоит 55 руб/литр. Водитель залил 32.5 литра и заплатил 2000 руб. Сколько сдачи?
Ответ модели

Рассуждение:

  1. Стоимость бензина: 32.5 × 55 = 1787.50 руб.
  2. Сдача: 2000 − 1787.50 = 212.50 руб. Ответ: 212 руб. 50 коп.

Подходит для: математических задач, логического анализа, задач с «ловушками», где модели часто допускают типичные ошибки.

Что такое Contrastive CoT

Классический Few-shot CoT показывает модели только правильные цепочки рассуждений. Это работает хорошо, но у подхода есть слабое место: модель не знает, какие ошибки типичны и как их избежать. Она учится «как правильно», но не учится «как неправильно».

Исследователи из DAMO-NLP-SG (Chia et al., 2023) предложили Contrastive CoT — технику, где каждый пример содержит пару рассуждений:

  • Положительный пример — правильное пошаговое решение
  • Отрицательный пример — ошибочное решение с указанием, где именно допущена ошибка

Идея вдохновлена контрастивным обучением (contrastive learning) в машинном обучении: модель учится различать правильное и неправильное, а не просто имитировать правильное.

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

Примеры использования

Математика: ловушка с процентами

ПромптClaude Sonnet 4
Вопрос: Товар стоил 2000 руб. Цену подняли на 25%, а потом снизили на 25%. Какова итоговая цена?

[Правильное рассуждение]
1. Повышение на 25%: 2000 × 1.25 = 2500 руб.
2. Снижение на 25% от новой цены: 2500 × 0.75 = 1875 руб.
Ответ: 1875 руб. ✓

[Неправильное рассуждение]
1. Подняли на 25%, потом снизили на 25% — значит вернулись к начальной цене.
Ответ: 2000 руб. ✗
← Ошибка: 25% снижения считается от новой цены (2500), а не от исходной (2000). Процент от разных баз даёт разные суммы.

Теперь реши:
Вопрос: Зарплату сотрудника увеличили на 20%, а через полгода снизили на 20%. Исходная зарплата — 80 000 руб. Какова итоговая?
Ответ модели

Рассуждение:

  1. Повышение на 20%: 80 000 × 1.20 = 96 000 руб.
  2. Снижение на 20% от новой зарплаты: 96 000 × 0.80 = 76 800 руб.

Важно: нельзя считать, что +20% и −20% «отменяют» друг друга. 20% от 96 000 (19 200) больше, чем 20% от 80 000 (16 000).

Ответ: 76 800 руб.

Логика: ошибка в порядке условий

ПромптChatGPT
Вопрос: Все розы — цветы. Некоторые цветы быстро вянут. Можно ли утверждать, что некоторые розы быстро вянут?

[Правильное рассуждение]
1. Все розы — подмножество цветов.
2. Некоторые цветы быстро вянут — но мы не знаем, входят ли розы в эту группу.
3. «Некоторые цветы» могут быть тюльпанами, ромашками и т.д. — не обязательно розами.
4. Вывод: нельзя утверждать наверняка.
Ответ: Нет, нельзя утверждать. ✓

[Неправильное рассуждение]
1. Все розы — цветы.
2. Некоторые цветы быстро вянут.
3. Значит, некоторые розы тоже быстро вянут.
Ответ: Да, можно утверждать. ✗
← Ошибка: из того, что розы — подмножество цветов, не следует, что свойство «некоторых цветов» обязательно распространяется на розы. Это классическая ошибка недопустимого среднего термина.

Теперь реши:
Вопрос: Все студенты сдали экзамен. Некоторые сдавшие получили отлично. Можно ли утверждать, что некоторые студенты получили отлично?
Ответ модели

Рассуждение:

  1. Все студенты входят в множество «сдавших экзамен».
  2. Некоторые из сдавших получили отлично.
  3. Студенты — подмножество сдавших. Но «некоторые сдавшие» с отличным результатом могут быть и не студентами (например, вольные слушатели).
  4. Без дополнительной информации мы не знаем, пересекаются ли множества «студенты» и «получившие отлично».

Ответ: нельзя утверждать наверняка — информации недостаточно для однозначного вывода.

Анализ данных: ошибка выжившего

ПромптClaude Sonnet 4
Вопрос: 5 из 6 успешных стартапов использовали методологию X. Стоит ли внедрять методологию X?

[Правильное рассуждение]
1. 5 из 6 успешных стартапов использовали X — но это только успешные.
2. Сколько неуспешных стартапов тоже использовали X? Если 500 из 600 провалившихся тоже использовали X, то корреляция с успехом ничтожна.
3. Без данных о неуспешных стартапах нельзя делать вывод о причинно-следственной связи.
4. Это классическая ошибка выжившего (survivorship bias).
Ответ: Недостаточно данных для вывода. ✓

[Неправильное рассуждение]
1. 5 из 6 успешных стартапов использовали X.
2. Значит, методология X ведёт к успеху.
3. Нужно срочно внедрять X.
Ответ: Да, стоит внедрять. ✗
← Ошибка: рассмотрены только успешные случаи. Игнорирование неуспешных стартапов с тем же X — ошибка выжившего.

Теперь реши:
Вопрос: 90% лучших программистов компании пьют более 3 чашек кофе в день. Стоит ли рекомендовать сотрудникам пить больше кофе для повышения продуктивности?
Ответ модели

Рассуждение:

  1. 90% лучших программистов пьют 3+ чашек кофе — но это данные только по лучшим сотрудникам.
  2. Нужно проверить: сколько слабых программистов тоже пьют 3+ чашек? Если большинство — корреляция ложная.
  3. Возможно, лучшие программисты просто работают дольше (и поэтому пьют больше кофе). Кофе — следствие, а не причина.
  4. Это и ошибка выжившего (смотрим только на лучших), и путаница корреляции с причинностью.

Ответ: нет, данных недостаточно. Нужно сравнение с остальными сотрудниками и контролируемый эксперимент.

Сравнение: стандартный CoT vs Contrastive CoT

Стандартный Few-shot CoT
Показываем только правильные рассуждения. Модель учится имитировать верный паттерн, но не знает, какие ошибки типичны. На задачах-ловушках может попасться.
Contrastive CoT
Показываем пары: правильное + ошибочное рассуждение. Модель видит типичные ловушки и активно их обходит. Особенно эффективно на задачах с контринтуитивными ответами.

Типичные ошибки для отрицательных примеров

Какие ошибки включать в отрицательные примеры? Вот самые распространённые:

Тип ошибкиПримерГде встречается
Пропуск шагаЗабыли учесть налог или скидкуМатематика, финансы
Неверная операцияСложение вместо умноженияАрифметика
Ошибка в логикеПутаница необходимого и достаточногоЛогические задачи
Ошибка выжившегоВывод только по успешным случаямАнализ данных
Корреляция как причинность«А связано с B, значит A вызывает B»Статистика, аналитика
Неверная база для процентовПроцент от начальной вместо текущей ценыФинансы, проценты

Когда negative examples реально окупаются

Контрастные примеры особенно полезны, если:

  • ошибка повторяется как pattern, а не как случайный шум;
  • вы можете показать правдоподобный wrong path;
  • задача похожа на экзаменационную или аналитическую, где ловушки типизируемы;
  • few-shot already используется и у вас есть место в prompt budget.

Если же задача open-ended и wrong answers почти всегда разные, contrastive block часто становится дорогим украшением и не даёт устойчивого uplift.

Когда использовать Contrastive CoT

Плюсы

  • Снижает количество типичных ошибок на 5-10% по сравнению с обычным CoT
  • Особенно эффективен на задачах с ловушками и контринтуитивными ответами
  • Простая модификация — не требует новых инструментов или API
  • Модель явно «понимает» чего избегать, а не только что имитировать
  • Работает с любыми моделями, поддерживающими few-shot

Минусы

  • Промпт длиннее — два примера вместо одного, больше токенов
  • Нужно придумывать правдоподобные ошибки — это требует экспертизы
  • Менее эффективен на задачах, где нет типичных ошибок
  • Эффект снижается, если отрицательные примеры нерелевантны целевой задаче

Практическое место техники в 2026

Сегодня Contrastive CoT — это скорее targeted prompt layer, чем универсальный default:

  • хороший fit для exams, finance, legal reasoning, analytics;
  • средний fit для обычного business Q&A;
  • слабый fit для creative writing и open generation.

То есть технику стоит включать по маршруту "мы уже знаем, какие именно ошибки модель любит делать", а не просто потому, что contrastive learning звучит солидно.

Результаты исследования

Точность на арифметических бенчмарках (Chia et al., 2023)
Стандартный промпт18%
Zero-shot CoT57%
Few-shot CoT73%
Contrastive CoT80%

По данным Chia et al. (2023), Contrastive CoT стабильно превосходит стандартный Few-shot CoT на бенчмарках арифметики и логического рассуждения, показывая наибольший прирост на задачах, где модели систематически ошибаются.

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

Contrastive CoT с OpenAI API

from openai import OpenAI

client = OpenAI()


def contrastive_cot(
    question: str,
    demos: list[dict],
) -> str:
    """
    Contrastive CoT: few-shot с парами правильных
    и ошибочных рассуждений.

    demos — список демонстраций:
    [
        {
            "question": "...",
            "correct": "правильное рассуждение...",
            "incorrect": "ошибочное рассуждение...",
            "error_explanation": "почему это ошибка..."
        },
        ...
    ]
    """
    # Собираем промпт с контрастивными парами
    prompt_parts = []
    for demo in demos:
        part = (
            f"Вопрос: {demo['question']}\n\n"
            f"[Правильное рассуждение]\n{demo['correct']}\n\n"
            f"[Неправильное рассуждение]\n{demo['incorrect']}\n"
            f"← Ошибка: {demo['error_explanation']}"
        )
        prompt_parts.append(part)

    prompt_parts.append(
        f"Теперь реши, избегая подобных ошибок:\n"
        f"Вопрос: {question}"
    )

    prompt = "\n\n---\n\n".join(prompt_parts)

    response = client.chat.completions.create(
        model="gpt-4o",
        messages=[
            {
                "role": "system",
                "content": (
                    "Ты решаешь задачи пошагово. Тебе показаны "
                    "примеры правильных и ошибочных рассуждений. "
                    "Учись на ошибках и избегай их."
                ),
            },
            {"role": "user", "content": prompt},
        ],
        temperature=0,
    )
    return response.choices[0].message.content


demos = [
    {
        "question": (
            "Товар стоил 1000 руб. Цену подняли на 10%, "
            "потом снизили на 10%. Какова итоговая цена?"
        ),
        "correct": (
            "1. Повышение: 1000 × 1.10 = 1100 руб.\n"
            "2. Снижение: 1100 × 0.90 = 990 руб.\n"
            "Ответ: 990 руб. ✓"
        ),
        "incorrect": (
            "1. +10% и −10% дают 0% изменения.\n"
            "Ответ: 1000 руб. ✗"
        ),
        "error_explanation": (
            "Проценты считаются от разных баз. "
            "10% от 1100 ≠ 10% от 1000."
        ),
    },
    {
        "question": "В команде 8 человек. 75% сдали тест. Сколько не сдали?",
        "correct": (
            "1. Сдали: 8 × 0.75 = 6 человек.\n"
            "2. Не сдали: 8 − 6 = 2 человека.\n"
            "Ответ: 2 человека. ✓"
        ),
        "incorrect": (
            "1. Не сдали: 75% от 8 = 6 человек. ✗"
        ),
        "error_explanation": (
            "75% — это доля сдавших, а не проваливших. "
            "Не сдали 25%, то есть 2 человека."
        ),
    },
]

answer = contrastive_cot(
    question=(
        "Вклад 100 000 руб. под 10% годовых. "
        "Через год начислили проценты, на второй год "
        "ставку снизили до 5%. Сколько на счёте через 2 года?"
    ),
    demos=demos,
)
print(answer)

Contrastive CoT с Anthropic API

import anthropic

client = anthropic.Anthropic()


def contrastive_cot_anthropic(
    question: str,
    demos: list[dict],
) -> str:
    """
    Contrastive CoT через Anthropic Messages API.
    Использует чередование user/assistant для демонстраций.
    """
    messages = []

    # Демонстрации как пары user/assistant
    for demo in demos:
        # User показывает задачу
        messages.append({
            "role": "user",
            "content": f"Вопрос: {demo['question']}",
        })
        # Assistant показывает оба варианта
        messages.append({
            "role": "assistant",
            "content": (
                f"[Правильное рассуждение]\n{demo['correct']}\n\n"
                f"[Неправильное рассуждение]\n{demo['incorrect']}\n"
                f"← Ошибка: {demo['error_explanation']}"
            ),
        })

    # Целевой вопрос
    messages.append({
        "role": "user",
        "content": (
            f"Реши задачу, избегая ошибок из примеров выше.\n"
            f"Вопрос: {question}"
        ),
    })

    message = client.messages.create(
        model="claude-sonnet-4-20250514",
        max_tokens=1024,
        system=(
            "Ты решаешь задачи пошагово. Тебе показаны примеры "
            "правильных и ошибочных рассуждений. Учись на ошибках: "
            "обращай внимание на типичные заблуждения и избегай их."
        ),
        messages=messages,
    )
    return message.content[0].text


logic_demos = [
    {
        "question": (
            "Все кошки — животные. Некоторые животные "
            "живут в воде. Живут ли некоторые кошки в воде?"
        ),
        "correct": (
            "1. Кошки — подмножество животных.\n"
            "2. «Некоторые животные живут в воде» — "
            "это могут быть рыбы, дельфины и т.д.\n"
            "3. Нет оснований утверждать, что кошки "
            "входят в группу водных животных.\n"
            "Ответ: Нельзя утверждать. ✓"
        ),
        "incorrect": (
            "1. Кошки — животные.\n"
            "2. Некоторые животные живут в воде.\n"
            "3. Значит, некоторые кошки живут в воде.\n"
            "Ответ: Да. ✗"
        ),
        "error_explanation": (
            "Ошибка недопустимого среднего термина. "
            "Свойство подмножества не обязано совпадать "
            "со свойством надмножества."
        ),
    },
]

answer = contrastive_cot_anthropic(
    question=(
        "Все программисты знают математику. "
        "Некоторые знающие математику работают в банках. "
        "Работают ли некоторые программисты в банках?"
    ),
    demos=logic_demos,
)
print(answer)

Генерация отрицательных примеров через LLM

def generate_negative_example(
    question: str,
    correct_reasoning: str,
) -> dict:
    """
    Генерирует правдоподобный, но ошибочный
    вариант рассуждения через LLM.
    """
    response = client.chat.completions.create(
        model="gpt-4o",
        messages=[
            {
                "role": "system",
                "content": (
                    "Ты генерируешь правдоподобные, но ошибочные "
                    "рассуждения для обучения. Ошибка должна быть "
                    "типичной и неочевидной, но явно помеченной."
                ),
            },
            {
                "role": "user",
                "content": (
                    f"Вопрос: {question}\n\n"
                    f"Правильное рассуждение:\n{correct_reasoning}\n\n"
                    f"Сгенерируй одну правдоподобную, но ошибочную "
                    f"цепочку рассуждений для этой же задачи. "
                    f"Формат ответа (строго):\n"
                    f"РАССУЖДЕНИЕ: <ошибочная цепочка>\n"
                    f"ОШИБКА: <объяснение, в чём ошибка>"
                ),
            },
        ],
        temperature=0.7,
    )
    text = response.choices[0].message.content

    # Парсим ответ
    parts = text.split("ОШИБКА:")
    reasoning = parts[0].replace("РАССУЖДЕНИЕ:", "").strip()
    explanation = parts[1].strip() if len(parts) > 1 else ""

    return {
        "question": question,
        "correct": correct_reasoning,
        "incorrect": reasoning,
        "error_explanation": explanation,
    }


demo = generate_negative_example(
    question=(
        "За 3 дня магазин продал 150 товаров. "
        "Сколько продаст за 2 недели при том же темпе?"
    ),
    correct_reasoning=(
        "1. Темп продаж: 150 ÷ 3 = 50 товаров/день.\n"
        "2. 2 недели = 14 дней.\n"
        "3. За 14 дней: 50 × 14 = 700 товаров.\n"
        "Ответ: 700 товаров."
    ),
)
print(f"Ошибочное: {demo['incorrect']}")
print(f"Почему ошибка: {demo['error_explanation']}")
Отрицательные примеры должны быть правдоподобными — такими, которые модель реально могла бы сгенерировать. Абсурдные ошибки (2 + 2 = 5) бесполезны. Самые ценные — ошибки, связанные с неверной интерпретацией условия, пропуском шага или путаницей причины и следствия.

Сравнение подходов

Прирост точности на бенчмарках рассуждения
Стандартный промпт18%
Zero-shot CoT57%
Few-shot CoT (3 примера)73%
Contrastive CoT (3 пары)80%
Contrastive CoT + Self-Consistency85%

Contrastive CoT даёт стабильный прирост в 5-10% над обычным Few-shot CoT. В сочетании с Self-Consistency (несколько прогонов + голосование) результат ещё выше.

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

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

1. Чем Contrastive CoT отличается от обычного Few-shot CoT?

2. Какие ошибки лучше всего включать в отрицательные примеры?

3. Главный недостаток Contrastive CoT по сравнению с обычным CoT?