Haystackとは?3.0の破壊的変更・パイプライン設計・LangChainとの選び分け【2026年版】
Haystackは、ドイツのdeepsetが開発するLLMアプリケーション向けのオーケストレーションフレームワークです。パッケージ名はhaystack-ai、ライセンスはApache-2.0、要求Pythonは3.10以上。GitHubのスター数は26,069(2026年7月31日時点)。2026年7月20日に3.0.0が出て、AsyncPipelineの廃止、レガシーGeneratorの削除、約30コンポーネントの外部パッケージ移設という広い範囲の破壊的変更が入りました。この記事では、パイプラインの構造、RAG実装のコードの形、3.0からの書き換え方、Agentのフック設計、LangChainやLlamaIndexとの選び分けを実装視点で整理します。
まとめ:Haystackを選ぶ条件と3.0で先に決めること
Haystackを採用する理由は4つに絞れます。1つ目は、コンポーネント同士を出力名と入力名で明示的につなぐ設計で、処理経路がコードから読み取れること。2つ目は、接続の型整合をconnect()の時点で検証するため、実行前に配線の誤りが落ちること。3つ目は、Document Storeが外部統合として切り出され、ベクトルDBを差し替えても検索側の構造が変わらないこと。4つ目はApache-2.0で、顧客環境へ組み込む受託案件でもライセンス上の制約が軽い点です。
見送る条件もはっきりしています。日本語の実装記事の量ならLangChain側が厚く、文書の索引構造に踏み込みたいならLlamaIndexのほうが道具が揃っています。そして3.0.0は公開から日が浅い版。既存の2.x本番があるなら、2.31.0(2026年7月8日)で止めたまま検証環境で3.0を通し、移設パッケージと許可リストの2点を確認してから移す順序を勧めます。
Haystackの定義とコンポーネント接続で組むパイプライン指向の設計
Haystackは「RAGを作るライブラリ」と説明されがちですが、実体はもう少し手前にあります。入力と出力に型を持った部品を並べ、どの出力をどの入力へ渡すかを宣言する実行基盤です。RAGはその上に載る構成のひとつにすぎません。
コンポーネントを出力名と入力名で明示接続するパイプラインの構造
最小単位はコンポーネントです。文書を分割する、埋め込みを作る、検索する、プロンプトを組む、モデルを呼ぶ。それぞれが独立したクラスで、入力と出力のソケットを持ちます。登録はadd_component(name, component)、配線はconnect("producer.output_name", "consumer.input_name")の2操作だけ。
ここで効くのが検証のタイミングです。connect()を呼んだ瞬間に、コンポーネントが登録済みか、指定した出力と入力が存在するか、型が噛み合うか、その入力がすでに埋まっていないか(Variadic型は例外)が確かめられます。配線ミスが実行前に例外として現れるため、APIを呼んで課金してから型の不一致に気づく事故が起きません。ループも上限付きで許容され、生成結果を検証コンポーネントへ通して不合格なら戻す自己修正の形が組めます。
Document Storeを外部統合として切り出す前提と差し替えの限界
文書の保存先はDocument Storeという抽象で分離されています。手元の検証にはインメモリ実装があり、本番ではElasticsearch、pgvector、Qdrant、Chroma、FAISS、Neo4j、Vespaといった統合を選びます。MilvusやPineconeも含め、一覧は2026年7月31日時点で30件です。
実務で効くのは、保存先を替えてもretrieverの差し替えで済む点でしょう。PoCをインメモリで通し、本番でマネージド型へ寄せる移行が現実的な工数に収まります。ただしretrieverのクラス名は保存先ごとに異なり、埋め込みの次元数と距離指標も保存先の設定に依存するため、完全な無改修とはいきません。RAGの仕組みを押さえ、どこまでを抽象に任せるか先に線を引いてください。
3.0で入った破壊的変更と2.x系コードからの具体的な書き換え方
日本語の解説記事はほぼすべて2.x前提で、3.0.0の変更点まで扱ったものは2026年7月31日時点で見当たりません。移行時に手が止まる箇所を、書き換え方まで含めて順に押さえます。
AsyncPipelineの廃止とPipelineへの統合で変わるimportと書き方
2.xでは同期用のPipelineと非同期用のAsyncPipelineが別クラスでした。3.0で後者は消え、Pipelineがrun()とrun_async()の両方を持ちます。加えてrun_async_generator()とstream()が入りました。移行はimport文とクラス名の置換で足り、同期と非同期で定義を二重に持っていたコードは1本へ寄せられます。Pipeline.stream()はStreamingChunkを生成され次第返すため、チャット系UIの初回表示までの待ち時間を短くする用途に向くでしょう。
レガシーGeneratorの削除とChat Generatorへ寄せるときの手当て
OpenAIGenerator、AzureOpenAIGenerator、HuggingFaceAPIGenerator、HuggingFaceLocalGeneratorが削除されました。いずれもChat Generator側の同等品へ置き換えます。文字列を投げて文字列を受け取る形から、ChatMessageのリストをやり取りする形へ変わるため、PromptBuilderもChatPromptBuilderへ寄せる必要があります。DALLEImageGeneratorはOpenAIImageGeneratorへ改名。ツール呼び出しもマルチターンもチャット形式が前提である以上、3.0はその二重構造を畳んだと読むのが実態に近いでしょう。
約30コンポーネントの外部パッケージ移設で増えるimportの書き換え
これが移行作業でいちばん手数を食います。約30のコンポーネントがhaystack-core-integrations配下の独立パッケージへ移りました。例を挙げます。
- SentenceTransformersTextEmbedder → sentence-transformers-haystack
- HuggingFaceAPIChatGenerator → huggingface-api-haystack
- AzureOCRDocumentConverter → azure-form-recognizer-haystack
- TikaDocumentConverter → tika-haystack
- LocalWhisperTranscriber → whisper-haystack
importパスはhaystack.components.embeddersからhaystack_integrations.components.embedders.sentence_transformersのように変わり、requirements側への追加とimport書き換えの両方が要ります。ローカルの埋め込みをSentenceTransformersで回している構成は必ず該当するので、見積もりで最初に洗い出す対象です。
許可制デシリアライズが既存のYAMLパイプライン運用へ与える影響
パイプライン定義をYAMLで持ち、実行時に読み込む運用は3.0で挙動が変わります。デシリアライズが信頼モジュールの許可制になり、既定ではhaystack、haystack_integrations、haystack_experimental、builtins、typing、collectionsだけが解決対象。自作コンポーネントを別モジュールに置く構成は、許可リストへ加えない限り読み込みに失敗します。
あわせてeval、exec、open、getattrといった危険なビルトインは、builtinsが許可されていても解決できなくなりました。信頼できるデータに限りunsafe=Trueを渡す逃げ道は残るものの、外部から受け取った定義に使う選択肢ではありません。許可リストを迂回できる脆弱性の修正も入っています。
Document.idの生成方式の変更とインデックス再構築を決める判断
見落とすと静かに壊れるのがここです。Documentのidを自動生成するハッシュが、metaをキー整列したJSONで直列化する方式へ変わりました。メタ情報が空でない文書は2.xと3.0で別のidになります。既存のベクトルDBの文書とidが一致しないため、重複排除や差分更新をid基準で回す処理は動きません。インデックスを作り直すか、idを自前で採番する方式へ切り替えるかを先に決めてください。
あわせて外部資源の生成が__init__()からwarm_up()へ移り、close()による後始末も実装対象になりました。自作コンポーネントのコンストラクタにHTTPクライアントやDB接続を置いているなら、そちらの書き換えも要ります。
RAGパイプラインを索引側と検索側に分けて実装する手順とコードの形
3.0の前提で構成を見ます。索引側と検索側で別のパイプラインを組むのが定石です。
索引側:変換・分割・埋め込み・書き込みを一列につなぐ組み立て方
索引側は、ファイルを読み込むConverter、文書を分割するDocumentSplitter、ベクトルを作るDocumentEmbedder、Document Storeへ書くDocumentWriterを一列につなぎます。connect("converter.documents", "splitter.documents")のように、出力名と入力名を明示して並べていくだけの形です。
精度を左右するのは分割の粒度で、ここはフレームワークが決めてくれません。チャンク分割の考え方を踏まえ、文書構造に合わせて長さと重なりを決める作業が要ります。契約書のように条項単位で意味が閉じる文書と、マニュアルのように前後の文脈を引きずる文書では同じ設定が通用しません。DocumentSplitterは単語・文・段落から単位を選べます。まず段落単位で通してから詰める進め方が現実的でしょう。
検索側:retrieverとChatPromptBuilderを並べる構成
検索側は、質問文をベクトル化するTextEmbedder、Document Storeを引くretriever、取得文書をプロンプトへ差し込むChatPromptBuilder、モデルを呼ぶChatGeneratorという並びです。テンプレートはJinja2で、ここに3.0の変更がひとつ効きます。変数が既定で必須になったため、条件によっては渡されない変数を置いていると実行時に落ちるようになりました。2.xの挙動へ戻すにはrequired_variables=Noneを明示します。なお3.0で入ったinsertタグを使えば、固定のシステムプロンプトの間へ会話履歴だけを差し込む構成も書けます。
検索の質を上げる段では、ベクトル検索だけに寄せない構成を検討する価値があります。BM25とベクトル検索を統合するハイブリッド検索はHaystackでも組めて、複数のretrieverの出力をDocumentJoinerでまとめる形。型番や社内用語のように表記の一致が効くクエリでは、この構成の有無で体感が変わります。
Agentのフックとツール結果オフロードで組むエージェント構成の判断
3.0でいちばん設計思想が出ているのがAgentまわりです。ツール実行を担っていたToolInvokerが削除され、Agentが端から端まで持つようになりました。実行結果は常にjson.dumps()で直列化されます。2.xはPythonのstr()表現で、辞書がシングルクォートのまま渡る不安定さがありました。
6つのフック位置と人手確認をbefore_toolへ寄せる設計の考え方
Agentにはbefore_run、before_llm、before_tool、after_tool、on_exit、after_runの6か所にフックを差し込めます。ガードレールや人手確認の割り込みを、内部を書き換えずに外側から足せる設計です。
2.xにあったconfirmation_strategiesという専用の仕組みは廃止され、人手確認はbefore_toolフックで表現します。haystack.hooks.human_in_the_loopのConfirmationHookを使う形。ツール単位で承認を挟むか素通しにするかを一箇所に書けるため、承認要否の一覧性が上がりました。破壊的な操作を伴うツールでは、この位置に承認と監査ログを置くのが扱いやすいでしょう。
Stateの扱いも変わり、3.0ではツール側がinputs_from_stateで読みたいキーを宣言します。書く量は増えるかわりに、どのツールが何を参照しているかが定義から追えるようになりました。
内省出力とツール結果のオフロードでコンテキスト消費を抑える仕組み
Agentはstep_count、token_usage、tool_call_countsを自動で出力します。反復回数やトークン消費を見て打ち切る制御を、アプリ側の集計なしで書けるようになりました。暴走時の費用を上限で止める仕組みは本番投入の前提条件になりがちです。
もうひとつ、ToolResultOffloadHookが入りました。after_toolで動き、大きなツール結果を外部ストレージへ書き出し、モデルへ渡す側は短いポインタへ差し替えるフックです。検索ツールが数万トークンを返してコンテキストを食い潰す詰まりに効きます。SkillToolsetによる段階開示も同じ狙いです。
LangChain・LlamaIndexとの設計思想の違いと案件別の選び分け基準
3つは設計の重心が違います。
| 観点 | Haystack | LangChain | LlamaIndex |
|---|---|---|---|
| 組み立て方 | 出力名と入力名を指定して接続 | 連鎖とグラフで記述 | 索引とクエリエンジンが軸 |
| 配線ミスの検出 | connect時に型検証 | 実行時に判明しやすい | 実行時に判明しやすい |
| 得意領域 | 検索と生成の本番運用 | 広い統合数と事例量 | 文書の索引構造と取り込み |
| 日本語情報の量 | 少なめ | 多い | 中程度 |
選び分けの基準はこう置けます。パイプラインの構造をコードとして読め、検索の経路を自分で握りたいならHaystack。統合数や参考実装の見つけやすさで工数を削りたいならLangChain。文書の取り込みと索引の作り方に踏み込むならLlamaIndexが近道です。エージェント構成まで含めた軸で候補を絞るなら、主要フレームワーク8種の比較を先に通したほうが早いでしょう。併用も成立しますが、依存が二重になるぶん版上げの手間も二重になります。
本番運用で効く観測・テスト・デプロイと版を据えるときの決めどころ
トレースが自動で入らなくなった点とダッシュボード側の作り直し
2.xはトレーシングが自動で有効でしたが、3.0では明示が要ります。OpenTelemetryConnectorかDatadogConnectorを置くか、tracing.enable_tracing()を呼ぶかのどちらか。span構造も変わり、chat generatorとtool invokerに分かれていたものがhaystack.agent.stepへまとまりました。既存のダッシュボードをspan名で組んでいるなら作り直しが要ります。移行後にトレースが消えて気づく順序になりがちなので、チェックリストへ入れておいてください。
Mockコンポーネントで配線の回帰をCIに載せるテストの組み立て
MockChatGenerator、MockTextEmbedder、MockDocumentEmbedderが標準で入りました。決定的な結果を返し、APIも呼ばず費用も出ません。配線とデータ整形が正しいかを、モデルの揺らぎと切り離して確かめられます。生成品質の評価は別建てが要るものの、配線の回帰はこれで押さえられるでしょう。
3.0を選ぶか2.31.0で据え置くかを分ける移行コストの見積もり
本番へ載せる段では、3.0.0を選ぶか2.31.0で止めるかの判断が先に来ます。新規案件なら3.0からで構いません。移設パッケージのimportを最初から新しいパスで書けばよく、レガシーGeneratorも知らずに済みます。
既存案件は移行コストの見積もりを先に出してください。SentenceTransformers系の埋め込みを使っている、定義をYAMLで外部化している、Documentのidを自前ロジックで参照している。この3つのいずれかに当てはまるなら作業は1日では終わりません。統合パッケージ側の対応が出揃ってから動く判断も合理的でしょう。RAG構築支援では、版の据え方と移行範囲の切り分けを含めて設計から引き受けています。
Haystackの採用判断:選ぶ4つの条件と見送るべき3つの場面の線引き
採用する条件は4つ。第1に、検索の経路を自分で設計したい案件であること。retrieverの構成、ハイブリッド検索の混ぜ方、再ランクの位置を明示的に握れます。第2に、構造をコードから読めることが引き渡し要件に含まれること。保守を引き継ぐ側が処理経路を追えるかは後々の費用に直結します。第3に、配線の誤りを実行前に落としたいこと。第4に、Apache-2.0が顧客側の要件に噛み合うこと。
見送る場面は3つ。1つ目は、プロンプトを1回呼ぶだけの機能。パイプラインの抽象は過剰投資になります。2つ目は、実装者が日本語の参考情報に強く依存する体制。英語のリリースノートを読む前提が置けないなら学習コストが工数を押し上げます。3つ目は、既存の2.x本番があって移行の余力がない状況です。
新規案件でLangChainとHaystackのどちらかで迷うなら、そのシステムを2年後に誰が保守するかで決めてください。自社の同じチームが持ち続けるなら統合数の広さが効き、顧客側へ引き渡すなら構造が明示されている側が効きます。
よくある質問
Haystack 2.xのコードは3.0でそのまま動きますか?
動きません。AsyncPipelineの廃止、レガシーGeneratorの削除、約30コンポーネントの移設という3点だけでも、多くのコードがimportの段階で失敗します。Jinja2変数の必須化とStateの暗黙注入の廃止は、実行時まで表面化しない差分です。まず検証環境でimportエラーを潰してください。
Haystackは日本語の文書でも精度が出ますか?
密ベクトル側の精度は埋め込みモデルで決まるため、日本語に対応したモデルを選べば問題は出ません。差が出るのは疎ベクトル側で、BM25を併用する場合はDocument Store側のトークナイザ設定が効きます。既定のままだと日本語が分かち書きされず、キーワード一致がほとんど機能しません。分割単位も実データで確認してから決めてください。
パイプライン定義をYAMLで持つ運用は3.0でも続けられますか?
続けられますが、設定の追加が要ります。デシリアライズが許可制になったため、自作コンポーネントを別モジュールに置いている構成では、そのモジュールを明示的に許可リストへ加える必要があります。eval・exec・open・getattrはbuiltinsが許可されていても解決できません。外部から受け取った定義をそのまま読み込む設計なら、方式ごと見直してください。
ToolInvokerが削除されましたが既存のツール実装は使えますか?
ツール自体の定義は使えます。変わるのは実行の担い手で、Agentがツール実行を持つようになりました。結果の直列化がstr()からjson.dumps()へ変わったため、戻り値をモデル側でパースする前提のプロンプトは表記の変化を確認しておくと安全です。async defで書いたツールは@toolデコレータがasync_functionへ振り分けます。
OSS版とdeepsetのマネージド側はどちらでPoCすべきですか?
コードで構造を握る前提の案件なら、OSS版のhaystack-aiから入るほうが後の移植性は高くなります。マネージド側は運用と権限管理まで任せられる反面、構造や版の上げ方を自分の判断で決めにくくなるでしょう。顧客環境へ引き渡す前提なら、まずOSS版で構造を固めてください。
関連記事
- LlamaIndexとは?RAG構築の仕組み・LangChainとの違いと採用判断を実装者向けに解説
- AIエージェントフレームワークとは?主要8種の比較と受託開発視点の選定基準【2026年版】
- LangChainとLangGraphの違い|v1.0で逆転した関係と使い分け
- Semantic Kernelとは|Microsoftの生成AIオーケストレーションSDKの仕組みとC#実装
- ハイブリッド検索とは?RAGでBM25とベクトル検索を統合する仕組み・RRF・実装コード
- チャンク分割とは?チャンクとトークンの違いから代表的な手法まで
- ChromaDBとは?アーキテクチャ・使い方・pgvectorとの違いまで
- Weaviateとは?ハイブリッド検索・マルチテナンシー・料金と選定基準
- RAG構築の手順とは?データ整備から精度向上・本番運用までの進め方