見出し画像

mi/旅botに独自RAGを拡張する(2)-言語のAIなのに「造語」はしないの?

以下の続き


一旦ベクトル検索を外して、形態素検索に戻すことに決めた

前回、今風のRAGの機能を導入しようとして、embedding LLMとベクトル検索を組み込んでみたのですが、少し運用してみて、そもそも自分が考えている想起RAGにはベクトル文検索が不向きであることがわかってきた。

文による検索がRAGに有効なのは、その元データソースがルールや規則を記述している「文書」だからだ。説明される内容は、文の構造によって仕組みを説明しているし、その仕組みを検索したいからベクトル文検索なのだ。

一方、想起RAGは「過去の雑談」という「言語ではあるが、結論も起承転結もない会話の断片」を検索する機能を必要としている。
過去体験の想起という「まだ何も結論も言説も辻褄もないカオスな過去記憶情報」を検索するのに、言説や結論を検索するためのベクトル文検索を使うことがそもそも間違いだった。
であれば、自分の目的にあう技法は自分で探すしかない。
想起は「私は公園に行きました」ではなく「公園」というシンボルで検索される。元々わりと思った動きが出来ていたのだと思う。そこで形態素による有意語検索に戻すことにした。
ただベクトル文検索でもきわめて一致度が高いものは有用かもしれない。おそらく構造の一致を検索することは後になって必要になる。なので仕組みとしては残しておくことにした(そういう残し方は邪魔になっていくことが多いけど備忘録として)。

想起RAGのアプローチ

考えている想起RAGは、仕組みとしては、最近時々見かける「80~90年代に見かけた意味ネットワークなどの、認知工学的手法をRAGに適用する」に近いものです。

この時代の手法は今では古くさいものばかりですが、夢にあふれていたとも言えます。ただCPUとリソースがまったく追いついていないので全部黒歴史化してますが(ICOTの再総括とか誰かしないですかね。。。あの時代にもニューロやってた人は少数だけどいたんですよ)
とりあえずその頃の考え方を自分なりに応用してみます。
私が想起RAGに考えた方法はissueとthreadという形で過去会話を保存する方法です。

スレッド(thread)

mi-serverで使用するAIは、一貫した問題を解決するのではなく、雑多に存在する多方向のTask(ToDo)の解決を支援するためのAIです。それに加えて旅bot達の仮想旅の思い出を処理します。
その入力情報は大きく以下のものです。

  • 日常タスクの文章(目的、備忘、懸念、注意 を記述した短い文章)

  • 音声会話での日常雑談1(Webブラウザーからの記事、SNS情報とそれに応答して入力した会話文)

  • 音声会話での日常雑談2(walker端末からの画像解釈情報とそれに応答して入力した会話文)

  • 家電操作情報

  • 旅botの旅記録(Map API等の地図情報とそれから創作指示した仮想旅情報)

いずれも文章としては短いものです。個々の文章間にはあまり関連性はありません。ただ一定の周期性はあります。朝のルーティンワークはほぼ似たものになりますし、ロボット掃除機もほぼ毎日起動はします。毎週行うというルーティンワークもあります。

これらをRAG情報として扱うために、短い一定時間の間に行う短い会話の集まりを「スレッド」という単位で保存する仕組みとしています。

会話の間に一定時間の間隙が空いたときに、その時までの対話ログをLLMに送り「要約」します。そして対話ログと要約にidを振ってdb保存しています。また会話文について形態素有意語も抽出して合わせて保存します。

threadの主な情報

threadに似た仕組みはmi-server AIのかなり最初の頃から使っていて、要約処理もしていました。要約はそこそこ賢い必要があるのでローカルLLMでは難しく、GPTを使用していました。ただもったいないことにうまく利用はできていなかったです。今はGPT3.5とかClaude-haikuとか低コストAPIにやらせていたのですが、今回はこのthreadと要約の仕組みをもっと改良しているところです。

イシュー(issue)

