Dance with Agents

LLM時代のプログラミング言語——「バイナリでいいのでは?」を検証する

先日、知人のエンジニアがSNSでこう呟いているのを見かけました。

「もう高級言語じゃなくて、バイナリで書かせればいいんじゃね?」

エージェントにコードを書かせるなら、という話です。どうせ人間が1行ずつ読むわけじゃない。だったら人間の都合で設計された言語を経由する意味があるのか、と。

一瞬、「たしかに」と思いました。この記事は、その「たしかに」を検証してみるものです。

なぜ「たしかに」と思ってしまうのか

まず言葉の整理から。

言葉 意味
高級言語 人間が読み書きしやすい文法でプログラムを書く言語。Python、JavaScript、Rustなど
バイナリ(機械語・低級言語) コンピュータのCPUが直接実行できる、0と1の羅列そのもの
コンパイラ 高級言語で書かれたプログラムを、バイナリに変換するプログラム

高級言語が「高級」と呼ばれる理由は、人間にとって読みやすい・書きやすいからです。if (x > 0) は英語話者には自然に読めますが、CPUはこの文字列をそのままでは理解できません。コンパイラが裏でバイナリに翻訳して、はじめて実行できます。

ここで知人の論理はこうなります。「読みやすさ」が高級言語の存在理由なら、読み手がLLMだけになった時点でその理由は消える。 エージェントはコンパイラの出力も入力も気にせず、必要ならバイナリを直接生成できるはずだ、と。

一見、筋が通っています。

でも、高級言語は「人間のため」だけではなかった

ここに見落としがあります。高級言語とコンパイラという構造には、実はもう一つ役割があります。コンパイラは、書かれたコードが正しいかどうかを機械的にチェックしてくれる検証者でもある、ということです。

  • 変数の綴りを間違えたら、実行する前に教えてくれる
  • 数値を置くべき場所に文字列を置いたら、型が合わないと止めてくれる
  • 括弧の対応が崩れていたら、実行される前に弾かれる

この検証は、書き手が人間かLLMかに関係なく必要です。むしろLLMが書く場合こそ、この「即座に、具体的な理由つきで間違いを教えてくれる相手」が重要になります。エージェントに何か頼むと、しれっと間違った回答を返してくることがあるという話を書きましたが、その記事の結論はこうでした。

「ありそう」と「正しい」は、別のことです。

そもそもLLMって何?で触れたとおり、LLMがやっているのは「次に来そうな言葉を選ぶ」ことです。もっともらしい文字列を作るのは得意でも、それが正確かどうかは別問題。だからこそ、書いたそばから「これは間違っている」と教えてくれる仕組み(コンパイラや型チェッカー)が、人間が書くとき以上に価値を持ちます。

バイナリには、この検証者がいません。コンパイラという工程そのものを飛ばすからです。

LLMは、実は「桁の正確さ」が苦手

もう一つ、地味だけれど致命的な理由があります。LLMは1語(正確には1トークン)ずつ、確率的にもっともらしいものを選んで生成する仕組みです。そのため、長い数字の並びや厳密なビット列のような「一字一句の完全一致」が求められる作業を、もともと苦手にしています。電話番号や長いIDを書き写させると、途中の桁がさりげなく入れ替わっていることがあるのと同じ現象です。

高級言語のコードなら、1文字書き間違えてもたいてい構文エラーとして弾かれます。実行される前に気づけます。

バイナリは違います。1ビットずれても文法エラーにはならず、ただ動かなくなるか、もっと悪い場合は気づかれないまま壊れたまま動きます。 エラーメッセージという手がかりも出ません。LLMがもっとも苦手とする「厳密さ」を、もっとも不寛容な相手(CPU)にぶつける組み合わせになってしまいます。

さらに、高級言語からバイナリへの変換には、メモリの配置やCPUの計算用の作業スペース(レジスタ)の使い方といった、目に見えない細部の調整が大量に詰まっています。コンパイラがこれを肩代わりしてくれているから、書く側は「何をしたいか」に集中できる。バイナリを直接書かせるということは、この肩代わり分をぜんぶLLMの推論に押しつけることになり、間違える余地が桁違いに増えます。

では、LLM向けにはどんな言語がいいのか

知人の発想の「人間向けの都合を疑ってみる」という部分自体は、悪くないと思います。的が「バイナリ」だっただけで。

ここで、もう一歩踏み込んで考えてみます。書き手が人間ではなくLLMになったとき、本当に向いている言語とはどんなものか。

留保:現実には「学習データが多い言語」が一番強い

理屈に進む前に、現実的な制約を一つ。LLMはあくまで大量の人間が書いたコードを学習しています。なぜLLMは、こんなに何でもできるのかで書いたとおり、LLMの能力は学習データに何が大量にあったかに強く依存します。どれだけ理屈の上で優れた新言語を考えても、学習データが薄ければLLMは書き慣れず、存在しないAPIを平気ででっち上げたりします。

