1. 简历筛选这件事为什么值得用 Agent 重做一遍招聘旺季的时候一个岗位放出去三天收两百份简历这不是夸张是很多技术团队的真实日常。HR 和用人部门面对的问题从来不是没有简历而是简历太多、时间太少、判断太累。传统做法无非两种要么人工逐份看看到后面眼睛发花、标准漂移要么用关键词匹配工具做初筛结果把精通 Java 并发和了解 Java 并发当成同一类人推上来。这两种做法的共同缺陷在于它们都在做匹配而不是在做校验。匹配解决的是这个人会不会我们要的技能校验解决的是这个人写的东西自己能不能对得上。后者才是筛简历时最容易被忽略、却最能暴露问题的环节。我举个真实场景。有份简历在项目经历里写主导设计日活百万级系统的缓存架构但在技能清单里连 Redis 都没提另一份简历写2021 年 3 月到 2023 年 6 月在 A 公司可教育经历里显示 2022 年 9 月到 2023 年 1 月还在全日制读研。这些矛盾不是造假很多时候是候选人自己写简历时前后没对齐但恰恰是这些细节能帮筛选者快速判断一份简历的可信度水位。Seed-2.1-pro 这个开源求职情报工具切入的正是这个点。它不替你做投不投的决定也不替候选人做投哪家的决定它做的事情很聚焦把一份 PDF 简历读进来用 Agent 的方式做结构化解析然后逐条比对简历内部的信息一致性把自相矛盾的地方标出来。关键词里出现的 PDF、Java、Agent、开源基本勾勒出了它的技术轮廓——一个用 Java 生态做 PDF 解析、用 Agent 编排校验逻辑的开源项目。这篇文章适合三类人看正在做招聘工具的产品或研发、想自己搭一套简历分析流水线的工程师、以及单纯对 Agent 落地场景感兴趣的技术人。我会从它解决的问题、PDF 解析的坑、Agent 校验逻辑的设计、以及实际跑起来之后的经验几个层面把这件事讲透。2. 简历 PDF 解析看起来最简单实际上最容易翻车的一环2.1 为什么 PDF 是简历分析的第一道坎很多人以为简历解析就是把 PDF 转成文字调个库就完事。真做过的人都知道PDF 是所有文档格式里最不结构化的一种。它本质上是打印指令的集合描述的是在哪个坐标画哪个字符而不是这段文字属于哪个段落。同样一份简历用 Word 导出、用 LaTeX 导出、用在线模板导出底层结构可能完全不同。这就导致一个很现实的问题你拿到的文字流顺序可能是乱的。比如一份双栏排版的简历解析出来可能是姓名 电话 邮箱 教育经历 项目经历 技能这样交错着来因为解析器是按坐标从上到下、从左到右扫的它不知道左边一栏和右边一栏是两个独立的阅读流。Seed-2.1-pro 在关键词里明确带了 PDF说明它把 PDF 解析当成核心能力来做而不是随便调个接口。这一点很关键因为简历解析的质量直接决定了后面 Agent 校验的准确率。解析错了后面全是误报。2.2 Java 生态里做 PDF 解析的几种路线关键词里有 Java这个项目的技术栈大概率是 Java 系。Java 做 PDF 解析主流路线有这么几条我按实际使用体验说一下。方案特点适合场景坑点Apache PDFBox纯 Java、开源、底层控制强需要精细控制文本位置API 偏底层提取段落要自己写逻辑iText功能全、商业版强生成 PDF 为主开源版 AGPL商用要注意授权Tika封装多格式、上手快快速做格式转换对复杂排版还原度一般POI 转换处理 Office 系简历多为 Word 时PDF 支持弱需先转格式如果目标是从简历 PDF 里稳定提取出结构化的字段PDFBox 是绕不开的选择。它的PDFTextStripper能拿到文本但默认是按阅读顺序拼的遇到双栏就乱。真正要做的是继承PDFTextStripper重写writeString方法结合每个字符的坐标TextPosition的 x、y 值做行聚类和列切分。我实测过一个思路先按 y 坐标把字符聚成行同一行内按 x 排序然后统计所有行的 x 分布找出明显的列间隙比如某段 x 区间在所有行里都没有字符用这个间隙把页面切成左右两栏再分别按栏内顺序输出。这套逻辑不复杂但能把双栏简历的还原准确率从六成提到九成以上。2.3 解析之后必须做的字段归一化拿到文字只是第一步。简历里的信息是高度非结构化的同一个意思有无数种写法。比如时间可能是2021.03-2023.06也可能是2021年3月至今还可能是21/3 - 23/6。如果不在解析后做归一化后面的矛盾检测根本没法做。归一化要处理几类东西时间格式统一转成YYYY-MM的区间表示遇到至今要替换成当前月份。公司/学校名称去掉有限公司科技这类后缀做模糊匹配避免字节和字节跳动被当成两家。技能词建立同义词表把JS和JavaScript、K8s和Kubernetes归到同一个标准词。职位名称把高级后端工程师资深后端开发归到后端工程师这个大类。这一步看起来是脏活累活但它是整个工具能不能用的分水岭。我见过太多简历分析项目死在归一化上——解析出来的字段五花八门后面所有逻辑都建立在流沙上。提示归一化规则不要写死在代码里建议做成可配置的映射表。不同行业、不同公司的简历写法差异很大硬编码的规则换个场景就失效。3. Agent 在这里到底做了什么从匹配到交叉验证的思路转变3.1 为什么这个场景适合用 Agent 而不是规则引擎看到简历矛盾检测第一反应可能是写一堆 if-else 规则如果技能清单里没有 Redis 但项目里提到缓存就报警。这种规则引擎能解决一部分问题但很快就会遇到瓶颈。瓶颈在于简历里的矛盾是语义级的不是字符串级的。比如一份简历写负责团队从 0 到 1 搭建微服务架构另一处写参与现有系统的日常维护这两句在字面上没有任何冲突但语义上一个强调从零建设一个强调维护存量放在同一段经历里就有点微妙。规则引擎抓不到这种Agent 可以。Agent 的优势在于它能理解上下文再判断。它可以把简历的不同部分当成不同的信息源让模型去比对这两处描述是否指向同一件事、是否存在时间或职责上的冲突。关键词里同时出现了 Agent、agent 开发、agent 框架说明这个项目的核心卖点就是 Agent 编排而不是简单的规则匹配。3.2 矛盾检测的几类典型模式我把实际会遇到的简历矛盾归成几类这也是 Agent 校验逻辑要覆盖的重点。时间线矛盾是最硬的一类。同一时间段出现在两个不同的全职岗位上或者教育经历和全职工作经历大面积重叠。这类矛盾客观、可验证Agent 只要把归一化后的时间区间做交集运算就能发现。技能与经历矛盾是第二类。技能清单里列了一堆前端技术但所有项目经历都是后端或者项目里明确写了用 Spark 做离线计算技能清单里却没有大数据相关词。这类矛盾需要 Agent 做跨字段的语义关联。职责与职级矛盾是第三类也是最微妙的。一个写着实习生的岗位职责描述却是主导核心模块设计、带领三人小组一个初级工程师的岗位写着制定团队技术路线。这类矛盾没有绝对标准需要 Agent 结合行业常识做判断误报率也最高。数据与描述矛盾是第四类。比如日活百万的系统却只用了单机 MySQL支撑千万级并发却没有任何分布式组件。这类矛盾需要 Agent 具备一定的技术常识。3.3 Agent 的编排结构分而治之再汇总一个靠谱的 Agent 校验流程不应该是一个大模型调用把所有简历丢进去问有没有矛盾。那样既慢又不准还容易漏。合理的做法是分而治之。我的思路是这样的先把解析归一化后的简历拆成几个信息块——基本信息、教育经历、工作经历、项目经历、技能清单。然后针对每一类矛盾设计一个专门的校验 Agent。时间线 Agent 只负责时间区间比对技能 Agent 只负责技能与经历的关联职责 Agent 只负责职级与描述的匹配。每个 Agent 拿到自己需要的那几个信息块做聚焦的判断。最后有一个汇总 Agent把各个子 Agent 的发现合并、去重、按严重程度排序。这样做的好处是每个 Agent 的 prompt 都很聚焦判断质量高而且可以并行跑速度快。关键词里出现了 harness 和 agent 区别、skill 和 agent 区别这其实点到了一个设计要点Agent 不是越全能越好。一个 Agent 如果什么都管它的判断就会变得模糊。把能力拆成 skill让不同的 Agent 各司其职才是工程上更稳的做法。3.4 用 Java 做 Agent 编排的现实考量关键词里有 Java也有 agent 框架。Java 生态做 Agent 编排和 Python 生态的体验不太一样。Python 那边 LangChain、LlamaIndex 之类的框架很成熟Java 这边相对少一些但也有 Spring AI 这类选择。如果这个项目是纯 Java 实现那它大概率是自己封装了一层 Agent 编排逻辑而不是依赖某个重型框架。这其实是好事——简历校验这种场景逻辑相对固定不需要特别复杂的 Agent 自主规划能力自己写一套轻量的编排反而更可控。具体来说Java 这边可以用CompletableFuture做子 Agent 的并行调用用线程池控制并发用简单的状态机管理每个 Agent 的执行阶段。模型调用层可以抽象成一个接口方便切换不同的模型服务。这套东西不复杂但比硬套一个不熟悉的框架要踏实。4. 从零跑通这个工具环境、依赖和第一次实测4.1 环境准备里最容易被忽略的细节假设你要把这个开源项目跑起来第一步是环境。Java 项目通常需要 JDK这里有个坑很多简历解析库对 JDK 版本有要求。PDFBox 3.x 需要 JDK 8 以上但如果你用的某些依赖要求 JDK 11 或 17版本冲突就会在编译期或运行期冒出来。我的建议是直接用 JDK 17这是目前兼容性最好的 LTS 版本。安装完之后记得配环境变量JAVA_HOME指向 JDK 安装目录PATH里加上%JAVA_HOME%\binWindows或$JAVA_HOME/binLinux/Mac。配完在命令行敲java -version能正确输出版本号才算过。关键词里有 java 环境变量配置、java 安装说明这是很多人的第一道门槛。别小看这一步环境没配对后面全是玄学报错。依赖管理方面Java 项目一般用 Maven 或 Gradle。如果项目提供了pom.xml直接mvn clean install拉依赖。这里要注意国内网络环境Maven 中央仓库拉包可能很慢建议配一下国内镜像。关键词里出现了阿里巴巴开源镜像这就是典型的加速方案。在settings.xml里把 mirror 指向国内源拉依赖的速度能快好几倍。4.2 模型服务怎么接Agent 要跑起来得有模型服务。这个项目既然是开源的通常会支持多种模型接入方式。关键词里有 ollama webui 中文便携版下载、开源镜像说明本地部署模型也是一条可行路线。如果你只是想先跑通流程用本地部署的小模型就够了虽然判断质量不如大模型但胜在免费、数据不出本地。简历是敏感信息本地跑模型在隐私上更让人放心。如果追求判断准确率那就接云端的大模型 API代价是简历内容要发出去这个取舍要自己权衡。配置模型服务的时候注意几个参数temperature要调低简历校验是判断任务不需要创造性0 到 0.3 之间比较合适max_tokens要留够因为简历文本可能很长超时时间要设合理大模型响应慢的时候别让程序直接崩掉。4.3 第一次实测拿自己的简历开刀跑通流程之后第一件事是拿自己的简历试。这一步的价值在于你对自己的简历最熟悉能立刻判断出工具报的矛盾是真矛盾还是误报。我实测的时候工具报了一条技能清单包含 Kubernetes但项目经历中未出现容器编排相关描述。这条其实是误报——我的项目里写了容器化部署只是没直接写 K8s 这个词。这就暴露了归一化词表不够全的问题需要把容器化容器编排和 Kubernetes 关联起来。这种误报在初期很常见处理方式不是去改 Agent 的判断逻辑而是去补归一化词表和同义词映射。判断逻辑没问题是输入的信息没对齐。4.4 批量测试与误报率观察单份简历测完要拿一批简历做批量测试。我建议至少准备二十份不同风格、不同行业的简历覆盖单栏、双栏、表格排版等不同格式。跑完之后统计误报率和漏报率。误报率高通常是归一化不够或者 Agent 的 prompt 太激进漏报率高通常是 Agent 覆盖的矛盾类型不够。这两个指标要分开看别混在一起调。我自己的经验是时间线矛盾的准确率最高基本能做到九成以上职责与职级矛盾的误报率最高因为这类判断太依赖行业语境。如果项目要上线用建议把职责类矛盾标成低置信度提示而不是确定矛盾避免误导使用者。5. 踩过的坑和几条实在的经验5.1 PDF 解析的坐标陷阱前面提过双栏排版的问题这里补充一个更隐蔽的坑有些简历模板会用文本框或者表格来排版PDFBox 提取出来的字符坐标会非常规整但阅读顺序完全乱掉。我遇到过一份简历解析出来第一行是技能第二行是姓名因为模板把姓名放在了一个悬浮文本框里坐标上它反而在技能下面。处理这类问题的办法是不要完全依赖坐标排序要结合字体大小和加粗信息做辅助判断。姓名通常是页面上字号最大的那行标题通常是加粗的。把这些视觉信号纳入解析逻辑能显著提升结构还原的准确率。5.2 模型判断的稳定性问题同一个 Agent同样的输入跑两次可能给出不同的结果。这是大模型的固有特性尤其在判断边界模糊的矛盾时。解决办法有两个一是把temperature调到接近 0减少随机性二是对关键判断做多次采样取多数虽然慢一点但更稳。还有一个经验是prompt 里要明确要求模型只输出结构化的判断结果比如 JSON 格式包含矛盾类型涉及字段置信度这几个字段。这样后续处理起来方便也避免模型输出一堆自由文本没法解析。5.3 隐私与合规的边界简历是个人信息处理的时候要格外注意。如果工具要给别人用一定要明确告知简历内容会被如何处理、是否会发送到第三方模型服务。本地部署模型是隐私上最稳妥的方案虽然效果打折扣但至少数据不出本地。另外工具的输出定位要清楚它是辅助发现疑点不是判定造假。简历里的矛盾很多时候只是表述不严谨不能直接等同于诚信问题。这个边界在工具设计和对外说明时都要守住。5.4 关于开源项目贡献的一点体会关键词里有开源文档贡献、开源项目管理这个项目本身是开源的意味着你可以参与进去。我的经验是参与开源项目别一上来就提大功能先从文档、测试用例、bug 修复这些小事做起。比如你发现某个 PDF 模板解析有问题可以提交一份脱敏后的测试样本和对应的解析失败日志这比空泛地提 issue 有价值得多。贡献代码之前先读CONTRIBUTING.md了解项目的代码风格和提交规范。很多开源项目对 commit message 格式有要求不按规范来会被直接打回。这些细节看起来琐碎但决定了你的贡献能不能被顺利合并。6. 这个工具还能往哪些方向长6.1 从发现矛盾到生成追问清单现在的工具做的是发现问题但发现问题之后呢筛选者还是要自己去想这个矛盾该怎么问。一个自然的延伸是让 Agent 在标出矛盾的同时生成一份面试追问清单。比如发现时间线重叠就生成请说明 2022 年 9 月到 2023 年 1 月期间您同时在全日制读研和全职工作的具体安排这样的问题。这个延伸的价值在于它把工具从筛选辅助变成了面试辅助使用场景一下子拓宽了。6.2 简历与岗位描述的交叉校验现在的校验是简历内部的自我一致性。再往前一步可以把岗位描述也解析进来做简历与岗位的交叉校验。比如岗位要求三年以上分布式系统经验简历里所有分布式相关经历加起来只有一年半这就是一个值得提示的点。这类交叉校验比内部校验更复杂因为岗位描述本身也是非结构化的而且经验这种概念很难精确量化。但方向是清晰的也是这类工具真正能产生业务价值的地方。6.3 多语言简历的处理关键词里有 pdf 图片中文设置、pdf 转 word说明文档处理的需求是多样的。如果工具要支持多语言简历解析层要能识别语言并切换分词和归一化策略Agent 层的 prompt 也要相应调整。这块工作量不小但对于有海外招聘需求的团队来说是刚需。6.4 把校验能力做成可复用的 skill关键词里反复出现 skill 和 agent 区别这提示了一个架构方向把每一类校验能力做成独立的、可复用的 skill而不是绑死在某个 Agent 里。这样时间线校验这个 skill既可以用在简历场景也可以用在合同审查、履历核查等其他场景。skill 化之后整个系统的扩展性会好很多。我在实际搭这类系统时的体会是一开始别追求大而全先把一类矛盾检测做扎实把解析、归一化、Agent 判断这条链路跑顺再往上加能力。简历分析这个场景最难的从来不是模型调用而是前面那些脏活累活——PDF 解析、字段归一化、词表维护。这些做扎实了后面的事情都是水到渠成。最后分享一个小技巧如果你要评估这个工具的效果别只看它报了多少条矛盾要看它报的矛盾里有多少是人看了会点头的。准备一份标注好的测试集人工标出哪些是真矛盾然后算准确率和召回率。这个测试集本身就是项目最宝贵的资产比任何模型调参都重要。