首页
/ career-ops 求职结果复盘:用 patterns 模式从申请数据中定位有效策略与无效投递

career-ops 求职结果复盘:用 patterns 模式从申请数据中定位有效策略与无效投递

2026-09-08 20:07:17作者:柯茵沙

本篇文章完整讲解开源求职工作流项目 career-ops 中的 patterns(拒绝模式检测)模式:它如何读取本地申请追踪器、逐条评估报告与面试会话记录,将成百上千条投递沉淀为可执行的策略结论——哪些岗位类型真正在转化、哪些远程策略正在浪费申请、ATS 与猎头渠道的通道产出如何比较。读完你将掌握从运行分析脚本、解析 JSON 统计口径、生成 pattern-analysis 复盘报告,到反向修改门户过滤器与投递目标这一整条"数据驱动求职"闭环的具体操作,以及脚本背后的分母逻辑与因果谦逊(causal humility)约束。

模式定位:从"赢没赢"到"打没打对靶"

patterns 模式解决的是一般求职复盘工具都做不好的核心问题:把零散的申请结果升维成可用于决策的模式信号。它的设计包含两层递进的信号源:

  • 结果层(Step 1):分析所有已跟踪申请的最终状态,找出哪些因素与"被推进"正相关、哪些因素与"被拒绝/无回应"强相关。可以量化的维度包括岗位类型(archetype)、远程策略(remote policy)、评估分数区间(score range)、技术栈缺口(tech stack gaps)、公司规模以及投递渠道。
  • 内容层(Step 1b):当存在模拟/真实面试会话记录时,进一步分析候选人在"房间里实际说了什么"——这是比胜败结果分辨率更高、噪声更低的角色匹配信号。如果候选人最流利、最具体的回答集中指向的角色类型(X),与他们持续投递的岗位类型(Y)不一致,就构成典型的角色错位(role misfit):"你一直在输"(结果层)与"你在瞄错靶"(内容层)是两回事,后者往往更隐蔽、也更值得优先修正。

模式的运行输入约定如下(用户本地数据层,公开仓库不携带个人数据,但提供对应模板):

