Модуль 01 Средний ⏱ 40 мин

Chain-of-Thought Prompting

Когда задача требует рассуждений — математика, логика, многошаговый анализ — модель даёт значительно лучший ответ, если думает вслух пошагово. Chain-of-Thought — самая мощная техника промптинга для сложных задач и фундамент всех современных агентных систем.

Что нужно знать: Zero-shot и few-shot, роли сообщений

Почему модель ошибается без CoT

LLM генерирует токены последовательно: каждый следующий токен зависит от предыдущих. Если сразу потребовать финальный ответ на сложный вопрос — модель «пропускает» промежуточные шаги и вероятность ошибки резко растёт.

Без CoT — прямой ответ
Вопрос: В магазине 23 яблока. Если купить ещё столько же минус 10, а потом съесть треть всех яблок — сколько останется?

Промпт: Ответь одним числом.
Ответ: 18  ✗ (неверно)
С CoT — пошаговое рассуждение
Шаг 1: Купить ещё 23 − 10 = 13 яблок
Шаг 2: Итого: 23 + 13 = 36 яблок
Шаг 3: Съесть треть: 36 / 3 = 12
Шаг 4: Остаток: 36 − 12 = 24
Ответ: 24  ✓ (верно)

Ключевая идея: сгенерированные токены рассуждения становятся частью контекста и помогают правильно вычислить следующий шаг. CoT превращает один «прыжок» в цепочку маленьких.

Zero-shot CoT: «думай пошагово»

Самый простой приём: добавить фразу, которая указывает модели рассуждать до ответа. Работает без примеров.

Python — zero-shot CoT триггеры
import anthropic

client = anthropic.AsyncAnthropic()

# Самая известная фраза — из оригинальной статьи Kojima et al. 2022
ZERO_SHOT_COT_TRIGGERS = [
    "Думай пошагово.",                          # самый простой
    "Let's think step by step.",                # оригинал (EN)
    "Реши задачу шаг за шагом, затем дай ответ.",
    "Сначала разбери задачу на шаги, потом ответь.",
    "Объясни своё рассуждение, прежде чем дать финальный ответ.",
]

async def solve_with_cot(problem: str, extract_answer: bool = True) -> dict:
    """
    Решаем задачу с zero-shot CoT.
    Если extract_answer=True — делаем второй запрос для извлечения ответа.
    """
    # Шаг 1: рассуждение
    reasoning_response = await client.messages.create(
        model="claude-opus-4-6",
        system="Ты — точный и аккуратный решатель задач.",
        messages=[{
            "role": "user",
            "content": f"{problem}\n\nДумай пошагово.",
        }],
        max_tokens=1024,
        temperature=0,
    )
    reasoning = reasoning_response.content[0].text

    if not extract_answer:
        return {"reasoning": reasoning, "answer": None}

    # Шаг 2: извлечение финального ответа
    extract_response = await client.messages.create(
        model="claude-opus-4-6",
        messages=[
            {"role": "user",      "content": f"{problem}\n\nДумай пошагово."},
            {"role": "assistant", "content": reasoning},
            {"role": "user",      "content": "Исходя из рассуждения выше, дай только финальный ответ — одно число или краткую фразу."},
        ],
        max_tokens=32,
        temperature=0,
    )
    answer = extract_response.content[0].text.strip()

    return {"reasoning": reasoning, "answer": answer}

# Пример
result = await solve_with_cot(
    "В магазине 23 яблока. Купили ещё столько же минус 10. "
    "Потом съели треть всех яблок. Сколько осталось?"
)
print("Рассуждение:\n", result["reasoning"])
print("\nОтвет:", result["answer"])
💡
Двухшаговый паттерн: рассуждение → извлечение
Разделение на два запроса даёт надёжный вывод: первый генерирует цепочку, второй — вытаскивает только ответ. Без этого модель может смешать рассуждение с ответом в непредсказуемом формате.

Few-shot CoT: показываем как рассуждать

Few-shot CoT — самый точный вариант: в примерах показываем не только ответ, но и всю цепочку рассуждений.

