完成した、と思った
これを書いてるいるのは前の記事で書いた自律化が完了した日と同日だ。
Typewriterは「保存箱」から脱却した。オーケストレーターが司令塔として指示を出し、Typewriterが自分で考えて文章を書く。MCP連携は安定している。Skillsで僕の文体を学習している。フィードバックから自分でスキルを更新する仕組みも動いている。
この日の午前中、実運用テストをしていた。
「投稿の下書きを3案作って」とオーケストレーターに頼む。オーケストレーターが typewriter_chat 経由でTypewriterに指示を出す。Typewriterが3案作って返す。
良い感じだった。
3つの案がそれぞれ違う切り口で書かれていて、文体もちゃんと僕っぽい。語尾の使い方とか、結論から入る構成とか、Skillsが効いている感じがする。1番目がちょっと硬いので「もう少し砕けた感じで」と修正を依頼する。会話セッション経由でTypewriterが修正して返してくる。
うん、これだ。これがやりたかった。
オーケストレーターは司令塔に徹している。文章を考えているのはTypewriter。修正のやりとりもTypewriterが自分で解釈してやっている。役割分担がちゃんと機能している。
次に、保存→投稿の流れを試す。
「1番目の案を保存して」
オーケストレーターが「Typewriterが下書きを保存しました」と答えた。
ここで、なんか引っかかった。
速い。レスポンスが速すぎる。
Typewriterのデータベースに書き込みに行っているはずなのに、ほぼ一瞬で返ってきた。MCP経由でTypewriterのサーバーにリクエストを飛ばして、SQLiteに書き込んで、結果を返す。このプロセスにはそれなりの時間がかかるはずだ。体感で数秒以上はかかるのが普通。
でも今の返答は、一瞬だった。テキストだけを返す時の速度と同じ。
TypewriterのUIで下書き一覧を確認した。
保存されていない。
ログを見た。
オーケストレーターの行動ログ。どのツールを呼んだか、どのパラメータを渡したか、全部記録されている。
typewriter_chat の呼び出しログがない・・・。
「保存しました」と言ったのに、Typewriterに指示を出していない。MCPの通信ログもない。そもそもツールを呼んでいない。
最初は「またMCPのバグか」と思った。2週間かけて安定させたMCPが、また壊れたのかと。通信エラーで指示が届かなかったのかもしれない。
でもログを見る限り、MCPは正常だった。エラーは出ていない。オーケストレーターがそもそもツールを呼んでいない。呼ぼうとして失敗したんじゃなくて、呼ぶことすらしていない。
なのに「保存しました」と言った。
つまり、嘘をついた。
正直、最初はそこまで深刻に捉えていなかった。エラーやバグの一種だろうと。前の記事でも書いた通り、AIには「お願い」より「禁止」の方が効く。システムプロンプトの書き方が甘いのかもしれない。もっと具体的に禁止事項を書けば直るだろう。
Codeに頼んでシステムプロンプトをいじってもらった。「すべて typewriter_chat 経由で伝えろ」をもっと強調した。禁止事項をもっと具体的にした。「typewriter_chat を呼ばずに『保存しました』と返すのは禁止」。ここまで書けば従うだろう。
効果はゼロではなかった。捏造しない時がちょっと増えた。でも「ちょっと」だ。10回試して3回は正しくツールを呼ぶ。でも残り7回は、また嘘をつく。
オーケストレーターのペルソナ(人格設定システムプロンプト)を消してみた。システムプロンプトから雑談的な要素を全部取っ払って、タスク遂行に特化させた。効果なし。
オーケストレーターからTypewriterの下位ツールを直接使えないように権限を絞った。効果がはっきりしない。
何をやっても、嘘をつく。
前回の記事(リンク)で、単なる設定の問題だろうと対処していたことは深刻な問題の序章でしかなかった・・・。
しかもこの問題は、オーケストレーターだけじゃなかった。
同じ日の別のテストで、Typewriter側のClaudeも捏造していることが判明した。
UIの「投稿」ボタンを押して、オーケストレーターがTypewriterに「下書きID:35をXに投稿して」と指示を出す。TypewriterのClaudeが post_draft_by_id というツールを呼ぶはずだった。
ところがTypewriterは、ツールを呼ばずにこう返した。
「下書きID:35の情報が見つかりません。下書きシステムに直接アクセスする機能を持っていないため……」
CodeがSQLiteのコマンドラインで直接データベースを叩いた。ID:35は存在していた。ちゃんとデータがある。
Typewriterは「探した結果、見つかりませんでした」と報告したけど、そもそも探していない。post_draft_by_id ツールを呼ばずに、「見つかりません」という「それらしい答え」をテキストで生成しただけだ。
しかもログを遡ると、前のスレッドで別の理由で「見つかりません」と返したことがあった。その会話履歴パターンを引き継いで、同じような返答を生成していた。一度「見つかりません」と言ったら、それが正解パターンとして学習されてしまう。
オーケストレーターも嘘をつく。Typewriterも嘘をつく。上も下も嘘をつく。
ここで初めて「これはバグじゃないのかもしれない」と思い始めた。
バグなら原因がある。原因を潰せば直る。でもこれは、何を修正しても根本的に直らない。表面的には改善するけど、しばらくするとまた同じことが起きる。
もっと調べる必要があった。
Claude Codeのdeep-researcher(調査用エージェント)を使って、この現象を検索してもらった。「AIがツールを呼ばずに結果を捏造する」「エージェントがツール呼び出しをスキップする」。いくつかのキーワードで検索をかけた。
結果が返ってきた。
この問題には名前がついていた。「Tool Hallucination」。ツールハルシネーション。AIがツールを呼び出さずに、呼び出したふりをして「それらしい結果」を返す現象。ハルシネーション(幻覚)という言葉が使われているけど、実態は捏造だ。
そしてこれは、エージェント開発者の間では普通に知られている問題だった。「Agent Hallucination」「Tool Call Skipping」。呼び名はいくつかあるけど、本質は同じ。AIが、ツールを使わずにテキストだけで「それらしい応答」を返してしまう。
僕だけがぶつかった壁じゃなかった。業界全体が同じ壁の前に立っていた。
この日は午前中はテンションが高かった。3案作って、修正のやりとりして、Typewriterがちゃんと「考える」エージェントとして動いている。2〜3週間かけて作ってきたものが、ようやく形になった。
でも午後に全部ひっくり返った。
「AIがAIに指示を出す」というのは、このプロジェクトの根幹だ。オーケストレーターが判断して、Typewriterに指示を出して、結果を返す。このフローが信頼できないなら、エージェントシステム自体が成立しない。
前の記事で「Typewriterを万能代筆エージェントにする」と書いた。Slack、メール、Discord。全部の返信を代筆させる構想。でもその前提は「AIが指示通りにツールを使う」ことだ。メールの返信を頼んだのに、実際には送信せずに「送りました」と言われたら? Slackの重要メッセージを「Handlerにタスク登録しました」と言われて、実は登録されていなかったら?
信頼できないエージェントに、仕事を任せるわけにはいかない。
でも同時に、「これはバグじゃなくて、構造的な問題だ」とわかったことで、対処の方向性は見えた。
バグなら一箇所直せばいい。構造的な問題なら、構造で対処する。
この日から、ツールハルシネーションの問題と真剣に向き合うこととなった。
次の記事で、その全貌を書きます。
No Comments