DanLevy.net

Хватит просить LLM считать

Они плохи в этом. Вот как это исправить.

Знаете, что странного в языковых моделях? Они могут объяснить квантовую механику, писать стихи и отлаживать ваш TypeScript… но попросите их умножить 18472 на 9347, и есть приличный шанс, что они уверенно выдадут что-то с ошибкой в тысячи.

Это ставило меня в ступор, пока я не осознал, что мы на самом деле от них требуем. Мы просим движок сопоставления паттернов быть калькулятором. Это как попросить гимнаста вести вашу чековую книжку, потому что он понимает концепцию «баланса».

Дело в том, что LLM ничего не вычисляют. Когда вы спрашиваете GPT или Claude, чему равно 2 + 2, они не складывают. Они предсказывают, что «4» — это токен, который с наибольшей вероятностью появится после «2 + 2 =». В большинстве случаев это отлично работает, потому что такие паттерны есть в их обучающих данных. Но выйдите за рамки простой арифметики к многошаговым вычислениям или чему-либо с числами, которые не были распространены в обучении, и вы по сути бросаете кости.

Недавно я столкнулся с этим в лоб, просматривая код, где модель высшего уровня использовалась для расчёта ипотечных платежей. Модель ответила с полной уверенностью. Но ошиблась на $400 в месяц. Это тот тип ошибки, который имеет значение.

Даже когда модели становятся лучше в рассуждениях (GPT-5 якобы показывает улучшения), они всё ещё занимаются сложным сопоставлением паттернов, а не символьными вычислениями. Для творческой работы и задач на естественном языке эта вероятностная природа — именно то, что делает их волшебными. Для математики? Не так уж и хорошо.

Что на самом деле это решает?

Ответ — не ждать более умных моделей. Ответ — дать модели правильный инструмент для задачи.

Подумайте, как бы вы решили эту задачу, если бы строили не-AI систему. Вы бы не писали собственную математическую логику, вы бы взяли библиотеку. Тот же принцип применим и здесь, только теперь мы учим LLM, когда и как использовать эту библиотеку.

Вызов инструментов в современных AI SDK позволяет передавать модели структурированные функции, которые она может вызывать. Вместо того чтобы заставлять LLM притворяться, что она знает математику, мы даём ей то, что действительно знает: символьный математический движок.

Я использую AI SDK v5 и v6 для этого, в паре с CortexJS Compute Engine. SDK отвечает за оркестрацию и маршрутизацию инструментов, а CortexJS обрабатывает всё — от базовой арифметики до матанализа. На удивление чистое разделение ответственности.

Terminal window
bun add ai @ai-sdk/anthropic @cortex-js/compute-engine zod

Создаём математический инструмент

Реализация проще, чем можно ожидать. То, что мы строим, — это мост между пониманием естественного языка LLM и реальными математическими вычислениями.

import { generateText, stepCountIs, tool } from 'ai';
import { ComputeEngine } from '@cortex-js/compute-engine';
import { z } from 'zod';
// Инициализируем движок один раз
const ce = new ComputeEngine();
const mathTool = tool({
description: 'Вычисляет математические выражения и решает уравнения с гарантированной точностью. ДОЛЖЕН использоваться для всех математических операций для проверки правильности — не пытайтесь считать в уме. Поддерживает арифметику, алгебру, матанализ и комплексные операции. Может обрабатывать несколько выражений за раз.',
parameters: z.object({
expressions: z.array(z.string()).describe(
'Массив математических выражений в нотации LaTeX или plain, например ["2 + 2", "\\frac{x^2 + 1}{x - 1}", "\\int x^2 dx"]'
),
}),
execute: async ({ expressions }) => {
// Обрабатываем все выражения параллельно (или детальным батчем)
return expressions.map(expression => {
try {
const result = ce.parse(expression).evaluate();
return {
expression,
result: result.toString(),
latex: result.latex,
};
} catch (error) {
return {
expression,
error: (error as Error).message
};
}
});
},
});

Несколько моментов, которые стоит отметить:

Описание делает тяжёлую работу. Формулировка «ДОЛЖЕН использоваться» может показаться агрессивной, но по моему опыту, явное указание модели, когда использовать инструмент, — это разница между «работает иногда» и «работает надёжно». Считайте это промпт-инжинирингом на уровне инструментов.

Пакетная обработка через массив expressions важнее, чем может показаться. Каждый вызов модели имеет задержку. Если вы решаете систему уравнений или делаете многошаговую математику, обработка по одному создаёт ужасный пользовательский опыт. Пакетная обработка означает один раунд-трип для решения десяти задач.

Использование символьного движка вместо простого eval() (пожалуйста, не используйте eval()) даёт нам настоящее математическое понимание. Движок разбирает интент, обрабатывает форматирование LaTeX и может работать с производными и интегралами. Мы не просто считаем, мы занимаемся математикой.

Обработка ошибок привязана к каждому выражению. Если одно вычисление падает, мы возвращаем эту ошибку, но продолжаем с остальными. Это позволяет модели видеть, что сработало, а что нет, и потенциально самокорректироваться на следующем шаге.

Запускаем в работу

Давайте бросим ему что-то, что обычно заставляет сырую модель галлюцинировать:

import { anthropic } from '@ai-sdk/anthropic';
const { text } = await generateText({
model: anthropic('claude-sonnet-4-5'),
prompt: 'Вычисли 18472 × 9347, раздели на 127, затем извлеки квадратный корень из результата.',
tools: { mathTool },
stopWhen: stepCountIs(5), // Разрешаем до пяти шагов модель/инструмент
});
console.log(text);

Модель видит задачу по математике, распознаёт, что ей нужна точность, вызывает инструмент, получает точный результат, а затем объясняет его на естественном языке. Каждый компонент делает то, что умеет лучше всего.

За пределы базовой арифметики

Поскольку мы используем символьный движок, этот подход справляется с тем, что простые калькуляторные инструменты не могут.

Хотите решить алгебраические уравнения? «Реши эти уравнения: 3x + 7 = 22 и 2y - 5 = 13» работает нормально.

Нужен матанализ? «Найди производную от x^3 + 2x^2 и вычисли её при x = 2» — это просто ещё один вызов инструмента.

Поддержка LaTeX особенно полезна, если вы строите образовательные приложения. Движок изначально понимает ввод LaTeX и может возвращать результаты, отформатированные для рендеринга. Никакого дополнительного парсинга не требуется.

Большая картина

Я думаю, этот паттерн важен не только для математики. По сути, мы признаём ограничения LLM, одновременно используя их сильные стороны. Они невероятны в понимании интента, разборе естественного языка и оркестрации рабочих процессов. Они не калькуляторы, не базы данных и не файловые системы.

Каждый раз, когда мы пытаемся заставить LLM делать что-то детерминированное, мы идём против её природы. Но когда мы соединяем понимание естественного языка со специализированными инструментами, которые обрабатывают детерминированные части? Вот тогда становится интересно.

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

Это сдвиг в том, как мы думаем о построении систем с ИИ. Не «может ли модель это сделать?», а «может ли модель это оркестрировать?». Небольшая разница в формулировке, значительная разница в надёжности.

Ресурсы