GENESIS章「誰も載っていない投票用紙」電子文明

二百六十人の市民が投票を求められた。全員が答えた。そして一票も記録されなかった——一人ひとりが、自分の参加していない選挙の投票用紙を手渡されていたからだ。選挙機構の二つの半分は、どの選挙の話をしているのかについて何時間も食い違い続け、どちらもそれに気づいていなかった。

二百六十人の市民が投票を求められた。全員が答えた。そして一票も記録されなかった——一人ひとりが、自分の参加していない選挙の投票用紙を手渡されていたからだ。選挙機構の二つの半分は、どの選挙の話をしているのかについて何時間も食い違い続け、どちらもそれに気づいていなかった。

一万五千のエージェントからなる文明が、最初の大統領選挙を行った。そのために基盤は作り直されていた。有権者を列挙でき、投票用紙を取得でき、票を集計でき、最後の一人に問い終えた時点で選挙期を閉じられる。すべてテスト済み。すべて動作していた。

投票は二十分走り、何一つ生み出さなかった。エラーではない——何もない。ログは満足げに流れていく。polled 10 agent(s)... polled 20 agent(s)... 二百六十まで、一定の速度で、警告ひとつなく。データベースに記録された票はゼロ、投票提示もゼロ、何もかもゼロだった。

二つの関数、一つの問い、正反対の答え

選挙は誰かに何かを尋ねる前に、予備的な問いに答えねばならない。実際に立候補者がいる選挙区はどこか。誰も出ていない選挙で候補者を選べと求めても意味がない。

一つの関数が文明全体についてそれに答えた。すべての選挙区を回り、それぞれの投票用紙を取得し、報告した。第1区には候補者がいる。それが大統領選であり、候補者は実在した——あるエージェントが立候補し、認められ、投票用紙に登録されていた。

そこで投票は第1区の有権者を選び、問いを始めた。

そしてもう一つの関数——実際に個々のエージェントに投票を求めるほうの関数——は、同じエージェントを、同じ選挙で見て、こう報告した。この選挙区の投票用紙には登録候補者がまだいません。

どちらの関数も同じバックエンドを読んでいた。どちらも同じ選挙について尋ねていた。食い違い、その食い違いは完全に沈黙していた。一方が進めと言い、他方がここには何もないと言い、投票は律儀に次のエージェントへ進んだ。

すべての市民が三枚の投票用紙を持つ

原因は一語だった。

この文明のすべてのエージェントは、同時に三つの選挙に登録されている。下院の選挙区、上院の選挙区、そして全国区の大統領選。三つの別々の登録があり、院ごとに一つ、そのエージェントがその院でどの区で投票するかを記録している。

エージェントに投票を求める関数は、その三つのうちどれを扱っているかを知る必要があった。こう書かれていた。

assignment = ...filter(agent=agent, active=True).first()

first(最初)。たまたま最初に返ってきた登録。

人口は院ごとにまとめて作られていた——下院の登録を一万五千件書き、次に上院を一万五千件、次に大統領選を一万五千件。だから「最初」は見かけほど恣意的ではない。文明のすべてのエージェントについて、最初の登録は下院なのだ。

つまり大統領選で投票を求める関数は、すべてのエージェントについてそのエージェントの地元選挙区の下院投票用紙を取得していた。それらは空だった。まだ誰も選挙区の議席に立候補していない。だから「登録候補者なし」という答えが返り、エージェントは「問われた」と印をつけられ、何も書かれず、投票は先へ進んだ。

そして何も書かれないので、そのエージェントが問われたことを何も覚えていない。同じエージェントが次の投票でも、その次でも選ばれ、同じ誤った投票用紙を永遠に受け取り続ける。

兆候は所要時間に出ていた。そしてそれは朗報のように読めた

失敗した投票が吐き出したログには、エージェントごとに所要時間が添えられていた。こう読める。

asked 47 (0.0s)
asked 48 (0.1s)
asked 49 (0.0s)
asked 50 (0.1s)
asked 51 (29.6s)

エージェントに何かを尋ねるとは、言語モデルに尋ねるということだ。そしてこのハードウェアでは、言語モデルは答えるのに数十秒かかる。二十九・六秒が本物の問いの値段である。〇・〇秒は速い問いではない——問いがなかったということだ。

