Metadata-Version: 2.4
Name: jobdeer
Version: 0.0.3
Summary: A CLI and Web-based job position management tool with full-text search, Excel import/export, and LLM-assisted entry.
Author-email: Chandler <275737875@qq.com>
License-Expression: MIT
Keywords: job,position,management,cli,tantivy,full-text-search
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Office/Business
Classifier: Topic :: Utilities
Requires-Python: >=3.8
Description-Content-Type: text/markdown
Requires-Dist: typer>=0.9.0
Requires-Dist: rich>=13.0.0
Requires-Dist: tantivy>=0.22.0
Requires-Dist: jieba>=0.42.1
Requires-Dist: openpyxl>=3.1.0
Requires-Dist: flask>=2.3.0
Requires-Dist: filelock>=3.12.0
Requires-Dist: llmdog>=0.0.1
Requires-Dist: larkfunc>=0.1.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: black>=23.0; extra == "dev"
Requires-Dist: ruff>=0.1.0; extra == "dev"
Provides-Extra: semantic
Requires-Dist: sentence-transformers>=2.2.0; extra == "semantic"
Requires-Dist: numpy>=1.24.0; extra == "semantic"

# JobDeer - 智能职位管理系统

> 一个基于 CLI 和 Web 双界面的职位管理工具，集成 Tantivy 全文检索、向量语义搜索、Excel 导入导出，以及 LLM 辅助录入功能。

## 📖 项目简介

JobDeer 是一个专为 HR 和技术团队设计的职位管理工具，支持通过**命令行**和 **Web 浏览器**两种方式进行职位信息的录入、查询、搜索和管理。

### 为什么选择 JobDeer？

- **零配置启动**：无需安装数据库，基于 JSON 文件存储，开箱即用
- **双界面操作**：技术人员用 CLI，HR 用 Web 界面，各取所需
- **智能搜索**：支持关键词、正则表达式、语义相似度三种搜索模式
- **AI 辅助**：集成 LLM 自动从 JD 文本中提取结构化字段
- **数据便携**：支持 Excel 导入导出，便于数据迁移和报表生成

---

## ✨ 功能特性

| 功能 | 说明 |
|------|------|
| 🔍 关键词搜索 | 基于 Tantivy BM25 算法的全文检索，支持中文分词 |
| 🔎 正则搜索 | 使用 Python re 模块进行正则表达式匹配 |
| 🧠 语义搜索 | 基于 Embedding 向量的语义相似度检索（可选） |
| 🎯 按字段精确检索 | 支持 26 个字段的组合 AND 查询 |
| ➕ 职位增删改查 | 完整的 CRUD 操作，自动备份数据 |
| 📊 Excel 导入导出 | 支持 .xlsx 格式的批量导入导出 |
| 🤖 LLM 辅助录入 | 粘贴原始 JD 文本，AI 自动提取结构化数据 |
| 🌐 Web 管理界面 | 基于 Alpine.js + TailwindCSS 的现代化 UI |
| 💻 CLI 命令行工具 | 基于 Typer + Rich 的交互式终端 |
| 🔄 数据自动备份 | 每次修改自动创建备份文件 |
| 🔒 并发安全 | 使用文件锁保证多进程写入安全 |

---

## 🏗️ 项目架构

### 整体架构设计

```
┌─────────────────────────────────────────────────────────┐
│                    JobDeer 应用层                        │
├──────────────────────┬──────────────────────────────────┤
│   CLI 命令行界面      │      Web Web 界面                │
│   (Typer + Rich)     │   (Flask + Alpine.js + Tailwind) │
├──────────────────────┴──────────────────────────────────┤
│                    核心业务层                            │
├──────────────┬──────────────┬──────────────┬────────────┤
│ models.py    │ search.py    │ excel_io.py  │ llm_helper │
│ (数据模型)   │ (搜索引擎)   │ (Excel处理)  │ (AI辅助)   │
├──────────────┴──────────────┴──────────────┴────────────┤
│                    数据存储层                            │
├─────────────────────────────────────────────────────────┤
│  joblist.json  │  tantivy_index/  │  vector_index/      │
│  (JSON文件)    │  (全文索引)      │  (向量索引)          │
└─────────────────────────────────────────────────────────┘
```

### 模块划分说明

