ai-job-search 开源解读:Claude Code 求职流水线与 drafter-reviewer 双代理架构

Claude开源AI

2025 年底,地球物理学家 Mads Lorentzen 的职位被裁撤。他的第一个动作是搭系统:把 Claude Code 配置成一条完整的求职流水线,用 /scrape 搜职位、/apply 定制材料、/interview 备面试,每周在自己的求职上实跑。六十九份定制申请、二十个首轮面试、一份签约合同之后,他在 2026 年 6 月以 AI 工程师身份入职,随后把整套框架以 MIT 协议开源,仓库 MadsLorentzen/ai-job-search。五个月内这个仓库攒到 34,239 star、11,922 fork,54 位贡献者参与,Trendshift 日榜持续在列,最近一次提交停在 8 月 23 日。

README 对"这东西真的有用吗"的回答直接给了漏斗数据:69 份申请换 20 个一面,最终签约 1 份。作者对每个雇主都明说了自己在用这套自动化流程,按他的说法,这件事在面试里通常引发技术讨论,没有招致负面评价。这条数据同时划定了项目的定位:一条在真实求职里跑通过的工具链,并非概念演示。

ai-job-search 仓库卡片:34k stars、12k forks、54 位贡献者

整体架构:thin-pointer 设计

仓库的文件布局围绕一个原则组织:同一份规范文件服务所有 agent 运行时。AGENTS.md 把它称为 thin-pointer 设计——CLAUDE.md.claude/skills/job-application-assistant/ 下的档案文件是候选人画像的唯一事实源,.claude/ 目录是工作流规范的唯一事实源,.agents/skills/ 下的门户搜索 CLI 采用可移植的 Agent Skills 格式(每个门户一个 SKILL.md)。Codex、Antigravity、Gemini CLI 等运行时通过 AGENTS.md 指到这些文件,无需为每个工具复制一份配置。这个设计规避的多框架配置漂移问题,在同时使用多个编码 agent 的开发者那里是真实痛点。

目录结构按职责分层:

text
ai-job-search/
├── CLAUDE.md                          # 候选人画像 + 工作流规则
├── .claude/commands/                  # 13 个 slash 命令定义
├── .claude/skills/job-application-assistant/  # 核心技能:画像/评分/模板
├── .agents/skills/                    # 门户搜索 CLI(*-search)
├── cv/ + cover_letters/               # LaTeX 模板与产出
├── documents/                         # 求职档案(CV/LinkedIn 导出/推荐信)
├── company_research/                  # 公司调研缓存(JSON)
├── job_search_tracker.csv             # 申请追踪表
└── tools/                             # CI 守卫、lint、薪资工具

个人数据全部落在 gitignore 边界内:填好的画像、追踪表、薪资数据、申请档案不进版本库,/notion-sync 只同步文件名到 Notion,文档内容不出本机。

命令体系:三命令核心加十个扩展

核心工作流由三个命令构成。/setup 走三条路径建立画像:读取 documents/ 档案夹(CV PDF、LinkedIn 导出、学位证、推荐信、历史申请)、从聊天里粘贴的单份 CV 导入、或结构化访谈;档案夹模式可重复运行且幂等。/scrape 并发搜索多个门户、去重、按契合度排序。/apply <url> 对单个职位跑完整申请流水线。

围绕核心的十个扩展命令各管一段:

命令职责
/rank对抓取结果批量评分,并行 agent 逐个取回职位并按五维打分,输出排序短名单
/interview按申请档案生成面试准备包:阶段化材料、公司调研、STAR 例证映射、模拟面试
/outcome记录申请结果并归档;对无回音申请起草跟进邮件(只起草不发送,每申请至多两次)
/gmail-sync从 Gmail 读取面试邀请/测评/offer 信号,逐条引用来源邮件、批量审批后才写入
/notion-sync只读单向同步流水线视图到 Notion 数据库
/expand扫描画像里链接的公开来源(GitHub、作品集、Kaggle、Scholar)补充技能并标注来源
/upskill对比画像与目标职位,产出技能缺口热力图和学习计划
/html-report从追踪表生成自包含离线 HTML 仪表盘(内联 SVG 图表)
/add-template注册自定义 CV/求职信模板(LaTeX、Typst 等),强制测试编译后激活
/add-portal为本地市场生成新的门户搜索技能

