无头 Chrome 自动填表投递:freehire auto-apply 如何做到“所有必答项都有答案才提交“ 无头 Chrome 自动填表投递freehire auto-apply 如何做到所有必答项都有答案才提交【免费下载链接】freehirefreehire — the open-source search engine for job seekers项目地址: https://gitcode.com/GitHub_Trending/fr/freehirefreehire 是一款开源的求职搜索引擎open-source job search engine它的 auto-apply 模块用无头 Chrome自动打开雇主招聘页、填写申请表并投递——但有一条铁律只有当每个必答项都存在一个候选人自己确认过的答案时才会点击提交缺任何一个答案整张表单原样搁置绝不猜测。本文完整解析这套自动填表投递机制的设计与实现。一、auto-apply 自动投递解决什么问题海投岗位时最消耗时间的不是写简历而是逐站重复填写申请表姓名、邮箱、电话、工作年限、签证情况……每个 ATSGreenhouse、Lever、Ashby 等的表单还都不一样。freehire 的方案是三个组件协作组件职责入口编排 worker定制简历 → 等待候选人审批 → 触发投递cmd/auto-apply-orchestrate/main.go投递 worker从队列认领任务驱动无头浏览器填表、提交cmd/auto-apply/main.go浏览器驱动层DOM 扫描、字段对齐、答案解析、填表提交internal/api/atsapply/队列表auto_apply_queue由迁移 migrations/0116_auto_apply_queue.sql 创建投递 worker 只消耗队列里已有的任务不负责产生任务。二、表单从哪里来DOM 扫描 平台 Schema 对齐提交前系统先回答一个问题这张表单上到底有哪些字段、哪些必填流程是captcha 短路检查 → 拉取平台官方 schema → 用无头 Chrome 渲染页面并扫描 DOM → 把两边**对齐reconcile**成一份合并字段清单。规则很有意思见 internal/api/atsapply/reconcile.go页面DOM决定存在什么API 声明了但页面上根本没渲染的字段会被丢弃——总不能往一个不存在的控件里写值必填是两边的并集曾有一个真实的 Greenhouse 岗位country字段在 HTML 里没有任何required属性靠 API 侧的必填标记才兜住平台识别靠实测而非猜测只有 internal/api/atsapply/layout.go 里逐字段实测过页面结构的平台目前是 Greenhouse、Lever才会被真正驱动其他平台解析得再完整也会以未实现为由搁置。设计文档特别强调定位表单、找提交按钮这类判断是从真实页面上量出来的不是推出来的——因为提交按钮一旦被误点无法撤回。三、核心机制必答项全有答案才允许点提交解析阶段会把每个合并字段对照候选人的已知答案逐一把关internal/api/atsapply/resolve.go必答项无答案→ 记入Unmapped清单字段 ID、标签、原因选填项无答案→ 直接跳过既不填写也不报错下拉/单选→ 答案必须精确匹配平台自己给出的某个选项否则视为无答案。然后是整个门禁的关键判定Plan.FullyResolved() (Unmapped 为空)只有FullyResolved()为真worker 才会真的去填表、点提交否则本次尝试直接返回StatusParked搁置雇主页面上的表单一个字符都不会被动过。搁置不是失败而是缺数据worker 会把阻塞原因和逐题明细落库候选人界面会明确显示是哪几道题挡住了投递状态为blocked见 internal/application/autoapply/status.go。补上答案后即可重新进入流程——这与技术性失败、值得重试是完全不同的语义。四、答案从哪里来宁缺毋滥的答案来源worker 通过 cmd/auto-apply/answers.go 调用统一的答案装配器 internal/api/candidateprofile/profile.go按固定优先级汇集候选人已经说过的每句话基础简历 CV候选人亲笔编辑的版本结构化简历从上传的简历文件中解析账户信息邮箱兜底自填筛查答案期望薪资、签证担保、可到岗时间等答案库 answer bank候选人在过往申请表里答过的同类问题。在此之上还有两个安全阀敏感问题闸门internal/api/atsapply/sensitive.go薪酬、工作许可/签证、EEO 人口统计类问题永远不交给模型代答无论模型多自信地理位置问题internal/api/atsapply/geography.go候选人档案只有一段自由文本地址无法验证对应某个下拉选项因此在代答之前就搁置而不是指望碰巧没匹配上。最后才是受控的 LLM 代答internal/api/atsapply/draft.go仅限必答、非敏感、前两路都没覆盖的自定义问题单次调用、只基于候选人亲口陈述的经历素材产出的答案还要再和选项列表对一次。模型没配置时所有字段要么确定性解析、要么搁置——能力可以缺席猜测永远不行。五、提交之后四种结局与绝不投两次提交动作的每个结果都被显式建模internal/application/autoapply/runner.go结局含义后续处理applied真实投递成功同一事务内记录投递 注销队列行原子完成parked必答项未全有答案不重试等候选人补数据unconfirmed点了提交但既没看到确认也没看到拒绝立即死信交人工核查captcha_refused平台明确说没有创建申请可安全重试独立计数器上限 20 次两处设计尤其严谨记录失败不重试如果浏览器已经真提交了、但本地落库失败走普通重试意味着第二次真实投递——所以该路径强制死信宁可报 CRITICAL 日志也要人来看确认是严格标记提交后只在页面上找确切的确认/拒绝文本匹配不到一律视为未确认绝不从叙述性文字里推断成功。无头浏览器本身用的是 Go 原生 chromedp 驱动真实 Chrome而非 Python 侧车配合disable-blink-featuresAutomationControlled一个启动参数即可通过主要自动化检测信号选型依据记录在 openspec/changes/auto-apply-worker/design.md。对 chromedp 无法覆盖的 Ashby/Workable还有一条默认关闭的 browser-use 云端兜底见 internal/api/atsapply/AGENTS.md。六、小结freehire auto-apply 的只在全员必答项都有答案时才提交并非一句口号而是一整条可验证的防线表单字段来自DOM × API 的并集对齐不猜控件答案来自候选人已确认的事实来源敏感问题与地理位置问题先行拦截FullyResolved()门禁保证缺一个答案就整体搁置并逐题告知候选人提交结果四态分治 原子落库保证同一份申请永远不会被投两次。对求职自动化而言少投错的一次比多投对的一次更有价值——这正是这套机制最可取的地方。【免费下载链接】freehirefreehire — the open-source search engine for job seekers项目地址: https://gitcode.com/GitHub_Trending/fr/freehire创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考