CareerNexus 职联:岗位能力图谱构建与动态演化分析平台(挑战杯 XH-202621)

- 全链路:多源JD接入/清洗交叉验证/结构化抽取(星火+离线双通道)/时序知识图谱/新岗位发现/演化分析/人岗匹配/学习路径
- WebUI 五视图:驾驶舱/图谱工作台/岗位演化/匹配(单人+批量)/学习路径
- 设置中心(config.json)/Token鉴权/SQLite状态库/语义向量通道(BGE-M3本地或联网API)
- 71 个 pytest 用例;数据目录仅保留结构(.gitkeep),数据由 scripts/generate_dataset.py 复现
This commit is contained in:
RoyirinRosmontis
2026-08-03 13:15:35 +08:00
commit a10442d78c
78 changed files with 12860 additions and 0 deletions
+212
View File
@@ -0,0 +1,212 @@
# CareerNexus 职联
多源异构数据驱动的**岗位与能力图谱构建与动态演化分析**平台 —— 挑战杯“揭榜挂帅”赛题 XH-202621 参赛作品。
核心链路:多源 JD 接入 → 清洗交叉验证(时滞/噪声/抄袭)→ 结构化抽取(离线规则 / 星火大模型双通道,JSON Schema 强校验)→ 时序知识图谱(Temporal KG)→ 新岗位发现与能力演化分析 → 简历解析、人岗匹配、差距分析与学习路径。
## 环境准备
```bash
python -m venv .venv
# Windows: .venv\Scripts\activate Linux: source .venv/bin/activate
pip install -r requirements.txt
```
## 一键启动
```bash
python main.py # 自动构建/加载图谱,启动服务并打开浏览器
# 浏览器访问 http://127.0.0.1:9008
```
## 系统设置(WebUI 右上角 ⚙)
设置保存在 `config.json`(优先级高于 config.yaml),包含:
- **服务监听**:`127.0.0.1`(仅本机)或 `0.0.0.0`(对外开放)+ 端口,修改后需重启(面板提供"保存并重启");
- **访问令牌**:设置后 `/api/*` 需携带 `X-Access-Token` 头(health 与只读 GET /api/config 豁免);
- **LLM API**:星火/OpenAI 兼容的 base_url、api_key、model,保存即热生效——配置后抽取走大模型通道,未配置时显式回退离线确定性抽取,前端有醒目的"离线回退模式"横幅提示。
## 数据生成(已随工程提交,可复现重建)
```bash
python scripts/generate_dataset.py
# random.seed(2026) 固定:1375 条 JD(2019-2026,18 个岗位,15 城市,7 行业,8% 抄袭、噪声植入)、
# 161 项技能词表(含 AIOps/微前端等新技能)、144 条测试集+标准答案、36 对人岗匹配标注、
# 7 份简历样例(txt,另附 resume_full.docx / resume_full.pdf 演示上传解析)、3 份行业报告样例、7 条审核留痕种子记录
```
简历上传支持 .txt / .docx / .pdf(`POST /api/resume/parse/file`,≤5MB,解析即脱敏,临时文件自动清理)。
## 真实数据集(data/real/,与合成集分开报告)
```bash
python scripts/build_real_dataset.py
# random.seed(2026) 固定,从 datasets/XH-202621/data/(只读)加工:
# autoweaks_jd/ 200 条 AutoWeakS 真实 JD(腾讯160/京东40,去分词+技术岗过滤)+ silver gold(初标+抽检30条)
# aistudio_resumes/ 50 份 AIStudio CC0 简历(保留 PII 验证脱敏链路)+ 字段级 gold(抽检10条)
# batch_demo_resumes/ 12 份 chinese_resume 渲染后经 mask_text 脱敏的批量演示素材(原始 PII 不落盘)
# esco_reference/ ISCO/ESCO 职业分类锚点(4 个关键 labels 文件)
```
来源/授权/处理口径/抽检一致率详见 `data/real/README.md`。
## 指标复现
```bash
python scripts/evaluate.py
# 输出 reports/metrics.json 与控制台报告
```
实测(从文本重新抽取再与 gold 比对,非硬编码):
| 指标 | 实测 | 目标 |
|---|---|---|
| JD 解析准确率 | 100% | ≥90% |
| 简历提取准确率 | 100% (7 份简历) | ≥90% |
| 人岗匹配准确率 | 100% (36 对标注) | ≥90% |
| 真实 JD(AutoWeakS)技能 F1 | 99.94% (200 条) | ≥80% |
| 真实简历(AIStudio)姓名/电话命中 | 100% / 100% (50 份) | 100% |
| 真实简历(AIStudio)技能 F1 | 100% (50 份) | — |
## 测试
```bash
python -m pytest tests/ --cov=app --cov-report=term-missing
# 71 passed
```
端到端冒烟(不启动服务):
```bash
python scripts/run_demo.py
```
## Docker
```bash
docker build -t careernexus .
docker run -p 9008:9008 careernexus
# 或 docker compose up(可选 Neo4j 增强见下节,默认不启动)
```
## 可选高级组件
### SQLite 应用状态库(默认启用,零配置)
审核留痕、待审核候选、异步任务状态统一存储在 SQLite(`data/careernexus.db`,
配置键 `state_db_path`),仅用标准库 sqlite3、**零新依赖**:WAL 模式、幂等建表,
服务启动时自动把旧的 `data/review/audit.jsonl` / `data/review/pending.json`
一次性迁移入库(原文件保留不删除)。表结构:`audit_log`(留痕)、
`review_candidates`(待审核候选)、`tasks`(异步任务,重启后可恢复查询)、
`job_overrides`(人工状态覆写层,见下节)。
### 人工状态持久化(跨图谱重建)
`job_overrides` 表保存两类人工操作:审核通过的岗位定义(含支撑 JD 来源与时间窗)、
图谱节点编辑。审核 `accept` 时用候选簇的真实统计入库——REQUIRES 边的 `intervals`
自首个支持窗口起、`sources` 为真实支撑 JD 的 source_id 列表,并补
`(Job)-[:APPLIES_TO]->(Scenario)` / `(Job)-[:BELONGS_TO]->(Industry)` 边与
"新增"演化事件;节点编辑同步 `kg.jobs` 的 JobDefinition(title/category/级别,
级别编辑同步回期望年限)。pipeline 全量重建图谱后自动重放覆写层:人工入库岗位
**强制纳入正式图谱**(不受"2023 年前出现"规则限制),节点编辑一并恢复;
候选被驳回时相应覆写记录自动清理。
### JD 手动导入
`POST /api/jd/import` 支持三种用法(均触发异步 pipeline 重建,任务 ID + 轮询):
- **文件上传**:multipart 表单 `file` 字段,支持 `.jsonl` / `.csv` / `.txt`
(UTF-8,≤10MB;CSV 需含 `jid,job_title,company,publish_date,text` 表头);
- **文本粘贴**:JSON 请求体 `{"text": "..."}`,每行一条 JSON 记录;
- **空请求**:仅触发全量重建(旧行为)。
逐行校验:合格记录写入 `data/jd_raw/import_YYYYmmdd_HHMMSS.jsonl`
(source_id 按 `文件名#行号` 重新分配,保留来源标记);坏行进入响应的
`bad_lines` 清单并写入 `logs/import_bad_时间戳.jsonl`。响应含
`imported / skipped / bad_lines / file / task_id`。WebUI 驾驶舱"导入 JD 数据"
按钮提供上传/粘贴对话框。
### Neo4j 图数据库(profile: advanced)
平台默认使用内置 TemporalKG + JSON 快照即可完整运行;需要企业级图库时可选启用:
```bash
pip install neo4j # 官方驱动为可选依赖
NEO4J_PASSWORD=你的密码 docker compose --profile advanced up -d neo4j
python scripts/sync_neo4j.py # MERGE 幂等同步 data/graph.json
python scripts/sync_neo4j.py --dry-run # 只打印节点/边统计,不连接
# 浏览器打开 http://localhost:7474 查看(Bolt 端口 7687)
```
### 为什么 BGE-M3 / FAISS / reranker / GLiNER / PaddleOCR 不做进容器
这些组件定位为**生产部署的可选增强,而非竞赛交付**:
- **模型权重 GB 级**(BGE-M3、reranker、GLiNER、PaddleOCR 均为深度模型),
镜像体积膨胀到数 GB,构建与分发成本高;
- **评审环境离线**,容器内无法下载模型权重,打进镜像又会进一步放大体积;
- 与赛题要求的**“普通笔记本 2 分钟启动”**目标直接冲突——默认链路用纯 Python
TF-IDF / 规则抽取即可达到实测指标(见上表),深度模型只在生产侧追求
长尾召回/版式鲁棒性时才有增量价值。
因此交付物默认链路保持轻量确定性实现,上述模型以接口预留方式作为生产增强路线。
## 大模型抽取通道(可选)
默认 `mode: demo` 全程离线,使用离线确定性抽取器。配置密钥并改 `mode: live` 即启用星火/OpenAI 兼容通道(JSON 模式、Schema 校验、失败重试 2 次、超时自动降级离线):
```bash
cp .env.example .env # 填入 SPARK_API_KEY(密钥只从环境变量读取)
```
## 目录结构
```
├── main.py # 唯一入口:加载配置→构建/加载图谱→启动服务→自动开浏览器
├── config.yaml # 端口/数据路径/运行模式 demo|live/匹配权重等
├── app/
│ ├── models.py # pydantic v2 全链路模型
│ ├── ingest/loader.py # jsonl 接入,source_id=文件#行号,坏行错误清单
│ ├── cleaning/ # MinHash(128哈希,5-gram)+LSH 近重复、噪声过滤、exp(-λ·age) 时间衰减
│ ├── extract/ # Extractor 协议+多源投票(base) / 离线规则(offline) / 大模型(llm) / 提示词(prompts)
│ ├── graph/tkg.py # TemporalKG:时间区间 + 事件追加不覆盖 + snapshot 切片 + JSON 持久化
│ ├── discover/cluster.py # TF-IDF+凝聚聚类新岗位发现 + 待审核分区 + 审核留痕
│ ├── storage/db.py # SQLite 应用状态库:audit_log/review_candidates/tasks/job_overrides + 旧文件一次性迁移
│ ├── evolution/analyzer.py# 季度窗口聚合,新增/增强/减弱/退出事件 + 中文说明
│ ├── match/ # 简历解析脱敏(resume) / 四维加权匹配+学习路径(engine)
│ ├── services/ # pipeline 端到端编排 / overrides 人工状态覆写层(跨重建重放)
│ └── api/routes.py # FastAPI:统一响应{code,message,data,request_id},任务ID+轮询
├── webui/ # 原生 HTML/JS 单页应用(无构建、完全离线):驾驶舱/图谱工作台/演化/匹配/学习路径
│ ├── index.html # 侧栏+顶栏外壳与五视图结构
│ ├── style.css # 设计系统(token/按钮/卡片/弹窗/表格等)
│ ├── app.js # 公共库 + 驾驶舱/演化/匹配/学习路径
│ └── graph.js # 图谱工作台:力导向画布/小地图/详情面板/编辑/备注/审核
├── scripts/ # 数据生成、真实数据集构建(build_real_dataset)、指标复现、端到端冒烟、sync_neo4j(可选 Neo4j 同步)
├── tests/ # pytest:cleaning/extract/tkg/evolution/match/api/real_data
└── data/ # 词表、JD 原始数据、测试集+gold、简历、报告、图谱快照、real/(真实公开数据集,见其 README)
```
## 人机协同审核(防“幻觉”)
新岗位簇与低置信抽取不直接入库:先进入“待审核”分区(SQLite 状态库 `review_candidates` 表),
人工 `accept / modify / reject` 后才写入正式图谱,全程留痕(操作人/时间/before/after/理由,
`audit_log` 表)。抽取结果每个字段附原文证据片段供核对。
## WebUI 图谱工作台与节点编辑
全景图谱页为“岗位能力图谱工作台”:canvas 力导向(缩放/平移/拖拽节点/小地图/筛选/关系标签开关),
节点类型覆盖 岗位/技能/行业/场景((Job)-[:BELONGS_TO]->(Industry) 行业归属边,可按类型筛选),
点击节点打开右侧详情面板(基础信息/关联跳转/证据来源/演化 sparkline/备注/审核留痕),
待审核候选以虚线节点并入画布,可直接确认/驳回。
配套接口(统一响应格式,全部留痕):
| 接口 | 说明 |
|---|---|
| `POST /api/graph/node/update` | 编辑节点展示属性(title/level/importance/weight 白名单字段),写图谱并在状态库 audit_log 表追加 `kind=node_update` 留痕;Job 节点同步 JobDefinition,编辑写入 job_overrides 覆写层(重建后自动重放);只改属性不动结构 |
| `GET /api/jd?sid=<文件#行号>` | 按 source_id 回查 JD 原文(证据来源弹窗),文件名/行号强校验防路径穿越 |
| `GET /api/audit?node_id=&limit=` | 查询审核/编辑留痕(新→旧),可按节点过滤 |
| `GET /api/graph` | 追加返回 `events`(演化事件列表),供前端趋势图与智能建议 |
节点备注保存在浏览器 localStorage(`cn_notes`),图谱视图布局保存在 `cn_graph_view`,均为纯前端持久化。