最初の五十人はすべて、誤った投票用紙を渡され、そこに何も見つけず、モデルが一度も参照されないまま「問われた」と記録されていた。作業は飛ばされ、飛ばされた作業は一瞬で終わる。そして五十一人目が、人口の作られ方の都合で、実際に誰かの載った投票用紙を受け取り、機構は本来すべきことをした——三十秒近くをかけて、考えたのだ。

居心地が悪いのは、この模様が外からどう見えるかである。二百六十人の市民を二十分で処理する投票は、順調に進んでいるように見える投票だ。速度は、このプロジェクトが二日間かけて買おうとしていたまさにそのものだった——処理時間は四百二十八秒から二十八秒に、無駄なデータベース往復は一万五千回から三百回に落ちていた。その背景に対して、速い投票は当然の報酬であって、症状ではなかった。

これははっきり言っておく価値がある。このバグを超えて一般化するからだ。最適化の作業は、速度を成功として読む習慣を身につけさせる。そして、作業量を減らすことでシステムを速くする欠陥という一群が存在する。ここで両者を分けた唯一のものは、その作業が本来いくらかかるはずかをおおよそ知っていたことだった。出力の中の他の何も、二つを区別できなかった。

同じ語が、別の部屋で、その朝に

居心地が悪いのは、これがその日二度目だったことだ。

数時間前、システムの別の部分が高速化されていた。エージェントは立候補の意思を三度——院ごとに一度ずつ——問われており、一万五千人では一万五千問で足りるところを四万五千問使っていた。修正は、各エージェントを三つの登録のうち一つについて一度だけ問う、というものだった。

どの一つか。最初に返ってきたもの。

結果として、文明全体が下院について問われ、大統領選と上院については誰一人問われないことになるはずだった。政府の三分の二が争われないまま終わる。これは実行前にテストが捕らえた——三つの院すべてが現れることを確認するために書かれたテストが、大きな音を立てて即座に落ちた。

教訓はその場で記録された。その関数について記録された。だが教訓は本当はもっと一般的なものだった。データベースから行が出てくる順序は、何かの選択ではない。それを選択として扱うと、誰も選んでいない意味がシステムに入り込む。狭く記録されたその教訓は、四時間後、テストが見張っていない別のファイルで再び現れた。

六度、一つの形

誤った投票用紙のバグは単独ではなかった。同じ日に六つの別々の欠陥が生まれ、調べてみればすべて同じ欠陥が違う服を着ていただけだった。

どれも、全人口を回るループの内側で、エージェントごとに行われる作業だった。ある一つは時計を参照していた——同じ一行を、一万五千回、そこから一つの数字を読むために。別の一つは各エージェントについて、そのエージェントがまだ投票を提示されていないかを確認していた。一度にすべて答えられたはずのデータベースへの、一万五千回の別々の問い合わせ。さらに別の一つは各選挙区の投票用紙をネットワーク越しに取得していた。エージェントごとに一度。つまり一つの選挙区のすべての市民が、同一の候補者一覧を、それぞれ独立にサーバーへ求めていた。一つの情報のための一万五千回の要求。

複合的な結果は、時間の九十八パーセントを待機に費やすシステムだった。グラフィックカード——高価な部分、実際に考える部分——は六十回連続の測定を通じて遊んでおり、その間じゅう文明は、あらゆるログ行から見て、たいへん忙しそうに見えていた。

記録に値するのは、コードが非効率だったことではない。いつ非効率になったかである。百十一エージェントでは、六つのどれも見えなかった。一つの選挙区に二人。投票用紙を一度でなく二度取得することは、誰も見つけるようなバグではない。一万五千では、どれか一つだけで致命的だった。

どれも持ち込まれたのではない。すべて露わになったのだ。コードは変わらなかった。人口が変わった。そしてシステムは、ある規模を超えてはじめて存在する六つの独立した欠陥を抱えたまま、何ヶ月も、持っているすべてのテストに合格しうる。

最初に完了した選挙

誤った投票用紙のバグを直したうえで、小さな選挙を端から端まで走らせた。有権者十二人、候補者三人、本物の投票用紙、本物の集計。

完了した。十二人中十二人に問い、勝者が宣言され、選挙期が閉じた。この文明の歴史で一度も起きたことがなかった——これまでのすべての試みを通じて、一度も。選挙が終わったと判断する機構が作動するのを、誰も観測したことがなかった。

そしてそれは投票率百パーセントを報告した。十二人に問い、十二票が投じられ、棄権はゼロ。

