Haystack で最小の検索 QA を作る:Retriever + Reader の 30 分ハンズオン
大量のドキュメントから素早く答えを見つけたい、そんな悩みを抱えていませんか。従来の検索エンジンでは「キーワードに一致するページ」は見つかりますが、「具体的な答え」までは教えてくれません。
本記事では、deepset 社が開発したオープンソースフレームワーク Haystack を使い、最小構成の質問応答(QA)システムを 30 分で構築する方法をご紹介します。Retriever でドキュメントを絞り込み、Reader で正確な回答を抽出する 2 段階のパイプラインを、実際のコードとともに学んでいきましょう。
初心者の方でも安心して取り組めるよう、環境構築から動作確認まで、ステップバイステップで解説していきます。
背景
検索 QA システムの重要性
企業の社内ドキュメント、製品マニュアル、FAQ、技術資料など、私たちの周りには膨大なテキストデータが存在します。これらの情報に素早くアクセスできれば、カスタマーサポートの効率化や、社内の知識共有が大幅に改善されるでしょう。
従来の全文検索エンジンは「どの文書に情報があるか」を教えてくれますが、「答えそのもの」を示してくれるわけではありません。ユーザーは検索結果を自分で読み、必要な情報を探し出す必要があります。
Haystack が提供する価値
Haystack は、自然言語処理(NLP)と情報検索技術を組み合わせ、質問に対してピンポイントで答えを返すシステムを構築できるフレームワークです。
以下の図は、Haystack を使った検索 QA システムの全体像を示しています。
mermaidflowchart TB
user["ユーザー"] -->|"質問:<br/>「Haystackとは?」"| pipeline["Haystack<br/>パイプライン"]
pipeline -->|1. 候補文書を検索| retriever["Retriever<br/>(文書検索)"]
retriever -->|2. 関連文書を抽出| docstore[("Document<br/>Store")]
docstore -->|絞り込まれた<br/>文書群| reader["Reader<br/>(回答抽出)"]
reader -->|3. 正確な回答| pipeline
pipeline -->|"回答:<br/>「オープンソースのNLPフレームワーク」"| user
図から読み取れる要点:
- ユーザーの質問はパイプラインで処理される
- Retriever が大量の文書から関連文書を高速検索
- Reader が絞り込まれた文書から正確な答えを抽出
- 2 段階処理により、精度とスピードを両立
Haystack のエコシステムには、Elasticsearch、FAISS、Transformer ベースの QA モデルなど、さまざまなコンポーネントが組み込まれており、柔軟にカスタマイズできます。
課題
従来の検索システムの限界
一般的な全文検索エンジンは、キーワードマッチングを基本とするため、以下のような課題があります。
| # | 課題 | 詳細 |
|---|---|---|
| 1 | キーワード依存 | 同義語や表記ゆれに対応しにくい |
| 2 | 結果が文書単位 | ユーザーは長い文書を読んで答えを探す必要がある |
| 3 | 回答の精度 | 質問の意図を理解せず、関連性の低い結果も返す |
| 4 | スケーラビリティ | 文書が増えると検索精度が低下しやすい |
例えば「Haystack はどんなフレームワークですか?」と質問した場合、従来の検索では「Haystack」を含むページ一覧が返ってきます。しかし本当に知りたいのは「オープンソースの NLP フレームワーク」という答えそのものですよね。
QA システム構築の難しさ
自前で質問応答システムを構築しようとすると、以下のような技術的ハードルがあります。
機械学習モデルの選定と学習には専門知識が必要です。BERT や RoBERTa などの Transformer モデルを fine-tuning し、質問応答タスクに最適化する作業は、初心者にとって大きな壁となります。
また、大規模なドキュメント群を効率的に検索するためのインフラ構築も容易ではありません。全文検索エンジンの導入、インデックス設計、クエリの最適化など、多岐にわたる作業が必要です。
さらに、検索コンポーネントと機械学習モデルを統合し、エンドツーエンドのパイプラインとして動作させるには、アーキテクチャ設計とシステム統合の経験が求められます。
Haystack はこれらの課題を解決し、最小限のコードで実用的な QA システムを構築できる環境を提供してくれるのです。
解決策
Haystack のアーキテクチャ
Haystack は、検索 QA システムをRetrieverとReaderという 2 つの主要コンポーネントに分割し、効率的な処理を実現します。
以下の図は、Retriever と Reader がどのように連携して動作するかを示しています。
mermaidsequenceDiagram
participant U as ユーザー
participant P as パイプライン
participant R as Retriever
participant D as DocumentStore
participant Re as Reader
U->>P: 質問を送信
P->>R: 関連文書を検索
R->>D: クエリ実行
D-->>R: 候補文書(Top-N)
R-->>P: 絞り込まれた文書
P->>Re: 文書と質問を渡す
Re->>Re: 機械学習モデルで<br/>回答を抽出
Re-->>P: 回答と確信度
P-->>U: 最終回答を返す
処理フローの要点:
- Retriever が高速に候補文書を絞り込み(数千件 → 数十件)
- Reader が精密に答えを抽出(重い処理を少数の文書に限定)
- 2 段階処理で速度と精度のバランスを実現
Retriever:高速な文書検索
Retriever の役割は、質問に関連する文書を Document Store から高速に取得することです。
Haystack では以下のような Retriever が利用できます。
| # | Retriever タイプ | 特徴 | 用途 |
|---|---|---|---|
| 1 | BM25Retriever | キーワードベースの検索 | 高速、軽量、シンプル |
| 2 | EmbeddingRetriever | ベクトル類似度検索 | セマンティック検索、多言語対応 |
| 3 | DensePassageRetriever | BERT ベースの密ベクトル検索 | 高精度、学習済みモデル利用 |
本記事では、最もシンプルで高速な BM25Retriever を使用します。BM25 は TF-IDF を拡張したランキングアルゴリズムで、検索エンジンで広く使われている手法です。
Reader:正確な回答抽出
Reader は、Retriever が取得した文書から、質問に対する正確な答えを抽出します。
内部では、事前学習済みの Transformer モデル(BERT、RoBERTa など)を使用し、文書中の「開始位置」と「終了位置」を予測することで、答えとなるテキストスパンを特定します。
Haystack では FARMReader や TransformersReader といった Reader が提供されており、Hugging Face の事前学習済みモデルをそのまま利用できます。
パイプラインで統合
Retriever と Reader を Pipeline で接続することで、質問から回答までの処理を自動化できます。
Pipeline は以下のような利点をもたらします。
まず、各コンポーネントの入出力を自動的に接続してくれるため、データの受け渡しを意識する必要がありません。また、複数のコンポーネントを柔軟に組み合わせられるため、システムの拡張が容易です。
さらに、REST API としてデプロイする際にも、Pipeline をそのまま利用できるため、本番運用への移行がスムーズに行えます。
具体例
それでは、実際に Haystack を使って最小の検索 QA システムを構築していきましょう。作業時間は約 30 分を想定しています。
環境構築
Python 環境の準備
Haystack は Python 3.7 以上で動作します。まず、仮想環境を作成しましょう。
bash# 仮想環境を作成
python -m venv haystack-env
# 仮想環境を有効化(macOS/Linux)
source haystack-env/bin/activate
# 仮想環境を有効化(Windows)
haystack-env\Scripts\activate
仮想環境を作成することで、プロジェクトごとに独立したパッケージ管理が可能になります。他のプロジェクトとの依存関係の競合を避けられるため、開発環境を清潔に保てます。
Haystack のインストール
次に、Haystack と必要な依存パッケージをインストールします。
bash# Haystack と Document Store 用パッケージをインストール
pip install farm-haystack[inference]
[inference] オプションを指定することで、推論に必要な最小限のパッケージがインストールされます。より高度な機能(PDF 処理、OCR など)が必要な場合は、追加のオプションを指定することもできます。
インストールには数分かかる場合があります。コーヒーでも飲みながら待ちましょう。
インストールの確認
インストールが完了したら、正しく動作するか確認します。
python# Python インタラクティブシェルを起動
python
# Haystack のバージョンを確認
import haystack
print(haystack.__version__)
バージョン番号が表示されれば、インストールは成功です。執筆時点での最新版は 1.x 系ですが、バージョンによって API が異なる場合があるため、公式ドキュメントも併せて確認することをおすすめします。
Document Store の準備
InMemoryDocumentStore の初期化
Document Store は、検索対象となる文書を保存する場所です。本記事では、最もシンプルな InMemoryDocumentStore を使用します。
pythonfrom haystack.document_stores import InMemoryDocumentStore
# メモリ内に Document Store を作成
document_store = InMemoryDocumentStore(use_bm25=True)
use_bm25=True を指定することで、BM25 アルゴリズムによる検索が有効になります。InMemoryDocumentStore はデータベース不要で、セットアップが非常に簡単です。
ただし、メモリ上にデータを保持するため、プログラムを終了するとデータは消えてしまいます。本番環境では Elasticsearch や FAISS などの永続化可能な Document Store の利用を検討しましょう。
サンプルドキュメントの準備
次に、検索対象となるドキュメントを準備します。
python# サンプルドキュメントのリスト
documents = [
{
"content": "Haystack は deepset 社が開発したオープンソースの NLP フレームワークです。質問応答システムを簡単に構築できます。",
"meta": {"source": "intro"}
},
{
"content": "Retriever はドキュメントストアから関連する文書を高速に検索するコンポーネントです。BM25 や Embedding ベースの手法があります。",
"meta": {"source": "retriever"}
},
{
"content": "Reader は Retriever が取得した文書から、質問に対する正確な回答を抽出します。BERT などの Transformer モデルを使用します。",
"meta": {"source": "reader"}
},
]
各ドキュメントは content(本文)と meta(メタデータ)を持ちます。メタデータには、ソース情報や日付、カテゴリなど、任意の情報を格納できます。
実際の運用では、PDF、HTML、テキストファイルなど、さまざまな形式のドキュメントを読み込むことになるでしょう。
Document Store への書き込み
準備したドキュメントを Document Store に書き込みます。
python# ドキュメントを Document Store に追加
document_store.write_documents(documents)
# 登録されたドキュメント数を確認
print(f"登録ドキュメント数: {document_store.get_document_count()}")
write_documents() メソッドは、ドキュメントのリストを受け取り、一括で Document Store に保存します。大量のドキュメントを扱う場合は、バッチ処理で分割して書き込むことをおすすめします。
この時点で、検索可能なドキュメント群が準備できました。
Retriever の設定
BM25Retriever の初期化
BM25Retriever を作成し、Document Store と接続します。
pythonfrom haystack.nodes import BM25Retriever
# Retriever を初期化
retriever = BM25Retriever(document_store=document_store)
BM25Retriever は、TF-IDF を拡張したランキングアルゴリズムで、文書の長さや用語の頻度を考慮した高精度な検索を実現します。
Retriever の初期化は非常にシンプルで、Document Store を渡すだけで完了します。内部で自動的にインデックスが構築され、検索可能な状態になります。
Retriever の動作確認
Retriever が正しく動作するか、テスト検索を実行してみましょう。
python# テスト検索を実行
query = "Haystack とは何ですか?"
retrieved_docs = retriever.retrieve(query=query, top_k=2)
# 取得した文書を表示
for doc in retrieved_docs:
print(f"スコア: {doc.score:.3f}")
print(f"内容: {doc.content}\n")
top_k=2 を指定することで、関連度が高い上位 2 件の文書を取得します。スコアは関連度を示す値で、高いほど質問との関連性が強いことを意味します。
実行すると、「Haystack は deepset 社が開発した...」という文書が最上位に返ってくるはずです。Retriever が質問に関連する文書を正しく見つけられていることが確認できますね。
Reader の設定
TransformersReader の初期化
次に、Reader を設定します。ここでは Hugging Face の事前学習済みモデルを使用する TransformersReader を利用します。
pythonfrom haystack.nodes import FARMReader
# Reader を初期化(日本語対応モデルを使用)
reader = FARMReader(
model_name_or_path="deepset/roberta-base-squad2",
use_gpu=False # GPU がない環境では False に設定
)
deepset/roberta-base-squad2 は、質問応答タスクで学習済みの RoBERTa モデルです。初回実行時は、モデルのダウンロードが行われるため、数分かかる場合があります。
日本語の質問応答には、cl-tohoku/bert-base-japanese-whole-word-masking などの日本語特化モデルを使用することもできますが、本記事では英語圏で広く使われているモデルで進めます。
use_gpu=False を指定すると、CPU で推論が実行されます。GPU がある環境では True に設定すると、処理速度が大幅に向上します。
Reader の動作確認
Reader 単体でテストしてみましょう。
python# Reader で回答を抽出
answers = reader.predict(
query=query,
documents=retrieved_docs,
top_k=1 # 上位 1 件の回答を取得
)
# 回答を表示
for answer in answers["answers"]:
print(f"回答: {answer.answer}")
print(f"確信度: {answer.score:.3f}")
print(f"文脈: {answer.context}\n")
Reader は、Retriever が取得した文書群の中から、質問に対する答えとなるテキストスパンを抽出します。確信度(score)は、その回答がどれだけ正確かを示す指標です。
実行すると、「オープンソースの NLP フレームワーク」といった回答が抽出されるはずです。文脈(context)には、回答を含む周辺のテキストが含まれます。
パイプラインの構築
ExtractiveQAPipeline の作成
Retriever と Reader を統合し、エンドツーエンドの QA パイプラインを構築します。
pythonfrom haystack.pipelines import ExtractiveQAPipeline
# パイプラインを作成
pipeline = ExtractiveQAPipeline(reader=reader, retriever=retriever)
ExtractiveQAPipeline は、質問を受け取ると、内部で以下の処理を自動実行します。
まず、Retriever を使って関連文書を検索します。次に、取得した文書を Reader に渡し、回答を抽出します。最後に、回答と確信度、ソース文書をまとめて返します。
わずか 1 行で、複雑な処理フローを抽象化できるのは驚きですよね。
パイプラインの実行
それでは、実際に質問を投げてみましょう。
python# 質問を実行
result = pipeline.run(
query="Retriever の役割は何ですか?",
params={
"Retriever": {"top_k": 3}, # 上位 3 件の文書を取得
"Reader": {"top_k": 1} # 上位 1 件の回答を抽出
}
)
# 結果を表示
print("質問:", result["query"])
print("\n【回答】")
for answer in result["answers"]:
print(f" 答え: {answer.answer}")
print(f" 確信度: {answer.score:.3f}")
print(f" ソース: {answer.meta['source']}\n")
params で各コンポーネントのパラメータを個別に指定できます。top_k を調整することで、検索精度と処理速度のバランスを調整できます。
実行すると、「ドキュメントストアから関連する文書を高速に検索する」といった回答が返ってくるはずです。質問に対してピンポイントで答えが得られましたね。
エラー処理とデバッグ
実際の運用では、さまざまなエラーに遭遇する可能性があります。代表的なケースと対処法を紹介します。
エラー 1: モデルのダウンロード失敗
エラーコード: OSError: Can't load config for 'deepset/roberta-base-squad2'
エラーメッセージ:
vbnetOSError: Can't load config for 'deepset/roberta-base-squad2'.
Make sure that 'deepset/roberta-base-squad2' is a correct model identifier
listed on 'https://huggingface.co/models'
発生条件: ネットワーク接続が不安定、または Hugging Face Hub にアクセスできない
解決方法:
- インターネット接続を確認する
- プロキシ設定が必要な環境では、環境変数
HTTP_PROXYとHTTPS_PROXYを設定する - 事前にモデルをダウンロードし、ローカルパスを指定する
python# ローカルモデルを使用する場合
reader = FARMReader(model_name_or_path="/path/to/local/model")
エラー 2: メモリ不足
エラーコード: RuntimeError: CUDA out of memory
エラーメッセージ:
sqlRuntimeError: CUDA out of memory. Tried to allocate 1.50 GiB
(GPU 0; 7.79 GiB total capacity; 6.12 GiB already allocated)
発生条件: GPU メモリが不足している
解決方法:
use_gpu=Falseに設定し、CPU で実行する- バッチサイズを小さくする
- より軽量なモデルを使用する(
distilbert-base-uncased-distilled-squadなど)
python# 軽量モデルを使用
reader = FARMReader(
model_name_or_path="distilbert-base-uncased-distilled-squad",
use_gpu=False
)
エラー 3: 回答が見つからない
質問に対して回答が返ってこない場合、以下の点を確認しましょう。
| # | チェック項目 | 対処法 |
|---|---|---|
| 1 | Document Store が空 | get_document_count() でドキュメント数を確認 |
| 2 | Retriever の top_k が小さい | 値を増やして関連文書を多く取得 |
| 3 | 質問とドキュメントの言語が不一致 | 言語に合ったモデルを使用 |
| 4 | ドキュメントに答えが含まれない | コンテンツを見直す |
デバッグ時は、各ステップの出力を確認することが重要です。Retriever が正しく文書を取得できているか、Reader に渡される文書が適切かを、段階的にチェックしていきましょう。
応用例:複数質問の一括処理
複数の質問を効率的に処理したい場合は、ループで実行できます。
python# 質問リスト
questions = [
"Haystack とは何ですか?",
"Retriever の役割は?",
"Reader は何をしますか?"
]
# 一括処理
for q in questions:
result = pipeline.run(query=q, params={"Retriever": {"top_k": 2}})
print(f"Q: {q}")
print(f"A: {result['answers'][0].answer}\n")
このコードは、質問リストを順次処理し、各質問に対する回答を表示します。カスタマーサポートの FAQ 自動応答や、社内 Q&A ボットなどに応用できます。
大量の質問を処理する場合は、並列処理や非同期処理を検討すると、さらに高速化できるでしょう。
まとめ
本記事では、Haystack を使った最小の検索 QA システムを、わずか 30 分程度で構築する方法をご紹介しました。
重要なポイント:
Retriever と Reader の 2 段階構成により、高速かつ高精度な質問応答が実現できます。BM25Retriever で関連文書を絞り込み、FARMReader で正確な回答を抽出する流れは、シンプルでありながら実用的です。
InMemoryDocumentStore を使えば、データベース不要で手軽に始められます。本番環境では Elasticsearch や FAISS など、スケーラブルな Document Store への移行も容易です。
ExtractiveQAPipeline は、複雑な処理フローを抽象化し、わずか数行のコードで QA システムを構築できる強力なツールです。
Haystack のエコシステムには、本記事で紹介した以外にも、文書の前処理(Preprocessor)、評価(Evaluation)、REST API 化(REST API)など、豊富な機能が用意されています。
まずは本記事のコードを実際に動かし、自分のドキュメントで試してみてください。実際に動くシステムを手にすることで、さらなる学習のモチベーションが高まるはずです。
次のステップとしては、以下のような拡張に挑戦してみるのも良いでしょう。
| # | 拡張項目 | 説明 |
|---|---|---|
| 1 | Document Store の変更 | Elasticsearch や FAISS を使った本格的な検索基盤 |
| 2 | Retriever の高度化 | Embedding ベースの Retriever でセマンティック検索を実現 |
| 3 | 日本語対応 | 日本語 BERT モデルの導入と日本語ドキュメントでの検証 |
| 4 | REST API 化 | FastAPI と組み合わせて Web API として公開 |
| 5 | UI の追加 | Streamlit や Gradio で対話的な UI を構築 |
Haystack は活発に開発が続けられており、コミュニティも非常に活発です。公式ドキュメントやチュートリアル、GitHub の Issue などを参考にしながら、自分だけの QA システムを作り上げてみてはいかがでしょうか。
関連リンク
articleHaystack で最小の検索 QA を作る:Retriever + Reader の 30 分ハンズオン
articleHaystack で RAG アーキテクチャ設計:ハイブリッド検索と再ランキングの最適解
articleHaystack チートシート:主要クラス・関数・CLI コマンド一枚まとめ
articleHaystack のインストール完全ガイド:Python 環境・GPU・依存ライブラリの最短手順
articleHaystack とは?RAG 検索 × 生成 AI を実務投入するための完全入門【2025 年版】
articleHaystack で最小の検索 QA を作る:Retriever + Reader の 30 分ハンズオン
articleJest のフレークテスト撲滅作戦:重試行・乱数固定・リトライ設計の実務
articleGitHub Copilot セキュア運用チェックリスト:権限・ポリシー・ログ・教育の定着
articleGrok で社内 FAQ ボット:ナレッジ連携・権限制御・改善サイクル
articleGitHub Actions ランナーのオートスケール運用:Kubernetes/actions-runner-controller 実践
articleClips AI で書き出しが止まる時の原因切り分け:メモリ不足・コーデック・権限
blogiPhone 17シリーズの発表!全モデルiPhone 16から進化したポイントを見やすく整理
blogGoogleストアから訂正案内!Pixel 10ポイント有効期限「1年」表示は誤りだった
blog【2025年8月】Googleストア「ストアポイント」は1年表記はミス?2年ルールとの整合性を検証
blogGoogleストアの注文キャンセルはなぜ起きる?Pixel 10購入前に知るべき注意点
blogPixcel 10シリーズの発表!全モデル Pixcel 9 から進化したポイントを見やすく整理
blogフロントエンドエンジニアの成長戦略:コーチングで最速スキルアップする方法
review今の自分に満足していますか?『持たざる者の逆襲 まだ何者でもない君へ』溝口勇児
reviewついに語られた業界の裏側!『フジテレビの正体』堀江貴文が描くテレビ局の本当の姿
review愛する勇気を持てば人生が変わる!『幸せになる勇気』岸見一郎・古賀史健のアドラー実践編で真の幸福を手に入れる
review週末を変えれば年収も変わる!『世界の一流は「休日」に何をしているのか』越川慎司の一流週末メソッド
review新しい自分に会いに行こう!『自分の変え方』村岡大樹の認知科学コーチングで人生リセット
review科学革命から AI 時代へ!『サピエンス全史 下巻』ユヴァル・ノア・ハラリが予見する人類の未来