Haystack 2.x Classifiers 组件详解:DocumentLanguageClassifier 与 TransformersZeroShotDocumentClassifier
【免费下载链接】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 仓库中 version-2.21 的 Classifiers API 参考文档(docs-website/reference_versioned_docs/version-2.21/haystack-api/classifiers_api.md),系统讲解 2.21 时期haystack.components.classifiers包下的两个文档分类组件:基于langdetect的DocumentLanguageClassifier与基于 Hugging Face 零样本分类管线(zero-shot-classification)的TransformersZeroShotDocumentClassifier。读完后你将能够:在自己的 Pipeline 中为文档自动标注语言元数据并配合MetadataRouter分流,以及使用 NLI 模型对文档内容做免训练的标签分类,把预测结果写入文档 metadata 供下游路由或筛选。
Classifiers 在 Haystack 2.x 组件体系中的定位
Haystack 2.x 对"分类"与"路由"做了明确区分:Classifier 负责改变文档的 metadata 值(如语言、标签),但不改变数据的流向;把数据分流到多个下游组件是 Router 的职责。这一点有明确的演进记录:仓库中的 separate-classifiers-from-routers 发布说明 记载了"从 DocumentLanguageClassifier 中移除路由功能、将 TextLanguageClassifier 更名为 TextLanguageRouter"的变更,并指出"在 Haystack 2.x 中,Classifiers 会改变 metadata 值但不将输入路由到多个输出,后者保留给 routers",还建议在索引管线中把DocumentLanguageClassifier与MetadataRouter组合使用来完成"分类 + 路由"。而 move-classifiers 发布说明 则记录了文本/文档语言分类组件从 routers 包迁移进 classifiers 包的历史。
这一设计决定了本文两个组件的共同使用模式:分类器只"贴标签"(写 metadata),路由由独立的 Router 组件完成。
DocumentLanguageClassifier:为文档标注语言元数据
DocumentLanguageClassifier会对每个文档进行语言识别,并把识别结果写进文档 metadata。初始化时传入候选语言列表;如果文档文本与所有指定语言都不匹配,metadata 值会被设为"unmatched"。要对文档按语言分流,需要在分类器之后接MetadataRouter;而要对纯文本(非 Document 对象)路由,则应改用TextLanguageRouter组件。
完整用法示例
下面这个示例构建了一条"语言分类 → 按元数据路由 → 写入文档存储"的索引管线,只有英文文档会被写入InMemoryDocumentStore:
from haystack import Document, Pipeline from haystack.document_stores.in_memory import InMemoryDocumentStore from haystack.components.classifiers import DocumentLanguageClassifier from haystack.components.routers import MetadataRouter from haystack.components.writers import DocumentWriter docs = [Document(id="1", content="This is an English document"), Document(id="2", content="Este es un documento en español")] document_store = InMemoryDocumentStore() p = Pipeline() p.add_component(instance=DocumentLanguageClassifier(languages=["en"]), name="language_classifier") p.add_component(instance=MetadataRouter(rules={"en": {"language": {"$eq": "en"}}}), name="router") p.add_component(instance=DocumentWriter(document_store=document_store), name="writer") p.connect("language_classifier.documents", "router.documents") p.connect("router.en", "writer.documents") p.run({"language_classifier": {"documents": docs}}) written_docs = document_store.filter_documents() assert len(written_docs) == 1 assert written_docs[0] == Document(id="1", content="This is an English document", meta={"language": "en"})几个关键点值得注意:
DocumentLanguageClassifier(languages=["en"])只识别英文;示例中的西班牙语文档不会被丢弃,而是被标记为meta={"language": "unmatched"},走不到en路由分支;MetadataRouter的规则语法为{路由名: {metadata字段: 过滤表达式}},上例中"en": {"language": {"$eq": "en"}}表示当文档meta里的language字段等于"en"时,从router.en输出 socket 放行;- 分类器的输出 socket 固定为
documents(list[Document]),与路由器的documents输入直接相连。
构造函数参数
def __init__(languages: Optional[list[str]] = None)| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
languages | list[str] | None(等价于["en"]) | ISO 语言代码列表,支持的具体语言以langdetect库的说明为准;文档内容与列表均不匹配时标记为unmatched |
底层实现依赖langdetect库做统计式语言识别,无需下载模型或调用外部 API,适合对多语言混合语料做粗粒度语言分拣。
run 方法
@component.output_types(documents=list[Document]) def run(documents: list[Document])- 输入:
documents—— 待做语言分类的文档列表; - 输出:
documents—— 每个文档的 metadata 中新增language字段的文档列表; - 异常:输入不是
Document列表时抛出TypeError。
分类结果只写 metadata、不改写content,因此该组件可以安全地放在索引管线的前置阶段,供后续的过滤器、路由器和写入器使用。
TransformersZeroShotDocumentClassifier:基于 Hugging Face 的零样本文档分类
TransformersZeroShotDocumentClassifier使用 Hugging Face 的 zero-shot-classification 管线(底层是 NLI,自然语言推理模型)对文档做零样本分类:无需针对标签做训练,只需在初始化时给出model和labels,预测出的标签会写入文档 metadata 的classification字段。分类默认作用于文档的content字段,也可以通过classification_field参数改到某个 metadata 字段上执行。
文档中列出的、可用于该任务的候选模型包括:
valhalla/distilbart-mnli-12-3cross-encoder/nli-distilroberta-basecross-encoder/nli-deberta-v3-xsmall
完整可选模型列表可参考 Hugging Face 模型库中 pipeline_tag 为 zero-shot-classification 的 NLI 类模型。
完整用法示例
下面这条管线演示了"BM25 检索 → 零样本情感分类"的组合:两条文档一条表达积极的一天、一条表达消极的一天,cross-encoder/nli-deberta-v3-xsmall模型配合["positive", "negative"]标签对检索结果自动打标签:
from haystack import Document from haystack.components.retrievers.in_memory import InMemoryBM25Retriever from haystack.document_stores.in_memory import InMemoryDocumentStore from haystack.core.pipeline import Pipeline from haystack.components.classifiers import TransformersZeroShotDocumentClassifier documents = [Document(id="0", content="Today was a nice day!"), Document(id="1", content="Yesterday was a bad day!")] document_store = InMemoryDocumentStore() retriever = InMemoryBM25Retriever(document_store=document_store) document_classifier = TransformersZeroShotDocumentClassifier( model="cross-encoder/nli-deberta-v3-xsmall", labels=["positive", "negative"], ) document_store.write_documents(documents) pipeline = Pipeline() pipeline.add_component(instance=retriever, name="retriever") pipeline.add_component(instance=document_classifier, name="document_classifier") pipeline.connect("retriever", "document_classifier") queries = ["How was your day today?", "How was your day yesterday?"] expected_predictions = ["positive", "negative"] for idx, query in enumerate(queries): result = pipeline.run({"retriever": {"query": query, "top_k": 1}}) assert result["document_classifier"]["documents"][0].to_dict()["id"] == str(idx) assert (result["document_classifier"]["documents"][0].to_dict()["classification"]["label"] == expected_predictions[idx])这个例子的要点:
- 分类器直接消费检索器的输出文档,
pipeline.connect("retriever", "document_classifier")自动完成 socket 匹配; - 分类结果存放在
document.to_dict()["classification"]中,其中label键就是预测出的标签; - 该模型需要本地下载并加载,属于较重的组件,
batch_size默认值为 1,处理大批量文档时建议显式调大。
构造函数参数
def __init__(model: str, labels: list[str], multi_label: bool = False, classification_field: Optional[str] = None, device: Optional[ComponentDevice] = None, token: Optional[Secret] = Secret.from_env_var( ["HF_API_TOKEN", "HF_TOKEN"], strict=False), huggingface_pipeline_kwargs: Optional[dict[str, Any]] = None)| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
model | str | 必填 | Hugging Face 上零样本文档分类模型(NLI 模型)的名称或本地路径 |
labels | list[str] | 必填 | 候选标签集合,例如["positive", "negative"];标签语义依赖所选模型的能力 |
multi_label | bool | False | 是否允许多个标签同时为真。False时对分数做归一化,使每个序列的标签似然之和为 1;True时各标签相互独立,对每个候选做"蕴含分 vs 矛盾分"的 softmax 归一化 |
classification_field | str | None | 用于分类的文档 metadata 字段名;不设置时默认使用Document.content |
device | ComponentDevice | None | 模型加载设备;为None时自动选择默认设备。若huggingface_pipeline_kwargs中指定了 device/device map,则以后者为准 |
token | Secret | 从环境变量HF_API_TOKEN或HF_TOKEN读取(非强制) | 访问 Hugging Face 的 HTTP Bearer 认证令牌,可在 HF 账户设置中查看 |
huggingface_pipeline_kwargs | dict[str, Any] | None | 透传给 Hugging Face 文本分类管线构造函数的关键字参数,可用于控制模型加载行为 |
其余方法与 run 行为
warm_up():在管线运行时初始化组件(加载模型);to_dict()/from_dict(data):组件的序列化与反序列化,支持把组件配置持久化进 Pipeline 的 YAML/JSON 表示;run:
@component.output_types(documents=list[Document]) def run(documents: list[Document], batch_size: int = 1)- 输入:
documents待分类文档列表;batch_size处理每个文档内容时使用的批大小; - 输出:
documents—— 每个文档的 metadata 中新增classification字典; - metadata 结构:
classification.label存放预测标签;当multi_label=True时,classification.details键下可读取每个候选标签各自的得分,便于自定义阈值或做细粒度过滤。
由于分类结果写在 metadata 里,TransformersZeroShotDocumentClassifier之后可以直接接MetadataRouter或文档过滤器,按classification.label将文档分流到不同处理分支,实现"检索 → 分类 → 分流"的完整链路。
版本语境与当前仓库状态
本文所依据的 API 文档属于version-2.21版本的存档(docs-website/reference_versioned_docs/version-2.21/haystack-api/classifiers_api.md),描述的是 Haystack 2.21 时期haystack.components.classifiers包内可用的组件。需要说明的是,当前仓库主干已经演进到 3.x:从 deprecate-langdetect-components 发布说明 和 remove-langdetect-components 发布说明 可以看到,基于langdetect的DocumentLanguageClassifier与TextLanguageRouter在 3.0 中被移出 Haystack 核心,迁移至独立的langdetect-haystack集成包(pip install langdetect-haystack),导入路径相应从haystack.components.classifiers变为haystack_integrations.components.classifiers.langdetect;在当前仓库的haystack/components/源码目录中也不再包含 classifiers 子包。因此如果你在 2.21 版本上维护既有管线,本文的组件名、导入路径与参数签名均可直接沿用;若升级到 3.x,语言分类部分需切换到集成包,而零样本分类组件的参数设计(model/labels/multi_label/classification_field/token/huggingface_pipeline_kwargs)在参考文档中的语义仍然是理解这类组件的标准范式。
小结与延伸阅读
- 两个组件的共同点是"只改 metadata、不改变流向",语言路由交给
MetadataRouter,纯文本语言路由交给TextLanguageRouter,这符合 Haystack 2.x classifiers 与 routers 的职责划分(见 separate-classifiers-from-routers 发布说明); DocumentLanguageClassifier轻量、离线、零依赖模型,适合多语言语料索引前的语言分拣,未命中语言统一落到unmatched;TransformersZeroShotDocumentClassifier以 NLI 零样本分类换取标签灵活性,预测标签与逐标签得分分别落在classification.label与classification.details,可无缝衔接后续的元数据路由与过滤;- 相关发布历史可参考 add-zero-shot-document-classifier 发布说明 与 document-language-classifier 发布说明,原始 API 文档位于 classifiers_api.md。
【免费下载链接】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),仅供参考