课程大作业 · 数学分析知识图谱
项目要求与材料边界
源目录保留的是小组任务讨论和最终项目仓库,没有一份冻结不变的教师题面。现有材料能确认的项目目标是:把数学分析知识点组织成知识图谱,并围绕图谱完成可浏览、可检索或可辅助学习的应用展示。我在下面以最终仓库代码和数据为准记录实际提交,不把早期讨论过但后来废弃的方案写成已完成能力。
查看项目提交与实现边界
我们最后真正做成了什么
小组最早讨论过一套更重的方案:用 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() 添加语义关系。当前校验逻辑是:
- 节点 ID 重复会直接报错;
parent_id必须指向一个已经注册的节点,因此数据要按父节点在前的顺序构建;- 关系两端的节点都必须存在;
- 关系不能形成自环;
- 完全相同的
(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.js | CDN 加载 | 本地文件 |
| 节点、关系实例查询 | 无 | 有 |
| 最短路径查询 | 无 | 有,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 分析。