3行で始める文章検索 ― txtai入門
AI Shift Tech Blog は、複雑な RAG 構築を簡素化するオールインワンフレームワーク「txtai」の導入方法と、ベクトル検索・SQL 連携・ハイブリッド検索の実装例を紹介している。
キーポイント
RAG 構築の複雑さ解消
ベクトル DB や Embedding モデルの個別管理が不要なオールインワン AI フレームワークとして txtai を紹介し、3 行コードで文章検索を実現可能であることを示している。
多様な検索手法の統合
デフォルトのベクトル検索に加え、SQL クエリによるフィルタリングや BM25 によるキーワード検索、そして両者を組み合わせたハイブリッド検索をサポートしている。
RAG システムの実装例
就業規則などの社内資料を索引化し、TinySwallow-1.5B-Instruct などのオープンウェイトモデルと連携させて質問応答システムを構築する具体的なコード例を示している。
txtai のコード簡潔性と LangChain との比較
LangChain や LlamaIndex に比べて実装コードが少なくシンプルであり、軽量な PoC(概念実証)や迅速な試作に適している。
ベクトル DB としての利便性
Meta のライブラリや Elasticsearch と比較し、前処理の負担が少なく、pip install で手軽に導入できるため、サクッと試したい場合に便利である。
影響分析・編集コメントを表示
影響分析
この記事は、RAG システムの導入障壁となっているインフラ構成の複雑さを解消するツール「txtai」の実用性を明確に示しており、特に中小企業や研究開発現場における迅速なプロトタイピングを支援する意義が大きい。ただし、既存の大規模システムとの統合や高度なカスタマイズが必要なケースでは、単なるパッケージ化以上の検討が必要となる可能性がある。
編集コメント
RAG の実装において、複雑なインフラ構成に悩む開発者にとって、txtai は非常に魅力的な選択肢となるでしょう。特に SQL とベクトル検索を組み合わせる機能は、実務データのフィルタリング要件に対応する上で強力な武器になります。
3行で始める文章検索 ― txtai入門
こんにちは AIチームの戸田です
今回は文章検索をひとつのパッケージで実現できるtxtaiを紹介したいと思います。
LLMは強力ですが、手元の社内資料や議事録などの固有データに関する質問には回答することができません。そこで重要になるのがRAG(Retrieval Augmented Generation)です。 固有データを検索し、関連コンテキストを取り出してPromptとしてLLMに渡すことで、固有データに関する質問に対しても回答することができるようになります。実務での活用において、現在も多くの企業がこの仕組みを使っています。
RAGは非常に強力なアプローチなのですが、固有データを保存するベクトルDBや文章を機械的に扱えるようにするためのEmbeddingなど構成が複雑になりがちで、管理コストも高くなってしまう、という課題もあります。
txtaiはall-in-one AI frameworkを自称しており上記のようなRAGによる検索システムを構築する上でややこしくなる各モジュールをひとつのパッケージとして提供しています。
本記事ではtxtaiの簡単な使い方と類似ライブラリと比較したメリットを紹介します。
txtaiはPythonベースのフレームワークで、ベクトル検索やグラフ分析、リレーショナルデータベースを組み合わせたEmbeddings Databaseを中心に構成されています。
今回は扱いませんがyamlでワークフローとして定義をすることも可能なようで、応用の幅も広そうです。
Hugging Face Transformers/Sentence Transformers、FastAPIで構成されており、Python 3.10以上の環境が必要です。今回はGoogle Colaboratoryで試します。
pipでインストールできます。
pip install txtai
以下に簡単なベクトルの類似度を利用した質問応答の例を示します。
import txtai embeddings = txtai.Embeddings() embeddings.index(["カーナビ", "ラーメン", "ダンゴムシ"]) print(embeddings.search("食べ物は?", 1)) # 第2引数の1は近傍サンプルを最大何件返すか # [(1, 0.33019936084747314)] # 出力: (index, cos類似度)
indexが1、つまり"ラーメン"が最も近いという予測でもっともらしいです。import文を除くと3行で文章検索が書けてしまいました。ちなみにEmbeddingsではモデルを指定することができるのですが、デフォルトだと sentence-transformers/all-MiniLM-L6-v2 が使われるようです。
作成したindexは以下の方法でローカルファイルとして読み書きできます。
embeddings.save("{folder_name}") embeddings = Embeddings() embeddings.load("{folder_name}")
これがtxtaiの最も基礎的な使い方ですが、ここから検索手法をカスタマイズすることもできます。
txtaiは自然文クエリだけでなく、SQLでのクエリも組み合わせることができます。
embeddings = txtai.Embeddings(content=True, objects=True) embeddings.index([{ "text": "カーナビ", "n_char": 4, "text": "ラーメン", "n_char": 4, "text": "ダンゴムシ", "n_char": 5, "text": "カレーライス", "n_char": 6, }]) query = """ SELECT text, n_char FROM txtai WHERE similar('食べ物は?') AND n_char >= 6 """ print(embeddings.search(query, 2)) # [{'text': 'カレーライス', 'n_char': 6}]
indexにはテキストに加え、文字数(n_char)も一緒に登録します。基礎的な使い方の部分と同じ質問を投げかけていますが6文字以上でフィルタリングしているので、"ラーメン"はヒットせず、indexに追加した"カレーライス"のみがヒットします。
ベクトル検索は優秀ですが、品番など単語の表層が重要になるケースもあります。txtaiはBM25による表層検索もサポートしており、こういったケースにも対応できます。
例えば以下の例を見てみましょう。
embeddings = txtai.Embeddings() embeddings.index(["iphone 13", "iphone 14", "iphone 15"]) print(embeddings.search("15日に買ったiphone 13", 1)) # [(2, 0.4083036184310913)]
この場合"iphone 13"をヒットさせたいのですが、デフォルトのベクトル検索だと15日の方に引っ張られてしまい、indexが2、つまり"iphone 15"がヒットしてしまっています。
ここで、keyword=Trueで表層検索をすると、求める挙動通りに"iphone 13"をヒットさせることが確認できました。・
embeddings = txtai.Embeddings(keyword=True) embeddings.index(["iphone 13", "iphone 14", "iphone 15"]) print(embeddings.search("15日に買ったiphone 13", 1)) # [(0, 0.7487186789512634)]
ちなみに今回は試しませんでしたが、txtai.Embeddingsでhybrid=Trueを指定すると、ベクトル検索と表層検索のハイブリッド検索も行うことができるようです。
txtaiは実際にRAGを構築することもできます。以下に例を示します。Generate部分はTinySwallow-1.5B-Instructを使用しました。GPTのようなAPI経由で一般によく使われるモデルも使用できるようなのですが、HuggingFaceに公開されているOpen Weightなモデルと簡単に連携できることがアピールされていたことが理由です。
架空の企業の就業規則 data = [ "勤務時間は原則9:30-18:30(休憩60分)で、フレックスはコアタイム11:00-15:00。", "月の所定労働時間は営業日数×8時間。半休は午前(9:30-13:30)と午後(14:30-18:30)。", "リモート勤務は週3日まで、申請は前日18:00までにWorkPortalで行う。", "年次有給休暇は入社日に10日付与し、以後毎年4月1日に11日、12日、…と1日ずつ加算され最大20日。", "病気休暇は年度あたり5日まで(有給とは別枠)。遅刻は30分単位で控除される。", "残業申請は当日中にSlackの#overtimeへ理由と見込み時間を投稿し、翌営業日までに精算する。" ] # IDをつける。なんでも良いが今回はuuid5 embeddings = txtai.Embeddings(content=True, autoid="uuid5") embeddings.index(data) # RAG用のPrompt Template template = """ Contextに応じて質問に答えてください。 Question: {question} Context: {context} """ # Create and run RAG instance rag = txtai.RAG( embeddings, "SakanaAI/TinySwallow-1.5B-Instruct", template=template, output="reference") # RAGの実行 resp = rag("フレックスのコアタイムは何時から何時まで?") print(resp) # {'answer': 'フレックスのコアタイムは 11:00 - 15:00 です。', 'reference': '20316a1f-6aa0-5680-97ba-b5e2e7b8461b'} # 参照先の確認 i = resp["reference"] ref = embeddings.search(f"select id, text from txtai where id = '{i}'") print(ref) # [{'id': '20316a1f-6aa0-5680-97ba-b5e2e7b8461b', 'text': '勤務時間は原則9:30-18:30(休憩60分)で、フレックスはコアタイム11:00-15:00。'}]
質問に対して正しいリファレンスをもとに答えられたことがわかるかと思います。
文章検索周りでよく聞くライブラリと比較したメリットを紹介したいと思います。
LLM周りでは一番よく聞くライブラリなのではないでしょうか?正直、今回試したようなことはLangChainでも実現可能なのですが、コード量が少なくシンプルに書けるという点に関してはtxtaiが優れていると思います。一方でHyDEのような複雑なRAGを実装しようとするならばLangChainの方が柔軟性が高くて良いかもしれません。類似するライブラリとしてLlamaIndexなどが挙げられます
いわゆるベクトルDBです。txtaiはpip install txtai
Metaが開発したベクトル検索ライブラリです。最近はあまり使っていませんが、以前Chat Bot開発をしている頃はお世話になりました。非常に高速なのですが、前処理などを自分で書く必要があり、そこをよしなに済ませてくれるtxtaiはサクッと試したい時に便利だと思いました。類似するものだとElasticsearchが代表的なものだと思います。
本記事では文章検索を簡単に実装できるtxtaiを紹介しました。
非常に少ないコード量で書くことができ、軽いPoCなどでは活躍するのではないかと考えています。
今回は試しませんでしたが、API化を簡単にできたり、マルチモーダル対応があったりと、まだ興味深い機能があるので、また機会をみて試した上で紹介したいと思います。
最後までお読みいただき、ありがとうございました!


