LLMに数学をさせるのはやめよう
彼らは下手です。解決策はこちら。
言語モデルの変なところって何だと思いますか?量子力学を説明したり、詩を書いたり、TypeScript のデバッグをしたりできるのに、18472 と 9347 を掛け算させようとすると、千単位でずれた答えを自信満々で返す可能性が高いんです。
これには以前は戸惑っていましたが、実際に何を求めているのかに気づいたときに納得しました。パターンマッチングエンジンに電卓の役割を期待しているわけです。これは、体操選手に「残高」の概念を理解しているからといって家計簿のバランスを取らせようとするようなものです。
実際、LLM は何も計算していません。GPT や Claude に「2 + 2 は何ですか?」と聞くと、彼らは足し算をしているわけではなく、「2 + 2 =」の後に最も出現しやすいトークンとして「4」を予測しているだけです。ほとんどの場合、訓練データにそのパターンがあるのでうまくいきますが、単純な算術を超えて多段階計算や訓練時にあまり見られなかった数値に踏み込むと、実質的にサイコロを振っているようなものです。
最近、トップクラスのモデルを使って住宅ローンの支払い額を計算するコードをレビューしたときに、この問題に直面しました。モデルは自信満々で答えを返しましたが、月額で 400 ドルも間違っていました。これが実際に問題になるエラーです。
たとえモデルが推論能力で(GPT‑5 が改善を示すとされても)向上したとしても、依然として行っているのは高度なパターンマッチングであり、シンボリック計算ではありません。創造的な作業や自然言語タスクにおいては、この確率的性質が魔法のように機能しますが、数学に関してはそうでもありません。
実際に解決策は?
答えは、より賢いモデルを待つことではありません。モデルに適切なツールを提供することです。
AI でないシステムを構築するときにこの問題をどう解決するか考えてみてください。独自の数学ロジックを書くだけでなく、ライブラリを利用しますよね。同じ原則がここでも当てはまりますが、今度は LLM にそのライブラリをいつ、どのように使うかを教えるのです。
最新の AI SDK のツール呼び出し機能を使えば、モデルに呼び出せる構造化された関数を渡すことができます。LLM に数学ができるふりをさせる代わりに、実際に計算を行うシンボリック数学エンジンを提供するわけです。
私は AI SDK v5 と v6 を CortexJS Compute Engine と組み合わせて使用しています。SDK がオーケストレーションとツールのルーティングを担当し、CortexJS が基本的な算術から微積分までを処理します。関心事の分離が驚くほどクリーンに実現できています。
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 またはプレーン表記の数式配列、例: ["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 }; } }); },});ここで注目すべき点は次の通りです。
-
description が重要
「MUST be used」という強い表現は攻撃的に見えるかもしれませんが、ツール使用のタイミングをモデルに明示することで、たまにしか動かない状態から信頼できる動作へと変わります。ツールレベルのプロンプトエンジニアリングです。 -
バッチ処理の意義
expressions配列でまとめて処理することは思った以上に効果があります。モデル呼び出しごとにレイテンシが発生するため、連立方程式や複数ステップの計算を個別に行うとユーザー体験が著しく低下します。バッチ化すれば 10 問を一往復で解決できます。 -
シンボリックエンジンの選択
eval()(絶対に使用しないでください)ではなくシンボリックエンジンを使うことで、真の数式理解が得られます。エンジンは意図を解析し、LaTeX 形式を扱い、微分や積分にも対応します。単なる計算ではなく、数学そのものを扱っています。 -
エラー処理は式単位
ある計算が失敗しても、他の式は継続して処理します。これによりモデルは「どれが成功し、どれが失敗したか」を把握でき、次のステップで自己修正しやすくなります。
実際に使ってみる
次のように、通常のモデルだけでは幻覚を起こしやすい質問を投げてみます。
import { anthropic } from '@ai-sdk/anthropic';
const { text } = await generateText({ model: anthropic('claude-sonnet-4-5'), prompt: 'Calculate 18472 × 9347, divide by 127, then take the square root of the result.', tools: { mathTool }, stopWhen: stepCountIs(5), // 最大5回のモデル/ツールステップを許可});
console.log(text);モデルは数式を認識し、精度が必要であることを判断してツールを呼び出し、正確な結果を取得した上で自然言語で説明します。各コンポーネントが得意な領域を担当することで、全体として信頼性の高いフローが実現します。
基本的な算術を超えて
シンボリックエンジンを使用しているため、単純な電卓ツールでは対処できないケースもこの手法で処理できます。
代数方程式を解きたいですか? 「次の方程式を解いてください: 3x + 7 = 22 および 2y - 5 = 13」のように指示すれば問題なく動作します。
微積分が必要ですか? 「x^3 + 2x^2 の導関数を求め、x = 2 で評価してください」という指示も、別のツール呼び出しとして扱われます。
LaTeX のサポートは、教育アプリを構築する際に特に有用です。エンジンは LaTeX 入力をそのまま理解し、レンダリング用に整形された結果を返すことができます。追加のパーシング処理は不要です。
より大きな視点
このパターンは単なる数学に留まらないと考えています。実際に行っているのは、LLM の限界を認識しつつ、その強みを活かすことです。LLM は意図の理解、自然言語の解析、ワークフローのオーケストレーションに優れていますが、電卓やデータベース、ファイルシステムのような決定論的な処理は得意ではありません。
LLM に決定論的なタスクを無理にやらせようとすればするほど、その本質に逆らうことになります。しかし、自然言語理解を専門ツールと組み合わせて、決定論的な部分を専用のコンポーネントに委ねたとき、初めて本当の価値が生まれます。
数学ツールは単なる一例に過ぎません。同じ原則は日付操作、金融計算、画像処理、データベースクエリ… 精度が創造性よりも重要になるあらゆる領域に適用できます。モデルにユーザーの意図を把握させ、実際の計算や処理はその仕事に最適化されたツールに任せるのです。
これは AI を用いたシステム構築の考え方の転換です。「モデルはこれができるか?」という問いから、「モデルはこれをオーケストレーションできるか?」という問いへとシフトします。表現のわずかな違いが、信頼性に大きな差をもたらすのです。