
先交代一下背景。我上个月帮一个做招聘的朋友处理简历他们用的是某SaaS招聘平台简历解析加智能筛选按份收费一份0.6元。当时手里有180份技术岗简历算下来要108块。钱不多但我就觉得这个钱花得不值——解析简历不就是读文本、提取字段筛选不就是按条件打标这活儿AI完全能干凭什么每份收我6毛于是周末我花了一天时间搭了一套零成本的AI工作流把这份钱彻底省了。这篇就把整个设计思路、搭建过程和踩过的坑完整写出来。如果你也想处理简历筛选、批量文档解析、多AI协作这类场景或者单纯好奇怎么用免费工具拼出一条自动化流水线这篇可以直接给你抄作业。1. 先算清楚账6毛钱到底花在哪了1.1 平台服务的真实成本拆解很多人觉得6毛钱一份不贵但你要看这个钱买的是什么。招聘平台的AI解析服务本质上是三件事的打包第一把PDF、Word格式的简历转成可结构化读取的文本第二从文本里抽取出姓名、工作年限、技能栈、教育背景这些字段第三按照岗位要求打个匹配分。这三件事每一件用开源工具或者免费AI都能做。平台收的6毛钱买的就是不用自己折腾的省心。但问题在于如果一个月只处理几份简历花这个钱完全合理——自己折腾的时间成本比6毛钱贵多了。可一旦到了批量场景比如秋招春招几百上千份简历这个成本就变成几十上百块。更重要的是平台服务是个黑盒筛选规则不能完全自定义想加一个排除频繁跳槽候选人的逻辑还得看平台支不支持。我朋友当时就卡在这平台打分老是把一些明显不合适的985硕士排前面因为他们项目经历写得多平台只看关键词匹配度。这就引出了自己做工作流的第一个理由成本可控规则可自定义。6毛钱只是导火索真正让人受不了的是黑盒逻辑不可控。1.2 省6毛钱和零成本之间的差距但零成本这事得说清楚它不完全是免费的。我指的零成本是不按调用次数付费、不买SaaS会员、不租云服务器但你需要付出的是自己的时间和学习成本。我在方案里对比了两条路线一条是纯免费额度路线用Coze扣子这类免费工作流平台另一条是本地部署路线用Ollama跑开源模型。两条路线都不能算绝对的零成本。Coze平台有免费额度但每天有调用上限本地部署不花钱但需要你有一台像样的电脑或者一台旧服务器。我当时的情况是手里恰好有台吃灰的MacBook ProM1芯片16G内存跑7B参数的小模型足够用了。如果你连合适硬件都没有那就走Coze路线免费额度对个人处理简历是完全够的。成本对比拉个表给各位看。方案单项成本硬件要求筛选100份简历耗时数据隐私SaaS平台0.6元/份共60元无浏览器操作平台处理约1小时简历上传到第三方Coze工作流免费额度内0元无网页操作约2-3小时含限额等待简历上传到字节云端Ollama本地脚本0元16G内存电脑约40分钟数据完全不出本机当初看到这个对比我毫不犹豫选了第三条。但这里也提醒一句如果你完全不会写代码建议先走Coze因为本地脚本路线需要你至少会一点Python不会写代码硬上的话学习成本可能真的超过省下的几十块钱。2. 工作流的核心设计思路2.1 四段式流水线解析、提取、硬筛、深评我搭的这套工作流核心是一条四段式流水线文档解析、结构化提取、硬性条件初筛、深度匹配评估。为什么分四段而不是让AI一次读完直接给结论因为一次读完直接给结论看着省事实际效果非常差。原因很简单大模型的上下文窗口再长处理一段几千字的简历时也会出现信息遗忘尤其是中后段内容。你让它读完简历后给个匹配分它往往只盯着开头和结尾看中间的工作经历细节被漏掉。把流程拆开之后每一步只聚焦一个任务模型出错率会低很多。四段式具体这么分的第一段把PDF/Word转成纯文本。这一步不需要AI用解析库直接做速度快、成本为零而且是后面所有步骤的基础。第二段把文本切成结构化字段。用AI从简历文本里提取出工作年限技能清单最近一段工作职责教育经历这些固定字段输出成JSON格式。第三段硬性条件筛选。这一步用的不是AI是普通代码逻辑。比如JD写了要求统招本科那就直接拒绝大专学历写了5年以上经验工作年限字段不足5的直接淘汰。硬条件用代码筛快、准、没有任何幻觉。第四段深度匹配评估。通过初筛的简历把结构化字段岗位JD一起扔给AI让它从技术栈匹配度、项目含金量、职业稳定性三个维度打分。这套设计的实际效果是180份简历到第三步就刷掉了120份真正进入AI深评的只有60份AI调用量降了三倍。不只是省钱速度也快很多。2.2 硬筛和AI评估的分工逻辑硬筛和AI评估的分工是我这套方案里最关键的一个决策。我见过很多人搭简历筛选工作流上来就让AI做所有事包括硬性条件判断。这是个巨大的坑。大模型做硬性判断有三个问题第一它会被幸存者偏差影响。比如你让它筛5年以上经验一份6年经验的简历可能因为某段经历描述不清晰被模型判成4年第二它容易受前后文干扰。候选人如果说自己本科学历硕士在读模型可能因为硕士两个字就把他归为硕士学历第三模型输出有随机性同一个判断多跑几次结果可能不一样。硬条件筛选需要的是确定性这种活交给代码是最稳的。但深评环节相反它需要的就是模型的综合理解能力。比如项目含金量这种判断没有绝对标准需要模型理解项目规模、技术难度、业务价值。这是代码做不了的事。所以我最后定的分工逻辑是能用规则判断的字段一律用代码需要综合理解的判断才交给AI。这个原则不仅适用简历筛选你搭任何工作流都能套用——先把能确定的部分固化成代码逻辑把不确定的部分留给模型才是效率最高的组合。2.3 为什么用多个AI协作而不是单模型硬扛我这个工作流里文档解析后的结构化提取、深度评估其实用了两个不同的模型调用。有时候还会用一个轻量模型先做提取一个更强模型做深评。为什么要多AI协作因为不同任务的难度不同用同一个模型是浪费。提取字段是个相对机械的任务模型只需要按照预设的JSON格式把信息填进去不需要太强推理能力用轻量快速的模型就够。深度评估需要理解力强模型明显更靠谱。如果全部用强模型调用成本高、速度慢全部用轻量模型深度评估的质量又不够。在Coze平台里你可以直接在工作流节点里配置不同的模型本地脚本里更简单同一个Ollama服务可以同时跑多个模型哪个环节需要哪个就调哪个。我在实操中就是用一个7B的qwen2.5做提取用一个13B的qwen2.5做深评。7B模型速度快处理一份简历的提取只要2秒13B模型质量高深评打分明显更合理。这里多说一句如果你的需求比较重比如要处理上百页的标书、论文提取环节本身也很复杂那就不能用轻量模型的惯性思维该上强模型就上强模型。多AI协作的本质不是谁的参数多就用谁而是哪个环节需要什么能力就给这个环节匹配对应能力的模型。3. 零成本工作流的搭建实操3.1 方案ACoze工作流——零代码也能搭如果你不会写代码Coze扣子是目前最适合你的免费工作流平台。我一开始先用Coze快速做了个原型验证思路确认四段式没问题之后才转头去写本地脚本。Coze路线大概分这么几步第一步创建一个Bot然后进入工作流标签页新建工作流。在画布上你会看到开始节点和结束节点中间可以拖入各种插件和模型节点。我的Coze工作流节点结构是开始节点上传简历文件 → 文档解析插件把PDF/Word转成纯文本 → 大模型节点1结构化提取让模型输出固定JSON字段 → 代码节点解析JSON执行硬性条件筛选 → 大模型节点2深度评估输入结构化字段JD要求 → 结束节点输出三个打分维度和综合结论第二步配置大模型节点1核心是写清楚提取规则。我用的提示词模板大概是你是一个简历信息提取助手。请从简历文本中提取以下字段并输出JSON格式 { 姓名: , 工作年限: 0, 最高学历: , 毕业院校: , 技能清单: [], 最近一段工作职责: , 项目经历摘要: } 要求工作年限请精确到年不足一年按0计算技能清单最多列10项只提取明确出现的技能名称输出必须为合法JSON不要包含任何其他文字。注意输出必须为合法JSON这句必须写。模型默认会给你补充一段解释性文字比如好的根据简历内容我提取到了以下信息这一坨文字会导致后续代码节点解析失败。加这句提示能大幅提高解析成功率但还不能保证百分之百所以后面要接一个代码节点做容错。第三步配置代码节点。Coze支持写JavaScript或Python代码这里我用Python把模型1的输出解析成字典然后做硬筛逻辑。核心逻辑大概是先尝试把模型输出转成JSON如果失败就用正则表达式提取关键字段兜底然后判断是否满足硬条件把不满足的简历标记为淘汰并说明原因。import json import re def main(extracted_text: str, jd_requirements: dict) - dict: # 解析模型输出兼容markdown代码块包裹 cleaned extracted_text.strip() if cleaned.startswith(): cleaned re.sub(r^(json)?|$, , cleaned, flagsre.MULTILINE) try: data json.loads(cleaned) except json.JSONDecodeError: # 兜底正则提取工作年限 match re.search(r工作年限\s*:\s*(\d), extracted_text) data {工作年限: int(match.group(1)) if match else 0} # 硬性筛选 if data.get(工作年限, 0) jd_requirements.get(min_years, 0): return {status: rejected, reason: f工作年限不足要求{jd_requirements[min_years]}年} ... return {status: passed, structured_data: data}第四步配置大模型节点2做深度评估。这个节点输入的是结构化字段和JD原文要求模型输出三个维度打分和综合建议。我实际用的提示词是这样的你是一位资深技术招聘专家。请根据以下候选人信息和岗位要求从三个维度打分每项1-10分 1. 技术栈匹配度候选人的技能与岗位JD要求的重合程度 2. 项目含金量候选人项目经历的复杂度、规模与业务价值 3. 职业稳定性候选人平均任职时长有无频繁跳槽倾向 候选人信息 {结构化JSON} 岗位要求 {JD原文} 请输出JSON格式 {技术栈匹配度: 8, 项目含金量: 7, 职业稳定性: 6, 综合建议: 一句话结论}第五步配置结束节点并发布工作流。Coze的结束节点可以直接输出最终JSON也可以做成对话式回复。发布之后你就能在工作台里上传简历文件跑完整条流程。Coze方案的优点是全程不用写代码除了那个可选的代码节点浏览器操作就能搭好。缺点是免费额度有每日调用上限处理大批量简历时可能排队。个人处理个几十份简历完全够用。3.2 方案BOllama本地部署——数据不出本机的终极方案对技术基础好一点的朋友我强烈推荐本地部署方案。这套方案的好处是真正的零成本没有额度限制数据完全不出本机简历这种敏感信息交给第三方平台毕竟还是有隐私顾虑的。先装Ollama这是目前最省事的本地模型运行工具。装好之后拉两个模型一个7B轻量模型做提取一个13B强一点的模型做深评。# 安装OllamamacOS/Linux curl -fsSL https://ollama.com/install.sh | sh # 拉取两个模型 ollama pull qwen2.5:7b ollama pull qwen2.5:13b我用的都是通义千问系列因为中文简历处理效果比同参数量的其他开源模型要好。如果你电脑内存只有8G就别拉13B了两个都用7B如果你的电脑跑32G内存可以试试qwen2.5:32b深评质量会再上一个台阶。然后写一个Python脚本处理流程是这样的import ollama import fitz # PyMuPDF用于解析PDF from pathlib import Path def extract_text_from_pdf(pdf_path): doc fitz.open(pdf_path) text for page in doc: text page.get_text() return text EXTRACT_PROMPT 你是一个简历信息提取助手。请从简历文本中提取以下字段并输出JSON格式 {...同上文Coze节点1的提示词...} def extract_structured(text): response ollama.chat( modelqwen2.5:7b, messages[{role: user, content: EXTRACT_PROMPT text}], options{temperature: 0} ) return parse_json_with_fallback(response[message][content])每一步都值得说细一点。PDF解析我用的是PyMuPDFfitz库这个库解析文字版PDF速度非常快一份简历几十毫秒就搞定。但要注意如果简历是扫描件图片型PDFget_text()捞出来是空字符串这种情况后面单独说怎么处理。模型调用的temperature参数必须调成0。这一点极其重要。大模型默认的temperature是0.7生成结果就是有随机性的。提取字段这种任务要的是确定性把temperature设为0之后同样输入跑三次能拿到基本一致的结果后续硬筛才能稳定。我一开始没调这个参数同一份简历跑两次一次提取出3年经验一次提取出4年经验直接让硬筛规则失效了。深评模型的调用代码大体一样只是换模型、换提示词。串起来的主流程大概长这样def process_resume(pdf_path, jd, min_years, min_degree): text extract_text_from_pdf(pdf_path) structured extract_structured(text) # 硬筛 if structured.get(工作年限, 0) min_years: return {file: pdf_path, status: 淘汰, reason: 工作年限不足} if education_level(structured.get(最高学历, )) min_degree: return {file: pdf_path, status: 淘汰, reason: 学历不达标} # 深评 score deep_evaluate(structured, jd) return {file: pdf_path, status: 进入面试, **score}最后把所有简历跑一遍结果写入CSV文件。用Python的csv模块把每个候选人的状态、打分、淘汰原因输出成表格用Excel打开就能排序筛选。这个输出格式我是按朋友的使用习惯定的——他们HR团队不用再打开任何工具直接用筛选后的表格按分数排序就行。3.3 实际跑批效果和成本核算我把180份简历跑了一遍说下实测数字。我手里这台M1 MacBook Pro 16G跑批处理一份简历平均耗时14秒PDF解析0.1秒7B提取2秒13B深评12秒180份全跑完用了40多分钟。注意这是单线程跑的时间如果能多线程并发时间还能再压缩但Ollama同时并发多个请求会吃内存16G内存跑13B模型本身已经有点紧了并发多了容易OOM。建议一份一份串行跑稳。零成本方案的成果是一份简历都没花那6毛钱。平台筛选的正确率我没实测过但我这套方案自己定义的规则全部严格执行硬条件零误判深评打分经过我抽样复核了20份人工判断和AI判断一致的有17份另外3份AI给的分数偏保守但方向上没错。可视化对比最终结果的话最直观的数据是成本。180份简历平台方案要花108元而且规则不可调我这套方案电费忽略不计、时间成本一个周末要求你会写点代码之后每月处理500份以上简历也依然是零成本。4. 实操中的常见问题和排查技巧4.1 JSON输出不稳定的终极解法结构化提取环节最折磨人的问题就是模型输出的JSON格式不稳定。明明提示词里写了只输出合法JSON模型偶尔还是会给你来个解释性前缀或者末尾加个句号、多一两个反引号。我建议分三层做容错。第一层提示词里用few-shot示例给模型一个明确格式示范出错率能降低一半第二层在解析层做兼容处理把markdown代码块包裹、前后解释性文字都剥掉再尝试解析第三层真解析不了用正则从原始文本里抠关键字段比如re.search(r工作年限\s*:\s*(\d), text)。这三层下来我在180份简历里只有2份需要人工兜底成功率在99%以上。Coze的代码节点里同样按这个思路写。另外把所有调用的temperature调成0这是从源头减少随机性比后面增加容错层更省心。4.2 模型上下文超长怎么处理有朋友问我如果把整份JD加100份简历丢给模型让它陆续输出是不是更省事千万别这么干这就是上下文超长的典型场景。100份简历文本可能有20万字超过模型的上下文窗口它会忽略中间大部分内容只凭开头几份和结尾几份给判断结果完全不可用。另外还有一种隐性超长的情况单份简历本身不长但你把JD、候选人全部信息、历史对话记录一股脑塞进同一个会话模型每轮都带着历史回放上下文就爆了。我处理这类问题的办法是保持单次调用无状态每次调用前把提示词和当前这份简历拼好不带任何历史消息单份简历超过模型上下文极少见除非是那种带论文级别的简历就先做摘要摘要长度限制在3000字以内。Coze里有个好处是官方已经帮你处理了上下文拼接逻辑但本地脚本你得自己控制好messages数组别把上一份简历的输入残留在messages里。我的习惯是每个新请求都从零组装messages绝不复用历史。4.3 扫描件简历和文字乱码简历PDF分两种文字版和扫描版。文字版用fitz.get_text()直接能提取扫描版是图片直接提取返回空字符串必须走OCR。我测过几个方案Tesseract中文识别效果一般有错别字PaddleOCR效果好但装起来比较麻烦。对于只有少数扫描件的情况我的建议是手动处理把扫描件扔给浏览器里的免费OCR工具或者干脆手动录入一下。但如果你是批量扫描件比如几百份纸质简历统一扫描那就得认真部署PaddleOCR这个不该省。文字乱码的问题主要是编码没对上。get_text()默认会按PDF内部编码输出有些老PDF会乱。加上这两个参数会好一些doc fitz.open(pdf_path, base_font_size12, sortTrue)排序参数解决多栏排版文字顺序错乱的问题。还有一点经验Word文档转出来的PDF乱码概率大遇到乱码直接回到Word源文件重新导一次一般就好了。4.4 常见问题速查表问题症状原因解法JSON格式不稳定解析报错、字段缺失模型随机性temperature设为0加few-shot正则兜底提取硬筛误判5年经验被计成4年提取环节出错检查提示词是否明确不足一年按0计算手动抽查提取结果深评打分不合理明显优秀候选人分数低提示词缺少评估标准给模型具体打分维度定义和各档位打分标准PDF解析出空文本简历内容为空扫描件或图片型PDF用OCR工具先转文字别直接硬解析提取字段乱码出现问号或乱码编码问题调整fitz参数换PDF源文件本地跑批速度慢单份要30秒以上模型参数太大或线程占用降模型参数关闭后台大内存应用免费额度耗尽跑着跑着不能调用了平台日限额错峰使用把部分环节切到本地模型4.5 一个容易被忽略的坑JD版本一致最后说一个实操中踩过的坑就是JD版本不一致的问题。朋友最早发我的JD是一份旧的后来岗位微调过要求但工作流里配的还是老版本结果筛选结果和HR预期对不上白白折腾了一晚上排查。后来我把JD固定为一个单独文本文件每次跑批前检查一下工作流引用的是不是最新版JD。如果你把JD写死在提示词里每次改JD都要改整个工作流配置非常麻烦。建议单独放一个配置项Coze里就放在变量里本地脚本里就放在一个config.json里跑批之前确认一下。这其实引出一个更通用的设计原则把会变的东西岗位要求、筛选标准、公司偏好全都抽出来作为配置参数工作流的逻辑骨架保持不变。这样岗位一变你只需要改配置不用改代码流复用价值大大提升。5. 这套工作流的后续扩展空间搭完这套简历筛选工作流之后我试着把同样的设计思路迁移到别的场景发现可玩性比想象中高。因为整个流水线的骨架其实是通用的解析输入、结构化提取、规则筛选、深度评估。换一套提示词和规则就能处理完全不同的任务。比如我把硬筛规则换成排除负面关键词深评维度从技术匹配改成文风匹配度就把它变成了一个批量审稿工具帮我处理几十篇投稿文章的初步筛选。朋友做电商的说想让我把流程改成竞品描述批量解析卖点热度打分逻辑也一样成立。要做这些扩展本地脚本方案最灵活因为你可以直接改配置文件Coze方案也支持就是每次改配置要去画布上调节点参数稍微繁琐一点。所以我的建议是如果你只是玩玩Coze足够了如果你预感到需要频繁调整、做多场景复用直接上本地脚本迁移成本低很多。我个人在实际操作中的体会是这套零成本工作流的最大价值不在于省了多少服务费而是它让我重新理解了大模型应用的边界。模型不是万能的能确定的事情用代码做才是最高效的模型也不是越强越好匹配任务难度的成本才是最优解。最后再分享一个小技巧搭这类工作流的时候第一版不要追求完美先拿10份样本跑通全程再逐步加规则优化。我最初直接拿全量180份跑结果因为JSON解析问题废了好几次批。先小批量验证确认无误再上全量能少走很多弯路。这个习惯到现在我搭任何自动化流程都会用。