Metadata-Version: 2.4
Name: github-reachout
Version: 0.1.0
Summary: GitHub 人才触达工具：获取 GitHub 信息 → 总结技术方向 → 生成触达邮件 → 发送
Author-email: Chandler <275737875@qq.com>
License: MIT
Keywords: github,recruiting,outreach,email,langgraph
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: typer>=0.9.0
Requires-Dist: openai>=1.0.0
Requires-Dist: requests>=2.28.0
Requires-Dist: langgraph>=0.0.40
Requires-Dist: python-dotenv>=1.0.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: ruff>=0.1.0; extra == "dev"
Dynamic: license-file

# github-reachout

> **GitHub 人才触达自动化工具** — 从 GitHub 信息采集到个性化邮件发送，全流程 LLM 驱动智能编排。

[![PyPI](https://img.shields.io/pypi/v/github-reachout)](https://pypi.org/project/github-reachout/)
[![Python](https://img.shields.io/pypi/pyversions/github-reachout)](https://pypi.org/project/github-reachout/)
[![License](https://img.shields.io/pypi/l/github-reachout)](./LICENSE)

---

## 目录

- [项目简介](#项目简介)
- [环境配置](#环境配置)
- [快速开始](#快速开始)
- [架构设计](#架构设计)
- [模块说明](#模块说明)
- [CLI 命令参考](#cli-命令参考)
- [断点续跑](#断点续跑)
- [项目结构](#项目结构)
- [免责声明](#免责声明)

---

## 项目简介

`github-reachout` 是一款面向技术招聘场景的自动化工具，帮助招聘人员高效触达 GitHub 上的技术人才。

**核心价值**：将原本需要人工逐一完成的「信息采集 → 背景分析 → 邮件撰写 → 校验发送」流程，通过 LLM 驱动的状态图自动编排，实现端到端自动化。

**工作流程**：

```
GitHub 用户列表
      │
      ▼
┌──────────┐    ┌──────────┐    ┌───────────┐    ┌──────────┐
│  获取     │───▶│  丰富     │───▶│  总结      │───▶│  生成     │
│ GitHub   │    │ 网页信息  │    │ 技术方向   │    │ 触达邮件  │
│ 用户信息  │    │          │    │           │    │          │
└──────────┘    └──────────┘    └───────────┘    └──────────┘
                                                       │
      ┌────────────────────────────────────────────────┘
      ▼
┌──────────┐    ┌──────────┐    ┌───────────┐
│  校验     │───▶│  发送     │───▶│  评估      │──▶ 循环 / 结束
│ 邮件质量  │    │ 邮件     │    │ 是否继续   │
└──────────┘    └──────────┘    └───────────┘
```

**设计哲学**：

- **状态驱动**：所有数据在 `ReachoutState` 中单向流动，每个节点只读取和更新自己负责的字段
- **逐步丰富**：从原始 GitHub 数据到最终邮件发送，人员信息在每个步骤中逐步被充实
- **可中断恢复**：基于 Checkpoint 机制，任何步骤中断后均可从断点恢复，不丢失进度

---

## 环境配置

所有配置通过项目根目录下的 `.env` 文件管理。首次使用前，请复制模板并填写：

```bash
cp .env.example .env
```

### LLM 配置

| 变量名 | 必填 | 默认值 | 说明 |
|--------|:----:|--------|------|
| `LLM_API_KEY` | ✅ | — | LLM API 密钥，兼容 OpenAI / DeepSeek / 华为内网等 |
| `LLM_BASE_URL` | ✅ | — | LLM API 地址，如 `https://api.openai.com/v1` |
| `LLM_MODEL` | ❌ | `deepseek-v3.1-terminus-chat` | 模型名称 |
| `LLM_TIMEOUT` | ❌ | `120` | 请求超时时间（秒） |
| `LLM_MAX_RETRIES` | ❌ | `3` | 失败重试次数（指数退避策略） |

### GitHub 配置

| 变量名 | 必填 | 默认值 | 说明 |
|--------|:----:|--------|------|
| `GITHUB_TOKEN` | ❌ | — | GitHub Personal Access Token。不填则匿名请求，速率限制为 60 次/小时 |

### SMTP 邮件配置

| 变量名 | 必填 | 默认值 | 说明 |
|--------|:----:|--------|------|
| `SMTP_HOST` | ❌ | `smtp.gmail.com` | SMTP 服务器地址 |
| `SMTP_PORT` | ❌ | `587` | SMTP 端口（465 自动启用 SSL，其他使用 STARTTLS） |
| `SMTP_USER` | ✅* | — | SMTP 登录用户名（通常为邮箱地址） |
| `SMTP_PASSWORD` | ✅* | — | SMTP 登录密码或授权码 |
| `SENDER_EMAIL` | ❌ | 同 `SMTP_USER` | 发件人邮箱地址 |
| `SENDER_NAME` | ❌ | — | 发件人显示姓名 |

> \* 使用 `--dry-run` 模式时不需要配置 SMTP 凭据。

### 业务配置

| 变量名 | 必填 | 默认值 | 说明 |
|--------|:----:|--------|------|
| `COMPANY_NAME` | ❌ | — | 我方公司名称（用于邮件生成） |
| `SENDER_TITLE` | ❌ | — | 发件人职位 |
| `TARGET_TECH` | ❌ | — | 目标技术方向（用于候选人匹配过滤） |

> CLI 参数优先级高于 `.env` 配置，两者可同时使用。

---

## 快速开始

### 1. 创建虚拟环境

```bash
# 使用 conda 创建 Python 3.10 环境
conda create -n github-reachout python=3.10 -y
conda activate github-reachout
```

### 2. 安装项目

```bash
# 进入项目目录
cd github-reachout

# 以开发模式安装（支持直接运行 gh-reachout 命令）
pip install -e .
```

### 3. 配置环境变量

```bash
# 复制环境变量模板
cp .env.example .env

# 编辑 .env，至少填写以下配置：
#   LLM_API_KEY=your-api-key
#   LLM_BASE_URL=https://api.openai.com/v1
```

### 4. 运行

```bash
# ✅ 试运行（不发送邮件，推荐首次使用时验证配置）
gh-reachout run sak0 -t "AI/ML" -c "MyCompany" -n "Sender" -d

# 正式运行（单人）
gh-reachout run sak0 -t "AI/ML" -c "MyCompany" -n "Sender"

# 正式运行（多人）
gh-reachout run user1 user2 user3 -t "Cloud Native" -c "MyCompany" -n "Sender"

# 输出结果到 JSON 文件
gh-reachout run sak0 -t "AI/ML" -d -o result.json
```

### 试运行模式（--dry-run）

添加 `-d` 或 `--dry-run` 参数后，流程会完整执行（获取信息、分析、生成邮件），但 **不会实际发送邮件**，仅打印邮件接收信息。适合：

- 首次使用时验证 LLM / GitHub 等配置是否正确
- 预览生成的邮件内容
- 测试完整流程而不产生副作用

---

## 架构设计

### LangGraph 状态图

项目基于 [LangGraph](https://github.com/langchain-ai/langgraph) 构建有向状态图，通过条件路由实现灵活的步骤编排：

```
                         ┌──────────────────────────────────────────────┐
                         │                                              │
                         ▼                                              │
                     ┌────────┐                                         │
                     │  plan  │ ◀────────────────────────────────────┐  │
                     └───┬────┘                                      │  │
                         │ 条件路由（plan 决定本轮执行哪些步骤）         │  │
                         ▼                                           │  │
                     ┌────────┐                                      │  │
                     │  fetch │  获取 GitHub 用户信息 + 仓库列表       │  │
                     └───┬────┘                                      │  │
                         ▼                                           │  │
                     ┌────────┐                                      │  │
                     │ enrich │  读取 LinkedIn / 博客 / 个人主页       │  │
                     └───┬────┘                                      │  │
                         ▼                                           │  │
                     ┌───────────┐                                   │  │
                     │ summarize │  LLM 总结候选人技术方向              │  │
                     └───┬───────┘                                   │  │
                         ▼                                           │  │
                     ┌──────────┐                                    │  │
                     │ generate │  LLM 生成个性化触达邮件               │  │
                     └───┬──────┘                                    │  │
                         ▼                                           │  │
                     ┌──────────┐                                    │  │
                     │ validate │  校验邮件 + 技术方向匹配              │  │
                     └───┬──────┘                                    │  │
                         ▼                                           │  │
                     ┌────────┐                                      │  │
                     │  send  │  SMTP 发送邮件                        │  │
                     └───┬────┘                                      │  │
                         ▼                                           │  │
                     ┌───────────┐                                   │  │
                     │ evaluate  │  评估处理进度，决定是否继续循环       │  │
                     └─────┬─────┘                                   │  │
                           │                                         │  │
                 ┌─────────┴──────────┐                              │  │
                 │                    │                              │  │
           continue_loop=True    continue_loop=False                 │  │
                 │                    │                              │  │
                 └──── 回到 plan ─────┘         结束 ────────────────┘  │
```

### 关键设计

| 设计要点 | 说明 |
|----------|------|
| **依赖注入** | `build_graph()` 通过闭包将 LLM、GitHub、Email 等客户端注入到各节点，便于测试和替换 |
| **条件路由** | `plan` 节点决定每轮执行哪些步骤，`evaluate` 节点决定是否继续循环，支持智能跳过已完成步骤 |
| **增量处理** | `fetch` 节点通过 `processed_logins` 实现增量去重，避免重复请求 GitHub API |
| **循环控制** | `evaluate` 节点根据处理进度和 `max_loops` 参数决定是否回到 `plan` 重新规划 |

---

## 模块说明

### `cli.py` — CLI 入口

基于 [Typer](https://typer.tiangolo.com/) 构建的命令行界面，提供 5 个子命令：`run`、`resume`、`list`、`show`、`cleanup`。详见 [CLI 命令参考](#cli-命令参考) 章节。

### `graph.py` — LangGraph 图编排

负责构建和编译 LangGraph 状态图：

- `build_graph(llm, github, web_reader, email_client)` — 注册所有节点和条件路由，编译为可执行图
- `create_initial_state(...)` — 创建初始 `ReachoutState`，包含输入参数和空的结果集合

### `nodes.py` — 节点函数

定义图中的 8 个节点和 3 个路由函数：

| 节点 | 职责 | 依赖组件 |
|------|------|----------|
| `plan` | 规划本轮执行步骤（首轮全量，后续由 LLM 根据状态决策） | LLM |
| `fetch` | 获取 GitHub 用户基本信息 + 仓库列表（增量处理，跳过已处理 login） | GitHub API |
| `enrich` | 读取 LinkedIn / 博客 / 个人主页网页内容，丰富候选人信息 | Web Reader |
| `summarize` | LLM 分析并总结候选人技术方向、主要语言、资历级别 | LLM |
| `generate` | LLM 根据候选人背景生成个性化触达邮件（主题 + 正文） | LLM |
| `validate` | 校验邮件可发送性：无占位符 + 技术方向匹配 + 有公开邮箱 | LLM + 规则 |
| `send` | 通过 SMTP 发送校验通过的邮件 | SMTP |
| `evaluate` | 评估处理进度，决定是否继续循环 | — |

### `llm.py` — LLM 客户端

封装 OpenAI 兼容 API 的轻量客户端：

- 支持 OpenAI / DeepSeek / Ollama 等任何兼容 OpenAI Chat API 的服务
- 内置流式请求 + 指数退避重试机制（针对 429 / Timeout）
- `chat()` — 返回纯文本响应
- `chat_json()` — 自动解析 JSON 响应（支持 markdown 代码块提取）
- 自动处理 `base_url` 末尾 `/v1` 路径，避免与 openai 库重复拼接

### `github_client.py` — GitHub API 客户端

轻量级 GitHub REST API 客户端：

- `get_user(login)` — 获取用户基本信息（bio、location、email 等）
- `get_user_repos(login)` — 获取公开仓库列表（按 star 数降序，跳过 fork）
- `fetch_person(login)` — 一次性获取完整用户信息（基本信息 + 仓库 + LinkedIn 提取）
- `_extract_linkedin(bio, blog)` — 从 bio/blog 字段中提取 LinkedIn URL
- 内置速率控制（请求间隔 0.5s）

### `email_client.py` — 邮件客户端

SMTP 邮件发送与校验：

- `send_email(to, subject, body, html)` — 通过 SMTP 发送邮件（支持 SSL / STARTTLS 自动切换）
- `validate_email_ready(email_body, subject, to)` — 静态方法，校验邮件是否可直接发送：
  - 无占位符（`{name}`、`{{company}}`、`[NAME]`、`<<name>>` 等多种格式）
  - 收件人邮箱有效
  - 主题和正文非空

### `web_reader.py` — 网页阅读器

轻量级网页内容抓取器：

- `fetch_page(url)` — 获取网页并提取纯文本（HTML → Text，正则实现，无外部依赖）
- `read_person_pages(linkedin_url, github_url, blog_url)` — 读取一个人的所有相关网页，合并为结构化文本
- 内置速率控制（请求间隔 1s）
- 内容截断保护（默认最大 8000 字符）

### `checkpoint.py` — 断点续跑

Checkpoint 持久化模块，详见 [断点续跑](#断点续跑) 章节。

### `state.py` — 状态定义

定义 LangGraph 图中流转的 TypedDict 结构：

- `PersonInfo` — 单个 GitHub 用户的完整信息（从基本信息逐步丰富到包含技术方向、邮件等）
- `ReachoutState` — 主状态，包含输入参数、规划信息、执行结果、循环控制、错误记录等

---

## CLI 命令参考

### `gh-reachout run`

执行完整的触达流程。

```bash
gh-reachout run <login_ids...> [OPTIONS]
```

| 参数 | 短选项 | 说明 | 默认值 |
|------|:------:|------|--------|
| `login_ids` | — | GitHub login ID 列表（必填，位置参数） | — |
| `--target-tech` | `-t` | 目标技术方向 | `.env` 中 `TARGET_TECH` |
| `--company` | `-c` | 我方公司名称 | `.env` 中 `COMPANY_NAME` |
| `--sender-name` | `-n` | 发件人姓名 | `.env` 中 `SENDER_NAME` |
| `--sender-title` | — | 发件人职位 | `.env` 中 `SENDER_TITLE` |
| `--max-loops` | `-l` | 最大循环次数 | `3` |
| `--dry-run` | `-d` | 试运行，不实际发送邮件 | `False` |
| `--output` | `-o` | 输出结果到 JSON 文件 | — |

### `gh-reachout resume`

从断点恢复中断的运行。

```bash
gh-reachout resume <run_id> [-d]
```

| 参数 | 说明 |
|------|------|
| `run_id` | 要恢复的运行 ID（必填） |
| `-d, --dry-run` | 以试运行模式恢复 |

### `gh-reachout list`

列出所有可恢复的 checkpoint 运行记录。

```bash
gh-reachout list
```

输出示例：

```
Run ID       技术方向           循环   步骤   人数   已发送
--------------------------------------------------------
bbfdd38e     AI/ML              1      8      1      0
a3f21bc9     Cloud Native       3      24     5      3
```

### `gh-reachout show`

查看某次运行的完整 state（JSON 格式输出）。

```bash
gh-reachout show <run_id>
```

### `gh-reachout cleanup`

删除某次运行的 checkpoint 数据。

```bash
gh-reachout cleanup <run_id>
```

---

## 断点续跑

### 存储结构

每次运行会自动在本地保存 checkpoint，便于中断恢复和数据查看：

```
.github_reachout_checkpoints/
└── {run_id}/
    ├── state.json              # 完整 state 快照（始终指向最新状态）
    ├── step_001_plan.json      # 各步骤完成后的增量快照
    ├── step_002_fetch.json
    ├── step_003_enrich.json
    ├── ...
    └── persons/
        ├── all_persons.json    # 所有候选人汇总数据
        ├── sak0.json           # 每个人的独立数据文件
        └── user2.json
```

### 自动保存机制

- 每个步骤完成后自动保存 checkpoint
- 按 `Ctrl+C` 中断时自动保存当前状态
- 发生异常时自动保存错误状态

### 使用方式

```bash
# 1. 查看所有运行记录
gh-reachout list

# 2. 查看某次运行的详情
gh-reachout show <run_id>

# 3. 从断点恢复运行
gh-reachout resume <run_id>

# 4. 清理不再需要的 checkpoint 数据
gh-reachout cleanup <run_id>
```

---

## 项目结构

```
github-reachout/
├── .env.example              # 环境变量配置模板
├── .gitignore                # Git 忽略规则
├── LICENSE                   # MIT 许可证
├── README.md                 # 项目文档（本文件）
├── pyproject.toml            # 项目打包配置（PyPI 发布用）
├── requirements.txt          # pip 依赖清单
└── github_reachout/          # 核心源码包
    ├── __init__.py           # 包初始化 + 版本号
    ├── __main__.py           # 支持 python -m github_reachout
    ├── checkpoint.py         # 断点续跑持久化
    ├── cli.py                # CLI 入口（Typer）
    ├── email_client.py       # SMTP 邮件客户端
    ├── github_client.py      # GitHub API 客户端
    ├── graph.py              # LangGraph 图编排
    ├── llm.py                # LLM 客户端（OpenAI 兼容）
    ├── nodes.py              # LangGraph 节点函数
    ├── state.py              # 状态类型定义（TypedDict）
    └── web_reader.py         # 网页内容抓取与提取
```

---

## 免责声明

1. **GitHub API 使用条款**：使用者应遵守 [GitHub 服务条款](https://docs.github.com/en/site-policy/github-terms/github-terms-of-service) 和 [API 使用政策](https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api)。建议配置 `GITHUB_TOKEN` 以提高速率限制（认证请求 5000 次/小时），避免频繁匿名请求。

2. **邮件发送合规**：批量发送邮件前，请确保遵守所在地区的相关法律法规，包括但不限于：
   - 中国《个人信息保护法》
   - 欧盟 GDPR（通用数据保护条例）
   - 美国 CAN-SPAM Act
   - 其他适用地区的反垃圾邮件法律

3. **隐私保护**：本工具获取的 GitHub 用户信息（邮箱、个人主页等）仅应用于合法的技术招聘目的。请勿将数据用于骚扰、垃圾邮件或其他违反隐私法规的用途。

4. **LLM 生成内容**：邮件内容由 LLM 自动生成，**发送前请务必人工审核**，确保内容准确、得体且符合公司规范。

5. **责任限制**：本项目以 "AS IS" 方式提供，作者不对因使用本工具产生的任何直接或间接损失承担责任。

---

## License

[MIT License](./LICENSE)
# github-reachout

GitHub 人才触达工具：获取 GitHub 信息 → 总结技术方向 → 生成触达邮件 → 发送

## 安装

```bash
pip install github-reachout
```

## 快速开始

1. 复制 `.env.example` 为 `.env` 并配置环境变量
2. 运行触达流程：

```bash
gh-reachout run user1 user2 user3 -t "AI/ML" -c "MyCompany" -n "SenderName"
```

## 命令

- `run` — 执行触达流程
- `resume` — 从断点恢复
- `list` — 列出所有 checkpoint
- `show` — 查看某次运行详情
- `cleanup` — 删除 checkpoint