| 模块文件 | 职责 | 技术栈 |
|---------|------|--------|
| `jobdeer/models.py` | 数据模型定义、JSON 文件存储、CRUD 操作 | filelock (并发锁) |
| `jobdeer/search.py` | 全文检索、向量语义搜索、按字段搜索 | Tantivy-py、jieba、sentence-transformers |
| `jobdeer/excel_io.py` | Excel 文件导入导出、字段映射 | openpyxl |
| `jobdeer/llm_helper.py` | LLM 辅助文本提取 | llmdog、larkfunc |
| `jobdeer/cli.py` | 命令行入口、交互式命令 | Typer、Rich |
| `jobdeer/web.py` | Web 服务端、RESTful API | Flask |
| `jobdeer/templates/index.html` | Web 前端页面模板 | HTML + TailwindCSS |
| `jobdeer/static/app.js` | 前端交互逻辑 | Alpine.js |
| `jobdeer/static/style.css` | 自定义样式 | CSS |

### 技术栈选型原因

1. **Flask**：轻量级 Web 框架，适合内部工具开发，易于部署
2. **Typer + Rich**：现代化的 CLI 框架，自动生成 help 文档，输出美观
3. **Tantivy-py**：Rust 实现的全文检索引擎，性能比 Whoosh 高 5-10x，活跃维护
4. **jieba**：成熟的中文分词库，与 Tantivy 预分词策略完美配合
5. **sentence-transformers**：提供轻量中文 Embedding 模型，支持语义搜索
6. **Alpine.js**：轻量级前端框架，无需构建工具，直接浏览器运行
7. **TailwindCSS**：实用优先的 CSS 框架，快速构建现代化 UI
8. **openpyxl**：Python 处理 Excel 的标准库，支持 .xlsx 格式
9. **filelock**：保证多进程并发写入 JSON 文件时的数据安全

---

## 🚀 安装与配置

### 环境要求

- Python 3.8+（推荐 3.10+）
- macOS / Linux / Windows

### 快速安装

```bash
# 1. 从 PyPI 安装（推荐）
pip install jobdeer

# 2. 或从源码安装
git clone https://github.com/jobdeer/jobdeer.git
cd jobdeer
pip install -e .
```

### 安装语义搜索支持（可选）

语义搜索功能需要额外安装 sentence-transformers 依赖（约 400MB 模型文件）：

```bash
pip install "jobdeer[semantic]"
```

### 验证安装

```bash
# 查看版本
jobdeer --help

# 测试搜索功能
jobdeer search "测试" --limit 5
```

### 数据目录配置

默认情况下，数据存储在运行目录：

```
./
├── joblist.json          # 职位数据文件
├── joblist_backup_*.json # 自动备份文件
├── tantivy_index/        # 全文检索索引
└── vector_index/         # 向量索引（使用语义搜索时）
```

可通过环境变量自定义数据目录：

```bash
export JOBDEER_DATA_DIR="/path/to/your/data"
```

---

## 📖 使用指南

### CLI 命令行使用

#### 添加职位

```bash
jobdeer add \
  --title "Python后端开发工程师" \
  --department "技术部" \
  --description "负责公司后端系统开发" \
  --company "某某科技" \
  --location "北京" \
  --requirements "熟悉Python, Django, Redis" \
  --category "后端开发" \
  --tech-stack "Python,Django,Redis" \
  --priority "高" \
  --urgency "紧急" \
  --job-level "高级" \
  --salary-range "20k,30k" \
  --contact-person "张三" \
  --contact-email "hr@example.com"
```

#### 查询职位

```bash
# 按 ID 查询
jobdeer get <job_id>

# 列表分页查看
jobdeer list --page 1 --per-page 20
```

#### 搜索功能

**关键词搜索**（默认）：

```bash
jobdeer search "Python后端"
jobdeer search "Python" --limit 10
```

**正则表达式搜索**：

```bash
# 使用 --regex 快捷参数
jobdeer search "Python|Java" --regex

# 或使用 --mode regex（等效）
jobdeer search "Python|Java" --mode regex --ignore-case
```

**语义搜索**（需安装 semantic 依赖）：

```bash
jobdeer search "做AI应用的后端开发" --mode semantic
```

**按指定字段搜索**：

```bash
# 仅搜索职位名称
jobdeer search "Python" --field title

# 仅搜索技术栈
jobdeer search "React" --field tech_stack
```

#### 更新职位

```bash
# 使用 --field 参数指定字段和值
jobdeer update <job_id> \
  --field "priority=非常高" \
  --field "urgency=紧急" \
  --field "salary_range=25k,35k"
```

#### 删除职位

```bash
# 交互式确认
jobdeer delete <job_id>

# 跳过确认
jobdeer delete <job_id> --yes
```

#### 重建索引

每次添加/删除大量职位后，建议重建索引：

```bash
jobdeer rebuild-index
```

