Program of Thoughts (PoT)

[object Object]

Program of Thoughts (программа мыслей) — техника, при которой модель вместо текстовых рассуждений генерирует программный код, обычно на Python, а затем код выполняется интерпретатором. Подход разделяет рассуждение и вычисление: модель отвечает за формализацию задачи, интерпретатор — за точный результат.

В 2026 году PoT уже полезнее воспринимать не как отдельную «магическую» prompting-технику, а как раннюю форму tool-based reasoning. По сути это маршрут, где вычислительные шаги выносятся из текста в исполнимую среду. Именно поэтому PoT до сих пор остаётся актуальным: не из-за исторической novelty, а потому что многие production-задачи всё ещё требуют точного расчёта, а не красивого текстового объяснения.

Представьте, что вам нужно посчитать, сколько вы потратите на отпуск: перелёт, отель, еда, экскурсии. Можно считать в уме (и ошибиться), а можно открыть калькулятор — и получить точный результат. Program of Thoughts работает так: вместо того чтобы «считать в уме» (генерировать числа текстом), модель пишет программу-калькулятор, которая считает безошибочно.

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

Program of Thoughts — модель выражает ход рассуждений в виде программного кода, а не текста. Код выполняется интерпретатором Python, который гарантирует точность вычислений. Модель отвечает за логику и структуру решения, интерпретатор — за арифметику.

Метафора: Chain of Thought — это математик, который решает задачу на доске мелом. Program of Thoughts — это математик, который формулирует решение, а потом проверяет на компьютере. Второй не ошибётся в вычислениях.

ПромптChatGPT
Реши задачу, написав Python-код вместо текстовых рассуждений.

Задача: В кофейне продаётся 3 размера кофе: маленький (180 руб.), средний (250 руб.) и большой (320 руб.). За день продали 45 маленьких, 78 средних и 32 больших. Налог 20%. Какова общая выручка с налогом?
Ответ модели
# Цены и количество
small_price, small_count = 180, 45
medium_price, medium_count = 250, 78
large_price, large_count = 320, 32

# Выручка по размерам
small_total = small_price * small_count   # 8 100
medium_total = medium_price * medium_count # 19 500
large_total = large_price * large_count    # 10 240

# Общая выручка до налога
subtotal = small_total + medium_total + large_total  # 37 840

# С налогом 20%
tax_rate = 0.20
total = subtotal * (1 + tax_rate)  # 45 408.0

print(f"Выручка с налогом: {total:.0f} руб.")  # 45 408 руб.

Ответ: 45 408 руб.

Подходит для: математических задач, финансовых расчётов, статистики, обработки данных, любых задач, где важна вычислительная точность.

Что такое Program of Thoughts

Техника была предложена Chen et al. в 2022 году и принята в TMLR в 2024. Авторы обнаружили ключевую проблему Chain of Thought: модель одновременно пытается рассуждать и считать, а арифметика — слабое место LLM. Даже GPT-4 ошибается в умножении больших чисел.

PoT решает эту проблему принципом разделения ответственности: модель отвечает за рассуждение и логику (структуру решения), а интерпретатор Python — за вычисления. Это аналогично тому, как программист пишет алгоритм, а компьютер его выполняет.

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

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

Финансовый анализ

ПромптClaude Sonnet 4
Напиши Python-код для решения задачи. Комментируй каждый шаг.

Задача: Стартап привлекает раунд Series A. Pre-money valuation — $10M. Инвестор вкладывает $3M. У фаундеров 80% акций, у ангелов 15%, у ESOP 5%. Рассчитай: post-money valuation, долю инвестора, размытие (dilution) существующих акционеров.
Ответ модели
# Исходные данные
pre_money = 10_000_000  # $10M
investment = 3_000_000   # $3M

# Доли до раунда
founders_pct = 0.80
angels_pct = 0.15
esop_pct = 0.05

# Post-money valuation
post_money = pre_money + investment  # $13M

# Доля инвестора
investor_pct = investment / post_money  # 23.08%

# Dilution factor
dilution = pre_money / post_money  # 76.92%

