Skip to main content

A CLI and Web-based job position management tool with full-text search, Excel import/export, and LLM-assisted entry.

Project description

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

快速安装

# 1. 从 PyPI 安装(推荐)
pip install jobdeer

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

安装语义搜索支持(可选)

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

pip install "jobdeer[semantic]"

验证安装

# 查看版本
jobdeer --help

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

数据目录配置

默认情况下,数据存储在运行目录:

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

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

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

📖 使用指南

CLI 命令行使用

添加职位

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"

查询职位

# 按 ID 查询
jobdeer get <job_id>

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

搜索功能

关键词搜索(默认):

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

正则表达式搜索

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

# 或使用 --mode regex(等效)
jobdeer search "Python|Java" --mode regex --ignore-case

语义搜索(需安装 semantic 依赖):

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

按指定字段搜索

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

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

更新职位

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

删除职位

# 交互式确认
jobdeer delete <job_id>

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

重建索引

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

jobdeer rebuild-index

启动 Web 界面

jobdeer web --port 5000 --host 127.0.0.1

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

Excel 导入导出

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

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

LLM 辅助录入

# 交互模式:粘贴原始 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

响应示例

{
  "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

响应示例

{
  "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

响应

{
  "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 算法计算每个文档的相关性得分,按得分降序返回

中文分词策略

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

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

Tantivy 查询语法

# 单字段搜索
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 或 Java 的职位
search_regex("Python|Java", ignore_case=True)

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

3. 语义搜索(向量相似度)

实现流程

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

余弦相似度计算

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

为什么用 text2vec-base-chinese?

  • 轻量模型(约 400MB),适合本地部署
  • 支持中英双语,适合技术职位搜索
  • 基于 SentenceTransformers,API 简洁

注意事项

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

🧩 扩展与维护

如何添加新的可搜索字段?

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

如何更换语义搜索模型?

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

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 中实现新的搜索函数:
def search_custom(query_str: str, limit: int = 100) -> List[Dict[str, Any]]:
    """自定义搜索逻辑"""
    # 你的实现
    return results
  1. jobdeer/web.pyapi_search() 路由中添加新模式处理:
elif mode == "custom":
    results = search_custom(q, limit=limit)
  1. jobdeer/cli.pycmd_search() 中添加新模式处理
  2. jobdeer/templates/index.html 的下拉选择器中添加新选项
  3. jobdeer/static/app.jssearchMode 逻辑中处理新模式

如何迁移到其他数据库?

当前使用 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!

开发环境设置

# 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 文件。


🙏 致谢


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

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

jobdeer-0.0.2.tar.gz (39.2 kB view details)

Uploaded Source

Built Distribution

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

jobdeer-0.0.2-py3-none-any.whl (34.2 kB view details)

Uploaded Python 3

File details

Details for the file jobdeer-0.0.2.tar.gz.

File metadata

  • Download URL: jobdeer-0.0.2.tar.gz
  • Upload date:
  • Size: 39.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.3

File hashes

Hashes for jobdeer-0.0.2.tar.gz
Algorithm Hash digest
SHA256 6fc2abae9fbeb4b7314af237e4dd505c1660868f2e220976c45591f617861a1f
MD5 0cdd9ad20241b48819d2b34d2c5379f7
BLAKE2b-256 3104d7520b39f2f9cfd142b57d9369867a6e670ea2d8f07c0c5396b9a02ac36d

See more details on using hashes here.

File details

Details for the file jobdeer-0.0.2-py3-none-any.whl.

File metadata

  • Download URL: jobdeer-0.0.2-py3-none-any.whl
  • Upload date:
  • Size: 34.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.3

File hashes

Hashes for jobdeer-0.0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 aea3d2f8a5aa81673ad311c90aba24d4a8c363a8d8e170a9cbc731d43f94f2a2
MD5 bb1bc11f64b8a44b4ca14110b59300e6
BLAKE2b-256 1c938df32593b10868287776552a162a22df7111379bd8132e24162c1df26e36

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