#### 启动 Web 界面

```bash
jobdeer web --port 5000 --host 127.0.0.1
```

然后在浏览器访问：`http://127.0.0.1:5000`

#### Excel 导入导出

```bash
# 导出所有职位到 Excel
jobdeer export jobs.xlsx

# 从 Excel 导入职位
jobdeer import_excel jobs.xlsx --conflict skip
```

#### LLM 辅助录入

```bash
# 交互模式：粘贴原始 JD 文本，AI 自动提取
jobdeer llm-add
```

系统会提示你粘贴职位描述文本，按 `Ctrl+D`（Unix）或 `Ctrl+Z` + `Enter`（Windows）结束输入。

---

### Web 界面使用

#### 搜索功能

Web 界面提供两种搜索方式：

**1. 全局搜索（顶部搜索栏）**

- **关键词模式**：输入关键词，使用 Tantivy BM25 算法全文检索
- **正则模式**：输入正则表达式，使用 Python re 模块匹配
- **语义模式**：输入自然语言描述，基于向量相似度匹配

**2. 高级搜索（点击"高级搜索"按钮展开）**

- 列出 26 个可搜索字段，按 5 组分类排列：
  - 基本信息：职位名称、所属部门、公司名称、工作地点、职位描述、任职要求
  - 分类标签：职位类别、标签、技术栈、级别、聘用年份
  - 薪酬福利：薪酬范围、福利
  - 目标画像：目标公司、目标职位、目标产品、目标项目、目标部门、目标角色、目标论文、目标GitHub、目标关键词、目标候选人
  - 联系信息：联系人、联系邮箱、面试官

- 可填写多个字段，之间为 **AND 逻辑**（必须同时满足）
- 高级搜索与全局搜索互斥：展开高级搜索时，全局搜索框自动禁用

#### 职位管理

- **新增职位**：点击"+ 新增职位"按钮，填写表单后保存
- **编辑职位**：点击每行的"编辑"按钮，修改后保存
- **删除职位**：点击每行的"删除"按钮，确认后删除
- **重建索引**：点击"重建索引"按钮，重建搜索索引

---

## 🔌 API 接口说明

### RESTful API

Web 界面基于以下 RESTful API：

#### 获取职位列表

```
GET /api/jobs?page=1&per_page=20
```

**响应示例**：
```json
{
  "jobs": [...],
  "page": 1,
  "per_page": 20,
  "total": 100
}
```

#### 创建职位

```
POST /api/jobs
Content-Type: application/json

{
  "title": "Python后端开发工程师",
  "department": "技术部",
  "description": "负责公司后端系统开发",
  "company": "某某科技",
  "location": "北京"
}
```

**响应**：返回创建的职位对象（HTTP 201）

#### 获取单个职位

```
GET /api/jobs/<job_id>
```

#### 更新职位

```
PUT /api/jobs/<job_id>
Content-Type: application/json

{
  "priority": "非常高",
  "urgency": "紧急"
}
```

#### 删除职位

```
DELETE /api/jobs/<job_id>
```

#### 搜索职位

```
GET /api/search?q=Python&mode=keyword&field=title&limit=100
```

**参数说明**：

| 参数 | 类型 | 必填 | 默认值 | 说明 |
|------|------|------|--------|------|
| q | string | 是 | - | 搜索关键词/正则/语义查询文本 |
| mode | string | 否 | keyword | 搜索模式：`keyword` / `regex` / `semantic` |
| field | string | 否 | 无（全字段） | 指定搜索字段名（仅 keyword/regex 模式有效） |
| ignore_case | bool | 否 | true | 大小写不敏感（仅 regex 模式有效） |
| limit | int | 否 | 100 | 最大返回数 |

**向后兼容**：`regex=true` 参数仍然生效（等价于 `mode=regex`）

**响应示例**：
```json
{
  "jobs": [...],
  "total": 10,
  "mode": "keyword"
}
```

语义模式下，每个 job 额外包含 `_score` 字段（相似度分数 0-1）。

#### 高级搜索（多字段组合）

```
POST /api/search/fields
Content-Type: application/json

{
  "fields": {
    "title": "Python",
    "company": "字节",
    "tech_stack": "React"
  },
  "mode": "keyword",
  "limit": 100
}
```

**响应**：与 GET /api/search 相同格式

#### 重建索引

```
POST /api/rebuild-index
```

**响应**：
```json
{
  "success": true
}
```

---

## 📦 依赖清单

### 核心依赖