U
User (пример 1)
У Пети 5 конфет. Маша дала ему ещё 3. Потом он съел 2. Сколько осталось?
A
Assistant (пример 1 — цепочка рассуждений)
Шаг 1: Начало — 5 конфет.
Шаг 2: Маша дала 3 → 5 + 3 = 8.
Шаг 3: Съел 2 → 8 − 2 = 6.
Ответ: 6
U
User (пример 2)
В корзине 12 яблок, 8 апельсинов. Убрали половину яблок. Сколько фруктов?
A
Assistant (пример 2 — цепочка)
Шаг 1: Яблоки: 12, апельсины: 8, итого 20.
Шаг 2: Половина яблок: 12 / 2 = 6.
Шаг 3: Осталось яблок: 12 − 6 = 6.
Шаг 4: Итого фруктов: 6 + 8 = 14.
Ответ: 14
?
User (реальный вопрос)
В магазине 23 яблока. Купили ещё столько же минус 10. Потом съели треть всех. Сколько осталось?
Python — few-shot CoT для арифметики
import anthropic

client = anthropic.AsyncAnthropic()

# Примеры с цепочками рассуждений
MATH_COT_EXAMPLES = [
    (
        "У Пети 5 конфет. Маша дала ему ещё 3. Потом он съел 2. Сколько осталось?",
        "Шаг 1: Начало — 5 конфет.\n"
        "Шаг 2: Маша дала 3 → 5 + 3 = 8.\n"
        "Шаг 3: Съел 2 → 8 − 2 = 6.\n"
        "Ответ: 6",
    ),
    (
        "В корзине 12 яблок и 8 апельсинов. Убрали половину яблок. Сколько фруктов стало?",
        "Шаг 1: Яблок 12, апельсинов 8, итого 20.\n"
        "Шаг 2: Половина яблок: 12 / 2 = 6.\n"
        "Шаг 3: Яблок после: 12 − 6 = 6.\n"
        "Шаг 4: Всего фруктов: 6 + 8 = 14.\n"
        "Ответ: 14",
    ),
]

def build_cot_messages(
    examples: list[tuple[str, str]],
    question: str,
) -> list[dict]:
    messages = []
    for q, reasoning in examples:
        messages.append({"role": "user",      "content": q})
        messages.append({"role": "assistant", "content": reasoning})
    messages.append({"role": "user", "content": question})
    return messages

async def few_shot_cot(question: str) -> str:
    messages = build_cot_messages(MATH_COT_EXAMPLES, question)
    response = await client.messages.create(
        model="claude-opus-4-6",
        system="Решай задачи строго пошагово, следуя формату из примеров.",
        messages=messages,
        max_tokens=512,
        temperature=0,
    )
    return response.content[0].text

result = await few_shot_cot(
    "В магазине 23 яблока. Купили ещё столько же минус 10. "
    "Потом съели треть всех яблок. Сколько осталось?"
)
print(result)
# Шаг 1: Купить ещё: 23 − 10 = 13.
# Шаг 2: Итого: 23 + 13 = 36.
# Шаг 3: Съесть треть: 36 / 3 = 12.
# Шаг 4: Остаток: 36 − 12 = 24.
# Ответ: 24

CoT в агентах: структурированное рассуждение

В агентных системах CoT — не просто «думай вслух». Это явная структура: наблюдение → анализ → план → действие. Именно на этом построен ReAct-паттерн.

Python — structured CoT для агента
import anthropic
import re

client = anthropic.AsyncAnthropic()

AGENT_COT_SYSTEM = """
Ты — агент для анализа данных. При получении задачи следуй строгому формату:


  Что я вижу в задаче? Какие данные есть?
  Что означают эти данные? Какие закономерности?
  Какие шаги нужно выполнить?
  Выполняю план шаг за шагом...



Финальный ответ здесь.


Всегда используй этот формат. Не пропускай блоки.
"""

def parse_structured_cot(response_text: str) -> dict:
    """Разбираем структурированный CoT-ответ."""
    result = {}

    # Извлекаем блоки thinking и answer
    thinking_match = re.search(r'(.*?)', response_text, re.DOTALL)
    answer_match   = re.search(r'(.*?)',   response_text, re.DOTALL)

    if thinking_match:
        thinking = thinking_match.group(1)
        for tag in ['observe', 'analyze', 'plan', 'execute']:
            m = re.search(rf'<{tag}>(.*?)', thinking, re.DOTALL)
            result[tag] = m.group(1).strip() if m else ""

    result['answer'] = answer_match.group(1).strip() if answer_match else response_text

    return result