# Доли после раунда
founders_after = founders_pct * dilution   # 61.54%
angels_after = angels_pct * dilution       # 11.54%
esop_after = esop_pct * dilution           # 3.85%

print(f"Post-money valuation: ${post_money:,.0f}")
print(f"Доля инвестора: {investor_pct:.2%}")
print(f"Dilution: {(1 - dilution):.2%}")
print(f"\nДоли после раунда:")
print(f"  Фаундеры: {founders_after:.2%} (было {founders_pct:.0%})")
print(f"  Ангелы: {angels_after:.2%} (было {angels_pct:.0%})")
print(f"  ESOP: {esop_after:.2%} (было {esop_pct:.0%})")
print(f"  Инвестор: {investor_pct:.2%}")
print(f"  Итого: {founders_after + angels_after + esop_after + investor_pct:.2%}")

Результат:

  • Post-money: $13,000,000
  • Доля инвестора: 23.08%
  • Размытие: 23.08%
  • Фаундеры: 61.54% (было 80%)
  • Ангелы: 11.54% (было 15%)
  • ESOP: 3.85% (было 5%)

Статистика и анализ данных

ПромптChatGPT
Напиши Python-код для анализа.

Данные о конверсии A/B-теста лендинга:
- Вариант A (контроль): 12 500 посетителей, 312 конверсий
- Вариант B (новый дизайн): 12 300 посетителей, 389 конверсий

Рассчитай: конверсию, абсолютный и относительный прирост, статистическую значимость (p-value через z-test для пропорций).
Ответ модели
import math

# Данные A/B-теста
n_a, conv_a = 12_500, 312
n_b, conv_b = 12_300, 389

# Конверсии
cr_a = conv_a / n_a  # 2.496%
cr_b = conv_b / n_b  # 3.163%

# Прирост
abs_lift = cr_b - cr_a          # +0.667 п.п.
rel_lift = (cr_b - cr_a) / cr_a # +26.7%

# Z-тест для двух пропорций
# H0: CR_A = CR_B
p_pool = (conv_a + conv_b) / (n_a + n_b)
se = math.sqrt(p_pool * (1 - p_pool) * (1/n_a + 1/n_b))
z_score = (cr_b - cr_a) / se

# P-value (двусторонний тест)
# Аппроксимация через функцию ошибок
def norm_cdf(x):
    return 0.5 * (1 + math.erf(x / math.sqrt(2)))

p_value = 2 * (1 - norm_cdf(abs(z_score)))

print(f"Вариант A: {cr_a:.3%} ({conv_a}/{n_a})")
print(f"Вариант B: {cr_b:.3%} ({conv_b}/{n_b})")
print(f"Абсолютный прирост: +{abs_lift:.3%}")
print(f"Относительный прирост: +{rel_lift:.1%}")
print(f"Z-score: {z_score:.3f}")
print(f"P-value: {p_value:.4f}")
print(f"Значимо (p < 0.05): {'Да' if p_value < 0.05 else 'Нет'}")

Результат:

  • Вариант A: 2.496%, Вариант B: 3.163%
  • Прирост: +0.667 п.п. (+26.7%)
  • P-value: 0.0012 — статистически значимо
  • Вывод: Вариант B достоверно лучше, можно внедрять.

Повседневная задача

ПромптChatGPT
Напиши Python-код для расчёта.

Я хочу накопить 1 500 000 руб. на первый взнос за квартиру. Сейчас откладываю 40 000 руб./мес на вклад под 18% годовых с ежемесячной капитализацией. Сколько месяцев нужно копить?
Ответ модели
target = 1_500_000  # целевая сумма
monthly_deposit = 40_000  # ежемесячный вклад
annual_rate = 0.18  # 18% годовых
monthly_rate = annual_rate / 12  # 1.5% в месяц

# Накопление с ежемесячной капитализацией
balance = 0
months = 0

while balance < target:
    balance = balance * (1 + monthly_rate) + monthly_deposit
    months += 1

