岗位覆盖扩充至 26 个 + 图谱岗位子图模式

- 新增 8 岗位族(大数据/机器学习/机器人/数据治理/边缘计算/物联网安全/智能系统架构/隐私计算),词表 161→179
- 图谱详情面板新增'岗位子图'模式(只看该岗位及一跳邻居,徽标可退出)
- 测试集扩至 176 条、匹配标注 52 对;real_jd_500 F1 99.82%;合成三项 100% 保持
This commit is contained in:
RoyirinRosmontis
2026-08-12 15:58:22 +08:00
parent 1722c00249
commit 33aee6b8ae
8 changed files with 296 additions and 20 deletions
+19 -8
View File
@@ -31,11 +31,22 @@ python main.py # 自动构建/加载图谱,启动服务并打开浏
```bash
python scripts/generate_dataset.py
# random.seed(2026) 固定:1375 条 JD(2019-2026,18 个岗位,15 城市,7 行业,8% 抄袭、噪声植入)、
# 161 项技能词表(含 AIOps/微前端等新技能)、144 条测试集+标准答案、36 对人岗匹配标注、
# random.seed(2026) 固定:1720 条 JD(2019-2026,26 个岗位,15 城市,7 行业,8% 抄袭、噪声植入)、
# 179 项技能词表(含 AIOps/微前端/ROS/SLAM/联邦学习/零信任等新技能)、176 条测试集+标准答案、
# 52 对人岗匹配标注、
# 7 份简历样例(txt,另附 resume_full.docx / resume_full.pdf 演示上传解析)、3 份行业报告样例、7 条审核留痕种子记录
```
岗位清单(26 个,对齐人工智能/大数据/智能系统/物联网等新一代信息技术领域):
- 后端与前端:Java开发工程师、Python开发工程师、Go开发工程师、前端工程师
- 大数据:数据工程师、大数据开发工程师、数据治理工程师
- AI 与算法:算法工程师、机器学习工程师、NLP工程师、CV工程师、智能机器人算法工程师、隐私计算工程师
- 智能系统与云原生:云原生工程师、智能系统架构师、边缘计算工程师
- 物联网与嵌入式:嵌入式工程师、物联网工程师、物联网安全工程师
- 测试/运维/分析/产品:测试开发工程师、运维工程师(SRE)、数据分析师、AI产品经理
- 新岗位发现入库(2023 年后出现):大模型应用工程师、RAG工程师、提示词工程师
简历上传支持 .txt / .docx / .pdf(`POST /api/resume/parse/file`,≤5MB,解析即脱敏,临时文件自动清理)。
## 真实数据集(data/real/,与合成集分开报告)
@@ -62,18 +73,18 @@ python scripts/evaluate.py
| 指标 | 实测 | 目标 |
|---|---|---|
| JD 解析准确率 | 100% | ≥90% |
| JD 解析准确率 | 100% (176 条) | ≥90% |
| 简历提取准确率 | 100% (7 份简历) | ≥90% |
| 人岗匹配准确率 | 100% (36 对标注) | ≥90% |
| 真实 JD(AutoWeakS)技能 F1 | 99.94% (200 条) | ≥80% |
| 真实简历(AIStudio)姓名/电话命中 | 100% / 100% (50 份) | 100% |
| 真实简历(AIStudio)技能 F1 | 100% (50 份) | — |
| 人岗匹配准确率 | 100% (52 对标注) | ≥90% |
| 真实 JD(AutoWeakS)技能 F1 | 99.80% (200 条) / 99.82% (500 条分层) | ≥80% |
| 真实简历(AIStudio)姓名/电话命中 | 100% / 100% (200 份) | 100% |
| 真实简历(AIStudio)技能 F1 | 100% (200 份) | — |
## 测试
```bash
python -m pytest tests/ --cov=app --cov-report=term-missing
# 71 passed
# 74 passed
```
端到端冒烟(不启动服务):
+103 -7
View File
@@ -27,7 +27,7 @@
"f1": 1.0
}
},
"samples": 144,
"samples": 176,
"bad_lines": 0
},
"resume_detail": {
@@ -262,6 +262,102 @@
"score": 6.67,
"ok": true
},
{
"job": "大数据开发工程师",
"expected": true,
"score": 75.0,
"ok": true
},
{
"job": "大数据开发工程师",
"expected": false,
"score": 6.67,
"ok": true
},
{
"job": "机器学习工程师",
"expected": true,
"score": 75.0,
"ok": true
},
{
"job": "机器学习工程师",
"expected": false,
"score": 6.67,
"ok": true
},
{
"job": "智能机器人算法工程师",
"expected": true,
"score": 75.0,
"ok": true
},
{
"job": "智能机器人算法工程师",
"expected": false,
"score": 6.67,
"ok": true
},
{
"job": "数据治理工程师",
"expected": true,
"score": 75.0,
"ok": true
},
{
"job": "数据治理工程师",
"expected": false,
"score": 6.67,
"ok": true
},
{
"job": "边缘计算工程师",
"expected": true,
"score": 75.0,
"ok": true
},
{
"job": "边缘计算工程师",
"expected": false,
"score": 6.67,
"ok": true
},
{
"job": "物联网安全工程师",
"expected": true,
"score": 75.0,
"ok": true
},
{
"job": "物联网安全工程师",
"expected": false,
"score": 6.67,
"ok": true
},
{
"job": "智能系统架构师",
"expected": true,
"score": 75.0,
"ok": true
},
{
"job": "智能系统架构师",
"expected": false,
"score": 4.0,
"ok": true
},
{
"job": "隐私计算工程师",
"expected": true,
"score": 75.0,
"ok": true
},
{
"job": "隐私计算工程师",
"expected": false,
"score": 5.0,
"ok": true
},
{
"job": "前端工程师",
"expected": true,
@@ -300,13 +396,13 @@
}
],
"real_jd": {
"accuracy": 0.9995,
"accuracy": 0.9984,
"title_accuracy": 1.0,
"fields": {
"required_skills": {
"precision": 1.0,
"precision": 0.9976,
"recall": 0.9988,
"f1": 0.9994
"f1": 0.9982
},
"preferred_skills": null,
"scenarios": {
@@ -319,13 +415,13 @@
"bad_lines": 0
},
"real_jd_500": {
"accuracy": 0.9991,
"accuracy": 0.9983,
"title_accuracy": 1.0,
"fields": {
"required_skills": {
"precision": 1.0,
"precision": 0.998,
"recall": 0.998,
"f1": 0.999
"f1": 0.998
},
"preferred_skills": null,
"scenarios": {
+90 -5
View File
@@ -57,10 +57,10 @@ SKILLS = [
("Struts", "后端框架", [], "L1", ["Java"]), ("Servlet", "后端框架", [], "L1", ["Java"]),
("JSP", "后端框架", [], "L1", ["Java"]), ("SSM", "后端框架", [], "L2", ["Spring", "MyBatis"]),
# 前端
("Vue", "前端", ["Vue.js"], "L2", ["JavaScript"]), ("React", "前端", ["React.js"], "L2", ["JavaScript"]),
("Vue", "前端", ["Vue.js"], "L2", ["JavaScript"]), ("React", "前端", ["React.js", "ReactJS"], "L2", ["JavaScript"]),
("Angular", "前端", ["AngularJS"], "L2", ["TypeScript"]), ("jQuery", "前端", [], "L1", ["JavaScript"]),
("Webpack", "前端", [], "L2", ["JavaScript"]), ("Vite", "前端", [], "L2", ["JavaScript"]),
("Node.js", "前端", ["Node"], "L2", ["JavaScript"]), ("小程序开发", "前端", ["微信小程序"], "L2", ["JavaScript"]),
("Node.js", "前端", ["Node", "NodeJS"], "L2", ["JavaScript"]), ("小程序开发", "前端", ["微信小程序"], "L2", ["JavaScript"]),
("Element UI", "前端", [], "L1", ["Vue"]), ("Ant Design", "前端", [], "L1", ["React"]),
("HTML", "前端", ["HTML5"], "L1", []), ("CSS", "前端", ["CSS3"], "L1", []),
("Tailwind CSS", "前端", [], "L2", ["CSS"]), ("低代码平台", "前端", [], "L2", []),
@@ -141,6 +141,28 @@ SKILLS = [
("Tableau", "数据分析", [], "L2", ["数据可视化"]), ("Power BI", "数据分析", ["PowerBI"], "L2", ["数据可视化"]),
("SPSS", "数据分析", [], "L2", []), ("数据可视化", "数据分析", [], "L2", ["数据分析"]),
("A/B测试", "数据分析", ["AB测试"], "L2", ["数据分析"]),
# 机器人与智能系统(岗位覆盖扩展)
("ROS", "AI算法", ["机器人操作系统"], "L2", ["C++", "Linux"]),
("SLAM", "AI算法", ["同步定位与建图"], "L3", ["计算机视觉"]),
("路径规划", "AI算法", [], "L3", ["机器学习"]),
# 数据治理
("数据治理", "大数据", [], "L2", ["SQL"]),
("数据脱敏", "大数据", ["数据遮蔽"], "L2", ["SQL"]),
("元数据管理", "大数据", ["元数据"], "L2", ["数据治理"]),
("数据血缘", "大数据", [], "L3", ["元数据管理"]),
("湖仓一体", "大数据", ["实时湖仓"], "L3", ["Hive"]),
# 边缘计算
("边缘推理", "物联网", ["边缘推理框架"], "L3", ["边缘计算", "深度学习"]),
("TensorRT", "AI算法", [], "L3", ["深度学习"]),
("KubeEdge", "云原生", [], "L3", ["Kubernetes", "边缘计算"]),
# 安全与隐私计算
("零信任", "安全", ["零信任架构"], "L3", []),
("入侵检测", "安全", [], "L2", ["Linux"]),
("等保合规", "安全", [], "L2", []),
("联邦学习", "AI算法", [], "L3", ["机器学习"]),
("多方安全计算", "安全", ["MPC"], "L3", []),
("同态加密", "安全", [], "L3", []),
("差分隐私", "安全", [], "L3", ["机器学习"]),
]
SCENARIOS = ["电商", "金融", "物流", "社交", "教育", "医疗", "政务", "游戏",
@@ -152,11 +174,11 @@ INDUSTRIES = ["互联网/软件", "人工智能", "金融科技", "智能制造"
# ---------------------------------------------------------------- 岗位原型
def arc(title, category, years, salary, scenarios, industries,
duty_tmpls, duty_objs, timeline, volumes):
duty_tmpls, duty_objs, timeline, volumes, test_n=8):
return dict(title=title, category=category, years=years, salary=salary,
scenarios=scenarios, industries=industries,
duty_tmpls=duty_tmpls, duty_objs=duty_objs,
timeline=timeline, volumes=volumes)
timeline=timeline, volumes=volumes, test_n=test_n)
BACKEND_DUTIES = ["负责{o}的设计与开发", "参与{o}的性能优化与重构", "配合产品团队完成{o}的需求评审",
"编写{o}的技术文档与接口文档", "承担{o}的线上问题排查与修复", "推动{o}的代码评审与质量改进"]
@@ -325,6 +347,69 @@ ARCHETYPES = [
["营销文案生成", "智能客服话术", "内容创作", "教学辅导", "商品描述", "对话流程"],
[(2024, 2026, ["提示词工程", "大模型应用开发", "Python", "数据分析"], ["LangChain", "RAG", "AI产品"])],
{2024: 2, 2025: 6, 2026: 10}),
# —— 岗位覆盖扩展(8 个既有岗位族,2019 年前已存在,支撑赛题领域覆盖) ——
# 大数据开发:2019-2021 Hadoop 离线系 → 2022+ Flink 实时/湖仓一体(演化信号)
arc("大数据开发工程师", "大数据", 3, "16K-32K", ["电商", "金融", "短视频"],
["互联网/软件", "金融科技"],
["负责{o}的离线计算任务开发", "搭建{o}的实时计算链路", "参与{o}的数据模型设计",
"优化{o}的任务性能与资源消耗", "维护{o}的调度依赖与数据质量"],
["离线数仓", "实时数仓", "用户行为日志", "流量数据", "交易数据", "数据集市"],
[(2019, 2021, ["SQL", "Hadoop", "Hive", "Spark", "HDFS"], ["Sqoop", "HBase", "Scala"]),
(2022, 2026, ["SQL", "Spark", "Flink", "Kafka", "Hive"], ["ClickHouse", "Doris", "湖仓一体"])],
{2019: 4, 2020: 4, 2021: 4, 2022: 5, 2023: 5, 2024: 6, 2025: 6, 2026: 6}, test_n=4),
# 机器学习工程师:2023+ 加分项出现"模型微调/大模型应用开发"(演化信号)
arc("机器学习工程师", "AI算法", 3, "18K-35K", ["金融", "电商", "广告"],
["人工智能"],
["负责{o}的特征工程与模型训练", "参与{o}的离线评估与线上实验", "优化{o}的推理性能与资源占用",
"建设{o}的样本与特征流水线", "跟进{o}的算法迭代与复盘"],
["风控模型", "搜索排序", "召回策略", "销量预测", "异常检测", "用户分层"],
[(2019, 2022, ["Python", "机器学习", "Scikit-learn", "SQL", "XGBoost"], ["LightGBM", "TensorFlow", "Spark"]),
(2023, 2026, ["Python", "机器学习", "深度学习", "PyTorch", "XGBoost"], ["模型微调", "大模型应用开发", "LightGBM"])],
{2019: 4, 2020: 4, 2021: 4, 2022: 5, 2023: 5, 2024: 6, 2025: 6, 2026: 6}, test_n=4),
arc("智能机器人算法工程师", "AI算法", 3, "20K-40K", ["智能制造", "物流", "出行"],
["人工智能", "智能制造"],
["负责{o}的算法研发与调优", "参与{o}的仿真验证与实机部署", "优化{o}的实时性与鲁棒性",
"建设{o}的数据闭环与评测体系", "跟进{o}的前沿方案并落地"],
["SLAM 建图", "定位导航", "运动规划", "多传感器融合", "机械臂控制", "避障策略"],
[(2019, 2026, ["C++", "Python", "ROS", "SLAM", "路径规划"], ["深度学习", "强化学习", "OpenCV"])],
{2019: 4, 2020: 4, 2021: 4, 2022: 5, 2023: 5, 2024: 6, 2025: 6, 2026: 6}, test_n=4),
arc("数据治理工程师", "大数据", 3, "15K-30K", ["金融", "政务", "医疗"],
["金融科技", "互联网/软件"],
["负责{o}的标准制定与落地", "建设{o}的元数据与血缘体系", "推进{o}的质量监控与整改",
"制定{o}的分级分类与脱敏策略", "组织{o}的盘点与确权工作"],
["数据资产", "主数据", "指标口径", "敏感数据", "数据目录", "质量规则"],
[(2019, 2026, ["SQL", "数据治理", "元数据管理", "数据脱敏", "Python"], ["数据血缘", "Hive", "Spark"])],
{2019: 4, 2020: 4, 2021: 4, 2022: 5, 2023: 5, 2024: 6, 2025: 6, 2026: 6}, test_n=4),
arc("边缘计算工程师", "物联网", 3, "16K-30K", ["智能制造", "安防", "物流"],
["物联网", "智能制造"],
["负责{o}的部署与调优", "参与{o}的模型压缩与推理加速", "建设{o}的云边协同链路",
"优化{o}的资源占用与稳定性", "编写{o}的技术文档"],
["边缘网关", "边缘节点", "端侧推理", "视频分析", "设备接入", "边云协同"],
[(2019, 2021, ["C++", "Python", "Linux", "MQTT", "嵌入式开发"], ["Docker", "边缘计算", "OpenCV"]),
(2022, 2026, ["Python", "边缘计算", "Docker", "Linux", "MQTT"], ["边缘推理", "TensorRT", "KubeEdge"])],
{2019: 4, 2020: 4, 2021: 4, 2022: 5, 2023: 5, 2024: 6, 2025: 6, 2026: 6}, test_n=4),
arc("物联网安全工程师", "物联网", 3, "16K-32K", ["智能制造", "安防", "政务"],
["物联网", "智慧城市"],
["负责{o}的安全评估与加固", "建设{o}的入侵检测与响应体系", "参与{o}的固件安全审计",
"制定{o}的接入认证策略", "跟踪{o}的漏洞情报与处置"],
["设备接入", "设备网络", "边缘网关", "固件", "通信协议", "密钥体系"],
[(2019, 2026, ["Linux", "Python", "入侵检测", "零信任", "MQTT"], ["等保合规", "嵌入式开发", "云计算"])],
{2019: 4, 2020: 4, 2021: 4, 2022: 5, 2023: 5, 2024: 6, 2025: 6, 2026: 6}, test_n=4),
arc("智能系统架构师", "云原生", 5, "30K-60K·14薪", ["金融", "政务", "智能制造"],
["互联网/软件", "人工智能"],
["负责{o}的整体架构设计", "主导{o}的技术选型与评审", "制定{o}的容量与容灾方案",
"推动{o}的架构演进与落地", "沉淀{o}的架构规范与最佳实践"],
["智能平台", "业务中台", "云边端协同", "高可用体系", "多云架构", "技术路线"],
[(2019, 2021, ["分布式系统", "微服务", "云计算", "高并发", "Linux"], ["Docker", "Kubernetes", "Service Mesh"]),
(2022, 2026, ["分布式系统", "微服务", "Kubernetes", "云计算", "高并发"], ["Service Mesh", "边缘计算", "物联网平台"])],
{2019: 4, 2020: 4, 2021: 4, 2022: 5, 2023: 5, 2024: 6, 2025: 6, 2026: 6}, test_n=4),
arc("隐私计算工程师", "AI算法", 4, "25K-45K", ["金融", "政务", "医疗"],
["人工智能", "金融科技"],
["负责{o}的方案设计与实现", "参与{o}的性能优化与工程化", "建设{o}的协同计算链路",
"评估{o}的安全性与合规性", "跟进{o}的最新研究成果"],
["联邦建模", "多方联合统计", "安全求交", "数据协作", "密文计算", "隐私保护"],
[(2019, 2026, ["Python", "机器学习", "联邦学习", "同态加密", "多方安全计算"], ["差分隐私", "模型微调", "Spark"])],
{2019: 4, 2020: 4, 2021: 4, 2022: 5, 2023: 5, 2024: 6, 2025: 6, 2026: 6}, test_n=4),
]
COMPANY_PRE = ["星澜", "云启", "数联", "智汇", "蓝湾", "极光", "恒宇", "锐思", "博远", "辰安",
@@ -779,8 +864,8 @@ def main() -> None:
# 5. 测试集(与 raw 不同 jid 命名空间)+ 标准答案
testset, gold = [], {}
tseq = 0
per_arc = 8 # 18 岗位 × 8 = 144 条 >= 120
for a in ARCHETYPES:
per_arc = a["test_n"] # 原 18 岗位 × 8 + 扩展 8 岗位 × 4 = 176 条 >= 120
years = sorted(a["volumes"])
for _ in range(per_arc):
tseq += 1
+19
View File
@@ -0,0 +1,19 @@
@echo off
chcp 65001 >nul
REM ============================================================
REM CareerNexus 职联 启动脚本(Windows)
REM 直接通过项目内 .venv 启动服务,自动打开浏览器
REM 首次使用请先运行 init.bat
REM ============================================================
cd /d "%~dp0"
if not exist ".venv\Scripts\python.exe" (
echo [错误] 未找到 .venv,请先运行 init.bat 完成初始化
pause
exit /b 1
)
echo 启动 CareerNexus 职联 ...
echo 服务地址 http://127.0.0.1:9008 (关闭本窗口即停止服务)
.venv\Scripts\python.exe main.py
pause
+22
View File
@@ -0,0 +1,22 @@
#!/usr/bin/env bash
# ============================================================
# CareerNexus 职联 启动脚本(Linux / macOS / Git Bash)
# 直接通过项目内 .venv 启动服务,自动打开浏览器
# 首次使用请先运行 bash init.sh
# ============================================================
set -e
cd "$(dirname "$0")"
# Windows venv 与 POSIX venv 路径兼容
if [ -f ".venv/bin/python" ]; then
VENV_PY=.venv/bin/python
elif [ -f ".venv/Scripts/python.exe" ]; then
VENV_PY=.venv/Scripts/python.exe
else
echo "[错误] 未找到 .venv,请先运行 bash init.sh 完成初始化"
exit 1
fi
echo "启动 CareerNexus 职联 ..."
echo "服务地址 http://127.0.0.1:9008 (Ctrl+C 停止服务)"
exec "$VENV_PY" main.py
+24
View File
@@ -14,6 +14,7 @@
filters: { types: { Job: true, Skill: true, Industry: true, Scenario: true }, category: "", level: "",
edges: { required: true, preferred: true, applies: true, belongs: true } },
selected: null, // 选中节点 id
soloJob: null, // 岗位子图模式:只看该岗位及其一跳邻居
hover: null,
navStack: [], // 详情面板导航栈
drag: null, // {mode:'pan'|'node', ...}
@@ -161,6 +162,9 @@
}
function visible(n) {
if (!G.filters.types[n.type]) return false;
if (G.soloJob) { // 岗位子图:只保留该岗位与一跳邻居
if (n.id !== G.soloJob && !neighborsOf(G.soloJob).has(n.id)) return false;
}
if (n.type === "Job") {
if (G.filters.category && n.category !== G.filters.category) return false;
if (G.filters.level && jobLevel(n) !== G.filters.level) return false;
@@ -597,6 +601,18 @@
}
CN.gotoNode = locateNode;
function updateSoloBadge() {
const badge = $("#solo-badge");
if (!badge) return;
badge.classList.toggle("hidden", !G.soloJob);
if (G.soloJob) {
const n = G.nodes[G.idx[G.soloJob]];
$("#solo-badge-text").textContent = `岗位子图:${(n && (n.title || n.id)) || G.soloJob}`;
}
}
const soloExit = $("#solo-exit");
if (soloExit) soloExit.onclick = () => { G.soloJob = null; updateSoloBadge(); requestDraw(); };
/* ================= 备注(localStorage) ================= */
const getNotes = () => JSON.parse(localStorage.getItem(NOTES_KEY) || "{}");
function nodeNotes(id) { return getNotes()[id] || []; }
@@ -720,6 +736,7 @@
<button class="btn btn-secondary btn-sm" id="dt-sources">查看来源</button>`}
<button class="btn btn-secondary btn-sm" id="dt-note">添加备注</button>
${n.type === "Job" && !isPending ? `<button class="btn btn-secondary btn-sm" id="dt-evo">查看演化</button>
<button class="btn btn-secondary btn-sm" id="dt-solo">岗位子图</button>
<button class="btn btn-primary btn-sm full" id="dt-learn">生成学习路径</button>` : ""}
</div>
</div>
@@ -765,6 +782,13 @@
$("#evo-job-select").value = id;
$("#evo-job-select").dispatchEvent(new Event("change"));
};
const soloBtn = $("#dt-solo");
if (soloBtn) soloBtn.onclick = () => {
G.soloJob = G.soloJob === id ? null : id;
updateSoloBadge();
requestDraw();
if (G.soloJob) toast(`已进入「${n.title || id}」岗位子图(只看该岗位与一跳技能/场景/行业)`, "success");
};
const learnBtn = $("#dt-learn");
if (learnBtn) learnBtn.onclick = () => {
CN.switchView("path");
+6
View File
@@ -135,6 +135,12 @@
<div class="graph-canvas-wrap card" id="graph-canvas-wrap">
<canvas id="graph-canvas"></canvas>
<!-- 岗位子图状态徽标 -->
<div class="solo-badge hidden" id="solo-badge">
<span id="solo-badge-text">岗位子图</span>
<button id="solo-exit" title="退出岗位子图">×</button>
</div>
<!-- 左上浮动工具条 -->
<div class="graph-toolbar">
<button class="tool-btn" id="tool-zoom-in" title="放大"><svg viewBox="0 0 24 24"><circle cx="11" cy="11" r="7"/><path d="M21 21l-4.35-4.35M11 8v6M8 11h6"/></svg></button>
+13
View File
@@ -911,3 +911,16 @@ svg { width: 16px; height: 16px; stroke: currentColor; fill: none; stroke-width:
border-top: 1px solid var(--border);
user-select: text;
}
/* 岗位子图状态徽标 */
.solo-badge {
position: absolute; top: 14px; left: 50%; transform: translateX(-50%);
display: flex; align-items: center; gap: 8px; z-index: 30;
background: var(--primary); color: #fff; font-size: 12px;
border-radius: 999px; padding: 4px 8px 4px 14px; box-shadow: var(--shadow-pop);
}
.solo-badge button {
border: none; background: rgba(255,255,255,.25); color: #fff;
width: 18px; height: 18px; border-radius: 50%; cursor: pointer; line-height: 1;
}
.solo-badge button:hover { background: rgba(255,255,255,.45); }