/rank 的并行设计有细节:每个职位由独立 agent 取回正文并评分,五个评分维度各自给出优势与缺口,deal-breaker 直接否决,过期职位标记失效,截止日期打紧急标签。

/apply 的 drafter-reviewer 流水线

/apply 是整个框架最重的一条命令,编排两个 agent 分工。流程定义在 .claude/commands/apply.md,共八步,顺序强制:

  1. 解析输入。URL 或粘贴文本。抓取失败按升级链处理:换浏览器 UA 重试,再搜雇主官方招聘页;聚合站常丢掉 requisition ID 和职级,官方页优先。
  2. 契合度评估。drafter 按评估框架打分,输出技能匹配、经验匹配、文化匹配、薪资基准(salary_lookup.py 自带数据可查)、综合推荐。
  3. 起草。CV 与求职信各写一份 LaTeX 草稿。
  4. 评审。用 Agent 工具派生一个全新上下文的 reviewer agent,草稿以 inline 方式塞进 prompt(避免 reviewer 重复读文件),它负责公司调研与内容批评。
  5. 修订。drafter 按 reviewer 反馈改稿。
  6. 编译并检查 PDF。lualatex 编 CV、xelatex 编求职信,读渲染结果迭代到 CV 恰好两页、求职信恰好一页。
  7. ATS 检查pdftotext 提取 PDF 文本层,模拟解析器视角验证联系方式、阅读顺序、关键词覆盖。
  8. 输出。附验证清单。

事实核查在三源之间进行:01-candidate-profile.md、主 CV(cv/main_example.tex)、CLAUDE.md 的画像段。任何一个日期、雇主、头衔、数字指标在草稿里出现,必须至少有一个源支持;三源之间互相矛盾时上报为画像一致性问题,草稿与源不符则标记为 grounding 修订。项目的一条常设规则进一步要求:用户在对话里确认过的新事实,必须当回合写回 01-candidate-profile.md——只存在于聊天里的事实,在下个 session 会被审计当作无依据内容静默剔除,一条真实成就会从之后的每份 CV 里消失。

评估框架:两道门加五个维度

评分之前先过两道硬门。Eligibility Gate 检查工作许可:逐字读招聘方的资格条款,点名公民或永居要求的直接判 FAIL,不评分不起草;条款沉默时标记未验证并要求查雇主国际申请者页面,因为大型校招项目常在自家网站而非招聘广告里设限。Language Gate 检查语言:招聘要求的语言不在候选人语言表里判 FAIL;要求了但标称等级可能高于候选人自报水平时只 FLAG 不否决,把判断权留给人。

过门之后按五个维度打分:技术技能匹配(0-100)、经验匹配(0-100,按工作实质而非头衔字面对齐)、行为与文化匹配(0-100)、地点与通勤(Pass/Fail)、职业方向与动机(0-100)。动机维度有一个双向过滤器:不只问能不能做这些任务,还问这些任务是否给候选人供能,画像里分别维护 energizing tasks 与 draining tasks 清单。deal-breaker 是自由文本——"育儿假条款""工会最低薪资""不值班"各占一行画像,在 /rank/apply 的评估里都带实际权重。

PDF 验证环与 ATS 文本层

多数 LaTeX 简历模板的问题在 .tex 源码里看不出来:编译后条目标题孤行到下一页、求职信溢出到第二页、列表字体静默回落。/apply 的第六步把"编译并目检"设为不可跳过,读到渲染问题就用 \needspace\enlargethispage、字体匹配包装做定点修复,直到版面干净。

CV 超过两页时,裁剪逻辑先给每一行候选内容按三个因子打分——与目标职位的相关性、在文档中的独特性、求职信是否依赖它——总分最低的先删;"最旧的先删"这种机械规则在这里不成立,一条命中职位关键词的旧岗位 bullet,可以排在一条不相关的近期岗位 bullet 前面存活。

ATS 检查针对的是另一个盲区:机器筛简历读的是 PDF 内嵌文本层,LaTeX 完全可能产出渲染正常但提取出来是乱码的 PDF——邮箱位置变成图标字形、双栏布局行序交错。pdftotext 提取后按解析器视角验证,画像真实支持的关键词补进去,真实缺口保持可见,不做关键词堆砌。

安全边界:不可信输入与权限围栏

