Skip to main content

一站式招聘自动化工具:简历邮件生成 | 邮件营销活动 | LinkedIn 自动化

Project description

talent-whale

一站式招聘自动化工具包:基于 LLM 的简历邮件生成、Zmail 邮件营销活动、LinkedIn Recruiter 自动化消息发送,以及邮件数据后处理。


功能特性

  1. 简历邮件生成 (email_gen)

    • 读取候选人简历(.txt / .json),调用 LLM 匹配职位并生成销售推介邮件
    • 支持自定义 Prompt 模板文件
    • 批量模式(URL 列表)+ 单文件模式,支持断点续传
  2. 邮件营销活动 (campaign)

    • 从 Excel 读取候选人与收件人信息,通过 Zmail API 批量发送邮件
    • 随机延迟、幂等发送(已发邮件自动跳过)
    • 可扩展的抽象接口(EmailSender / EmailRepository / CandidateSource)
  3. LinkedIn 自动化 (linkedin)

    • Selenium 驱动 LinkedIn Recruiter,自动发送 InMail
    • diskcache + JSON 双重去重,断点续传
    • 职位关键词过滤、忽略名单支持
  4. 数据后处理 (data)

    • 批量读取 JSON 邮件文件,清洗签名,整合输出为 Excel
  5. 统一 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 对话接口。configNone 时自动从 .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

核心业务流程

  1. 读取:通过 larkfunc.read_txt_data 读取简历文件(支持 .txt / .json
  2. 选择 Prompt:若调用方传入 prompt_template 则使用自定义模板;否则自动回退到 DEFAULT_PROMPT_TEMPLATE
  3. 调用 LLM:将简历文本 + Prompt 模板拼接,调用 llm/client.chat()(最多 3 次重试)
  4. 解析 JSONlarkfunc.clean_llm_response 去除 Markdown 代码块标记,larkfunc.parse_json_safely 安全解析
  5. 保存larkfunc.save_json_to_file 输出到 {output_dir}/{linkedin_id}.json
  6. 断点续传:若输出文件已存在,自动跳过

可扩展性

  • 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() 单行彩色文本

如何保证可扩展性与可维护性

  1. 接口抽象 + 依赖注入 campaign/linkedin/ 均通过 ABC 定义契约,通过构造函数注入具体实现。新增邮件服务商或数据源只需实现接口,无需修改核心逻辑。

  2. 工厂模式隔离组装复杂度 EmailCampaignFactory.create_service()create_default_orchestrator() 将对象图的创建集中管理,调用方零感知内部依赖。

  3. 单向依赖无环图 依赖方向严格从 CLI → 业务模块 → utils → llm → config → output,消除循环引用风险。

  4. 延迟导入(Lazy Import)处理可选依赖 所有 Selenium / diskcache 导入均放在方法内部,模块顶层仅依赖纯 Python 包,确保基础功能(email_gen / campaign / data)在无浏览器环境下正常运行。

  5. 配置文件外部化 .env 管理密钥,pyproject.toml 管理依赖,Prompt 模板通过 --template-file 外部传入,代码与配置彻底分离。

  6. frozen dataclass 确保数据不可变 SenderConfigDataSourceConfigRepositoryConfigCampaignConfigAppConfig 全部使用 frozen=True__post_init__ 中做格式校验,fail-fast 原则。

  7. 组件化策略与组合优于继承 CompositeDuplicateChecker 组合多个检查器;Orchestrator 组合 Navigator / Parser / Sender / Checker,而非通过多层继承。

  8. 统一输出主题 全局 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 (无)

贡献指南与许可证

贡献指南

  1. Fork 本仓库,创建 feature 分支
  2. 遵循代码规范:CLI 使用 Typer,输出使用 Rich(禁止裸 print()
  3. 提交 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

talent_whale-0.1.7.tar.gz (47.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

talent_whale-0.1.7-py3-none-any.whl (50.3 kB view details)

Uploaded Python 3

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

Hashes for talent_whale-0.1.7.tar.gz
Algorithm Hash digest
SHA256 606b651f5110ef2ae58b18c51cc094a7b0572f44a7bf6d2497852dba84eacad7
MD5 0d17d1c3b5dbe3b75b195f85981c9972
BLAKE2b-256 740add0e2d8557de5146632389bb233e178e9c2ede950dd77a4af43e635295e6

See more details on using hashes here.

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

Hashes for talent_whale-0.1.7-py3-none-any.whl
Algorithm Hash digest
SHA256 7b78c68175da20f61ba0a97a423a4f8be192b2f8bed65a581293cc5ad89dbf7e
MD5 e70cfa1dfe239d8ab6613431c0327e0d
BLAKE2b-256 7052331034f8e73e6a911e4ea921d6afb5d372f0d06f8e2492d323003311fb69

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page