async def agent_cot(task: str) -> dict:
    response = await client.messages.create(
        model="claude-opus-4-6",
        system=AGENT_COT_SYSTEM,
        messages=[{"role": "user", "content": task}],
        max_tokens=1024,
        temperature=0,
    )
    return parse_structured_cot(response.content[0].text)

# Пример задачи
result = await agent_cot(
    "Продажи: январь 100к, февраль 85к, март 120к, апрель 95к. "
    "Есть ли тренд? Какой прогноз на май?"
)
print("Наблюдения:", result.get('observe', '')[:100])
print("Анализ:",     result.get('analyze', '')[:100])
print("Ответ:",      result.get('answer',  '')[:200])

CoT с XML-тегами — рекомендация Anthropic

Python — thinking тег для скрытого рассуждения
import anthropic
import re

client = anthropic.AsyncAnthropic()

# Паттерн: скрытое рассуждение + публичный ответ
SCRATCHPAD_SYSTEM = """
Используй тег  для рассуждения перед ответом.
Содержимое scratchpad видно только тебе — думай там свободно, черновики, расчёты.
После scratchpad дай чистый финальный ответ пользователю.
"""

async def think_then_answer(question: str) -> tuple[str, str]:
    """
    Возвращает (reasoning, answer).
    reasoning — скрытый scratchpad, answer — финальный ответ.
    """
    response = await client.messages.create(
        model="claude-opus-4-6",
        system=SCRATCHPAD_SYSTEM,
        messages=[{"role": "user", "content": question}],
        max_tokens=1024,
        temperature=0,
    )
    full = response.content[0].text

    # Разделяем scratchpad и ответ
    sp_match = re.search(r'(.*?)', full, re.DOTALL)
    reasoning = sp_match.group(1).strip() if sp_match else ""

    # Ответ — всё после закрывающего тега
    if sp_match:
        answer = full[sp_match.end():].strip()
    else:
        answer = full.strip()

    return reasoning, answer

reasoning, answer = await think_then_answer(
    "Если у меня 3 кошки и каждая ловит 2 мыши в день, "
    "сколько мышей поймают за рабочую неделю (5 дней)?"
)
print(f"[Рассуждение]: {reasoning}")
print(f"[Ответ]: {answer}")

Extended Thinking в Claude

Начиная с Claude 3.7 Sonnet, Anthropic добавила встроенный механизм Extended Thinking — модель выделяет отдельный блок внутреннего рассуждения перед ответом. Это аналог CoT, но реализованный на уровне модели, а не промпта.

Python — Extended Thinking в Claude API
import anthropic

client = anthropic.AsyncAnthropic()

async def extended_thinking(question: str, budget_tokens: int = 5000) -> dict:
    """
    Используем Extended Thinking Claude.

    budget_tokens — максимум токенов для внутреннего рассуждения.
    Чем сложнее задача, тем больше нужно (обычно 1000–16000).
    """
    response = await client.messages.create(
        model="claude-sonnet-4-6",          # Extended Thinking доступен с 3.7+
        max_tokens=16000,                    # должен быть > budget_tokens
        thinking={
            "type": "enabled",
            "budget_tokens": budget_tokens,  # сколько токенов отдать на рассуждение
        },
        messages=[{"role": "user", "content": question}],
    )

    result = {"thinking": "", "answer": ""}

    for block in response.content:
        if block.type == "thinking":
            result["thinking"] = block.thinking   # внутреннее рассуждение
        elif block.type == "text":
            result["answer"] = block.text          # финальный ответ

    return result

# Задача, требующая глубокого рассуждения
result = await extended_thinking(
    "Логическая задача: Пять друзей (Аня, Боря, Вася, Галя, Дима) живут "
    "в домах разных цветов, содержат разных животных и пьют разные напитки. "
    "Аня живёт в красном доме. Боря держит собаку. Вася пьёт кофе. "
    "Галин дом стоит рядом с синим. Владелец зелёного дома пьёт чай. "
    "Кто держит рыбку?",
    budget_tokens=8000,
)
print("Внутреннее рассуждение (первые 300 символов):")
print(result["thinking"][:300], "...")
print("\nОтвет:", result["answer"])


