学习路径多阶段化:拓扑层按每阶段≤3技能拆分,阶段命名改为 基础补齐→岗位核心*→项目验证;简历裸姓名/专硕识别修复
This commit is contained in:
+18
-8
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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
@@ -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>` : ""}
|
||||
|
||||
Reference in New Issue
Block a user