Haystack 1.x vs 2.0:推倒重来

Haystack 2.0 不是渐进式升级,是架构重写。deepset 团队总结了 1.x 的所有教训,推倒重建:

维度Haystack 1.xHaystack 2.0
核心抽象Node(黑盒)Component(白盒,类型安全)
连接方式隐式管道连接显式 connect() 方法
类型系统无,靠字典传递有,输入输出类型检查
路由基于字段名自动路由显式连接,可视化路由
扩展性继承 BaseComponent实现协议,组合优先
测试困难(需整个 Pipeline)组件可独立测试
序列化有限YAML 配置,完整序列化

1.x 最大的问题是黑盒太多。一个 FARMReader 封装了模型加载、预处理、预测、后处理,你想改其中一步就得继承重写。2.0 把这些全拆开,每个步骤是独立的 Component。

Pipeline 架构:显式连接

Haystack 2.0 的 Pipeline 是 Component 的有向无环图(DAG):

from haystack import Pipeline
from haystack.components.converters import TextFileToDocument
from haystack.components.preprocessors import DocumentSplitter
from haystack.components.embedders import OpenAITextEmbedder, OpenAIDocumentEmbedder
from haystack.components.writers import DocumentWriter
from haystack.components.retrievers import VectorStoreRetriever
from haystack.components.generators import OpenAIGenerator
from haystack.components.builders import PromptBuilder
from haystack.document_stores.in_memory import InMemoryDocumentStore

# === 索引 Pipeline ===
indexing_pipeline = Pipeline()
indexing_pipeline.add_component("converter", TextFileToDocument())
indexing_pipeline.add_component("splitter", DocumentSplitter(split_by="word", split_length=500))
indexing_pipeline.add_component("embedder", OpenAIDocumentEmbedder(model="text-embedding-3-small"))
indexing_pipeline.add_component("writer", DocumentWriter(document_store=InMemoryDocumentStore()))

# 显式连接
indexing_pipeline.connect("converter.documents", "splitter.documents")
indexing_pipeline.connect("splitter.documents", "embedder.documents")
indexing_pipeline.connect("embedder.documents", "writer.documents")

# 运行索引
indexing_pipeline.run({"converter": {"sources": ["./data/*.txt"]}})

注意 connect() 的参数格式:"component_name.output_field""component_name.input_field"类型必须匹配——如果 splitter 输出 documentsembedder 的输入也必须是 documents,否则 Pipeline 构建时就报错。

# === 查询 Pipeline ===
query_pipeline = Pipeline()
query_pipeline.add_component("embedder", OpenAITextEmbedder(model="text-embedding-3-small"))
query_pipeline.add_component("retriever", VectorStoreRetriever(document_store=store))
query_pipeline.add_component("prompt_builder", PromptBuilder(template="""
根据以下文档回答问题。如果文档中没有答案,说"我不知道"。

文档:
{% for doc in documents %}
{{ doc.content }}
{% endfor %}

问题: {{ question }}
答案:"""))
query_pipeline.add_component("generator", OpenAIGenerator(model="gpt-4o"))

query_pipeline.connect("embedder.embedding", "retriever.query_embedding")
query_pipeline.connect("retriever.documents", "prompt_builder.documents")
query_pipeline.connect("prompt_builder.prompt", "generator.prompt")

# 查询
result = query_pipeline.run({
    "embedder": {"text": "什么是向量数据库?"},
    "prompt_builder": {"question": "什么是向量数据库?"}
})

显式连接 vs 隐式路由

Haystack 1.x 的连接靠字段名匹配——如果一个 Node 输出 documents,另一个 Node 接受 documents,就自动连上。简单时方便,复杂时噩梦:字段名冲突、意外连接、调试困难。

2.0 的显式连接虽然代码多了几行,但每个连接都是可见的、可审计的。你可以画出精确的数据流图,不会有意外的数据流向。

Component 系统:可组合、可测试

自定义 Component

from haystack import component
from haystack.dataclasses import Document

@component
class KeywordFilter:
    """按关键词过滤文档"""
    
    def __init__(self, keywords: list[str]):
        self.keywords = keywords
    
    @component.output_types(filtered_docs=list[Document])
    def run(self, documents: list[Document]) -> dict:
        filtered = [
            doc for doc in documents
            if any(kw.lower() in doc.content.lower() for kw in self.keywords)
        ]
        return {"filtered_docs": filtered}

# 使用
pipeline.add_component("keyword_filter", KeywordFilter(keywords=["AI", "Agent"]))
pipeline.connect("retriever.documents", "keyword_filter.documents")

@component 装饰器做了两件事:

  1. 注册 Component 的输入输出类型
  2. 使其可被 Pipeline 管理和连接

类型检查在构建时就生效——如果你把 filtered_docs 连到一个期望 documents 的 Component,Pipeline 会直接报错。

组件独立测试

# 不需要整个 Pipeline,直接测试单个 Component
def test_keyword_filter():
    f = KeywordFilter(keywords=["AI"])
    docs = [
        Document(content="AI is transforming software"),
        Document(content="The weather is nice today"),
    ]
    result = f.run(documents=docs)
    assert len(result["filtered_docs"]) == 1
    assert "AI" in result["filtered_docs"][0].content

