Haystack Extractors 组件深度指南:NER 实体抽取、LLM 元数据提取与图像文档内容抽取
2026/9/13 5:17:29 网站建设 项目流程

Haystack Extractors 组件深度指南:NER 实体抽取、LLM 元数据提取与图像文档内容抽取

【免费下载链接】haystackOpen-source AI orchestration framework for building context-engineered, production-ready LLM applications. Design modular pipelines and agent workflows with explicit control over retrieval, routing, memory, and generation. Built for scalable agents, RAG, multimodal applications, semantic search, and conversational systems.项目地址: https://gitcode.com/GitHub_Trending/ha/haystack

本文聚焦 Haystack 开源 AI 编排框架中的extractors(抽取器)组件家族,系统讲解 version-2.18 API 文档所定义的三类文本/文档信息抽取组件:基于 Hugging Face 或 spaCy 的NamedEntityExtractor(命名实体识别)、基于大语言模型的LLMMetadataExtractor(文档元数据提取),以及面向图像型文档的LLMDocumentContentExtractor(视觉 LLM 内容抽取)。读完本文,你将掌握这三个组件的完整 API 签名、参数语义、序列化机制与失败处理策略,能够直接在 Haystack Pipeline 中搭建从"原始文本/图片 → 结构化实体与元数据"的信息抽取链路。

说明:本文以 version-2.18 的 Extractors API 文档 为核心骨架,并结合当前仓库中的 源码 与测试用例进行佐证与延伸。从当前仓库的 release notes(如 remove-named-entity-extractor-a8d65992a201a775.yaml)看,NamedEntityExtractor已在后续主版本中被移除,LLMMetadataExtractorLLMDocumentContentExtractor则持续演进;本文对 v2.18 文档内容的讲解以其文档为准,版本差异会作特别标注。

一、Extractors 组件家族概览

在 Haystack 中,haystack.components.extractors目录专门存放"从文档中抽取结构化信息"的组件。version-2.18 API 文档定义了三个核心类,各自解决一类典型问题:

组件模块路径输入输出落点适用场景
NamedEntityExtractorhaystack.components.extractors.named_entity_extractorlist[Document]注解写入文档metadata传统 NER:人物、组织、地点等预定义实体
LLMMetadataExtractorhaystack.components.extractors.llm_metadata_extractorlist[Document]+ 提示词元数据写入文档metadata由 LLM 驱动的灵活元数据抽取
LLMDocumentContentExtractorhaystack.components.extractors.image.llm_document_content_extractorlist[Document](含图片/PDF 路径)抽取文本写入文档content图像/扫描件/PDF 页面的内容还原

从当前仓库的目录结构(haystack/components/extractors/)可以看到,extractors 家族还包括regex_text_extractor.py(正则文本抽取器,不在本文 API 文档范围内),以及image/子目录下的llm_document_content_extractor.py。三个文档化组件的共同设计哲学是:输入一批 Document,输出处理后的 Document 或明确的失败列表,从而天然适配 Haystack 的 Pipeline 数据流。

二、NamedEntityExtractor:经典 NER 抽取器

2.1 双后端设计与枚举类型

NamedEntityExtractor的核心特性是支持两种 NLP 后端,由NamedEntityExtractorBackend枚举标识:

  • HUGGING_FACE:使用 Hugging Face 模型与 pipeline,可加载 Hugging Face model hub 上的任意序列分类/序列标注模型;
  • SPACY:使用 spaCy 模型与 pipeline,要求模型包含 NER 组件。

枚举还提供静态方法NamedEntityExtractorBackend.from_str(string),用于把"hugging_face""spacy"这类字符串安全地转换为枚举值,便于在 YAML 配置或命令行参数中传递后端名称。从源码结构看,构造时传入的backend参数支持Union[str, NamedEntityExtractorBackend],字符串会自动经from_str归一化。

2.2 注解数据结构:NamedEntityAnnotation

每个被识别出的实体由NamedEntityAnnotation描述,包含四个字段:

  • entity:实体标签(例如人名、组织名、地名);
  • start:实体在文档中的起始下标;
  • end:实体在文档中的结束下标;
  • score:模型给出的置信度分数。