# ── Streaming с Extended Thinking ──
async def extended_thinking_stream(question: str, budget_tokens: int = 4000):
    """Стримим Extended Thinking с разделением thinking/text блоков."""
    async with client.messages.stream(
        model="claude-sonnet-4-6",
        max_tokens=8000,
        thinking={"type": "enabled", "budget_tokens": budget_tokens},
        messages=[{"role": "user", "content": question}],
    ) as stream:

        current_block_type = None

        async for event in stream:
            if event.type == "content_block_start":
                current_block_type = event.content_block.type
                if current_block_type == "thinking":
                    print("\n[Thinking...]", end="", flush=True)
                elif current_block_type == "text":
                    print("\n[Answer]: ", end="", flush=True)

            elif event.type == "content_block_delta":
                delta = event.delta
                if hasattr(delta, "thinking"):
                    print(".", end="", flush=True)  # точки пока думает
                elif hasattr(delta, "text"):
                    print(delta.text, end="", flush=True)

    print()
💡
Extended Thinking vs промпт-CoT
Промпт-CoTExtended Thinking
МоделиЛюбыеClaude 3.7+ Sonnet
ВидимостьРассуждение в ответеОтдельный блок thinking
КонтрольЧерез промптbudget_tokens параметр
СтоимостьОбычные токеныThinking-токены (дешевле)
КачествоЗависит от промптаВстроено в модель

Self-Consistency: голосование среди ответов

Вместо одного CoT-ответа генерируем несколько (с температурой > 0) и выбираем самый частый. Улучшает точность на 5–15% на задачах с однозначным ответом.

Python — Self-Consistency через majority voting
import asyncio
import re
from collections import Counter
import anthropic

client = anthropic.AsyncAnthropic()

async def single_cot_sample(question: str, temperature: float = 0.7) -> str:
    """Один CoT-прогон с заданной температурой."""
    response = await client.messages.create(
        model="claude-opus-4-6",
        system="Решай задачи пошагово. В конце выведи: 'Ответ: <число>'",
        messages=[{"role": "user", "content": question}],
        max_tokens=512,
        temperature=temperature,
    )
    return response.content[0].text

def extract_final_answer(cot_text: str) -> str | None:
    """Извлекаем финальный ответ из CoT-вывода."""
    # Ищем паттерн "Ответ: <значение>"
    patterns = [
        r'Ответ:\s*([^\n.]+)',
        r'Answer:\s*([^\n.]+)',
        r'итого[:\s]+(\d+)',
        r'=\s*(\d+)\s*$',
    ]
    for pattern in patterns:
        m = re.search(pattern, cot_text, re.IGNORECASE | re.MULTILINE)
        if m:
            return m.group(1).strip()
    return None

async def self_consistency(
    question: str,
    n_samples: int = 5,
    temperature: float = 0.7,
) -> dict:
    """
    Self-Consistency: n независимых CoT-рассуждений → majority vote.
    """
    # Генерируем все образцы параллельно
    samples = await asyncio.gather(*[
        single_cot_sample(question, temperature)
        for _ in range(n_samples)
    ])

    # Извлекаем ответы
    answers = []
    for s in samples:
        ans = extract_final_answer(s)
        if ans:
            answers.append(ans.lower().strip())

    if not answers:
        return {"answer": None, "confidence": 0, "samples": samples}

    # Majority vote
    counter = Counter(answers)
    best_answer, count = counter.most_common(1)[0]
    confidence = count / len(answers)

    return {
        "answer": best_answer,
        "confidence": confidence,
        "vote_counts": dict(counter),
        "n_samples": n_samples,
        "samples": samples,
    }

# Пример
result = await self_consistency(
    "В магазине 23 яблока. Купили ещё столько же минус 10. "
    "Потом съели треть всех яблок. Сколько осталось?",
    n_samples=5,
)
print(f"Ответ: {result['answer']} (уверенность: {result['confidence']:.0%})")
print(f"Голоса: {result['vote_counts']}")
⚠️
Self-Consistency дорогой!
n=5 означает в 5 раз больше токенов и расходов. Используй только там, где точность критична: медицина, финансы, юридические документы. Для повседневных задач хватает одного CoT с temperature=0.

