
“Companies use AI to filter candidates. I just gave candidates AI to choose companies.” —— santifer,career-ops ↗ README 第一行
一、先讲个故事#
如果你曾经投过 200 份简历、收到 199 份沉默,你大概会对 santifer 写 README 的第一句话感同身受——
“I spent months applying to jobs the hard way. So I engineered the system I wish I had.”
他最后用这套系统自己拿到了一个 Head of Applied AI 的 offer。但和”上岸”这个浪漫结局比起来,更有意思的是:他把这套系统全部开源了。
于是我花了点时间把 santifer/career-ops ↗ 翻了个底朝天——43MB,124K 行 JavaScript,123 个 .mjs 模块,WIRED 和 Business Insider 都报道过,被 17 种语言翻译过。
读完之后我决定:如果你身边有正在求职的朋友,我要把这个项目甩给他。
不是因为它”AI 功能强”。而是因为它做了一件别的 AI 工具都没在做的事——它把”求职”当成 production system 来运营。
二、它不叫”career-assistant”,叫”career-ops”#
我第一次看这个名字,觉得只是又一个营销词。但读完 ARCHITECTURE.md 之后才意识到这两个字母的份量:
ops = operations。
santifer/career-ops:44 的 manifesto 里有一句话:
“We run our job searches the way engineers run production: with evidence, with discipline, with tools on our side of the table.”
翻译过来就是:把 SRE 那一套搬进求职领域。
证据?整套项目里这些工具名就是 SRE 词汇:
pipeline-lock.mjs(文件级 mutex)reconcile-pipeline.mjs(数据对账)check-table-freshness.mjs(表新鲜度校验)update-system.mjs apply / check / rollback(带 rollback 的版本升级)status-log.tsv(append-only ledger,immutable 不可改)- 还有那句贯穿全文的话——“files are canonical, indexes are derived”(数据库/DWH 术语)
求职不是一次性行为,而是一个长期可观察、可回滚、可对账的 pipeline。
三、Reverse ATS——把 ATS 武器还回去#
ATS(Applicant Tracking System)是招聘方用的关键词过滤算法,把 200 份简历在毫秒级筛成 3 份,剩下 197 份候选人收到沉默。
santifer 的反击:
“Apply better to fewer. Ten applications you believe in beat two hundred you don’t.”
career-ops 用对手的武器给候选人搭了一套反向 ATS:与其把 200 份简历砸给 ATS 算法,不如先让 AI 帮你筛掉那 180 个不该投的公司。
具体做法藏在 modes/oferta.md(西班牙语里”oferta” = “offer”,招工帖)。每一条 JD 经过 7 个 block 的硬约束评估:
- Block A Role Summary(含 Geo-mismatch + Work-authorization 检查——sponsorship 缺失不是扣分,是 ⛔ hard blocker)
- Block B CV Match(按 archetype 自适应匹配——AI 平台/LLMOps/Agentic/Tech PM/Solutions Architect/FDE/Transformation Lead 6 类)
- Block C Level Strategy(“sell senior without lying” + “if they downlevel me” 双预案)
- Block D Compensation(按公司类型给工资可信度评级——public big tech / growth-stage / agency / SMB / recruiter / govt / OSS 各自不同可靠性)
- Block E Personalization Plan(CV 哪些 section 要改、改成什么、为什么)
- Block F Interview Plan(6-10 个 STAR+R story,每个拆成 Situation/Task/Action/Result/Reflection 5 列)
- Block G Posting Legitimacy(独立检测 ghost job / 钓鱼 JD)
最狠的是 Block G——和 A-F 解耦。一条 JD 可能在 A-F 都满分,但是 ghost job,整个评估就废掉。
而且 oferta.md:13 写明:评估前必须先过 liveness gate——链接已经 404 了,整套 A-G 评估直接 short-circuit。
求职不是玄学,是工程。
四、最让我想偷的一个设计:agent 之间没有 master#

我看过几个号称”多 agent”的系统,里面都有一个 orchestrator 在中心调度子 agent。career-ops 不是。
它的 agent 之间根本没有中心调度。
数据流是这样的(ARCHITECTURE.md:79):
scan ──► data/pipeline.md ──► evaluate (oferta + cv) ──► reports/NNN-*.md
│ │
└──► data/applications.md (tracker)
│
apply (human reviews + clicks)plaintext几个关键设计:
data/pipeline.md= 输入队列(待评估 URL)reports/{NNN}-{company}-{date}.md= 各 agent 的产出data/applications.md= canonical trackerdata/scan-history.tsv= append-only,靠 SimHash fingerprint 去重data/agent-inbox.md= session 启动时的 request queue(你在 dashboard 上想到”评估这个 URL”,append 一行,下次 session 启动自动 drain)
外加几个非常 SRE 的工具:
pipeline-lock.mjs——文件级 mutex,防止并发覆盖reserve-report-num.mjs——原子分配 report numbermerge-tracker.mjs+dedup-tracker.mjs——CRDT-like 合并,多个 worker 并发写 tracker 时各产batch/tracker-additions/{ID}.tsv,再原子合并
结果是:任何 CLI agent(Claude Code、Codex、OpenCode、Qwen、Kimi、Grok、Cursor……)进来读这些 markdown,就能”接入”整个集群。Agent 之间通信靠的是 filesystem RPC——本质上是个”LLM-friendly FUSE”。
五、“cv.md 是 single source of truth”——一份事实,N 份文档#