start/end 下标与文本内容配合,可以精确定位实体在原文中的 span;score 则用于下游按置信度过滤。

2.3 构造参数

def __init__( *, backend: Union[str, NamedEntityExtractorBackend], model: str, pipeline_kwargs: Optional[dict[str, Any]] = None, device: Optional[ComponentDevice] = None, token: Optional[Secret] = Secret.from_env_var(["HF_API_TOKEN", "HF_TOKEN"], strict=False) ) -> None
参数说明默认值/备注
backend使用的 NER 后端(Hugging Face 或 spaCy)必填
model模型名称或本地磁盘上的模型路径,取值依赖后端必填
pipeline_kwargs传递给底层 pipeline 的关键字参数(pipeline 可覆盖这些参数),取值依赖后端None
device模型加载的设备;为None时自动选择默认设备。若在pipeline_kwargs中指定了 device/device map,则覆盖此参数(仅对 HuggingFace 后端生效)None
token从 Hugging Face 下载私有模型所需的 API token从环境变量HF_API_TOKENHF_TOKEN读取,strict=False表示未设置时不报错

值得注意的是token采用了Secret类型并从环境变量读取,体现了 Haystack 组件"密钥不进代码"的安全设计。

2.4 生命周期:warm_up / run / initialized

NamedEntityExtractor遵循 Haystack 标准的组件生命周期:

  • warm_up():初始化底层模型与 pipeline,是"惰性加载"的关键步骤。若后端初始化失败,抛出ComponentError。这保证了模型只被加载一次,而非每个run调用都重新加载;
  • initialized(属性):返回抽取器是否已准备好执行注解,可在调用run前做状态检查;
  • run(documents, batch_size=1):对每个文档执行实体注解,将注解存入文档metadata,返回{"documents": [...]}batch_size控制处理时的批大小,默认 1。若后端处理单个文档失败,抛出ComponentError

2.5 读取注解:get_stored_annotations

@classmethod def get_stored_annotations(cls, document: Document) -> Optional[list[NamedEntityAnnotation]]

这是一个类方法,用于从 Document 的metadata中取出NamedEntityExtractor之前存入的注解列表;若文档中没有注解则返回None。它把"注解如何存储"的内部细节封装起来,让下游组件无需关心 metadata 的具体键名。

2.6 序列化与版本演进

to_dict()/from_dict()提供了标准的序列化/反序列化能力,to_dict返回包含序列化数据的字典,from_dict从字典还原组件实例。这两者是 Haystack YAML Pipeline 描述(marshal/yaml.py)得以工作的基础。

需要特别说明版本差异:当前仓库主版本(VERSION.txt显示 3.2.0-rc0)的 haystack/components/extractors/ 目录中已不存在named_entity_extractor.py,release notes 中的 remove-named-entity-extractor-a8d65992a201a775.yaml 也印证了该组件已被移除。因此 v2.18 文档中的NamedEntityExtractor适用于 2.18 及相近版本;新项目建议直接用 LLM 方案(如下文的LLMMetadataExtractor)或正则方案(regex_text_extractor.py)完成实体抽取。

2.7 使用示例(来自 v2.18 文档)

from haystack import Document from haystack.components.extractors.named_entity_extractor import NamedEntityExtractor documents = [ Document(content="I'm Merlin, the happy pig!"), Document(content="My name is Clara and I live in Berkeley, California."), ] extractor = NamedEntityExtractor(backend="hugging_face", model="dslim/bert-base-NER") extractor.warm_up() results = extractor.run(documents=documents)["documents"] annotations = [NamedEntityExtractor.get_stored_annotations(doc) for doc in results] print(annotations)

该示例展示了完整调用链:构造 →warm_up()run()→ 用get_stored_annotations读取结果。dslim/bert-base-NER是文档中给出的 HF 模型示例,换成任何序列标注模型即可。

三、LLMMetadataExtractor:用 LLM 抽取文档元数据