Tree of Thoughts: исследование пространства решений

Tree of Thoughts (ToT) обобщает CoT: вместо одной цепочки строится дерево промежуточных шагов. Модель генерирует несколько вариантов на каждом шаге и оценивает их перспективность.

Задача: как добраться из А в Б за минимум стоимости?
├─ Вариант 1: автобус
├─ Прямой маршрут → стоимость: 50р ✓ исследуем
└─ С пересадкой → стоимость: 80р ✗ отсекаем
├─ Вариант 2: метро
├─ 2 линии → стоимость: 45р ✓ исследуем
└─ 3 линии → стоимость: 45р, долго ✗ отсекаем
└─ Вариант 3: такси → стоимость: 300р ✗ отсекаем сразу
→ Лучший путь: метро, 2 линии, 45р
Python — упрощённый Tree of Thoughts
import asyncio
import anthropic
from dataclasses import dataclass, field

client = anthropic.AsyncAnthropic()

@dataclass
class ThoughtNode:
    thought: str
    score: float = 0.0
    children: list["ThoughtNode"] = field(default_factory=list)
    depth: int = 0

async def generate_thoughts(
    problem: str,
    current_state: str,
    n_thoughts: int = 3,
) -> list[str]:
    """Генерируем несколько вариантов следующего шага."""
    response = await client.messages.create(
        model="claude-opus-4-6",
        system=(
            "Генерируй следующие шаги для решения задачи. "
            f"Предложи ровно {n_thoughts} разных варианта. "
            "Формат: каждый вариант с новой строки, начиная с номера."
        ),
        messages=[{
            "role": "user",
            "content": (
                f"Задача: {problem}\n\n"
                f"Текущее состояние решения:\n{current_state}\n\n"
                f"Предложи {n_thoughts} варианта следующего шага."
            ),
        }],
        max_tokens=512,
        temperature=0.8,   # нужна вариативность
    )
    text = response.content[0].text
    # Простой парсинг нумерованного списка
    lines = [l.strip() for l in text.split('\n') if l.strip()]
    thoughts = [re.sub(r'^\d+[.)]\s*', '', l) for l in lines if l[0].isdigit()]
    return thoughts[:n_thoughts]

async def evaluate_thought(
    problem: str,
    thought_path: str,
) -> float:
    """Оцениваем перспективность текущего пути (0.0 – 1.0)."""
    response = await client.messages.create(
        model="claude-opus-4-6",
        system=(
            "Оцени насколько текущий путь решения перспективен. "
            "Ответь одним числом от 0.0 до 1.0."
        ),
        messages=[{
            "role": "user",
            "content": f"Задача: {problem}\n\nПуть решения:\n{thought_path}",
        }],
        max_tokens=5,
        temperature=0,
    )
    try:
        return float(response.content[0].text.strip())
    except ValueError:
        return 0.5

async def tree_of_thoughts(
    problem: str,
    max_depth: int = 3,
    branching: int = 3,
    beam_width: int = 2,          # сколько лучших путей сохраняем
) -> str:
    """
    Beam search по дереву мыслей.
    На каждом уровне: генерируем branching вариантов,
    оцениваем, оставляем beam_width лучших.
    """
    # Начальные ветки
    current_paths = [""]  # каждый элемент — история шагов

    for depth in range(max_depth):
        candidates = []

        # Генерируем продолжения для всех текущих путей
        for path in current_paths:
            new_thoughts = await generate_thoughts(problem, path, branching)
            for thought in new_thoughts:
                new_path = f"{path}\nШаг {depth+1}: {thought}" if path else f"Шаг 1: {thought}"
                candidates.append(new_path)

        # Оцениваем всех кандидатов параллельно
        scores = await asyncio.gather(*[
            evaluate_thought(problem, c) for c in candidates
        ])

        # Берём beam_width лучших
        ranked = sorted(zip(scores, candidates), key=lambda x: -x[0])
        current_paths = [path for _, path in ranked[:beam_width]]

    # Финальный ответ на основе лучшего пути
    best_path = current_paths[0]
    response = await client.messages.create(
        model="claude-opus-4-6",
        messages=[{
            "role": "user",
            "content": (
                f"Задача: {problem}\n\n"
                f"Цепочка рассуждений:\n{best_path}\n\n"
                "Дай финальный ответ."
            ),
        }],
        max_tokens=256,
        temperature=0,
    )
    return response.content[0].text

