開場まで、あと二十分足らず。劇場の正面で、着膨れの扉番人形が、入場審査の途中で完全に凍りついていました。
表の路地には、開演を待つ長い行列が伸びています。今夜は、この劇場が開いて以来、最大規模の合同公演でした。旅回りのフェアファクス一座が客演に加わると聞きつけて、後援者も評論家も、いつもの何倍もの一般客も、みな詰めかけています。列の先頭近くでは、扉と行列のあいだで立ち往生してしまった数人の客が、困惑した顔で扉を見上げていました。ガス灯の明かりの下、誰かの扇子が苛立たしげに開いては閉じる音だけが、やけにはっきりと聞こえます。
第1幕: 予兆と調律 ── すでに止まっている扉
私は道具箱を放り出すようにして駆けつけました。人形の腕にも胴にも、見覚えのない真鍮の道具や、使い道の分からない鍵束が、幾重にも括りつけられています。指先で一つずつ触れて確かめるうち、その重みに、思わず息を呑みました。よく見ると、その中には見覚えのある部品もあります——記憶の蓄音機の予備シリンダーの欠片、いつかの認可灯の試作品らしい残骸、進行表の写しを収めた小さな真鍮の匣。これまでの改修のたび、誰かが「念のため」と括りつけてきたものが、使われないまま今も残っているのでした。持ち上げようとした腕が、思ったより重くて驚きます。
「……何枚、着込んでいるの」
思わず零れた呟きが、そのままこの人形の呼び名になりました。以来、私はこれを着膨れの扉番人形と呼んでいます。
人形の胸元には、小さな記録板が埋め込まれていました。「本日入場: 187名」——けれど、実際に中へ入った客の顔を目で数えれば、明らかにそれより少ない人数しか見当たりません。数字だけが、実態より先に進んでいるのです。私は記録板の数字と行列の顔ぶれを、何度も見比べました。合いません。何度数え直しても、合わないのです。
記録の奥を覗き込み、私はさらに手を進めました。券の種類によって、処理の道筋が分かれていることが分かります。あらかじめ予約されたVIP席の客と、当日窓口で買った一般の客とで、別々の道を通っている。どちらも「名簿や台帳に確認が取れたら通す」という、同じはずの決まりのつもりでした。それなのに、何かが食い違っている——そこまでは見当がつきましたが、まだ核心には届きません。
少し離れた場所で、マエストロが様子を見ていました。今日はいつも通りの見守りで、まだ近づいてはきません。行列のざわめきが、少しずつ大きくなっていきます。誰かが苛立ったように、閉じたままの扉を軽く叩く音がしました。開演までの時間が、容赦なく減っていきます。額の汗を拭う暇も惜しんで、私は記録板ともう一度向き合いました。
第2幕: 絡糸と破綻 ── 全部を書き足す手が、止まらない
「時間がない。使えるものは、全部使おう」
私は手元の紙に、これまで学んだ技法を次々と書き出していきました。「記憶を辿る仕組み」と書きながら、記憶の蓄音機が低く唸る音を思い出します。「道具を絞り込む仕組み」と書けば、道具箱が重すぎた大工人形の姿がよぎりました。「粘り強く考え直す仕組み」の一行には、まだ足りないと繰り返した肖像人形の隻眼鏡が重なります。「複数の人形で手分けする仕組み」「危ない判断は人に委ねる仕組み」——言葉にするたび、これなら今夜を乗り切れるという安心感が広がっていきました。書き足すごとに紙の余白が埋まっていく様子に、私は奇妙な高揚を覚えていました。過去十一話で覚えたものを、全部この一体に注ぎ込んでしまえば、もう何も心配することはない——そんな気さえしてきます。ペン先が紙を擦る音だけが、やけに速く重なっていきました。
けれど、書き終えたはずの案を読み返して、私は手を止めました。「怪しい券は人の判断を待つ」という一行と、「判定に自信が持てるまで何度も検分し直す」という一行が、すぐ近くに並んでいます。判断を待っているあいだも検分をやめないなら、いったいいつ待つのをやめて、いつ検分をやめればいいのでしょうか。読み返すほどに、自分でも筋道を追えなくなっていきました。
ふと顔を上げると、列の中ほどで、小さな子供が親とはぐれて泣き出しているのが見えました。長く待たされた行列の圧に押され、はぐれてしまったようです。誰も彼女を責められません。悪いのは、まだ紙の上で唸っている私自身でした。考え込んでいる時間そのものが、こうして代償を生んでいるのです。
紙の余白は、もう新しい案を書き込む隙もないほど埋まっていました。それを見下ろした瞬間、目の前で凍りついたままの扉番人形と、自分がこれから作ろうとしていたものが、同じ形をしていることに気づきます。着膨れているのは、人形だけではなかったのです。
第3幕: 示唆と編み直し ── 対等に、問い返す
通りかかったアンが、埋め尽くされた紙を一瞥しました。
「あなた、これ全部いっぺんにやるつもり?書けば書くほど、扉が重くなるだけよ」
「記憶を辿る仕組みを使えば、常連のお客様なら顔を覚えて融通が……道具を絞り込む仕組みも足して、台帳の種類ごとに探し方を選び直せば……」と口にしかけた私に、マエストロが道具を置いて初めて向き直ります。今回は一言だけ残して離れることも、作業を続けたまま口を挟むこともせず、正面から問いを重ねてきました。
「その記録は、覚えておくべきほど複雑な形をしているのか」
「……いえ、ただの数字です。増えたか、増えていないか、それだけの……」言いながら、自分の言葉で気づきます。「……もう、それだけで十分な形をしているんですね」
マエストロは頷きもせず、次の問いへ移りました。私が「なら、自信が持てるまで粘り強く検分し直せば」と続けようとした矢先でした。
「一枚の券に、“良くなる"余地があるのか。通すか、通さぬかだけだろう」
「……無い、です。良し悪しの段階が、そもそも……」
私が「複数の人形で手分けすれば」と言いかけたところで、先に割り込んできたのはマエストロでなく、アンでした。
「今夜、この門はいくつあるのよ」
「……一つだけ」
「なら答えは出てるじゃない」
マエストロは何も言わず、ただ小さく顎を引きました。アンの言葉を、そのまま認めるように。
ここまでの三つのやり取りで、私はようやく気づき始めていました。記憶を検索する仕組みは、今夜のこの記録がすでに一つの数字に収まっている以上、要りません。道具を絞り込む仕組みも、参照すべき台帳はVIP用と一般用の二つだけとあらかじめ決まっていて、状況に応じて実行時に候補を探し直す必要がある、可変で大量の道具群ではない以上、当てはまりませんでした。何度も考え直す仕組みも、通すか通さぬかの二択に「良し悪し」という段階がない以上、意味を持ちません。複数の人形で足並みを揃える仕組みも、揃えるべき相手がここにはいない以上、要らない。そして、危ない判断を人に委ねる仕組みも——貴賓や評論家のような、本当に判断の難しいお客様は、そもそも人間の案内係が個別に出迎える昔からの決まりがあります。この人形の担当は、あくまで一般とVIPの、型通りの確認だけでした。
それでも、まだ一つだけ、迷いが残っていました。台帳への問い合わせが、もし一時的にうまく繋がらなかったら——今度は、マエストロの問いを待ちませんでした。私の方から、一歩を差し出します。
「……台帳が繋がらない時のやり直しは、残していいですか。ただ、答えが出るまでは数えない、という約束で」
マエストロは、初めて小さく頷きました。
「それでいい」
短い、だが確かな一言でした。
四つのやり取りを経て、私は静かに気づきます。
「……動いてるだけじゃ、美しい舞台にはならない」
言葉にしてみて、自分でも驚きました。これまで「とりあえず動いてるし」から始まった口癖が、ずいぶん遠くまで来たものだと思います。
さて、編み直しです。
KISS原則(Keep It Simple, Stupid ── 必要最小限の道具だけを選び、最も単純な仕組みで最大の安定性を生み出す設計の規律)が、今夜の私に求めているものが、ようやく見えてきました。これまで積み上げてきた十の技法の一つ一つは、それぞれ確かに正しい場面で正しく効いていました。けれど、それを一度に全部この一体に注ぎ込もうとすることは、また別の過ちです。この、成功したはずの積み重ねが、かえって次を過剰にしてしまう罠には、名前があります——Second-System Effect(第二システム効果 ── 慎重に作られた最初のシステムの後継として、自信過剰から過剰設計され肥大化したシステムが作られがちだという傾向)。過去十一話は、それぞれ慎重に組まれた最初のシステムでした。今夜の私が陥りかけていたのは、その全部を一度に注ぎ込もうとする、最も危険な二番目のシステムだったのです。
まず、着膨れの扉番人形の記録を、正確に読み直すところから始めます。
| |
このadmitという一つのメソッドの中には、実は三つの異なる仕事が同居しています。券の種類によって行き先を決める仕事、外部の台帳へ実際に問い合わせる仕事、そして入場者数という記録を書き換える仕事です。VIP分岐だけを見ると、self.admitted_today += 1が、台帳への問い合わせ(_call_ledger)より前の行に書かれています。そして、問い合わせの結果verifiedがFalseだったとしても、先に数えた分を戻す一行が、どこにもありません。一方、一般客の分岐は、問い合わせの後、verifiedがTrueだったときだけ数える——同じ「確認してから通す」という決まりのはずなのに、状態を書き換えるタイミングが、分岐ごとに食い違っているのです。
技法で埋め尽くした紙を裏返して、二つの分岐で手が動く順番を、糸の通り道として描き出してみました。並べてみると、同じ二つの仕事が、途中でねじれて逆順になっているのが見て取れます。