LLMMetadataExtractor把"元数据抽取"这项传统上依赖规则或小模型的任务交给 LLM 完成。它的工作方式非常直观:向 LLM 提供一个包含文档内容的提示词,让 LLM 生成 JSON 形式的元数据,再把元数据合并回每个 Document 的metadata字段

3.1 工作原理与内部组成

从 llm_metadata_extractor.py 源码 可以看到,该组件内部组合了三样东西:

  • PromptBuilder:提示词模板引擎。文档约定提示词中必须有且仅有一个变量document,通过{{ document.content }}访问当前文档内容。_prepare_prompts方法调用self.builder.run(template=self.prompt, template_variables={"document": doc_copy})完成逐文档渲染,并用SandboxedEnvironment做沙箱化模板解析;
  • DocumentSplitter:当指定page_range时,先用分割器把文档按页切开,再只把目标页的内容拼回doc_copy.content送入提示词(源码中self.splitter.run(documents=[doc_copy])expand_page_range配合实现);
  • ChatGenerator:真正的 LLM 调用点。每个渲染后的提示词被包装为ChatMessage.from_user(...),并发给self._chat_generator.run(messages=[prompt])

3.2 构造参数详解

def __init__(prompt: str, chat_generator: ChatGenerator, expected_keys: Optional[list[str]] = None, page_range: Optional[list[Union[str, int]]] = None, raise_on_failure: bool = False, max_workers: int = 3)
参数说明默认值
prompt提供给 LLM 的提示词模板,内含{{ document.content }}变量必填
chat_generatorChatGenerator实例,代表 LLM。为保证组件工作,LLM 应配置为返回 JSON 对象——例如使用OpenAIChatGenerator时,需在generation_kwargs中传入{"response_format": {"type": "json_object"}}必填
expected_keysLLM JSON 输出中期望出现的键名列表,用于输出校验None
page_range抽取元数据的页码范围。例如['1', '3']表示抽取每个文档的第 1、3 页;也接受可打印的区间字符串,如['1-3', '5', '8', '10-12']表示抽取第 1、2、3、5、8、10、11、12 页。为None时对整篇文档抽取。可在run方法中覆盖None
raise_on_failure生成器执行失败或 JSON 输出校验失败时是否抛出异常False
max_workers线程池执行器(ThreadPoolExecutor)的最大工作线程数,用于跨文档并行调用 LLM3

源码进一步印证:page_range会经expand_page_range(haystack/utils 工具)展开为具体的页码列表;raise_on_failure=False时,LLM 异常会被记录日志并转化为错误结果,而不是中断整批处理(见_run_on_thread中的logger.exception{"error": ...}返回)。

3.3 完整 NER 元数据抽取示例(文档原例)

以下示例来自 v2.18 文档,展示了一个完整的提示词工程 + 组件配置 + 运行输出的闭环:

