career-ops ATS 体检指南:用 verify-ats.mjs 给生成简历打出可解析性评分与修复清单

career-ops ATS 体检指南:用 verify-ats.mjs 给生成简历打出可解析性评分与修复清单 career-ops ATS 体检指南用 verify-ats.mjs 给生成简历打出可解析性评分与修复清单【免费下载链接】career-opsOpen-source AI job search: scan job portals, evaluate listings into a structured A-H report with a global 1-5 score, tailor your CV, track applications — runs locally in your AI coding CLI (Claude Code, Codex, OpenCode, Antigravity…)项目地址: https://gitcode.com/GitHub_Trending/ca/career-ops简历能不能被 ATSApplicant Tracking System正确解析与简历声称了什么内容是两件不同的事。career-ops 的ats模式文档位于 modes/ats.md专门回答后者通过verify-ats.mjs这一确定性、只读的 HTML 体检器为pdf模式生成、尚未转成 PDF 的简历 HTML 打出一个 0–100 的结构评分与 A–F 等级并列出一份可直接动手修复的具体问题清单。读完本文你将掌握该检查器的全部命令行用法、8 项 100 分制打分细则、退出码约定以及它在 pdf 事实门禁fact gate之外如何作为纯建议性环节嵌入简历流水线。定位PDF 模式回答生成ATS 模式回答能否被读取career-ops 的简历产出由 modes/pdf.md 定义的多步流水线驱动解析 JD、抽取关键词、做 skill-gap 零 LLM 检查、按 JD 改写摘要与经历、构建 JSON payload再经build-cv-html.mjs渲染为 HTML之后过verify-cv-facts.mjs事实门禁最后generate-pdf.mjs渲染 PDF。这份流水线在内容层面已为 ATS 做了大量预防工作——modes/pdf.md 的 ATS Rules (clean parsing) 一节明确要求单列布局、标准分节标题、图片中不嵌入文字、PDF 页眉页脚不放关键信息、UTF-8 可选中文本、无嵌套表格、关键词分散布局、无隐藏文字与白字填充。ats模式则是把这条规则清单变成了可执行的自动化评分器verify-ats.mjs不调用任何模型、不访问网络、不写任何文件只读入一个简历 HTML输出分数与问题。它与verify-cv-facts.mjs形成互补verify-cv-facts.mjs守卫简历声称了什么事实门禁硬性阻断verify-ats.mjs守卫 ATS 是否读得懂这份简历结构门禁纯建议。一个关键事实是verify-ats.mjs不接入 pdf 主流水线它由用户在生成 PDF 前后按需调用modes/pdf.md 的 Step 18 附近将其描述为 Optional parseability check。因此它的失败永远不会阻止简历生成——这是它与事实门禁在职责上最本质的差别。输入与基本用法检查器读取的是pdf模式渲染 HTML 的产物也就是PDF 渲染之前的那个 HTML 文件而不是 PDF 本身通常位于output/cv-{candidate}-{company}.html。入口脚本是仓库根目录下的 verify-ats.mjs仓库同时提供了 npm 别名npm run cv:verify-ats见 package.json 的scripts段。# 基本用法只打分与列问题 node verify-ats.mjs output/cv-jane-smith-acme.html # 附带目标关键词查看命中覆盖 node verify-ats.mjs output/cv-jane-smith-acme.html --keywords python,kubernetes,rag # 附带职位名称作为一个完整短语参与匹配 node verify-ats.mjs output/cv-jane-smith-acme.html --role Senior Backend Engineer # 修改通过线 输出机器可读 JSON node verify-ats.mjs output/cv-jane-smith-acme.html --min-score 80 --json命令行参数由 verify-ats.mjs 的 CLI 段解析缺值或值非法都会以非零码报错退出参数含义说明--keywords a,b,c目标关键词逗号分隔报告这些词在简历文本中的命中覆盖情况--role ...职位名称加入关键词集合作为单个短语参与精确匹配--min-score N通过阈值默认70合法范围 0–100--json机器可读输出通过或失败都会把完整结果打到 stdout--self-test内置回归套件逐条断言 8 项检查行为--help/-h用法说明显式请求帮助时以 0 退出退出码约定结构分 ≥--min-score且不存在任何critical级问题 → 退出码0否则退出码1——与verify-cv-facts.mjs等其他 verifier 保持同样的 0/1 契约。打分模型8 项结构性检查合计 100 分每个检查项都带固定权重任一扣分都会附带一条critical/warning/info级别的问题说明告诉你哪里有问题、为什么、怎么改。下表完整列出原文档中的权重结构权重检查项为什么重要15存在真实、可选中的文本≥ 300 字符纯图片 / 栅格化简历没有文本层ATS 无内容可读20标准分节标题Experience、Education、Skills 必选Summary/Projects/Certifications 为加分项ATS 解析器依赖可识别的标题来切分简历15联系邮箱在正文中可达电话只查存在性不查位置ATS 经常丢弃语义化header/footer区域邮箱必须位于主体正文20单列布局无表格布局 / 多列 CSS表格和多栏会打乱提取器遵循的阅读顺序10简历文字未烘进图片ATS 读不到图片内的文字10使用标准、可嵌入字体冷门字体会被提取为乱码或缺字形5声明 UTF-8保证重音字符与符号在提取中不损坏5无隐藏文字 / 关键词堆砌白底白字或display:none关键词会被惩罚上述权重在源码中以常量WEIGHTS显式声明见 verify-ats.mjs 顶部注释写明保留显式是为了让分数可审计、自测可逐项锁定分数计算全程是普通字符匹配与正则统计无任何模型调用因此同一份 HTML 永远得到同一分数deterministic。等级换算在 verify-ats.mjs 的gradeFor中实现A≥ 90B≥ 80C≥ 70D≥ 60其余为F。两条重要豁免源码刻意为之单个display:table元素不会被标记——随附模板的 certifications 块使用的就是这种定义列表式写法它不会重排内容只有真正的table标签与多列 CSS 才算问题。.cv-photo类的照片img不计入烘进文字的内容图该豁免逻辑见图片检查的实现。逐项拆解每项检查背后的实现细节1. 真实可选中文本15 分——先做提取模拟ATS 最终拿到的不过是文本层。因此检查器先做一次提取预演stripNonContentRegions先剔除script、style与 HTML 注释这些区域 ATS 从不当内容读取再由extractVisibleText剥掉标签、解码实体、折叠空白得到提取器大致能读到的东西。值得注意的工程细节实体解码顺序是最后才解amp;否则amp;lt;会被二次解码成把本非实体的文本误转义注释在 verify-ats.mjs 的extractVisibleText内。随后比较文本长度低于常量TEXT_MIN_CHARS 300就给出critical提示可能是图片版 / 栅格化简历。2. 标准分节标题20 分extractHeadings同时识别两类标题来源模板使用的.section-titledivtemplates/cv-template.html 中以{{SECTION_SUMMARY}}、{{SECTION_EXPERIENCE}}等占位符渲染以及通用的h1–h6全部小写化后再做不区分大小写的匹配。必选三项 Experience / Education / Skills 各占 5 分正则分别覆盖experience|work history|employment、education|academic、skills|competenc|proficiencSummary / Projects / Certifications 为加分项每个 2封顶 5 分缺 1 项 →warning缺 2 项及以上 → 直接升级为critical因为ATS 解析器靠可识别标题切分简历这一前提已不成立。3. 联系信息可达性15 分邮箱 10 电话 5这项检查模拟了 ATS 提取器的丢弃语义区行为先剥离真正的header/footer标签再提取正文邮箱必须落在正文中才给分。这里有个精妙的区分随附模板的联系区放在普通文档流里的div classheader不是语义化header因此不会被剥离、不会误报源码注释明确说明若不区分会导致每一份出厂 CV 都被误判。实现层面还做了两件防绕过的事mailto:/tel:链接兜底linkContacts解析真实a href双引号与单引号两种写法都覆盖处理可见文字不是地址本身如a hrefmailto:…Email me/a的情况但藏在注释或script里的mailto:不会蒙混过关有对应自测用例。电话识别带边界正则PHONE_CANDIDATE_RE限定长度{6,23}天然防 ReDoS≥ 9 位数字才算真电话的规则放到hasPhoneNumber里用数位统计实现把2019 - 2024这种 8 位数字的日期区间排除在电话误判之外候选数上限PHONE_MAX_CANDIDATES 50防病态输入拖垮性能。判定与扣分对应正文无邮箱且全文档也没有 →criticalATS 与招聘官需要可解析的联系邮箱邮箱只存在于语义header/footer→critical注释指出若只给warning会被isPass忽略从而放行——自测专门断言了这种简历不通过默认门禁电话缺失只是info可选但很多 ATS 表单要求。4. 单列布局20 分从 20 分起扣出现table标签 → 每处扣 12 分并给critical表格布局会打乱 ATS 提取器的阅读顺序CSS 多列 → 扣 8 分warning。hasMultiColumn对column-count: NN≥2与columns简写都检测且正确区分栏数与栏宽——带单位的11px是栏宽不会被误判position: absolute→ 扣 4 分warning绝对定位可能破坏阅读顺序。样式来源同时扫描style块与内联style…使把规则藏进内联样式无法绕过检测。5. 图片中不烘文字10 分先筛出非.cv-photo的内容图若内容图数量 0且正文文字很少低于TEXT_LOW_WITH_IMG 800→ 判为critical并清零该项文字大概率被烘进图片若有内容图但文字充足 → 扣 5 分给warning。6. 标准可嵌入字体10 分从样式块与内联样式中提取所有font-family与ATS_SAFE_FONTS白名单比对白名单外的字体每个扣 3 分下限 0并给warning。白名单verify-ats.mjs 源码可见覆盖 Arial、Helvetica、Calibri、Times New Roman、Georgia、Lato、Roboto、Open Sans 等通用字体并包含随附模板使用的 CJK / 阿拉伯回退字体Hiragino、Yu Gothic、Noto Sans CJK、PingFang、Microsoft YaHei、Source Han Sans 等——因此真实的多语言简历不会被误罚。纯泛型 CSS 族sans-serif、serif、system-ui 等永远合法直接跳过。7. UTF-8 声明5 分正则检测meta charsetutf-8类声明缺失给warning提示声明 UTF-8 以保证重音字符在提取中存活。8. 无隐藏文字 / 关键词堆砌5 分display:none、visibility:hidden、font-size:0三类信号同时在样式块与内联样式中扫描而白字white-on-white只查内联样式这是刻意设计样式表里的白色极大概率是合法的白字徽章、分节头、渐变标题内联stylecolor:#fff才是经典的白字堆词手法。四种信号同时支持单双引号写法。命中任意信号给warning列出命中的具体手段。关键词覆盖可选且绝不干扰结构分未传--keywords/--role时关键词覆盖为null结构分完全不受影响——因此只跑不带角色的基础检查永远不会因为关键词而误失败。normalizeKeywords的切分规则值得一提--keywords按逗号切分--role只按逗号、斜杠和单词 and切分于是 Senior Backend Engineer 保持为单个短语对简历文本做逐字整句匹配、不做词元化避免把 Engineer 拆出去导致一堆无关命中。匹配对小写化后的可见文本做子串包含判断结果输出total / found / percent与缺失清单。这正是 modes/pdf.md 关键词注入策略的可验证闭环pdf 模式把 JD 关键词真实、不编造地注入摘要与经历措辞ats模式则给出这些关键词到底有没有落进最终文本的客观读数。通过判定、JSON 输出与自检回归isPass(result, minScore)的判定是score minScore且不存在critical问题。--json输出形如{ file, pass, minScore, score, grade, issues, keywordCoverage }源码见 verify-ats.mjs 的 CLI 段通过/失败都会完整打印。CLI 还刻意用process.exitCode而非process.exit()收尾保证 stdout 缓冲先排空再退出。内置回归套件--self-test覆盖了多项容易被忽略的对抗场景verify-ats.mjs 的runSelfTest干净单列简历 → A 级、无 critical、默认门禁通过缺 EducationSkills 两节 → critical且两个名字都出现在问题文本中表格布局 → critical内容图 极少量文字 → critical非标准字体Comic Sans MS→ 被点名的 warning全文档无邮箱 → critical白字堆词单/双引号内联样式→ warning内联font-family与样式表同等计分年份区间 2019 - 2024 不算电话、真电话可被识别邮箱只存在于语义header→ critical 且不过默认门禁藏在注释 /script里的mailto:不算可达邮箱但可见文字非地址的mailto:链接算两位数column-count: 10与内联columns: 2简写都被标记传入关键词会算出覆盖百分比、列出缺失词且不改变结构分。仓库级集成回归在 test-all.mjs 中进一步验证端到端行为干净 CV 走--json应退出 0 且 score ≥ 80恶意 CV表格布局 内容图 缺失分节应退出 1、pass: false且问题文本里必须同时包含table与缺失分节Education/Skills。使用守则Rules与职责边界原文档明确了四条必须遵守的规则只读。检查器只读一个 HTML 文件、不写任何内容从不触碰用户层文件遵循 DATA_CONTRACT.md 的数据契约。建议性、非门禁。与作为 pdf 硬门禁链一环的verify-cv-facts.mjs不同它不在生成链上把分数与问题呈现给用户但不要因它阻断简历生成。确定性。同样的 HTML 进、同样的分数出——无模型调用、无网络。在呈现边界本地化。verify-ats.mjs刻意输出固定英文零 LLM 设计的代价把分数与问题转述给用户时须按 AGENTS.md 的 Output Language vs Market Modes 一节用[Render in {language.output}: …]机制将人类可见摘要译为language.output指定的语言该语言由 config/profile.yml 之类的用户配置决定而检查器原始 stdout 保持英文不动。此外要牢记问题的性质边界检查器给出的 issue 描述的是结构风险而非内容事实要做事实 / 编造把关请走verify-cv-facts.mjs该 verifier 与脚本本身、用户事实文件config/cv-facts.json的关系见其脚本头注释与 config/cv-facts.example.json。建议工作流五步走结合 modes/ats.md 原文与 pdf 流水线推荐的落地顺序是通过pdf模式生成简历 HTML此时已过事实门禁运行node verify-ats.mjs output/cv-{candidate}-{company}.html逐条修复critical/warning问题通常出在模板或渲染 payload而非正文内容然后重跑可选渲染 PDF 前把 JD 关键词用--keywords传入确认覆盖把结果转述给用户格式为[Render in {language.output}: 分数与等级然后逐条解释每个 issue 的含义与修法如有关键词覆盖行一并给出]。实际使用时产出 HTML →verify-ats打分 → 修模板/渲染层 → 过verify-cv-facts→ 渲染 PDF这个顺序能让绝大部分结构问题在 PDF 之前暴露因为此时文件仍是可读的 HTML 文本层定位display:none、非标准字体或表格布局的成本远低于拿到 PDF 之后返工。总结从预防规则到可度量体检ats模式把 modes/pdf.md 中散落的 ATS 规则沉淀成一个确定性的 100 分制体检器8 项检查对应 ATS 提取器最常见的 8 类失败模式0/1 退出码让它可以被脚本与 CI 复用如 test-all.mjs 所做--json让它能被上层 Agent 消费纯英文输出 呈现层翻译的边界让它保持零 LLM 的廉价与可预测。它回答的问题始终只有一个——我的简历机器真的读得懂吗——而这正是从看起来不错到能被正确解析、归档、检索之间最常被忽视的一公里。继续深入阅读模式定义见 modes/ats.md完整实现与自测见 verify-ats.mjs生成侧规则见 modes/pdf.md默认渲染模板见 templates/cv-template.html集成回归见 test-all.mjs。【免费下载链接】career-opsOpen-source AI job search: scan job portals, evaluate listings into a structured A-H report with a global 1-5 score, tailor your CV, track applications — runs locally in your AI coding CLI (Claude Code, Codex, OpenCode, Antigravity…)项目地址: https://gitcode.com/GitHub_Trending/ca/career-ops创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考