なぜ、こんな非対称が生まれてしまったのでしょうか。振り分け・外部呼び出し・状態更新という三つの関心を一つのメソッドに同居させたまま、改修者たちがそのときそのときの都合で手を加え続けてきたからです。VIP分岐に確認前カウントを足した誰かは、その時その分岐だけを見ていて、一般客の分岐が確認後にカウントしているという前提とは無関係に、手を加えました。一つのメソッドに複数の関心が同居していると、分岐同士の一貫性を保証するものは、書く人の注意力以外に何もありません。
では、「次はもっと注意深く書く」で足りるのでしょうか。答えは否です。今夜だけでも、券の種類はこの先増えるかもしれません——団体割引の券が新設されるかもしれませんし、貴賓対応の枠が今後増えるかもしれません。分岐が増えるたびに、新しい分岐が同じ非対称を持ち込まない保証は、注意力に頼る限り築けないのです。必要なのは、状態を書き換えられる場所そのものを、一つに絞るという構造的な保証でした。
三つの関心を、はっきり分けて組み直します。
| |
ROSTER_BY_KINDは、券の種類から参照すべき台帳の名前を選ぶだけの、小さな対応表です。これがRouter——どの窓口に振り分けるかを決めるだけの役目で、判断の中身にも記録にも一切触れません。verify_with_ledgerは、実際に外部の台帳へ問い合わせるだけの役目——これがFunction Callingです。そしてGateLedgerが、入場者数という状態を持つ唯一の場所になりました。record_admissionという公開メソッドが、状態を書き換えられるただ一つの窓口です。
編み直した糸の通り道も、同じ紙の裏に描き添えます。今度はどこにもねじれがなく、選ぶ糸、確かめる糸、数える糸——三本がまっすぐ一列に連なるだけの、拍子抜けするほど簡素な図になりました。

