Files
CareerNexus/README.md
T
RoyirinRosmontis a10442d78c CareerNexus 职联:岗位能力图谱构建与动态演化分析平台(挑战杯 XH-202621)
- 全链路:多源JD接入/清洗交叉验证/结构化抽取(星火+离线双通道)/时序知识图谱/新岗位发现/演化分析/人岗匹配/学习路径
- WebUI 五视图:驾驶舱/图谱工作台/岗位演化/匹配(单人+批量)/学习路径
- 设置中心(config.json)/Token鉴权/SQLite状态库/语义向量通道(BGE-M3本地或联网API)
- 71 个 pytest 用例;数据目录仅保留结构(.gitkeep),数据由 scripts/generate_dataset.py 复现
2026-08-03 13:15:35 +08:00

213 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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`,均为纯前端持久化。