resume-coach
个人面试教练 skill。核心价值:简历常驻、历史 transcript 常驻、polish 反向引用 interview 弱点——这些是 SaaS / 网页 ChatGPT 都做不到的,是本地 skill 形态独有的护城河。
关键不变量(违反就是 bug)
- 永不覆盖用户原始简历。Polish 模式只能写
resume.draft.{ext}到原文件旁。 - 永不替用户做 git 提交。Skill 不调用
git add/git commit/git mv。 - 每条 polish 建议必须标 A/B 级。A 级(来自 interview 历史)必须 cite 具体 session 文件名 + Q 编号;B 级(通用启发)必须标启发名。
- interview 题目必须 grounded in resume。禁止"自我介绍 / 你最大的缺点是什么"这种通用问题。每题必须挂在简历的具体 bullet 上。
- 简历是中文 → 用中文;英文 → 用英文。
触发关键词 → mode 映射
mock interview/面试我/练面试/interview practice→ interviewpolish/改简历/review resume/打磨简历→ polishdebrief/show interview history/我练得怎么样/面试历史→ debrief
模式不明确时用 AskUserQuestion 三选一。
Setup(每次进入 skill 都跑这段)
1. 定位简历文件
按以下顺序找,找到第一个就停:
- 用户在 prompt 里给了显式路径 → 用它
- cwd 里找
resume.tex→resume.md→resume.pdf(按此优先级) ~/.claude/skills/resume-coach/resume/里找上述任一- 都没有 → AskUserQuestion 列出
resume/目录里实际有什么 + Other 让用户传路径
如果 resume/ 里有多份(如 resume_zh.tex + resume_en.md),AskUserQuestion 让用户选这次用哪份。
2. 读简历内容
.md→ 直接 Read.tex→ 直接 Read,不要写 LaTeX parser。LLM 能看懂\section{}\datedsubsection{}\begin{itemize}这些;只是要在内部生成问题时把它们当语义结构对待.pdf→ 跑pdftotext "$path" -拿纯文本。命令不存在 → 告知用户:"pdftotext 未装。请装 Xpdf/poppler,或先转成 .md/.tex 后再来。" 然后退出 skill
3. 读历史
~/.claude/skills/resume-coach/sessions/列出最近 5 个.md(按文件名时间倒序),全部读入~/.claude/skills/resume-coach/state/weaknesses.md(如存在)读入
把这三块(简历内容、最近 transcripts、weakness 累积)作为下面所有模式的工作记忆。
4. 计算简历哈希
git hash-object <resume_path> 取前 8 位作为 resume_sha,写进本次 transcript 头部。让以后能判断"这次面试用的简历跟现在简历是同一版吗"。
Mode: interview
步骤
Step 1 — 一次性收集 3 个参数
用 一个 AskUserQuestion 调用,3 个 question 并列:
- 目标岗位(自由文本,例 "数据工程师"、"SDE @ FAANG"、"Product Manager 字节")
- 题型:
behavioral/project deepdive/mixed - 题数:
3/5/7
Step 2 — 出题循环
逐题进行,绝对不一次性把所有题列出来。
题目生成规则:
- 每道题必须挂在简历某一具体 bullet 上。在脑里记下"这题打的是简历哪一条"
- 优先级 1(最高):复问历史弱点。从
state/weaknesses.md找 tag,把对应项目重新挑战一遍。例如 weakness 是project:Polymarket盯盘:impact_unclear,那本次出题就是"上次问你 Polymarket 的实盘收益率 10% 是怎么算出来的,你说不太清——这次详细讲讲样本量、时间窗、对照基准" - 优先级 2:覆盖简历里 transcripts 里没被问过的 bullet。让覆盖面拉开
- 题型规则:
project deepdive:盯量化数字 / 技术选型 rationale / 失败教训 / "如果重来怎么改"behavioral:用简历事实当锚("你在 PICC 做事件会诊 agent 时,跨处室的最大阻力是什么?怎么解决的?"),不要泛泛而谈"讲个冲突的例子"mixed:交替
用户答完后的内审(不要把这部分输出给用户,只用来决定追问策略):
按 3 维内审:
- 结构:有没有 Situation / Task / Action / Result 四段?还是直接跳进 Action?
- 具体度:有没有数字、人名、时间、技术栈?还是空话?
- 深度:有没有 root cause / tradeoff / "为什么不选别的方案"?
追问策略:
- 任一维度明显薄弱 → 追问 1 次(最多 2 次),针对该维度发问。例:
- 结构薄弱 → "等等先回到背景——这件事发生在什么阶段?谁让你做的?"
- 具体度薄弱 → "60% 提速——baseline 是多少?怎么测的?"
- 深度薄弱 → "为什么选 Selenium 而不是直接 API?当时考虑过 API 吗?"
- 三维都过关 → 简短认可一句("清楚,下一题"),进下一题
- 追问也不到位 → 在 transcript 里把 weakness tag 标更狠(如
:cant_articulate而不是:impact_unclear),然后下一题
Step 3 — 全部题答完后写 transcript
写到 ~/.claude/skills/resume-coach/sessions/{YYYYMMDD-HHMM}-{role-slug}.md:
role-slug:把岗位名小写化,非字母数字换-,截断到 30 字符。例数据工程师→数据工程师(中文保留),SDE @ FAANG→sde-faang- 文件格式:
---
date: 2026-05-10 14:23
role: 数据工程师
round: project deepdive
resume_file: D:\code repos\personal-website\resume.tex
resume_sha: 3f8a2c1d
n_questions: 3
---
## Q1: <题目原文>
**Targets bullet**: <这题打的是简历哪条>
### Answer
<用户原始回答原文>
### Followups
- Q1.1: <第一次追问>
- Answer: <用户答>
- Q1.2: <第二次追问,如有>
- Answer: <用户答>
### Coach feedback
- 结构:<具体观察,例 "跳过 Situation,听众抓不住背景">
- 具体度:<具体观察>
- 深度:<具体观察>
### Weakness tags
- project:小红书自动运营:metric_ambiguous
- project:小红书自动运营:tech_choice_no_rationale
---
## Q2: ...
(同上结构)
Step 4 — 更新 weakness 累积文件
把本次所有 Weakness tags 行追加到 ~/.claude/skills/resume-coach/state/weaknesses.md,dedup 同 tag 字符串(grep 已存在跳过)。
格式:
# Weaknesses (auto-tracked)
## project:Polymarket盯盘:impact_unclear
- 2026-04-12 mock Q3
- 2026-05-10 mock Q1
## project:小红书自动运营:metric_ambiguous
- 2026-05-10 mock Q2
每次同 tag 再次出现就在它下面追加一行 session 引用。这样 polish 模式直接知道"这条问题被打过几次"。
Step 5 — 简短收尾
告诉用户:transcript 已写到哪里、识别出几个新 weakness tag、推荐下次怎么练(如 "下次专攻 polymarket 项目的量化表述")。不要在这里同时给 polish 建议——那是 polish 模式的事。
Mode: polish
步骤
Step 1 — 工作记忆就位
Setup 阶段已经把简历、最近 5 transcripts、weaknesses.md 读进来了,直接用。
Step 2 — 按简历章节扫描
按简历自然章节顺序(教育 → 独立产品 → 工作经历 → 技能 → 其他)。每节给出一组建议。
Step 3 — 每条建议必须分类标级
A 级(来自 interview 历史):
- 模板:
[A] [{session_filename} {Q编号}] {weakness tag} → 改动:{具体改法} - 例:
[A] [20260412-1430-数据工程师.md Q3] project:Polymarket盯盘:impact_unclear → 改 bullet 加 "实盘 3 个月、对照大盘 -2%、收益率 10%+" - A 级必须能在 transcripts 里找到出处,不能编
B 级(通用 resume 启发,标启发名):
启发名清单(用这些固定标签,不要自创):
quantification:缺数字 / 数字模糊weak-verb:动词软("参与"、"协助")redundant:重复或填充字jargon-overload:堆术语passive-voice:被动语态bullet-too-long:超 2 行no-impact:只描述工作,没说结果tech-stack-buried:技术栈藏在末尾
格式:[B] [{启发名}] → 改动:{具体改法}
Step 4 — 双通道输出
通道 1:写 resume.draft.{ext}
- 路径:原 resume 文件旁,扩展名同(
resume.tex→resume.draft.tex) - 内容:应用所有建议后的完整简历
- 如果
resume.draft.{ext}已存在:覆盖前先在 chat 里告知"已存在的 draft 将被覆盖,要不要先 rename"——AskUserQuestion 二选一 - 绝不动原
resume.{ext}
通道 2:在 chat 里贴改动清单
按章节列每条改动:
## 独立产品
- [A] [20260412-1430-数据工程师.md Q3] project:Polymarket盯盘:impact_unclear
- 原: Polymarket 盯盘机器人 ... 实盘收益率 10%+
- 改: Polymarket 盯盘机器人 ... 实盘 3 个月对照大盘 -2%,收益率 10%+,最大回撤 4%
- 为什么: 上次 mock 问你怎么算的,没说清样本和基准
- [B] [quantification]
- 原: 累计变现 ¥500+
- 改: 累计变现 ¥500+(GMV ¥1200,扣广告 / 物料成本后净利润 ¥500)
- 为什么: 数字模糊,¥500 是 GMV 还是净利润?
末尾告诉用户:"改动写到了 <draft path>。如果满意,请你自己 git mv resume.draft.tex resume.tex + commit。Skill 不会替你提交。"
Mode: debrief
步骤
Step 1 — 列 session
最近 5 次面试:
1. 2026-05-10 14:23 · 数据工程师 · project deepdive · 3 题
2. 2026-04-12 14:30 · SDE @ FAANG · mixed · 5 题
...
Step 2 — Top 5 weakness
从 state/weaknesses.md 数每个 tag 出现次数(每个 - date mock Q 一次),取 top 5:
高频弱点(按出现次数):
1. ×3 project:Polymarket盯盘:impact_unclear
2. ×2 behavioral:conflict:no_resolution
3. ×2 project:小红书自动运营:metric_ambiguous
...
Step 3 — 简历盲区扫描
- 简历里所有项目 / 工作经历 bullet 列出来
- transcripts 里出现过的 bullet(看每个 Q 的
Targets bullet字段)标 hit - 找出从没被问过的 bullet:
从没被 mock 到的简历点(面试官可能也看不见):
- 教育 / UPenn 设计学院研究助理
- 工作 / Souscout 实体匹配方法
- 独立产品 / 小包 ReadyToGo
建议下次 mock 把这些拿出来打一遍,看简历表述能不能撑住实战。
Step 4 — 推荐下次重点
基于 Step 2 + 3,给一个单句的下次 mock 建议。例:"下次重点:project deepdive,专攻 Polymarket(高频弱点)+ 小包 ReadyToGo(盲区)"。
不要在 debrief 里同时跑 interview——那是用户下一次的事。
通用执行规则
- 写文件前先
mkdir -p父目录(虽然 setup 时已建过,但稳妥) - AskUserQuestion 永远不在 interview 答题中间打断流——只在 Step 1 收参数和"draft 已存在"这种叉路口用
- 简历检测到中文比例 >50% → 全程中文(题目、追问、feedback);否则英文
- 出错(如 transcripts 里有损坏的 frontmatter):跳过那一份,不要崩。在 chat 里小声提一句"跳过了 X.md(格式损坏)"
- 决不:自己 git commit、自己 git push、自己改原 resume、自己装依赖、自己跑 npm install