Shobr: 通过浏览器自动化、事件溯源和LLM的命令行求职工具

Lobsters Hottest 工具

摘要

Shobr是一个开源的CLI工具,它使用浏览器自动化、事件溯源和LLM来自动化求职搜索,采用隐秘性和人在回路的原则设计。

<p><a href="https://lobste.rs/s/bslggr/shobr_job_seach_cli_via_browser">评论</a></p>
查看原文
查看缓存全文

缓存时间: 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。

然后,按顺序:

  1. 运行 shobr setup 以在 $XDG_CONFIG_HOME/shobr/config.toml 下生成SHOBR配置文件模板、配置文件模板,并使 shobr 的 beachpatrol 命令可用(通过将其符号链接到预期文件夹)。填写配置文件。
  2. 确保你有一个已登录LinkedIn的 beachpatrol 配置文件。将该*beachpatrol 配置文件名称*填写到 config.toml 的 beachpatrol_profile 键值中。
  3. 对于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智能体订阅)。
  4. 对于SHOBR中需要读取主简历的部分,你可以通过环境变量 SHOBR_MAIN_CV_PATH 指定其路径(或参见下一步)。
  5. 对于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服务条款的风险由你承担。

相似文章

Browse.sh

Product Hunt

Browse.sh 是一款为AI代理提供网络任务自动化操作记忆的工具。