career-ops 的 CV 子系统有一个非常优雅的设计:
cv.md写一次,所有格式都从它派生。
具体链路:
cv.md (markdown 事实清单)
│
├─► build-cv-html.mjs ──► templates/modern.html / executive.html / zh-minimal.html
├─► build-cv-latex.mjs ──► Overleaf PDF
├─► generate-pdf.mjs ──► Playwright HTML→PDF
├─► cv-sync-check.mjs ──► 校验 HTML/LaTeX/PDF 不漂移
└─► verify-cv-facts.mjs──► 反向校验事实是否一致plaintext字体都自托管(Space Grotesk + DM Sans),避免 ATS 抓不到字体显示成 fallback。这是细节里的工程。
而且每次申请一条新 JD,batch-tailor.mjs 会针对那条 JD 单独走”读取 → 评估 → CV 重写” pipeline,产出一份量身定制的 CV——不是改一句话那种,是 section 级别的重组。
README 里那个”100+ personalized CVs”就是这么来的。
六、批处理 worker prompt——写在 markdown 里的人肉容错#

career-ops 的批量求职靠 batch/batch-prompt.md(约 350 行)驱动。这不是代码,是写在 markdown 里的 worker 协议:
1. 拿 {{URL}} + {{JD_FILE}}
2. 跑 A-G 评估
3. 写 reports/NNN-*.md
4. 生成 PDF
5. 写 batch/tracker-additions/{ID}.tsv
6. 输出 final JSONplaintext关键的容错在 batch/batch-prompt.md:144:
“If both fail, this is a hard stop — do ALL of the following… Do NOT invent, estimate, or guess a score”
这段注释背后有个真实事故——以前有过 worker 没读到 JD,但 commit 进 tracker 一条带假分数的记录。所以这条规则被写成硬规则:没有数据就 hard stop,绝不发明分数。
这种”曾经的故障长在 prompt 里”的设计,比任何代码层面的 validation 都更 robust。因为 validation 会被绕过去,但 prompt 注释是被 LLM 直接读进上下文的。
七、MANIFESTO 里那句”never submits”——立场即架构#
santifer/career-ops:14-19plaintext“career-ops never sends, submits, or clicks anything for you. Every send button is yours to press.”
这不是技术限制,是立场。把”永远不替你按提交”写成 manifesto 里的不可妥协条款。
对应的工程实现是分层:
browser-extract.mjs注释里明说:“STRICTLY READ-ONLY: it navigates and reads the DOM. No clicks, typing, or form fills — that boundary is exactly what keeps this separate fromapply”apply流程的每一步都要求 human review- 17 种语言的 README 都强调这条规则
不是”我们不能做”,是”我们故意不做”。这和 ATS 算法把候选人无声过滤掉形成对称:
ATS 把候选人无声拒掉 → career-ops 把”无人类按下提交”做成 first-class invariant
立场即架构。
八、和前两篇多 agent 框架对比:调度 vs 黑板#
顺便把这四篇博客里见到的多 agent 范式横向比较一下:
| 维度 | Strix | ACS | career-ops |
|---|---|---|---|
| 业务目标 | 安全测试(红队) | 领域知识检索 | 个人求职 |
| Agent 通信 | Hierarchical orchestrator | YAML frontmatter | 共享 FS 黑板 + markdown RPC |
| 状态持久化 | DB/内存 | YAML + JSON | TSV + markdown(git-diffable) |
| 编排协议 | 中心调度(树状) | 静态索引 | 去中心化(任何 CLI 都能挂) |
| HITL | 强人工审批 | 无 | 强 human-in-the-loop + 不替你点提交 |
| 隐私模型 | 沙箱 | 公开 | 本地优先 + 数据所有权 |
| 本质 | 调度智能体 | 调度 metadata | 调度 markdown |
career-ops 是唯一一个拒绝中心调度的——它的”多 agent”不是因为有一个聪明的 orchestrator,而是因为每个 CLI agent 都能 read 自己关心的那部分 markdown。
九、几个让我佩服的小细节#
9.1 评估日志 = 结构化反对”vendor assessment 越来越水”#
assessment-log.mjs + data/assessments.tsv:候选人收到 eSkill / HackerRank / Criteria Corp 的 skills assessment 时 append 一行:
{date, company, report#, platform, subject, threshold%, score%, stale_note}plaintext关键是 stale_note——候选人自己标注”考题内容过时吗”。比如 “test content references Adobe Acrobat 9, a 2008-era version”。
这种”把对 vendor 的抱怨结构化”的工程能力,比任何情绪宣泄都更有力量。
9.2 oferta.md:50:“5 次 WebSearch 封顶”#
deep research 模式有 bound:单次评估最多 5 次 WebSearch。把 research 失控做成硬约束,而不是”尽量别太多”。
9.3 reply-matcher.mjs 把”雇主的回复”做成信号#
候选人和雇主对话时,把回复 paste 进来自动分类(offer / rejection / interview request / ghosted),然后 analyze-patterns.mjs 长期看哪些渠道 / 哪些措辞让你转化率高。
把”求职的运气”变成”求职的数据”。
9.4 local-parser-cookbook.md#
README 文档之外,专门有一份教”怎么本地解析 JD”。这是把”模型很强 = 不需要解析”这种幻觉剥掉——模型再强,原始 HTML 也得清洗。
十、可借鉴 & 局限#
10.1 值得抄到任何个人生产力 AI 系统#
- manifesto-first 设计:把”我做什么 / 我不做什么”写成不可妥协条款,不是写到 README 末尾,是写到代码注释里;
- markdown 当 schema:
modes/*.md是给 LLM 的 schema,比 JSON Schema 更易改、更易读; - filesystem 当通信总线:agent 之间靠 git-diffable 文件通信,比 message queue 简单 10 倍,比 in-memory state 可靠 10 倍;
- append-only ledger:求职每次状态变迁都被记录,能回放、能分析;
- “single source of truth + 多格式派生”:cv.md → HTML / LaTeX / PDF / DOCX 派生,跟 IaC(Terraform → 多云 provider)同构;
- liveness gate:评估前先验证输入活着。404 就别浪费一次 A-G;
- hard-stop on missing data:
"Do NOT invent, estimate, or guess a score"——LLM 时代的最重要 invariant。
10.2 局限#
- 作者个人流程的产品化:虽然 60K stars,但 README 写得坦白——这是”santifer 用了几个月最后拿到 offer 的系统”,不是”经过 N 个用户验证的通用系统”;
- 124K 行 JS + 17 语言 README + 60+ 模式 + 90+ provider 适配器:维护成本极高。
update-system.mjs的存在本身就是证据——这是一个会变的活物; - provider registry 覆盖:90+ ATS 适配器看起来很全,但 LinkedIn / Indeed 这种”算法黑盒 + 反爬激进的”渠道不一定稳;
cv.md是 single source of truth 的代价:所有 CV 变更必须走这一个文件,团队场景下不容易 fork;- 隐私优先与生态化的张力:本地优先意味着没有 SaaS、没有数据飞轮、没有”基于你的数据训练的模型”。这在 AI 时代可能是双刃剑。
十一、读完这个项目之后我想问的一个问题#
我读完 santifer/career-ops 之后一直在想一个问题:
当一个 AI 工具不是替你做事,而是帮你”避免做错事”——它的产品价值怎么量化?
career-ops 不替你投简历、不替你发邮件、不替你点提交。它只帮你判断哪些事不该做、哪些事该做、怎么做。
“避免投错公司”这件事,对用户的回报是多少?“避免准备错的面试”呢?这种”被保护的价值”很难量化,但用户能感受到。
也许这就是为什么 santifer 选择开源——这种”反生产力”工具的商业化逻辑很难讲清楚,但它的代码可以被人读、被人 fork、被人改。
附录:关键源码定位速查#
| 模块 | 文件 |
|---|---|
| README manifesto | README.md:1-50 |
| 架构原则 | ARCHITECTURE.md:33、MANIFESTO.md:14-19 |
| 单 agent 模式 | modes/oferta.md:13-180、modes/auto-pipeline.md、modes/apply.md |
| 多 agent 黑板 | data/pipeline.md、data/applications.md、data/agent-inbox.md |
| 锁与对账 | pipeline-lock.mjs、reconcile-pipeline.mjs、merge-tracker.mjs |
| CV 生成 | build-cv-html.mjs、build-cv-latex.mjs、generate-pdf.mjs |
| 评估流程 | oferta.md、analyze-patterns.mjs、funnel-velocity.mjs |
| 批处理协议 | batch/batch-prompt.md:144、batch/batch-runner.sh |
| 公司研究 | company-history.mjs、company-funded.mjs |
| ATS 适配器 | providers/_registry.mjs + 90+ providers/*.mjs |
| 评估日志 | assessment-log.mjs、data/assessments.tsv |
| 隐私立场 | MANIFESTO.md:14-19、browser-extract.mjs 顶部注释 |
| 系统更新 | update-system.mjs apply / check / rollback |
| 多语言 README | README.{es,de,fr,pt-BR,ko,ja,cn,zh-TW,ua,ru,pl,da,ta,ar,hi,tr}.md |
📘 项目信息
- 仓库:santifer/career-ops ↗
- 作者:santifer ↗(“I spent months applying to the hard way”)
- License:MIT
- 媒体报道:WIRED(希腊版)、Business Insider(2026-04)、Product Hunt Featured
- Star 趋势:https://warpchart.dev/hq ↗