工房の奥、これまでこの手で調整してきた肖像画がいくつも額装されて並ぶ一角で、私は道具箱の中身を整理していました。手はそちらに向いているのに、視線はつい、飾られた絵の一枚一枚に吸い寄せられます。この絵は、どの一筆で「もう仕上がった」と決まったのだろう——特に理由もなく、そんなことを考えている静かな午後でした。
けれどその日はまだ知りませんでした。「もう十分」というたった一言が、これほど遠いものになるとは。
第1幕: 予兆と調律 ── 終わりの見えない検分
工房の入り口で人影が一度立ち止まり、それから小走りに近づいてきました。両腕に、大きな画布と、真鍮の腕を持つ人形を大事そうに抱えています。声を張るタイプではないようで、その代わりに、画布の角を握りしめた指先の白さが、抱えている焦りをそのまま物語っていました。息を切らしているのに、歩みだけは崩さないよう気をつけている——そんな不釣り合いな慎重さが、かえって余裕のなさを浮き立たせていました。
「あの……コレットさん、で合っていますか。噂を聞いて、藁にもすがる思いで参りました」
見習いの肖像画家だと名乗った彼女は、リゼットと言いました。私と同じくらいの年頃で、控えめな物腰の中に、職人らしい張り詰めた気配があります。椅子を勧めても、画布を抱えたまま座ろうとしません。手短に、けれど言葉を選びながら事情を話してくれました。師匠のもとを長く継いで、正式に工房を任されるための卒業制作として、目利きの後援者から肖像画を託されていること。仕上げの検分は人形に手伝わせなさいと、師匠が貸してくれたのがこの人形だということ。そして——
「この子、いつまで経っても、描き終わらないんです」
抱えていた人形をそっと作業台に下ろしながら、リゼットは画布に目を落とします。真鍮の細い腕に貂の筆を握った機構が、静かに佇んでいました。もう片方には、片眼に嵌める真鍮の隻眼鏡が下がっています。
「もう長いこと、こうなんです。直したはずの箇所を、また直して……」
私は人形に、目の前でもう一度検分をさせてみることにしました。片眼に嵌まった真鍮の隻眼鏡が、小さな歯車の音を立てながらすっと下り、画布を覗き込みます。しばらく考え込むような間があって、それから流れるような講評を口にしました。好意的とも辛口ともつかない、微妙な言い回しです。言い終わるが早いか、細い真鍮の腕が貂の筆を持ち上げ、また同じ箇所に細かく筆を入れ直します。指の動きだけを見れば、迷いのかけらもない、確かな手つきでした。
「これが、ずっと続いているんです。私にはもう、何が正しいのか分からなくなってしまって……」
その一言に、私はふと引っかかりを覚えました。「分からなくなった、というのは?」
「師匠なら、講評を一つ聞いただけで『もういい、そこで置け』って言えるんです。私には、まだその勘が……」リゼットは声を落とします。膝の上で組んだ両手に、また力がこもるのが見えました。「師匠は今、長患いで臥せっていて。判断を仰げる人が、今はいないんです。この一枚に、私一人の判断で……本当にいいのか、分からないまま」
なるほど、と私は思いました。この人形には、そもそも自分で「終わり」を決める仕組みが備わっていなかったのです。いつもは師匠が講評を耳で聞いて、頃合いを見て手を止めさせていた——人形の側は、ただ描いて、ただ検分するだけの役目しか担っていませんでした。
検分の様子をもっと近くで見ようと、私は画布の縁にそっと指を這わせます。指先に、わずかな引っかかりを感じました。依頼主の襟元に描かれた飾り石のあたりだけ、絵の具が幾重にも重なって、ほんの少し盛り上がっています。数えきれないほどの描き直しの跡が、そこに積もっているのでした。
「厚塗りの……」
思わず声が漏れました。以来、私はこの人形をそう呼ぶことにしています。
工房の隅では、マエストロがいつも通り道具の手入れをしています。今日は特に近づいてくることもなく、手を止めることもなく、ちらとこちらへ視線をやっただけでした。いつもと変わらない、静かな見守りです。
第2幕: 絡糸と破綻 ── たった一言に、賭けてしまう
「師匠の勘を、人形自身にやらせればいいのね」
思いついたことは、そう難しいことのようには思えませんでした。隻眼鏡の講評の中に「もう十分」という一言が含まれていたら、そこで筆を置かせればいい。人間の判断を機械に肩代わりさせるだけの、力任せの指示でも複雑な条件分岐でもない、筋の通った思いつきのつもりでした。人形一体一体の判断は、コードとして書き起こせます。私はさっそく手を動かしました。
| |
試しに一度動かしてみると、思いのほかあっさりと止まりました。「動いてる……」——いつもの口癖が、口をついて出かけます。
けれど、言い終わる前でした。画布の縁に触れていた指先が、さっきよりもはっきりとした段差を感じ取ります。見ると、あの盛り上がりが、目に見えて大きくなっていました。たった一度の首尾よい結果に浮かれかけていた自分に気づいて、私は言葉を飲み込み、黙って画布を見つめます。
「その箇所、さっきより……」
リゼットも気づいたようでした。私たちは顔を見合わせます。今のはたまたま「もう十分」という言葉がそのまま出てきただけかもしれない——そんな不安が、胸の奥をかすめました。
それでも、私はまだ楽観していました。本番同様、後援者に見せる前の最後の通し検分をやってみることにします。隻眼鏡が下り、講評を口にするたび、筆の腕がまたあの一点へ戻っていきます。「大変よく描けています」「これで結構だと思います」「申し分のない出来です」——どの言い回しも、内容としては明らかに好意的でした。けれど、どれ一つとして「もう十分」という一言そのものとは一致しません。筆の腕は律儀に、何度も何度も同じ場所へ筆を重ねていきます。
五度、六度と検分が繰り返されるうち、リゼットの顔から少しずつ表情が消えていくのが分かりました。私は自分のノートに書き足した判定の一行を、何度も目でなぞります。まさか、と思う気持ちと、まだ大丈夫、と思う気持ちが、交互に押し寄せてきました。工房の隅で、マエストロが手を止めることなく、けれど心なしかこちらへ体を向けているのが視界の端に映ります。それでも、私は自分から声をかけには行きませんでした。
不意に、乾いた小さな音がしました。
重なりすぎた絵の具の層が、筆先の重みに耐えかね、薄く欠け落ちたのです。覗いた下地の色に、リゼットの顔から血の気が引きます。
「これ……もう、直せない……」
声にならない声でそう呟き、リゼットは両手で口元を覆いました。私は筆の腕を止めようと手を伸ばしますが、触れた瞬間に伝わってくる硬い抵抗に、無理に動かせば人形自体を壊しかねないと悟り、すぐに手を離します。何をどうすればいいのか分からず、私たちは画布の前で立ち尽くすことしかできませんでした。
第3幕: 示唆と編み直し ── 言葉でなく、型を見る
通りかかったアンが、傷ついた画布と、私の手元のノートを一瞥しました。
「あなた、その子が"同じ一言"を言ってくれるのを、ただ待ち続けるつもり?言葉を足しても引いても、当てが外れるのは変わらないわ。決めるべきは"何と言うか"じゃない、“何を満たしたら終わりか"のはずでしょう」
言い返せませんでした。反射的に、いつものようにマエストロの方を見てしまいます。彼は工房の隅で、布を持つ手を止めていませんでした。助けを求めそうになった自分にふと気づいて、私は視線を戻します——今回は、アンの言葉だけを頼りに、自分の考えを組み立て直そうと思いました。
マエストロは道具を磨く手を止めないまま、こちらを見ずに一言だけこぼします。
「……型、か」
それだけでした。すぐにまた、手元の作業に戻っていきます。答えを示すわけでも、詳しく説明してくれるわけでもありません。いつもならここで「動いてる」の一言が口をつくところですが、今回は何も出てきませんでした。ノートを見つめたまま、私はしばらく黙り込みます。静かな時間の中で、考えが少しずつ形になっていきました。
隻眼鏡の言葉そのものを当てにいくのは、もうやめよう。言葉を増やすのでも、待つのでもなく、隻眼鏡自身に「合格かどうか」を、型で答えさせればいい。
Structured Reflection——評価結果を、定性的な自然文でなく、型として構造化する設計を、こう呼びます。そして、ループを終了させる、機械的に検証可能な条件のことを、**停止条件(Stopping Condition)**と呼びます。私が組み直すべきだったのは、講評の言い回しを追いかけることではなく、この二つでした。
Pydanticを使えば、隻眼鏡の答えを「型」として縛ることができます。response_formatにその型を渡すと、思考のゼンマイ(LLM API)は出力そのものをその形に沿うよう制約されます。返ってきた結果を、今度はPydanticがもう一度検証する——型を求める指示と、型を確かめる検証との二段構えだからこそ、講評の言い回しがどれほど揺れても、プログラムが受け取る値の「形」だけは崩れません。
| |
scoreは0.0から1.0までの範囲に、passedはブール値に、それぞれ型で縛られています。隻眼鏡がどんな言い回しで講評を語ろうと、最終的にプログラムが目にするのは、この四つのフィールドだけです。隻眼鏡が実際に何を基準に検分しているのか——輪郭線の精度、陰影の一貫性、配色の均衡、依頼主への忠実度、といった見どころ——は、検分を求める一言の中に委ねたままです。型が保証してくれるのは、判定の「結果」がこの決まった形で返ってくることだけであり、その採点基準そのものの妥当性までは、型の外側の話なのだと、私は自分に言い聞かせました。
そう自分に言い聞かせながらも、目は書いたばかりのCriticVerdictという文字から離れませんでした。score、passed、failed_items、reason――四角い罫線に区切られた、たった四つの欄です。耳の奥には、まだあの日の講評が残っています。「大変よく描けています」「これで結構だと思います」「申し分のない出来です」。どれも間違いなく、合格を告げる言葉でした。けれど「もう十分」という並びは、その響きのどこにも、一度も現れなかった。同じ「合格」のはずなのに、片方は言葉の川に紛れて流れ去り、もう片方は、四つの欄の中に、指で示せる形をして収まっています。