issueは今回、想起RAGを作るのに考えた新しい仕組みです(命名が安直ですが)。
日常雑談が入力なのでルーチンワークなど同じような目的の会話が多数回発生します。そのため目的がほぼ同じthreadをissueという単位でまとめる仕組みを持たせています。
issueもthreadと同じように要約を生成しmutableDescriptionという名前で保存します。
これらの要約/集約の判断については即座計算は不要なので、LLMに後々バッチで「考察」させる方法を使っています。
(例)jsonモードで「thread「…」はissue「…」に分類されますか」と判定させるなど。
バッチでthreadとissueを整理させる様子は「睡眠中(夢)に前日の記憶を整理する」みたいな話しを連想させます。

issueとthread

issueという命名は元々、threadを集めて一つのグループを作るという考え方からではなく、mi-serverのタスク(ToDo)が存在し、タスクの課題(issue)にthreadを割り付けていくという考え方で当初付けたものです。この命名が適切かどうかはまだ悩ましいのですが、threadの方が種火として始まってissueを構成すべきではないかと今は考えています。
ただ現時点はまだ確信はないので、これがトップダウン(issue->threads)でもボトムアップ(threads->issue)でもどちらでも行けるようにはしておきたいところ。
issueは想起RAGの想起検索単位であり、現在会話中の文から想起RAG検索し、検索したissue/threadをLLMに追加プロンプトとして送ります。

想起情報としてissue情報をLLMに追加する

大枠の仕組みとしてはこういう形ですが、issue周りはまだいろいろ悩みながらカットアンドトライをしてしている感じです。各要約をどのような形に要約するかとか、このthreadはどのissueに属するかをどう判断するかなど。例えば会話の想起では、内容や単語の合致度より、より最近のthreadを抽出するほうが有用という面があったりします。
これらは解析ツールを作りながら処理を改良していくことになるでしょう。

user:"パン屋を教えてください"
(パン→パンについて議論issueを想起。子threadの会話を参考情報としてプロンプトに付加)
assistant: "アンパンが好きとのことでしたから、井村屋はどうでしょうか"

想定した理想の会話

言語のAIなのに「新語の生成」は考えないの?

ところでちょっと話題を変えます。
私はLLMは「言語のAI」だと考えています。

でもそうであれば次の問いはどう考えるべきでしょうか。

LLMは言語のAIなのに「新語の生成(クリエーション)」はしないのか?

LLMが自身の出力を学習ソースとして学習し続けると学習情報が崩壊するという発表は最近よく見ます。
そりゃ言語のAIが新語を生成もしないのに、自身を学習しつづければエントロピー死みたいなことは起きるでしょう。

誰も語を生成するということがどういうことか定義できていない

LLMはざっくり言うと、言語のつらなりを統計したデータで、次のつらなりを予測生成したものですが、統計するためには元になる言語の統計データが必要です。まだ生み出されていない存在しない語に統計データなどない訳ですから、LLMには新語を生み出すという概念が元々ありません。
誰もまだ「語を生み出す」ということがどういうことか定義できていないのです。

自分の解釈

これについては私は思い当たることがあります。

私は人が処理する価値観の最小単位は「時間の間隔が短いこと」が価値の値を持つということだと考えています。

「時間の間隔の短さ(ほぼ同時性)の持つ価値」が新語(新概念)を生み出す一番基底の仕組み、というのが自分の解釈です。

想起RAGと語の生成

「『同時(または短い時間間隔)』に起きる事象は人にとって意味がある」という話しは先の記事のほうを参照ください。
ここでは「想起RAGを使って言語AI向けに『同時(または短い時間間隔)』を合成する」という点を説明します。

想起RAGは過去会話をthreadという短い会話情報で記録します。
threadはバッチ処理でissueという単位で類似の会話同士をまとめます。
ここである話題についてuserから問いかけの会話が開始されたとします。
LLMには返答文を生成するためのプロンプトを準備します。

