用 Haystack 搭简历语义检索管线岗位筛选实操指南【免费下载链接】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/haystackHR 手里堆着两百份 PDF 简历岗位要求写在一段文字里人工逐份翻看完天就黑了。Haystack 的文档转换、语义检索组件把这套流程收敛成一条可复用的简历语义检索管线从 PDF 解析到按岗位匹配度排序全程代码化。项目速览Haystack 是 Deepset 开源的 AI 编排框架定位是构建生产级 LLM 应用文档解析、向量检索、Prompt 组装、模型调用拆成一个个组件用 Pipeline 串起来。对做 RAG、语义搜索的团队来说它提供的是显式可控的数据流向而不是一个黑盒问答接口。快速起步装包只需一条命令haystack-ai是核心包文档转换依赖 pypdf、python-docx 等由 extras 拉齐。跑一个简历筛选 demo本地只需要 Python 环境和一个 OpenAI API Key向量化和生成阶段用文档库用内置的 InMemoryDocumentStore不用装数据库pip install haystack-ai[openai,pypdf,docx] export OPENAI_API_KEYsk-...下面这段验证最小链路一份 PDF 简历转成Document对象确认正文真的提取出来了。from haystack.components.converters import PyPDFToDocument from haystack.dataclasses import Document docs PyPDFToDocument().run(sources[resumes/sample.pdf])[documents] print(len(docs), docs[0].content[:80])给一份 JD 筛两百份简历批量转换一次读进整个文件夹简历有 PDF、Word、纯文本混杂。MultiFileConverter按文件后缀自动路由到对应的子转换器不用自己写 if-else 分格式。它支持给每个源文件挂元数据我们把文件名写进meta[source]后面聚合分数、回溯原件都靠它。from haystack.components.converters import MultiFileConverter, PyPDFToDocument, DocxToDocument, TextFileToDocument converter MultiFileConverter( converters{pdf: PyPDFToDocument(), docx: DocxToDocument(), txt: TextFileToDocument()} ) docs converter.run(sourcesresumes/, meta{source: resumes/})[documents]清洗与分块因为扫描件和模板化简历里页眉页脚、多余空白特别多先过一遍DocumentCleaner去重去噪再用DocumentSplitter切块——块太小检索粒度不够块太大一个块里混进多个人的信息200 词左右是经验值from haystack.components.preprocessors import DocumentCleaner, DocumentSplitter docs DocumentCleaner().run(documentsdocs)[documents] chunks DocumentSplitter(split_byword, split_length200, split_overlap20).run(documentsdocs)[documents]向量化入库OpenAIDocumentEmbedder同时处理简历块和岗位描述用同一个模型保证两者在同一向量空间。注意 embedding 是在入库前算好的检索时只对 query 做一次向量化。from haystack.components.embedders import OpenAIDocumentEmbedder from haystack.document_stores.in_memory import InMemoryDocumentStore embedder OpenAIDocumentEmbedder(modeltext-embedding-3-small) chunks embedder.run(documentschunks)[documents] store InMemoryDocumentStore() store.write_documents(chunks)双路召回 融合排序纯关键词检索会漏掉同义表述K8s vs Kubernetes纯向量检索又会漏掉硬性术语。所以这里 BM25 和向量检索各跑一路用DocumentJoiner做倒数排名融合相当于让两位独立评委各交一份短名单再合并from haystack.components.retrievers.in_memory import InMemoryBM25Retriever, InMemoryEmbeddingRetriever from haystack.components.joiners import DocumentJoiner bm25 InMemoryBM25Retriever(document_storestore) vector InMemoryEmbeddingRetriever(document_storestore) joiner DocumentJoiner(join_modereciprocal_rank_fusion) query embedder.run(textjd)[embedding] bm25_docs bm25.run(queryjd)[documents] vector_docs vector.run(query_embeddingquery)[documents] best joiner.run(documents[bm25_docs, vector_docs])[documents]生成匹配度报告最后一段 Prompt 把排名靠前的简历片段交给 LLM。ChatPromptBuilder把岗位 JD 简历片段填进模板OpenAIChatGenerator出结构化结论ChatMessage用fromassistant构造系统指令。from haystack.components.builders import ChatPromptBuilder from haystack.components.generators.chat import OpenAIChatGenerator from haystack.dataclasses import ChatMessage builder ChatPromptBuilder( template[You are an HR assistant. Score each candidate 1-10.\nJD: {{ jd }}\nResumes: {{ resumes }}] ) messages builder.run( messages[ChatMessage.from_assistant(Only output JSON with name, score, reasons.)], jdjd, resumes[c.content for c in best[:10]], )[messages] reply OpenAIChatGenerator(modelgpt-4o-mini).run(messagesmessages)[replies][0]到这里两百份简历进、一份带评分的短名单出这条主线已经闭环。想固化成服务就照上面顺序add_componentconnect挂进Pipeline每一段单独可测、可换。检索链路怎么串起来拆开看有两个点值得理解。一是分块与聚合DocumentSplitter按词数切开、块间保留重叠防止断句一份简历变成多个Document块所以某块命中约等于这个候选人命中。想按人出最终排名需要按meta[source]把各块分数归并——取最大值或加权平均都行这一步框架不做留给业务代码。二是混合检索的融合方式。DocumentJoiner支持四种join_modeconcatenate拼接、merge去重、reciprocal_rank_fusion倒数排名融合、distribution_based_rank_fusion基于分数分布融合。RRF 的好处是不依赖两个检索器分数的绝对量纲只吃排名所以 BM25 的词频分和向量的余弦分可以安全混在一起比较。过来人提醒这里有个容易忽略的点InMemoryDocumentStore重启即空适合本地验证管线上生产要换成 Elasticsearch、OpenSearch 这类持久化后端写库和检索代码几乎不用改只换 DocumentStore 和对应的 Retriever 即可。还有个坑PDF 转出来的文本质量参差扫描件基本提不出正文。DocumentCleaner默认会去重复行、压缩空白能兜住一部分模板噪声纯图片简历得先接 OCR再走后面的转换链路。最后是过滤。InMemoryEmbeddingRetriever的run支持运行时传filters比如先按学历硬条件缩小召回范围比检索完再手动过滤省事还能配合FilterPolicy决定与初始化时过滤条件的合并方式。下一步把InMemoryDocumentStore换成 Elasticsearch 或 OpenSearch 后端支撑十万级简历和持久化。用haystack.tracing的 Tracing 机制给pipeline.run()打点定位是转换、向量化还是生成阶段慢。利用OpenAIChatGenerator的response_format把匹配度评分从自由文本改成 JSON Schema 约束的结构化输出方便直接落库。Pipeline 编排文档docs-website/docs/concepts/InMemoryDocumentStore 用法haystack/document_stores/in_memory/document_store.pyPDF 转换器源码haystack/components/converters/pypdf.py【免费下载链接】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),仅供参考