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 руб.
Подходит для: математических задач, финансовых расчётов, статистики, обработки данных, любых задач, где важна вычислительная точность.
Техника была предложена Chen et al. в 2022 году и принята в TMLR в 2024. Авторы обнаружили ключевую проблему Chain of Thought: модель одновременно пытается рассуждать и считать, а арифметика — слабое место LLM. Даже GPT-4 ошибается в умножении больших чисел.
PoT решает эту проблему принципом разделения ответственности: модель отвечает за рассуждение и логику (структуру решения), а интерпретатор Python — за вычисления. Это аналогично тому, как программист пишет алгоритм, а компьютер его выполняет.
Напиши Python-код для решения задачи. Комментируй каждый шаг.
Задача: Стартап привлекает раунд Series A. Pre-money valuation — $10M. Инвестор вкладывает $3M. У фаундеров 80% акций, у ангелов 15%, у ESOP 5%. Рассчитай: post-money valuation, долю инвестора, размытие (dilution) существующих акционеров.
Напиши Python-код для анализа.
Данные о конверсии A/B-теста лендинга:
- Вариант A (контроль): 12 500 посетителей, 312 конверсий
- Вариант B (новый дизайн): 12 300 посетителей, 389 конверсий
Рассчитай: конверсию, абсолютный и относительный прирост, статистическую значимость (p-value через z-test для пропорций).
Напиши 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 тыс. руб. «бесплатно».
Точность: 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-код для расчётов».
встроенными sandbox-инструментами в agent runtimes
любыми reasoning-сценариями, где вычислительная часть должна быть воспроизводимой
То есть PoT — это не просто способ “писать код в ответе”, а дисциплина разделения слоёв. Всё, что можно детерминированно посчитать, лучше не оставлять на свободное текстовое рассуждение. Это особенно заметно в финансах, аналитике, pricing, KPI-моделях, data transformations и симуляциях.
важна проверяемость результата командой, а не только моделью
вычисление длинное, многошаговое или содержит условные ветвления
Если ваша система всё равно должна вызывать калькулятор, SQL, dataframe-пайплайн или Python sandbox, PoT даёт естественный формат для формализации рассуждений перед выполнением.
она не помогает, если ошибка сидит не в вычислении, а в неверной интерпретации условий
она не решает fact-checking и не заменяет retrieval
она не подходит для задач, где шаги нельзя разумно формализовать в код
На практике многие команды пытаются засунуть в PoT слишком много семантики. Если задача не сводится к данным, формулам, условиям и воспроизводимым операциям, лучше оставить её в обычном reasoning-слое или перейти к гибридному Chain of Code.
В production PoT удобнее всего использовать как один из маршрутов router-системы:
simple factual -> direct answer или retrieval
symbolic/computational -> PoT / Python tool
mixed semantic + computational -> CoC или workflow с несколькими инструментами
Такой взгляд делает технику устойчивее. Вместо попытки применять её “везде, где есть reasoning”, вы используете её там, где цена вычислительной ошибки выше цены дополнительного runtime-шага.
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 п.п.), но всё равно значимый.