迁移指南
了解如何从 Haystack 1.x 迁移到 Haystack 2.x。
本指南专为已有 Haystack 使用经验并希望了解 Haystack 1.x 和 Haystack 2.x 之间差异的用户设计。如果您是 Haystack 新手,请跳过此页,直接阅读 Haystack 2.x 的 文档。
主要变更
Haystack 2.x 对 Haystack 1.x 进行了重大改进,需要注意的是,本节概述的某些关键概念在两个版本之间没有直接的对应关系。
包名
Haystack 1.x 使用名为farm-haystack 的包进行分发。要迁移您的应用程序,您必须卸载farm-haystack 并安装 Haystack 2.x 的新haystack-ai 包。
请注意,这两个版本的项目不能在同一个 Python 环境中共存。
一种选择是卸载安装在同一环境中的两个包,然后仅安装其中一个。
pip uninstall -y farm-haystack haystack-ai pip install haystack-ai
节点 (Nodes)
虽然 Haystack 2.x 仍然依赖于Pipeline 抽象,但现在管道图中的元素被称为 *组件*(components),取代了前版本中使用的 *节点*(nodes)和 *管道组件*(pipeline components)等术语。下面的 迁移组件 段落将概述 Haystack 2.x 中的哪些组件可以替代 Haystack 1.x 的特定节点。
Pipelines (管道)
管道 (Pipelines) 仍然是所有 Haystack 应用程序的基本结构。虽然Pipeline 抽象的概念保持一致,但 Haystack 2.x 引入了重大增强,解决了其前身存在的各种限制。例如,管道现在支持循环。管道还提供更高的输入灵活性,输入不再局限于查询。管道现在允许将组件的输出路由到多个接收者。这增加了灵活性,但与前一个版本相比,Haystack 2.x 中的管道定义过程存在一些显著差异。
在 Haystack 1.x 中,管道是通过一个接一个地添加节点来构建的。在生成的管道图中,会自动添加边来按添加顺序连接这些节点。
在 Haystack 2.x 中构建管道是一个两步过程:
- 首先,在不指定特定顺序的情况下将组件添加到管道中,调用
add_component方法。 - 然后,必须通过调用
connect方法显式连接组件来定义最终图。
要迁移现有管道,第一步是遍历节点并识别它们在 Haystack 2.x 中的对应项(有关指导,请参阅以下部分 迁移组件)。如果所有节点都可以被相应的组件替换,则必须使用add_component 将它们添加到管道中,并通过适当调用connect 来显式连接它们。示例如下:
Haystack 1.x
pipeline = Pipeline()
node_1 = SomeNode()
node_2 = AnotherNode()
pipeline.add_node(node_1, name="Node_1", inputs=["Query"])
pipeline.add_node(node_2, name="Node_2", inputs=["Node_1"])
Haystack 2.x
pipeline = Pipeline()
component_1 = SomeComponent()
component_2 = AnotherComponent()
pipeline.add_component("Comp_1", component_1)
pipeline.add_component("Comp_2", component_2)
pipeline.connect("Comp_1", "Comp_2")
如果某个节点没有可用的特定替换组件,则迁移管道仍可能通过以下方式实现:
- 要么 创建自定义组件,要么
- 更改管道逻辑(作为最后手段)。
请参阅我们 2.x 文档的 Pipelines 部分,以更详细地了解新管道的工作方式。
Document Stores (文档存储)
Document Stores 作为访问数据库中存储的文本和元数据的网关这一基本概念在 Haystack 2.x 中没有改变,但与 Haystack 1.x 相比存在显著差异。
在 Haystack 1.x 中,Document Stores 是一种特殊的节点,您可以以两种方式使用它:
- 作为索引管道的最后一个节点(例如,最终目标是将数据存储到数据库的管道)。
- 作为传递给 Retriever 节点的普通 Python 实例。
在 Haystack 2.x 中,Document Store 不是一个组件,因此要将上述两种用例迁移到 2.x 版本,您可以分别:
- 用
DocumentWriter组件替换管道末尾的 Document Store。 - 确定正确的 Retriever 组件,并创建该组件,将 Document Store 实例作为参数传入,就像在 Haystack 1.x 中一样。
Retrievers (检索器)
Haystack 1.x 提供了一系列节点,用于根据给定的查询从不同的数据源过滤相关文档。每个节点都实现特定的检索算法,并支持一种或多种类型的 Document Stores。例如,Haystack 1.x 中的BM25Retriever 节点可以与 OpenSearch 和 Elasticsearch 无缝协作,但不能与 Qdrant 协作;而EmbeddingRetriever 则可以与这三个数据库中的任何一个协作。
在 Haystack 2.x 中,这个概念被颠倒了,每个 Document Store 提供一个或多个检索器组件,具体取决于底层向量数据库支持的检索方法。例如,OpenSearchDocumentStore 提供了 两个 Retriever 组件,一个依赖于 BM25,另一个依赖于向量相似度。
要将 1.x 的检索管道迁移到 2.x,第一步是确定正在使用的 Document Store,并将 Retriever 节点替换为 Haystack 2.x 中相应的 Retriever 组件,并指定所选的 Document Store。例如,在 Haystack 1.x 管道中使用 Elasticsearch 的BM25Retriever 节点应被替换为 ElasticsearchBM25Retriever 组件。
PromptNode
该PromptNode 在 Haystack 1.x 中代表了与任何大型语言模型 (LLM) 推理提供商的接口,无论它是本地可用还是远程。根据模型名称,Haystack 会推断出正确的提供商来调用并转发查询。
在 Haystack 2.x 中,使用 LLM 的任务分配给了 Generators。这些组件是高度专业化的,并针对每个推理提供商进行了定制。
迁移带有PromptNode 的管道时,第一步是确定使用的模型提供商,并用两个组件替换该节点:
- 一个用于您选择的模型提供商的 Generator 组件,
- 一个
PromptBuilder或ChatPromptBuilder组件用于构建要使用的提示。
下面的 迁移示例 部分展示了如何将使用 OpenAI 和提示模板的PromptNode 迁移到使用OpenAIGenerator 结合PromptBuilder 组件的相应 Haystack 2.x 管道。
Agent
代理 (Agent) 方法有助于回答比通常通过抽取式或生成式问答技术所解决的问题复杂得多的问题。
Haystack 1.x 提供了 Agents,允许在循环中使用 LLM。
目前在 Haystack 2.x 中,您可以使用管道中的三个主要元素来构建 Agents:Chat Generators、ToolInvoker 组件和 Tools。Haystack 2.x 中的独立 Agent 抽象仍处于实验阶段。
Agents 文档页面
请查看我们 2.x Agents 文档页面以获取更多信息和详细示例。
REST API
Haystack 1.x 支持通过 HTTP 上的 RESTful API 部署管道。此功能由一个名为rest_api 的独立应用程序实现,该应用程序仅以 GitHub 源代码 的形式提供。
Haystack 2.x 采用相同的 RESTful 方法,但在此情况下,用于部署管道的应用程序名为 Hayhooks,可以通过以下方式安装:pip install hayhooks.
目前,使用rest_api 项目部署 Haystack 1.x 的现有部署迁移到 Hayhooks 将需要完全重写该应用程序。
依赖项
为了最大限度地减少运行时错误,Haystack 1.x 被分发在一个相当大的包中,因为它试图在 Python 环境中尽可能多地设置依赖项。
相比之下,Haystack 2.x 采用了更精简的方法,开箱即用提供最少的依赖项。它有一个系统,当需要额外的依赖项时会发出警告,从而为用户提供必要的说明。
为确保在将 Haystack 1.x 应用程序迁移到 2.x 版本时满足所有依赖项,一个好的策略是运行端到端测试并覆盖所有执行路径,以确保在目标 Python 环境中可用所有必需的依赖项。
迁移组件
此表概述了在将 Haystack 1.x 管道移植到最新的 2.x 版本时,可以使用哪些组件(或一组组件)来替换特定节点。需要注意的是,当 Haystack 2.x 中没有可用的替换项时,这并不一定意味着我们正在计划此功能。
如果您需要帮助迁移没有 2.x 对应项的 1.x 节点,请在 Haystack GitHub 存储库中提出一个 issue。
数据处理
| Haystack 1.x | 描述 | Haystack 2.x |
|---|---|---|
| 爬虫 (Crawler) | 从网站抓取文本。示例用法:对您的网站内容运行搜索。 | 不可用 |
| 文档分类器 (DocumentClassifier) | 通过附加元数据来分类文档。示例用法:根据文档的特征进行标记(例如,情感)。 | TransformersZeroShotDocumentClassifier |
| DocumentLanguageClassifier | 检测您传入文档的语言,并将其添加到文档元数据中。 | DocumentLanguageClassifier |
| 实体提取器 (EntityExtractor) | 从一段文本中提取预定义的实体。示例用法:命名实体识别 (NER)。 | NamedEntityExtractor |
| 文件分类器 (FileClassifier) | 区分文本、PDF、Markdown、Docx 和 HTML 文件。示例用法:将文件路由到合适的转换器(例如,将 PDF 文件路由到PDFToTextConverter). | FileTypeRouter |
| 文件转换器 (FileConverter) | 清理和拆分不同格式的文档。示例用法:在索引管道中,从文件中提取文本并将其转换为 Document 类格式。 | Converters (转换器) |
| 预处理器 (PreProcessor) | 清理和拆分文档。示例用法:规范化空格,去除页眉和页脚,将文档拆分成更小的文档。 | PreProcessors (预处理器) |
语义搜索
| Haystack 1.x | 描述 | Haystack 2.x |
|---|---|---|
| 排序器 (Ranker) | 根据文档与查询的相关性对文档进行排序。示例用法:在查询管道中,在基于关键字的 Retriever 之后对检索到的文档进行排序。 | Rankers (排序器) |
| 阅读器 (Reader) | 通过在文档中选择一个文本片段来找到答案。示例用法:在查询管道中,当您想知道答案的位置时。 | ExtractiveReader |
| Retriever (检索器) | 从 Document Store 中获取相关文档。示例用法:在查询管道中将 Retriever 与 Reader 结合使用以加快搜索速度(Reader 只处理从 Retriever 接收到的文档)。 | Retrievers (检索器) |
| 问题生成器 (QuestionGenerator) | 给定一个文档,它会生成该文档可以回答的问题。示例用法:搜索应用中的自动建议问题。 | Prompt Builders 配合专用提示,Generators |
提示和 LLM
| Haystack 1.x | 描述 | Haystack 2.x |
|---|---|---|
| PromptNode | 使用大型语言模型在管道中或独立执行各种 NLP 任务。示例用法:这是一个非常通用的组件,可以执行摘要、问答、翻译等任务。 | Prompt Builders,Generators |
路由
| Haystack 1.x | 描述 | Haystack 2.x |
|---|---|---|
| 查询分类器 (QueryClassifier) | 对查询进行分类。示例用法:区分关键字查询和自然语言问题,并将它们路由到最能处理它们的 Retriever。 | TransformersZeroShotTextRouter TransformersTextRouter |
| 路由文档 (RouteDocuments) | 根据文档的内容类型或元数据字段,将文档路由到管道的不同分支。示例用法:将表格数据路由到TableReader,将文本数据路由到TransfomersReader 以获得更好的处理。 | Routers (路由器) |
实用组件
| Haystack 1.x | 描述 | Haystack 2.x |
|---|---|---|
| 文档合并器 (DocumentMerger) | 将多个文档连接成一个。示例用法:在摘要管道中合并文档以进行摘要。 | Prompt Builders |
| 文档转答案 (Docs2Answers) | 将文档转换为答案。示例用法:在使用 REST API 进行文档检索时。REST API 期望 Answer 作为输出,您可以使用Doc2Answer 作为最后一个节点将检索到的文档转换为答案。 | AnswerBuilder |
| 合并答案 (JoinAnswers) | 将多个组件返回的答案合并成一个答案列表。示例用法:对不同类型的文档运行查询(例如,表格和文本),其中文档被路由到不同的阅读器,每个阅读器返回一个单独的答案列表。 | AnswerJoiner |
| 合并文档 (JoinDocuments) | 将不同组件返回的文档合并成一个文档列表。示例用法:在文档检索管道中,存在不同类型的文档,每个文档都被路由到不同的 Retriever。每个 Retriever 返回一个单独的文档列表,您可以使用合并文档 (JoinDocuments). | DocumentJoiner |
| Shaper | 目前主要作为PromptNode 的辅助工具,确保PromptNode 的输入或输出正确。示例用法:在使用PromptNode 的问答管道中,PromptTemplate 期望将问题作为输入,而 Haystack 管道使用查询。您可以使用 Shaper 将查询重命名为问题。 | Prompt Builders |
| 摘要器 (Summarizer) | 创建文档的概述。示例用法:快速了解 Retriever 返回的文档。 | Prompt Builders 配合专用提示,Generators |
| TransformersImageToText | 为图像生成字幕。示例用法:自动为一系列图像生成字幕,之后您可以在知识库中使用这些字幕。 | VertexAIImageQA |
| 翻译器 (Translator) | 将文本从一种语言翻译成另一种语言。示例用法:对其他语言的文档进行搜索。 | Prompt Builders 配合专用提示,Generators |
附加功能
| Haystack 1.x | 描述 | Haystack 2.x |
|---|---|---|
| 答案转语音 (AnswerToSpeech) | 将文本答案转换为语音答案。示例用法:通过提供一种方式来朗读答案及其上下文,从而提高搜索系统的可访问性。 | ElevenLabs 集成 |
| 文档转语音 (DocumentToSpeech) | 将文本文档转换为语音文档。示例用法:通过提供朗读文档的选项,提高文档检索管道的可访问性。 | ElevenLabs 集成 |
迁移示例
随着我们帮助用户解决他们的用例,本节可能会继续增长。
索引管道
Haystack 1.x
from haystack.document_stores import InMemoryDocumentStore
from haystack.nodes.file_classifier import FileTypeClassifier
from haystack.nodes.file_converter import TextConverter
from haystack.nodes.preprocessor import PreProcessor
from haystack.pipelines import Pipeline
# Initialize a DocumentStore
document_store = InMemoryDocumentStore()
# Indexing Pipeline
indexing_pipeline = Pipeline()
# Makes sure the file is a TXT file (FileTypeClassifier node)
classifier = FileTypeClassifier()
indexing_pipeline.add_node(classifier, name="Classifier", inputs=["File"])
# Converts a file into text and performs basic cleaning (TextConverter node)
text_converter = TextConverter(remove_numeric_tables=True)
indexing_pipeline.add_node(text_converter, name="Text_converter", inputs=["Classifier.output_1"])
# Pre-processes the text by performing splits and adding metadata to the text (Preprocessor node)
preprocessor = PreProcessor(
clean_whitespace=True,
clean_empty_lines=True,
split_length=100,
split_overlap=50,
split_respect_sentence_boundary=True,
)
indexing_pipeline.add_node(preprocessor, name="Preprocessor", inputs=["Text_converter"])
# - Writes the resulting documents into the document store
indexing_pipeline.add_node(document_store, name="Document_Store", inputs=["Preprocessor"])
# Then we run it with the documents and their metadata as input
result = indexing_pipeline.run(file_paths=file_paths, meta=files_metadata)
Haystack 2.x
from haystack import Pipeline
from haystack.components.routers import FileTypeRouter
from haystack.document_stores.in_memory import InMemoryDocumentStore
from haystack.components.converters import TextFileToDocument
from haystack.components.preprocessors import DocumentCleaner, DocumentSplitter
from haystack.components.writers import DocumentWriter
# Initialize a DocumentStore
document_store = InMemoryDocumentStore()
# Indexing Pipeline
indexing_pipeline = Pipeline()
# Makes sure the file is a TXT file (FileTypeRouter component)
classifier = FileTypeRouter(mime_types=["text/plain"])
indexing_pipeline.add_component("file_type_router", classifier)
# Converts a file into a Document (TextFileToDocument component)
text_converter = TextFileToDocument()
indexing_pipeline.add_component("text_converter", text_converter)
# Performs basic cleaning (DocumentCleaner component)
cleaner = DocumentCleaner(
remove_empty_lines=True,
remove_extra_whitespaces=True,
)
indexing_pipeline.add_component("cleaner", cleaner)
# Pre-processes the text by performing splits and adding metadata to the text (DocumentSplitter component)
preprocessor = DocumentSplitter(
split_by="passage",
split_length=100,
split_overlap=50
)
indexing_pipeline.add_component("preprocessor", preprocessor)
# - Writes the resulting documents into the document store
indexing_pipeline.add_component("writer", DocumentWriter(document_store))
# Connect all the components
indexing_pipeline.connect("file_type_router.text/plain", "text_converter")
indexing_pipeline.connect("text_converter", "cleaner")
indexing_pipeline.connect("cleaner", "preprocessor")
indexing_pipeline.connect("preprocessor", "writer")
# Then we run it with the documents and their metadata as input
result = indexing_pipeline.run({"file_type_router": {"sources": file_paths}})
查询管道
Haystack 1.x
from haystack.document_stores import InMemoryDocumentStore
from haystack.pipelines import ExtractiveQAPipeline
from haystack import Document
from haystack.nodes import BM25Retriever
from haystack.nodes import FARMReader
document_store = InMemoryDocumentStore(use_bm25=True)
document_store.write_documents([
Document(content="Paris is the capital of France."),
Document(content="Berlin is the capital of Germany."),
Document(content="Rome is the capital of Italy."),
Document(content="Madrid is the capital of Spain."),
])
retriever = BM25Retriever(document_store=document_store)
reader = FARMReader(model_name_or_path="deepset/roberta-base-squad2")
extractive_qa_pipeline = ExtractiveQAPipeline(reader, retriever)
query = "What is the capital of France?"
result = extractive_qa_pipeline.run(
query=query,
params={
"Retriever": {"top_k": 10},
"Reader": {"top_k": 5}
}
)
Haystack 2.x
from haystack.document_stores.in_memory import InMemoryDocumentStore
from haystack import Document, Pipeline
from haystack.components.retrievers.in_memory import InMemoryBM25Retriever
from haystack.components.readers import ExtractiveReader
document_store = InMemoryDocumentStore()
document_store.write_documents([
Document(content="Paris is the capital of France."),
Document(content="Berlin is the capital of Germany."),
Document(content="Rome is the capital of Italy."),
Document(content="Madrid is the capital of Spain."),
])
retriever = InMemoryBM25Retriever(document_store)
reader = ExtractiveReader(model="deepset/roberta-base-squad2")
extractive_qa_pipeline = Pipeline()
extractive_qa_pipeline.add_component("retriever", retriever)
extractive_qa_pipeline.add_component("reader", reader)
extractive_qa_pipeline.connect("retriever", "reader")
query = "What is the capital of France?"
result = extractive_qa_pipeline.run(data={
"retriever": {"query": query, "top_k": 3},
"reader": {"query": query, "top_k": 2}
})
RAG 管道
Haystack 1.x
from datasets import load_dataset
from haystack.pipelines import Pipeline
from haystack.document_stores import InMemoryDocumentStore
from haystack.nodes import EmbeddingRetriever, PromptNode, PromptTemplate, AnswerParser
document_store = InMemoryDocumentStore(embedding_dim=384)
dataset = load_dataset("bilgeyucel/seven-wonders", split="train")
document_store.write_documents(dataset)
retriever = EmbeddingRetriever(embedding_model="sentence-transformers/all-MiniLM-L6-v2", document_store=document_store, top_k=2)
document_store.update_embeddings(retriever)
rag_prompt = PromptTemplate(
prompt="""Synthesize a comprehensive answer from the following text for the given question.
Provide a clear and concise response that summarizes the key points and information presented in the text.
Your answer should be in your own words and be no longer than 50 words.
\n\n Related text: {join(documents)} \n\n Question: {query} \n\n Answer:""",
output_parser=AnswerParser(),
)
prompt_node = PromptNode(model_name_or_path="gpt-3.5-turbo", api_key=OPENAI_API_KEY, default_prompt_template=rag_prompt)
pipe = Pipeline()
pipe.add_node(component=retriever, name="retriever", inputs=["Query"])
pipe.add_node(component=prompt_node, name="prompt_node", inputs=["retriever"])
output = pipe.run(query="What does Rhodes Statue look like?")
Haystack 2.x
from datasets import load_dataset
from haystack import Document, Pipeline
from haystack.document_stores.in_memory import InMemoryDocumentStore
from haystack.components.builders import PromptBuilder
from haystack.components.generators import OpenAIGenerator
from haystack.components.embedders import SentenceTransformersDocumentEmbedder
from haystack.components.embedders import SentenceTransformersTextEmbedder
from haystack.components.retrievers import InMemoryEmbeddingRetriever
document_store = InMemoryDocumentStore()
dataset = load_dataset("bilgeyucel/seven-wonders", split="train")
embedder = SentenceTransformersDocumentEmbedder("sentence-transformers/all-MiniLM-L6-v2")
embedder.warm_up()
output = embedder.run([Document(**ds) for ds in dataset])
document_store.write_documents(output.get("documents"))
template = """
Given the following information, answer the question.
Context:
{% for document in documents %}
{{ document.content }}
{% endfor %}
Question: {{question}}
Answer:
"""
prompt_builder = PromptBuilder(template=template)
retriever = InMemoryEmbeddingRetriever(document_store=document_store, top_k=2)
generator = OpenAIGenerator(model="gpt-3.5-turbo")
query_embedder = SentenceTransformersTextEmbedder(model="sentence-transformers/all-MiniLM-L6-v2")
basic_rag_pipeline = Pipeline()
basic_rag_pipeline.add_component("text_embedder", query_embedder)
basic_rag_pipeline.add_component("retriever", retriever)
basic_rag_pipeline.add_component("prompt_builder", prompt_builder)
basic_rag_pipeline.add_component("llm", generator)
basic_rag_pipeline.connect("text_embedder.embedding", "retriever.query_embedding")
basic_rag_pipeline.connect("retriever", "prompt_builder.documents")
basic_rag_pipeline.connect("prompt_builder", "llm")
query = "What does Rhodes Statue look like?"
output = basic_rag_pipeline.run({"text_embedder": {"text": query}, "prompt_builder": {"question": query}})
Haystack 1.x 的文档和教程
您可以在 GitHub 历史记录 中访问旧教程,并将 Haystack 1.x 文档下载为 ZIP 文件。
ZIP 文件包含版本 1.0 至 1.26 的所有次要版本的文档。
要下载特定版本的文档,请替换以下 URL 中的版本号https://core-engineering.s3.eu-central-1.amazonaws.com/public/docs/v1.26.zip.
更新于 6 个月前