from haystack import Document from haystack.components.extractors.llm_metadata_extractor import LLMMetadataExtractor from haystack.components.generators.chat import OpenAIChatGenerator NER_PROMPT = ''' -Goal- Given text and a list of entity types, identify all entities of those types from the text. -Steps- 1. Identify all entities. For each identified entity, extract the following information: - entity: Name of the entity - entity_type: One of the following types: [organization, product, service, industry] Format each entity as a JSON like: {"entity": <entity_name>, "entity_type": <entity_type>} 2. Return output in a single list with all the entities identified in steps 1. -Examples- ###################### Example 1: entity_types: [organization, person, partnership, financial metric, product, service, industry, investment strategy, market trend] text: Another area of strength is our co-brand issuance. Visa is the primary network partner for eight of the top 10 co-brand partnerships in the US today and we are pleased that Visa has finalized a multi-year extension of our successful credit co-branded partnership with Alaska Airlines, a portfolio that benefits from a loyal customer base and high cross-border usage. We have also had significant co-brand momentum in CEMEA. First, we launched a new co-brand card in partnership with Qatar Airways, British Airways and the National Bank of Kuwait. Second, we expanded our strong global Marriott relationship to launch Qatar's first hospitality co-branded card with Qatar Islamic Bank. Across the United Arab Emirates, we now have exclusive agreements with all the leading airlines marked by a recent agreement with Emirates Skywards. And we also signed an inaugural Airline co-brand agreement in Morocco with Royal Air Maroc. Now newer digital issuers are equally ------------------------ output: {"entities": [{"entity": "Visa", "entity_type": "company"}, {"entity": "Alaska Airlines", "entity_type": "company"}, {"entity": "Qatar Airways", "entity_type": "company"}, {"entity": "British Airways", "entity_type": "company"}, {"entity": "National Bank of Kuwait", "entity_type": "company"}, {"entity": "Marriott", "entity_type": "company"}, {"entity": "Qatar Islamic Bank", "entity_type": "company"}, {"entity": "Emirates Skywards", "entity_type": "company"}, {"entity": "Royal Air Maroc", "entity_type": "company"}]} ############################# -Real Data- ###################### entity_types: [company, organization, person, country, product, service] text: {{ document.content }} ###################### output: ''' docs = [ Document(content="deepset was founded in 2018 in Berlin, and is known for its Haystack framework"), Document(content="Hugging Face is a company that was founded in New York, USA and is known for its Transformers library") ] chat_generator = OpenAIChatGenerator( generation_kwargs={ "max_tokens": 500, "temperature": 0.0, "seed": 0, "response_format": {"type": "json_object"}, }, max_retries=1, timeout=60.0, ) extractor = LLMMetadataExtractor( prompt=NER_PROMPT, chat_generator=generator, expected_keys=["entities"], raise_on_failure=False, ) extractor.warm_up() extractor.run(documents=docs) >> {'documents': [ Document(id=.., content: 'deepset was founded in 2018 in Berlin, and is known for its Haystack framework', meta: {'entities': [{'entity': 'deepset', 'entity_type': 'company'}, {'entity': 'Berlin', 'entity_type': 'city'}, {'entity': 'Haystack', 'entity_type': 'product'}]}), Document(id=.., content: 'Hugging Face is a company that was founded in New York, USA and is known for its Transformers library', meta: {'entities': [ {'entity': 'Hugging Face', 'entity_type': 'company'}, {'entity': 'New York', 'entity_type': 'city'}, {'entity': 'USA', 'entity_type': 'country'}, {'entity': 'Transformers', 'entity_type': 'product'} ]}) ] 'failed_documents': [] } >>

这个示例的价值在于其提示词结构本身就是一个可复用的模板范式:-Goal- / -Steps- / -Examples- / -Real Data-四段式组织,用 few-shot 示例约束输出格式,最后用{{ document.content }}注入真实数据。temperature: 0.0与固定seed保证抽取结果的可复现性;expected_keys=["entities"]则要求输出 JSON 必须包含entities键。

需要留意的是,示例输出中 LLM 返回的实体类型(如citycountry)与提示词中声明的[company, organization, person, country, product, service]基本一致,但个别类型(如city未在列表中出现)——这正是 LLM 抽取的固有特征,实践中可通过更严格的提示词约束或expected_keys校验来兜底。

3.4 run 方法与失败处理

@component.output_types(documents=list[Document], failed_documents=list[Document]) def run(documents: list[Document], page_range: Optional[list[Union[str, int]]] = None)

run的返回值包含两个键:

  • documents:成功更新元数据的文档列表;
  • failed_documents:抽取失败的文档列表。失败文档的metadata中会写入两个保留键:metadata_extraction_error(错误详情)与metadata_extraction_response(LLM 的原始响应)。

失败重试机制是这套设计的亮点:由于metadata_extraction_errormetadata_extraction_response都被保留在文档元数据中,你可以把这些失败文档连同错误信息一起重新喂给另一个(或同一个人)抽取器,并在提示词中引用这两个字段做针对性修复——例如"上次你返回了非法 JSON,错误是 X,请重新抽取"。这与文档说明"These documents can be re-run with another extractor to extract metadata"完全一致。

3.5 序列化与异步能力

to_dict/from_dict负责组件序列化,from_dict内部调用deserialize_chatgenerator_inplace把字典中的chat_generator还原为真实的生成器实例(源码第 258-269 行)。

