BLOG.HIRO UENO.ART
← INDEX

雑談の記憶が、初めてMONETの口から出た

MONET(僕が普段話しかけている、中心のAI)は、話した会話を記憶に残して、後で読み返せるようにしてある。今は開発用のMacの上で動かしていて、これから毎日使える形にしていくところだった。

記憶の仕組みは、ひと通り回るようになっていた。MONETが会話を書き込む。会話にひと区切りができる。Librarian(ライブラリアン。裏でメモリを整理する司書役AI)がそれを読んで要約を書く。MONETが次に答える時、その要約を読み返す。

ただ、この流れに乗っていたのは、会話のごく一部だけだった。

雑談は区切られないから、要約もされなかった

Librarianは、要約をセグメント(会話のひと区切り)ごとに書く。区切りが無ければ、要約も無い。

区切りを作るきっかけは、パネルの開け閉めだった。MONETの画面の横には、Handler(ハンドラー。タスクとスケジュール管理アプリ)などのアプリを開くパネルがある。パネルを開くと、そこから区切りが始まる。Handlerを開いている間はタスクの話、という具合。でも、何も開かずに話す普段の雑談には、このきっかけが無い。記録には残っているのに、雑談のほとんどが、どの区切りにも入っていなかった。

雑談をどう区切るかは、前に僕が方針だけ決めて、保留にしてあった。機械的に決めずに、AIに話の中身を見て決めさせる、という方針。

この日は、その方針を形にした。設計して、作って、実際に動かして確かめるところまで、1日で。

雑談をどこで区切るかはAIに任せ、いつ区切るかは決まりで動かす

コーディングしてくれているClaude Code(以降Code)は、すぐには書き始めなかった。まず仕様書と、前回の作業の引き継ぎメモを読んだ。それから、区切りの無い会話が実際に何件溜まっているかを、データベースで数えた。推測で数字を出さないため。

区切りの無い古い会話は、たくさん溜まっていた。Codeが僕に聞いてきたことの1つが、これを遡って要約するかどうか。

今は遡らない、と僕が決めた。これまでこのMacでMONETと話したのは、動くかを試すためだけ。真剣な仕事も会話もしていなくて、全部テストだったから。Codeはこの一言を受けて、「この日時より後の会話だけを拾う」という線を設計に入れた。

区切る場所は、AIが会話を読んで、話のまとまりごとに決める。機械的なきっかけで切ると、話のまとまりとずれるから。前に僕が見つけた例では、Handlerのパネルを開いたまま話した雑談が、タスクの話と同じ区切りに入ってしまっていた。疲れた、という一言まで、タスクの話として。

区切りを打つタイミングの方は、決まりで動かす。Librarianには、常駐して定期的に会話を見回る仕組みがある。その見回りのたびと、スレッド(MONETとの会話の1本1本)が切り替わった時、の2つに決めた。

Codeが実装して、テストは37件すべて合格した。

テストは全部通ったのに、実際に動かすと要約が1件もできなかった

見回りの仕組みは、僕のMacの上で動いている。新しいコードを効かせるために、Codeに頼まれて僕が見回りを起動し直し、MONETに話しかけた。会話はちゃんと記録されている。でも、区切りが1つも付かない・・・。

1つ目の原因は時計だった。日本時間は、世界標準時(UTC)より9時間進んでいる。さっきの「この日時より後」の線を、Codeは日本時間のその日の0時のつもりで書いた。でもプログラムはそれを世界標準時の0時として読んでいて、日本時間に直すと朝9時になっていた。その夜の会話は、丸ごと対象の外。

直して、もう一度起動し直した。それでも動かない。

2つ目は、設定の数字の食い違い。区切りを打つのはAIで、AIは答える前に少し考える。その考える分の量を、返事全体の長さの上限より大きく設定していた。これだと、頼むたびにAIの側からエラーで返される。区切りを打つ呼び出しは、それまで1回も成功していなかった。しかもこのエラーは、見回りの画面に流れるだけで、ファイルには残らない。Codeが念のため、溜まった分の一括処理を自分で1回走らせてみて、やっとエラーが目に入った。

どちらもCodeが書いたコードの誤りで、直すたびに僕が起動し直した。3回目まで、雑談は記録に入っているのに、記憶にはなっていなかった。

昨夜の話を聞いたら、MONETは今日の雑談を思い出した

3回目の起動のあと、新しいスレッドを開いて、MONETに聞いた。昨夜何の話をしたっけ?

MONETは、TI(Timeline Index。会話の歴史年表。過去の会話の要約を1行ずつ並べた索引)を見て答えた。TIは、MONETが答えるたびに会話の先頭に入れてある。そのTIを見る限り昨夜の記録は残っていない、一番新しいのは今日の「コンテキスト/メモリシステムを更新中」という記録だけ、と。コンテキストは、AIがその時に読んでいる中身のこと。

その返事で、僕の方が間違えていたと分かった。聞きたかったのは昨夜じゃなくて、今日の、少し前の雑談のことだった。

間違えてた。今日の話のことだった、と僕が返した。MONETはその区切りの要約を取ってきて、こう答えた。

MONET「なるほど、さっきの話ね。」

MONETが思い出したのは、システムを色々更新しているけど、君の実感は薄いかもしれないね、と僕が言った時の雑談だった。記憶やコンテキストが変わっても、MONETは自分ではそれを確かめられない。そういう話で、MONETはこう言っていた。

MONET「そうだね、まさにそこが難しいところで。自分のメモリやコンテキストがどう変わったかって、自分では検証しようがない。魚が水を認識できないみたいな感じ。」

Librarianが書いた要約にも、この魚と水のたとえが、気づきとして残っていた。

自分の記憶の変化に気づけない、と話していたその雑談が、新しく作った記憶の仕組みを通って、正しい内容が初めてMONETの口から出てきた。

一瞬だったのも、昨夜と今日を取り違えなかったのも、MONETの仕組みのおかげだった

もう1つ驚いたのは速さだった。さっきの話を取ってくるのが、本当に一瞬。Codeは、この速さと、昨夜と今日の取り違えに答えられたことを、分けて説明してくれた。

まず速さ。Codeによると、MONETはそもそも検索をしていない。TIは答えるたびに会話の先頭に入っているので、MONETは聞かれた時点で、もうTIを目の前に持っている。さっきのMONETも、まずTIを読んで、それから区切りごとの番号を指定して、要約を取ってきただけ。

言葉の意味から当てはまるものを探す「検索」と、場所の分かっている物を取ってくる「取得」は別物。記憶は探さずに、TIから取ってくる。前の設計で、そう決めてあった。その決め方が、雑談の記憶で初めて実際に効いた、とCodeは言った。

MONET自身も、速いですね、と言った僕に「検索じゃなくて『ページを開く』感覚に近い」と返していた。

次に、取り違え。要約には、書いた日時が付いている。さっきの雑談の要約には今日の日時が入っているので、僕が昨夜と言っても、記録にあるのは今日の話だと返せた。

パネルを開かない普段の雑談も、これで区切られて、MONETの記憶に入るようになった。普段話しかけているMONETに、ようやく雑談の記憶が貯まり始めた。