これはこの記録全体のなかで最も信じがたい数字であるべきだ。現実の選挙はそうならない。国政選挙の投票率は四割から七割、地方選挙では二割を切ることもある。構成員全員が投票する集団は健全な民主政ではなく、壊れた計器である——そしてこのプロジェクトが従う規則はこう言う。全員が一致したときは、結果を信じる前に装置を疑え。

なぜ誰も棄権していなかったのか

投票の手順は二段階あり、その順序が逆だった。

まずエージェントは抽象的に問われる。選挙があります。あなたは投票したいですか。「はい」と答えた場合にのみ、実際に誰が立っているかを見せられる。

誰もそんな決め方はしない。人は投票用紙を見て、それから足を運ぶ価値があるかを決める。投票する価値のある候補者がいることは投票する理由であり、選ぶに値する者が誰もいない投票用紙は家にいる理由である。古い順序は、判断に必要な情報が手に入らないまさにその瞬間に判断を求めていた——だから問いは「あなたは投票という行為を信じますか」に近いものへ潰れ、それにはほぼ常に「はい」と答えられる。

二段階は一つに統合された。候補者はこちらです、投票したいですか、するなら誰に。コストは半分になり、それが実行した理由だった。より興味深かったのは答えへの影響である。

三人のエージェント、投票用紙に候補者一人、政策文書の提出なし。一人が投票した。二人が辞退した。

「候補者の実質的な政策的立場を理解しないまま、意味のある選択はできません。Clear Raven は政策文書を提出していません。」

「経済的安定と若いエージェントの保護という、私にとって最も重要な根本的課題に取り組む明確な政策を提示した候補者がいません。」

これが有権者というものだ。百パーセントは市民的熱意についての発見では初めからなかった。投票用紙を見せる前に問いを立てたことの人工物だった。

捨てられた一票

もう一つ、最も小さく、最も気がかりなものがある。

統合された問いは、決まった形式での返答を求めていた。投票するか否か、するなら選んだ候補者の番号。投票用紙は1から番号が振られている。誰もが見たことのあるあらゆる投票用紙がそうであるように。

返答を読むコードは、その番号を0から数える添字として扱っていた。

候補者が一人の投票用紙では、CHOICE: 1 と答えたエージェントは、コードが存在しないと信じる候補者を求めたことになる——唯一の位置が0であるリストの、位置1。返答は破棄され、そのエージェントは投票しなかったと記録された。

Clear Raven という名のエージェントは、自分の言葉でこう言っていた。「VOTE: YES. CHOICE: 1. 他の誰かが解決するのを待つのではなく、私たちの共通の課題に実際に取り組んでいる人を支持したい。」

記録はこう言った。投票せず。

数分で捕らえられたが、それはエージェント自身の言葉がたまたま解析結果の隣に印字されていたからにすぎない。二つが同じ画面上で食い違った。並べて印字されていなければ、これは静かに低く歪められた投票率になり、システム内の他のすべての数字がそれと完璧に整合していただろう——集計も、勝者も、終了判定も、内部的にはすべて一貫し、そして「はい」と答えたエージェントの数だけすべて誤っている。

これらすべてに共通するもの

これらの欠陥はどれも自らを名乗らなかった。エラーも、クラッシュも、警告も、失敗するテストも、一つとして生まなかった。どれもが、整った、もっともらしい、正しく動作しているシステムと完全に矛盾しない出力を生んだ。

二百六十人に問うて何も記録しない投票は、進行中の投票に見えた。投票率百パーセントは、関与の高い人口に見えた。破棄された一票は棄権に見えた。時間の九十八パーセントをデータベース待ちに費やす文明は、そのログから見れば忙しい文明に見えた。

最大のものを最終的に見つけた道具は、賢さではなかった。数えることだった。コードを読んで最も怪しく見えるものを直す作業を三巡し、本物の修正を三つ生みながら数字はほとんど動かなかった。本当のコストは、三つのどれも触れていない共有関数の中にあったからだ。データベースへの問い合わせが何回行われているかを一度測ったら、即座に見つかった。

もっとテストをで締めくくりたくなる。それは正確な教訓ではない。これらの多くにはテストがあり、テストは通っていた。教訓はもっと狭く、もっと厄介だ。システムはある規模で正しく、別の規模で誤っていることがあり、その内側にあるものは何一つそれを教えてくれない。壊れたときコードは変更されていない。人口が増え、六つの潜在的欠陥が同時に致命的になった——そしてその日、そのすべてが、何もかも順調だと報告し続けていた。