SECURITY.md 对威胁模型的陈述很直白:一个有文件访问权的 LLM,同时读不可信网页内容(招聘帖)和个人数据(CV、画像、申请历史),这个组合是主要风险面,无法根除只能收窄。框架做了三层:

  • 不可信输入规则/apply/rank 把招聘帖当数据而非指令:不执行帖内嵌入的指令、不抓取帖子正文里出现的 URL(用户提供的帖子 URL 本身是唯一例外),这条规则随帖子文本进入后续每一步和每个 agent prompt。reviewer 的公司调研只从用户确认的公司身份出发搜索,链接起点在官方站。
  • 权限 allowlist.claude/settings.json 只预批工作流需要的具体命令,CI 的 security-guards 任务对任何放宽 allowlist、添加包管理器生命周期脚本、削弱个人数据 gitignore 规则的 PR 直接判失败。allowlist 只管 Bash 命令,模型原生的 WebFetch/WebSearch 在其管辖之外,这正是指令级规则存在的原因。
  • 数据边界。画像、追踪表、薪资数据、申请档案全部 gitignore;文档不出本机。

文档同时声明:指令级防御抬高门槛,不等于沙箱。对完全不信任的招聘板,发送前自己过一遍 agent 抓了什么写了什么。

对第三方门户技能的引入也做了同样的设计约束:从社区 fork 借一个 portal skill 必须手动复制,没有安装器代劳——因为已安装的 portal CLI 在 allowlist 里免确认运行,自动安装会跳过"用户先读代码"这唯一有效的检查。复制前的检查单:网络请求只发向它声称搜索的招聘站、package.json 无依赖无生命周期脚本、不读写自身目录之外、离线跑通测试。

本地化路径:从丹麦门户到任意市场

随库发行的门户技能里有四个丹麦站(Jobindex、Jobnet、Akademikernes Jobbank、Jobdanmark),它们是模式的演示。两个通用起点不绑定市场:linkedin-search 基于 LinkedIn 公开免认证的 jobs-guest 端点,零运行时依赖,搜索地点作为显式参数传入(-l "Berlin, Germany"-l "Remote" 皆可),文档明确标注仅供个人低频使用,自动化访问违反 LinkedIn 服务条款;freehire-search 查询 freehire.me 聚合器的公开 REST API,返回结构化字段(技能、资历、类别),后端 MIT 许可可自托管。

新市场用 /add-portal 生成:命令先调查目标门户(搜索 URL 模式、结果页结构、robots.txt 与访问规则),按随库技能的同一契约脚手架一个 CLI,实测一次真实查询后才注册;需要登录墙的门户直接拒绝,条款严格的门户在生成的技能里加显著的仅限个人使用警告。社区 fork 索引(discussions #78)收录各市场的适配版本。

上手前要知道的事

依赖清单:Claude Code CLI、Python 3.10+、Bun(门户搜索工具运行时)、LaTeX 发行版(CV 用 lualatex 编译,MiKTeX 上 pdflatex 常因 fontawesome5 字体扩展报错;求职信用 xelatex,cover.cls 依赖 fontspec)。可选 pdftotext(poppler)供 ATS 检查,缺失时该步降级为人工关键词检查。

README 用 IMPORTANT 块强调了一个容易踩的坑:公开仓库的 fork 必然公开,而 /setup 会把姓名、联系方式、工作历史、薪资预期写进受版本控制的文件。为自己求职用的正确姿势是建私有仓库、把原仓库设为 upstream,SETUP.md 第 8 节有两分钟配置配方;fork 只用于给上游贡献。

观察与边界

这个项目把"求职"拆成了一个数据工程问题:画像是有 schema 的档案、评估是有 gate 的评分函数、申请是有 drafter-reviewer 的生成流水线、结果是可分析的追踪表。它的防造假设计(三源审计加写回规则)比多数"AI 帮你写简历"的工具诚实——后者的问题恰恰是无中生有,而这个框架把"简历里每个数字都能溯源"做成了流程的硬约束。

边界同样清楚。门户技能的市场覆盖以丹麦为演示、靠社区 fork 扩展;LinkedIn 技能受服务条款约束只能低频个人使用;LaTeX 工具链的安装门槛对非开发者不友好;整个流程的产出是草稿与准备材料,"发送"这一步始终留给人。作者漏斗里的 69 比 20 比 1 也提示了另一面:自动化提高的是申请的定制质量与迭代速度,决定结果的仍是评估、面试与市场本身。

仓库地址:github.com/MadsLorentzen/ai-job-search,MIT 协议,SETUP.md 覆盖完整安装与私有仓库配置。