| 包名 | 版本要求 | 用途 |
|------|---------|------|
| `typer` | >=0.9.0 | CLI 框架 |
| `rich` | >=13.0.0 | 终端美化输出 |
| `tantivy` | >=0.22.0 | 全文检索引擎（Rust 底层） |
| `jieba` | >=0.42.1 | 中文分词 |
| `openpyxl` | >=3.1.0 | Excel 文件处理 |
| `flask` | >=2.3.0 | Web 框架 |
| `filelock` | >=3.12.0 | 文件锁（并发安全） |
| `llmdog` | >=0.1.0 | LLM 服务客户端 |
| `larkfunc` | >=0.1.0 | LLM 函数调用工具 |

### 可选依赖

| 依赖组 | 包名 | 用途 |
|--------|------|------|
| `semantic` | `sentence-transformers>=2.2.0`, `numpy>=1.24.0` | 语义向量搜索 |
| `dev` | `pytest>=7.0`, `black>=23.0`, `ruff>=0.1.0` | 开发工具 |

### 前端依赖（CDN 引入）

- `TailwindCSS`（v3.x）：CSS 框架
- `Alpine.js`（v3.x）：前端交互框架

---

## 🔍 搜索功能技术原理

### 1. 关键词搜索（Tantivy BM25）

**实现流程**：

1. **写入索引**：职位数据写入时，使用 jieba 对中文文本进行预分词，以空格连接后存入 Tantivy
2. **搜索查询**：用户输入查询词同样经过 jieba 分词，构建 Tantivy 查询语句
3. **BM25 排序**：Tantivy 使用 BM25 算法计算每个文档的相关性得分，按得分降序返回

**中文分词策略**：

```python
# 写入索引前
text = "Python后端开发工程师"
tokens = jieba.cut(text)  # ["Python", "后端", "开发", "工程师"]
indexed_text = " ".join(tokens)  # "Python 后端 开发 工程师"

# 搜索时
query = "后端开发"
query_tokens = jieba.cut(query)  # ["后端", "开发"]
indexed_query = " ".join(query_tokens)  # "后端 开发"
```

**Tantivy 查询语法**：

```python
# 单字段搜索
query = ix.parse_query("后端", ["title"])

# 多字段搜索
query = ix.parse_query("后端 开发", ["title", "description", "requirements"])

# 高级搜索（AND 逻辑）
query_str = "+title:后端 +title:开发 +company:字节"
query = ix.parse_query(query_str, all_fields)
```

### 2. 正则搜索（Python re）

**实现流程**：

1. 使用 `re.compile()` 编译正则表达式
2. 遍历所有职位数据，将每个职位的所有字段拼接为文本
3. 使用 `prog.search()` 匹配，返回所有匹配的职位

**支持选项**：

- `ignore_case=True`：忽略大小写（默认）
- `field="title"`：仅在指定字段搜索

**示例**：

```python
# 匹配包含 Python 或 Java 的职位
search_regex("Python|Java", ignore_case=True)

# 仅搜索 title 字段，忽略大小写
search_regex("^Python", ignore_case=True, field="title")
```

### 3. 语义搜索（向量相似度）

**实现流程**：

1. **向量化**：使用 `sentence-transformers` 的 `shibing624/text2vec-base-chinese` 模型，将所有职位的可搜索字段拼接后生成 768 维向量
2. **存储**：向量矩阵保存为 `vectors.npy`，对应的 job_id 列表保存为 `ids.json`
3. **搜索**：用户查询文本同样生成向量，与所有职位向量计算余弦相似度
4. **排序**：按相似度降序返回 top-N 结果

**余弦相似度计算**：

```python
# 向量已归一化，直接点积即为余弦相似度
scores = vectors @ query_vec  # (N,) 数组
top_indices = np.argsort(-scores)[:limit]
```

**为什么用 text2vec-base-chinese？**

- 轻量模型（约 400MB），适合本地部署
- 支持中英双语，适合技术职位搜索
- 基于 SentenceTransformers，API 简洁

**注意事项**：

- 语义搜索为可选功能，不安装 `sentence-transformers` 不影响其他搜索
- 首次使用会自动下载模型文件（约 400MB），后续缓存到本地
- 每次添加/删除/更新职位后，需同步更新向量索引

---

## 🧩 扩展与维护

### 如何添加新的可搜索字段？

1. 在 `jobdeer/models.py` 的 `CORE_FIELDS` 集合中添加字段名
2. 在 `jobdeer/search.py` 的 `SEARCHABLE_FIELDS` 列表中添加字段名
3. 在 `jobdeer/templates/index.html` 的高级搜索面板中添加对应的输入框
4. 在 `jobdeer/static/app.js` 的 `advancedFields` 对象中添加初始值
5. 执行 `jobdeer rebuild-index` 重建索引

