| name | pxb-liepin |
| description | Pixboom 招聘搜人筛人作业流程(猎聘 liepin-cli)。当为 Pixboom 任一岗位搜候选人、 筛简历、提报、维护候选人池/去重台账/JD经验时使用——"下一轮""继续搜""搜人""筛简历" "提报候选人""开简历给我看"等。封装排队抢锁、两段式粗筛/精筛、默认无头+反爬、 去重落盘、给用户看简历/出 PDF。岗位标准看该岗 JD,本流程岗位中立。 |
Pixboom 搜人筛人作业流程
⚠️ 改本 Skill 前先想:这里只放泛用作业流程(所有岗通用、可复用)。单一案例/某轮/某岗的标准经验 → 进该岗 JD.md,不进这里。
每岗:JD.md(对外JD + 内部备注表,判断只看它)· 去重台账.csv(resume_id,status,仅去重)· 候选人池.md(确认候选人)· 待定/(简历 PDF 中转)。
路径:命令里 <岗位>\… 等相对路径相对工作区根目录(本 CLAUDE.md 所在);脚本统一在 .claude/skills/pxb-liepin/scripts/(下文简写 SC)。机械活全在脚本里、跨 Win/mac;判断在本文+agent。
全流程:两层循环(主 agent 直接串行跑,不派 subagent)
- 为什么不用 subagent:整个流程本质单账号、单锁、串行——subagent 没有并行收益,反而启动慢、跨工具/跨模型对子代理控制差、易卡死。主 agent 自己从头跑到尾最稳、最快、最通用。
- 小循环(步骤 1-3,主 agent 自跑、不打扰用户):搜一页 → 粗筛挑 ~10 → 精筛 → 合适标
待提报 → 没攒够回步骤 1 再搜,连轮跑到攒够 ~5 个 待提报。绝不跑一轮就停等用户。
🔴 边跑边报,别闷头静默:每跑完一轮,先输出一行进度再继续下一轮,别一口气跑到底让用户 10-20 分钟看不到动静。"不打扰用户"指没攒够时别停下问用户拍板,不是全程不吭声;进度是单向心跳,不算打扰。
每轮播报格式:第N轮(词:X):搜A/精筛B/新增待提报C,累计待提报 D/目标 E,继续…(熔断/出批时另行说明)。带上本轮关键词,方便步骤 6 复盘哪个词有效。
- 大循环(步骤 4-6,用户校准 JD):一批出 PDF → 用户复核 → 入池 → 迭代回写 JD(重点是进化下一轮关键词)→ 下一批。JD 越调越准、小循环越搜越准。
- 上下文:脚本只输出窄表(非原始 JSON)。看完窄表做完判断后,记住结论即可(待提报的
id=标签、方向感、哪个词有效),不必把整页窄表一直留在上下文;轮数多了丢掉旧轮明细。
P. 确认/补全 JD(搜之前最先做) → 0. 排队抢锁 → 0.5 确认登录(主 agent 引导用户扫码) → 1. 粗筛 → 2. 精筛 → 3. 够一批没(没够回 1)→ 4. 出 PDF 提醒 → 5. 入池 → 6. 迭代回 0。
全程红线:① 单账号串行,绝不并发调 liepin ② 反爬宁慢勿封:脚本非零退出即停、别密集连跑(脚本内已带抖动)③ 原始 JSON 不进上下文(脚本只输出窄表)④ 隐私:简历只存本地、不外发、不传外部服务 ⑤ 只 search/resume,不 greet(沟通归 HR)⑥ 无头硬执行 + 脚本独占:所有 liepin 访问只走脚本(search.mjs/fetch.mjs/pdf.mjs,已内置强制无头)——除用户手动 liepin login 外,绝不直接敲 liepin search/resume。裸调用在 npm 发布版默认有头会弹窗(new CdpBrowser() 不传参,只看 config 默认)。过程中绝不该弹出任何浏览器窗口;一旦看到浏览器弹窗又关闭=有人裸调了 liepin、或没先登录,立即停手排查,别继续刷(可选:在该机器跑一次 patch-headless.mjs 把发布版默认也改成无头,当安全网)。
所有脚本无参运行会打印用法;任一脚本退出码 ≠ 0 = 停手,按其报错处置(多半反爬/被踢/未登录)。
P. 确认 / 补全 JD(搜之前最先做,主 agent+用户,不碰 liepin、不抢锁)
搜任何人之前,先确保该岗有一份够用的 JD——全程判断只看 JD,JD 不全 = 瞎搜。这一步在跟用户的对话里完成,不动 liepin、不抢锁(别占着锁让用户慢慢想)。
- 用户直接给了 JD → 校验完整度,尤其硬约束齐不齐:预算 / 城市 / 经验年限 / 学历 / 年龄 / 目标人数,以及"最看重的 1-2 项能力"。缺哪补哪。
- 用户没 JD,或只有一句话(很常见:客户还没成形)→ 别急着搜,先多轮提问补全:这岗做什么、核心产出是什么、最看重哪 1-2 项能力(硬门槛)、经验年限 / 学历、薪资预算、城市 / 是否远程、目标招几个、有没有对标公司 / 明确不要的背景。一次问一两个,别糊用户一脸;公司公共素材见
CLAUDE.md。
- 补全后写进该岗
JD.md(对外 JD + 硬约束行),用户确认"就按这个搜"后,才进入步骤 0 抢锁。
- 已有成熟 JD、用户也明说直接搜 → 快速过一眼完整度即可,别为流程而流程。
0. 排队 + 抢锁
搜人先往 _共享/搜索队列.md 追一行 待办。当 worker:抢锁 → 挑最早 待办 → 跑 → 标完成 → 解锁。
node SC/lock.mjs acquire <岗位>
node SC/lock.mjs renew <岗位>
node SC/lock.mjs release
- 单账号单锁:同一时刻只一个对话/进程持锁跑 liepin,多岗/多线只能排队(账号物理唯一,与是否用 subagent 无关)。
- 锁的生命周期 = 一"批" = 一次持锁:
acquire(本批第一轮前)→ 每轮 renew → 步骤 4 出 PDF 交用户复核时 release(复核是纯等待、不碰 liepin,把账号让给别的岗)→ 用户复核完、回来搜下一批时再 acquire。别一把锁从头持到尾(会把同时在招的别的岗饿死整段复核时间),也别每轮 acquire/release(和排队逻辑打架)。
- 多岗位同招:各岗在
_共享/搜索队列.md 排队;靠"批次边界释放锁"自然交错——A 岗出批给用户复核时让出账号,B 岗就能搜它那批。
0.5 确认登录(主 agent 亲自跑命令,用户只扫码)
开搜之前,先确认这台机器已登录猎聘。 用户不该自己敲命令行——主 agent 直接运行 login 命令,浏览器弹出二维码,让用户扫一下即可。首跑/换机/被踢后的头号坑:漏了这步就开搜,会撞登录页弹有头浏览器又关掉。
开搜前一次性告诉用户预期:「开始搜了,每轮约 10–20 分钟(单账号串行 + 反爬抖动,快不得),我每轮报一下进度,攒够一批叫你复核。」别让用户干等以为卡死。
1. 粗筛(脚本自动登 CSV)
node SC/search.mjs "<岗位>\去重台账.csv" "关键词" --city 深圳 --limit 40
脚本一步做完:search → 去掉台账已召回的 → 新人登记 未精筛 → 打印窄表(id/姓名/现职/公司/年限/薪资/学历)。登 CSV 是查重命根,脚本已强制做。
- 卡住保护:search 内置 6 分钟硬超时(resume 2.5 分钟),liepin 卡死(撞反爬/滑块/未登录/Chrome 卡住)会强杀并报错,不会静默挂死。一旦报超时:告诉用户"搜索卡住已中断",关掉残留 Chrome、确认已登录、过几分钟再试或把
--limit 临时调小,别立刻重刷(越刷越被反爬盯上)。
卡片只有标题/公司/年限/薪资/学历,判不了能力,只做:① JD 硬约束明显出局的砍(城市/薪资/年限);② 卡片能看出的通用排除砍(见步骤 2);③ 按 JD 正/负向建议挑 ~10 个最对口进精筛(别只挑 5、也别宽到 20+),其余留 未精筛——这是库存:召回变稀时直接挑台账 未精筛 再拉精筛,不重搜。
--limit 40 起步(自动翻页,别 --page):漏斗要宽,召回 ~40-50 里才挑得出 ~10(约 4-5:1)。别为了快降到 20——那样挑 10 几乎没筛。只有反复卡死时才临时调小排查,排查完放回。健康比 未精筛:精筛 ≈ 4:1,随时 node SC/dedup.mjs stats "<岗位>\去重台账.csv" 自查。
2. 精筛(批量取、按 JD 下判决)
拉这页挑出的 ~10 份,一个循环拉完 = 1 个回合,别开一份判一份:
node SC/fetch.mjs <id1> <id2> ...
每份过三关(对照该岗 JD):
- 硬约束:城市/薪资/年限/学历/年龄任一明显违反 →
精筛不合适。
- 正/负向建议:命中正向画像几条?命中负向排除信号 → 砍。
- 真伪:看
work_history 实做、别信技能列表;薪资-成色错配(带队多年要地板价=注水)、方向偏离 → 砍。
通用排除(岗位中立,叠加在 JD 上,命中即砍):① 简历空洞、看不出实际职责产出(是空洞、不是写得短);② 频繁跳槽——多段 <1 年且无任何 ≥2 年稳定段;③ 在职 + 暂无跳槽打算(work_status 为"在职-暂不考虑/暂无跳槽打算"等)——统一砍:约不动、即便约到也议价难,性价比低;"离职找工作""在职-看机会"保留。①卡片看不出留精筛、②卡片能初判的粗筛先排、③看 work_status(精筛见;卡片若显示在职状态可粗筛先排)。
归档:过三关、对口 → 待提报;真·边界且方向稀缺才 候补;其余 精筛不合适("够得着但不对"直接砍)。判完升级状态:
node SC/dedup.mjs set "<岗位>\去重台账.csv" 待提报 <过关id...>
node SC/dedup.mjs set "<岗位>\去重台账.csv" 候补 <边界id...>
node SC/dedup.mjs set "<岗位>\去重台账.csv" 精筛不合适 <淘汰id...>
3. 够一批没?
攒够 ~5 个 待提报(或到目标人数)没?
- 没够 → 回步骤 1 再搜下一页(别停问用户)。
- 够了 → 步骤 4。
- 方向熔断:本次循环已粗筛 ~100 人(≈2 页)却 0 个进
待提报 → 停循环、反馈用户:搜了 N 人 0 命中,把卡住的共性(都倒在哪关 / 哪类问题)说清楚,多半方向或 JD 标准有偏,等校准再搜。别闷头继续刷。
- 路径枯竭(召回越来越稀)→ 先扫
未精筛 库存(dedup.mjs seen 比对);仍无 → 停,报情况 + 换路径建议。
4. 出 PDF + 一次性提醒(不开网页)
这批 待提报 一次性出 PDF 到 待定/,对话提醒用户复核。默认不开 Chrome(占 profile 挡并行搜索)。
LIEPIN_PDF_DIR="<岗位>\待定" node SC/pdf.mjs "<id1>=张三-大疆" "<id2>=李四-影石"
node SC/lock.mjs release
告诉用户,并明确要四样(不能只问合适/不合适——理由是下一轮 JD 收敛的唯一燃料):
「本批 N 份已出 PDF 到 <岗位>\待定。请逐个告诉我:① 哪些合适、为什么合适(对口在哪:背景/公司/作品/某段经历);② 哪些不合适、为什么不合适(差在哪:方向偏/成色虚/硬约束)。理由越具体,下一轮搜得越准。」
- 用户只甩"这个行那个不行"没给理由时,追问为什么——步骤 6 的正/负向建议全靠它,缺了就是瞎搜。
- 只存本地、不外发;别只贴文字不出 PDF;仅用户要"在线看原页"才临时开 Chrome(看完关掉再跑 liepin)。
待定/ 留删:入选的 PDF 留下(候选人池直链,是成品);落选/候补的拍板后删;下批出新 PDF 前清掉上批未入选的。
5. 入池
用户复核拍板后:
- 合适 →
候选人池.md 加一行(姓名·现职·经验·薪资·学历·一句判断 + 两列链接 [PDF](待定/简历_<标签>.pdf)、[猎聘](https://lpt.liepin.com/cvview/showresumedetail?resIdEncode=<id>)),dedup.mjs set ... 入选,PDF 留 待定/。
- 不合适 →
dedup.mjs set ... 精筛不合适、删 PDF;留候补标 候补。
6. 迭代:进化下一轮的搜索关键词(本流程成败在此)
核心认知:每轮真正改变候选池的只有"搜索关键词"。 关键词决定"捞来谁"(池子),正/负向建议只决定"怎么筛"(过滤)。池子捞错了,再严的筛也救不回来。 所以别"随便换个词、再用更严的标准卡一轮"——要让上一轮的结果告诉你下一轮搜什么:把搜索词当假设、把结果当数据来修正。
A. 关键词进化的发动机:扒「入选」简历,反推下一轮的词。 逐个打开本批入选简历(必须真读、不能拍脑袋),从他们身上抽出真实可搜的词:
- 职位名变体 ← 他们简历上自称什么?同一岗常有多种叫法(动效设计 / 交互动效 / motion designer / UI动效 / H5动效)——都拿来当下一轮的并列(OR)词。
- 出身公司 ← 入选的人来自哪些公司?→ 开一条「目标公司」轴,直接搜这些公司及其对标/竞品/上下游的人(HR 公认最高效的一招)。
- 技术/工具/项目名词 ← 反复出现的工具(AE / Lottie / Figma…)、项目或产品词,都是锚点词。
B. 诊断本轮每个关键词,决定怎么调(一次只动一个轴,才看得出是哪步起效):
- 某词召回多但大多不对口(太宽/有歧义)→ 收紧:加硬约束、换更具体的职位变体、或加排除词。
- 某词召回太少 / 快枯竭(太窄)→ 放宽:上同义词/近义词、职位变体(如 品牌↔市场、外贸↔贸易),或扩到邻近行业(如 医美→生物科技/个护)。
- 某词连 ≥2 轮净入池 = 0 → 判死,移进弱搜索词,弃用,换锚点。
- 关键词轴轮换:① 职位名+变体/同义词 ② 目标公司(对标/竞品/上下游)③ 技术/工具名词 ④ 项目/产品名词 ⑤ 邻近行业(本行枯竭时)。
C. 追加一行到 JD 内部备注表(别改旧行,一轮一行):
- 强搜索词 ← 本轮有效词 + A 步从入选简历新抽出的词(职位变体/目标公司/锚点词)。
- 弱搜索词 ← 本轮无效/太宽/枯竭的词,写清为什么废,别下轮再踩。
- 正向建议 ← 用户说的"为什么合适" + 你真读入选简历的交叉印证(画像/背景/公司/技能锚点),落成下批筛人要找的信号。
- 负向建议 ← 用户说的"为什么不合适",归纳成排除信号(方向偏/成色虚/某类背景)。
- 每格要点即可,别散文、别重抄硬约束。
⚠️ 锁:步骤 4 已 release。下面 D(复判)和 E(新搜)要再调 liepin——动手前先 node SC/lock.mjs acquire <岗位> 重新占锁(占不到=别的岗在搜,排队)。
D. 回扫错杀(本轮标准/方向有调整时必做——否则越搜越少): 标准是动态的,上几轮按旧标准砍掉的人,换新标准可能是错杀。回去新搜之前,先按新正/负向建议复判已召回的库存,别只顾往前找新词:
node SC/dedup.mjs list "<岗位>\去重台账.csv" 候补 → 先复判候补(本就是边界,最易翻案)。
node SC/dedup.mjs list "<岗位>\去重台账.csv" 未精筛 → 库存里没细看过的,直接精筛(本就是该挖的库存)。
- 标准明显变向时(某硬约束放宽 / 新增了正向信号)→
node SC/dedup.mjs list "<岗位>\去重台账.csv" 精筛不合适 取近几轮的,挑可能受这次变化影响的重新 fetch.mjs + 复判。注意这要重拉简历、耗 liepin 调用,只挑相关的、别全表重刷。
- 复判翻案的 →
dedup.mjs set ... 待提报,并入本批。
错杀回收常比新搜更快补人,尤其方向一调。"越搜越少"多半就是只往前搜、不回头捡。
E. 用进化后的关键词回步骤 0 继续,到满额收尾。
收尾自检:召回全员登 CSV 了(脚本已保证)?待提报 都归档了?未精筛:精筛 还 ~4:1(dedup.mjs stats)?入选进候选人池了?JD 表追加本批行了(强搜索词含从入选简历新抽的词、正向建议含真读印证)?lock.mjs release 了?
脚本清单(SC = .claude/skills/pxb-liepin/scripts/)
| 脚本 | 用法 | 干什么 |
|---|
lock.mjs | acquire|renew|release <岗位> | 单账号搜索锁,30 分钟过期,退出码表态 |
search.mjs | <csv> "<词>" [--city --limit --experience --salary --degree] | 搜+去重+登记未精筛+打窄表 |
fetch.mjs | <id...> | 批量拉简历窄字段,内置反爬抖动 |
dedup.mjs | seen|add|set|list|stats <csv> [状态] [id...] | 台账查重/改状态/按状态列 id(回扫错杀)/比例自查 |
pdf.mjs | <id=标签...>(配 LIEPIN_PDF_DIR) | 整页存 PDF;npm root -g 动态定位 liepin-cli |
patch-headless.mjs | (装完跑一次,可选) | 把发布版 liepin-cli 默认改无头,免疫裸调用弹窗;幂等 |
liepin 命令本身细节见同目录 liepin-cli skill。环境/安装见仓库根 README.md。