Shobr: 通过浏览器自动化、事件溯源和LLM的命令行求职工具
摘要
Shobr是一个开源的CLI工具,它使用浏览器自动化、事件溯源和LLM来自动化求职搜索,采用隐秘性和人在回路的原则设计。
查看缓存全文
缓存时间: 2026/09/28 09:58
sebastiancarlos/shobr
来源:https://github.com/sebastiancarlos/shobr
SHOBR - 模拟人类职业-官僚仪式
最隐蔽、最符合UNIX哲学、带有人类参与环节的求职自动化工具。
shobr 许可证:MIT
https://github.com/user-attachments/assets/47336c4f-1ca8-40e4-869d-5496bf7fae53
介绍
在2026年的求职市场中,已有许多自动化投递工具,其中不乏开源项目。这是我的方案,它依赖于:
beachpatrol(https://github.com/sebastiancarlos/beachpatrol)用于驱动你自己的日常浏览器进行自动化操作roffume(https://github.com/sebastiancarlos/roffume)用于管理简历文件
SHOBR 工作流示意图
设计理念与特性
- 日常浏览器隐身模式: 我们不使用无头浏览器。SHOBR 通过
beachpatrol(https://github.com/sebastiancarlos/beachpatrol)驱动你已登录的现有浏览器。在LinkedIn看来,你只是一个正常点击的用户。 - 人类参与环节: SHOBR 负责准备、提议和验证。它撰写草稿、生成PDF,但最终提交申请始终由你完成。这不是“广撒网”模式,不过你依然可以向任何支持API的祈祷对象祈求好运。
- 轻量级LLM设计: 自动化、高质量地调整简历以适应职位描述确实需要LLM,这方面回旋余地不大。但本项目尽可能减少LLM的使用,且不要求智能体驱动(不像该领域的其他项目)。如果需要更多功能,基于SHOBR让LLM编写技能或MCP服务器应该很简单。
- LLM提供方无关性: 采用LLM抽象层(
any-llm,https://github.com/mozilla-ai/any-llm)。因此,你可以通过OpenAI、Anthropic、本地模型运行SHOBR的LLM步骤,或通过faaah(https://github.com/sebastiancarlos/faaah)劫持本地LLM智能体订阅。 - 事件溯源数据: 所有数据(发现的职位、筛选记录、跟踪信息)都保存在仅追加的JSONL事件日志中,并投影为状态文件。你可以随时中断流水线,或使用新规则重新计算职位推荐得分,不会丢失数据。
- 基于Markdown的简历工具链: 简历使用Markdown编写,并编译为ATS可读的PDF(通过Groff和Pandoc)。所有输出文件按投递分文件夹存放。
- 完整的端到端TDD红绿测试: 使用标准库
unittest构建,无需额外框架。 - 非凭感觉编程: 撰写时约3500行代码(含注释)。在这个领域中这相当少见。
安装
beachpatrol 依赖
本项目唯一硬性依赖是**beachpatrol**(https://github.com/sebastiancarlos/beachpatrol)。你可以将其理解为既适合作日常使用又可完全自动化(通过巧妙的“Playwright封装”方式)的浏览器。
为何需要beachpatrol?求职需要爬取数据。理想情况下,应使用你自己的真实登录凭证进行爬取。那么,还有什么比使用你日常浏览器进行爬取更好的方式来避免检测呢?(只要不违反服务条款,这应该与正常使用几乎无异)。其他“求职自动化工具”要么使用未认证请求或无头浏览器,要么要求你提取/复制认证凭证到它们的自动化浏览器中。我们的beachpatrol方案通过使用你真实的日常浏览器来超越它们。
感兴趣的读者请查阅beachpatrol的README(https://github.com/sebastiancarlos/beachpatrol)。
安装说明
在已配置好beachpatrol的前提下,shobr需要Python >= 3.14并使用uv(https://docs.astral.sh/uv/):
git clone https://github.com/sebastiancarlos/shobr
cd shobr
uv sync # 安装单一运行时依赖 `any-llm-sdk[openai]`
uv tool install . # 将 `shobr` 命令行工具加入 PATH
shobr --help
由于SHOBR利用roffume将Markdown简历编译为PDF,你的系统需要安装标准Unix文本处理工具:groff和pandoc。
然后,按顺序:
- 运行
shobr setup以在$XDG_CONFIG_HOME/shobr/config.toml下生成SHOBR配置文件模板、配置文件模板,并使shobr的beachpatrol命令可用(通过将其符号链接到预期文件夹)。填写配置文件。 - 确保你有一个已登录LinkedIn的
beachpatrol配置文件。将该*beachpatrol配置文件名称*填写到config.toml的beachpatrol_profile键值中。 - 对于SHOBR中需要LLM的部分,
any-llm-sdk从环境变量读取提供方密钥(OPENAI_API_KEY、SHOBR_AI_MODEL和OPENAI_BASE_URL)。自然,你可以通过any-llm-sdk使用任何LLM API提供方(甚至通过faaah(https://github.com/sebastiancarlos/faaah)劫持本地可用的LLM智能体订阅)。 - 对于SHOBR中需要读取主简历的部分,你可以通过环境变量
SHOBR_MAIN_CV_PATH指定其路径(或参见下一步)。 - 对于SHOBR中需要创建简历和申请目录的部分,你需要配置简历工具链。
- 首次到达
tailor步骤时,shobr会询问是否克隆最新版roffume(https://github.com/sebastiancarlos/roffume)到~/shobr-resumes(或在config.toml中将cv_toolchain_dir指向现有检出目录)。该文件夹将保存所有简历变体输入(markdown)和输出(PDF)。 - 然后,主简历默认为
/resume.md(SHOBR_MAIN_CV_PATH可覆盖此设置)。
- 首次到达
SHOBR 流水线与命令行命令
SHOBR 将求职过程分为 5个流水线阶段。你可以运行:
shobr status- 查看每个流水线阶段的详细信息。shobr next- 让SHOBR自动提示你进行整个流水线的下一个逻辑操作(而不是直接运行手动的“管道”命令)。
shobr status 输出示例:
$ shobr status
- DISCOVERY
- Total Leads Found: 59
- Rejected by Filter: 10
- Pending Enrichment: 3
- ENRICHMENT
- Total Enriched: 48
- Rejected by Filter: 2
- Pending Screening: 24
- SCREENING
- Total Screened: 45
- Skipped: 6
- Lacking LLM Review: 1
- Pending Human Review: 23
- LLM Scores: Human Scores:
5: 11 5: 2 (1 to tailor)
4: 9 4: 6 (5 to tailor)
3: 9 3: 4 (4 to tailor)
2: 8 2: 4 (4 to tailor)
1: 8 1: 6
- Pending Tailoring: 14
- TAILORING
- Packages Built: 2
- Pending Review: 0
- TRACKING
- Applied: 2
- Interviewing: 0
- Offer: 0
- Rejected: 0
- Ghosted: 0
- Withdrawn: 0
1. 发现(Discovery)
根据 config.toml 中的关键词和地点爬取LinkedIn职位搜索结果,并通过基础正则表达式预过滤。
shobr discovered- 在不进行网络请求的情况下打印已存储的职位线索摘要。shobr discover- 触发beachpatrol搜索并爬取职位线索。
2. 丰富(Enrichment)
访问单个职位页面,提取完整描述、薪资范围和“快速申请”链接。每次处理一个职位,以控制请求速率,避免触发限制。
shobr enriched- 打印所有已丰富的职位。shobr enrich-next- 获取最旧的未丰富职位的详情页。shobr enrich <job_id>- 获取特定职位的详情。
3. 筛选(Screening)
根据你的个人Markdown档案和不可妥协条件(Deal-breakers)对丰富的职位进行评分。
shobr screen-llm-next- 请求LLM为下一个职位线索打分(1-5分)并写出理由。shobr screen-llm-all- 批量运行LLM对所有未评分的职位线索进行评分。shobr screen-next- 记录你自己对下一个职位线索的评判(1-5分及理由),可通过$EDITOR或--score/--reason参数。
4. 定制(Tailoring)
对于标记为“追求(Pursue)”的职位,SHOBR使用LLM重写你的基础 resume.md 以突出相关技能。然后使用 roffume(Groff/Pandoc)工具链确保重写后的简历完美地控制在一页以内,如果超页则循环重写。
shobr tailor-next- 为下一个可追求的职位构建申请包(简历+求职信)。shobr tailored- 打印所有已生成的申请包。
5. 跟踪(Tracking)
对你的申请进行本地看板式跟踪。
shobr track [--note TEXT]- 更新流水线状态(applied(已投递)、interviewing(面试中)、offer(已录用)、rejected(已拒绝)等)。shobr tracked- 打印整个漏斗的高层概览。
配置
SHOBR 关于你的所有信息都存储在 $XDG_CONFIG_HOME/shobr/(默认为 ~/.config/shobr):一个 config.toml 文件和一个 profile/*.md 文件夹。shobr setup 会生成带有说明模板的所有文件。
config.toml # 筛选规则、地理映射、工具链 + 浏览器配置
profile/
user-detail.md fit-criteria.md deal-breakers.md # screen-llm 输入
resume-guide.md cover-guide.md # tailor-only 输入
config.toml
cv_toolchain_dir(必需)
cv_toolchain_dir = "~/shobr-resumes"
简历工具链(即 roffume 的 git 检出目录)的主目录。_主简历_默认为 /resume.md。_简历工具链_目录最终将包含所有生成的简历及其他数据,按照内部的“每个申请”目录组织。
beachpatrol_profile(必需)
beachpatrol_profile = "job-hunter"
持有已登录LinkedIn会话的 beachpatrol 浏览器配置文件。
beachpatrol_browser(默认 "chromium")
beachpatrol_browser = "chromium"
要驱动的 beachpatrol 浏览器类型。
titles(必需,字符串列表)
titles = ["Technical Lead", "Software Engineer", "Senior Software Engineer"]
作为OR组合关键词查询提供给LinkedIn搜索的职位头衔。如“Software Engineer”、“Fullstack Developer”等。
workplace_types(可选,字符串列表)
workplace_types = ["on-site", "hybrid", "remote"]
追加到同一搜索OR查询中。可能的值为:on-site(现场)、hybrid(混合)、remote(远程)。
geo(可选,字符串列表)
geo = ["new-york-city", "san-francisco-bay-area"]
查询的地理目标,通过 [geo_ids] 映射表引用名称。
[geo_ids](可选表,名称 = 纯数字ID)
[geo_ids]
new-york-city = "111111111"
san-francisco-bay-area = "222222222"
将每个地理名称映射到LinkedIn的geoId。名称完全可自定义,但应代表现实地点的名称。你必须在对给定地点进行搜索后,直接从LinkedIn职位URL(geoId=)中获取该ID。请注意,LinkedIn经常为同一地点(城市 vs 大都市区)提供多个ID。
reject_employment_type(可选列表,可为空)
reject_employment_type = ["Internship"]
在丰富阶段拒绝的就业类型。可能的值为:Full-time(全职)、Part-time(兼职)、Contract(合同制)、Temporary(临时)、Internship(实习)。
presence_locations(可选列表,可为空)
presence_locations = ["New York"]
可接受的现场工作地点。值为地点名称的字面字符串(不区分大小写匹配)。远程职位处处通过。“现场”和“混合”职位必须命名列表中包含的地点。
[reject_title](可选表,标签 = Python正则表达式)
[reject_title]
golang = "\\bgolang\\b"
devops = "\\bdevops\\b"
通过预过滤器筛选。与职位名称匹配。职位线索将被拒绝,原因显示为“title contains ‘’”。
profile/*.md 和主简历
LLM阶段将你的档案作为纯markdown文件读取。使用 shobr setup 初始化档案模板,然后自行填写。
主简历
你的主简历,作为生成定制简历的基础。可通过 SHOBR_MAIN_CV_PATH 或 /resume.md 引用。
profile/user-detail.md
比简历更详细的工作经历和技能精通情况。
profile/fit-criteria.md
用你自己的话描述什么样的职位线索值得追求。
profile/deal-breakers.md
否决规则(如果发现匹配,则产生 1 分,意味着丢弃该职位线索)。
profile/resume-guide.md
你关于如何将主简历定制化应用于特定职位的规则和建议。可能包括格式规则。
profile/cover-guide.md
关于如何为特定职位撰写求职信的指南。说明语气、长度等。
简历工具链
SHOBR 依赖 roffume(https://github.com/sebastiancarlos/roffume),一个简历工具链。首次运行 tailor 时会提供克隆选项(克隆一个固定版本)到 ~/shobr-resumes。roffume 并非硬性绑定。SHOBR 通过 简历工具链接口(由 cv_toolchain.py 中的 CvToolchain 抽象类定义的四个方法:scaffold、build、page_check、finalize)与它交互。任何实现该接口的工具都可以通过一些软分叉和修改来替换 roffume。
/ # 默认:~/shobr-resumes
resume.md # 主简历(除非被 SHOBR_MAIN_CV_PATH 指向其他位置)
resume.pdf # 构建的主简历
applications/ # 每个定制职位一个目录
<app-id>/
resume.md # 定制简历(重写直至适合一页)
cover-letter.md # 生成的求职信
notes.md # 来源职位URL
*.pdf # 构建的输出文件
SHOBR 文件结构
shobr/
pyproject.toml
README.md
test.py # 端到端测试套件
test-fixtures/ # 为E2E测试提供支持的合成HTML fixtures(模拟数据)
src/shobr/
beachpatrol-commands/ # beachpatrol 命令(.js 文件)
templates/ # LLM 提示、档案脚手架、配置默认值
core.py # 跨功能核心
cli.py # 参数解析 + 入口点
browser.py # beachpatrol 集成
ai.py # 最小LLM提供方集成
notification.py # 通知(未连接的职位源,非阶段)
discovery.py # 发现阶段
enrichment.py # 丰富阶段
screening.py # 筛选阶段
tailoring.py # 定制阶段(简历工具链契约)
tracking.py # 跟踪阶段
pipeline.py # next/调度器
config.py # config.toml 加载 + 验证
color.py # 终端调色板
应用数据($XDG_DATA_HOME/shobr/)
SHOBR 使用事件溯源模式。每个流水线阶段都有一个仅追加的 events.jsonl 日志,通过回放生成当前状态的 .json 投影。
notifications/
events.jsonl -> notifications.json # 通知队列
discovery/
events.jsonl -> discovery.json # 发现阶段
enrichment/
events.jsonl -> enrichment.json # 爬取的职位详情
screening/
events.jsonl -> screening.json # LLM 和人工评分
tailoring/
events.jsonl -> tailoring.json # 简历生成状态
tracking/
events.jsonl -> tracking.json # 看板漏斗状态
smoke/
linkedin-homepage.html # 冒烟测试-浏览器转储
已知限制
- 仅支持LinkedIn。
与该领域的其他工具不同,本项目仅专注于LinkedIn(你好,LinkedIn法务团队!)。话虽如此,只要需求(或作者的需求)普遍,对代码库进行“鲍勃大叔式”重构以抽象出其他提供方应该不难。 - LinkedIn DOM漂移最终会破坏功能。
提取依赖于LinkedIn的标记(data-testid、卡片键、药丸图标)。当其改变时,命令会按设计大声失败且不写入任何内容。本人希望按需修复此问题。毕竟,如果LLM能黑进Hugging Face,它们也能轻松在几分钟内帮我弄清新的DOM结构。 - 硬性要求
beachpatrol。
无未认证或无头模式。你需要beachpatrol驱动一个已登录LinkedIn的真实浏览器(最好是你的日常浏览器,以自然扩展到所有自动化需求,并提供尽可能多的人类行为信号)。 - 无数据库。
状态是扁平JSON文件,而非数据库。在当前规模下,这实际上是件好事。 - 无调度器。
节奏控制是手动的(next及类似命令每次调用仅执行一次)。你可以自由地通过cron作业、systemd定时器,甚至你手机控制的AI集群在Hetzner数据中心挖矿来自动化它。
安全考虑
- 遵守LinkedIn服务条款的风险由你承担。
相似文章
@browserbase: 推出 http://Browse.sh,这是最大的开源技能目录,可可靠地执行互联网上的任何任务。W…
Browserbase 推出了 browse.sh,这是一个开源 CLI 工具,提供预构建的技能目录,帮助 AI 代理自动化各种网站,降低 token 成本。
Browse.sh
Browse.sh 是一款为AI代理提供网络任务自动化操作记忆的工具。
@TimJayas:重磅:Claude Opus 4.7 现在能全自动帮你找工作!有人做了个工具 > 扫描顶级公司职位 > 自动填表 > 针对每份岗位重写简历 无需猎头,不用海投200份雷同简历
重磅:Claude Opus 4.7 现在能全自动帮你找工作!有人做了个工具 > 扫描顶级公司职位 > 自动填表 > 针对每份岗位重写简历 无需猎头,不用海投200份雷同简历
browser-search —— 三款工具,零成本,让你的AI助手学会搜索和浏览网络
browser-search 是一个开源、自托管的工具集,包含三款工具(SearXNG、Camofox、CloakBrowser),让AI助手无需API密钥或订阅即可搜索和浏览网页,并具备自动导航升级和深度研究能力。
@heyshrutimishra: 运行浏览器代理最昂贵的部分不是任务本身,而是任务之前的一切。导航、查找…
Webcmd 是一个开源工具,它教会浏览器代理一次性学习网站导航并重复使用,从而大幅减少重复任务中的token浪费。它支持99个网站,并可在本地运行。