从源码看,该组件还提供了完整的异步与资源管理能力:warm_up_asynccloseclose_async以及基于Semaphore/gather_run_async路径(源码第 322 行起),内部通过_execute_component_async(haystack/utils/async_utils.py)调度。这意味着该组件既可作为同步 Pipeline 节点,也可接入pipeline.run_async()的异步执行体系。此外,每次 LLM 调用都被_trace_chat_generator_run包裹并挂载到当前 tracing span,可与 haystack/tracing/ 的追踪系统集成。

四、LLMDocumentContentExtractor:视觉 LLM 抽取图像文档内容

LLMDocumentContentExtractor解决的是"图像型文档"(扫描件、截图、PDF 页面)的文本还原问题:输入一批指向图片/PDF 文件的 Document,用视觉 LLM 把图像内容抽取为结构化文本,写回 Document 的content字段

4.1 工作原理

根据 llm_document_content_extractor.py 源码 与文档说明,其处理链路为:

  1. 每个输入 Document 先经DocumentToImageContent组件(haystack/components/converters/image/document_to_image.py)转换为图像内容——它依据 Document 元数据中的文件路径(默认字段file_path,可选page_number指定 PDF 页码)加载文件;
  2. 提示词与图像数据一起打包成一条 chat message 发给ChatGenerator(必须支持视觉输入,例如配置了视觉能力的 OpenAI 模型);
  3. 解析 LLM 响应,写回 Document 的content

4.2 提示词约束:不能有变量

LLMMetadataExtractor不同,本组件的提示词不能包含任何 Jinja 变量,只能包含抽取指令。源码用_validate_prompt_no_variables在构造时强制校验(源码第 245-254 行):通过SandboxedEnvironment().parse+meta.find_undeclared_variables检测模板变量,一旦发现变量立即抛出ValueError。原因在于图像数据是随 chat message 一并传递的,提示词本身无需也不允许引用动态内容。

组件内置了默认提示词模板DEFAULT_PROMPT_TEMPLATE(源码第 33-61 行),其要点包括:按阅读顺序用 Markdown 抽取内容;图形/图表/地图等视觉元素不抽取而是用[img-caption][/img-caption]标注描述性说明;表格用 Markdown 输出并在下方加[table-caption][/table-caption]说明;表单用 Markdown 还原复选框状态;最终返回包含document_content键的单个 JSON 对象。

4.3 构造参数详解

def __init__(*, chat_generator: ChatGenerator, prompt: str = DEFAULT_PROMPT_TEMPLATE, file_path_meta_field: str = "file_path", root_path: Optional[str] = None, detail: Optional[Literal["auto", "high", "low"]] = None, size: Optional[tuple[int, int]] = None, raise_on_failure: bool = False, max_workers: int = 3)
参数说明默认值
chat_generator代表 LLM 的ChatGenerator实例,必须支持视觉输入并返回纯文本响应必填
prompt提供给 LLM 的指令文本,不得包含 Jinja 变量DEFAULT_PROMPT_TEMPLATE
file_path_meta_fieldDocument 元数据中保存文件路径的字段名"file_path"
root_path文档文件所在的根目录。提供后,元数据中的文件路径将相对于该路径解析,并保证不越出该目录;为None时按绝对路径处理且不做包含性检查None
detail图像的细节级别(仅 OpenAI 支持):"auto"/"high"/"low",处理图片时传给 chat_generatorNone
size若提供,将图像等比缩放到指定 (width, height) 范围内,减小文件体积、内存占用与处理耗时,适合有分辨率限制的模型或需传输到远程服务的场景None
raise_on_failureTrue时 LLM 异常直接抛出;为False时记录日志并返回失败文档False
max_workersThreadPoolExecutor跨文档并行调用 LLM 的最大线程数3

安全提示(源码文档字符串明确强调):该组件会按file_path_meta_field指向的路径读取宿主机文件系统。如果文档元数据可能受不可信输入影响,务必设置root_path指向专用数据目录,使绝对路径或../这类路径穿越载荷被拒绝而不是被读取。

4.4 响应处理:三种解析分支

