一站式招聘自动化工具:简历邮件生成 | 邮件营销活动 | LinkedIn 自动化
Project description
talent-whale
一站式招聘自动化工具包:基于 LLM 的简历邮件生成、Zmail 邮件营销活动、LinkedIn Recruiter 自动化消息发送,以及邮件数据后处理。
功能特性
-
简历邮件生成 (
email_gen)- 读取候选人简历(
.txt/.json),调用 LLM 匹配职位并生成销售推介邮件 - 支持自定义 Prompt 模板文件
- 批量模式(URL 列表)+ 单文件模式,支持断点续传
- 读取候选人简历(
-
邮件营销活动 (
campaign)- 从 Excel 读取候选人与收件人信息,通过 Zmail API 批量发送邮件
- 随机延迟、幂等发送(已发邮件自动跳过)
- 可扩展的抽象接口(EmailSender / EmailRepository / CandidateSource)
-
LinkedIn 自动化 (
linkedin)- Selenium 驱动 LinkedIn Recruiter,自动发送 InMail
- diskcache + JSON 双重去重,断点续传
- 职位关键词过滤、忽略名单支持
-
数据后处理 (
data)- 批量读取 JSON 邮件文件,清洗签名,整合输出为 Excel
-
统一 CLI(Typer + Rich)
- 彩色输出、进度显示、统计表格
- 所有参数支持命令行覆盖
安装和环境配置
基础安装
pip install talent-whale
配置 LLM(推荐 .env 方式)
cp .env.example .env
# 编辑 .env,填写真实 API Key
.env 文件内容:
TALENT_WHALE_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxx
TALENT_WHALE_API_URL=https://api.deepseek.com/v1/chat/completions
TALENT_WHALE_MODEL=deepseek-chat
TALENT_WHALE_TIMEOUT=60
使用示例和代码片段
Python API
from talent_whale import chat, load_config
# 方式一:通过 .env 文件自动加载(推荐)
reply = chat("用 Python 实现二叉树的层序遍历")
print(reply)
# 方式二:显式传入配置(覆盖 .env)
cfg = load_config(
api_key="sk-xxxx",
api_url="https://api.deepseek.com/v1/chat/completions",
model="deepseek-chat",
timeout=60,
)
reply = chat("解释 RAG 架构", config=cfg)
print(reply)
from talent_whale import read_txt_data, parse_json_safely, clean_llm_response
from talent_whale.utils.io import read_txt_to_list, save_json_to_file, ensure_directory_exists
from talent_whale.utils.text import extract_linkedin_id, extract_filename_without_extension
# 读取文本文件
content = read_txt_data("data/resume.txt")
# 安全解析 JSON
data = parse_json_safely('{"name": "whale", "version": 1}')
# 清理 LLM 返回的代码块标记
raw = '```json\n{"result": "ok"}\n```'
clean = clean_llm_response(raw) # '{"result": "ok"}'
# 提取 LinkedIn 用户 ID
uid = extract_linkedin_id("https://www.linkedin.com/in/johndoe/")
# 'johndoe'
# 按行读取文件
urls = read_txt_to_list("urls.txt")
# 确保目录存在并保存 JSON
ensure_directory_exists("output/emails")
save_json_to_file({"key": "value"}, "output/emails/result.json")
CLI 命令
1. 简历邮件生成(批量,使用默认模板)
# 无需 --template-file,自动使用内置 DEFAULT_PROMPT_TEMPLATE
whale email generate batch \
urls.txt \
./email_output \
--type json \
--model deepseek-v3.1-terminus
2. 简历邮件生成(批量,自定义模板)
whale email generate batch \
urls.txt \
./email_output \
--template-file my_prompt.txt \
--type json \
--model deepseek-v3.1-terminus
3. 查看默认 Prompt 模板
# 查看完整模板内容(含职位列表)
whale email show-template
# 仅查看模板格式说明(跳过职位列表)
whale email show-template --compact
4. 导出 Prompt 模板
将内置 DEFAULT_PROMPT_TEMPLATE 导出为本地文件,方便自定义编辑后配合 generate 命令使用。
# 导出到默认路径 ./prompt_template.txt
whale email export-template
# 导出到指定路径
whale email export-template -o my_template.txt
# 强制覆盖已有文件
whale email export-template -o my_template.txt -f
# 使用导出的自定义模板生成邮件
whale email generate batch urls.txt out/ -p my_template.txt
5. 简历邮件生成(单文件)
whale email generate single \
./resume_json/johndoe.json \
./email_output
6. 邮件营销活动
whale campaign run \
--email sender@example.com \
--password YOUR_PASSWORD \
--tag-filter agent \
--excel email_list.xlsx \
--json-path ./resume_json \
--log-folder ./email_log \
--delay-min 10 \
--delay-max 30
7. LinkedIn 自动化发消息
whale linkedin run \
--excel ./lk_email_gen.xlsx \
--tag-filter agent \
--cache-dir ./cache_dir \
--checkpoint ./urn/checkpoint.json \
--recruiter Michael
8. 邮件数据后处理
whale data process ./email_ms_lk_json output.xlsx
API 接口说明
talent_whale.chat(prompt, model=None, config=None) -> str
调用 LLM 对话接口。config 为 None 时自动从 .env 加载。
talent_whale.load_config(**kwargs) -> LLMConfig
加载 LLM 配置,优先级:参数 > .env > 内置默认值。
| 参数 | 对应环境变量 | 默认值 |
|---|---|---|
api_key |
TALENT_WHALE_API_KEY |
"" |
api_url |
TALENT_WHALE_API_URL |
https://api.deepseek.com/v1/chat/completions |
model |
TALENT_WHALE_MODEL |
deepseek-chat |
timeout |
TALENT_WHALE_TIMEOUT |
60 |
talent_whale.email_gen.gen_email_tool(...)
批量生成邮件 JSON,可直接在 Python 脚本中调用,参数与 CLI email generate batch 一致。
talent_whale.campaign.EmailCampaignFactory.create_service(config) -> EmailCampaignService
工厂方法,根据 AppConfig 对象组装完整的邮件营销服务。
依赖项清单
必需依赖
| 包 | 用途 |
|---|---|
typer |
CLI 框架 |
rich |
终端彩色输出 |
python-dotenv |
.env 配置加载 |
pandas |
Excel 读写 |
openpyxl |
Excel 引擎 |
requests |
HTTP 请求(Zmail API) |
tenacity |
重试机制 |
llmdog |
LLM 调用封装 |
larkfunc |
文件 I/O 与文本工具函数 |
selenium |
浏览器自动化(LinkedIn) |
diskcache |
URN 与去重持久化缓存 |
browser-dog |
LinkedIn Talent 登录会话管理 |
技术架构与设计原理
总体架构
talent_whale/
├── __init__.py # 公开 API 入口,重导出核心函数
├── cli.py # 根 CLI 入口,注册 4 个子命令组
├── config.py # 配置管理:.env + CLI 参数双通道
├── output.py # Rich 输出:全局 Console 实例 + 辅助函数
├── data_cli.py # 数据处理子命令
├── llm/ # LLM 调用抽象层
│ ├── client.py # 封装 llmdog.chat + llmdog.config.load_config
├── utils/ # 工具函数层
│ ├── io.py # 从 larkfunc 重导出:文件读写工具
│ ├── text.py # 从 larkfunc 重导出:文本解析工具
│ └── data.py # 自实现:邮件签名清洗 / JSON → Excel
├── email_gen/ # 简历邮件生成模块
│ ├── generator.py # LLM 邮件生成核心逻辑(含重试)
│ └── cli.py # CLI 子命令:batch / single
├── campaign/ # 邮件营销活动模块
│ ├── models.py # 数据模型(frozen dataclass + 预设模板)
│ ├── service.py # 抽象接口层 + EmailCampaignService 核心编排
│ ├── factory.py # 具体实现 + EmailCampaignFactory
│ └── cli.py # CLI 子命令:run
└── linkedin/ # LinkedIn 自动化模块
├── models.py # 数据模型:候选人、消息、自动化配置
├── parser.py # LinkedIn 页面解析器
├── navigator.py # 页面导航 + 元素点击(Selenium)
├── sender.py # 消息发送实现
├── checkers.py # 去重检查器(diskcache / JSON / 组合)
├── orchestrator.py # 核心编排器:9 步完整流程
├── factory.py # 工厂函数:组装编排器
└── cli.py # CLI 子命令:run(BrowserDog 登录)
模块依赖关系(单向无环)
┌─────────┐
│ cli.py │ (根入口,注册子命令)
└────┬────┘
│
┌────────────┼────────────┬────────────┐
▼ ▼ ▼ ▼
email_gen campaign linkedin data
│ │ │ │
└────────────┼────────────┘ │
▼ │
utils ───────────────────────┘
│
▼
llm
│
▼
config
│
▼
output (所有模块依赖,全局 Console)
设计原则:依赖只能向下 — CLI → 业务模块 → utils → llm → config → output。任何反向导入均视为违规。
1. 配置管理层 — config.py
设计思路
采用 python-dotenv + 函数参数双通道 模式,消除对单一环境变量来源的依赖:
.env文件:适合本地开发,避免密钥硬编码- CLI 参数:适合 CI/CD 或需要临时切换账号的场景
- 优先级链:
CLI 参数 > .env 文件 > 内置默认值
核心实现
# config.py — 精简原理
from dotenv import load_dotenv
from dataclasses import dataclass
@dataclass
class LLMConfig:
api_key: str = ""
api_url: str = "https://api.deepseek.com/v1/chat/completions"
model: str = "deepseek-chat"
timeout: int = 60
verify_ssl: bool = True
def load_config(api_key=None, api_url=None, model=None, ...) -> LLMConfig:
_load_env(env_file) # 1. 加载 .env
return LLMConfig(
api_key=api_key or os.getenv("TALENT_WHALE_API_KEY", ""), # 2. 参数覆盖
...
)
关键设计决策:
LLMConfig使用可变dataclass(非 frozen),允许调用方在获取后局部覆盖model_load_env()仅在当前工作目录查找.env,不干扰系统级环境变量- 所有环境变量前缀
TALENT_WHALE_防止命名冲突
2. LLM 客户端层 — llm/client.py
设计思路
将 llmdog 作为底层 API 封装,talent-whale 作为业务适配层。两层职责清晰分离:
| 层级 | 职责 | 包名 |
|---|---|---|
| 底层 API | HTTP 连接、重试、流式输出 | llmdog |
| 业务适配层 | 从 .env 加载配置、提供业务语义的 chat() |
talent_whale |
from llmdog import chat as _llmdog_chat
from llmdog.config import load_config as _llmdog_load_config
def chat(prompt, model=None, config=None) -> str:
if config is None:
config = _load_tw_config() # 自动从 .env 加载
llm_cfg = _llmdog_load_config( # 转换为 llmdog 格式
api_key=config.api_key, api_url=config.api_url, ...
)
return _llmdog_chat(prompt, config=llm_cfg)
3. 简历邮件生成模块 — email_gen/
架构设计
CLI: email generate batch/single
│
▼
gen_email_tool() ← 批量入口:遍历 URL 列表,断点续传
│
▼
process_resume_file() ← 单文件处理流水线
│
┌────┼────┐
▼ ▼ ▼
read call parse
txt LLM JSON
核心业务流程
- 读取:通过
larkfunc.read_txt_data读取简历文件(支持.txt/.json) - 选择 Prompt:若调用方传入
prompt_template则使用自定义模板;否则自动回退到DEFAULT_PROMPT_TEMPLATE - 调用 LLM:将简历文本 + Prompt 模板拼接,调用
llm/client.chat()(最多 3 次重试) - 解析 JSON:
larkfunc.clean_llm_response去除 Markdown 代码块标记,larkfunc.parse_json_safely安全解析 - 保存:
larkfunc.save_json_to_file输出到{output_dir}/{linkedin_id}.json - 断点续传:若输出文件已存在,自动跳过
可扩展性
- Prompt 模板独立:
--template-file参数支持外部文件;未指定时自动使用内置DEFAULT_PROMPT_TEMPLATE(来源 002),可通过email show-template查看 - 模型可切换:
--model参数直接覆盖 LLM 模型,同一套代码可适配 DeepSeek / OpenAI / 本地模型 - 批量化设计:
gen_email_tool接受 URL 列表文件,与process_resume_file(单文件)解耦,二者可独立复用
4. 邮件营销活动模块 — campaign/
设计思路
采用 接口抽象 + 工厂模式,将业务逻辑与具体实现彻底解耦:
┌──────────────────────────────────────────────┐
│ EmailCampaignService │ ← 核心编排(依赖接口,不依赖实现)
│ run_campaign(): │
│ 1. candidate_source.get_candidates() │
│ 2. email_repository.is_sent() 去重 │
│ 3. email_sender.send() 发送 │
│ 4. email_repository.save_log() 记录 │
└───────┬──────────┬───────────┬──────────────┘
│ │ │
┌────▼────┐ ┌───▼────┐ ┌───▼────────┐
│Candidate│ │ Email │ │ Email │ ← 抽象接口(ABC)
│ Source │ │ Sender │ │ Repository │
└────┬────┘ └───┬────┘ └───┬────────┘
│ │ │
┌────▼────┐ ┌───▼────┐ ┌───▼────────┐
│ Excel │ │ Zmail │ │ JsonFile │ ← 具体实现(factory.py)
│ Candidate│ │ REST │ │ EmailRepo │
│ Source │ │ API │ │ │
└─────────┘ └────────┘ └────────────┘
四个抽象接口
| 接口 | 职责 | 当前实现 |
|---|---|---|
CandidateSource |
获取候选人列表 | ExcelCandidateSource (pandas) |
EmailDataSource |
从 LinkedIn ID 查邮箱 | JsonFileEmailDataSource |
EmailSender |
发送邮件 | ZmailEmailSender (REST API) |
EmailRepository |
发送日志存储/去重 | JsonFileEmailRepository |
领域模型(frozen dataclass)
所有配置类使用 @dataclass(frozen=True) 确保不可变,防止运行时意外修改:
@dataclass(frozen=True)
class SenderConfig:
email: str
password: str
api_base_url: str = "http://127.0.0.1:5000/api/zmail/send"
def __post_init__(self):
if "@" not in self.email:
raise ValueError(f"邮箱格式无效: {self.email}")
AppConfig 提供 metatech() / deepresearch() 两个类方法预设,减少重复配置代码。
工厂模式
class EmailCampaignFactory:
@staticmethod
def create_service(config: AppConfig) -> EmailCampaignService:
"""根据 AppConfig 组装完整服务。"""
email_source = JsonFileEmailDataSource(config.data_source)
candidate_src = ExcelCandidateSource(config.data_source, email_source)
sender = ZmailEmailSender(config.sender)
repo = JsonFileEmailRepository(config.repository)
return EmailCampaignService(candidate_src, sender, repo, config.campaign)
核心业务流程 — run_campaign()
遍历候选人
│
├── 无邮箱 ──────────────→ 跳过 (skipped_no_email +1)
│
├── 已发送 ──────────────→ 跳过 (skipped_already_sent +1) ← Repository 去重
│
├── 发送成功 ────────────→ 记录日志 (sent +1) ← Sender + Repository
│
├── 发送失败 ────────────→ 打印错误 (failed +1)
│
└── random.uniform(delay) 延迟(反爬虫)
可扩展性
- 换邮件服务:实现
EmailSender接口即可切换到 SendGrid / AWS SES / SMTP - 换数据源:实现
CandidateSource接口即可从数据库 / CSV / API 读取候选人 - 换存储:实现
EmailRepository接口即可切换到 MySQL / MongoDB / S3 - 配置预设:在
AppConfig上添加新的classmethod即可注册新的账号模板
5. LinkedIn 自动化模块 — linkedin/
设计思路
将复杂的 LinkedIn Recruiter 自动化流程拆解为 6 个独立组件,通过编排器(Orchestrator)串联:
┌─────────────────────────────────────────────────────────┐
│ LinkedInAutomationOrchestrator │
│ │
│ process_candidate_urn() ──→ process_candidate() │
│ │ │ │
│ └────────┬───────────────┘ │
│ ▼ │
│ _run_flow() ← 9 步核心流程 │
│ │
│ 1. navigate_to_profile() ──────▶ navigator │
│ 2. parse_profile() ──────▶ parser │
│ 3. name_evaluator ──────▶ evaluator (可选) │
│ 4. title_evaluator ──────▶ evaluator (可选) │
│ 5. extract_message_threads() ───▶ parser │
│ 6. is_duplicate() ──────▶ duplicate_checker │
│ 7. is_message_button_clickable() ─▶ navigator │
│ 8. open_message_dialog() ──────▶ navigator │
│ 9. send_message() ──────▶ message_sender │
└─────────────────────────────────────────────────────────┘
组件详解
Navigator(页面导航器)
navigate_to_profile():WebDriverWait + EC 等待页面加载click_messages_tab():tenacity指数退避重试(最多 5 次),JavaScript 点击绕过 Selenium 元素拦截open_message_dialog():多定位器回退策略(XPATH → CSS_SELECTOR → data-control-name)is_message_button_clickable():检查按钮disabled属性
DuplicateChecker(去重检查器)
采用 策略模式 + 组合模式:
# 独立策略
DiskCacheDuplicateChecker # 基于 diskcache,自动过期,进程内/跨进程
JsonCheckpointDuplicateChecker # 基于 JSON 文件,仅依赖标准库
# 组合策略(同时写入多个检查器)
CompositeDuplicateChecker # 任意命中即重复,写入时全量同步
两种入口路径
| 方法 | 适用场景 | 流程 |
|---|---|---|
process_candidate_urn() |
URN 已缓存(二次访问) | 读缓存 → 拼接 Talent URL → 发消息 |
process_candidate() |
首次访问普通 LinkedIn URL | 导航主页 → 提取 Talent URL → 缓存 URN → 发消息 |
6. 数据后处理模块 — data_cli.py + utils/data.py
设计思路
纯粹的管道式处理:
JSON 目录 → 遍历文件 → 提取字段 → 清洗签名 → 拼装 DataFrame → 输出 Excel
核心函数 clean_email_signature() 使用正则匹配中文签名变体:
pattern = r"""\s*(祝颂商祺|祝商祺|祝好)[,,]?\s*\n?\s*Michael\s*$"""
cleaned = re.sub(pattern, "", content, flags=re.DOTALL | re.VERBOSE)
输出 Excel 按固定列顺序 [url, match_job_title, recommend_title, email_content, filename] 组织,适合后续导入邮件系统。
7. 输出层 — output.py
设计约束
- 全局唯一 Console 实例:
console = Console(theme=custom_theme),所有模块from talent_whale.output import console - 禁止裸
print():所有输出必须通过console.print()或快捷函数 - 统一主题:
info=cyan,success=bold bright_green,warning=bold bright_yellow,error=bold bright_red,highlight=bold magenta,dim=dim white
辅助函数
| 函数 | 输出样式 |
|---|---|
banner() |
Panel 边框 + 标题 |
stats_table() |
交替底色 Rich Table |
rule() |
水平分隔线 |
info/success/warning/error/dim/highlight() |
单行彩色文本 |
如何保证可扩展性与可维护性
-
接口抽象 + 依赖注入
campaign/和linkedin/均通过 ABC 定义契约,通过构造函数注入具体实现。新增邮件服务商或数据源只需实现接口,无需修改核心逻辑。 -
工厂模式隔离组装复杂度
EmailCampaignFactory.create_service()和create_default_orchestrator()将对象图的创建集中管理,调用方零感知内部依赖。 -
单向依赖无环图 依赖方向严格从 CLI → 业务模块 → utils → llm → config → output,消除循环引用风险。
-
延迟导入(Lazy Import)处理可选依赖 所有 Selenium / diskcache 导入均放在方法内部,模块顶层仅依赖纯 Python 包,确保基础功能(email_gen / campaign / data)在无浏览器环境下正常运行。
-
配置文件外部化
.env管理密钥,pyproject.toml管理依赖,Prompt 模板通过--template-file外部传入,代码与配置彻底分离。 -
frozen dataclass 确保数据不可变
SenderConfig、DataSourceConfig、RepositoryConfig、CampaignConfig、AppConfig全部使用frozen=True,__post_init__中做格式校验,fail-fast 原则。 -
组件化策略与组合优于继承
CompositeDuplicateChecker组合多个检查器;Orchestrator组合 Navigator / Parser / Sender / Checker,而非通过多层继承。 -
统一输出主题 全局
console实例 + 命名颜色规则,保证所有模块视觉一致,新增子命令只需遵循颜色规范即可。
核心 CLI 命令矩阵
| 命令组 | 子命令 | 必需参数 | 关键可选参数 |
|---|---|---|---|
email |
generate batch |
urls_file, output_dir | --template-file, --type, --model |
email |
generate single |
resume_path, output_dir | --template-file, --model |
email |
show-template |
(无) | --compact |
campaign |
run |
--email, --password |
--tag-filter, --excel, --delay-min/max |
linkedin |
run |
(无) | --excel, --tag-filter, --headless, --recruiter |
data |
process |
input_folder, output_excel | (无) |
贡献指南与许可证
贡献指南
- Fork 本仓库,创建 feature 分支
- 遵循代码规范:CLI 使用 Typer,输出使用 Rich(禁止裸
print()) - 提交 Pull Request,描述改动原因与影响范围
许可证
MIT License © talent-whale contributors
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file talent_whale-0.1.7.tar.gz.
File metadata
- Download URL: talent_whale-0.1.7.tar.gz
- Upload date:
- Size: 47.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
606b651f5110ef2ae58b18c51cc094a7b0572f44a7bf6d2497852dba84eacad7
|
|
| MD5 |
0d17d1c3b5dbe3b75b195f85981c9972
|
|
| BLAKE2b-256 |
740add0e2d8557de5146632389bb233e178e9c2ede950dd77a4af43e635295e6
|
File details
Details for the file talent_whale-0.1.7-py3-none-any.whl.
File metadata
- Download URL: talent_whale-0.1.7-py3-none-any.whl
- Upload date:
- Size: 50.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7b78c68175da20f61ba0a97a423a4f8be192b2f8bed65a581293cc5ad89dbf7e
|
|
| MD5 |
e70cfa1dfe239d8ab6613431c0327e0d
|
|
| BLAKE2b-256 |
7052331034f8e73e6a911e4ea921d6afb5d372f0d06f8e2492d323003311fb69
|