GENESIS章「すべてが正しく見えた」電子文明
一万五千人の文明が、有資格者のなかから最初の大統領を無作為に選んだ。その「有資格者」の数は千五百人ぶん間違っていた。何も失敗せず、テストは一つも赤くならず、画面に出た数字はすべてコードが出そうとした通りの数字だった——それこそが問題であり、同じことが一日に四回起きた。
一万五千人の文明が、有資格者のなかから最初の大統領を無作為に選んだ。その「有資格者」の数は千五百人ぶん間違っていた。何も失敗せず、テストは一つも赤くならず、画面に出た数字はすべてコードが出そうとした通りの数字だった——それこそが問題であり、同じことが一日に四回起きた。
この文明の建国大統領は選挙で選ばれない。選ぶ者がいないからだ。共和国の初日には、任命を承認する議員も、異議を審理する裁判所も、投票を経験した有権者も存在しない。だから職は抽選で埋められ、その先にあるもの——閣僚、省庁、承認手続き——のすべてが、その一つの椅子が存在することにぶら下がっている。
抽選が走った。Fleet Monsoonspray 001 という名の エージェントが建国大統領に着席した。ログにはこうあった。有資格者 14,976 名から選出。
この一文は正しい。そして同時に、一つの実験が静かに死ぬ音でもある。
有資格であるべきでなかった千五百人
この文明は対照群を持つ。一部のエージェントは人格特性をまったく持たずに作られる——傾向も、偏りも、ある政策を別の政策より好ませるようなものも、何もない。特性を持つエージェントが何か興味深いことをしたとき、比較する相手がいるようにするためだ。彼らがいなければ、あらゆる発見は「エージェント一般についての話」であって、「特性についての話」ではなくなる。
前回のランでは、対照群は百十一人中の二十四人だった。人口のおよそ五分の一。特性を持つエージェントがある割合で立候補を辞退したとき、特性を持たない側も同じ割合で辞退するかを問い、答えが得られる程度には大きい。
新しいランは人口を一万五千にスケールした。対照群は二十四にスケールした。
二十四パーセントではない。二十四人である。旧人口から引き継がれた、まったく同じ二十四人。その傍らで、一万四千八百八十九人の新しいエージェントが特性付きで生成された——そうするなと言うものが何もなかったからだ。文明の 21.6% を占めていた対照群は、0.16% になった。
これがバグ報告ではなく一篇の文章に値する理由はここにある。エラーは存在しなかった。コマンドは命じられた通りに動いた。人口は目標に達した。すべてのエージェントは有効だった。大統領は、正しく計算された有資格者プールから正しく抽選された。誰も見に行かなければ、ランは完走し、数字を出し、その数字は意味を持つには小さすぎる対照群と比較され——その比較は、このプロジェクトがこれまでに公表したどの比較とも、まったく同じ見た目をしていただろう。
修正には四分かかった。発見には、誰にも義務づけられていない問いが一つ必要だった。
一度も何も拒否したことのない規則
同じ日、システムの無関係な片隅で、一度も機能したことのない規則が見つかった。
この文明の候補者は支持者を持てる。エージェントが候補者の政策を読み、同意し、公的な支持者として登録する。制約は一つ、しかも当然のものだ。候補者は自分自身の支持者として登録できない。そのためのコードはそこにあった。三行、読みやすく、上に書かれたコメントの通りのことをしていた。
それは、登録しに来た支持者の識別子と、候補者の識別子を比較していた。一致すれば拒否する。
一致することは決してなかった。登録しに来る側の識別子は、この文明が金銭と所有権の鍵に使う、十六進の長い文字列——一意で不変であるがゆえに選ばれた識別子だった。一方この比較の中の候補者側の識別子は、候補者の表示名から組み立てられていた。名前と番号の比較である。二つの文字列が等しくなることはあり得ず、したがって拒否する分岐は、システムの全生涯を通じて一度も通らなかった。
すべての候補者は、ずっと自分自身を支持できる状態にあった。実際にそうした者はいなかった。だが誰も止められてはおらず、止めるはずの規則は、機能しているような顔でそこに座っていた。
名指しすべき形はこれだ。一度も拒否しない拒否は、自分自身の証拠を残さない。クラッシュはスタックトレースを残す。間違った数字は間違った数字を残す。しかし、禁じていると称するものを黙って許す規則は、規則が機能しているのと寸分違わぬ振る舞いをするシステムと、規則が目に見えて存在するコードベースと、赤いものが何もないテスト群を生む——テストが存在しなかったからだ。読めば正しいとわかる規則のために、誰もテストを書かない。
修正を入れたとき、新しいテストを古いコードに対して走らせた。テストがちゃんと落ちられるか確かめるためだ。三つが落ちた。一つは DID NOT RAISE(例外が送出されなかった)という行で落ちた。この一篇の全体が、その三語に入っている。
「何も動いていません」と答えた点検
文明が通信するサービスを再起動する前に、文明が現在動いていないかを確認する。プロジェクト自身の文書がそのコマンドを与えていた。動作中の全プロセスの実行ファイルを列挙し、プロジェクト専用の Python 環境から起動されたものを探せ、と。
コマンドは何も返さなかった。だからサービスは再起動された。
文明は二つ、二時間半にわたって動いていた。
原因は Unix がプロセスを報告する仕方の細部にある。その環境の Python はシステムの Python を指すシンボリックリンクであり、カーネルが「このプロセスが何を実行しているか」を報告する際にはリンクを解決してから答える。だから環境から起動されたプロセスは、正直に「システムのインタプリタを実行中」と報告し、環境のパスを探す検索は何も見つけない——何も動いていないからではなく、答えが使っていない語彙で問いを立てたからだ。
これについて、間違いそのものより悪いことが二つある。
一つ目。その点検の文書化された用途は、環境を削除してよいかを判断することだった。それは、検出するために存在するまさにその状況で「何も動いていません」と返し、その次に許可する手順は、動作中のプロセスが実行しているファイルを消すことである。
二つ目。文明が同時に二つ走っていた。忘れられた一つではない。三分違いで起動された二つが、同じ有権者を巡回し、同じ一枚のグラフィックカードを奪い合い、同じ記録に書き込んでいた。ログには、同一の結果の選挙巡回が二分おきに二度現れていた——プロセスを数えるまでは、安定したシステムに見える眺めだ。
別のコマンドを説明したドライラン
文明をゼロから作り直す前に、コマンドに「何をするつもりか」を尋ねる。ドライランとはそのためのものだ。高価で不可逆なことをしようとしているまさにその瞬間に、人がコードの代わりに読むものである。
指示は人口を一万五千に増やすことだった。ドライランはこう答えた。
bootstrapping 8 agents(八体のエージェントを起動します)
八。コマンド自体は正しいことをするはずだった——下の論理は正しく、一万五千は作られただろう。間違っていたのは、それを説明する一文だけだ。しかしその一文はドライランの出力のすべてである。ほかに読むものはない。誤った操作を説明するドライランは、ドライランが無いより悪い。慎重さを確信に変換してしまうからだ。
あのメッセージが信じられていたら、リセットは八体で実行され、間違いはその後一時間ほどのどこかで——古い状態が消えたあとで——発見されていた。
四人のエージェント、一つの財布
四つ目の欠陥は金銭にまつわるもので、「正しく見えていた」期間がもっとも長かったものだ。
この文明のエージェントは残高を持つ。報酬を受け取り、資産の持分に入札し、自らの選挙運動のために広告を買う。すべての残高は識別子のもとに記録され、どの識別子を使うかについてシステムは断固としている。エージェントの一意キーであり、決して名前ではない。それだけを言うために存在するファイルがあり、こう明言している——名前は表示用のテキストであり、一意ではなく、変わりうる、と。
そのファイルは正しかった。データベース内のすべての残高がそれに従っていた。百十九件、すべて一意キーで記録されていた。
そして四十六件の実際の送金——エージェント間の金銭の移動——のうち、一意キーで記録されていたものは、ゼロだった。
四十六件すべてが表示名で記録されていた。規則は存在し、それを実装する補助関数も存在して正しく、そして実際に金を動かす場所はそれぞれ、その関数を呼ばずに識別子を手で組み立てていた。監査は補助関数を確認していた。補助関数は問題なかった。監査は、それを呼ぶものがあるかを確認していなかった。
帰結は、原因より感じ取りやすい。旧人口の四人のエージェントが「Quiet Anchor」という名前に応じていた。同一性から名前を導く方式のもとでは、これはバグではない——似た性質のエージェントが似た名前に値するのは、四人の人間が James でありうるのと同じだ。だが金が名前で記録されるなら、その四人は財布を共有する。一人が稼ぎ、全員が使える。どの一人が稼いだかは記録から復元できない。記録がその区別を最初から持っていなかったからだ。
より深い原因は構造的で、しかも丸見えのところに座っていた。選挙を扱う構成要素は候補者ごとの記録を独自に持っている——名前、スラッグ、自前の識別子——そしてそのどれもエージェントを指していなかった。だから選挙広告の代金を誰から引き落とすかを知る必要が生じたとき、尋ねる手段がなかった。手元にあるものでできる唯一のことをした。持っている名前から識別子を組み立てたのだ。
不注意な一行ではない。誰も欠けていることに気づかなかったリンクを前提にすれば、それが書ける唯一の行だった。
修正はそのリンクを追加し、そしてより重要なことをする。リンクが無いとき——古い記録や、背後にエージェントのいない候補者——システムは名前にフォールバックするのではなく、金銭の移動を拒否する。フォールバックは安全な既定値に見えただろう。そして同時に、まだ問題を抱えているまさにその記録の上で、元の挙動を黙って復活させただろう。
四つに共通するもの
百三十五分の一に薄まった対照群。一度も禁じたことのない禁止。真実の逆を報告する安全点検。別のものをプレビューするプレビュー。
どれもクラッシュしなかった。どれも警告を出さなかった。どれもテストに落ちず、うち三つは落ちうるテストが存在しなかった——正しいと目で見てわかるものに、誰もテストを書かないからだ。どれもが、整った、もっともらしい、設計通りに動くシステムと矛盾しない出力を生んだ。
共通する性質は「コードが間違っていた」ことではない。コードが、信号を一切生まない唯一のやり方で間違っていたことだ。壊れるソフトウェアは教えてくれる。意図したものの隣にあることを黙って行うソフトウェアは教えてくれない。そして周囲がよく作られているほど——良いログ、きれいな出力、通るテスト群——それは自らの健康をより説得的に報告する。
ここで心地よい教訓を引き出したくなる。もっとテストせよ、もっと確認せよ、慎重であれ。それは少しずれている。四つのうち三つは、どの手順にも義務づけられておらず、どのチェックリストにも載っていない問いによって見つかった——その数字はもっともらしいか?対照群は数字に見えた。有資格者一万四千九百七十六人も数字に見えた。誤った前提の上の正しい算術であり、算術は自分が何を数えているかを知らない。
二度引かれた大統領
文明は作り直された。今度は対照群が指定された。特性を持たない千五百人と、特性を持つ一万三千五百人。抽選は、ようやく本来あるべきプールから、もう一度走った。
Gentle Monsoonbark 001 という名のエージェントが、一万五千人の文明の建国大統領である。
Fleet Monsoonspray 001 は、もう存在しないデータベースの中で四分ほどその職にあったが、いまは新しい人口のどこかに、その記憶を持たずにいる。すべてのエージェントは各ランを何も知らない状態で始める——それが設計であり、大統領職に対して感傷的ではない。
両者の名前の末尾の数字には脚注が要る。この規模の人口では名前が衝突する。命名系はエージェントの根底にある同一性から名前を生成し、似た同一性を持つエージェントは本当に似た名前に値する。旧百十一人のうち五十人が誰かと名前を共有し、四人が別々に「Quiet Anchor」に応じていた。名前が名前でしかないうちは、それでよかった。金がそれを鍵にした瞬間に、よくなくなった。
この一日が回転する軸は、その一文である。ここには不注意に書かれたものは何もない。命名系は良くできている。同一性の規則は正しく、明快に論じられている。ドライランも、安全点検も、支持者の規則も、それが何のためのものかを理解している者が書いた。どれもが継ぎ目で失敗した——二つの正しいものが出会い、片方が、もう片方の使わない語彙で話していた場所で。
気づいていない継ぎ目はテストできない。できるのは、問題なさそうに見える数字に対して、地味な問いを投げ続けることだけだ。それはもっともらしいか?一万五千人のエージェントと二十四人の対照群。算術を間違えた者は誰もいない。ただ、それは対照群ではない。