原文を表示
3行で始める文章検索 ― txtai入門
こんにちは AIチームの戸田です
今回は文章検索をひとつのパッケージで実現できるtxtaiを紹介したいと思います。
LLMは強力ですが、手元の社内資料や議事録などの固有データに関する質問には回答することができません。そこで重要になるのがRAG(Retrieval Augmented Generation)です。 固有データを検索し、関連コンテキストを取り出してPromptとしてLLMに渡すことで、固有データに関する質問に対しても回答することができるようになります。実務での活用において、現在も多くの企業がこの仕組みを使っています。
RAGは非常に強力なアプローチなのですが、固有データを保存するベクトルDBや文章を機械的に扱えるようにするためのEmbeddingなど構成が複雑になりがちで、管理コストも高くなってしまう、という課題もあります。
txtaiはall-in-one AI frameworkを自称しており上記のようなRAGによる検索システムを構築する上でややこしくなる各モジュールをひとつのパッケージとして提供しています。
本記事ではtxtaiの簡単な使い方と類似ライブラリと比較したメリットを紹介します。
txtaiはPythonベースのフレームワークで、ベクトル検索やグラフ分析、リレーショナルデータベースを組み合わせたEmbeddings Databaseを中心に構成されています。
今回は扱いませんがyamlでワークフローとして定義をすることも可能なようで、応用の幅も広そうです。
Hugging Face Transformers/Sentence Transformers、FastAPIで構成されており、Python 3.10以上の環境が必要です。今回はGoogle Colaboratoryで試します。
pipでインストールできます。
pip install txtai
以下に簡単なベクトルの類似度を利用した質問応答の例を示します。
import txtai embeddings = txtai.Embeddings() embeddings.index(["カーナビ", "ラーメン", "ダンゴムシ"]) print(embeddings.search("食べ物は?", 1)) # 第2引数の1は近傍サンプルを最大何件返すか # [(1, 0.33019936084747314)] # 出力: (index, cos類似度)
indexが1、つまり"ラーメン"が最も近いという予測でもっともらしいです。import文を除くと3行で文章検索が書けてしまいました。ちなみにEmbeddingsではモデルを指定することができるのですが、デフォルトだと sentence-transformers/all-MiniLM-L6-v2 が使われるようです。
作成したindexは以下の方法でローカルファイルとして読み書きできます。
embeddings.save("{folder_name}") embeddings = Embeddings() embeddings.load("{folder_name}")
これがtxtaiの最も基礎的な使い方ですが、ここから検索手法をカスタマイズすることもできます。
txtaiは自然文クエリだけでなく、SQLでのクエリも組み合わせることができます。
embeddings = txtai.Embeddings(content=True, objects=True) embeddings.index([{ "text": "カーナビ", "n_char": 4, "text": "ラーメン", "n_char": 4, "text": "ダンゴムシ", "n_char": 5, "text": "カレーライス", "n_char": 6, }]) query = """ SELECT text, n_char FROM txtai WHERE similar('食べ物は?') AND n_char >= 6 """ print(embeddings.search(query, 2)) # [{'text': 'カレーライス', 'n_char': 6}]
indexにはテキストに加え、文字数(n_char)も一緒に登録します。基礎的な使い方の部分と同じ質問を投げかけていますが6文字以上でフィルタリングしているので、"ラーメン"はヒットせず、indexに追加した"カレーライス"のみがヒットします。
ベクトル検索は優秀ですが、品番など単語の表層が重要になるケースもあります。txtaiはBM25による表層検索もサポートしており、こういったケースにも対応できます。
例えば以下の例を見てみましょう。
embeddings = txtai.Embeddings() embeddings.index(["iphone 13", "iphone 14", "iphone 15"]) print(embeddings.search("15日に買ったiphone 13", 1)) # [(2, 0.4083036184310913)]
この場合"iphone 13"をヒットさせたいのですが、デフォルトのベクトル検索だと15日の方に引っ張られてしまい、indexが2、つまり"iphone 15"がヒットしてしまっています。
ここで、keyword=Trueで表層検索をすると、求める挙動通りに"iphone 13"をヒットさせることが確認できました。・
embeddings = txtai.Embeddings(keyword=True) embeddings.index(["iphone 13", "iphone 14", "iphone 15"]) print(embeddings.search("15日に買ったiphone 13", 1)) # [(0, 0.7487186789512634)]
ちなみに今回は試しませんでしたが、txtai.Embeddingsでhybrid=Trueを指定すると、ベクトル検索と表層検索のハイブリッド検索も行うことができるようです。
txtaiは実際にRAGを構築することもできます。以下に例を示します。Generate部分はTinySwallow-1.5B-Instructを使用しました。GPTのようなAPI経由で一般によく使われるモデルも使用できるようなのですが、HuggingFaceに公開されているOpen Weightなモデルと簡単に連携できることがアピールされていたことが理由です。
架空の企業の就業規則 data = [ "勤務時間は原則9:30-18:30(休憩60分)で、フレックスはコアタイム11:00-15:00。", "月の所定労働時間は営業日数×8時間。半休は午前(9:30-13:30)と午後(14:30-18:30)。", "リモート勤務は週3日まで、申請は前日18:00までにWorkPortalで行う。", "年次有給休暇は入社日に10日付与し、以後毎年4月1日に11日、12日、…と1日ずつ加算され最大20日。", "病気休暇は年度あたり5日まで(有給とは別枠)。遅刻は30分単位で控除される。", "残業申請は当日中にSlackの#overtimeへ理由と見込み時間を投稿し、翌営業日までに精算する。" ] # IDをつける。なんでも良いが今回はuuid5 embeddings = txtai.Embeddings(content=True, autoid="uuid5") embeddings.index(data) # RAG用のPrompt Template template = """ Contextに応じて質問に答えてください。 Question: {question} Context: {context} """ # Create and run RAG instance rag = txtai.RAG( embeddings, "SakanaAI/TinySwallow-1.5B-Instruct", template=template, output="reference") # RAGの実行 resp = rag("フレックスのコアタイムは何時から何時まで?") print(resp) # {'answer': 'フレックスのコアタイムは 11:00 - 15:00 です。', 'reference': '20316a1f-6aa0-5680-97ba-b5e2e7b8461b'} # 参照先の確認 i = resp["reference"] ref = embeddings.search(f"select id, text from txtai where id = '{i}'") print(ref) # [{'id': '20316a1f-6aa0-5680-97ba-b5e2e7b8461b', 'text': '勤務時間は原則9:30-18:30(休憩60分)で、フレックスはコアタイム11:00-15:00。'}]
質問に対して正しいリファレンスをもとに答えられたことがわかるかと思います。
文章検索周りでよく聞くライブラリと比較したメリットを紹介したいと思います。
LLM周りでは一番よく聞くライブラリなのではないでしょうか?正直、今回試したようなことはLangChainでも実現可能なのですが、コード量が少なくシンプルに書けるという点に関してはtxtaiが優れていると思います。一方でHyDEのような複雑なRAGを実装しようとするならばLangChainの方が柔軟性が高くて良いかもしれません。類似するライブラリとしてLlamaIndexなどが挙げられます
いわゆるベクトルDBです。txtaiはpip install txtai
Metaが開発したベクトル検索ライブラリです。最近はあまり使っていませんが、以前Chat Bot開発をしている頃はお世話になりました。非常に高速なのですが、前処理などを自分で書く必要があり、そこをよしなに済ませてくれるtxtaiはサクッと試したい時に便利だと思いました。類似するものだとElasticsearchが代表的なものだと思います。
本記事では文章検索を簡単に実装できるtxtaiを紹介しました。
非常に少ないコード量で書くことができ、軽いPoCなどでは活躍するのではないかと考えています。
今回は試しませんでしたが、API化を簡単にできたり、マルチモーダル対応があったりと、まだ興味深い機能があるので、また機会をみて試した上で紹介したいと思います。
最後までお読みいただき、ありがとうございました!


関連記事
今日のまとめ
AI日報で今日の重要ニュースをまとめ読み