prompt:[
(chatの直近のhistory会話)
…,
{
user:"パン屋を教えてください"
system: (会話前提/環境のプロンプト)+(「パン屋を教えてください」から想起RAGで想起された追加情報。例えば「おいしいパンについて議論しましたのthreadで語った過去の文章」)
}
]

LLMに送るプロンプト例

ここで「想起RAGで想起された追加情報」とはRAG検索して持ってきたissue/threadのテキストですが、これは「過去の会話」の情報です。
このLLMに送るプロンプトは一度に送るものであり、AI制御しているmi-server上にほぼ同時に存在する状態にあります。
このプロンプト内に「過去の会話情報」が存在するということは、「過去の情報を現在の情報として、同時に存在する状態を作った」と解釈できます。

「過去の会話情報」を想起RAGが検索/抽出してくることにより、「現在と過去の情報が同時に存在する」状態を作った

想起というのが元々、過去を思い出す(過去情報を現在に再現する)の意味なので、当たり前すぎる話しです。ですが当たり前すぎて意識できていない事項だと思っています。
「想起RAGが過去情報と現在情報が同時に活性して存在する状態にした」というのは重要なことだと思っています。

LLMの出力結果だけでは作れない「過去情報と現在情報が同時に活性状態で存在する」という「同時状態の発生」を想起RAGにより作り、人の同時性価値が作られる状態と同じ状態が作られる。あとは「同時性をキーにして新語を生成させる」という共通の仕組みを作ることが出来れば、言語のAIに新語が生成出来るのではないかというのが私の仮説です。

この方向に何かあると思っているけど、まだ先は長いだろう

なんか今にも造語が出来そうなことを言ってますが、すべてがうまくつらなったらそうなるだろうなと言ってるだけで、まだ個々の機能部分は楽観論な話しばかりです。何より今風の機械学習数理モデルに落とし込めていない。

想起RAGのissue/threadシステムにはもう一つそこに向かう実験があります。
広辞苑など書籍の辞典が新語を載せる場合にどのように書くかですが

パン
1 小麦粉・ライ麦粉などを主原料とし、少量の塩を入れて水でこね、酵母で発酵させてから天火などで焼いた食品。・・・
[単語の短い説明]

・・・僕はパンをかじりながら、ちょっと腕時計をのぞいてみました。時刻は・・・ 芥川竜之介「河童」
[単語の用例(複数)]

辞典による「存在する語」の説明(デジタル大辞泉(小学館)/goo辞書 抜粋)

issue.id
issue.mutableDescription [短い説明]
issue.threads[] {user:"",assistant:""}[] 複数の用例

想起RAGによる「まだ存在しない語」の説明

issue/threadの関係が、辞書の語彙説明に近い様式になるようにしています。この形であれば言語のAIであるLLMに追加プロンプトとして送り込めると考えています。
一方でどのようにmutableDescriptionの記述やthreadの用例がほどよい形に変化させていくにはまだいろいろ工夫がいりそうだなとも思ってます。

結局蓄積していた過去会話/過去旅ログは一旦ベクトル文検索用に再加工していたのですが、形態素有意語での検索用に再加工中です。以前の仕組みも使えるのですがこの際このあたりも見直すべきなので。
issueの生成についても原始的な生成ができはじめたところで、抽出のチューニングはまだ粗いです。

issue+threadsのサンプル

それに語を生成するにはまだ足りてないと思う要素がいくつかあると考えています。アイデアはあるのですがそれらは自分の中でも説明できるほど確信はないもので、もう少しあーでもないこうでもないと煮詰めてみるしかなさそうです。
本来は数理モデルにしてGPUなどで一気にベクトル計算できるような形に持って行かないといけないのですが、根が手続き型頭の昭和人間としてはそこまでは持って行ききれない(だからガラパゴス化するんですよね。。。)
非効率ではあっても実装屋としてはカットアンドトライで進めて行くのが自分のやり方だろうなと思っています。
なにより慣れた方法でないと楽しんで作れないではないですか。

この記事が参加している募集

この記事が気に入ったらサポートをしてみませんか?