BossAssistant 项目架构:面向 BOSS 直聘的半自动求职助手
BossAssistant 是我26年7月初做的一个浏览器插件项目。
它的目标不是替用户完全自动求职,而是把 BOSS 直聘上高频、重复、容易漏记的动作变得更可控:
职位列表扫描↓关键词 / 城市 / HR 活跃状态筛选↓半自动打开岗位并沟通↓本地记录投递状态↓避免重复投递↓聊天页识别 HR 问题↓生成本地回复草稿我对 BossAssistant 的定位是:
一个面向个人求职场景的半自动化浏览器助手,让重复操作可控、可追踪、可暂停,同时把最终决策权留给用户。
一、为什么要做这个项目
Section titled “一、为什么要做这个项目”集中找工作时,很多时间并不是花在真正判断岗位是否合适,而是花在重复操作上。
例如:
打开职位列表↓逐个看岗位↓判断城市、关键词、薪资、HR 活跃状态↓点击立即沟通↓复制自我介绍↓记录哪些岗位已经投过↓再去聊天页组织回复这些动作本身并不复杂,但数量一多就很容易出现问题:
重复投递漏记岗位忘记某个 HR 是否沟通过复制话术很耗时间投递过程无法复盘聊天回复临时组织不稳定所以我想做一个工具,把这些机械动作集中到一个页面内控制台里完成。
但这个项目一开始就没有设计成“全自动投递器”。
我更希望它保持几个边界:
用户配置规则用户决定是否开始用户可以暂停、继续、停止回复只生成草稿最终发送仍然由用户确认不绕过验证码、登录、风控或平台限制因此,BossAssistant 的核心不是“自动化越强越好”,而是:
在不丢失控制权的前提下,减少重复劳动。
二、BossAssistant 总体架构图
Section titled “二、BossAssistant 总体架构图”
整体架构可以概括为:
Chrome Extension MV3↓Popup / Options / Background / Content Script↓页面内控制面板↓职位扫描与筛选↓半自动沟通流程↓本地投递记录↓聊天页回复草稿其中最重要的是 Content Script。
因为它真正运行在 BOSS 直聘页面里,负责:
识别当前页面注入控制面板扫描岗位卡片读取岗位信息执行受控点击记录运行日志识别聊天消息生成回复草稿Background Service Worker 更像消息中转层,负责在 Popup、Options 和 Content Script 之间转发配置、页面状态和任务状态。
三、技术栈选择
Section titled “三、技术栈选择”BossAssistant 当前使用的技术栈如下:
| 分类 | 技术 |
|---|---|
| 扩展标准 | Chrome Extension Manifest V3 |
| 前端框架 | React 19 |
| 开发语言 | TypeScript 5.8 |
| 构建工具 | Vite 7 + esbuild |
| 图标库 | lucide-react |
| 本地存储 | chrome.storage.local |
| 目标浏览器 | Chrome 120+ |
选择 Chrome Extension MV3 的原因很直接:这是目前 Chrome 扩展的主流标准。
React 和 TypeScript 主要负责 Popup、Options 设置页和页面内控制面板的开发。
Vite 用来构建 React 页面,esbuild 用来单独打包 Content Script 和 Background。
这个地方有一个比较关键的工程细节:
Popup / Options 可以走 ViteContent Script 需要打成 IIFEBackground Service Worker 需要打成 ESM所以项目采用了双构建管线:
React Popup / Options ── Vite ──> dist/assets/*.js
Content Script ─────── esbuild ──> dist/assets/content.js (IIFE)
Background SW ──────── esbuild ──> dist/assets/background.js (ESM)对应的构建命令是:
npm.cmd run build构建完成后,加载 dist/ 目录即可在 Chrome 中调试扩展。
四、项目整体运行流程
Section titled “四、项目整体运行流程”BossAssistant 的运行流程可以分成两个场景。
第一个场景是职位列表页:
用户进入 BOSS 职位列表页↓Content Script 检测页面类型↓注入 BossAssistant 控制面板↓读取用户配置↓扫描当前页面岗位卡片↓按关键词、城市、HR 活跃状态筛选↓跳过已沟通过或已记录岗位↓依次打开匹配岗位详情↓点击立即沟通↓记录成功、跳过或失败原因
第二个场景是沟通聊天页:
用户进入 BOSS 沟通聊天页↓识别聊天消息区域↓提取 HR 消息↓判断问题类型↓匹配简历资料↓生成回复草稿↓用户选择复制或填入输入框↓用户确认后手动发送
这两个场景共用一套配置、本地存储、任务状态和日志系统。
五、用户配置层:Options 设置页
Section titled “五、用户配置层:Options 设置页”Options 设置页是整个工具的配置中心。

目前支持配置:
自我介绍职位关键词目标城市HR 活跃时间每日投递上限简历资料回复风格其中自我介绍支持多行配置。
例如:
您好,我对这个岗位很感兴趣我是计算机专业学生,熟悉 Java、Spring Boot、MySQL做过浏览器插件、AI 实验和 Web 项目希望能有机会进一步沟通在半自动沟通时,系统会按行拆分这些内容。
这样比写成一整段更接近真实沟通场景。
配置数据统一抽象为:
export type AssistantConfig = { introText: string; keywords: string; cities: string; hrActiveFilter: HrActiveFilter; dailyLimit: number; resumeText: string; replyTone: ReplyTone;};配置保存到浏览器本地:
chrome.storage.local这也是项目里“本地优先”设计的一部分。
简历资料、投递记录、回复草稿相关配置都不上传到外部服务。
六、页面识别层:判断当前能做什么
Section titled “六、页面识别层:判断当前能做什么”BossAssistant 不是所有页面都执行功能。
它主要支持两个页面:
| 页面 | URL | 功能 |
|---|---|---|
| 职位列表页 | /web/geek/jobs | 岗位扫描、筛选、半自动沟通、投递记录 |
| 沟通聊天页 | /web/geek/chat | HR 消息识别、回复草稿生成、复制或填入 |
页面识别逻辑可以概括为:
if (/\/web\/geek\/jobs/.test(href)) { return { kind: "jobs", label: "职位列表", supported: true };}
if (/\/web\/geek\/chat/.test(href)) { return { kind: "chat", label: "沟通聊天", supported: true };}
if (/zhipin\.com/.test(href)) { return { kind: "zhipin-other", label: "其他页面", supported: false };}这样做的好处是:
职位列表页只展示投递控制聊天页只展示回复助手其他页面不注入无关功能同时,Content Script 会定期检测 URL 是否变化。
因为 BOSS 这类网站经常是前端路由切换,页面地址变化不一定会重新加载整个网页。
七、职位扫描与筛选层
Section titled “七、职位扫描与筛选层”职位扫描是 BossAssistant 的核心能力之一。
它需要从第三方页面 DOM 中识别岗位卡片,并提取结构化信息。
每个岗位卡片会被整理成类似这样的数据:
export type JobCardInfo = { element: HTMLElement; id: string; title: string; company: string; location: string; salary: string; hrActiveText: string; hrActiveLevel: HrActiveLevel; detailUrl: string; rawText: string;};1. 卡片识别
Section titled “1. 卡片识别”项目里没有只依赖一个固定选择器,而是使用多组候选选择器:
.job-card-wrapper.job-card-box.job-card-body.job-primaryli[class*='job-card']div[class*='job-card']li[class*='job-list']然后再通过内容判断它是否真的像岗位卡片。
判断依据包括:
是否包含 /job_detail/ 链接是否包含薪资文本是否包含职位关键词是否排除广告、推广、页脚元素这样做是为了提高兼容性。
因为第三方页面结构随时可能调整,如果只写死一个选择器,很容易失效。
2. 字段提取
Section titled “2. 字段提取”提取字段时也不是只写一个选择器,而是准备多组候选。
例如职位名称可能来自:
.job-name.job-title.job-info .name[class*='job-name'][class*='job-title']a[href*='/job_detail/']公司名称可能来自:
.company-name.company-text.company-info a[class*='brand-name']a[href*='/gongsi/']h3[class*='name']如果选择器没有命中,还会使用文本兜底规则。
这类项目最麻烦的地方并不是写 UI,而是和真实网页 DOM 打交道。
页面结构稍微变化,就可能导致:
职位名识别不到公司名识别错误地点被误判按钮找不到聊天消息重复提取所以扫描层必须尽量做成“多策略 + 可降级”。
八、条件筛选:让跳过原因可解释
Section titled “八、条件筛选:让跳过原因可解释”扫描到岗位后,BossAssistant 会根据用户配置进行筛选。
当前主要筛选条件包括:
职位关键词目标城市HR 活跃状态筛选逻辑不是简单返回 true 或 false,而是会记录原因。
例如:
职位不包含关键词:React、Vue地点不匹配:期望 杭州,实际 北京HR 活跃度不匹配:半月内活跃这样设计的原因是:
自动化工具最怕“它替你做了决定,但你不知道为什么”。
所以在页面内控制台里,不仅要展示哪些岗位匹配,也要展示哪些岗位被跳过,以及为什么被跳过。
这让整个流程更容易复盘。
九、半自动沟通流程
Section titled “九、半自动沟通流程”BossAssistant 的投递流程不是一口气乱点页面,而是带状态控制的受控流程。
整体过程可以概括为:
用户点击开始↓扫描当前页面岗位↓筛选候选岗位↓检查本地记录是否已投递↓检查今日新增投递上限↓滚动岗位卡片到视野↓点击岗位卡片↓等待详情面板渲染↓查找“立即沟通”或“继续沟通”↓点击立即沟通↓处理平台提醒弹窗↓记录结果↓继续下一个岗位如果页面显示“继续沟通”或“已沟通”,说明这个岗位之前已经处理过。
这类岗位不会重复发送,而是补记为:
已沟通如果点击失败、按钮未找到、页面跳走,也不会让整个任务卡死,而是记录失败原因并继续处理后续岗位。
十、任务控制:开始、暂停、继续、停止
Section titled “十、任务控制:开始、暂停、继续、停止”BossAssistant 的页面内控制台支持:
开始暂停继续停止这点非常重要。
因为浏览器插件直接操作真实网页,如果没有暂停和停止机制,用户会很没有安全感。
任务运行过程中会维护一个运行状态:
type RunControl = { id: string; paused: boolean; stopRequested: boolean;};每处理一个岗位前后,都会检查:
用户是否暂停用户是否停止当前页面是否仍在职位列表页今日上限是否已经达到如果用户点击暂停,任务不会继续点击新岗位。
如果用户点击停止,当前运行会终止。
如果页面离开职位列表页,也会立刻停止,避免误操作。
十一、每日上限与防重复机制
Section titled “十一、每日上限与防重复机制”BossAssistant 不是无限制运行。
它会根据用户配置的每日上限控制新增投递数量。
例如用户设置:
每日上限:50启动任务时会先读取今天已经成功投递的数量:
remaining = dailyLimit - todayCount每成功新增一次沟通:
remaining -= 1如果到达上限,任务自动停止。
防重复主要有三层。
第一层是本地记录。
系统会基于岗位详情链接生成 key:
export function createJobKey(job: Pick<JobCardInfo, "id" | "title" | "company" | "location" | "detailUrl">): string { const source = normalizeDetailUrl(job.detailUrl) || normalizeDetailUrl(job.id) || `${job.title}-${job.company}-${job.location}`; return source.trim().toLowerCase();}第二层是本轮运行内去重。
因为页面滚动加载后,旧岗位可能再次出现在扫描结果里,所以运行中会维护:
seenIds第三层是页面状态判断。
如果岗位详情页出现“继续沟通”或“已沟通”,即使本地没有记录,也会补记并跳过。
这三层结合起来,可以减少重复沟通和重复记录问题。
十二、投递记录:让求职过程可追踪
Section titled “十二、投递记录:让求职过程可追踪”
投递记录保存在本地 chrome.storage.local 中。
每条记录包含:
export type ApplicationRecord = { id: string; jobId: string; title: string; company: string; location: string; detailUrl: string; hrActiveText: string; status: ApplicationStatus; reason: string; appliedAt: string;};状态包括:
successcommunicatedskippedfailed在设置页中,可以查看:
新投递数量已沟通数量失败记录跳过原因按日期筛选CSV 导出自动刷新这让 BossAssistant 不只是一个点击工具,也承担了求职记录管理的作用。
我觉得这一点很关键。
因为求职过程本身就是一个需要长期跟踪的过程:
今天投了哪些岗位哪些已经沟通过哪些失败了哪些不匹配后续要不要复盘关键词如果这些信息只停留在页面上,很快就会丢失。
十三、聊天页回复助手
Section titled “十三、聊天页回复助手”除了职位列表页,BossAssistant 还支持 BOSS 沟通聊天页。
它会尝试识别 HR 发来的消息,并生成本地回复草稿。
回复流程是:
进入聊天页↓识别聊天消息区域↓排除系统消息、用户自己的消息、输入框提示↓提取 HR 消息↓判断问题类型↓匹配简历资料↓生成回复草稿↓用户复制或填入输入框↓用户确认后手动发送目前支持识别的问题类型包括:
薪资期望到岗时间面试安排实习周期工作时间求职状态岗位意向地点意向学历专业作品资料项目经验技术能力自我介绍通用问题这里的“AI 回复”目前更准确地说是本地规则生成。
它不会调用外部大模型 API,也不会上传简历和聊天内容。
我这样设计主要是为了先保证隐私和稳定性。
后续如果接入 LLM,也应该默认保留人工确认,不应该自动代发。
十四、本地优先与安全边界
Section titled “十四、本地优先与安全边界”BossAssistant 处理的是求职相关信息,所以隐私边界非常重要。
当前项目坚持几个原则:
配置保存在本地投递记录保存在本地简历资料保存在本地聊天内容不上传回复草稿不自动发送不绕过登录、验证码、风控或平台限制这类项目很容易被误解成“自动化刷投递”。
但我希望它的定位更清晰:
它是个人效率工具,不是绕过平台规则的工具。
所以项目里保留了每日上限、暂停机制、日志记录和用户确认。
尤其是回复助手部分,只提供:
复制填入输入框最终是否发送,仍然由用户自己判断。
十五、页面内控制面板
Section titled “十五、页面内控制面板”BossAssistant 会在支持的 BOSS 页面内注入一个控制面板。
控制面板主要展示:
当前页面类型配置摘要任务状态处理数量成功数量跳过数量失败数量操作日志岗位处理结果回复草稿控制面板使用 Shadow DOM 注入。
这样可以尽量避免和 BOSS 页面原有样式相互影响。
页面内控制面板的价值在于:
用户不需要频繁打开扩展弹窗任务状态实时可见失败原因实时可见可以随时暂停或停止对于这种半自动化工具来说,UI 不是装饰,而是安全感的一部分。
十六、项目目录结构
Section titled “十六、项目目录结构”当前 BossAssistant 的主要目录如下:
BossAssistant/├── public/│ └── manifest.json # Chrome Extension MV3 清单├── src/│ ├── background/│ │ └── index.ts # Service Worker:消息路由与状态缓存│ ├── content/│ │ ├── index.ts # Content Script:页面注入、投递流程、回复助手│ │ ├── deliveryView.ts # 控制台岗位结果视图│ │ ├── dom.ts # DOM 工具函数│ │ └── selectors.ts # BOSS 页面选择器与按钮文本│ ├── options/│ │ ├── Options.tsx # 设置页与投递记录页│ │ ├── main.tsx # Options 入口│ │ └── styles.css # Options 样式│ ├── popup/│ │ ├── Popup.tsx # 扩展弹窗│ │ ├── main.tsx # Popup 入口│ │ └── styles.css # Popup 样式│ └── shared/│ ├── aiReply.ts # 回复草稿生成与 HR 问题分类│ ├── applications.ts # 投递记录、去重、CSV 导出│ ├── config.ts # 配置模型、默认值、校验│ ├── jobs.ts # 岗位扫描与筛选│ ├── messages.ts # 扩展消息类型│ ├── page.ts # BOSS 页面识别│ ├── storage.ts # chrome.storage.local 封装│ ├── task.ts # 任务状态与日志│ └── time.ts # 北京时间工具├── dist/ # 构建产物├── popup.html # Popup HTML 入口├── options.html # Options HTML 入口├── vite.config.ts # Vite 配置├── tsconfig.json # TypeScript 配置└── package.json这个结构里,我觉得 shared/ 目录很重要。
因为很多能力不应该散落在 Content Script 里。
例如:
配置校验岗位筛选投递记录时间处理消息类型任务状态回复草稿这些都放在 shared 里,后续维护会更清楚。
十七、当前已经完成的功能
Section titled “十七、当前已经完成的功能”目前 BossAssistant 已经完成了一个比较完整的 MVP 闭环:
Chrome Extension MV3 基础工程↓React + TypeScript 设置页↓Popup 状态展示↓BOSS 页面识别↓页面内控制面板注入↓职位卡片扫描↓关键词 / 城市 / HR 活跃筛选↓半自动沟通↓每日上限↓防重复记录↓投递记录管理↓CSV 导出↓聊天页 HR 消息识别↓本地回复草稿生成也就是说,它已经不只是一个 Demo,而是具备了实际使用闭环:
配置规则↓运行任务↓查看日志↓记录结果↓复盘投递↓辅助回复十八、开发中的几个关键问题
Section titled “十八、开发中的几个关键问题”开发这个项目时,我遇到的主要问题并不是某个算法有多难,而是真实网页环境非常不稳定。
1. 第三方页面 DOM 不稳定
Section titled “1. 第三方页面 DOM 不稳定”BOSS 页面结构不是我控制的。
选择器今天能用,不代表以后一直能用。
所以项目里需要多组选择器和兜底判断。
后续还应该增加:
选择器回归测试关键 DOM 失效提示页面结构变化时自动停止任务2. 自动化流程不能失控
Section titled “2. 自动化流程不能失控”投递动作必须可暂停、可停止。
所以每个关键步骤都要检查状态:
是否暂停是否停止页面是否跳走按钮是否存在是否达到每日上限3. 聊天消息识别容易误判
Section titled “3. 聊天消息识别容易误判”聊天页里不仅有 HR 消息,还有:
系统提示安全提醒简历发送状态输入框提示用户自己的消息平台推荐内容如果不做过滤,回复助手就会拿错误内容生成草稿。
所以消息识别不能只靠文本,还要结合 DOM 结构、头像、发送者名称、对齐方向等信息。
4. 隐私和合规边界必须提前设计
Section titled “4. 隐私和合规边界必须提前设计”求职助手涉及简历、聊天和投递记录。
如果一开始没有明确边界,后面很容易把项目做偏。
所以当前版本选择先做本地规则草稿,不接外部服务。
十九、我的阶段性理解
Section titled “十九、我的阶段性理解”做完 BossAssistant 后,我对浏览器插件项目有了更清楚的认识。
浏览器插件不是简单写一个页面。
它更像是多个运行环境协作:
Popup 负责入口Options 负责配置Background 负责消息和状态Content Script 负责真实页面交互Shared 模块负责通用逻辑而这类求职自动化项目的关键也不是“点得多快”,而是:
是否可控是否可解释是否可暂停是否能记录是否尊重用户确认是否有清晰边界如果没有这些设计,自动化很容易变成黑盒。
用户不知道它做了什么,也不知道哪里出了问题。
BossAssistant 真正想解决的问题是:
让求职过程中的重复动作变成一套可配置、可追踪、可复盘的本地工作流。
二十、后续优化方向
Section titled “二十、后续优化方向”接下来我准备继续优化几个方向。
1. 更稳定的选择器适配层
Section titled “1. 更稳定的选择器适配层”重点解决:
BOSS 页面 DOM 改版职位卡片误识别详情按钮识别失败聊天消息重复提取后续可以把选择器统一集中管理,并加入调试模式。
当某个关键选择器失效时,控制面板应提示:
当前页面结构可能已变化,请停止任务并检查选择器2. 单元测试和回归测试
Section titled “2. 单元测试和回归测试”目前项目已经有测试计划,后续可以补充:
配置校验测试关键词筛选测试城市筛选测试HR 活跃状态测试投递记录 key 生成测试CSV 导出测试回复类型识别测试页面识别测试这样以后改代码时,不至于影响核心流程。
3. 投递数据可视化
Section titled “3. 投递数据可视化”投递记录后续可以继续扩展成简单看板:
每日新增投递数量已沟通数量失败数量不同城市岗位数量不同关键词命中数量不同公司投递情况这样它就不只是记录工具,也可以辅助求职复盘。
4. 更细粒度的岗位匹配评分
Section titled “4. 更细粒度的岗位匹配评分”目前筛选主要是规则过滤。
后续可以加入岗位匹配评分:
关键词匹配度城市匹配度HR 活跃度薪资区间岗位标题相关性公司名称去重最后给每个岗位一个分数。
这样处理顺序可以更合理。
5. 可选接入 LLM
Section titled “5. 可选接入 LLM”回复助手后续可以支持可选接入 LLM API。
但即使接入,也应该保留几个原则:
默认不开启用户自己配置 API发送前必须确认不自动代发敏感信息可脱敏6. 多平台适配
Section titled “6. 多平台适配”当前项目只适配 BOSS 直聘。
后续可以考虑抽象平台适配层:
BOSSAdapterLiepinAdapterLagouAdapter每个平台只负责自己的:
页面识别岗位卡片选择器详情页按钮选择器聊天页消息识别公共能力继续复用:
配置任务状态记录存储回复草稿日志系统二十一、总结
Section titled “二十一、总结”BossAssistant 当前已经形成了一个比较完整的求职辅助闭环:
配置求职规则↓扫描岗位↓筛选匹配职位↓受控半自动沟通↓本地记录投递状态↓避免重复投递↓聊天页生成回复草稿↓用户确认后发送它的核心价值不是替我完全找工作,而是帮我完成:
减少重复操作保持投递记录降低漏记和重复让流程可暂停让结果可解释辅助组织回复我后续会继续把它作为一个真实求职场景下的浏览器插件项目维护。
最终我希望 BossAssistant 不只是一个简单脚本,而是一个更完整的本地求职工作台:
帮用户提高效率,但不替用户做最终决定;提供自动化能力,但始终保留可控、可追踪和可审核的边界。