课程大作业 · 数学分析知识图谱

项目要求与材料边界

源目录保留的是小组任务讨论和最终项目仓库,没有一份冻结不变的教师题面。现有材料能确认的项目目标是:把数学分析知识点组织成知识图谱,并围绕图谱完成可浏览、可检索或可辅助学习的应用展示。我在下面以最终仓库代码和数据为准记录实际提交,不把早期讨论过但后来废弃的方案写成已完成能力。

查看项目提交与实现边界

我们最后真正做成了什么

小组最早讨论过一套更重的方案:用 Ollama 生成候选语义关系,在标注界面中审核出 Gold Set,比较 prompt v1 与 v2,再把结果导入 Neo4j,完成知识图谱约束的问答演示。

这套旧 Neo4j 方案后来被明确废弃。当前主分支已经删除 Neo4j、自动候选生成、标注界面和 prompt 闭环,转而把重点放在一份可审查的手写数学分析知识图谱,以及零数据库、零构建的静态浏览器上。旧任务文档只保留为方案讨论背景,不能当作已经交付的功能。

当前主分支的数据流非常直接:

data/build_kg.py
  → 注册节点、父子层级和显式语义关系
  → 生成 data/kg.json
  → 浏览器加载 JSON
  → Cytoscape.js 绘制局部知识网络

我们以构建脚本和生成的 JSON 为现状依据。项目 README 仍有“根 → 册 → 章 → 节 → 知识点”的旧表述,但实际数据已经改成按知识体系组织:

数学分析
  → 6 个主题领域
  → 20 章
  → 101 节
  → 455 个知识点

六个主题领域分别是实数与极限、单变量微分学、单变量积分学、多变量微分学、多变量积分学、级数与微分方程。它们不是教材上、下册节点。

图谱规模与关系语义

当前图谱共有 583 个节点。层级分布为:

层级内容节点数
0课程根节点1
1主题领域6
2章20
3节101
4知识点455
合计583

JSON 中存有 634 条显式语义边,分为 7 类:

关系含义数量
PREREQUISITE_OF前者是理解后者的前置知识166
USED_IN前者的方法或结论应用于后者171
GENERALIZES前者推广后者78
SPECIAL_CASE_OF前者是后者的特例41
SIMILAR_TO两者结构或方法相似39
EASILY_CONFUSED_WITH两者容易混淆,需要辨析25
RELATED_TO两者存在较弱的相关联系114
合计634

CONTAINS 为什么不在 634 条边里

层级包含关系没有重复写进显式边数组。除根节点外,其余 582 个节点各自保存一个 parent_id;浏览器启动时据此建立父子索引,在打开“显示层级”后,动态合成 parent → child 的 CONTAINS 边。

因此,关系类型元数据里虽然有 CONTAINS,生成统计中的显式数量却是 0。右侧详情仍会始终显示“包含”和“属于”,图上的层级边则由开关决定是否绘制。不能把 634 条显式语义边与 582 条隐式层级边混成一个数字。

数据生成时做了哪些校验

build_kg.py 用两个小接口集中注册数据:add() 添加节点,link() 添加语义关系。当前校验逻辑是:

  1. 节点 ID 重复会直接报错;
  2. parent_id 必须指向一个已经注册的节点,因此数据要按父节点在前的顺序构建;
  3. 关系两端的节点都必须存在;
  4. 关系不能形成自环;
  5. 完全相同的 (source, target, type) 再次出现时会被集合去重并直接忽略。

最后一点也修正了 README 的一句旧描述:重复边不会“立刻报错”,实际行为是静默去重。当前辅助函数也没有显式检查关系名是否属于类型白名单,或检查节点层级数字是否与父子层级严格相差 1;这些仍需要维护者审查。生成脚本会把节点数、边数、层级分布和关系类型分布写进 JSON,方便做结果校验。

静态浏览器怎样运行

修改数据后先重新生成 JSON:

python3 data/build_kg.py
python3 -c "import json; data = json.load(open('data/kg.json')); print(data['stats'])"

再启动静态服务:

./serve.sh

Windows 下使用:

serve.bat

浏览器访问 http://127.0.0.1:8000/viewer/。当前主分支使用原生 JavaScript 和 Cytoscape.js,不需要数据库、后端 API 或前端构建步骤;不过 Cytoscape.js 从 CDN 加载,所以第一次打开仍依赖网络或浏览器缓存。

