Skip to content

Architecture and Development

YanQuan edited this page Oct 21, 2026 · 9 revisions

架构与开发

面向想理解 BossClaw 内部结构、参与开发或排查线上问题的读者。 文档基线:desktop-app/package.json 的 version = 2.5.7(2026-10-13 发布),对应 main 分支实现;文中所有路径均相对仓库根目录。 只想使用?先看快速开始与功能指南;安全边界见安全与合规。

读法建议

  • 第一次读:一 → 二 → 三 → 七,先建立「技术栈 / 进程 / 目录 / IPC」的空间感。
  • 改业务逻辑:四(模块地图)→ 五(数据与状态机)→ 六(链路),改完按十跑门禁。
  • 排查问题:十二(排障手册)→ 十(回归矩阵)→ 六(对应链路)。
  • 接新平台:九(扩展指南)是逐项清单,照做即可。

目录


一、技术栈与运行环境

层 选型 说明
桌面框架 Electron ^42.11.10(Chromium 148) 主进程用 CommonJS(electron/*.cjs);electron-builder ^26 打包
渲染层 React 18.3 + TypeScript 5.6 + Vite 5.4 SPA;生产加载 dist/index.html,开发加载 http://localhost:5173
UI / 状态 Ant Design 5.21 + Zustand(persist → localStorage) 主题 Teal-on-Neutral,深浅色双套变量
运行环境 Node.js 20+(推荐 22,.nvmrc 固定) 仅源码构建 / 打包需要
可选引擎 Python 3.10+(Camoufox / Playwright) 仅「隐身引擎」批量沟通需要;不装不影响内置浏览器链路
数据 localStorage,零后端 备份写盘由主进程完成;不依赖任何云服务
语音 / 模拟面试 sherpa-onnx-node(N-API 原生模块) + edge-tts 依赖不再是「全纯 JS」;语音模型不随包分发,运行时下载到 <userData>/speech/models/

Electron 42 的两处 API 适配已收敛:console-message 首参改为对象(parseConsoleMessage()),session.setPreloads/getPreloads 改为 registerPreloadScript(旧接口保留双版本兼容)。升级评测见仓库 Issue / Release 记录。


二、进程模型与信任边界

┌──────────────────────── 主进程 electron/main.cjs(CommonJS,Node 全权限) ────────────────────────┐
│ 单窗口生命周期 · BrowserWindow + 内置 <webview> · 112 条白名单 IPC(safeHandle 包装)              │
│ 子进程管理:Camoufox(Python,127.0.0.1:18767)· CloakBrowser · OpenClaw Node 桥(127.0.0.1:18765)│
│ 文件能力:PDF 打印、CSV/JSON 导出、备份、技能库读写、素材读取(全部经用户选择或白名单)              │
│ 本地控制桥 control-bridge.cjs(127.0.0.1:17650,默认关闭,供外部 Agent / MCP 使用)                │
├───────────────────────────────────────────────────────────────────────────────────────────────┤
│ 预加载 preload/app.cjs      主窗口唯一出口:contextBridge → window.electron(IPC 白名单 + LLM 代理)│
│ 预加载 preload/webview.cjs  guest 页注入:采集、回传、真实输入、投递状态机(平台差异走适配表)        │
│ 预加载 platform-adapters.cjs 多平台 DOM 适配表(纯数据 + 纯函数,零 DOM 依赖,可离线单测)           │
│ 预加载 liepin-im.cjs        猎聘 IM 只读状态(判断「是否新增本人文字气泡」)                        │
├───────────────────────────────────────────────────────────────────────────────────────────────┤
│ 渲染进程 src/(ESM):React SPA;**只能**通过 window.electron 与主进程交互,无 Node 权限            │
└───────────────────────────────────────────────────────────────────────────────────────────────┘

安全基线(不可放宽)

配置 值 原因
contextIsolation true 渲染层拿不到 Node / Electron 内部对象
nodeIntegration false 同上;页面脚本无法直接 require
webviewTag true 内置浏览器依赖 <webview>;其 preload 必须 sandbox: false 才能注入
IPC 白名单 preload/app.cjs::ALLOWED_IPC_CHANNELS(66 项) 未登记通道在运行时直接抛错;新增 jc: 通道必须同步登记
控制桥监听 仅 127.0.0.1 不暴露局域网;除 /health 外全部要求令牌
OpenClaw 桥来源校验 Host 白名单 + Origin 白名单(null / localhost / 127.0.0.1) 防 DNS rebinding,且不再反射任意 Origin
令牌 随机 24 字节 hex,写入 <userData>/control-bridge.json(0600) 不用硬编码令牌;可用 BOSSCLAW_CONTROL_TOKEN 覆盖

三条本地桥的令牌不要混用:

桥 端口 令牌位置
控制桥(Agent / MCP) 17650 <userData>/control-bridge.json
OpenClaw Node 桥 18765 ~/.bossclaw/bridge-token(渲染层经 jc:bridge-token 取)
Camoufox Python 桥 18767 ~/.bossclaw/camoufox-token(跨启动稳定)

三、目录结构(v2.5.7)

Boss-claw/
├── desktop-app/                      主应用(Electron + React)
│   ├── electron/
│   │   ├── main.cjs                  主进程:窗口 / webview / IPC / 子进程 / 文件能力
│   │   ├── control-bridge.cjs        本地控制桥(/health /state /action,默认关闭)
│   │   ├── mockInterviewRepo.cjs     模拟面试:会话全量 + 轻量索引(可重建)
│   │   ├── mockInterviewBankRepo.cjs 题库注册表 / 用户题库 / BM25 索引缓存
│   │   ├── mockInterviewVoiceRepo.cjs + VoicePack.cjs  克隆音色仓库与音色包
│   │   ├── speechService.cjs + speechWorker.cjs + speechInstall.cjs  本地 ASR/TTS 服务、模型下载
│   │   ├── edgeTts.cjs               在线 TTS 通道
│   │   ├── preload/
│   │   │   ├── app.cjs               主窗口安全接口(IPC 白名单 + LLM 代理)
│   │   │   ├── webview.cjs           guest 页采集 / 真实输入 / BOSS 投递状态机
│   │   │   ├── platform-adapters.cjs 多平台 DOM 适配表(零 DOM 依赖)
│   │   │   ├── liepin-im.cjs         猎聘 IM 只读状态(确认本人文字消息)
│   │   │   ├── humanize.cjs          人类化输入节奏
│   │   │   └── stealth.cjs           内置浏览器反检测补丁
│   │   └── cloakbrowser/             CloakBrowser 生命周期与页面预加载
│   ├── bridge/                       OpenClaw Node 桥(mammoth / 文件 / 任务恢复)
│   ├── camoufox/
│   │   ├── camoufox_server.py        多平台调度基座(/search /send /login /chat-watch)
│   │   ├── platforms/                base.py 三段骨架 + 各平台差异声明 + 能力/筛选码值
│   │   └── tests/                    Playwright DOM 回归(聊天上下文、猎聘投递)
│   ├── skills/                       AI 技能库(9 个 SKILL.md,只读随包;含模拟面试出题 / 报告)
│   ├── resources/                    随包资源(docs「使用前必读」/ bank 内置题库 / speech 语音配置)
│   ├── scripts/                      回归脚本(见第十节)
│   ├── src/
│   │   ├── store/                    useAppStore / useDataStore / useSettingsStore / useScheduleStore / useRuntimeLogsStore / useAutoChatStore / useMockInterviewStore / useBankStore
│   │   ├── lib/                      storage / electronApi / bridgeClient / localBackup / scheduler / controlRuntime / browserRegistry
│   │   ├── lib/bossclaw/             业务核心(模块地图见第四节);模拟面试 = mockInterview* 全家桶
│   │   ├── components/               TitleBar / Sidebar / StatusBar / BrowserView / PlatformChip / MarkdownView / MockBankPanel
│   │   └── pages/                    Home / Workbench / Resume / Directions / Tasks / ScheduleTasks / Stats / Assistant / OpenClaw / AutoChat / MockInterview / Settings
│   └── package.json                  依赖、scripts、electron-builder 配置
├── mcp/bossclaw-mcp/                 零依赖 stdio MCP 服务器(9 工具 / 3 组)
├── start-bossclaw.cmd                Windows 一键启动(默认开控制桥;--dev / --visible / --no-agent)
├── install-deps.cmd                  依赖安装(Node + Electron,可选 Python 引擎)
└── README.md                         项目主页与使用说明

docs/ 目录为本地开发文档,不随仓库分发(.gitignore),因此 README 不链接其中任何文件;对外公开的技术内容以本 Wiki 为准。


四、核心模块地图(唯一权威)

改业务逻辑前先在表里找到权威模块,只改权威处,调用方不复制逻辑。

领域 权威模块 关键口径
任务阶段机 lib/bossclaw/taskState.ts + types.ts::TaskStage 阶段 → 文案/进度映射;PHASE_LABELS 五段
岗位状态 lib/bossclaw/types.ts::PendingStatus + statusMeta.ts 状态词汇见 5.3,UI 文案不得另造
岗位优先级 lib/bossclaw/priority.ts approved(0) → approved_queue(1) → pending(2) → failed(3);risks×-90 / gaps×-45
岗位去重 / 投递锁 store/useDataStore.ts::addPendingItem + deliveryLock.ts 去重 = 归一化 URL 或 jobId 或 sameJobSoft;投递锁 claim/release 必须成对
账号级节流 deliveryThrottle.ts 进程级单例 sharedPacer();禁止各引擎 new ActionPacer
风控冷却 safety.ts::nextCooldownUntil 单调合并,禁止裸 Date.now() + cooldownMs 覆盖写
安全上限 limits.ts::SAFETY_LIMITS 150/日、8/分钟、间隔基数 15s;agent 不得修改
匹配与评分 matching.ts / jobMatch.ts / fitLevel.ts AI 四层裁决;AI 分即最终分,本地分只展示/兜底
技能本体 skillTaxonomy.ts 禁止再自建别名表
薪资 helpers.ts::cleanSalary + workSchedule.ts 外送前必清洗;月工作日 = 周天数 × 52/12
JD 学历 jobMatch.ts::jdRequiredDegreeLevel 「本科及以上」不升档;「优先」不是要求
岗位过期 jobExpiry.ts 仅 JD 显式写截止日期才判定;当天仍有效
面试方式 interviewMode.ts 未披露 = 合格,不误杀
校名披露 schoolTier.ts 仅 985/211 写校名;判不出即禁止
目标城市 targetLocations.ts + targetLocationSync.ts 设置页与画像 hardConstraints.locations 同源
平台注册 / 能力 platforms.ts ↔ camoufox/platforms/capabilities.py 双源同步;判定走 platformSupports()
搜索 URL platformUrls.ts / searchUrl.ts 各平台参数构造唯一入口
跨平台筛选码值 camoufox/platforms/filters.py 平台条件码映射唯一权威
采集故障范围 platforms.ts::collectFaultScope() 见 6.1;未知码 fail-safe 归队列级
webview DOM electron/preload/platform-adapters.cjs webview.cjs 禁止内联平台选择器
jobId platform-adapters.cjs::jobIdFromUrl() 唯一入口;各平台 URL 形态不同
AI 调用 llm.ts(callModel / cachedCallModel) 禁止在别处直接发 HTTP,禁止绕过缓存
思考强度 thinkingCapability.ts 未验证模型 = unsupported,绝不发参数
上下文预算 contextBudget.ts 窗口 × 用量档位;预算签名进缓存 key
超窗分片 oversizedContext.ts::prepareContextText ≤3 片;主裁决恒一次调用
AI 降级 / 代答 agentAnswer.ts + llm.ts 的 !apiKey 分支 只搬「提示词 ↔ 文本」
控制桥白名单 src/lib/controlRuntime.ts 约 60 个动作;发送类受 executionMode 守卫
招呼语 greetings.ts::clampGreetingText 全文 120–250 字,250 为硬上限;禁止裸 slice
浏览器白名单 browserWhitelist.ts browserOpenJob 必须过白名单
本地备份 localBackup.ts + storage.ts::STORAGE_KEYS / BACKUP_KEYS 5 分钟脏检查;STORAGE_KEYS 是唯一登记表,BACKUP_KEYS 由 backup 标记派生
作息保护(活跃时段 / 批次休息) activityWindow.ts 默认 8:00–23:00(±25 分钟抖动)、每 15 个岗位休 8–15 分钟;两引擎同口径;只约束时间,不改速率上限
定制简历管线 tailorJob.ts / tailorBatch.ts + electron/resume-pdf-batch.cjs 单岗位 / 批量 / 迭代三档;批量 PDF 由主进程独占写盘(wx + 同名编号)
模拟面试出题 / 评分 mockInterviewEngine.ts + mockInterviewTypes.ts 两段式;模型只给档位,分数由本地五维权重算;N/A 不参与加权
模拟面试题库 / 会话 electron/mockInterviewBankRepo.cjs / mockInterviewRepo.cjs 内置题库只读不复制;index.json 是派生缓存,可重建
内置浏览器反检测 electron/preload/stealth.cjs + TARGET_PROFILE 一份画像驱动 UA / 请求头 / 主世界补丁;BOSCLAW_STEALTH 可逐项开关

五、数据模型与持久化

5.1 localStorage 键

键 内容 换机导出 本地备份
bossclaw-app 运行时 UI 状态(主题、当前路由、侧栏、桥状态、登录态) 是 是
bossclaw-settings-v2 用户配置(模型密钥、求职条件、平台开关/优先级/每日目标、节奏与安全参数) 是 是
bossclaw-data 业务数据(岗位 /pending、任务 /taskRuns、画像、方向、招呼语、沟通信息) 是 是
bossclaw-schedule 定时任务(动作 / 时间 / 星期 / 平台 / 单轮上限)+ 采集请求 是 是
bossclaw-runtime-logs 运行日志 / 聊天日志(独立键,避免日志膨胀拖慢业务数据序列化) 否 否
bossclaw-skills-v1、bossclaw-city-codes、bossclaw-ai-cache-* 技能启用态、城市码缓存、AI 缓存(可重建) 否 否
bossclaw-tailor-history-v1 定制简历历史 否 是
bossclaw-resume-module-order-v1、-template-v1、-photo-v1 简历模块排序 / 模板 / 照片 否 否
bossclaw-data-version 数据版本号(启动迁移依赖,不可清空) 否 否

备份由 localBackup.ts 每 5 分钟做一次脏检查(persistDirtyEpoch),写盘到用户指定目录;主存储缺失时按代数锚定回签恢复。所有键只在 storage.ts::STORAGE_KEYS 登记一次(export / backup / clear 三个开关),EXPORT_KEYS / BACKUP_KEYS 由其派生;新增持久化键必须登记,否则导出 / 恢复 / 清空会漏。

5.2 核心类型

PendingItem {
  id; runId?; job: JobMeta; analysis?: JobAnalysis; deliveryGreeting;
  status: PendingStatus; createdAt; approvedAt?; priorityScore?; priorityRank?;
  retryCount?; error?; retryable?;
  sentAt?;            // 成功投递时间(计入每日上限)
  replySentAt?;       // AI 跟聊回复时间(不计入每日上限)
  openedAt?; riskBlocked?;
  hrRepliedFingerprint?; hrRejectedAt?;
}

JobAnalysis {
  score; fitLevel; decision; hardBlocks[]; matchedEvidence[]; gaps[]; risks[];
  reason; greeting; dimensions?; scoreSource?: 'ai' | 'local';
}
  • JobMeta 保留平台、URL、jobId、JD 文本、HR 活跃度、福利标签、面试方式、招聘方姓名等采集字段;platform 缺省视为 boss(兼容存量数据)。
  • TaskRun 是采集/分析批次记录:status(running / success / failed / skipped / ignored / waiting_review / queued / pending)+ stage + progress + 计数(discovered / analyzed / failed)。
  • 评分与决策:fitLevel 四档 strong / match / cautious / unfit ↔ 分数区间固定;decision 由档位与硬约束共同决定。

5.3 岗位状态(唯一口径)

状态 UI 文案 含义
pending 待确认 AI 已分析,等待人工批准
approved 待投递 已批准,尚未进入投递循环
approved_queue 投递中 引擎已认领,按优先级排队投递
opened 已打开沟通窗口 已打开聊天页,等待发送/核对
sent 已投递 文字气泡已确认(或平台等价成功信号)
failed 投递失败 可重试的失败;风控失败另有 riskBlocked
skipped 未达到推荐条件 外部网申 / 已投递 / 条件不符
ignored 已忽略 用户主动忽略或过期
rejected 不推荐 硬约束拦截,不入队

状态流转不变量

  1. 批准即入队:批准时招呼语必须非空,approved 只由 promoteApprovedToQueue() 单向提升为 approved_queue。
  2. approved_queue 不得回写 approved(会造成重复投递与队列卡死)。
  3. 首次成功投递后必须 pauseAssist(强制暂停验收),该分支不能裸 return。
  4. 同一岗位同一时刻只允许一个引擎持有投递锁;claimDelivery 失败即放弃本轮,不重试抢锁。
  5. 任何按 status 分支的 UI,改动口径后必须全仓 grep(tsc 不会报错,架空态会静默失效)。

5.4 任务阶段与进度

阶段 文案 进度
discovered 已发现岗位 12
collect_detail 读取岗位详情 24
ai_analyze AI 匹配分析 42
ai_complete AI 分析完成 56
waiting_review 等待人工确认 60
queued / retry_queued 等待投递 / 等待重新投递 64 / 66
open_job 打开岗位页面 70
open_chat 打开沟通窗口 78
opened 已打开沟通窗口,等待发送/核对 80
verify_chat_target 核对 HR 与岗位 82
fill_message 填写求职招呼语 86
send_message 发送求职招呼语 90
verify_message 确认文字已发送 94
send_resume 发送简历附件 97
verify_result 确认投递结果 98
success / failed / ignored / skipped 终态 100

PHASE_LABELS = ['搜索','匹配','确认','投递','完成'] 是进度条五段口径;stageToPhase() 是唯一映射。新增阶段必须同时补 TASK_STAGE_META 与 STAGE_TO_PHASE(缺一个会回退到「处理中 / 60%」)。


六、核心链路

6.1 搜索采集(两条通道)

通道 触发 覆盖 详情 JD
内置浏览器(webview) 默认;未启用隐身引擎时采集也走这条 四平台列表级;BOSS 列表内联详情 智联 = 同源详情 JSON;猎聘 = 同源 SSR 详情页 HTML;前程无忧 = 仅列表字段
Camoufox(Python) 设置页启用隐身引擎后,采集由它接管 四平台列表级(BOSS 保留既有采集/投递链路) 非 BOSS 仍由内置浏览器 enrichJobDetail() 补齐;与是否安装 Camoufox 无关

口径更正:camoufox/platforms/liepin.py / zhaopin.py / job51.py 只实现列表级采集;不要以为启用隐身引擎就能拿到非 BOSS 详情 JD。详情补齐的唯一入口是内置浏览器的平台同源接口。

  • BOSS 是唯一「列表内联详情」平台,禁止给非 BOSS 加「逐卡点开详情」;非 BOSS 详情补齐唯一入口是 webview.cjs::enrichJobDetail()。
  • 登录墙即时收口:判定未登录 = loginWall 且 cards <= 0;命中即 reason='login',不重试,立即收口本平台,后续平台继续。失败日志必须带 describeProbe() 诊断。
  • 故障范围唯一权威 collectFaultScope(code):
    • 平台级(只收口当前平台):31 / 400 / 403 / 404 / 500 / 501 / 600 / 1006;
    • 队列级(整批中止交人工):32 / 35 / 36 / 37 / 38;
    • 无码 / 0 → 平台级;未知码 → 队列级(fail-safe,宁停不错)。
    • 所有采集循环遇到 'abort' 必须 break。
  • 断点续采只做词级:(platform, city, keyword),TTL 24h,存 ~/.bossclaw/collection-progress.json。页级断点禁止加回(整批返回架构下会静默丢岗位);命中风控必须 clear_combo 从头采,宁重复不遗漏。
  • 并发:采集后的 AI 分析走有界队列(analysisQueue.ts + analysisConcurrency,默认 3),禁止 fire-and-forget 造成无界 LLM 调用。

6.2 AI 分析(四层裁决)

岗位 JD + 简历 + 画像 + 方向
  → 硬门槛(学历/经验/城市/薪资/过期/面试方式…)──命中→ 直接 reject(不消耗 AI)
  → AI 四层整体裁决:硬门槛 → 优先条件 → 职责信号 → 团队信号
  → fitLevel 档位(strong / match / cautious / unfit)→ 分数随档映射
  → decision + 硬拦截说明 + 匹配证据 + 缺口 + 风险 + 招呼语
  → 本地五维分(仅展示/兜底,不与 AI 融合)
  • AI 分即最终分;本地唯一改分权是 hardBlocks(score ≤ 35 或 reject)。
  • AI 输出必须走 llm.ts 的 JSON 管线:response_format: json_object + prompt 含 json + 填充版样例 + max_tokens(≥2000,封顶 8192);截断内容不做宽松截尾修复。
  • 失败语义:AI_EMPTY / AI_TRUNCATED / AI_INVALID_JSON 由上层走本地规则兜底;AI 缺字段时不回填本地候选(除明确的兜底路径)。
  • 思考强度只由 thinkingCapability.ts 决定;未知模型一律不发思考参数(部分模型传 disabled 会 400)。

6.3 入队与排序

  1. 采集 → 分析与硬约束 → 分数低于 minQueueScore 不入队。
  2. 入队后状态 pending,按 priority.ts 计算 priorityRank;入队时按「归一化 URL / jobId / 软匹配」三路去重(addPendingItem),同一岗位不叠卡。
  3. 用户批准(招呼语非空)→ approved;投递引擎起跑时 promoteApprovedToQueue() → approved_queue。
  4. 重排 rerankPending() 只影响 approved / approved_queue / pending,opened 不参与排名。

6.4 投递(三条通道,一个安全内核)

Workbench.runNext 只做「选岗位 + 预检 + 分派」,不承载具体点击逻辑:

通道 平台 实现 成功判定
BOSS DOM boss webview.cjs 聊天发送状态机 本次新增的完整本人文字气泡
Camoufox 仅 boss(设置页「优先走隐身通道」) Python /send(官方接口 + 真实点击兜底) 接口结果 + 气泡/会话确认,风控码即停
非 BOSS DOM liepin / zhaopin / job51 webviewApi.platformApply() 猎聘 = 新增本人文字消息;智联/前程无忧 = 简历投递成功页/按钮终态

BOSS 文字发送(唯一权威 = webview.cjs 的聊天发送上下文状态机)

  • 上下文判定:chatSendVisible / chatSendEditors / chatSendPanel / createChatSendContext / currentChatSendInput → sendButton → trustedClickElement → confirmOwnMessage。
  • 硬不变量:编辑器、面板、头部、消息列表、发送按钮都必须唯一;任一不唯一立即放弃(宁可不发,不可发错会话)。
  • 确认口径:entry.text === needle + 节点身份 / 消息 ID + DOCUMENT_POSITION_FOLLOWING,必须是本次新增的完整己方气泡;长度阈值已整体退场,禁止重新引入;禁止整页 innerText.includes() 兜底。
  • 坐标换算唯一入口 main.cjs::nativeMousePoint:CSS 视口坐标只乘 guest zoomFactor,不得叠 Windows DPR / 宿主缩放 / webview 偏移。
  • 输入主通道是主进程 sendInputEvent(isTrusted: true);页面内仍有少量合成事件(el.click() / dispatchEvent)仅用于非关键兜底,不得用于发送确认;把剩余交互全部收口到真事件通道仍是待办项。

投递并发与节奏

  • 投递锁:deliveryLock.ts 按 id + platform 认领,try/finally 释放;platformApply 另有 busy / activeSeq 守卫。
  • 账号级节流:deliveryThrottle.ts 的 sharedPacer() 是跨两套引擎的进程级单例,预算天花板 SAFETY_LIMITS.MAX_ACTIONS_PER_MINUTE = 8;批次休息计数同样跨引擎累计。
  • 冷却:safety.ts::nextCooldownUntil 单调合并(旧冷却未结束不会被新值缩短);isLockedOut / cooldownRemaining 是所有引擎的同一判据。
  • 每日上限:min(平台每日目标, 平台侧上限, 150);BOSS / 猎聘 / 前程无忧默认 120,智联 100。
  • 作息保护:唯一权威 activityWindow.ts。活跃时段默认 8:00–23:00(每日 ±25 分钟抖动,避免「每天精确 8:00」的机器特征),批次休息默认每 15 个岗位休 8–15 分钟;工作台与自动沟通两个引擎同口径,窗口外主动让路。它只约束作业时间,不改变任何速率上限与安全不变量;两项配置经控制桥 CONFIG_DENY 拒绝写入,只能设置页手动改。
  • 猎聘 platform-apply 超时放宽到 55s;超时带 needsReview 且保留标签交人工,分支必须早于 closeTab。

6.5 自动沟通与 AI 跟聊

  • 自动沟通(可选 Camoufox):按平台优先级串行;BOSS 发文字招呼语(气泡确认)、猎聘「聊一聊」注入本岗位招呼语、智联/前程无忧投递简历;各平台独立计数,遇风控码立即中止本批交人工。
  • AI 跟聊(BOSS 专属,可选):/chat-watch 单一常驻浏览器停在会话页(start / scan / open / send / stop);渲染层 2 分钟一轮、每轮 ≤6 会话、两次回复最小间隔 20s;与批量投递共用 pacer 与冷却(批量投递运行时监听让路)。
  • 跟聊只写 replySentAt,不改 sentAt、不计入每日投递上限;HR 明确拒绝写 hrRejectedAt 并收口该会话。

6.6 内置浏览器真实性与输入通道

  • 入口唯一:electron/preload/stealth.cjs。<webview> preload 跑在隔离世界,改 navigator 对页面无效,因此该模块只产出源码字符串,由 main.cjs::injectStealth 在 did-start-loading + dom-ready 两个时机 executeJavaScript 注入主世界(脚本自带 __bossclawStealth 幂等守卫)。不要再走 preload 阶段注入(实测时机不早于主进程通道)。
  • 画像单一权威:stealth.cjs::DEFAULT_PROFILE(uaChromeVersion / brands / acceptLanguage / languages)一份同时驱动 session.setUserAgent、webRequest.onBeforeSendHeaders、主世界补丁三处。任何一处单独改都会造出「JS 说一套、请求头另一套」的新矛盾;覆盖顺序 默认值 < BOSSCLAW_UA_VERSION < BOSSCLAW_PROFILE_JSON。
  • 升级内核必同步:uaChromeVersion 与 brands(含 grease 品牌名 / 版本,随 Chromium 主版本轮换)必须等于当前内核真实形态(Electron 42 = Chromium 148 → Not/A)Brand/99),resolveProfile() 启动时自检并告警。
  • 逐项开关:BOSSCLAW_STEALTH = off / only=a,b / skip=a,b,项名见 PATCH_KEYS,用于二分定位副作用。
  • 输入通道:文本优先走主进程 sendInputEvent(isTrusted: true);页面内合成事件只作非关键兜底,不得用于发送确认。坐标换算唯一入口 main.cjs::nativeMousePoint。

6.7 定制简历管线

  • 单岗位 tailorJob.ts、批量 / 迭代 tailorBatch.ts,视图在 pages/JobAssistant.tsx + TailorResultView;输出必须挂 source(原文摘录),能力描述语原样保留。
  • 批量导出 PDF 走主进程 electron/resume-pdf-batch.cjs,独占写盘防竞态(wx 打开 + 同名自动编号),草稿异常保留待人工核对。
  • 回归:test:tailor-job / test:tailor-batch / test:tailor-iteration 三支脚本。

6.8 模拟面试

落地状态:2026-10-20 合入 main,未包含在 v2.5.7 安装包(该包 2026-10-14 构建)。

页面 pages/MockInterview.tsx + 引擎 lib/bossclaw/mockInterviewEngine.ts;输入 = 目标岗位 JD +(可选)简历原文。

  • 两段式出题:阶段一按 JD 出基础题;答完阶段一后用你的回答生成针对性深挖题(阶段二),阶段二评分会参考简历原文。
  • 只记录、不逐题评分:每题答完仅落库,全部结束后一次性出报告,避免「边答边被判分」影响临场状态。
  • 评分口径:模型只给档位,分数由本地按五维权重计算 —— relevance / evidence(证据具体性,权重 0.25)/ structure / depth / reflection;N/A 维度不参与加权、剩余权重按比例归一(不按 0 分处理),同一份回答重算结果恒定。回答中引用的原句若无法在你的回答里定位,相关维度按「无证据」压分(防模型替你编造引用)。
  • 语音:mockInterviewRecorder.ts 采集 → mockInterviewAsr.ts 调本地 ASR(speechService.cjs,sherpa-onnx-node,模型下载到 <userData>/speech/models/);朗读走本地 TTS 或 edgeTts.cjs 在线通道(<userData>/speech/settings.json 里可切在线高音质档,默认关);克隆音色由 mockInterviewVoiceRepo.cjs / mockInterviewVoicePack.cjs 管理。
  • 落盘:会话 <userData>/mock-interview/sessions/<id>.json(全量)+ index.json(派生索引,损坏可扫目录重建);题库 <userData>/mock-interview/bank/banks.json + <bankId>.json + index.cache.json(BM25 缓存,可删),内置题库只读 <appPath>/resources/bank/zh-interview-small.json、不复制到用户目录。
  • IPC / 回归:jc:mock-interview-* 共 46 条(会话 / 题库 / 语音 / ASR / TTS);回归 test:mock-interview(评分)、-tts、-asr、-clone、-delivery、-stream,以及 test:voice、test:edge-tts。

七、IPC 与外部通道

7.1 IPC(渲染层 ↔ 主进程,112 条白名单)

分组 代表通道 用途
应用 / 窗口 jc:app-info、jc:window-*(含 jc:window-always-on-top*) 版本信息、自绘标题栏、置顶
webview jc:webview-input(-done)、jc:webview-diag、jc:webview-source、jc:webview-open-link 真实输入、诊断、源码、链接
Camoufox jc:camoufox-call / status / stop / restart Python 桥调度(按平台分发)
CloakBrowser jc:cloak-start / -status / -page-* 隐身浏览器生命周期与页面操作
文件 / 数据 jc:read-doc、jc:save-pdf(-batch)、jc:save-file、jc:save-report-pdf、jc:material-*、jc:qualified-jobs-*、jc:show-item 只读文档、PDF/CSV 导出、素材路径、定位文件
技能 / 代理 jc:skills-*、jc:llm-proxy、jc:fetch-url 技能库读写、LLM 代理、跨域抓取
备份 / 自启 jc:backup-*、jc:autostart-* 本地备份目录与写盘、Windows 登录项
桥 jc:bridge-control、jc:bridge-token OpenClaw 桥启停、运行时令牌
模拟面试 jc:mock-interview-*(46 条) 会话 / 题库 CRUD、题目导入、语音安装与状态、ASR / TTS、导出

硬约束:新增 jc: 通道必须同时加入 preload/app.cjs::ALLOWED_IPC_CHANNELS,否则运行时抛「拒绝非白名单 IPC 通道」。不要把安全敏感能力做成无白名单的通用 invoke。 已知例外:jc:save-pdf-batch* 仍由 preload 直接 invoke,未过 assertIpcChannel(待统一,不应作为新通道的样板)。

7.2 本地控制桥(外部 Agent / MCP)

  • 端点:GET /health(免令牌,仅存活探测)、GET /state(渲染层实时快照,支持 path 裁剪)、POST /action(白名单动作)。
  • 开启:BOSSCLAW_CONTROL=1 或 --control-bridge;--no-control-bridge / BOSSCLAW_CONTROL=0 优先关闭。start-bossclaw.cmd 默认开启,--no-agent 可关。
  • 白名单:src/lib/controlRuntime.ts 的 handlers(约 60 个动作),分为状态读取、页面导航/主题、配置补丁(patchConfig,DENY 列表外且字段必须已存在)、业务数据写入、任务/队列操作、AI 生成、浏览器只读与白名单操作、统计导出。
  • 禁止:发消息、批量投递、绕验证码、改 SAFETY_LIMITS、直接改写备份文件。发送类动作仅当 config.executionMode === 'auto' 时可用,且复用应用自带安全引擎。
  • CONFIG_DENY 至少包含:executionMode、apiKey、pausedUntil、platforms、activeHours、batchRest、requireSingleJobValidation、singleJobValidationCompletedAt、maxDailySent、dailyTarget。

7.3 MCP 服务器(9 工具 / 3 组)

组 工具 说明
runtime(3) bossclaw_app_start / bossclaw_app_stop / bossclaw_app_status 启停应用(默认带 --control-bridge)、进程与桥健康
control(2) bossclaw_app_state / bossclaw_app_action 读实时快照、执行白名单动作(含截图)
agent(4) bossclaw_agent_tasks / bossclaw_agent_submit / bossclaw_agent_cancel / bossclaw_agent_send 代答领取(领取即心跳)/ 回填 / 取消 / 全自动发送(受 executionMode 守卫)
  • 代答在线判定:最近 90s 内调用过 bossclaw_agent_tasks 才算在线;单任务等待 30s–240s,超时 / 离线 / 取消立即回落本地规则。
  • 只搬运「提示词 ↔ 生成文本」:回填内容仍过事实、口吻、校名、招呼语长度等全部校验;不合规照样被本地规则替换。
  • 验证:cd mcp/bossclaw-mcp && node test/selftest.mjs(协议 + 工具自检)、node test/bridge-e2e.mjs(隔离实例全链路)。

八、安全不变量(不可协商)

不变量 落地位置
未确认本人文字气泡 → 不发附件、不计成功、不跳下一岗位 webview.cjs::confirmOwnMessage / liepin-im.cjs
首次成功投递后强制暂停验收 Workbench.handleDelivered 首次分支
目标会话 / HR 与岗位不一致 → 不发 聊天上下文状态机 + 会话身份
外部网申 / 已投递 → 跳过,不消耗配额 投递预检
风控码 32 / 35 / 36 → 立即停止并交人工 safety.ts::CODE_MAP + 各引擎
环境异常 37 / 38、1006 限速 → 退避,不重试登录 nextCooldownUntil + 采集故障范围
日上限 150、分钟 8、岗位间隔基数 ≥15s(再叠加人类化抖动) limits.ts(agent 不可改)
招呼语 120–250 字、必须求职者口吻、只引用真实事实 greetings.ts + prompts
校名仅 985/211 可披露 schoolTier.ts
不绕过验证码 / 登录验证 / 速率限制 / 账号限制 全链路设计约束;无任何相关实现
控制桥默认关闭、仅回环、令牌鉴权 control-bridge.cjs
Agent 不得改安全参数或代替用户确认 controlRuntime.ts 白名单 + DENY

出现验证码、安全验证、账号异常、页面结构变化导致目标不明确、发送结果无法确认时,立即暂停是设计行为,不是缺陷。


九、扩展指南:新增一个招聘平台

按顺序做,缺一项就会出现「有界面但采不到 / 投不了」:

  1. 平台元数据:src/lib/bossclaw/platforms.ts 增加 PLATFORM_META(名称、品牌色、能力矩阵、deliveryKind、阶段标签、每日上限、外部网申文案)。
  2. 能力双源:camoufox/platforms/capabilities.py::PLATFORM_CAPABILITIES 与 PLATFORM_DELIVERY_KIND 同步,业务判定只用 platformSupports(),禁止硬编码 platform === 'boss'。
  3. 搜索 URL:platformUrls.ts / Python 侧搜索差异声明;参数码值进 camoufox/platforms/filters.py。
  4. DOM 适配:electron/preload/platform-adapters.cjs 增加卡片 / 容器 / 链接 / 详情 / 投递选择器与 jobIdFromUrl;卡片选择器与容器选择器必须同时改(失配会把整页塌成 1 条且不报错)。
  5. 投递动作:platformApply 规格(按钮定位、终态、确认信号);需要 IM 确认的写独立只读模块(参考 liepin-im.cjs)。
  6. Python 差异声明:camoufox/platforms/<platform>.py 只写差异(搜索附加、投递、登录),骨架复用 base.py;注册进 registry.py。
  7. UI:平台筛选 / 标签 / 配额 / 登录卡片由注册层驱动,检查侧栏与设置页是否有硬编码平台列表。
  8. 能力守卫:采集通道注册 visual-collect / collect-control 必须全平台注册(漏注册会让界面静默卡到 15 分钟超时)。
  9. 测试:URL / 解析 / 投递确认各补离线回归;至少跑 webview-send-regression、liepin-im-regression(若为 chat 型)与 Python 单测。
  10. 文档:更新功能指南、安全与合规的平台差异说明。

十、测试与回归(门禁)

交付前必跑(desktop-app/ 下):

npm run typecheck          # tsc -b
npm run build              # tsc -b + vite build
# 或 npm run verify

沙箱内 npm 不可用时直调:node node_modules/typescript/bin/tsc -b + node node_modules/vite/bin/vite.js build。

回归脚本矩阵(desktop-app/scripts/,按改动面选择):

脚本 守住的边界
score-regression.mjs AI 档位 / 本地五维 / 硬约束评分
greeting-regression.mjs 招呼语长度、口吻、校名披露
cooldown-regression.mjs 风控冷却、配额、冷却写入单调合并
delivery-throttle-regression.mjs 双引擎共享 pacer / 计数、分钟与日上限
llm-json-regression.mjs JSON 解析、截断不判成功、二次补齐
context-budget-regression.mjs 上下文预算与分片
css-guard-regression.mjs / theme-vars-regression.mjs 死类、语义色、主题变量悬空
pdf-parse-regression.mjs / doc-parse-regression.mjs PDF / 旧版 .doc 解析(字体流、分词回溯、正文还原)
webview-send-regression.mjs BOSS 文字发送状态机(真 Chromium,可用 BOSSCLAW_TEST_BROWSER 指向本机 Chrome)
webview-native-send-regression.mjs 真 Electron + Playwright 原生输入(仅 Windows 真机/CI)
liepin-im-regression.mjs 猎聘 IM 发送与「新增本人消息」确认
zhaopin-jd-regression.mjs / liepin-jd-regression.mjs 详情 JD 补齐与解析
tailor-*-regression.mjs 定制简历(单岗位 / 批量 / 迭代)保真与结构
mock-interview-*-regression.mjs / -smoke.mjs 模拟面试评分(同答重算恒定)、出题 / 深挖、ASR / TTS / 克隆音色、交付节奏
voice-*-regression.mjs / edge-tts-*-regression.mjs / tts-*-regression.mjs 音色仓库与音色包、在线 TTS 协议、流式与 PCM 输出
jsx-key-regression.mjs / humanize-regression.mjs UI key 与人类化行为

Python 侧:

cd desktop-app
python -m unittest discover -s camoufox/tests -p "test_liepin_delivery.py"
python -m unittest discover -s camoufox/tests -p "test_chat_context.py"

判据:脚本失败 = 回归未通过,不算「环境问题跳过」;真机专用脚本(webview-native-send)缺 Electron 运行时按失败处理。


十一、构建、打包与发布

11.1 常用命令(desktop-app/)

命令 作用
npm run dev Vite dev server(5173)
npm run dev:electron 构建 renderer 并以 Electron 打开
npm run typecheck / build / verify 类型检查 / 构建 / 二者组合
npm run package Windows NSIS + 便携版
npm run package:portable / package:dir 仅便携版 / 仅解压目录
npm run package:linux / package:mac / package:all 对应平台产物(macOS 只能在 macOS 构建)

11.2 环境变量

变量 作用
BOSSCLAW_DEBUG=1 写 userData/bossclaw-debug.log,启用白屏 / webview 诊断日志
BOSSCLAW_CONTROL=1 开启本地控制桥(等价 --control-bridge;=0 显式关闭优先)
BOSSCLAW_CONTROL_PORT 覆盖控制桥端口 17650(被占用自动 +1…+9)
BOSSCLAW_STEALTH 反检测补丁开关:off / only=a,b / skip=a,b
BOSSCLAW_UA_VERSION / BOSSCLAW_PROFILE_JSON 覆盖浏览器画像(UA 版本 / 整份画像,后者优先级最高)
BOSSCLAW_TEST_BROWSER 发送回归脚本指向本机 Chrome/Edge 可执行文件
ELECTRON_MIRROR 构建期加速 Electron 二进制下载(npm 安装阶段生效)

11.3 打包已知坑

  1. Electron 二进制下载失败(app-builder.exe 走代理异常):用已下载好的官方 zip 绕过:
    node node_modules/electron-builder/cli.js --win -c.electronDist=tmp/electron-upgrade
    日志出现 resolved electronDist 即命中;产物等价标准路径。
  2. 依赖三处一致:package.json / package-lock.json / node_modules。当前锁为 Electron 42 / electron-builder 26;若 npm ci 后版本回退,先修 lock 再打包(否则会打出 Electron 31 的包)。
  3. 产物:release/BossClaw-<version>-x64.exe、BossClaw-<version>-portable.exe、win-unpacked/。

11.4 文档发布(Wiki)

  • GitHub Wiki 是独立仓库 Boss-Claw.wiki.git(默认分支 master),页面源文件由开发者本地 docs/wiki/ 维护(docs/ 不随仓库分发,见文首说明),含 _Sidebar.md。
  • 同步 = 覆盖页面 → 用仓库 noreply 身份提交 → 推送 wiki 仓库;缺 _Sidebar.md 会被镜像删除导航。
  • 主仓库的 docs/ 被 .gitignore 忽略,README 不得链接 docs/...(公开页会 404)。
  • 推送必须带本机代理,并用 noreply 身份,否则报 push declined due to email privacy restrictions。
  • 发布后可用 https://raw.githubusercontent.com/wiki/<owner>/<repo>/<Page>.md 校验是否已上线。

11.5 可选依赖安装

隐身引擎(Camoufox / Playwright)——需 Python 3.10+:

cd desktop-app/camoufox
python -m venv .venv
.venv\Scripts\pip install -r requirements.txt
.venv\Scripts\python -m camoufox fetch   # 必装:Camoufox 原生内核(~150MB),本地 Chrome/Edge 不可复用

CloakBrowser——首次启用时下载隐身 Chromium(~200MB)到用户目录并校验签名;离线环境按包内说明指向本地二进制。两者都是可选增强,不装不影响 BOSS 默认通道。


十二、排障手册

现象 最可能原因 处理
启动白屏 preload 未注入 / renderer 资源缺失 / 沙箱 GPU 看 userData/bossclaw-debug.log(BOSSCLAW_DEBUG=1);确认 dist/ 已构建;无 GPU 环境加 --no-sandbox
采不到岗位、候选只有 1 条 卡片选择器与容器选择器失配(整页塌成 1 条,不报错) 用离线 DOM 样本跑解析;两处选择器同时改;核对 CSS Modules 驼峰类名
界面一直转圈到 15 分钟 某个平台漏注册 visual-collect / collect-control,或 preload 未注入 检查通道是否全平台注册;看 describeProbe() 诊断
提示未登录但页面已登录 判定用了「登录墙 或 无卡片」 必须是 loginWall && cards <= 0 双条件;确认会话分区未被清空
webview 高度只剩约 150px 改了 display(应为 flex) 恢复 display:flex;尺寸变化走 ResizeObserver + force-resize
聊天发送「成功」但实际没发 确认逻辑用了长度阈值 / 整页文本兜底 只能认「本次新增完整本人气泡」;检查编辑器/面板/按钮是否唯一
猎聘投递超时且标签被关 needsReview 分支晚于 closeTab 保留标签交人工;超时上限 55s
投递频率突然翻倍 某引擎自建了 ActionPacer 全部改用 sharedPacer();检查冷却是否被裸赋值覆盖
触发风控后仍在投 冷却被新值缩短 冷却只走 nextCooldownUntil 单调合并;检查 isLockedOut 是否被绕过
AI 请求 400(思考参数) 给不支持的模型发了 thinking 查 thinkingCapability.ts;未验证模型一律 unsupported
AI 返回半截 JSON 被当成功 截断后做了宽松截尾 只接受完整 JSON;finish_reason=length 抬 max_tokens 重试一次,仍失败抛 AI_TRUNCATED
外部 Agent 连不上(401) 令牌文件不匹配 / 用了旧实例令牌 读 <userData>/control-bridge.json 的当前 token;确认没有第二个实例抢占端口
MCP 工具全部超时 应用未以 --control-bridge 启动,或渲染层未装控制运行时 用 start-bossclaw.cmd 启动;先跑 bossclaw_app_status
打包报 Bad Gateway 拉 Electron app-builder 代理行为异常 用 -c.electronDist= 指向本地已下载 zip(见 11.3)

十三、术语表

术语 含义
待确认 / 待投递 / 投递中 / 已投递 对应 pending / approved / approved_queue / sent,UI 只用这组词
硬约束 本地确定性拦截(学历、薪资、城市、过期、面试方式等),不消耗 AI
四层裁决 硬门槛 → 优先条件 → 职责信号 → 团队信号,一次给出档位分
投递锁 按岗位 + 平台的互斥认领,防双引擎重复投递
pacer 账号级动作限速器(8 次/分钟上限),两套引擎共享
词级断点 (平台, 城市, 关键词) 维度的续采进度;页级断点已禁用
登录墙 平台未登录的信号;必须与「零卡片」同时成立才判未登录
收口 停止当前平台/队列并交人工,不自动重试
agent 代答 未配置 API Key 时,由外部 Agent 用自有模型回答应用内 AI 请求

本文与代码同步维护:改模块口径、状态、限额、端口、工具数量时,请在同一提交内更新对应章节;docs/wiki/ 是 Wiki 发布源,docs/ 其余文件为本地历史资料。

Clone this wiki locally