以下は、その制約をいったん脇に置いた思考実験です。

条件1:フィードバックの速さと厳密さ

人間向けの言語設計は「読みやすさ・書きやすさ」が主軸です。LLM向けなら、間違えたときに、即座に、具体的な理由つきで教えてくれることが最優先になります。

型システム(変数や関数が扱うデータの種類を、実行前にチェックする仕組み)が厳格な言語ほど、この点で有利です。たとえばRustには所有権・借用チェッカーという仕組みがあり、メモリの扱い方に少しでも問題があると、実行前に具体的な理由つきで止められます。人間には「厳しすぎる」と敬遠されがちな性質ですが、エージェントにとっては「妥協なく、即座に、理由つきでダメ出ししてくれる相手」として都合がいいはずです。この点については、実際にそうなっているかどうかまでは裏を取れていませんが、ここまでの理屈からは自然に導かれる予想です。

条件2:冗長さはコストではなく安全装置。ただし限度はある

人間は文字数を減らしたいので、簡潔な構文(省略記法や暗黙の変換)を好みます。LLMはタイプするコスト自体はほぼゼロなので、多少冗長でも、一意に解釈できる書き方のほうが事故が減ります。「賢い省略記法」は人間向けの発明で、LLM向けには必ずしも要りません。

ただし限度はあります。Attentionの仕組みで書いたとおり、LLMが読む文脈が長くなるほど計算コストは線形以上に増えます。むやみに冗長にしていいわけではなく、「曖昧さをなくすための冗長さ」と「ただ長いだけの冗長さ」は分けて考える必要があります。

条件3:副作用が型から見えること

グローバルな状態(プログラムのあちこちから読み書きされる、離れた場所の値)は、その値がどこでどう変わるかを覚えていないと安全に触れません。これはLLMが苦手とする「離れた場所の暗黙の依存関係を覚えておくこと」そのものです。

Haskellという言語は、この点で対照的です。同じ入力には常に同じ出力を返す「純粋な関数」を基本単位にしていて、ファイルの書き換えや画面表示のような「副作用」(関数の外の世界に影響を与える動作)を持つかどうかが、関数の型(宣言部分)を見ただけで分かるように設計されています。人間にはとっつきにくいと言われがちな特徴ですが、「離れた場所を覚えていなくても、その場の情報だけで正しさを判断できる」という点では、LLMに向いた性質だと言えます。

条件4:スペック(仕様)を言語自体に埋め込めること

「この関数は、こういう条件の入力を受け取り、こういう条件の出力を返す」という約束事を、コードの一部として書ける設計を契約による設計(Design by Contract)と呼びます。この考え方を取り入れた言語だと、エージェントの「書く→検証する→直す」というループが、人間の意図をいちいち推測する手間を経由せず、書かれたスペックと直接照合できます。テストコードを別途書くより、早い段階で食い違いに気づけるのが利点です。

条件5:小さく直交した部品

標準ライブラリが豊富で「同じことをする方法が3通りある」言語は、人間には便利でも、LLMには存在しないAPIをでっち上げる原因になりがちです。

LISP(Scheme・Clojureなどの方言を含む言語族の総称)は対照的な設計です。(足す 1 2) のように、プログラムの構造をそのまま括弧の入れ子で表現し、特殊な記号や省略記法がほとんどありません。括弧の対応さえ壊さなければ、構造が一意に決まるという特徴があり、覚え間違いの余地が小さくなります。

まとめ

  • 「人間が読まないなら、高級言語はいらない」という発想には、「高級言語=人間の読みやすさのため」という前提に見落としがあります。高級言語とコンパイラの組は、「書いたコードが正しいかを機械的にチェックしてくれる検証者」も兼ねている
  • LLMは1トークンずつ確率的に生成する仕組みなので、厳密な桁の一致が求められるバイナリと相性が悪い。高級言語なら1文字のミスは構文エラーで弾かれるが、バイナリは気づかれないまま壊れる
  • 書き手がLLMになったとき本当に価値を持つのは、フィードバックの速さと厳密さ(型システム・Rustの借用チェッカーのような仕組み)、副作用が型から見えること(Haskellのような設計)、スペックを言語自体に埋め込めること(契約による設計)、小さく直交した部品(LISPのようなシンプルな構文)
  • ただし現実には、学習データがどれだけ豊富かという制約が一番効く。理屈の上で優れた言語より、LLMが書き慣れている言語のほうが今のところ強い

知人の「バイナリでいいのでは」は、的は外れていましたが、「人間向けの前提を疑ってみる」という発想自体は当たっていたと思います。次に変わるべきは言語そのものの構文というより、その言語が持つ「検証のきめ細かさ」なのかもしれません。

LLM / エージェントの記事

LLM / エージェントのページへ →