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 自动提取结构化数据 |
| ⚙️ LLM 配置管理 | 交互式配置向导、API 连接测试、多种配置方式 |
| 🌐 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 |
技术栈选型原因
- Flask:轻量级 Web 框架,适合内部工具开发,易于部署
- Typer + Rich:现代化的 CLI 框架,自动生成 help 文档,输出美观
- Tantivy-py:Rust 实现的全文检索引擎,性能比 Whoosh 高 5-10x,活跃维护
- jieba:成熟的中文分词库,与 Tantivy 预分词策略完美配合
- sentence-transformers:提供轻量中文 Embedding 模型,支持语义搜索
- Alpine.js:轻量级前端框架,无需构建工具,直接浏览器运行
- TailwindCSS:实用优先的 CSS 框架,快速构建现代化 UI
- openpyxl:Python 处理 Excel 的标准库,支持 .xlsx 格式
- 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
# 使用自定义 API 配置
jobdeer llm-add --api-url "https://api.example.com/v1" --api-key "sk-xxx" --model "gpt-3.5-turbo"
系统会提示你粘贴职位描述文本,按 Ctrl+D(Unix)或 Ctrl+Z + Enter(Windows)结束输入。
LLM 配置管理
JobDeer 提供了完整的 LLM API 配置管理命令 jobdeer llm-config,支持多种配置方式。
命令功能
1. --show - 显示当前配置
jobdeer llm-config --show
显示内容:
- 配置文件位置(
~/.llmdog.yaml或./.llmdog.yaml) - 环境变量设置状态(
LLM_API_KEY、LLM_API_URL) - 当前所有配置项(API Key 自动脱敏,只显示前 8 位)
- 配置完整性验证提示
2. --test - 测试 API 连接
jobdeer llm-config --test
功能:
- 使用
test_llm_api()函数验证 API 连接 - 显示测试结果和详细的错误诊断信息
- 提供故障排除建议(API Key 无效、URL 错误、网络问题等)
3. --create - 创建配置文件模板
jobdeer llm-config --create
功能:
- 复制
.llmdog.yaml.example到当前目录 - 检查文件是否已存在,避免意外覆盖
- 提供下一步操作指引
4. --wizard - 交互式配置向导(推荐新用户)
jobdeer llm-config --wizard
功能:
- 逐步引导配置 API URL、API Key、Model、Timeout
- 显示常用配置示例(OpenAI、Azure、Ollama)
- 支持使用默认值(按 Enter 键)
- 配置摘要确认
- 自动保存为 YAML 格式配置文件
- 可选的 API 连接测试
5. 快速设置 - 命令行参数直接配置
jobdeer llm-config --api-url "https://api.example.com/v1" \
--api-key "sk-xxx" \
--model "gpt-3.5-turbo"
功能:
- 自动读取现有配置并更新指定字段
- 保存为 YAML 格式配置文件
- 支持部分更新(只传需要的参数)
# 查看当前生效的配置
jobdeer llm-config --show
# 测试 API 连接
jobdeer llm-config --test
# 创建示例配置文件到当前目录
jobdeer llm-config --create
# 启动交互式配置向导(推荐新用户)
jobdeer llm-config --wizard
# 快速设置配置
jobdeer llm-config --api-url "https://api.example.com/v1" --api-key "sk-xxx" --model "gpt-3.5-turbo"
使用场景示例
场景 1:新用户快速开始
# 方式 1:使用交互式向导(推荐)
jobdeer llm-config --wizard
# 方式 2:创建配置文件后手动编辑
jobdeer llm-config --create
vim .llmdog.yaml
# 方式 3:直接通过命令行设置
jobdeer llm-config --api-url "https://api.openai.com/v1" \
--api-key "sk-xxx" \
--model "gpt-3.5-turbo"
场景 2:日常使用
# 查看当前配置
jobdeer llm-config --show
# 测试配置是否有效
jobdeer llm-config --test
# 使用 LLM 辅助录入(自动使用已配置的 API)
jobdeer llm-add
场景 3:高级用法
# 临时使用不同的 API(不修改配置文件)
jobdeer llm-add --api-url "http://localhost:11434/v1" \
--api-key "ollama" \
--model "qwen2.5:32b"
# 使用环境变量覆盖配置
LLM_API_KEY="sk-different-key" jobdeer llm-config --show
# 测试不同的 API 提供商
jobdeer llm-config --api-url "https://api.anthropic.com/v1" --api-key "sk-ant-xxx"
jobdeer llm-config --test
配置优先级
llmdog 的配置加载遵循以下优先级(从高到低):
1. 命令行参数 (--api-url, --api-key, --model)
↓
2. 环境变量 (LLM_API_URL, LLM_API_KEY, LLM_MODEL)
↓
3. 配置文件 (~/.llmdog.yaml 或 ./.llmdog.yaml)
↓
4. 内置默认值
重要说明:
- 环境变量优先级高于配置文件
- 命令行参数优先级最高,可临时覆盖所有配置
- 配置文件支持 YAML 和 JSON 格式
配置文件格式
使用 YAML 格式(通过 PyYAML 库):
# API 配置(必填)
api_key: "sk-your-api-key-here"
api_url: "https://api.openai.com/v1/chat/completions"
model: "gpt-3.5-turbo"
# 可选配置
timeout: 120 # 请求超时(秒)
max_retries: 3 # 最大重试次数
backoff_multiplier: 1 # 指数退避倍率(秒)
verify_ssl: false # 是否验证 SSL 证书
backend: "llmapi" # 后端标识符
最佳实践建议
- 开发环境:使用
.llmdog.yaml配置文件,方便管理 - 生产环境:使用环境变量,避免敏感信息泄露
- 团队协作:提供
.llmdog.yaml.example模板,每个人复制后填写自己的配置 - 安全注意:永远不要将包含真实 API Key 的配置文件提交到 Git 仓库(已自动添加到
.gitignore)
技术实现细节
核心函数:
_print_llm_config_help()- 显示帮助信息(支持 Rich Markdown)_show_current_config(config)- 显示当前配置_test_api_connection(config)- 测试 API 连接_create_config_file()- 创建配置文件_quick_set_config(config, api_url, api_key, model)- 快速设置_run_config_wizard()- 交互式向导
设计特点:
- 遵循 Typer CLI 规范,使用
@app.command()和Annotated类型提示 - 复用现有的
test_llm_api()函数,保持一致性 - 使用
print_info、print_success、print_error等输出函数 - API Key 显示时自动脱敏(只显示前 8 位)
- 错误提示包含详细的解决建议
扩展功能(未来计划)
- 支持多配置配置文件(如
--config-file参数) - 支持配置导入/导出
- 支持配置历史记录
- 支持配置验证规则自定义
- 集成到 Web 界面的配置管理
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 函数调用工具 |
pyyaml |
>=6.0 | YAML 配置文件处理 |
可选依赖
| 依赖组 | 包名 | 用途 |
|---|---|---|
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):前端交互框架
⚙️ LLM API 配置指南
JobDeer 的 LLM 辅助录入功能依赖于 llmdog 库,需要配置 API URL、API Key 和 Model 等参数。支持以下三种配置方式(按优先级从高到低):
方式 1:环境变量(推荐用于生产环境)
在终端中设置环境变量:
# macOS / Linux
export LLM_API_KEY="sk-your-api-key-here"
export LLM_API_URL="https://api.openai.com/v1/chat/completions"
export LLM_MODEL="gpt-3.5-turbo"
# Windows (CMD)
set LLM_API_KEY=sk-your-api-key-here
set LLM_API_URL=https://api.openai.com/v1/chat/completions
set LLM_MODEL=gpt-3.5-turbo
# Windows (PowerShell)
$env:LLM_API_KEY="sk-your-api-key-here"
$env:LLM_API_URL="https://api.openai.com/v1/chat/completions"
$env:LLM_MODEL="gpt-3.5-turbo"
支持的环境变量列表:
| 环境变量 | 对应配置 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
LLM_API_KEY |
api_key | ✅ 是 | - | API 鉴权密钥 |
LLM_API_URL |
api_url | ✅ 是 | - | API 端点地址 |
LLM_MODEL |
model | 否 | qwen2.5-coder-32b-instruct |
模型名称 |
LLM_TIMEOUT |
timeout | 否 | 120 |
请求超时(秒) |
LLM_MAX_RETRIES |
max_retries | 否 | 3 |
最大重试次数 |
LLM_VERIFY_SSL |
verify_ssl | 否 | false |
是否验证 SSL 证书 |
LLM_BACKEND |
backend | 否 | llmapi |
后端标识符 |
持久化配置(添加到 shell 配置文件):
# 添加到 ~/.bashrc 或 ~/.zshrc
echo 'export LLM_API_KEY="sk-your-api-key-here"' >> ~/.bashrc
echo 'export LLM_API_URL="https://api.openai.com/v1/chat/completions"' >> ~/.bashrc
source ~/.bashrc
方式 2:本地配置文件(推荐用于开发环境)
创建 YAML 或 JSON 格式的配置文件,llmdog 会自动按以下顺序查找:
- 当前目录:
./.llmdog.yaml或./.llmdog.json - 用户目录:
~/.llmdog.yaml或~/.llmdog.json
YAML 配置示例(.llmdog.yaml):
# API 配置(必填)
api_key: "sk-your-api-key-here"
api_url: "https://api.openai.com/v1/chat/completions"
model: "gpt-3.5-turbo"
# 可选配置
timeout: 120
max_retries: 3
verify_ssl: false
JSON 配置示例(.llmdog.json):
{
"api_key": "sk-your-api-key-here",
"api_url": "https://api.openai.com/v1/chat/completions",
"model": "gpt-3.5-turbo",
"timeout": 120,
"max_retries": 3,
"verify_ssl": false
}
快速创建配置文件:
# 复制示例配置文件
cp .llmdog.yaml.example .llmdog.yaml
# 编辑配置文件
vim .llmdog.yaml
方式 3:Web 界面动态配置(仅影响当前会话)
在 Web 界面中使用 LLM 功能时,会在浏览器中临时配置 API 参数:
- 打开 Web 界面:
jobdeer web - 点击"LLM 辅助录入"按钮
- 填写 API URL 和 API Key
- 点击"测试连接"验证配置
- 配置会保存到
localStorage(仅在当前浏览器有效)
注意:Web 界面配置仅用于前端临时调用,不会写入本地配置文件或环境变量。
配置优先级说明
llmdog 的配置加载遵循以下优先级(从高到低):
1. 函数调用时显式传入的参数(代码内部使用)
↓
2. 环境变量(LLM_API_KEY、LLM_API_URL 等)
↓
3. 配置文件(~/.llmdog.yaml 或 ./.llmdog.yaml)
↓
4. 内置默认值
最佳实践建议:
- 开发环境:使用
.llmdog.yaml配置文件,方便版本控制(记得添加到.gitignore) - 生产环境:使用环境变量,避免敏感信息泄露
- 团队协作:提供
.llmdog.yaml.example模板,每个人复制后填写自己的配置 - 安全注意:永远不要将包含真实 API Key 的配置文件提交到 Git 仓库
验证配置
配置完成后,可以通过以下方式验证:
方法 1:使用 CLI 测试
# 尝试使用 LLM 辅助录入功能
jobdeer llm-add
方法 2:使用 Python 代码测试
from jobdeer.llm_helper import test_llm_api
# 如果已配置环境变量或配置文件,可以直接调用
from llmdog.config import load_config
config = load_config()
print(f"API URL: {config.api_url}")
print(f"Model: {config.model}")
# 测试 API 连接
success = test_llm_api(config.api_url, config.api_key)
print(f"API 连接: {'成功' if success else '失败'}")
方法 3:使用 Web 界面测试
- 启动 Web 服务:
jobdeer web - 访问
http://127.0.0.1:5000 - 点击"LLM 辅助录入"
- 填写 API URL 和 API Key
- 点击"测试连接"按钮
常见问题
Q1: 提示 "api_key 未配置" 错误?
A: 检查是否正确设置了 LLM_API_KEY 环境变量或 .llmdog.yaml 配置文件中的 api_key 字段。
Q2: 如何切换不同的 LLM 提供商?
A: 修改 api_url 和 model 即可。例如:
# OpenAI
api_url: "https://api.openai.com/v1/chat/completions"
model: "gpt-3.5-turbo"
# Azure OpenAI
api_url: "https://your-resource.openai.azure.com/openai/deployments/your-deployment/chat/completions?api-version=2023-05-15"
model: "gpt-35-turbo"
# 本地部署(如 Ollama)
api_url: "http://localhost:11434/v1/chat/completions"
model: "qwen2.5:32b"
Q3: CLI 的 llm-add 命令是否支持自定义 API 配置?
A: 当前版本的 jobdeer llm-add 命令依赖于环境变量或配置文件。如果需要临时使用不同的 API 配置,可以先设置环境变量:
# 场景 1:开发环境(使用配置文件)
cp .llmdog.yaml.example .llmdog.yaml
vim .llmdog.yaml # 填写真实 API Key
jobdeer llm-add # 直接使用
# 场景 2:生产环境(使用环境变量)
export LLM_API_KEY="sk-prod-key"
export LLM_API_URL="https://api.openai.com/v1/chat/completions"
jobdeer llm-add
# 场景 3:临时测试不同 API
jobdeer llm-add --api-url "http://localhost:11434/v1" --api-key "ollama" --model "qwen2.5:32b"
# 场景 4:验证配置
python3 -c "from llmdog.config import load_config; c = load_config(); print(f'URL: {c.api_url}, Model: {c.model}')"
Q4: 配置文件和环境变量同时存在时,哪个生效?
A: 环境变量优先级高于配置文件。如果同时设置了 LLM_API_KEY 环境变量和 .llmdog.yaml 中的 api_key,环境变量的值会覆盖配置文件。
🔍 搜索功能技术原理
1. 关键词搜索(Tantivy BM25)
实现流程:
- 写入索引:职位数据写入时,使用 jieba 对中文文本进行预分词,以空格连接后存入 Tantivy
- 搜索查询:用户输入查询词同样经过 jieba 分词,构建 Tantivy 查询语句
- 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)
实现流程:
- 使用
re.compile()编译正则表达式 - 遍历所有职位数据,将每个职位的所有字段拼接为文本
- 使用
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. 语义搜索(向量相似度)
实现流程:
- 向量化:使用
sentence-transformers的shibing624/text2vec-base-chinese模型,将所有职位的可搜索字段拼接后生成 768 维向量 - 存储:向量矩阵保存为
vectors.npy,对应的 job_id 列表保存为ids.json - 搜索:用户查询文本同样生成向量,与所有职位向量计算余弦相似度
- 排序:按相似度降序返回 top-N 结果
余弦相似度计算:
# 向量已归一化,直接点积即为余弦相似度
scores = vectors @ query_vec # (N,) 数组
top_indices = np.argsort(-scores)[:limit]
为什么用 text2vec-base-chinese?
- 轻量模型(约 400MB),适合本地部署
- 支持中英双语,适合技术职位搜索
- 基于 SentenceTransformers,API 简洁
注意事项:
- 语义搜索为可选功能,不安装
sentence-transformers不影响其他搜索 - 首次使用会自动下载模型文件(约 400MB),后续缓存到本地
- 每次添加/删除/更新职位后,需同步更新向量索引
🧩 扩展与维护
如何添加新的可搜索字段?
- 在
jobdeer/models.py的CORE_FIELDS集合中添加字段名 - 在
jobdeer/search.py的SEARCHABLE_FIELDS列表中添加字段名 - 在
jobdeer/templates/index.html的高级搜索面板中添加对应的输入框 - 在
jobdeer/static/app.js的advancedFields对象中添加初始值 - 执行
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 重新构建向量索引。
如何添加新的搜索模式?
- 在
jobdeer/search.py中实现新的搜索函数:
def search_custom(query_str: str, limit: int = 100) -> List[Dict[str, Any]]:
"""自定义搜索逻辑"""
# 你的实现
return results
- 在
jobdeer/web.py的api_search()路由中添加新模式处理:
elif mode == "custom":
results = search_custom(q, limit=limit)
- 在
jobdeer/cli.py的cmd_search()中添加新模式处理 - 在
jobdeer/templates/index.html的下拉选择器中添加新选项 - 在
jobdeer/static/app.js的searchMode逻辑中处理新模式
如何迁移到其他数据库?
当前使用 JSON 文件存储,适合小规模数据(<10,000 条)。如需迁移到数据库:
- 修改
jobdeer/models.py中的JobStore类,替换为 SQLAlchemy 或 ORM - 替换
store.add()、store.get()等方法为数据库操作 - 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 流程
- Fork 本仓库
- 创建特性分支:
git checkout -b feature/amazing-feature - 提交修改:
git commit -m 'Add amazing feature' - 推送分支:
git push origin feature/amazing-feature - 提交 Pull Request
📄 许可证
本项目采用 MIT 许可证。详见 LICENSE 文件。
🙏 致谢
- Tantivy - Rust 全文检索引擎
- sentence-transformers - 语义向量模型
- jieba - 中文分词
- Typer - CLI 框架
- Flask - Web 框架
- Alpine.js - 前端框架
- TailwindCSS - CSS 框架
⚠️ 免责声明
重要提示:
-
使用风险:本软件按“现状”提供,不提供任何形式的明示或暗示保证,包括但不限于适销性、特定用途适用性和非侵权性的保证。使用本软件所产生的任何风险由用户自行承担。
-
数据安全:
- 本工具处理的数据(包括职位信息、API Key 等)由用户自行管理
- 建议定期备份
joblist.json数据文件 - 切勿将包含 API Key 的配置文件(
.llmdog.yaml)提交到版本控制系统 - 使用强密码和安全的 API Key,并定期更换
-
第三方服务:
- 本工具集成的 LLM 服务(通过 llmdog 库)由第三方提供商提供
- LLM 服务的质量、可用性和准确性不由本工具保证
- 使用 LLM 功能时,请遵守相关服务提供商的条款和条件
- LLM 生成的内容可能存在错误,请人工审核后使用
-
隐私保护:
- 本工具在本地运行,数据默认存储在本地文件系统
- 使用 LLM API 时,职位描述等数据会发送到第三方 API 服务器
- 请确保您有权限将相关数据发送到外部服务
- 对于敏感职位信息,建议使用本地部署的 LLM 服务
-
合规性:
- 用户在使用本工具时应遵守适用的法律法规
- 在收集、存储和处理个人信息时,请遵守相关数据保护法规(如 GDPR、个人信息保护法等)
- 本工具不提供法律建议,如有合规性问题请咨询专业法律顾问
-
技术支持:
- 本工具为开源软件,社区提供支持
- 不保证及时的技术响应和问题修复
- 鼓励用户通过 Issue 和 Pull Request 参与贡献
-
版本兼容性:
- 不同版本之间可能存在不兼容的变更
- 升级前请仔细阅读更新日志
- 建议在测试环境验证后再升级到生产环境
通过使用本软件,您同意接受上述所有条款。如果您不同意这些条款,请不要使用本软件。
Project details
Release history Release notifications | RSS feed
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 jobdeer-0.1.0.tar.gz.
File metadata
- Download URL: jobdeer-0.1.0.tar.gz
- Upload date:
- Size: 68.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 |
54cb619c215712cc628057735e6e2b819870a9777c4478c7727095958800cde1
|
|
| MD5 |
998d2e9582589202fed3a9d7f3628b5a
|
|
| BLAKE2b-256 |
020b01ac818bc93a09e01921bfdf101b21a3347e738639070ff58e5a17e60c4d
|
File details
Details for the file jobdeer-0.1.0-py3-none-any.whl.
File metadata
- Download URL: jobdeer-0.1.0-py3-none-any.whl
- Upload date:
- Size: 48.2 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 |
387a9e7168c1768056f859ccf91ee4256e449379ef756b037b217dc48875d3e0
|
|
| MD5 |
30cb6835f9d6cb47d471029ddca3e01f
|
|
| BLAKE2b-256 |
043abaac8f21e27e7a7135a359c47c9aac5783a09b97422f2779d8f1d191dcce
|