📘 **TELUS Agriculture & Consumer Goods** 如何通过 **Haystack Agents** 转变促销交易

将 Jina Embeddings v2 与 Haystack 管道结合使用以总结法律文件

了解如何通过我们新的 Haystack 集成,在 RAG 管道中使用 Jina v2 嵌入模型。

Jina.ai 最近升级并扩展了其先前嵌入模型的功能,发布了 v2 版本。

通过 Jina Haystack 扩展,您现在可以在 Haystack 管道中利用这些新的文本嵌入器!在这篇文章中,我们将展示 Jina Embeddings v2 的亮点以及如何使用它们。

您可以跟随附带的 Colab 笔记本,其中包含一个使用 Jina Haystack 扩展的 RAG 管道。

Jina Embeddings v2 的优势

  • 处理长文档。 巨大的 token 窗口,最多可容纳 8192 个 token,允许您将嵌入分成更大的块。使用少量较大的向量比使用大量小向量在计算和内存效率上更高,因此这使得 Jina v2 能够高效地处理长文档。
  • 改进的语义理解。 更大的文本块在每个块中包含更多上下文,这有助于 LLM 更好地理解您的文档。改进的理解意味着更好的长文档检索、语义文本相似度、文本重排序、推荐、RAG 和基于 LLM 的生成搜索。
  • 向量长度短:Jina Embeddings v2 发出的嵌入向量长度为 768(基础模型)或 512(小型模型),两者都明显小于支持 8k token 输入长度的唯一其他嵌入模型,同时不损害检索、相似度、重排序或其他下游任务的质量。更短的向量长度意味着向量数据库可以节省成本,因为向量数据库通常根据存储的向量维度收费。
  • 完全开源 💙 有小型和大型嵌入模型可供选择,具体取决于您的计算资源和需求。要自己运行嵌入模型,请 查看 HuggingFace 上的此文档。或者,您可以使用 Jina 的完全托管的嵌入服务来处理,我们将在本次演示中这样做。

开始使用 Jina Embeddings v2 和 Haystack

要使用此集成,您需要一个免费的 Jina API 密钥 - 在 这里 获取。

您可以使用 Jina Embedding 模型与两个 Haystack 组件:JinaTextEmbedderJinaDocumentEmbedder

要为文档创建语义嵌入,请在索引管道中使用 JinaDocumentEmbedder。要为查询生成嵌入,请使用 JinaTextEmbedder

在以下代码中,我们将演示如何使用这两个组件。您也可以 查看 Haystack 文档中一些最小可行代码示例。

我不是律师,大型语言模型也不是。但 LLM 擅长分析长篇、复杂的文档。所以,让我们尝试使用 Jina v2 嵌入模型进行一些法律摘要。

2023 年 10 月,我侥幸躲过了陪审团审判。我有点 FOMO(害怕错过),因为这个案件听起来很有趣(Google v. Sonos)。让我们看看结果如何。

要跟进此演示,除了 Jina API 密钥外,您还需要一个 Hugging Face 访问令牌,因为我们将使用 Mixtral 8x7b LLM 进行问题解答。

首先,让我们安装所有需要的包。

pip install jina-haystack chroma-haystack pypdf

然后,让我们输入凭据。或者,如果您想更酷一点,也可以将它们设置为环境变量。

from getpass import getpass
import os

os.environ["JINA_API_KEY"] = getpass("JINA api key:")
os.environ["HF_API_TOKEN"] = getpass("Enter your HuggingFace api token: ")

构建索引管道

我们的索引管道将预处理法律文档,将其转换为向量,并存储它们。我们将使用 Chroma DocumentStore 来存储向量嵌入,通过 Chroma Document Store Haystack 集成

from chroma_haystack.document_store import ChromaDocumentStore
document_store = ChromaDocumentStore()

总的来说,LinkContentFetcher 从其 URL 中获取此文档。然后,我们将其从 PDF 转换为 Haystack 可以理解的 Document 对象。

我们通过删除空格和冗余子字符串来预处理它。然后将其分割成块,生成嵌入,并将这些嵌入写入 ChromaDocumentStore

from haystack import Pipeline

from haystack.components.fetchers import LinkContentFetcher
from haystack.components.converters import PyPDFToDocument
from haystack.components.writers import DocumentWriter
from haystack.components.preprocessors import DocumentCleaner
from haystack.components.preprocessors import DocumentSplitter
from chroma_haystack.retriever import ChromaEmbeddingRetriever
from haystack.document_stores.types import DuplicatePolicy

from jina_haystack.document_embedder import JinaDocumentEmbedder
from jina_haystack.text_embedder import JinaTextEmbedder

