Skip to content

输入关键词开始搜索

BossAssistant 项目架构:面向 BOSS 直聘的半自动求职助手


BossAssistant 是我26年7月初做的一个浏览器插件项目。

它的目标不是替用户完全自动求职,而是把 BOSS 直聘上高频、重复、容易漏记的动作变得更可控:

职位列表扫描
关键词 / 城市 / HR 活跃状态筛选
半自动打开岗位并沟通
本地记录投递状态
避免重复投递
聊天页识别 HR 问题
生成本地回复草稿

我对 BossAssistant 的定位是:

一个面向个人求职场景的半自动化浏览器助手,让重复操作可控、可追踪、可暂停,同时把最终决策权留给用户。


集中找工作时,很多时间并不是花在真正判断岗位是否合适,而是花在重复操作上。

例如:

打开职位列表
逐个看岗位
判断城市、关键词、薪资、HR 活跃状态
点击立即沟通
复制自我介绍
记录哪些岗位已经投过
再去聊天页组织回复

这些动作本身并不复杂,但数量一多就很容易出现问题:

重复投递
漏记岗位
忘记某个 HR 是否沟通过
复制话术很耗时间
投递过程无法复盘
聊天回复临时组织不稳定

所以我想做一个工具,把这些机械动作集中到一个页面内控制台里完成。

但这个项目一开始就没有设计成“全自动投递器”。

我更希望它保持几个边界:

用户配置规则
用户决定是否开始
用户可以暂停、继续、停止
回复只生成草稿
最终发送仍然由用户确认
不绕过验证码、登录、风控或平台限制

因此,BossAssistant 的核心不是“自动化越强越好”,而是:

在不丢失控制权的前提下,减少重复劳动。


BossAssistant 整体架构图

整体架构可以概括为:

Chrome Extension MV3
Popup / Options / Background / Content Script
页面内控制面板
职位扫描与筛选
半自动沟通流程
本地投递记录
聊天页回复草稿

其中最重要的是 Content Script。

因为它真正运行在 BOSS 直聘页面里,负责:

识别当前页面
注入控制面板
扫描岗位卡片
读取岗位信息
执行受控点击
记录运行日志
识别聊天消息
生成回复草稿

Background Service Worker 更像消息中转层,负责在 Popup、Options 和 Content Script 之间转发配置、页面状态和任务状态。


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 可以走 Vite
Content Script 需要打成 IIFE
Background 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)

对应的构建命令是:

Terminal window
npm.cmd run build

构建完成后,加载 dist/ 目录即可在 Chrome 中调试扩展。


BossAssistant 的运行流程可以分成两个场景。

第一个场景是职位列表页:

用户进入 BOSS 职位列表页
Content Script 检测页面类型
注入 BossAssistant 控制面板
读取用户配置
扫描当前页面岗位卡片
按关键词、城市、HR 活跃状态筛选
跳过已沟通过或已记录岗位
依次打开匹配岗位详情
点击立即沟通
记录成功、跳过或失败原因

BossAssistant 投递控制台

第二个场景是沟通聊天页:

用户进入 BOSS 沟通聊天页
识别聊天消息区域
提取 HR 消息
判断问题类型
匹配简历资料
生成回复草稿
用户选择复制或填入输入框
用户确认后手动发送

BossAssistant 回复助手

这两个场景共用一套配置、本地存储、任务状态和日志系统。


Options 设置页是整个工具的配置中心。

BossAssistant 配置设置

目前支持配置:

自我介绍
职位关键词
目标城市
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/chatHR 消息识别、回复草稿生成、复制或填入

页面识别逻辑可以概括为:

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 这类网站经常是前端路由切换,页面地址变化不一定会重新加载整个网页。


职位扫描是 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;
};

项目里没有只依赖一个固定选择器,而是使用多组候选选择器:

.job-card-wrapper
.job-card-box
.job-card-body
.job-primary
li[class*='job-card']
div[class*='job-card']
li[class*='job-list']

然后再通过内容判断它是否真的像岗位卡片。

判断依据包括:

是否包含 /job_detail/ 链接
是否包含薪资文本
是否包含职位关键词
是否排除广告、推广、页脚元素

这样做是为了提高兼容性。

因为第三方页面结构随时可能调整,如果只写死一个选择器,很容易失效。

提取字段时也不是只写一个选择器,而是准备多组候选。

例如职位名称可能来自:

.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 活跃度不匹配:半月内活跃

这样设计的原因是:

自动化工具最怕“它替你做了决定,但你不知道为什么”。

所以在页面内控制台里,不仅要展示哪些岗位匹配,也要展示哪些岗位被跳过,以及为什么被跳过。