源码中的_process_response(第 256-269 行)定义了 LLM 响应的三种处理分支,理解这一点对写提示词至关重要:

  1. 纯字符串(非 JSON 或非 JSON 对象):整个响应直接作为 Document 的content
  2. 仅含document_content键的 JSON 对象:该键的值写入content
  3. 含多个键的 JSON 对象document_content(若存在)的值写入content,其余键全部合并进 Document 的metadata——这允许你在同一次 LLM 调用中同时抽取文本与附加元数据(如来源、创建日期等)。

如果 LLM 返回合法的 JSON 但不是对象(如数组或原始值),则被判定为错误。因此推荐在chat_generatorgeneration_kwargs中配置{"response_format": {"type": "json_object"}}以强制结构化输出。

4.5 使用示例与失败语义

from haystack import Document from haystack.components.generators.chat import OpenAIChatGenerator from haystack.components.extractors.image import LLMDocumentContentExtractor chat_generator = OpenAIChatGenerator() extractor = LLMDocumentContentExtractor(chat_generator=chat_generator) documents = [ Document(content="", meta={"file_path": "image.jpg"}), Document(content="", meta={"file_path": "document.pdf", "page_number": 1}), ] updated_documents = extractor.run(documents=documents)["documents"] print(updated_documents) # [Document(content='Extracted text from image.jpg', # meta={'file_path': 'image.jpg'}), # ...]

注意示例中两个 Document 的content都为空,真正的输入是meta中的file_path(图片)与file_path+page_number(PDF 指定页)。run(documents)返回{"documents": [...], "failed_documents": [...]}:失败的文档会带有content_extraction_error元数据键,可据此调试或稍后重新处理。

LLMDocumentContentExtractor同样实现了warm_up(若生成器有warm_up方法则调用之)、to_dict/from_dict(反序列化时经deserialize_chatgenerator_inplace还原生成器)、warm_up_async/close/close_async等完整生命周期方法,可直接放入异步 Pipeline。

五、如何选择:三个组件的对比与组合

维度NamedEntityExtractor (v2.18)LLMMetadataExtractorLLMDocumentContentExtractor
抽取内容预定义实体(人物/组织/地点等)任意 JSON 元数据图像文档的正文文本
模型类型专用 NER 模型(HF/spaCy)文本 LLM视觉 LLM
输出落点metadatametadatacontent(+ 可选metadata
失败处理ComponentErrorfailed_documents+ 两个元数据键failed_documents+content_extraction_error
提示词变量必须有{{ document.content }}禁止任何变量
典型场景高吞吐、低成本的离线实体标注文档标签、实体、摘要等灵活元数据抽取扫描件、PDF、截图的文本化

三者可以在同一条 Pipeline 中组合使用:先用LLMDocumentContentExtractor把扫描 PDF 转成文本,再交给LLMMetadataExtractor抽取实体类元数据,形成"图像文档 → 文本 → 结构化元数据"的完整链路。这也是文档所述失败重试机制(metadata_extraction_response/metadata_extraction_error参与下一轮提示词)最常出现的组合场景。

六、延伸阅读

  • Extractors API 参考(本文依据):docs-website/reference_versioned_docs/version-2.18/haystack-api/extractors_api.md
  • 源码实现:haystack/components/extractors/llm_metadata_extractor.py、haystack/components/extractors/image/llm_document_content_extractor.py
  • 测试用例:test/components/extractors/test_llm_metadata_extractor.py(含page_range、失败处理、序列化等行为的验证)
  • 依赖组件:图片转文档 DocumentToImageContent、异步调度工具 haystack/utils/async_utils.py
  • 版本演进记录:extractors 相关 release notes 见 releasenotes/notes/(如 remove-named-entity-extractor-a8d65992a201a775.yaml、add-token-to-named-entity-extractor-3124acb1ae297c0e.yaml)

【免费下载链接】haystackOpen-source AI orchestration framework for building context-engineered, production-ready LLM applications. Design modular pipelines and agent workflows with explicit control over retrieval, routing, memory, and generation. Built for scalable agents, RAG, multimodal applications, semantic search, and conversational systems.项目地址: https://gitcode.com/GitHub_Trending/ha/haystack

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询