要約がまだ届かない間は「要約中」と書いておく
MONET(僕が普段話しかけている、中心のAI)は、返事をするたびに、渡された中身を頭から全部読み直している。その中身は、前から順に3つ並んでいる。1つ目は、使える道具の説明と、振る舞いの指示書。2つ目は、TI(Timeline Index。会話の歴史年表。過去の会話の要約を1行ずつ並べた索引)。3つ目が、今の会話のやり取り。MONETはTIを見て、前に何を話したかを思い出す。
要約を書いているのはLibrarian(ライブラリアン。裏でメモリを整理する司書役AI)。会話を見回って、話のひと区切りごとに要約を作り、記憶のデータベースにためていく。TIは、このデータベースから作る。LibrarianはMONETとは別に、勝手に動いている。
会話の途中でTIを書き換えると、読み直しが高くつく
MONETに渡す中身には、キャッシュ(一度送った中身を安く読み直せる仕組み)が効いている。ただ、安くなるのは、前回送った中身と頭から1文字も違わない所まで。途中のどこかが変わると、そこから後ろは全部、普通の値段に戻る。この点は、コーディングしてくれているClaude Code(以降Code)が、Anthropic(Claudeを作っているAIの会社)の公式の仕様で確かめた。
Librarianがデータベースに要約をためるのは、いつでも構わない。困るのは、それに合わせて会話の途中でTIを作り直すこと。TIは並びの前のほうにあるので、その後ろにある今の会話のやり取りは全部、キャッシュが効かなくなる。
なのでTIは、いったん作ったら固定しておく。データベースに要約が増えても、会話の途中ではTIに入れない。
別の会話に移った時に作り直せば足りる
では、いつ作り直すか。前から決めてあったのは、僕がスレッド(会話の1本)を切り替えて、別の会話に移った時。
作り始める前に、Codeがプログラムを読み直して分かったことがある。MONETに渡すやり取りは、今いるスレッドの分だけ。だから別のスレッドに移った後、前のスレッドの話は要約でしか届かない。移った時に前のスレッドの要約をTIに入れておけば、それで足りる。
ただ、移った瞬間には、前のスレッドの最後のほうの要約がまだできていないことがある。そこで、要約がまだ無い会話のひと区切りは「待ち」として控えておく。その要約が全部そろったところで、もう1回だけTIを作り直す。要約が届くたびに作り直していたら、そのたびにキャッシュが効かなくなる。この形なら、効かなくなるのは移った時と待ちがそろった時で、1回の切り替えにつき多くて2回で済む。
この仕組みを入れたMONETを再起動して試すと、会話の最中にLibrarianが要約を書き込んでも、TIは固定されたままで、その後ろのやり取りもキャッシュが効いたままだった。ここまでは思った通りに動いた。
別の会話に移ってすぐ前の話を聞くと、「覚えていない」と返ってきた
続けて、スレッドを切り替えて、MONETに前のスレッドの話を振った。
MONETは、前のスレッドの内容を覚えていないと答えた。
Codeがログとデータを調べた。切り替えた時の作り直しは、ちゃんと動いていた。それより前の会話の要約も、TIに入っていた。足りなかったのは、切り替える直前の会話そのものの要約。Librarianの見回りは5分おきで、次の見回りがまだ来ていなかった。僕は切り替えてから1分半もしないうちに質問していた。
要約ができるまでの間、TIのその場所は空いている。空いていたら、MONETは「覚えていない」と言うしかない。
後から埋めることは、「待ち」の仕組みで考えてあった。でも、埋まるまでの間にMONETがどう答えるかは、仕組みのどこにも無かった。
記憶はまだ作っている途中で、忘れたわけではない。なのにMONETは「覚えていない」と答える。
空いている所には「要約作成中」の行を置いておく
そこで、TIに仮の行を置くようにした。
スレッドを切り替えた瞬間から、要約を待っている区切りを、「直前のスレッドの会話 — 要約作成中…」という行としてTIに載せておく。これでTIのその場所は、もう空かない。MONETはこれを見て、覚えていないではなく、いま要約しているところだと答えられるはず。要約が届いたら、仮の行は本物の要約に置き換わる。
仮の行は、移った時の作り直しに一緒に入れる。作り直しの回数は増えないので、キャッシュが効かなくなる回数も増えない。
待ち時間そのものを縮める案も出た。Librarianの見回りを、5分おきから1分おきにする。ただCodeの話では、Librarianは見回りのたびにAIを呼んでいるので、見回りを増やせば費用も増える。仮の行があれば、待っている間もTIは空かない。なので見回りは5分のまま、様子を見ることにした。