项目背景:从手动海投到 AI 全自动投递
主流招聘平台 日沟通上限 150 次,而人工投递面临响应延迟、消息模板化、跨城搜索难、反爬封号四大核心难题。客户是一位拥有 10+ 年经验的独立全栈开发者(主营远程办公与兼职外包),此前使用手动搜索 + 复制粘贴的方式投递,历经 4 次账号封禁后,面临以下痛点:
- 投递效率低:手动搜索-点开-阅读-复制消息-发送,单条耗时 3-5 分钟,一天极限只能投 30-50 份,日额度浪费达 70%
- 消息模板化:千篇一律的开场白被招聘方一眼识破,回复率不足 5%
- 跨城搜索难:目标覆盖中西部 30+ 个非一线城市,人工逐城切换,体力透支
- 反爬封号频发:曾因 15 分钟跨 9 省狂搜 513 次触发账号级封禁 24 小时;又在连续高强度采集后经历 3 次封号,封禁时长递增(2h -> 7.5h -> 更久)
- 精准筛选缺失:大量投递命中 Java 方向、大厂外包、大型项目分模块开发、招聘者多日不活跃等不匹配岗位,无效沟通浪费每日额度
- 重复投递:采集、审核、投递记录散落各处,无法去重,重复发消息给同一招聘者屡有发生
解决思路:让 AI 承担所有采集、审核与执行,人只负责技能画像与审核标准。AI 不知疲倦、消息千变万化,可以同时覆盖十余个关键词 × 160 个城市,让求职投递摆脱”人”的瓶颈,回归”系统”的本质。
“最崩溃的一次是连续搜了 15 分钟,9 个省份 513 次请求,账号直接被封 24 小时。那天本可以投 100 份的,结果一份都没投出去。”–客户张先生
解决方案:四大 Agent 协同的智能投递闭环
不是单一脚本包打天下,而是按”采集 -> 审核 -> 投递 -> 风控”全链路拆分为 4 个独立 Agent,每个 Agent 职责单一、解耦清晰、可独立升级。核心设计是三阶段分离–采集慢、投递快、中间 AI 审核离线–彻底根除”跨区域短时间大量采集”这一封号主因。
| Agent | 职责 | 核心技术 |
|---|---|---|
| 采集 Agent | 一城多词搜索,card.json 预过滤活跃度与岗位描述,剔除不匹配岗位 | CDP session 隔离、securityId 透传、拟人化限速 |
| 审核 Agent | 逐条六维度评估岗位匹配度,动态生成非模板消息,100% 离线零网络 | 大模型推理、postDescription 解析、7 类岗位子型措辞轮换 |
| 投递 Agent | CJK 安全输入消息,弹窗拦截,重复防护,每日额度守护 | Input.insertText、会话标记检测、encryptJobId 去重 |
| 风控 Agent | CDP 反检测、Token 临时 Tab 刷新、刷新计数器、地域纪律、采集硬阈值 | Target.attachToTarget(flatten=True)、跨 Tab cookie 共享 |
系统架构:三阶段分离的工程级设计
系统采用三阶段分离架构:采集阶段(慢)-> 审核阶段(离线)-> 投递阶段(快),层层解耦,采集的风控风险绝不传导到投递环节。
| 层级 | 技术栈 | 核心组件 |
|---|---|---|
| 浏览器控制层 | Chrome DevTools Protocol (CDP) + browser-harness | Target.createTarget 建独立 Tab、attachToTarget(flatten=True) 获取 session_id、所有 Runtime.evaluate/Page.navigate/Input.insertText 均带 session_id |
| 数据管线层 | Python + JSONL 三阶段数据流 | collected.jsonl(采集候选)-> approved.jsonl(审核通过+定制消息)-> applied.jsonl(已投递去重源) |
| AI 推理层 | 大模型 + 动态消息生成 | 六维度岗位评估、7 类岗位子型 × 2-3 套措辞轮换、远程/兼职/外包意向智能包装 |
| 风控防护层 | 反检测 + Token 刷新 + 硬阈值 | session 隔离规避 about:blank 重定向、临时 Tab 刷新 __zp_stoken__、刷新计数器 10 次即停 |
| 部署运行层 | 本地 Chrome + Python 脚本 | –remote-debugging-port=9222、RDP 断开 tscon 保活、统一数据目录 |
技术栈全景:从浏览器底层到 AI 推理
| 层级 | 技术选型 | 核心能力 |
|---|---|---|
| 浏览器层 | Chrome CDP + browser-harness | 全程不依赖 UI 自动化高层封装,直接操作底层协议;session 隔离稳定绕过反自动化检测 |
| 反检测层 | Target.attachToTarget(flatten=True) | 创建独立 session,所有 JS 执行带显式 session_id,规避默认会话检测;不注入任何反检测脚本(避免触发额外检测) |
| Token 层 | 临时 Tab navigate + 跨 Tab cookie 共享 | 纯 fetch 无法刷新 __zp_stoken__(不执行 JS);临时 Tab 加载页面执行 JS 重新生成 token cookie,cookie 浏览器层跨 Tab 共享而主会话不重载 |
| 数据层 | JSONL 三阶段数据流 | collected / approved / applied 三文件分离,append-only 可追溯,encryptJobId 全局去重 |
| API 透传层 | 搜索/卡片接口 JS 封装完整透传 | code=32(封禁)/code=35(IP 验证)/code=37(token 过期)分类处理,杜绝风控信息被静默吞掉 |
| 限速层 | 拟人化随机间隔 | 搜索间 4-9 秒随机,每 15-20 次请求插入 60-120 秒长休息,模拟真人离开 |
| 部署层 | 本地 Chrome + Python + tscon | –remote-debugging-port=9222 启动,RDP 断开用 tscon 切换 console 会话保活,适配远程桌面办公 |
三阶段数据管线:采集、审核、投递分离
三阶段脚本各司其职,数据在 JSONL 文件间流转,每一步都可中断、可重跑、可审计。
| 阶段 | 脚本 | 数据文件 | 职责 |
|---|---|---|---|
| 阶段一 采集 | collect_jobs.py | collected.jsonl | 一城多词搜索,获取 securityId 后调卡片接口,前置拉取活跃度与岗位描述,剔除不匹配岗位 |
| 阶段二 审核 | review_jobs.py | approved.jsonl | 读取 collected.jsonl,100% 离线 AI 逐条审核,通过则生成定制消息写入 approved.jsonl |
| 阶段三 投递 | apply_approved.py | applied.jsonl | 读取 approved.jsonl 逐条投递,成功写入 applied.jsonl 作为全局去重源 |
| 辅助 | dump_review.py | – | 把 collected.jsonl 转成可读列表,便于人工抽检 |
| 辅助 | gen_report.py | – | 生成投递清单 HTML 报告 |
| 辅助 | preflight.py | – | 启动前预检账号是否受限 |
采集阶段过滤引擎(前置剔除,避免浪费审核算力):
- 排除销售/客服/行政等非技术岗
- 排除 Java 方向(技能栈不匹配)
- 排除大厂与大型外包公司(腾讯/阿里/字节/中软国际/软通动力等 50+ 家)
- 排除大型项目分模块开发(架构师/中台/微服务,偏好中小型项目独立全栈)
- 排除 100 人以上公司
- 排除招聘者超过 3 天未活跃的岗位(activeTimeDesc 判断)
风控体系:CDP 隔离 + Token 刷新 + 硬阈值
“先不被封,再谈投递效率”–所有操作都附带多层风控保护。这套体系是用 4 次真实封禁换来的。
CDP Session 隔离(反检测核心)
招聘平台会检测默认 CDP 会话的 Runtime.evaluate 调用,在 2-3 秒内重定向到 about:blank 中断操作。解决方案:
| 步骤 | 操作 | 作用 |
|---|---|---|
| 1 | Target.createTarget 创建独立 Tab | 脱离默认会话 |
| 2 | Target.attachToTarget(flatten=True) 获取 session_id | 拿到隔离会话句柄 |
| 3 | 所有 Runtime.evaluate / Page.navigate / Input.insertText 均带 session_id | 规避默认会话检测 |
| 4 | 不注入任何反检测脚本 | 避免触发额外检测特征 |
Token 临时 Tab 刷新机制
| 问题 | 纯 fetch 请求无法刷新 __zp_stoken__(不执行 JS),token 过期后请求全部失败 |
|---|---|
| 错误尝试 | 直接 fetch 刷新 -> token 不更新 |
| 最终方案 | 创建临时 Tab navigate 加载页面执行 JS 重新生成 token cookie |
| 关键点 | cookie 在浏览器层跨 Tab 共享,而主会话 session 不变–这是”既刷新 token 又保持 CDP 会话”的唯一正确解 |
Token 刷新计数器(风控晴雨表)
实测发现 token 刷新频率是账号风控状态的最准指标–第 4 次封禁时 50 分钟内 token 刷新高达 49 次(平均每分钟 1 次)。
| 指标 | 数值 |
|---|---|
| 会话内累计刷新阈值 | 10 次即强制停止 |
| 实测封禁触发点 | 49 次 / 50 分钟 |
| 安全裕度 | 远早于封禁触发点 |
地域纪律 + 采集硬阈值
| 维度 | 规则 | 依据 |
|---|---|---|
| 地域纪律 | 单次运行锁定一个城市圈,跨省分多天进行 | 单区单日 150 次沟通 = 安全;跨 9 省 15 分钟狂搜 = 24h 封禁 |
| 单会话采集上限 | 400 条(约 550 API 请求) | 留足安全裕度 |
| 单日总量上限 | 600 条 | 防止日累积触发风控 |
| 空转停止 | 连续 3 轮无新增自动停止 | 避免无效请求 |
| 会话时长 | ≤ 4 小时 | 控制单次暴露时长 |
风控指标总览
| 风控指标 | 数值 |
|---|---|
| Token 刷新监控 | 会话内累计 10 次即停 |
| 搜索间隔 | 4-9 秒随机 |
| 长休息间隔 | 每 15-20 次请求 60-120 秒 |
| 单日沟通上限守护 | 触达 150 次立即停止 |
| 误封率(部署后) | 0 |
数据设计:三阶段 JSONL 全审计
每一批采集、每一条审核结果、每一次投递,全部持久化、可追溯、可去重。
| 分类 | 文件 | 内容 | 作用 |
|---|---|---|---|
| 采集候选 | collected.jsonl | card 全部信息(securityId/encryptJobId/jobName/postDescription/skills/activeTimeDesc/公司规模) | 审核输入源 |
| 审核通过 | approved.jsonl | 审核通过岗位 + 定制化消息文本 | 投递输入源 |
| 已投递 | applied.jsonl | 已投递记录(encryptJobId + 时间戳) | 全局去重源,避免重复发送 |
去重三重防护:
| 层级 | 机制 | 触发点 |
|---|---|---|
| 采集阶段 | 读取 friendStatus 跳过已建立好友关系 | 采集时 |
| 投递阶段 | 检查会话内”送达/已读”标记 | 投递前 |
| 全局阶段 | applied.jsonl 按 encryptJobId 比对 | 投递前 |
三层防护下重复发送率为 0。
消息生成:动态非模板的千人千面
拒绝固定模板,每条消息基于岗位实际需求(postDescription)推理生成,杜绝千篇一律。
| 岗位类型 | 消息策略 |
|---|---|
| 常规技术岗 | 从 postDescription 提取 1-2 项最贴合的技能亮点,主动询问远程/兼职/外包意向 |
| 远程包装 | 将远程需求包装为”降本增效”的双赢方案,强调双方收益 |
| 爬虫/逆向类 | 7 类子型 × 2-3 套措辞轮换,避免被识别为批量发送 |
| 技能不匹配 | 审核阶段直接剔除,不生成消息 |
审核维度(六维评估):技能匹配 / 薪资合理性 / 公司规模 / 远程意向 / 项目类型(中小型独立全栈 vs 大型分模块)/ 活跃度。每条排除都注明理由,保留率与排除率汇总可见。
城市覆盖:160 个已验证城市代码
| 范围 | 说明 |
|---|---|
| 城市代码库 | 160 个城市代码,全部经 API 实测验证(如武汉 = 101200100,而非错误的 101200500) |
| 重点覆盖 | 中西部非一线省会(武汉/长沙/成都/重庆/西安/昆明/贵阳)及周边卫星城市 |
| 适配策略 | “避开一线城市内卷、主攻中西部机会多”的远程求职策略 |
| 跨城纪律 | 每日一城圈,避免跨省爆发式采集触发风控 |
部署与运维:本地化零成本运行
- 最低要求:本地 Chrome + Python 运行时,零云服务成本
- Chrome 启动:–remote-debugging-port=9222 –user-data-dir=.chrome-debug
- 统一数据目录:合并两台电脑数据后的唯一数据目录
- browser-harness:Python 运行时容器,统一管理 CDP 连接
- RDP 保活:远程桌面断开用 tscon 切换 console 会话,脚本继续运行
- 预检机制:preflight.py 启动前检查账号是否受限
- 日志可追溯:三阶段 JSONL 全审计,任何一次投递可精确归因
开发历程:从 4 次封号到 0 误封
| 版本 | 阶段 | 核心变化 |
|---|---|---|
| v1.0 | 起步 | 手动投递,单城单关键词,日投 30-50 份,回复率不足 5% |
| v1.5 | 扩展 | 多城市搜索 -> 15 分钟跨 9 省 513 次请求 -> 第 1 次封禁 24h |
| v2.0-alpha | CDP 接入 | 引入 CDP 自动化 -> 默认会话触发 about:blank 重定向 -> 改用 session 隔离 |
| v2.0-beta | 风控 | Token 刷新 49 次 / 50 分钟 -> 第 4 次封禁 -> 引入刷新计数器 + 地域纪律 + 硬阈值 |
| v2.0-stable | 当前版本 | 三阶段分离 + 四 Agent 协同 + 160 城代码库 + 0 误封稳定运行,累计投递 116 条 |
开发者复盘
“从手动海投到 AI 全自动投递,最大的变化不是’投得快’,而是投得准还不出事。以前手动海投一天累死累活 40 份,回复两三个;现在 AI 一天投满 150 份,回复二十多个,而且再也没有被封过号。最让我惊喜的是消息生成–AI 会根据每个岗位的实际需求写不同的开场白,有次招聘方直接回我’你的消息看起来很懂这个项目’,那是个 WordPress 站群岗位,AI 准确抓到了站群采集和 SEO 的卖点。”
— 张先生 · 独立全栈开发者(武汉,10+ 年经验,主营远程办公与兼职外包)
四点核心思考
- 风控优先于效率:4 次封禁教会我一件事–再快的投递,封号一天全归零。Token 刷新计数器 10 次即停,看似保守,却是稳定运行的基石
- 采集与投递必须分离:把慢速采集和快速投递拆成两个阶段,中间用离线 AI 审核缓冲,彻底切断了”跨区域短时间大量采集”这个封号主因
- 消息必须千人千面:模板消息回复率 5%,动态消息回复率 18%。AI 根据 postDescription 写不同开场白,是回复率提升的核心
- 数据全审计才有底气:三阶段 JSONL 让每一条投递可追溯,去重三重防护让重复率为 0。没有这层底座,效率越高翻车越快
落地成果
- 投递效率提升 15 倍:从人工 30-50 份/天到 AI 150 份/天(触达平台上限)
- 回复率从 5% 提升到 18%(动态消息 + 六维精准匹配双驱动)
- 累计采集 183 条高质量候选,审核通过 87 条,成功投递 116 条(0 重复)
- 反爬绕过成功率 100%:CDP session 隔离后未再触发 about:blank 重定向
- 风控误封率降至 0:4 次封禁经验沉淀为硬阈值
- 活跃度过滤命中:招聘者超 3 天未活跃的岗位 100% 前置剔除
- 每日额度利用率从 30% 提升到 100%
武汉本地服务
我们在武汉设有技术服务点,服务华中地区自由开发者与远程办公团队。求职投递场景对时效性要求高(岗位发布后黄金 24 小时内触达),我们提供 7×24 小时技术保障。武汉及周边城市(长沙、南昌、合肥等)客户可享受工程师远程协助调试,Chrome 环境配置与账号风控状态诊断 2 小时内响应。
常见问题
系统会被招聘平台检测封号吗?
不会。系统采用 CDP Session 隔离技术–所有浏览器操作通过独立会话的 Target.attachToTarget(flatten=True) 执行,规避了平台对默认会话 Runtime.evaluate 调用的检测。同时内置 Token 刷新计数器(10 次即停)、地域纪律(单次锁定一个城市圈)、拟人化限速(4-9 秒随机间隔 + 周期性长休息)三重防护。客户历经 4 次封禁后沉淀的硬阈值,已实现部署后 0 误封稳定运行。
系统如何保证投递的岗位都是匹配的?
三重过滤机制:采集阶段自动排除非技术岗、Java 方向、大厂外包、100 人以上公司、招聘者超 3 天不活跃的岗位;审核阶段 AI 逐条阅读岗位描述,按技能匹配、公司规模、远程意向、项目类型(中小型独立全栈 vs 大型分模块)六维度评估,每条排除都注明理由;投递阶段动态生成贴合岗位需求的消息,杜绝模板化。最终审核通过率约 47%(87/183),确保每一条投递都精准。
为什么把采集和投递拆成两个阶段?
这是用 4 次封禁换来的核心设计。封号主因是”跨区域短时间大量采集”–单次脚本既采集又投递时,为了投递效率会加快采集节奏,触发风控。拆成两阶段后:采集慢速拟人化运行(4-9 秒间隔 + 长休息),投递快速执行(已有审核数据),中间用离线 AI 审核缓冲。采集的风控风险完全不传导到投递环节,这是 0 误封的关键。
Token 刷新计数器为什么设在 10 次?
实测数据:第 4 次封禁时 50 分钟内 Token 刷新高达 49 次(平均每分钟 1 次)。10 次的阈值远早于封禁触发点,留足安全裕度。这个数字不是拍脑袋–它是”账号从健康到被风控”最灵敏的领先指标,比请求频率、采集量都准。阈值演进经历了 25 次 -> 15 次 -> 10 次的收敛过程,越用越保守越稳定。
如果招聘方已经联系过了,系统会重复发消息吗?
不会。系统有三层去重:采集阶段读取 friendStatus 字段跳过已建立好友关系的岗位;投递阶段检查会话内是否已有”送达/已读”标记;全局 applied.jsonl 按 encryptJobId 记录所有已投递岗位,每次投递前读取比对。三层防护下重复发送率为 0。
系统支持哪些城市?
覆盖全国 160 个已验证城市代码(均经 API 实测确认,如武汉 = 101200100 而非错误的 101200500)。重点覆盖中西部非一线省会城市及其周边,适配远程求职者”避开一线城市内卷、主攻中西部机会”的策略。跨城市拓展采用”每日一城圈”策略,避免跨省爆发式采集触发风控。
能对接我自己的技能背景吗?
可以。审核 Agent 基于可配置的用户画像运行–技能栈、求职方向(远程/兼职/外包)、公司规模偏好、地域偏好、排除规则全部参数化。更换求职者只需更新画像配置,无需改动代码。消息生成也会根据新画像自动调整技能亮点与措辞。