fetcher = LinkContentFetcher()
converter = PyPDFToDocument()
# remove repeated substrings to get rid of headers/footers
cleaner = DocumentCleaner(remove_repeated_substrings=True)

# Since jina-v2 can handle 8192 tokens, 500 words seems like a safe chunk size
splitter = DocumentSplitter(split_by="word", split_length=500)

# DuplicatePolicy.SKIP is optional but helps avoid errors if you want to re-run the pipeline
writer = DocumentWriter(document_store=document_store, policy=DuplicatePolicy.SKIP)

retriever = ChromaEmbeddingRetriever(document_store=document_store)

document_embedder = JinaDocumentEmbedder(model="jina-embeddings-v2-base-en")

indexing_pipeline = Pipeline()
indexing_pipeline.add_component(instance=fetcher, name="fetcher")
indexing_pipeline.add_component(instance=converter, name="converter")
indexing_pipeline.add_component(instance=cleaner, name="cleaner")
indexing_pipeline.add_component(instance=splitter, name="splitter")
indexing_pipeline.add_component(instance=document_embedder, name="embedder")
indexing_pipeline.add_component(instance=writer, name="writer")

indexing_pipeline.connect("fetcher.streams", "converter.sources")
indexing_pipeline.connect("converter.documents", "cleaner.documents")
indexing_pipeline.connect("cleaner.documents", "splitter.documents")
indexing_pipeline.connect("splitter.documents", "embedder.documents")
indexing_pipeline.connect("embedder.documents", "writer.documents")

# This case references Google V Sonos, October 2023
urls = ["https://cases.justia.com/federal/district-courts/california/candce/3:2020cv06754/366520/813/0.pdf"]

indexing_pipeline.run(data={"fetcher": {"urls": urls}})

构建查询管道

现在,真正的乐趣开始了。让我们创建一个查询管道,以便我们可以开始提问。我们编写了一个提示,允许我们将文档传递给 Mixtral-8x7B LLM。然后,我们通过 HuggingFaceAPIGenerator 初始化 LLM。

要使用此模型,您需要在此处接受条款:https://hugging-face.cn/mistralai/Mixtral-8x7B-Instruct-v0.1

在 Haystack 2.0 中,retrieverDocumentStores 紧密耦合。如果我们传入之前初始化的 retriever 中的文档存储,此管道就可以访问我们生成的嵌入,并将它们传递给 LLM。


from haystack.components.generators import HuggingFaceAPIGenerator
from haystack.components.builders.prompt_builder import PromptBuilder

from jina_haystack.text_embedder import JinaTextEmbedder
prompt = """ Answer the question, based on the
content in the documents. If you can't answer based on the documents, say so.

Documents:
{% for doc in documents %}
  {{doc.content}}
{% endfor %}

question: {{question}}
"""

text_embedder = JinaTextEmbedder(model="jina-embeddings-v2-base-en")
generator = HuggingFaceAPIGenerator(
    api_type="serverless_inference_api",
    api_params={"model": "mistralai/Mixtral-8x7B-Instruct-v0.1"})  

prompt_builder = PromptBuilder(template=prompt)
query_pipeline = Pipeline()
query_pipeline.add_component("text_embedder",text_embedder)
query_pipeline.add_component(instance=prompt_builder, name="prompt_builder")
query_pipeline.add_component("retriever", retriever)
query_pipeline.add_component("generator", generator)

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

是时候提问了!

question = "Summarize what happened in Google v. Sonos"

result = query_pipeline.run(data={"text_embedder":{"text": question},
                                  "retriever": {"top_k": 3},
                                  "prompt_builder":{"question": question},
                                  "generator": {"generation_kwargs": {"max_new_tokens": 350}}})

print(result['generator']['replies'][0])
Answer: Google v. Sonos is a patent infringement case in which Sonos sued Google for infringing on two of its patents related to customizing and saving overlapping groups of smart speakers or other zone players according to a common theme..

探索更多问题和文档

您可以替换 question 变量,然后再次调用 pipeline.run

  • “If This Then That” 在 Google v. Sonos 案中扮演了什么角色?
  • Google v. Sonos 案由哪位法官审理?
  • Sonos 应该采取什么不同的做法?

索引管道的编写方式是您可以插入其他文档并进行分析。您可以尝试将以下 URL(或任何英文 PDF)插入索引管道并重新运行下面的所有代码块。

注意:如果您想更改提示模板,您还需要重新运行从定义 DocumentStore 的地方开始的代码块。

总结

感谢您的阅读!如果您想随时了解最新的 Haystack 开发动态,您可以 订阅我们的新闻通讯加入我们的 Discord 社区

要了解更多关于这里使用的技术,请查看这些博客文章