输入 作用
data/applications.md 申请追踪器(主数据源)
reports/ 每份职位评估报告
config/profile.yml 用户画像(为建议提供上下文,参考 config/profile.example.ymlexamples/dual-track-engineer-instructor/profile.yml
modes/_profile.md 用户角色类型(archetype)与表述框架(公开仓库提供骨架模板 modes/_profile.template.md
portals.yml 门户/职位源配置(用于生成过滤条件更新建议,参考 templates/portals.example.yml
interview-prep/sessions/*.md 面试会话记录(可选;驱动 Step 1b)

运行前置条件:最低数据阈值

在开始任何分析前,脚本会校验 data/applications.md 中是否有至少 5 条真正已投递的记录(状态为 Applied、Responded、Interview、Offer、Hired、Rejected 之一)。SKIP 与 Discarded 行不计入——它们不是投递,不能构成可学习的结果。

若不足 5 条,应向用户提示并优雅退出:

"Not enough data yet -- {N}/5 applications have progressed beyond evaluation. Keep applying and come back when you have more outcomes to analyze."

这一阈值可在命令行用 --min-threshold <n> 覆盖(默认 5),其实现与校验参见 analyze-patterns.mjsMIN_THRESHOLD 的解析,以及 tests/analyze-patterns-outcomes.test.mjs 中对"数据下限只统计已投递行(13 行中 11 行,不含 Evaluated 与 Discarded)"的端到端断言。

Step 1:运行分析脚本并解析 JSON 输出

分析主命令:

node analyze-patterns.mjs

脚本以 JSON 形式向 stdout 输出结构化结果。先决参数说明(见脚本头注释与 tests/analyze-patterns-cli-flags.test.mjs):

参数 说明 默认值
--summary 输出人类可读的汇总表格(替代 JSON) 关闭
--min-threshold <n> 分析所需的最小已投递数(数据下限) 5
--min-vendor-n <n> 单渠道(厂商/机构)可作结论的最小样本数 8
--self-test 运行内置一致性自检并退出 关闭
--help / -h 打印用法

JSON 顶层键位及其语义如下(每个百分比都能在输出中找到它的分母):

Key 内容
metadata 总条目、日期范围、分析日期、各结果计数,以及 outcomeRates——把整张追踪器放进与每个分组行相同的 submitted / decided 口径中计算
funnel 各阶段计数(evaluated、applied、interview、offer……)
scoreComparison 按结果分组(positive、awaiting、negative、discarded、self_filtered、pending)的分数均值/最小值/最大值
archetypeBreakdown 按角色类型分组:total、submitted、decided、positive、awaiting、negative、discarded、self_filtered、conversionRate、decidedRate
blockerAnalysis 最频繁的硬性阻断因素:geo-restriction、stack-mismatch、seniority、onsite;每个 percentage 都是占 blockerBase 的比例
blockerBase 携带非空 gaps 数组的条目数——blockerAnalysis[].percentage 的分母
remotePolicy 远程策略分桶(global、regional、geo-restricted、hybrid/onsite……),列结构与 archetypeBreakdown 相同
companySizeBreakdown 公司规模分桶:startup、scaleup、enterprise
vendorAnalysis ATS 渠道分析:按厂商的推进率 + 覆盖率
viaChannelAnalysis 猎头渠道分析(#1596):按机构的推进率 + 机构 vs 直投汇总
scoreThreshold recommended(在 sufficientSample 满足前为 null)、observedMinimumsampleSizesufficientSample、reasoning
techStackGaps negative / discarded 结果中最常见的技能缺口
discardReasonStats 用户主动跳过/放弃的原因;每个 percentage 是占 discardReasonBase 的比例
discardReasonBase self_filtered、discarded 或 negative 条目数——discardReasonStats[].percentage 及建议阈值分母
recommendations 前 5 条可执行建议(含理由与影响级别)

若脚本返回 error,直接展示错误消息并退出(数据不足时以非零状态码退出)。

统计口径是分析的灵魂:分母与结果分类

patterns 的结论可信度完全取决于统计口径。脚本源码把口径纪律内建到了命名与函数中,这是整份报告最值得先理解的部分。

六个结果桶classifyOutcome() 定义(analyze-patterns.mjs):hired/interview/offer/respondedpositiveappliedawaitingrejectednegativediscardeddiscardedskipself_filtered;其余(Evaluated)→ pending。脚本同时内置了多语言状态别名表(如西语的 aplicado/enviada 归入 awaiting,rechazado 归入 negative 等),且测试明确断言"已投未回绝不等于积极结果"。

三个分母贯穿所有分组行(withOutcomeRates()):

  • submitted = positive + negative + awaiting——一切转化率的分母;
  • decided = positive + negative——一切决定率的分母;
  • metadata.total 禁止作为任何比率的分母,因为它把从未投出的 Evaluated 行也计入;整表口径请一律引用 metadata.outcomeRates

conversionRate = positive / submitted(所以一个"已投未回"占多数的细分段天然读数偏低);decidedRate = positive / decided,且在没有任何 decided 结果时为 null 而不是 0——"尚无数据"与"0% 成功率"是两回事。测试(tests/analyze-patterns-outcomes.test.mjs)用一个 25 行含 15 行从未投出的夹具精确断言了 submitted=10conversionRate=10%(若除以 total 会错误得 4%)。

建议的触发也以计数而非百分比为准bestDecidedSegment()/worstDecidedSegment() 都要求至少 2 条 decided 结果(MIN_DECIDED_FOR_RECOMMENDATION = 2),因为雇主沉默在正反两个方向都不构成证据;1 条拒绝 + 4 条未回不是"该避开这个细分段"的理由。

scoreThreshold:观测值 vs 阈值

最低积极分数的计算遵循"先观测、后建议":

  • 只要存在带分数的积极结果,observedMinimum 就报告最低积极分;
  • 只有当带分数的积极结果 ≥ 3 条MIN_POSITIVE_SCORES_FOR_THRESHOLD),且存在低于该下限而被拒绝的申请时,recommended 才被给出(向下取整到 0.1);
  • 若样本不足或"没有低于该分数的申请被拒",recommended 保持 null——此时 recommended 只作为对 sampleSize 条结果的观测呈现,不应持久化为过滤阈值("没有任何低于 X 的结果能推进"在从未有低于 X 的结果被决定时是空洞论证)。

通道分析:ATS 厂商与猎头机构的"通道产出"

vendorAnalysis——ATS 渠道产出(必须保持因果谦逊)

vendorAnalysis已投递申请分组,厂商来源由每份报告的 **URL:** 字段检测。检测范围是拥有干净、公开 URL 指纹的社区 ATS:Greenhouse、Lever、Ashby、Workday(源码中实际还纳入了 iCIMS,见 analyze-patterns.mjsVENDOR_HOST_PATTERNS);白标 ATS 无法凭 URL 识别,落入不报告的 unknown 桶。advanceRate = 达到 Responded/Interview/Offer 的比例(裸 Applied 不计为推进)。

其动机源自学术界关于"招聘算法单一化"的观察(Bommasani et al., FAccT 2026;该引文在脚本中作为 vendorAnalysis.citation 字段随输出携带):经由共享筛选器被拒的申请之间是相关而非独立的,因此一个高度集中的死渠道继续重复投喂同样画像的边际收益递减。

向用户叙述时必须遵守三条纪律:

  1. 报告通道产出,而非歧视结论。 单一追踪器无法区分"该厂商算法筛掉了我"与"该厂商的用户群偏向我不契合的细分人群"。诚实而有用的表述是:"你 X% 的申请经由 {vendor},而它的推进率远低于你的其他通道——请将这些公司改走内推/直接联系。"
  2. 尊重 sufficientSample 若为 false,厂商只能作为观测提及("样本过少,不足以定论"),绝不能作为建议。
  3. 始终说明覆盖率 coveragePct,让用户知道统计只覆盖提交集的一个子集。

当某厂商满足条件时,recommendations 数组中已包含一条 high 影响力的通道建议(触发条件:sharePct >= 25%、样本足够、推进率低于其他所有通道至少 10 个百分点,采用 leave-one-out 比较基准避免自我稀释)——逐字呈现它,而不是编造更强的说法

viaChannelAnalysis——按猎头机构的推进率(#1596)

已投递申请按 Via 列(招聘机构,需可选 Via 列支持,#1596)分组;无 Via 列的追踪器产出空桶且不做任何断言。 行计为 directbreakdown 列出每家机构的 total/advanced/advanceRate/sufficientSample。Via 单元格为空(旧版追踪器或空单元格,区别于显式的 直投标记)的已投行既不属任一桶,计入 unknownVia——非零时需向用户说明机构/直投拆分只覆盖提交子集。

在猎头主导的求职中,最高杠杆的决策是投资哪几条猎头关系——该分析直接显示哪家真正在转化。与 vendorAnalysis 相同的因果谦逊规则适用:报告通道产出而非因果主张;尊重 sufficientSample;强机构是"优先投经由 X 的岗位——它转化好",弱机构是观测而非指控。符合条件的"最佳转化机构"建议已以 medium 影响力存在于 recommendations 中,逐字呈现即可。

附加分析镜头:薪酬、公司历史与漏斗校准

在卡片结论前,可选叠加三个互补镜头(文档均注明"零 token——绝不手工重算这些数字"):

  • 薪酬镜头:若存在薪酬观测(报告的 advertised_comp 键或 data/salary-observations.tsv),运行 node salary-gap.mjs --summary,观察按(公司、角色)与币种的广告薪资→实际到手的折价及期望达成度。对其数据质量章节要与 sufficientSample 同等对待:小样本只是观测,不是建议。
  • 公司历史镜头:运行 node company-history.mjs --summary 提供额外视角。
  • 漏斗校准镜头:若追踪器存在 Applied 及以后的行,运行 node funnel-velocity.mjs --summary,得到候选侧漏斗率 vs 市场基准区间、超过典型首回窗口的在途申请,以及(一旦 data/status-log.tsv 积累足够状态迁移后)每个阶段跳转的中位天数。

漏斗校准的呈现规则是硬性 MUST:

  • 高于区间:可以鼓励(该镜头本就为对抗"沉默焦虑"而设),但必须保留脚本的选择偏差提示——定向申请本应优于大众平台均值。这确认的是过滤质量,不是市场宽松。
  • 低于区间:做校准 + 恰好一条具体行动(用 followup 模式做跟进合规,或用 patterns Step 2 复核分数阈值)。绝不给出对候选人的裁决。
  • 每个基准提及都要带年份与"方向性"字样——随脚本交付的基线是招聘行业聚合值,不是同行评审统计。
  • n<20 已投时禁止输出比较性倍数声明(脚本已抑制,不要用原始数字手工重建)。
  • 速度中位数必须复述脚本打印的删失计数("n 仍在等待,已排除")——只对已答复申请算中位数,没有该提示就是幸存者偏差。

Hygiene First:陈旧记录优先于一切卡片

任何卡片之前,汇总先输出疑似"静默"的 aged-Applied 行清单。要求用户确认真实状态或用 node set-status.mjs --row <num> <state> --on <response-date> 更新,再据此得出卡片结论——一条陈旧的追踪器行产生的信号与真实静默完全相同。随后按静默优先的顺序展示卡片(脚本已如此排序)。

因果谦逊在此同样强制:这些卡片只是"关于你自己文件的事实"——某公司显示 silent-on-you 仅表示"你的追踪器/跟进记录中没有回应",而非"该公司从未回应"。高容量收件箱、常青职位、重开的搜索、收到回应却未记录,都是常见且无辜的解释。呈现事实(自某日起静默 N 天、已发 M 条跟进),由用户自行判断,绝不把卡片表述成对公司的裁决。

Step 1b:会话内容定向信号——检测"瞄错靶"

结果数据(Step 1)说明你是否在赢;面试会话说明你在房间里实际推销的是哪种角色——这是比胜败(被薪酬、时机、编制及十余种与匹配无关的因素混淆)分辨率更高、噪声更低的角色匹配信号。

仅在存在会话数据时运行此步:检查 interview-prep/sessions/*.md(排除 README.md.gitkeep)。没有会话则静默跳过并继续纯结果分析——该步纯粹是增量价值,模式完整可离它运行,随会话积累逐步获得分辨率。

若存在会话,逐份执行:

  1. 将候选人回答与面试官提问分离(缺少说话人标签时,按会话格式约定的 **Interviewer:** / **Candidate:** 轮次推断);
  2. 判断每条实质回答所展示的能力/角色信号(如 instructional-designsystems-architecturedata-analysisstakeholder-managementpeople-leadership)。标签优先、推断兜底:若回答已携带显式能力标签(按 interview-prep/sessions/README.md 约定的 <!-- competency: ... -->,无论手写还是由复盘工具如 interview/debrief 输出)直接采用;仅在无标签时才自行推断;
  3. 标记该回答是流利且具体(有具体指标、具名工具、真实决策)还是平淡且笼统(含糊、空泛、教科书式)。

随后跨会话聚合:

  • 流利/具体回答聚集在哪个能力簇? 该簇就是候选人实际最强的角色类型——无论简历上的头衔写着什么;
  • 将该簇与(a)modes/_profile.md 中的角色类型及(b)data/applications.md 中实际投递角色的分布对比;
  • 暴露错位:若最强簇(X)在已投角色(Y)中占比不足,即构成定向修正信号:

"Your answers consistently light up around X, but you're mostly applying to Y. Consider adding archetype X and reweighting portals.yml title_filter.positive toward it."

这就是"你一直在输"(Step 1 结果层)与"你在瞄错靶"(Step 1b 内容层)的本质区别。结论汇入 Step 2 报告与 Step 4 建议。

隐私红线:会话含真实面试官姓名与公司。仅本地阅读;绝不把真实姓名或公司写进会被提交的报告。只总结信号(能力簇),绝不总结内容。

Step 2:生成复盘报告

将报告写入 reports/pattern-analysis-{YYYY-MM-DD}.md,沿用以下结构(每一节的渲染规则都直接继承脚本的统计口径):

# Pattern Analysis -- {YYYY-MM-DD}

**Applications analyzed:** {total}
**Date range:** {from} to {to}
**Outcomes:** {positive} positive, {awaiting} awaiting a reply, {negative} negative, {self_filtered} self-filtered, {pending} not sent

---

## Conversion Funnel

Show each status with count and percentage of total. Use a simple table:

| Stage | Count | % |
|-------|-------|---|
| Evaluated | X | X% |
| Applied | X | X% |
| ... | | |

## Score vs Outcome

| Outcome | Avg Score | Min | Max | Count |
|---------|-----------|-----|-----|-------|
| Positive | X.X/5 | X.X | X.X | X |
| Negative | ... | | | |
| Awaiting | ... | | | |
| Self-filtered | ... | | | |
| Pending | ... | | | |

## Archetype Performance

Table with each archetype: `submitted`, `decided`, positive, `decidedRate` (lead with it) and `conversionRate`. Highlight the best-performing archetype and the worst.

`conversionRate` = positives over everything sent, so a mostly-unanswered segment reads low by construction. `decidedRate` = positives over the applications with a recorded outcome, and is `null` while nothing is decided -- report that as "no data yet", never 0%. Never divide by `metadata.total`: it counts evaluated rows that were never sent; the whole-tracker rates live in `metadata.outcomeRates`.

## Top Blockers

Frequency table of recurring hard blockers (geo-restriction, stack-mismatch, etc.).
Show each frequency against `blockerBase`; its percentage is the share of
gap-bearing entries, not the share of all applications.

## Remote Policy Patterns

Table showing conversion rate by remote policy bucket (global, regional, geo-restricted, hybrid/onsite).

**Never call a segment bad on `conversionRate` alone** -- an all-awaiting bucket shows a low rate and a `null` `decidedRate`, which is silence, not rejection. Call `0%` negative only when `positive` is 0 and `decided` >= 2, and name the decided count.

## Tech Stack Gaps

List of most common missing skills in negative / discarded / self-filtered outcomes with frequency.

## Recommended Score Threshold

State the data-driven minimum score and reasoning. When `sufficientSample` is false, `recommended` is `null`: report `observedMinimum` as an observation over `sampleSize` outcome(s) and do not offer to persist it.

## Targeting Signal (interview sessions)

*Include this section only if Step 1b ran.* Summarize, in competency terms only (no real names/companies):
- Which competency cluster the candidate's answers are strongest at (X)
- Which role-types they're actually applying to (Y)
- The misfit gap and the suggested realignment (add archetype X / reweight `portals.yml`)

## Recommendations

Number the top recommendations (from the script output). For each:
1. **[IMPACT]** Action to take
   Reasoning behind the recommendation.

各章节的硬性口径在脚本与测试中均有对应实现:archetype 分段"双倍下注"推荐按 decidedRate 排序而非 conversionRate(避免"1 positive + 1 awaiting"压倒"5 positive + 15 awaiting");远程策略分段绝不因全 await 桶的低转化率判死刑——只有当 positive 为 0 且 decided >= 2 时才把 0% 表述为负面并点名 decided 数(tests/analyze-patterns-outcomes.test.mjs 对 hybrid/onsite 桶"1 拒 + 4 未回不得触发 Avoid"有逐字断言)。

Step 3:呈现摘要

向用户展示浓缩版,包含:

  1. 一行来自 metadata.outcomeRates 的统计(X 已投、Y 已决、Z% 的已决有进展)——绝不使用 metadata.total 的占比
  2. Top 3 发现(影响力最大的模式);
  3. 完整报告链接。

摘要示例:

Pattern Analysis Complete (24 applications, Apr 7-8)

Key findings:

  • Geo-restricted roles: 0 of 7 decided applications advanced -- stop evaluating US/Canada-only postings
  • Regional/global remote roles convert at 57-67% -- these are your sweet spot
  • No positive outcomes below 4.2/5 -- consider this your score floor

Full report: reports/pattern-analysis-2026-04-08.md

Step 4:落地建议(把结论写回配置)

分析的价值终点是把结论写回配置。询问用户是否要执行建议:

"Want me to apply any of these recommendations? I can:

  • Update portals.yml to filter out geo-restricted roles
  • Set a score threshold in _profile.md for PDF generation (only when scoreThreshold.sufficientSample is true)
  • Adjust archetype targeting based on what's converting
  • Realign targeting from the session signal -- add the under-targeted archetype X to modes/_profile.md and reweight portals.yml title_filter.positive (if Step 1b ran)

Just say which ones, or 'all' to apply everything."

若用户同意,遵循三条明确的落点规则:

  • 门户过滤改动 → 编辑 portals.yml。其中 title_filter.positive 是关键字段:它按子串(大小写不敏感)匹配标题,2–3 字母关键字按词边界匹配;每条目还可加 word:(要求整词,任意长度)与 stem:(要求词首)前缀来精确控制匹配面。逐条精确语义见 templates/portals.example.ymltitle_filter 的长注释。
  • 画像/角色类型改动 → 编辑 modes/_profile.md绝不改 _shared.md,后者是系统可更新的共享层;你的覆盖文件永远优先,骨架参考 modes/_profile.template.md)。
  • 分数阈值 → 在 config/profile.ymlpatterns 键下新增。

脚本产出的建议还覆盖了"把最常见的跳过原因(如 geo-block)写成过滤器到 modes/_custom.md"、"过滤掉要求特定主栈(top 缺口技能)的角色"等高影响动作,全部在 recommendations 数组中逐字可用。

附:结果分类参考

结果分类约定如下(与 analyze-patterns.mjsclassifyOutcome() 及状态别名表一致):

状态 结果
Interview、Offer、Responded、Hired Positive(雇主将其向前推进)
Applied Awaiting(已投、暂无回复——计入转化分母,永不计为成功)
Rejected Negative(雇主拒绝)
Discarded Discarded(你撤回或职位关闭——非雇主决定、非已证实的投递;submitteddecided 均不计)
SKIP、NO APLICAR Self-filtered(用户决定不投)
Evaluated Pending(从未投出)

深挖指引:想验证任意口径,去读这三处

  • 实现analyze-patterns.mjs——结果分类、三个分母、scoreThreshold、厂商检测、机构分桶与全部建议触发器都在这里;运行 node analyze-patterns.mjs --self-test 可对内建一致性断言做自检。
  • 统计口径回归测试tests/analyze-patterns-outcomes.test.mjs——逐条锁定"Awaiting 只进分母""decidedRate 无数据时为 null""Avoid 至少需 2 条 decided"等防回归规则。
  • CLI 校验测试tests/analyze-patterns-cli-flags.test.mjs——覆盖 --min-threshold/--min-vendor-n 的空格与等号两种写法及非法参数拒绝。

整体而言,patterns 模式把"求职失利"从情绪事件变成可审阅的数据事件:整条链路从追踪器出发,经由统一的分母纪律与因果谦逊约束,最终回到门户过滤器、角色类型与分数阈值的精确调优,让每一份申请都成为下一次投递的校准样本。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.76 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
858
1.35 K
docsdocs
暂无描述
Markdown
899
5.82 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
923
1.85 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.83 K
1.02 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
532
596
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.03 K
524
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.37 K
1.46 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
393