ReAct: CoT + действия с инструментами

ReAct (Reasoning + Acting) — паттерн, в котором CoT чередуется с реальными действиями (вызовами инструментов). Это основа большинства production-агентов: думаю → действую → наблюдаю → думаю → ...

T
Thought (рассуждение)
Мне нужно узнать текущую цену биткоина, чтобы ответить на вопрос.
A
Action (вызов инструмента)
search("bitcoin price USD today")
O
Observation (результат)
Bitcoin: $67,450 USD (+2.3% за 24ч)
T
Thought (рассуждение)
Теперь мне нужно перевести в рубли. Ищу курс USD/RUB.
F
Final Answer
Биткоин стоит $67,450 ≈ 6,14 млн рублей (курс ~91 руб/$).
Python — ReAct агент с явным CoT
import anthropic
import asyncio
import json
import re

client = anthropic.AsyncAnthropic()

REACT_SYSTEM = """
Ты — агент, решающий задачи с помощью инструментов.
Используй строго следующий формат:

Thought: [рассуждение о следующем шаге]
Action: [название_инструмента]
Action Input: [параметры в JSON]

После получения результата:
Observation: [результат инструмента — заполняется системой]
Thought: [следующее рассуждение]
...

Когда задача решена:
Thought: Теперь у меня достаточно информации для ответа.
Final Answer: [финальный ответ]

Доступные инструменты: calculator, search, get_weather
"""

# Инструменты
async def calculator(expression: str) -> str:
    try:
        result = eval(expression, {"__builtins__": {}})
        return str(result)
    except Exception as e:
        return f"Ошибка: {e}"

async def search(query: str) -> str:
    # В реальности — запрос к поисковику
    return f"Результаты поиска по '{query}': [заглушка данных]"

async def get_weather(city: str) -> str:
    return f"Погода в {city}: 15°C, облачно"

TOOLS = {
    "calculator": calculator,
    "search": search,
    "get_weather": get_weather,
}

def parse_react_output(text: str) -> dict:
    """Разбираем ReAct-ответ на компоненты."""
    result = {}
    for key in ["thought", "action", "action input", "final answer"]:
        pattern = rf'{key}:\s*(.+?)(?=\n(?:thought|action|observation|final answer):|$)'
        m = re.search(pattern, text, re.IGNORECASE | re.DOTALL)
        if m:
            result[key] = m.group(1).strip()
    return result

async def react_agent(task: str, max_steps: int = 8) -> str:
    """ReAct агент: думает → действует → наблюдает → повторяет."""
    messages = [{"role": "user", "content": task}]
    history = ""    # накапливаем историю шагов

    for step in range(max_steps):
        # Генерируем следующий шаг
        response = await client.messages.create(
            model="claude-opus-4-6",
            system=REACT_SYSTEM,
            messages=messages + ([
                {"role": "assistant", "content": history}
            ] if history else []),
            max_tokens=512,
            temperature=0,
        )
        output = response.content[0].text
        parsed = parse_react_output(output)

        print(f"\n[Шаг {step+1}]")
        if "thought" in parsed:
            print(f"  Thought: {parsed['thought'][:100]}")

        # Финальный ответ
        if "final answer" in parsed:
            print(f"  Final Answer: {parsed['final answer']}")
            return parsed["final answer"]

        # Вызов инструмента
        if "action" in parsed:
            tool_name  = parsed["action"].strip().lower()
            tool_input_str = parsed.get("action input", "{}")

            try:
                tool_input = json.loads(tool_input_str)
            except json.JSONDecodeError:
                tool_input = {"input": tool_input_str}

            print(f"  Action: {tool_name}({tool_input})")

            tool_fn = TOOLS.get(tool_name)
            if tool_fn:
                observation = await tool_fn(**tool_input)
            else:
                observation = f"Инструмент '{tool_name}' не найден"

            print(f"  Observation: {observation[:100]}")

            # Добавляем в историю
            history += (
                f"\nThought: {parsed.get('thought', '')}"
                f"\nAction: {tool_name}"
                f"\nAction Input: {tool_input_str}"
                f"\nObservation: {observation}"
            )
        else:
            break

    return "Не удалось завершить задачу за отведённые шаги"