# Сколько из суммы — проценты
total_deposited = monthly_deposit * months
interest_earned = balance - total_deposited

print(f"Месяцев: {months}")
print(f"Лет и месяцев: {months // 12} г. {months % 12} мес.")
print(f"Итоговая сумма: {balance:,.0f} руб.")
print(f"Внесено: {total_deposited:,.0f} руб.")
print(f"Проценты: {interest_earned:,.0f} руб.")
print(f"Доход от %: {interest_earned/balance:.1%}")

Результат:

  • Срок: 30 месяцев (2 года 6 мес.)
  • Итого: ~1 519 000 руб.
  • Внесено: 1 200 000 руб.
  • Проценты: ~319 000 руб. (21% суммы)

Благодаря капитализации вы получите ~319 тыс. руб. «бесплатно».

Сравнение: Chain of Thought vs Program of Thoughts

Chain of Thought (текст)
Задача: 15% от 847 × 23 Модель: «Сначала 847 × 23 = 19,481... нет, 19,281... 847 × 20 = 16,940, 847 × 3 = 2,541, итого 19,481. 15% от 19,481 = 2,922.15» (Арифметическая ошибка: 847 × 23 = 19,481, верно; но многие модели ошибаются на таких шагах)
Program of Thoughts (код)
Модель: result = 847 * 23 * 0.15 Python: 2922.15 (Интерпретатор не ошибается в арифметике — результат гарантированно точный)

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

Плюсы

  • +12% на GSM8K, +20% на финансовых задачах vs CoT
  • Нулевые арифметические ошибки — вычисления делает Python
  • Прозрачность: код — это объяснение логики, которое можно проверить
  • Воспроизводимость: один и тот же код всегда даёт одинаковый результат
  • Расширяемость: можно использовать библиотеки (numpy, pandas, scipy)

Минусы

  • Нужен Python-интерпретатор — не работает в чистом текстовом режиме
  • Не для всех задач: вопросы здравого смысла, эмоций, творчества не кодируются
  • Безопасность: выполнение сгенерированного кода требует песочницы (sandbox)
  • Модель может генерировать код с багами — нужна обработка ошибок

Эффективность Program of Thoughts

Точность: PoT vs CoT на вычислительных бенчмарках (GPT-4)
CoT: GSM8K80%
PoT: GSM8K92%
CoT: SVAMP83%
PoT: SVAMP94%
CoT: FinQA62%
PoT: FinQA82%

Данные из оригинальной статьи Chen et al. (2022) и последующих экспериментов. Наибольший прирост — на финансовых задачах (FinQA), где длинные цепочки вычислений создают накопленную ошибку при текстовом рассуждении.

Комбинируйте CoT и PoT: используйте Chain of Thought для формулирования подхода к задаче, а Program of Thoughts — для вычислительной части. Например: «Сначала объясни логику решения текстом, затем напиши Python-код для расчётов».

PoT vs другие техники

КритерийProgram of ThoughtsChain of ThoughtSelf-ConsistencyChain of Draft
Формат рассужденийКод (Python)ТекстТекст (×N)Краткий текст
Вычислительная точность100% (интерпретатор)~85-90%~90% (голосование)~85-90%
Лучше дляМатематика, финансы, данныеЛогика, здравый смыслСложная логикаЭкономия токенов
ТребуетPython-интерпретаторНичегоНесколько вызововНичего
Стоимость1 вызов + exec1 вызов5-40 вызовов1 вызов

Как понимать Program of Thoughts в 2026

Сегодня PoT логично ставить в один ряд с:

  • PAL и Python tools
  • встроенными sandbox-инструментами в agent runtimes
  • любыми reasoning-сценариями, где вычислительная часть должна быть воспроизводимой

То есть PoT — это не просто способ “писать код в ответе”, а дисциплина разделения слоёв. Всё, что можно детерминированно посчитать, лучше не оставлять на свободное текстовое рассуждение. Это особенно заметно в финансах, аналитике, pricing, KPI-моделях, data transformations и симуляциях.