あの言葉たちは、隻眼鏡が次に口を開いた瞬間、もう二度と同じ形では戻ってきません。けれどscoreという数字とpassedという真偽値は、何度呼び出しても、いつも同じ欄に、比べられる形で収まって出てきます。型が変えたのは、講評の中身の良し悪しではありません。その良し悪しを、私が確かめられる形にしたこと、それだけでした。
筆の腕(paint)の実装は、Beforeから一字一句変えていません。変わったのは、検分の隻眼鏡(critique)が、講評を自然文でなくCriticVerdictという型で返すようになったことだけです。
| |
なぜ、こう書き直すだけで話が変わるのでしょうか。順を追って考えてみます。
そもそも、なぜ"もう十分" in feedbackという一行は、事実上ループを終わらせられなかったのでしょうか。隻眼鏡は、思考のゼンマイ(LLM)の出力です。呼び出すたびに、微妙に違う言い回しで講評を語ります。「もう十分」という厳密な文字列が毎回そのまま現れることは、まず期待できません。たとえ講評の中身がどれほど明確な合格を意味していても、狙った一言一句がその中に現れない限り、機械的には「不合格」として扱われ続けてしまいます。
では、一致すべき言葉をもっと増やせば足りたのでしょうか。答えは否です。言い回しの候補を増やせば、偶然一致する確率はいくらか上がるかもしれません。けれど、判定の土台が「文字列が一致するかどうか」である限り、確率が上がるだけで、一致しない場合の脆さそのものは消えません。実際、あの日の私は「もう十分」というたった一つの言い回しにさえ、一度は偶然救われ、次には裏切られました。運のいい一致は、狙って再現できる挙動ではないのです。
だからこそ、隻眼鏡の判定結果をCriticVerdictという型で受け取ることに意味があります。「合格かどうか」を隻眼鏡の言い回しに依存せず、プログラムが機械的に比較できる値として扱えるようになるからです。verdict.passed or verdict.score >= score_threshold——この一行は、「合格」に至る二つの独立した道を表しています。隻眼鏡がはっきりと合格を示す場合と、点数が閾値を超える場合。どちらか一方で十分であり、両方を待つ必要はありません。
passedは隻眼鏡の総合判断そのものであり、これもまた一種の「言い分」には違いありません。それでも、Beforeの脆さとは性質が違います。Beforeは、内容がどれほど良くても、狙った文字列が一致しない限り機械的に「不合格」として扱われ続ける仕組みでした。Afterでは、たとえ隻眼鏡の総合判断に多少のブレがあっても、scoreという数値の物差しが、もう一つの独立した合格ルートとして控えています。どちらか一方の物差しが緩んでも、もう一方が拾える——passedとscoreを「か」で結んだのは、そういう理由からでした。
加えて、max_iterationsという反復上限を設けたことにも理由があります。たとえルーブリックの設計そのものに見落としがあっても、無限に描き直し続けることだけは、型システムの外側からでも防ぎたかったのです。上限に達してなお基準を満たせない場合は、ReflectionExhaustedという例外を送出し、そこで正直に立ち止まります。黙って描きかけのまま返してしまえば、基準に届いていないことにリゼットが気づかないまま、後援者の前に出てしまうかもしれません。だからこそ、続けるか諦めるかの判断そのものを、呼び出す側に押し返すことにしたのです。
ただし、正直に言わなければならないことがあります。型が保証するのは「ループが必ず終わること」であって、「隻眼鏡の採点そのものが正しいこと」ではありません。ルーブリックの基準が甘すぎれば早すぎる合格を、厳しすぎれば毎回上限まで描き直してから諦めることになります——型は、その中身までは面倒を見てくれないのです。
言葉だけを目で追っていると、何が変わったのか、かえって見失いそうになります。私は紙の端に、二つの仕立てを並べて整理し直してみることにしました。
Beforeの仕立てには、「含まれない」場合の行き先が一つしかありません。何度巡っても、律儀に描き直しへ舞い戻るだけです。反復の回数を数える者も、見張る者も、どこにもいませんでした。
Afterの仕立てには、「型で判定」という分岐が新しく加わり、さらにその先に「反復回数」という、もう一段の見張りが立っています。合格への道は二つに増えましたが、諦めるべき瞬間もまた、初めてはっきりと定義されました。
机上の小さな試作の隻眼鏡で、まずはBeforeがどう壊れていたのかを確かめ直すことにしました。
| |
_LoopGuardTrippedは、製品コードのどこにも存在しない、テストだけの安全装置です。Beforeのrefine_until_satisfiedは自力で途中停止する仕組みを持たないため、意味的には明確な合格を示す講評が20回続いても、一度も終了しませんでした。だからこそ、テストの側で強制的に打ち切って、その事実を証明する必要があったのです。
対して、たまたま「もう十分」という言葉がそのまま出てきた場合には、Beforeも呆気なく終了します。
| |
二つを並べると、Beforeの終了が内容の良し悪しでなく、言葉の巡り合わせに左右されていたことが、数字の上でもはっきりと分かります。
Afterの側は、二つの独立した道のどちらでも、正しく終わることを確かめます。一つは、点数が閾値へ届くまで描き直しを重ね、直前の指摘がきちんと次の指示に織り込まれていく道です。
| |
一回目の指摘「輪郭の粗さ」は二回目の指示にだけ現れ、二回目の指摘「陰影の不足」は三回目の指示にだけ現れる——過去の指摘が未来へ正しく橋渡しされ、まだ起きていない検分の結果が紛れ込むこともない。この順序も併せて確かめておきました。
もう一つの道は、点数が閾値に届かなくても、隻眼鏡が明確に「合格」を示せば、そこで即座に終わる場合です。実際、passed=Trueだがscore=0.6という検分結果を一件だけ用意して呼び出すと、たった一回で終了しました。二つの道が、それぞれ独立して機能していることの証明です。
反対に、何度描き直しても基準に届かない意地の悪い設定では、max_iterationsちょうどの回数でReflectionExhaustedが送出され、それ以上は一度も試みられません。最後に、検分結果にscore=1.5のような、あり得ない数字を紛れ込ませると、Pydanticがきちんと撥ねつけることも確かめました。
静かな確認で幕を閉じます。試作の隻眼鏡が、短いやり取りの末に静かに動きを止めました。私は小さく息をつき、本物の人形の前に戻ります。
第4幕: 開演と調和 ── 筆が、自ら置かれる
新しい仕立てで、本物の検分をやり直します。隻眼鏡が下り、CriticVerdictを返すたび、私の手元の記録には、はっきりとした数字と項目が並んでいきました。一度目、二度目と、狙いを定めた描き直しが続きます。けれど、あの欠けた一点には、もう一度も筆が触れませんでした。傷はもう増えないのだと、私は静かに確信していきます。
三度目の検分で、隻眼鏡の答えが基準を超えました。筆の腕がすっと持ち上がり、そのまま静かに——まるで自分の意志であるかのように——傍らの筆置きへと下ろされます。それ以上、何も描こうとはしませんでした。
リゼットが画布に駆け寄り、覗き込みます。欠けた一点にも、当然気づいたはずです。指先がその傷にそっと触れ、一瞬だけ止まりました。私は思わず身構えます——また塗り直させてほしいと、言い出すのではないかと。
けれど彼女は、それを隠そうとも、描き足させようともしませんでした。しばらく画布を見つめたあと、ふっと肩の力を抜いて、小さく笑います。
「……この傷も、含めて。これが今の、精一杯です」
その声には、迷いよりも、静かな納得が滲んでいるように、私には聞こえました。師匠の勘には、まだ届かないかもしれません。それでも、この一枚を「もう十分」だと決めたのは、他でもない彼女自身の言葉でした。
「上出来じゃない」
通りかかったアンが、軽く言い捨てます。
「言葉を数えるより先に、型を疑うべきだったのよ——次はもっと早く気づくことね」
マエストロは道具の手入れを続けたまま、こちらを見ずに、短く言いました。
「……筆は、置けるところで置くものだ」
それだけ言うと、また静かに手元の作業へ戻っていきます。技術的な答えを口にしたわけではありません。けれど、なぜかその一言は、画布に残ったあの小さな傷のことも、同時に語っているような気がしました。
リゼットは画布と人形を丁寧にまとめながら、初めて私の名を呼びました。
「コレットさん、本当に、ありがとうございました」
同業の見習い同士の、対等な呼びかけでした。
隻眼鏡の言葉を当てにいくのではなく、型で「もう十分」を言い切る——それが今日の答えでした。そう自分の中で結論を確かめたとき、ふと気づきます。あの一連の出来事のあいだ、私は「動いてるからいいのよね」を、一度も口にしていなかったのだと。
部屋に戻り、今夜の出来事をひと息に書き終えます。いつもなら書いたものを見返して、言葉を直したり書き足したりするところですが、今回はペンを置いた手で、そのまま静かにノートを閉じました。書き終えたものを、もう見返す必要はないのだと、初めて素直にそう思えたのです。
🎭 操り人形師の覚書(Colette’s Puppeteer Log)
- 調整したからくり(モジュール/関数名):
PortraitPuppet.critiqueとそれを束ねる制御ループ(Before:refine_until_satisfiedが自然文の部分文字列一致で終了判定 → After:refine_until_passedがCriticVerdict〔Pydantic、score/passed/failed_items/reason〕の型判定+max_iterationsのOR条件で終了判定。尽きればReflectionExhausted) - 思考のゼンマイ(思考素子/LLMの特性):
- 検分の隻眼鏡(Critic)の講評は、同じ内容を意味していても、呼び出すたびに言い回しが変わる自然言語であり、二度と同じ文字列が現れるとは限らない
- 操り糸の張り具合(運糸の法/プロンプトと制御):
- Pydantic
CriticVerdict(score・passed・failed_items・reason)+verdict.passed or verdict.score >= score_thresholdのOR判定 +max_iterations上限に達した場合のReflectionExhausted
- Pydantic
- 舞台裏の絡まり(背伸びの過ち/避けるべき罠):
- 人間の判断(頃合いを見て打ち切る)を、たった一つの言葉の一致に賭けて機械へ肩代わりさせようとしたこと(効果が偶然任せなのは変わらない)と、それによって代わりのきかない画布を後戻りできない形で傷めたこと
- 模擬公演の記録(検証のポイント):
- Beforeが内容的に明確な合格でも文字列が一致せず終了しないこと(かつ偶然の一致でしか終了しないこと)、Afterがスコア閾値到達・合格フラグ・反復上限のいずれの経路でも正しく終了すること、直前の指摘が次の指示に反映されること、型が不正な値を拒むこと
- マエストロの言葉:
- 「……筆は、置けるところで置くものだ」
