「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がなければ何もしない。

テストでは、監査で実際に起きた順番を再現した。

  1. ID付きで作業開始
  2. IDなしで途中経過
  3. ID付きで作業終了
  4. 名前のない仕事も終了しているか確認

実際の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は、ようやく商品としての出発点に立った。

次は、もっと普通に使えて、もっと多くの人へ渡せる秘書にする。