页面支持:

  • 从左侧树逐级浏览六个领域、章、节和知识点;
  • 按名称片段搜索并定位节点;
  • 查看选中节点周围默认 2 跳的局部子图;
  • 按 7 类语义关系筛选边;
  • 单独切换动态 CONTAINS 层级边;
  • 在详情栏按关系类型查看出边、入边、父节点和子节点。

最小人工验收可以搜索“泰勒公式”“Stokes 公式”“条件收敛”,检查能否定位节点、展开合理的局部子图,并看到按类型分组的邻居。

当前主分支与展示分支不能混写

项目还有一个独立的 final_branch_for_PPT 展示分支。它保留相同的 583 个节点和 634 条显式语义边,但扩展了查询、学习路径和可选的本地大模型服务。这些能力没有进入当前主分支。

能力当前主分支final_branch_for_PPT
静态图谱浏览有有
Cytoscape.jsCDN 加载本地文件
节点、关系实例查询无有
最短路径查询无有,BFS 最多 4 跳
学习路径助手无有,确定性图规则
错题分析 API无有,可选 Ollama
Neo4j、向量数据库无无

展示分支的路径查询允许沿一条边的两个方向搜索,但结果仍用箭头标出图谱中真实的边方向。学习路径助手根据 PREREQUISITE_OF、USED_IN、GENERALIZES 等现有关系组织先修、后续应用和易混淆知识,不调用大模型。

展示分支的静态服务与 AI 服务

在展示分支中,静态模式仍然可以直接运行:

./serve.sh

如果要启用错题分析,需要本地 Ollama 模型,再运行 AI 服务:

ollama run qwen2.5:7b
./serve_ai.sh

二者都在 http://127.0.0.1:8000/viewer/ 提供页面,同一端口上只能选择一种服务。AI 服务除了静态文件,还提供:

  • GET /api/models:读取本地 Ollama 模型列表;
  • POST /api/analyze-problem:接收题目并返回图谱约束的分析结果。

默认模型是 qwen2.5:7b,默认等待时间为 300 秒。较慢模型可以这样调整:

OLLAMA_MODEL=qwen3.5:9b OLLAMA_TIMEOUT=420 ./serve_ai.sh

它不需要 BGE、向量数据库、Neo4j 或额外 Python 依赖。

Ollama 错题分析的完整检索链

展示分支没有把整张图直接塞给大模型,也没有让模型自由编造节点。检索和约束链如下:

题目文本
  → 正则保留拉丁词和数字,并切出中文 2~4 gram
  → 与每个节点的富文本计算匹配分数
  → 取前 36 个文本候选
  → 对最强 8 个候选做一跳图扩散,邻居继承 0.4 倍分数
  → 截取最终 top-k 候选节点
  → 把候选 ID 与题目交给 Ollama,要求输出 JSON
  → 后端再次校验节点 ID,丢弃候选集外的内容
  → 返回考点、条件检查、解题提示、易错点和图谱证据
  → 前端高亮命中节点与直接关系

节点富文本由名称、简介、层级路径和部分相邻关系说明组成。基础召回使用 IDF 加权的词项重合,让稀有专业词比“函数”“求”等高频词更重要;节点全名直接出现在题面时额外加分。

为了补足题面不会明说标准考点名的问题,检索器还会识别分段点求导、振荡极限、广义积分、交错级数、Taylor 展开等题型结构,把相应概念词加入匹配。随后用“级数还是积分”等领域信号消解跨章节同名概念,并通过层级先验偏向知识点和节,弱化根节点与领域节点。这些规则匹配真实名称和文本,不直接写死演示用节点 ID。

大模型只能从候选 ID 中选考点。后端最多接收 8 个有效节点,并再次过滤不存在或不在候选集中的 ID;学习路线也会去重和限长。因此,大模型负责理解题面和组织解释,知识图谱负责限定可选事实范围。

Ollama 不可用时怎样降级

Ollama 未启动、请求超时、返回内容无法解析,或者返回的节点 ID 全部不在候选集合中时,服务会自动退回本地结果:选取分数最高的 6 个候选,按相对分数分配权重,并用这些节点生成图谱证据和高亮路线。

降级模式会明确返回 warning,也会说明当前只有文本与图邻域匹配,没有大模型生成的条件分析和题目解释。如果连本地检索都没有命中,则返回空结果并提示检查题目是否属于数学分析图谱范围。这样,即使本地模型失效,图谱检索和可视化仍能工作,但不能把降级结果描述成完整的 AI 分析。

评论