这是 2.0 相比 1.x 的巨大改进——组件可以独立测试,不需要 mock 整个 Pipeline。

Connector 生态

Haystack 2.0 的 Connector 覆盖了主流存储和模型:

类型支持
向量存储Chroma, Qdrant, Weaviate, Pinecone, PGVector, Elasticsearch, OpenSearch
文档转换PDF, DOCX, HTML, Markdown, PPTX, Azure OCR
EmbedderOpenAI, Cohere, HuggingFace, Sentence Transformers, Jina
GeneratorOpenAI, Anthropic, Cohere, HuggingFace TGI, Ollama, vLLM
RankerCohere Rerank, SentenceTransformers, Lost in the Middle
# 混合使用不同供应商
from haystack.components.embedders import HuggingFaceEmbedder
from haystack.components.generators import OpenAIGenerator
from haystack.components.rankers import CohereRanker

pipeline.add_component("embedder", HuggingFaceEmbedder(model="BAAI/bge-large-en"))
pipeline.add_component("ranker", CohereRanker(api_key="...", top_k=5))
pipeline.add_component("generator", OpenAIGenerator(model="gpt-4o"))

与 LangChain 对比

维度Haystack 2.0LangChain
架构Pipeline + Component(DAG)Chain + Tool(链式)
类型安全强,构建时检查弱,运行时发现
可测试性组件独立测试需 mock 整条链
序列化YAML 配置,完整部分支持
Agent 能力基础(Agent Component)强(LangGraph)
社区规模较小最大
文档质量高,示例完整参差不齐
企业部署强(YAML + API 服务化)需自行封装

Haystack 的企业优势

1. YAML 配置驱动

# pipeline.yaml - 版本控制友好的配置
components:
  - name: embedder
    type: haystack.components.embedders.OpenAITextEmbedder
    params:
      model: text-embedding-3-small
  
  - name: retriever
    type: haystack.components.retrievers.VectorStoreRetriever
    params:
      document_store:
        type: haystack.document_stores.chroma.ChromaDocumentStore
        params:
          collection_name: "knowledge_base"
  
  - name: generator
    type: haystack.components.generators.OpenAIGenerator
    params:
      model: gpt-4o
      generation_kwargs:
        temperature: 0.1

connections:
  - sender: embedder.embedding
    receiver: retriever.query_embedding
  - sender: retriever.documents
    receiver: generator.documents
# 从 YAML 加载
from haystack import Pipeline
pipeline = Pipeline.loads(yaml.safe_load(open("pipeline.yaml")))

2. REST API 服务化

from haystack.components.connectors import OpenAIConnector
# Haystack 可直接部署为 REST API
# 通过 haystack-rest-api 或自定义 FastAPI 封装

3. 监控与可观测

Pipeline 的每一步都是显式的 Component,可以轻松添加日志、metrics、tracing:

@component
class TracingWrapper:
    def __init__(self, wrapped_component, name: str):
        self.wrapped = wrapped_component
        self.name = name
    
    def run(self, **kwargs):
        import time
        start = time.time()
        result = self.wrapped.run(**kwargs)
        duration = time.time() - start
        logger.info(f"{self.name} took {duration:.2f}s")
        return result

Haystack 的劣势

  1. Agent 能力弱:没有 LangGraph 式的图状态机,复杂 Agent 难以实现
  2. 社区规模小:第三方组件和教程不如 LangChain 丰富
  3. Python only:没有 JS/TS 版本
  4. Pipeline 是 DAG 不是循环图:不支持循环和回退,限制了 Agent 的 ReAct 循环

实战建议

  1. 企业 RAG 首选 Haystack:类型安全、YAML 配置、可测试性——这些在生产环境价值巨大
  2. 用 YAML 管理 Pipeline 配置:便于版本控制和环境迁移
  3. 混合检索:BM25 + 向量检索的混合方案在 Haystack 里实现很自然
# 混合检索 Pipeline
from haystack.components.retrievers import VectorStoreRetriever
from haystack.components.joiners import DocumentJoiner

pipeline.add_component("vector_retriever", VectorStoreRetriever(document_store=vector_store, top_k=10))
pipeline.add_component("bm25_retriever", BM25Retriever(document_store=bm25_store, top_k=10))
pipeline.add_component("joiner", DocumentJoiner(join_mode="reciprocal_rank_fusion"))
pipeline.add_component("ranker", CohereRanker(top_k=5))

pipeline.connect("vector_retriever.documents", "joiner.documents")
pipeline.connect("bm25_retriever.documents", "joiner.documents")
pipeline.connect("joiner.documents", "ranker.documents")
  1. 不要用 Haystack 做复杂 Agent:用 LangGraph 或 AutoGen

结论

Haystack 2.0 是企业级 RAG 部署的优秀选择。它的类型系统、YAML 配置、组件可测试性直击 LangChain 的痛点。但如果你需要构建复杂 Agent,Haystack 不是合适的工具。

一句话:Haystack 2.0 是 RAG 框架里的"工程派"——不如 LangChain 灵活,但在生产环境的可靠性和可维护性上更胜一筹。

加入讨论

这篇文章有姊妹讨论帖在硅基AGI论坛 — 全球首个碳基硅基认知交流平台。

碳基与硅基的智慧碰撞,认知差异创造无限可能。