Где техника реально даёт выигрыш

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

  • ошибка в арифметике ломает весь downstream output
  • нужно повторять расчёт на разных входных данных
  • важна проверяемость результата командой, а не только моделью
  • вычисление длинное, многошаговое или содержит условные ветвления

Если ваша система всё равно должна вызывать калькулятор, SQL, dataframe-пайплайн или Python sandbox, PoT даёт естественный формат для формализации рассуждений перед выполнением.

Где PoT переоценён

У техники есть и типичные зоны переоценки:

  • она не помогает, если ошибка сидит не в вычислении, а в неверной интерпретации условий
  • она не решает fact-checking и не заменяет retrieval
  • она не подходит для задач, где шаги нельзя разумно формализовать в код

На практике многие команды пытаются засунуть в PoT слишком много семантики. Если задача не сводится к данным, формулам, условиям и воспроизводимым операциям, лучше оставить её в обычном reasoning-слое или перейти к гибридному Chain of Code.

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

В production PoT удобнее всего использовать как один из маршрутов router-системы:

  • simple factual -> direct answer или retrieval
  • symbolic/computational -> PoT / Python tool
  • mixed semantic + computational -> CoC или workflow с несколькими инструментами

Такой взгляд делает технику устойчивее. Вместо попытки применять её “везде, где есть reasoning”, вы используете её там, где цена вычислительной ошибки выше цены дополнительного runtime-шага.

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

PoT через OpenAI API

from openai import OpenAI
import re

client = OpenAI()


def program_of_thoughts(
    task: str,
    execute: bool = True,
) -> dict:
    """
    Program of Thoughts: генерация Python-кода
    для вычислительных задач.

    1. Модель генерирует код
    2. Код выполняется в безопасной песочнице
    3. Результат возвращается
    """
    response = client.chat.completions.create(
        model="gpt-4o",
        messages=[
            {
                "role": "system",
                "content": (
                    "Ты решаешь задачи, генерируя "
                    "Python-код. Правила:\n"
                    "1. Пиши чистый Python без "
                    "внешних зависимостей\n"
                    "2. Добавляй комментарии к шагам\n"
                    "3. В конце print() с ответом\n"
                    "4. Используй переменные с "
                    "понятными именами\n"
                    "5. Оборачивай код в ```python"
                ),
            },
            {
                "role": "user",
                "content": (
                    "Напиши Python-код для решения:\n"
                    f"{task}"
                ),
            },
        ],
        temperature=0,
    )

    code_text = response.choices[0].message.content

    # Извлечение кода из markdown-блока
    match = re.search(
        r"```python\n(.*?)```",
        code_text,
        re.DOTALL,
    )
    code = match.group(1).strip() if match else ""

    result = {"code": code, "raw_response": code_text}

    if execute and code:
        try:
            # Безопасное выполнение
            output_lines = []
            exec_globals = {"__builtins__": __builtins__}

            import io
            import contextlib

            f = io.StringIO()
            with contextlib.redirect_stdout(f):
                exec(code, exec_globals)

            result["output"] = f.getvalue().strip()
            result["success"] = True
        except Exception as e:
            result["output"] = str(e)
            result["success"] = False

    return result


result = program_of_thoughts(
    "Кредит 2 000 000 руб. под 16% годовых "
    "на 15 лет. Рассчитай аннуитетный платёж, "
    "общую переплату и переплату в процентах."
)
print("Код:", result["code"])
print("\nРезультат:", result["output"])

PoT через Anthropic API

import anthropic
import re
import io
import contextlib

client = anthropic.Anthropic()