admitの中身を見比べると、違いは一目瞭然です。roster = ROSTER_BY_KIND[ticket.kind]でどの台帳を見るか決め、verified = self.verify_with_ledger(ticket, roster)で実際に問い合わせ、if verified: ledger.record_admission()で、確認が取れた場合にのみ記録する。たとえば、団体割引の券が新設されたとしても、ROSTER_BY_KINDに一行足すだけで済みます。admitの中身——Routerで選び、Function Callingで確かめ、確認が取れてからStateに記録する、という順序そのものには、一切手を加える必要がありません。券の種類が今後いくつ増えても、record_admissionが呼ばれる場所は、この一行のまま変わらないのです。非対称が生まれる余地そのものが、なくなりました。
机上の小さな模擬公演で、この違いを数字として確かめておきます。まず、Beforeがどう壊れるかの再現からです。無効なVIP券を何度も試すと、そのたびにadmitted_todayだけが積み上がり、やがて実際にはまだ誰も入っていないはずなのに定員に達したと扉番人形が思い込み、後から訪れた正規の一般客まで締め出してしまうことを確かめました。台帳への問い合わせ自体が例外を投げて失敗した場合でも、同じように数だけが先に進んでしまうことも確認済みです。
対して、Afterでは、VIP券・一般券のどちらであっても、確認が取れた場合にのみ、ちょうど一件だけ記録されることを確かめます。定員に達すれば、券の種類を問わず正しく締め出されること、台帳への問い合わせが一時的にうまく繋がらなかった場合でも、記録には一切触れず、後から呼び出し側がもう一度同じadmitを試せば——今日、マエストロに認めてもらったやり直しです——安全に、ちょうど一件だけ入場が記録されることも確かめました。問い合わせに失敗した回はrecord_admissionが一度も呼ばれないため、成功するまで何度試しても、実際に記録が増えるのは成功したその一回だけです。最後に、券の種類に見たこともない文字列を紛れ込ませると、Pydanticがきちんと撥ねつけることも確認しています。
正直に、一つだけ留保を置いておかなければなりません。この設計が保証するのは、「入場者数の記録が、確認が取れた場合にのみ、常に同じ場所で行われること」であって、「外部の名簿や台帳そのものが正しいこと」ではありません。名簿に誤りがあれば、正しい客を誤って弾いてしまう可能性は残ります。それは、今回の設計変更が触れる範囲の、外側にある話でした。
第4幕: 開演と調和 ── 三人で見届ける
新しい仕立てで、着膨れの扉番人形が動き出します。行列が、滞ることなく流れ込んでいきました。記録の数字は、実際に入場した客の数と、寸分違わず重なったまま増えていきます。定員に達した瞬間、扉は静かに、しかし今度こそ正しい理由で入場を止めました——満席になったのだと、私は初めて、安堵とともにその数字を受け止めます。
開幕の合図が鳴り、舞台の幕が上がりました。フェアファクス一座の一団が、客席の熱気の中に紛れているのが遠目に見えます。
アンとマエストロが、ほぼ同時に言葉を重ねました。
「今回ばかりは、認めてあげる」
「……よく、堪えた」
どちらが先だったのか、私の耳にはほとんど分からないほど近い間合いでした。マエストロが、初めて私と正面から目を合わせます。何かを言うでもなく、ただそれだけで、これまでのどの一言よりも多くを伝えられた気がしました。
いつもなら、こういう時マエストロは一言残して工房の方へ戻っていくはずでした。今回、彼はそうしませんでした。道具箱にも手を伸ばさず、その場に立ったまま、私たちと同じ方を向いています。対等に議論を交わした相手として、最後まで隣にいることを選んだのだと、私にはそう感じられました。
私たちは三人並んで、舞台袖からしばらく開演を見届けます。部屋に戻って一人で書くのではなく、その場でノートを開き、十二ページ目を書き終えました。書き終えた手で、初めて表紙をめくり直し、一ページ目から今夜までの全十二ページを、ゆっくりと読み返します。
拙い字で「動いてるからいいのよね」と無邪気に書きつけた最初のページ。迷路から出られなくなった探鉱人形に、回数の上限を書き足した日のページ。アン自身が声を詰まらせ、フリーズを繰り返した夜の、震えた筆跡のページ。指揮棒と譜面台の絵を隅に描き添えた、進行表の夜のページ。どのページも、そのときどきの精一杯の理解が、そのままの形で残っていました。それぞれの夜の、それぞれの人形たちの記録が、そこに束ねられています。
十のからくりを覚えたことは、間違いではありませんでした。間違っていたのは、それを全部一度に使おうとしたことです。着膨れの扉番人形が本当に必要としていたのは、券の行き先を選ぶ糸と、台帳に確かめる糸と、確かめてから数える糸——たった三本だけだったのです。
🎭 操り人形師の覚書(Colette’s Puppeteer Log)
- 調整したからくり(モジュール/関数名):
GatekeeperPuppet.admit(Before: 券種の分岐ごとに振り分け・台帳照合・入場者数の記録が同居し、VIP分岐だけ確認前カウント+失敗時ロールバック無しという非対称なタイミングを持つ → After:ROSTER_BY_KIND〔Router〕・verify_with_ledger〔Function Calling〕・GateLedger.record_admission〔State、確認後にここでのみ呼ばれる〕への分離) - 思考のゼンマイ(思考素子/LLMの特性):
- 本話の核心は特定のLLMの揺らぎではなく、振り分け・外部呼び出し・状態更新という3つの関心を1箇所に同居させたときに生じる、人間の注意力だけに頼った一貫性の弱さそのもの
- 操り糸の張り具合(運糸の法/プロンプトと制御):
- Pydantic
Ticket(Literal["vip","general"]で券種を閉じる)+GateLedger(record_admissionを唯一の状態書き換え窓口とする)+ROSTER_BY_KINDによるRouter +verify_with_ledgerによるFunction Calling。自己修復の回の教訓は、verify_with_ledger内に新規のリトライを追加するのでなく、「呼び出す側が何度やり直しても、確認が取れるまでは記録に触れない」という順序として活かした
- Pydantic
- 舞台裏の絡まり(背伸びの過ち/避けるべき罠):
- 複合的な危機を前に、過去10話で得た技法を「使えるものは全部使えば安全」と一度に積み増そうとしたこと。対象の人形でなく、自分自身の改修案がその過剰さで身動きを取れなくしていたこと
- 模擬公演の記録(検証のポイント):
- Beforeが券種によって記録のタイミングが非対称であること(VIP券が照合失敗でもカウントされ続けること)、Afterがどちらの券種も確認後にのみ・かつ確実に1件ずつ記録されること、定員到達で種別を問わず正しく締め出すこと、リトライ中は記録が増えないこと、不正な券種をPydanticが撥ねること
- マエストロの言葉:
- 「揃える相手のいない足並みを、揃えようとするな」
