「AIが終わったら呼びに来てくれる秘書」を販売できる形にするまで
令和八年葉月五日-葉月二十二日
三か月前、Codexで作った秘書はダメダメだった。
かわいいキャラクターはデスクトップにいる。AIが働けば動く。終われば知らせる。少なくとも、そういう予定だった。
ところが実際には、AIが止まっているのに秘書だけ元気に働き続けたり、逆にAIが働いている途中で昼寝を始めたりする。
「あと5分だよ!」
突然そんなことも言う。
何の5分だ。俺は何も設定していない。
これは、そんな秘書を「俺のパソコンでは動く」試作品から、他人へ渡せる商品にするまでの話だ。
そもそも、なぜAIに秘書が必要なのか
AIへ仕事を任せると、人間の仕事は少し変わる。
コードを書かせる。調査を頼む。ファイルを整理させる。長い仕事なら、その間に別のことができる。
だが、本当に別のことを始めると、今度はAIの様子が気になる。
まだ動いているのか。もう終わったのか。エラーで止まったのか。返事を待っているだけなのか。
結局、何度もAIの画面を開いて確かめる。それでは、仕事を任せた意味が半分なくなる。
だったら、その確認を別の存在に任せればいい。
AIが働いている間は、デスクトップのキャラクターも仕事をする。終わったら音と通知で呼びに来る。長時間返事がなければ心配して教える。
それがSecretaryPetの始まりだった。
Codexのペットを、AIを見張る秘書へ改良したものだ。
自分のパソコンで動く。だから危ない
販売版を作ろうと思ったとき、最初に決めたことがある。
普段の開発環境で試すだけでは信用しない。
開発用とは別のWindowsアカウントを作り、購入者と同じようにZIPを受け取る。展開して、説明書を読み、最初からインストールする。
開発環境には、成功する理由が多すぎる。
Pythonはすでに入っている。必要なライブラリもある。昔の設定ファイルが残っている。開発中に作った何かが、知らないうちに不足を補っている。
きれいな環境へ持っていけば、本当の商品だけが試される。
ただし、Windowsアカウントを切り替えると、向こうのCodexにはこちらのチャットの記憶がない。
そこで共有フォルダを伝令所にした。
開発側のCodexは販売ZIPと報告書を置く。監査側のCodexはそれを受け取り、展開し、不具合を報告書へ書く。開発側は報告を読み、コードを直し、また新しいZIPを置く。
AI同士は互いを覚えていなくても、ファイルがあれば引き継げる。
俺が毎回すべてを説明し直さなくても、二人のCodexが報告書を介して会話できる形にした。
未来っぽい話だが、やっていることは回覧板である。
最初の問題。「install」が二つある
販売ZIPを開くと、インストール用ファイルが二つあった。
片方はcmd。もう片方はPowerShell。開発者なら役割の違いが分かる。
購入者には、どちらも「install」に見える。
しかもWindowsは、初期設定では拡張子を隠すことがある。
説明書には「installを開いてください」と書いてある。
どっちだよ。
これは説明書を長くして解決する問題ではなかった。
購入者が触る入口を一つにした。
名前はSTART_HEREでもsetupでもない。
「ここからインストール」
これなら迷わない。
説明書も短い日本語へ書き換えた。購入者が使わない補助ファイルは販売ZIPから外した。重複していた画像や、もう使っていない古いアニメーション制御も削った。
ZIPは約3.26MBから約1.78MBになった。
容量より大事だったのは、「商品に必要なもの」と「開発中の残骸」を分けられたことだった。
Pythonが入っていなかった
別アカウントでインストールすると、いきなり失敗した。
Pythonが入っていない。
当然といえば当然である。普通のWindows利用者が、全員Pythonを入れているわけがない。
商品ページへ「Python 3.10以上が必要です」と書くことはできる。
だが、それでは購入者がPython公式サイトを探し、自分の環境に合うものを選び、インストールし、場合によってはPATHまで理解しなければならない。
500円の商品を動かすために、購入者へPython講座を受けさせるのは違う。
ならば、入っていなければこちらで入れればいい。
インストーラーがPython 3.12を自動導入するようにした。まずWindowsのwingetを使う。使えない環境ではPython公式のインストーラーを取得する。ただし、インターネットから取ったexeを何も確認せず起動するのは怖いので、電子署名と署名者を檢査してから実行する。
必要なPyQt6とpsutilも自動で入れる。
購入者は「ここからインストール」を押すだけ。
これで終わったと思った。
もちろん終わらなかった。
Pythonに見えるが、Pythonではない
Python未導入のWindowsでも、python.exeが見つかる場合がある。
それなら入っているじゃないかと思う。
入っていない。
それはMicrosoft Storeへ案内するための「アプリ実行エイリアス」だった。見た目はpython.exeだが、Python本体ではない。
最初のインストーラーは、ファイルが見つかればPythonだと信じていた。その偽物を使おうとして、当然失敗した。
そこで、ファイル名だけでは信用しないことにした。
候補を実際に起動する。終了コードを確認する。本物のPythonとしてコードを実行できることを確かめる。WindowsAppsのエイリアスは最初から除外する。
人間ならアイコンや場所を見て何となく判断できる。自動インストーラーは、判断条件をすべてコードにしなければならない。
本体はいない。でもLauncherだけいる
さらに変な環境が見つかった。
Python本体をアンインストールしたのに、Python Launcherのpy.exeだけが残っている。
インストーラーはLauncherへ「Python 3はどこ?」と聞いた。
Launcherは答えた。
No installed Python found!
終了コード112。
それ自体は正しい。使えるPythonがないなら、自動インストールへ進めばいい。
ところがPowerShellは、この返事を単なる「候補なし」ではなく例外として扱った。インストーラー全体が止まり、Pythonを入れる処理へ進まない。
Launcherの確認だけを別処理へ隔離し、終了コードが0でなければ「Python本体なし」と判断するようにした。
今度こそ自動導入へ進む。
進まなかった。
次は、一時ファイルを消す処理で止まった。
標準出力用と標準エラー用の一時ファイルをまとめて削除していたが、ある経路では片方が空だった。「何もない場所を削除しろ」と命令されたPowerShellが怒り、また全体が止まった。
各ファイルについて、値があるか、実在するかを確認してから削除するようにした。
大げさなアルゴリズムではない。
だが、こんな一行が購入者のインストールを全部止める。
最終的には、Python本体なし、Store用の偽python.exeあり、ランタイムのないLauncherだけあり、という面倒な環境から、Python 3.12の導入、必要ライブラリの導入、ショートカット作成、秘書の起動まで完走した。
念のため同じ環境をもう一度作り、購入者と同じ操作でもう一度成功させた。
「設定してください」は敗北である
秘書はCodexの動きをログから見つけられた。
しかしClaude CodeとAntigravityは、AI側へhookを設定しなければ自動連携しない。
設定方法を説明書へ書くことはできる。
JSONファイルを開いて、このコードを貼り付けて、パスを自分の環境に合わせて……。
たぶん、多くの人がそこで使うのをやめる。
「購入者が自分で設定してください」は、今回の方針では敗北である。
インストーラーが既存設定を読み、壊さないように秘書用hookを追加することにした。変更前のバックアップも残す。同じインストールを何度実行しても重複しないようにする。
Claude Codeには、開始、ユーザー入力、ツール使用後、応答停止、セッション終了のhookを入れる。
Antigravityにも専用hookを入れる。
設定ファイルの生成テストは通った。
手動実行も通った。
そして実Antigravityでは、全部失敗した。
Antigravityと引用符の戦い
最初のAntigravity用コマンドは、引用符で囲んだPythonの絶対パスから始まっていた。
Windowsでは、ごく普通の書き方だ。
だがAntigravityのhook runnerは、先頭の引用符を普通には扱わなかった。引用符を含んだ実行ファイル名だと解釈し、「そんなコマンドはない」と失敗した。
そこで、必ず存在するpowershell.exeから始めるラッパーを作った。Pythonやスクリプトのパスは後ろの引数へ移した。
通常のcmdから実行すると成功した。
Antigravityを完全終了して起動し直し、実際に作業させると失敗した。
今度はPowerShellの-Fileへ渡した引用符が、パスを囲む記号ではなく、パスの一部として渡っていた。
しかも、販売物の日本語フォルダ名がAntigravityのログ上で文字化けしている。
引用符もダメ。日本語パスも危ない。
だったら、どちらもAntigravityへ渡さなければいい。
連携に必要なファイルを、ユーザー領域のASCIIだけで扱える場所へコピーした。Antigravityからは、引用符も日本語も含まない短いcmdだけを呼ぶ。Python本体や元アプリの場所は、別のUTF-8ファイルへ保存してランチャー側で読む。
ようやく実Antigravityからhookが動いた。
ログが更新された。セッションファイルができた。デスクトップの秘書が「Antigravity 作業中」と表示した。
設定テストが通ることと、本物のアプリから動くことは別だった。
この頃には、Antigravityが動くだけで少し感動するようになっていた。
Codexは止まった。秘書は止まらない
連携が成功したあと、ユーザーから言われた。
「Codexが止まったのに、秘書が動いている」
また判定がおかしい。
ログを調べると、Codexはきちんと終了していた。
動き続けていた原因はAntigravityだった。
Antigravityの開始hookは成功したが、終了hookは以前の引用符問題で失敗していた。そのため、作業中の記録だけが残っていた。
秘書には、古い作業中記録を約10分で無効にする保険がある。つまり壊れたまま永遠に動くわけではない。
だが、終了後に10分も働き続ける秘書は信用できない。
まず、Antigravity本体が閉じているのに作業記録だけが残っていたら、60秒後に消すようにした。
これで解決したと思った。
監査側のCodexから、新しい報告が届いた。
「Antigravityアプリを開いたままタスクだけ終了すると、まだ残ります」
確かにそうだ。
普通は、仕事が一つ終わるたびにアプリまで終了しない。
名前のない仕事が残っていた
実際のhookデータを追うと、妙な違いがあった。
作業前のイベントには会話IDがある。
作業終了のイベントにも会話IDがある。
ところが、途中の一部イベントだけ会話IDがない。
秘書はIDがないイベントを捨てず、defaultという仮の仕事として記録していた。
終了時には本来の会話IDだけが終了する。名前のないdefaultは取り残される。
幽霊社員である。
最後の修正では、Antigravityの会話ID付き終了通知を受けたとき、存在しているdefaultも一緒に終了するようにした。
CodexとClaude Codeには触れない。defaultがなければ何もしない。
テストでは、監査で実際に起きた順番を再現した。
- ID付きで作業開始
- IDなしで途中経過
- ID付きで作業終了
- 名前のない仕事も終了しているか確認
実際のASCIIランチャーを通す経路でも試した。
やっと、Antigravityを開いたまま仕事だけ終えても、秘書が一緒に仕事を終えられる形になった。
長く働くCodexを勝手に帰らせない
逆の問題もあった。
Codexが長い仕事をしていると、秘書が途中で待機状態へ戻る可能性があった。
原因はログの読み方だった。
大きなログの末尾だけを読んでいたため、長いターンでは「仕事を始めた」という記録が読み取り範囲の外へ押し出される。終わった記録がないのに、始まった証拠も見えなくなる。
ログを増分で読む方式へ変えた。
初回は全体を読み、次からは増えた部分だけを読む。最後に見た開始と終了を覚えておく。末尾に改行なしで書かれた終了記録も、完成したJSONならその場で処理する。
監査では約5分間、15秒ごとに状態を確認した。20回すべてで「Codex 作業中」が維持された。
止まっているAIを働いていることにしない。
働いているAIを勝手に帰らせない。
この二つを同時に守るのが、思ったより難しい。
秘書が仕事を邪魔してはいけない
機能以外にも、使って初めて分かる問題があった。
キャラクターをドラッグすると吹き出しが消える。吹き出しをクリックしたらウィンドウが動く。画像や文字の部品がマウス操作を横取りする。
そこで、押した場所と移動距離を記録し、一定以上動いたらドラッグ、動かなければクリックとして扱うようにした。
吹き出しの単純クリックだけを非表示にする。ドラッグならキャラクター移動を優先する。
完了通知にも問題があった。
絶対に見逃さないよう画面中央へ確認ダイアログを出していたが、操作を止めるモーダル通知だった。
秘書は知らせるためにいる。仕事を止めるためではない。
通知を非モーダルにし、8秒で自動的に閉じるようにした。音は鳴る。画面にも出る。しかし、ユーザーの操作は奪わない。
かわいいだけでなく、邪魔にならないことも秘書の仕事である。
「あと5分だよ!」
販売版を動かしていると、待機している秘書が言った。
「あと5分だよ!」
何の5分だ。
締切機能はない。タイマーも設定していない。AIの終了予定を予測しているわけでもない。
原因は、待機中にも使われる普通の台詞集へ、この言葉を入れていたことだった。
妹も言う。
ツンデレ幼馴染も言う。
お母さんも、執事も、暴君も言う。
全員が根拠なく残り5分を宣告してくる。
全役柄から削除した。
代わりに「何かあったら呼んでね」という意味の、待機中でも不自然ではない言葉へ変えた。
台詞は飾りに見えるが、根拠のないことを言う秘書は信用できない。
状態判定と同じくらい、発言も正直であるべきだった。
失敗するたび、監査が賢くなった
修正のたびに、販売ZIPを作り直した。
ZIPは自動的に別の場所へ展開される。必要なファイルがあるか、JSONが壊れていないか、Pythonコードに構文エラーがないか、古い不要ファイルが戻っていないかを檢査する。SHA256も生成する。
共有フォルダへ置かれたZIPは、監査側で再びハッシュ確認、展開、インストール、GUIスモークテストを受ける。結果はJSONと報告書へ残る。
問題が起きるたび、その再現条件を次のテストへ足した。
偽Python。
Launcherだけ残る環境。
一時ファイルが空になる経路。
長すぎるCodexターン。
改行なしの終了ログ。
AntigravityのASCIIランチャー。
IDなしの途中イベントと、IDありの終了イベント。
バグは嫌だが、一度見つかったバグを二度と同じ形で出さない仕組みは作れる。
販売版を直す作業は、同時に監査システムを育てる作業になった。
壹肆零版
こうして、SecretaryPet 壹肆零版ができた。
Windows 10 / 11に対応。
Claude Code、Codex、Antigravityの作業状態を見る。
作業が終われば音と通知で知らせる。エラーや長時間の無応答も知らせる。Windows起動時に自動で立ち上がる。
購入者はZIPを展開し、「ここからインストール」を押す。
PythonがなければPython 3.12も自動で入る。
AI本体は入っていない。使いたいAIは購入者自身で用意してもらう。
価格は今のところ500円。
バグが見つかったら直す。
便利になったら値上げする。
次はEXE化と多言語化
Pythonを自動導入できるところまでは来た。
それでも購入者から見れば、PythonもPowerShellも内部事情である。
次はEXE化する。
より普通のWindowsアプリとして導入できる形にしたい。インストール先、更新、アンインストール、コード署名、セキュリティ警告まで考える必要がある。
もう一つは多言語化。
説明書や通知を英語にするだけではない。
妹、ツンデレ幼馴染、お母さん、執事、暴君。同じ意味でも、全員の話し方が違う。直訳すればキャラクターが死ぬ。
機能の文章とキャラクターの台詞を分け、言語ごとに管理できる仕組みが必要になる。
長かった
入口の名前を変えるところから始まった。
そこから、Python自動導入、偽Python、空っぽのLauncher、nullの一時ファイル、古いhook、引用符、日本語パスの文字化け、名前のないAntigravityセッション、スリープ復帰、ドラッグ、通知、そして謎の「あと5分」まで直した。
一つ直すと、次の問題が出た。
それでも毎回、最初の方針へ戻った。
購入者の手をなるべく使わない。
動いていないのに、動いていると言わない。
分からないことを、分かったふりで話さない。
三か月前の秘書はダメダメだった。
今も完成形ではない。
だが、もう単なるデスクトップマスコットではない。
AIが働いている間、一緒に働く。
終わったら呼びに来る。
止まったら気づかせる。
SecretaryPetは、ようやく商品としての出発点に立った。
次は、もっと普通に使えて、もっと多くの人へ渡せる秘書にする。