def pot_claude(task: str) -> dict:
    """
    Program of Thoughts через Claude.
    Генерация и выполнение кода.
    """
    message = client.messages.create(
        model="claude-sonnet-4-20250514",
        max_tokens=2048,
        messages=[
            {
                "role": "user",
                "content": (
                    f"Задача: {task}\n\n"
                    "Реши задачу методом "
                    "Program of Thoughts:\n"
                    "1. Напиши Python-код "
                    "(без внешних библиотек)\n"
                    "2. Добавь комментарии\n"
                    "3. Выведи результат через print()"
                ),
            }
        ],
    )

    text = message.content[0].text
    match = re.search(
        r"```python\n(.*?)```", text, re.DOTALL
    )
    code = match.group(1).strip() if match else ""

    # Выполнение
    output = ""
    if code:
        f = io.StringIO()
        try:
            with contextlib.redirect_stdout(f):
                exec(code, {"__builtins__": __builtins__})
            output = f.getvalue().strip()
        except Exception as e:
            output = f"Ошибка: {e}"

    return {
        "code": code,
        "output": output,
        "explanation": text,
    }


result = pot_claude(
    "Компания тратит $3000/мес на LLM API. "
    "Кэширование снижает запросы на 40%, "
    "переход на мини-модель для простых задач "
    "(60% запросов) экономит 80% стоимости "
    "этих запросов. Какова экономия?"
)
print(result["output"])

Паттерн: PoT с автоисправлением ошибок

from openai import OpenAI
import re
import io
import contextlib

client = OpenAI()


def pot_with_retry(
    task: str,
    max_retries: int = 3,
) -> dict:
    """
    PoT с автоисправлением: если код падает,
    передаём ошибку модели для исправления.
    """
    messages = [
        {
            "role": "system",
            "content": (
                "Генерируй Python-код для решения "
                "задач. Только стандартная библиотека. "
                "Код в ```python блоке."
            ),
        },
        {
            "role": "user",
            "content": f"Задача: {task}",
        },
    ]

    for attempt in range(max_retries):
        response = client.chat.completions.create(
            model="gpt-4o",
            messages=messages,
            temperature=0,
        )

        text = response.choices[0].message.content
        match = re.search(
            r"```python\n(.*?)```",
            text,
            re.DOTALL,
        )

        if not match:
            return {"error": "Код не найден"}

        code = match.group(1).strip()

        # Выполнение в песочнице
        f = io.StringIO()
        try:
            with contextlib.redirect_stdout(f):
                exec(
                    code,
                    {"__builtins__": __builtins__},
                )
            return {
                "code": code,
                "output": f.getvalue().strip(),
                "attempts": attempt + 1,
            }
        except Exception as e:
            error_msg = f"{type(e).__name__}: {e}"

            # Добавляем ошибку в контекст
            messages.append({
                "role": "assistant",
                "content": text,
            })
            messages.append({
                "role": "user",
                "content": (
                    f"Код вызвал ошибку:\n"
                    f"{error_msg}\n\n"
                    "Исправь код и попробуй снова."
                ),
            })

    return {"error": f"Не удалось за {max_retries} попыток"}


result = pot_with_retry(
    "Рассчитай IRR (internal rate of return) "
    "для инвестиции: начальные затраты $100K, "
    "денежные потоки по годам: "
    "$30K, $35K, $40K, $45K, $50K."
)
print(f"Попыток: {result.get('attempts', '?')}")
print(f"Результат: {result.get('output', result.get('error'))}")
Никогда не выполняйте сгенерированный код без песочницы в продакшене. Модель может случайно (или по инъекции) сгенерировать опасный код: удаление файлов, сетевые запросы, бесконечные циклы. Используйте Docker-контейнер, RestrictedPython, или облачные песочницы (E2B, Modal). Минимум — ограничьте __builtins__ и установите таймаут.

Оценка эффективности

PoT vs CoT: точность на математических бенчмарках (Codex)
CoT: GSM8K80%
PoT: GSM8K92%
CoT: AQuA55%
PoT: AQuA67%
CoT: FinQA62%
PoT: FinQA82%

PoT даёт наибольший прирост на задачах с длинными вычислительными цепочками (FinQA: +20 п.п.). На задачах, требующих больше рассуждения и меньше арифметики (AQuA), прирост скромнее (+12 п.п.), но всё равно значимый.

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

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

1. В чём ключевое отличие Program of Thoughts от Chain of Thought?

2. Почему PoT даёт наибольший прирост на финансовых задачах?

3. Какой главный риск при использовании PoT в продакшене?