# Запуск
answer = await react_agent("Сколько будет (15 * 23) + 47 / 2? Покажи рассуждения.")
print(f"\nИтог: {answer}")

Обзор техник CoT

Zero-shot CoT
Kojima et al. 2022
«Думай пошагово» без примеров. Быстро, дёшево, работает на GPT-4/Claude.
Few-shot CoT
Wei et al. 2022
Примеры с цепочками рассуждений. Точнее zero-shot, но дороже (токены).
Self-Consistency
Wang et al. 2022
N независимых CoT → majority vote. +5–15% accuracy, в N раз дороже.
Tree of Thoughts
Yao et al. 2023
Дерево шагов + backtracking. Для задач с пространством поиска.
ReAct
Yao et al. 2023
CoT + tool calls. Стандарт для агентов. Thought → Action → Observation.
Extended Thinking
Anthropic 2025
Встроенный CoT в Claude 3.7+. budget_tokens контролирует глубину.

Когда CoT не нужен

⚠️
CoT — не всегда хорошо
  • Простые задачи: «Переведи на английский», «Суммаризуй» — CoT только добавляет токены и иногда путает модель
  • Латентность важнее точности: CoT увеличивает TTFB и общее время ответа
  • Задачи с коротким однозначным ответом: классификация, выбор из вариантов — zero-shot + temperature=0 работает отлично
  • Малые модели: CoT может ухудшить результат на моделях < 7B параметров
Используй CoT только когда задача реально требует многошагового рассуждения.

Проверь себя

Вопросы для самопроверки

  1. Почему CoT улучшает точность на сложных задачах? Какой механизм за этим стоит?
  2. Чем few-shot CoT отличается от zero-shot CoT?
  3. Когда Self-Consistency оправдана, а когда — нет?
  4. Что такое ReAct и чем он отличается от обычного CoT?
  5. В чём разница между Extended Thinking и промпт-CoT?
  6. Назови три ситуации, когда CoT не нужен.
Показать ответы
  1. Каждый токен рассуждения попадает в контекст и становится «памятью» для генерации следующего. Цепочка маленьких шагов вместо одного «прыжка» снижает ошибку на каждом шаге.
  2. Zero-shot CoT — просто фраза «думай пошагово». Few-shot CoT — примеры с полными цепочками рассуждений, обучает конкретному стилю мышления.
  3. Оправдана когда точность критична (медицина, финансы) и задача имеет однозначный ответ. Не нужна для творческих/субъективных задач и там где стоимость важна.
  4. ReAct чередует CoT (Thought) с реальными вызовами инструментов (Action) и наблюдением результатов (Observation). Обычный CoT — только рассуждение без внешних действий.
  5. Extended Thinking встроен в модель, отдельный блок с budget_tokens. Промпт-CoT — через инструкцию в промпте, работает на любой модели, но зависит от формулировки.
  6. Простые задачи (перевод, суммаризация), задачи с коротким ответом (классификация), когда латентность критична.

Итог урока

  • CoT — заставляем модель «думать вслух»; каждый токен рассуждения помогает следующему
  • Zero-shot CoT: добавить «Думай пошагово.» — быстро и бесплатно
  • Few-shot CoT: примеры с полными цепочками — точнее, обучает вашему стилю
  • Двухшаговый паттерн: запрос на рассуждение → запрос на извлечение финального ответа
  • Структурированный CoT: XML-теги (<thinking>, <scratchpad>) для агентов
  • Extended Thinking: встроенный CoT в Claude 3.7+, управляется через budget_tokens
  • Self-Consistency: N прогонов → majority vote, +accuracy, ×N стоимость
  • ReAct: Thought → Action → Observation — стандарт для агентов с инструментами
  • CoT не нужен для простых задач, классификации и когда важна скорость