会話を続けるには、前の発言が入力に必要になる

第2章では、一回の入力から応答を受け取る流れを学びました。会話では、その流れを繰り返し、過去のやり取りも必要に応じて入力へ加えます。記録の基本の形には、第4節の役割付きメッセージを使います。

利用者が「大阪出張の申請方法を教えて」と聞き、回答の後に「日帰りの場合は?」と質問したとします。二つ目の質問だけでは、何の日帰りなのかが分かりません。人は直前の会話から補えますが、モデルにも、その会話を入力として与える必要があります。

同じモデルを繰り返し呼ぶだけで、以前のやり取りを自動的にすべて覚えてくれると考えてはいけません。会話を継続するための情報は、アプリケーション側で保持し、利用する仕組みに応じて次の入力へ含めます。画面に過去の発言が表示されていることと、その発言がモデルの入力に入っていることも別です。

会話履歴は、役割と順序を持つデータ

会話履歴には、利用者の質問とモデルの応答を、役割付きメッセージとして順に保持します。ツールを利用した場合は、その要求と結果も含めて扱います。単に「前に出張について話した」という一文を保存する場合と、やり取りを元の構造で保存する場合では、保持できる情報が異なります。

モデルへの入力は、共通の回答方針、必要な履歴、今回の質問などから構成されます。過去の応答は、会話の文脈としては有用ですが、事実として常に正しいとは限りません。以前の回答が誤っていた場合、その誤りを繰り返さないよう、現在の資料や訂正情報を踏まえて扱う必要があります。

LangChainの短期メモリとthread

状態は、処理を続けるために持っておく情報のまとまりです。ここでは、過去のメッセージや、今どんな相談をしているかが当てはまります。メモリは、過去のやり取りに関する情報を保持し、次の処理で使う仕組みです。モデル本体を追加学習することとは分けて考えます。

LangChainのエージェントでは、会話履歴などを状態として管理し、checkpointer(チェックポインター)を組み合わせて会話単位で保持できます。checkpointerは、処理の途中や終了時の状態を記録する仕組みです。前回の状態を復元することで、次のやり取りへつなげます。エージェントの動作自体は第14節で扱いますが、ここでは会話の状態を扱う仕組みに注目します。

この会話のまとまりをthread(スレッド)と呼び、thread_idという識別子で区別します。同じ利用者でも、出張申請の相談と採用に関する相談を別のthreadにすれば、それぞれの会話を分けて扱えます。逆に、会話を続けたいのに毎回別のIDを付けると、過去の状態とつながりません。

threadのIDは、本人確認そのものではありません。IDを知っているだけで別の人の履歴を読める設計にしてはいけません。アプリケーション側で、その利用者がその会話を参照してよいかを確認する必要があります。LangChainによる状態管理と、利用者の認証・権限管理を区別します。

認証は「その人が誰か」を確認すること、認可は「その人が何をしてよいか」を決めて確認することです。ログインできた利用者でも、ほかの人の会話を読めるとは限りません。この違いは、後に資料検索やツールを利用するときにも必要になります。

保存先によって、保持できる期間が変わる

起動して動いているプログラムの実行単位を、プロセスと呼びます。実行中だけ使う記憶領域に情報を置く場合と、ファイルやデータベースへ書き残す場合では、終了・再起動した後の扱いが違います。データベースは、情報を整理して保存・取得するための仕組みです。

メモリ上に状態を置く方法では、同じプロセスが動いている間は会話を継続できても、再起動すると失われることがあります。データベースなどへ保存するcheckpointerを使うと、プロセスを越えた再開を構成できます。短期メモリという名前は、必ず短時間で消えるという意味ではなく、主に一つのthreadの範囲で使う情報であることを指します。

「アプリケーションを再起動したら会話を忘れた」という問題は、モデルの知能の問題ではなく、状態の保存先や復元方法の問題かもしれません。会話の仕組みを、生成、状態、保存の三つに分けて考えると、原因を見つけやすくなります。

たとえば一回目の質問と回答を「出張相談の状態」として保存し、二回目の「日帰りの場合は?」で同じ状態を取り出します。その履歴を今回の入力に含めることで、何の相談が続いているかをモデルへ伝えられます。保存しただけでモデルへ自動的に伝わるのではなく、保存・復元・入力への反映がつながって会話になります。

履歴は多ければよいとは限らない

会話が長くなるほど、モデルへ渡す情報の量も増えます。入力の上限に近づくだけでなく、関係の薄い過去の話題が混ざり、今回の質問に必要な条件が埋もれることがあります。また、モデルへ渡す情報が増えることで、時間や費用にも影響します。

そのため、履歴を保存することと、保存した履歴を毎回すべて渡すことは分けて設計します。直近のやり取りだけを使う、古い部分を要約する、必要な条件だけを残す、といった方法があります。ただし、要約で日付や例外条件を失う可能性もあります。次の節では、保存と入力の範囲をもう少し広く整理します。