### 如何更换语义搜索模型？

修改 `jobdeer/search.py` 中的 `_get_embedding_model()` 函数：

```python
def _get_embedding_model():
    global _embedding_model
    if _embedding_model is None:
        from sentence_transformers import SentenceTransformer
        # 更换为你想使用的模型
        _embedding_model = SentenceTransformer("your-model-name")
    return _embedding_model
```

**注意**：更换模型后需执行 `jobdeer rebuild-index` 重新构建向量索引。

### 如何添加新的搜索模式？

1. 在 `jobdeer/search.py` 中实现新的搜索函数：

```python
def search_custom(query_str: str, limit: int = 100) -> List[Dict[str, Any]]:
    """自定义搜索逻辑"""
    # 你的实现
    return results
```

2. 在 `jobdeer/web.py` 的 `api_search()` 路由中添加新模式处理：

```python
elif mode == "custom":
    results = search_custom(q, limit=limit)
```

3. 在 `jobdeer/cli.py` 的 `cmd_search()` 中添加新模式处理
4. 在 `jobdeer/templates/index.html` 的下拉选择器中添加新选项
5. 在 `jobdeer/static/app.js` 的 `searchMode` 逻辑中处理新模式

### 如何迁移到其他数据库？

当前使用 JSON 文件存储，适合小规模数据（<10,000 条）。如需迁移到数据库：

1. 修改 `jobdeer/models.py` 中的 `JobStore` 类，替换为 SQLAlchemy 或 ORM
2. 替换 `store.add()`、`store.get()` 等方法为数据库操作
3. Tantivy 索引和向量索引无需修改，它们独立于数据存储

### 性能优化建议

| 场景 | 优化方案 |
|------|---------|
| 数据量 > 10,000 条 | 考虑迁移到 PostgreSQL/SQLite |
| 搜索速度慢 | 检查索引是否最新，执行 `rebuild-index` |
| 语义搜索加载慢 | 预加载模型到内存，或使用更轻量的模型 |
| 并发写入冲突 | filelock 已处理，无需额外配置 |
| Web 响应慢 | 使用 gunicorn 替代 Flask 内置服务器 |

### 代码结构规范

```
jobdeer/
├── __init__.py          # 包入口，导出公共 API
├── models.py            # 数据模型和存储
├── search.py            # 搜索引擎实现
├── excel_io.py          # Excel 导入导出
├── llm_helper.py        # LLM 辅助功能
├── output.py            # CLI 输出工具函数
├── cli.py               # CLI 入口
├── web.py               # Web 服务端
├── templates/
│   └── index.html       # Web 页面模板
└── static/
    ├── app.js           # 前端逻辑
    └── style.css        # 自定义样式
```

**开发建议**：

- 新增功能优先在对应模块中实现
- CLI 和 Web 共用业务逻辑（models/search/excel_io）
- 前端修改只需刷新浏览器（Alpine.js 响应式）
- 每次修改搜索相关代码后，务必重建索引测试

---

## 🤝 贡献指南

欢迎提交 Issue 和 Pull Request！

### 开发环境设置

```bash
# 1. 克隆仓库
git clone https://github.com/jobdeer/jobdeer.git
cd jobdeer

# 2. 安装开发依赖
pip install -e ".[dev,semantic]"

# 3. 运行测试
pytest

# 4. 代码格式化
black jobdeer/
ruff check jobdeer/
```

### 提交 PR 流程

1. Fork 本仓库
2. 创建特性分支：`git checkout -b feature/amazing-feature`
3. 提交修改：`git commit -m 'Add amazing feature'`
4. 推送分支：`git push origin feature/amazing-feature`
5. 提交 Pull Request

---

## 📄 许可证

本项目采用 MIT 许可证。详见 [LICENSE](LICENSE) 文件。

---

## 🙏 致谢

- [Tantivy](https://github.com/quickwit-oss/tantivy) - Rust 全文检索引擎
- [sentence-transformers](https://github.com/UKPLab/sentence-transformers) - 语义向量模型
- [jieba](https://github.com/fxsjy/jieba) - 中文分词
- [Typer](https://typer.tiangolo.com/) - CLI 框架
- [Flask](https://flask.palletsprojects.com/) - Web 框架
- [Alpine.js](https://alpinejs.dev/) - 前端框架
- [TailwindCSS](https://tailwindcss.com/) - CSS 框架

---

**版本**：v1.0 
**更新日期**：2026-04-26  
**作者**：JobDeer Team