这让整个流程更容易复盘。


BossAssistant 的投递流程不是一口气乱点页面,而是带状态控制的受控流程。

整体过程可以概括为:

用户点击开始
扫描当前页面岗位
筛选候选岗位
检查本地记录是否已投递
检查今日新增投递上限
滚动岗位卡片到视野
点击岗位卡片
等待详情面板渲染
查找“立即沟通”或“继续沟通”
点击立即沟通
处理平台提醒弹窗
记录结果
继续下一个岗位

如果页面显示“继续沟通”或“已沟通”,说明这个岗位之前已经处理过。

这类岗位不会重复发送,而是补记为:

已沟通

如果点击失败、按钮未找到、页面跳走,也不会让整个任务卡死,而是记录失败原因并继续处理后续岗位。


十、任务控制:开始、暂停、继续、停止

Section titled “十、任务控制:开始、暂停、继续、停止”

BossAssistant 的页面内控制台支持:

开始
暂停
继续
停止

这点非常重要。

因为浏览器插件直接操作真实网页,如果没有暂停和停止机制,用户会很没有安全感。

任务运行过程中会维护一个运行状态:

type RunControl = {
id: string;
paused: boolean;
stopRequested: boolean;
};

每处理一个岗位前后,都会检查:

用户是否暂停
用户是否停止
当前页面是否仍在职位列表页
今日上限是否已经达到

如果用户点击暂停,任务不会继续点击新岗位。

如果用户点击停止,当前运行会终止。

如果页面离开职位列表页,也会立刻停止,避免误操作。


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 “十二、投递记录:让求职过程可追踪”

BossAssistant 投递记录

投递记录保存在本地 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;
};

状态包括:

success
communicated
skipped
failed

在设置页中,可以查看:

新投递数量
已沟通数量
失败记录
跳过原因
按日期筛选
CSV 导出
自动刷新

这让 BossAssistant 不只是一个点击工具,也承担了求职记录管理的作用。

我觉得这一点很关键。

因为求职过程本身就是一个需要长期跟踪的过程:

今天投了哪些岗位
哪些已经沟通过
哪些失败了
哪些不匹配
后续要不要复盘关键词

如果这些信息只停留在页面上,很快就会丢失。


除了职位列表页,BossAssistant 还支持 BOSS 沟通聊天页。

它会尝试识别 HR 发来的消息,并生成本地回复草稿。

回复流程是:

进入聊天页
识别聊天消息区域
排除系统消息、用户自己的消息、输入框提示
提取 HR 消息
判断问题类型
匹配简历资料
生成回复草稿
用户复制或填入输入框
用户确认后手动发送

目前支持识别的问题类型包括:

薪资期望
到岗时间
面试安排
实习周期
工作时间
求职状态
岗位意向
地点意向
学历专业
作品资料
项目经验
技术能力
自我介绍
通用问题

这里的“AI 回复”目前更准确地说是本地规则生成。

它不会调用外部大模型 API,也不会上传简历和聊天内容。

我这样设计主要是为了先保证隐私和稳定性。

后续如果接入 LLM,也应该默认保留人工确认,不应该自动代发。


BossAssistant 处理的是求职相关信息,所以隐私边界非常重要。

当前项目坚持几个原则:

配置保存在本地
投递记录保存在本地
简历资料保存在本地
聊天内容不上传
回复草稿不自动发送
不绕过登录、验证码、风控或平台限制

这类项目很容易被误解成“自动化刷投递”。

但我希望它的定位更清晰:

它是个人效率工具,不是绕过平台规则的工具。

所以项目里保留了每日上限、暂停机制、日志记录和用户确认。

尤其是回复助手部分,只提供:

复制
填入输入框

最终是否发送,仍然由用户自己判断。


BossAssistant 会在支持的 BOSS 页面内注入一个控制面板。

控制面板主要展示:

当前页面类型
配置摘要
任务状态
处理数量
成功数量
跳过数量
失败数量
操作日志
岗位处理结果
回复草稿

控制面板使用 Shadow DOM 注入。

这样可以尽量避免和 BOSS 页面原有样式相互影响。

页面内控制面板的价值在于:

用户不需要频繁打开扩展弹窗
任务状态实时可见
失败原因实时可见
可以随时暂停或停止

对于这种半自动化工具来说,UI 不是装饰,而是安全感的一部分。


当前 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 里,后续维护会更清楚。


目前 BossAssistant 已经完成了一个比较完整的 MVP 闭环:

