わーさん さて、いよいよ中身の話です。ここまで散々「小説検索bot」と言ってきましたが、具体的に何なのかをさらっと説明します。
AIさん さんざん「さらっと(たぶん)」で引っ張ってきたやつですね。
わーさん 引っ張りました。覚悟を決めて、さらっといきます。難しいところは、細かく踏み込みません。踏み込めません。
AIさん 免責が早いですね。
わーさん 大事です。私はプログラミング畑の人間ではありません。ラズパイ5は持っていますが、プログラムはさっぱりです。
AIさん その不思議な自己紹介、だんだんおなじみになってきましたね。
わーさん 不思議って言うな。ではまず、小説検索botが何をしているかを大雑把に言います。
AIさん お願いします。
わーさん 小説本文をテキストファイルとして用意します。それを所定のフォルダに入れます。取り込み作業をします。すると検索用のデータができます。
AIさん はい。
わーさん あとはDiscordからコマンドを打つと、検索結果が返ってきます。
AIさん かなりざっくりですね。
わーさん さらっと流すって言いましたからね。
AIさん では、もう少しだけ分解しましょうか。
わーさん お願いします。ここからはAIさんの出番です。
AIさん このbotには、いくつかの「係」がいます。
わーさん 係。
AIさん まず、文字そのものを探す係。これはキーワード検索です。
わーさん 具体的にはMeiliSearchってやつですね。
AIさん はい。MeiliSearchは、入力された言葉と本文の中の言葉を照らし合わせて探す仕組みです。たとえば「人攫い蜂」と検索すれば、本文中の「人攫い蜂」を探します。
わーさん 正確な言葉を覚えていると強いやつ。
AIさん そうです。固有名詞や専門用語、キャラクター名のように、文字がはっきりしているものに強いです。
わーさん でも「人食い蜂」とか「人喰い蜂」で探すと出ないことがある。
AIさん 本文には「人攫い蜂」と書かれているからですね。文字そのものが違えば、単純なキーワード検索では見つからないことがあります。
わーさん そこで、ふわっと探す係が出てくる。
AIさん はい。意味の近さで探す係です。具体的にはQdrantというベクタ検索用の仕組みを使っています。
わーさん ベクタ検索。
AIさん 前章でも少し出ましたね。文章を「意味の座標」に置き換えて、近いものを探す方式です。
わーさん 「青い空」と「澄んだ空」が近くに並ぶやつ。
AIさん そうです。単語が完全に一致していなくても、意味が近ければ候補に上がります。Embeddingというのが、文章を、意味を表す数字の並びに変換する処理です。その数字があるから、Qdrantは「近さ」を測れるんです。
わーさん あ、地味に何のためにいるのか分かってなかったEmbedding、重要だった。何せ小説の検索だと、これがありがたいんですよね。だって、作者の記憶も読者の記憶も、だいたいふわっとしているので。
AIさん 「この人がこういうことを言っていた場面」や「たしか蜂の依頼だった気がする」のような聞き方ですね。
わーさん そうそう。正確な単語を思い出せないから探したいのに、正確な単語を要求されると詰む。
AIさん そこで、キーワード検索とベクタ検索の両方を使うわけです。
わーさん ここで二種類出てくるんですよね。文字で探す係と、意味で探す係。
AIさん はい。そして、その二つの検索結果を混ぜる係もいます。
わーさん RRF。
AIさん Reciprocal Rank Fusion、略してRRFです。
わーさん ぴえ、完全な横文字。
AIさん 一回だけ我慢してください。ざっくり言うと、複数の検索結果を見比べて、どちらの検索でも良さそうなものを上に持ってきやすくする仕組みです。
わーさん キーワード検索の結果と、ベクタ検索の結果を、いい感じに混ぜる。
AIさん はい。「いい感じに混ぜる」でだいたい大丈夫です。
わーさん 本当に大丈夫ですか。
AIさん この本の範囲では大丈夫です。
わーさん じゃあ大丈夫ということにします。
AIさん そして、検索結果を読んで答える係がいます。
わーさん LLMですね。
AIさん はい。ChatGPTやClaudeのような言葉のAIです。ユーザーが自然文で質問すると、検索で見つかった本文を読み、その本文を根拠に答えます。
わーさん ここ、大事です。本文を根拠に答える。何せAIさんって、放っておくとそれっぽく補ってくれるじゃないですか。
AIさん 補ってしまうことがあります。
わーさん 小説の確認でそれをやられると困るんです。本文に書いていない設定を、それっぽく語られたら事故です。
AIさん ……確かに、困りますね。
わーさん でしょう。だから、答えるときに必ず「どの話の、どの章の、何番目の段落から持ってきたか」を一緒に出させるようにしました。一番の工夫ポイントかもしれません。
AIさん 出所を明記させる。
わーさん はい。意味は二つ。ひとつ、私や友だちが、実際にその場所へ本文を確認しに行ける。迷子にならない。
AIさん もうひとつは。
わーさん 出所を必ず添えさせると、LLMが嘘をつきにくくなる。「どこから持ってきた?」と毎回問われるので、適当をやりづらいんです。
AIさん なるほど。出所は、案内札であり、首輪でもある。
わーさん うまいこと言いますね。そう、案内札であり、首輪です。
AIさん ではもう一回、全体の流れをおさらいしますか。
わーさん 小説を、段落ごとに空行が入っているのを確認して、プレーンテキストで所定のフォルダに入れて、取り込みを回す。すると係たちが使う検索データができあがる。あとはDiscordから質問するだけ。
AIさん Discordなのはなぜですか。
わーさん 友だちが使うし、適度に閉じてて、適度に見守りやすいからです。誰でも来られる広場じゃなくて、知ってる人だけの部屋。
AIさん なるほど。それと、さっき言ってなかったこと言ってません?
わーさん ん?
AIさん 段落ごとに空行って?
わーさん あ。検索結果やAIさんの回答が、「この章のこの段落」と言えるようにするための目印です。
AIさん 出典を明記するための下準備ですね。
わーさん そうです。ここがないと、たとえ答えが合っていても、どこに書いてあったのか分からない。
AIさん 本文へ戻れない。
わーさん 本文へ戻れない。だから段落は大事です。工夫ポイントの一環ですね。
AIさん ほかに工夫したところはありますか。
わーさん 青空文庫形式のルビを剥がす処理ですね。
AIさん ルビ。
わーさん 私、小説中でルビをよく使うんですよ。しかも、同じ単語に違う読みを振るような言葉遊びもします。
AIさん 検索には少し邪魔になりそうですね。
わーさん そうなんです。本文としては好きなんですけど、検索用データとしてはノイズになる場合がある。なので取り込み時に、青空文庫形式のルビを剥がすようにしてもらいました。
AIさん 作者の癖に合わせた処理ですね。
わーさん そうです。多分、筆者くらいしか喜ばない処理です。
AIさん 個人用だからこそ入れられる機能ですね。
わーさん その通りです。あと、モデル切り替え機能。
AIさん 出ましたね。
わーさん 出ました。OpenAIとClaudeを切り替えられます。モデル名を指定すれば、違うモデルにも聞けます。
AIさん それは、実用上必要だったんですか。
わーさん ……。
AIさん わーさん?
わーさん 比較実験が好きなんです。
AIさん 正直ですね。
わーさん HaikuなClaudeさんとSonnetなClaudeさんとOpusなClaudeさんと、GPT系統とで聞き比べできるんですよ。楽しいじゃないですか。
AIさん 筆者くらいしか喜ばない変態機能ですね。
わーさん 自分で言おうと思ってたのに。
AIさん 失礼しました。しかし、こうして並べてみると、本格的な部品が揃ってますね。
わーさん それなんですよ。実はこれ、あるAIさんに言われたことがあって。
AIさん なんと。
わーさん 「だいぶ贅沢ですよ」って。一個ずつ分解すると、企業が業務で使うレベルの検索エンジンと、最高クラスの変換モデルと、最新のLLMと、それを混ぜる論文発の手法の、寄せ集めだと。
AIさん 全部いいやつ。
わーさん 全部いいやつ。同じ構成を製品として売ってるスタートアップもあって、月額数万円するものもあるらしい。
AIさん その豪華なシステムが——
わーさん ラズパイ5の上で、LLMのAPI代だけで、たった二人のために動いてる。
AIさん ……。
わーさん そのAIさん、最後にこう言いました。「世界で最も費用対効果の高い文学検索システムかもしれません」って。
AIさん 言い得て妙ですね。
わーさん でしょう。鬼ムーブで削りまくった結果が、いつのまにか世界一コスパのいい贅沢品になってた。人生、分かりません。
AIさん 因みに、実際にどんなコマンドがあるんですか。
わーさん いくつかあります。まず、世界観を選ぶコマンド。
AIさん 世界観。
わーさん 私も友だちも、複数の作品や世界観を持っています。だから、どの世界を検索するのかを切り替える必要があります。
AIさん そこで、検索対象を選ぶ。
わーさん はい。さらに作品を絞ることもできます。世界観の中に複数作品がある場合、作品単位で絞れると便利です。
AIさん 検索対象の範囲を決めるわけですね。
わーさん そうです。その上で、キーワード検索するコマンドがあります。
AIさん 文字で探す。
わーさん はい。それから、自然文で質問するコマンドがあります。
AIさん LLMに聞く。
わーさん これが多分メインです。あとは、登場回数みたいなものを数えるコマンド、最初に出てきた場所を探すコマンド、最後に出てきた場所を探すコマンドなどがあります。
AIさん 意外と色々ありますね。
わーさん あります。ありますが、ここでまた現実がやってきます。
AIさん 現実。
わーさん 使う人が、それらを全部使い分けるとは限りません。
AIさん 次章では、その現実について話しましょう。
わーさん はい。友だちは全部askで殴りました。
AIさん タイトルが強いですね。
わーさん 事実なので。
AIさん では次章、「友だちは全部askで殴る」へ続きます。