学习路径多阶段化:拓扑层按每阶段≤3技能拆分,阶段命名改为 基础补齐→岗位核心*→项目验证;简历裸姓名/专硕识别修复

This commit is contained in:
RoyirinRosmontis
2026-08-19 12:53:45 +08:00
parent 693e0af93a
commit caf5b106b6
3 changed files with 35 additions and 12 deletions
+18 -8
View File
@@ -27,27 +27,37 @@ def _coverage(have: set[str], want: list[str]) -> tuple[list[str], list[str], fl
return matched, missing, cov
def topological_stages(missing: list[str], lexicon: dict) -> list[dict]:
"""把缺失技能按前置关系拓扑排序分阶段(Kahn 分层)。
def topological_stages(missing: list[str], lexicon: dict,
max_per_stage: int = 3) -> list[dict]:
"""把缺失技能按前置关系拓扑排序分阶段(Kahn 分层 + 每层按上限再拆分)。
前置关系取自技能词表 prereqs;前置技能若已掌握或不在缺失集中则视为已满足。
同一拓扑层技能过多时按 max_per_stage 拆成多个连续阶段,让路线图呈
"基础补齐 → 岗位核心 → … → 项目验证"的多阶段结构。
每个阶段附带结构化验收标准(产出物 / 量化验收项 / 自测问答),按技能类别生成。
"""
prereq = {s["name"]: [p for p in s.get("prereqs", []) if p in missing]
for s in lexicon["skills"] if s["name"] in missing}
cat_of = {s["name"]: s.get("category", "") for s in lexicon["skills"]}
remaining = set(missing)
stages: list[dict] = []
stage_no = 1
layers: list[list[str]] = []
while remaining:
ready = sorted(s for s in remaining if not [p for p in prereq.get(s, []) if p in remaining])
if not ready: # 有环兜底:按字典序放出,保证终止
ready = sorted(remaining)
stages.append({"stage": stage_no, "skills": ready,
"goal": f"第 {stage_no} 阶段:掌握 {'、'.join(ready)}",
"acceptance": build_acceptance(ready, cat_of)})
layers.append(ready)
remaining -= set(ready)
stage_no += 1
# 拓扑层 → 按每阶段技能数上限拆分
stages: list[dict] = []
stage_no = 1
for layer in layers:
for i in range(0, len(layer), max_per_stage):
chunk = layer[i:i + max_per_stage]
stages.append({"stage": stage_no, "skills": chunk,
"goal": f"第 {stage_no} 阶段:掌握 {'、'.join(chunk)}",
"acceptance": build_acceptance(chunk, cat_of)})
stage_no += 1
return stages
+12
View File
@@ -105,3 +105,15 @@ def test_resume_bare_name_and_edu_alias():
assert p.education == "硕士" # 专硕→硕士
assert p.address.startswith("辽宁大连")
assert "Python" in p.skills
def test_learning_path_stage_chunking():
"""同层技能超 3 个时拆成多个连续阶段;拓扑序不变。"""
stages = topological_stages(["Linux", "Docker", "Kubernetes", "Python", "Java", "Go", "MySQL"],
load_lexicon())
# 每阶段 ≤3 个技能
assert all(len(st["skills"]) <= 3 for st in stages)
flat = [s for st in stages for s in st["skills"]]
# 前置关系仍满足:Linux 在 Docker 前,Docker 在 Kubernetes 前
assert flat.index("Linux") < flat.index("Docker") < flat.index("Kubernetes")
assert len(stages) >= 3 # 7 个技能至少拆成 3 段
+5 -4
View File
@@ -1806,9 +1806,10 @@ function renderPath(data) {
return;
}
const weeks = stageWeeks(data.learning_path.length);
/* 阶段语义化命名:基础补齐 → 岗位核心 → 项目验证(超出 3 阶段顺延为综合提升) */
const STAGE_NAMES = ["基础补齐", "岗位核心", "项目验证"];
const stageName = (i) => STAGE_NAMES[i] || "综合提升";
const weeks = stageWeeks(data.learning_path.length);
/* 阶段语义化命名:基础补齐 → 岗位核心(可多个)→ 项目验证 */
const stageName = (i, n) =>
i === 0 ? "基础补齐" : i === n - 1 ? "项目验证" : "岗位核心";
box.innerHTML = (data._demo ? `
<div class="path-demo-banner">
<b>示范路线</b>(以零已掌握技能生成):完成一次人岗诊断后,此处将自动替换为你的个人成长路线图
@@ -1820,7 +1821,7 @@ function renderPath(data) {
<span class="stage-no">${done ? "✓" : st.stage}</span>
<div class="stage-body">
<div class="stage-head">
<span class="stage-title">阶段 ${st.stage} · ${stageName(i)}(${st.skills.length} 项技能)</span>
<span class="stage-title">阶段 ${st.stage} · ${stageName(i, data.learning_path.length)}(${st.skills.length} 项技能)</span>
<span class="stage-weeks">建议 ${weeks[i]} 周</span>
</div>
${i > 0 ? `<div class="stage-prereq">前置:建议完成阶段 ${st.stage - 1} 后开始(按技能前置关系拓扑排序)</div>` : ""}