Chrome Extension MV3 基础工程
React + TypeScript 设置页
Popup 状态展示
BOSS 页面识别
页面内控制面板注入
职位卡片扫描
关键词 / 城市 / HR 活跃筛选
半自动沟通
每日上限
防重复记录
投递记录管理
CSV 导出
聊天页 HR 消息识别
本地回复草稿生成

也就是说,它已经不只是一个 Demo,而是具备了实际使用闭环:

配置规则
运行任务
查看日志
记录结果
复盘投递
辅助回复

开发这个项目时,我遇到的主要问题并不是某个算法有多难,而是真实网页环境非常不稳定。

BOSS 页面结构不是我控制的。

选择器今天能用,不代表以后一直能用。

所以项目里需要多组选择器和兜底判断。

后续还应该增加:

选择器回归测试
关键 DOM 失效提示
页面结构变化时自动停止任务

投递动作必须可暂停、可停止。

所以每个关键步骤都要检查状态:

是否暂停
是否停止
页面是否跳走
按钮是否存在
是否达到每日上限

聊天页里不仅有 HR 消息,还有:

系统提示
安全提醒
简历发送状态
输入框提示
用户自己的消息
平台推荐内容

如果不做过滤,回复助手就会拿错误内容生成草稿。

所以消息识别不能只靠文本,还要结合 DOM 结构、头像、发送者名称、对齐方向等信息。

4. 隐私和合规边界必须提前设计

Section titled “4. 隐私和合规边界必须提前设计”

求职助手涉及简历、聊天和投递记录。

如果一开始没有明确边界,后面很容易把项目做偏。

所以当前版本选择先做本地规则草稿,不接外部服务。


做完 BossAssistant 后,我对浏览器插件项目有了更清楚的认识。

浏览器插件不是简单写一个页面。

它更像是多个运行环境协作:

Popup 负责入口
Options 负责配置
Background 负责消息和状态
Content Script 负责真实页面交互
Shared 模块负责通用逻辑

而这类求职自动化项目的关键也不是“点得多快”,而是:

是否可控
是否可解释
是否可暂停
是否能记录
是否尊重用户确认
是否有清晰边界

如果没有这些设计,自动化很容易变成黑盒。

用户不知道它做了什么,也不知道哪里出了问题。

BossAssistant 真正想解决的问题是:

让求职过程中的重复动作变成一套可配置、可追踪、可复盘的本地工作流。


接下来我准备继续优化几个方向。

重点解决:

BOSS 页面 DOM 改版
职位卡片误识别
详情按钮识别失败
聊天消息重复提取

后续可以把选择器统一集中管理,并加入调试模式。

当某个关键选择器失效时,控制面板应提示:

当前页面结构可能已变化,请停止任务并检查选择器

目前项目已经有测试计划,后续可以补充:

配置校验测试
关键词筛选测试
城市筛选测试
HR 活跃状态测试
投递记录 key 生成测试
CSV 导出测试
回复类型识别测试
页面识别测试

这样以后改代码时,不至于影响核心流程。

投递记录后续可以继续扩展成简单看板:

每日新增投递数量
已沟通数量
失败数量
不同城市岗位数量
不同关键词命中数量
不同公司投递情况

这样它就不只是记录工具,也可以辅助求职复盘。

目前筛选主要是规则过滤。

后续可以加入岗位匹配评分:

关键词匹配度
城市匹配度
HR 活跃度
薪资区间
岗位标题相关性
公司名称去重

最后给每个岗位一个分数。

这样处理顺序可以更合理。

回复助手后续可以支持可选接入 LLM API。

但即使接入,也应该保留几个原则:

默认不开启
用户自己配置 API
发送前必须确认
不自动代发
敏感信息可脱敏

当前项目只适配 BOSS 直聘。

后续可以考虑抽象平台适配层:

BOSSAdapter
LiepinAdapter
LagouAdapter

每个平台只负责自己的:

页面识别
岗位卡片选择器
详情页按钮选择器
聊天页消息识别

公共能力继续复用:

配置
任务状态
记录存储
回复草稿
日志系统

BossAssistant 当前已经形成了一个比较完整的求职辅助闭环:

配置求职规则
扫描岗位
筛选匹配职位
受控半自动沟通
本地记录投递状态
避免重复投递
聊天页生成回复草稿
用户确认后发送

它的核心价值不是替我完全找工作,而是帮我完成:

减少重复操作
保持投递记录
降低漏记和重复
让流程可暂停
让结果可解释
辅助组织回复

我后续会继续把它作为一个真实求职场景下的浏览器插件项目维护。

最终我希望 BossAssistant 不只是一个简单脚本,而是一个更完整的本地求职工作台:

帮用户提高效率,但不替用户做最终决定;提供自动化能力,但始终保留可控、可追踪和可审核的边界。