内容
# Canvas 知识图谱与学习助手
将您的 Canvas 课程转换为**本地 Obsidian 仓库** — 讲座幻灯片转为 Markdown,跨讲座概念图,作业提示,公告和课程大纲 — 然后通过 MCP 与 Claude 或 Gemini 进行学习,基于您自己的材料并附有引用。
本地优先:所有内容都作为您拥有的纯 Markdown 文件存储在您的本地机器上。

<sub>**示例输出** — 135 个概念和 221 个链接从某门课程的 16 次讲座中自动提取,按来源讲座着色。你可以得到**您自己的**课程图,无论课程内容是什么;这里没有任何特定于学科的内容。概念在**跨讲座**合并,因此一个概念链接到它出现的所有讲座。相同的仓库可以直接在 Obsidian 中打开以查看图谱和反向链接 — 或者使用 `tools/graph_svg.py` 为任何课程重新生成此图像。</sub>
## 为什么要做这个,而 Canvas AI 工具已经存在?
诚实的定位 — 已经有几个成熟的工具与该项目的部分功能重叠:
- **Canvas API MCP 服务器** 存在,它们更完整地封装了 Canvas API(90 多个工具,包括评分和教师功能)。如果您只需要**与 Canvas 对话**,请使用其中一个 — 这里的实时 Canvas 工具故意简化。
- **托管的学习助手** 对您的课程文件进行 RAG,并有**概念图生成器** 将上传的 PDF 转为图表。两者都是云端 SaaS:您的材料存放在他们的服务器上。
这个项目提供的内容是它们所没有的:
- **构建真正的 Obsidian 仓库** — 纯 `.md` 文件,带有 `[[wikilinks]]`,跨讲座的概念图(一个概念链接到它出现的所有讲座),以及免费的反向链接和图谱视图。
- **本地优先,您拥有数据**。没有任何内容托管;Markdown 文件在工具之外仍然存在。
- **免费使用您已经拥有的订阅** — 仓库通过 MCP 暴露,Claude(Pro/Max)或免费的 Gemini CLI 作为聊天层。无需按查询计费 API。
## 要求
- Python 3.10+
- **LibreOffice** — 用于将 `.pptx`/`.docx` 转换为 PDF 进行转录(macOS `brew install --cask libreoffice`,Debian/Ubuntu `apt install libreoffice`)。自动检测 `$PATH`;或使用 `SOFFICE=/path/to/soffice` 重写。
- **Canvas API Token**(Canvas → 账户 → 设置 → 新的访问令牌)
- **Gemini API 密钥**([免费层](https://aistudio.google.com/app/apikey))— 仅用于转录幻灯片和提取概念
## 设置
```bash
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt
cp .env.example .env # 然后填写
```
`.env`(gitignored — 永远不要提交它):
```
CANVAS_URL=https://<您的学校>.instructure.com
CANVAS_TOKEN=<您的 Canvas 访问令牌>
GEMINI_API_KEY=<您的 Gemini API 密钥>
# CANVAS_TZ=America/New_York # 可选;默认为您的 Canvas 账户时区
```
## 同步您的课程
```bash
.venv/bin/python sync.py # 您当前学期正在上的所有课程
.venv/bin/python sync.py --list # 显示检测到的课程,不做任何更改
.venv/bin/python sync.py --only CS101 # 只同步一门课程
```
每个课程:**导入**(文件 + 作业 → 通过 Gemini Vision 转为 Markdown)→ **更新**(公告 + 课程大纲)→ **提取**(概念图)→ **仪表盘**,然后是一个带有标签的搜索索引。
> **第一次运行需要一些时间**(数 MB 的幻灯片通过视觉模型)并且可能会达到 Gemini 免费层的每日配额。它可以恢复 — 所有内容都经过哈希缓存,因此只需重新运行即可;已完成的文件不会产生额外费用,也不会被重新下载。如果课程的“文件”选项卡被禁用,则该课程将降级为仅限作业,而不是失败。
个别步骤仍然有效:`ingest.py <course_id>`,`extract.py <SLUG>`,`updates.py <course_id>`,`dashboard.py`,`chat.py index`。
### 跟上课程进度
课程每天都在变化 — 公告、新幻灯片、新作业。重新运行 `sync.py` 是增量更新:未更改的文件不会被重新下载,缓存的文件不会重新访问模型,搜索索引仅更新更改的内容。它完成后会告诉您有什么新内容:
```
What's new:
DS4400
- announcement: 2026-07-28 — Dan Office Hours 7/28
- new assignment: Homework #4
index: 3 chunk(s) updated
```
有两种方法可以保持更新,它们可以一起使用:
```bash
tools/install-daily-sync.sh # macOS:每天早上 07:30 同步(launchd)
tools/install-daily-sync.sh 18 00 # ...或其他时间
tools/install-daily-sync.sh --uninstall
```
计划任务运行 `sync.py --quiet`,它只会在实际更改时写入 `cache/sync.log` — 不会有每日噪音。在 Linux 上,可以使用 cron 实现相同的效果:`30 7 * * * cd /path/to/repo && .venv/bin/python sync.py --quiet`。
或者直接询问您的 LLM — MCP `refresh` 工具按需同步:`"检查我的课程是否有新内容。"`
### 布局
```
notes/<SLUG>/ 转录的 Markdown(讲座,作业,代码,公告)
vault/Dashboard.md 所有课程的截止日期
vault/<SLUG>/ concepts/ lectures/ updates/ Dashboard.md <- 在 Obsidian 中打开
```
在 Obsidian 中打开 `vault/` 文件夹作为图谱、反向链接和仪表盘的仓库。
## 使用 LLM 学习 — 仓库 + MCP
`mcp_server.py` 将您的课程和实时 Canvas 暴露给任何 MCP 客户端(Claude Desktop,Claude Code,Gemini CLI):`list_courses`,`upcoming_assignments`,`announcements`,`syllabus`,`search_notes`(对您的幻灯片、作业和笔记本进行语义搜索),`concept`,以及 `refresh`(按需从 Canvas 拉取任何新内容)。每个工具都接受一个可选的 `course` 标识 — 省略它以跨所有课程。
### 连接客户端
每个 MCP 客户端都使用**相同的服务器定义** — 只有文件位置不同。使用**绝对路径**:客户端不在仓库目录中运行。
```json
{
"mcpServers": {
"canvas": {
"command": "/absolute/path/to/canvas-obsidian/.venv/bin/python",
"args": ["/absolute/path/to/canvas-obsidian/mcp_server.py"]
}
}
}
```
| 客户端 | 配置文件位置 |
|---|---|
| **Claude Code** | `.mcp.json` — 已包含在此仓库中,自动加载 |
| **Claude Desktop** | macOS `~/Library/Application Support/Claude/claude_desktop_config.json` · Windows `%APPDATA%\Claude\claude_desktop_config.json` · Linux `~/.config/Claude/claude_desktop_config.json` |
| **Gemini CLI** | `~/.gemini/settings.json`(或 `.gemini/settings.json`) |
| **Cursor** | `.cursor/mcp.json`(或 `~/.cursor/mcp.json`) |
| **VS Code / Copilot** | `.vscode/mcp.json` — 注意 VS Code 将服务器嵌套在 `"servers"` 下,而不是 `"mcpServers"` |
| **Continue** | `.continue/mcpServers/mcp.json` |
如果文件中已经存在 `mcpServers` 对象,请将其合并 — 不要覆盖。重启客户端后,大多数客户端只会在启动时读取配置文件。
**关于 Groq、OpenAI 和其他提供商的说明:** MCP 是一个*客户端*协议,而不是模型 API。Groq、OpenAI 和 Together 是推理提供商 — 它们自己不连接 MCP 服务器。要将这些模型与此服务器一起使用,请将支持自定义提供商的 MCP 客户端(Continue、Cline、LibreChat、Goose)指向它们;上面的服务器定义保持不变。零额外成本路径是 Claude(Pro/Max 订阅)和 Gemini CLI 免费层。
### 实际提问
客户端会自动选择合适的工具 — 您只需要正常聊天。关于课程内容的答案基于*您的*材料,并引用了讲座和章节,因此您可以查看源内容。
**跟上课程进度**
- "本周所有课程有哪些截止日期?"
- "我有没有错过任何公告?考试有什么变化吗?"
- "我的 ML 课程中的迟交政策是什么?" *(读取课程大纲)*
**理解某个概念**
- "按照我的教授讲解的方式,而不是教材版本,解释梯度下降。"
- "我不明白第 12 张幻灯片的 Lecture 8 — 给我讲解一下。"
- "我的笔记在两个不同的讲座中提到了 KL 散度。它们是不是在说同一件事?"
**考试准备** — 这是它发挥作用的地方:
- "这里是期中考试 2 的主题列表。对于每个主题,请提取我的讲座中实际涉及的内容,并告诉我哪些主题我的理解不足。"
- "使用我的课程材料,为期中考试 2 制作一份一页的备忘录。"
- "用熵和交叉熵来问我问题。一次一个问题,根据我的答案检查并告诉我错在哪里。"
- "学习指南中哪些主题在我的笔记中出现频率最低?" *(找出您在考试中可能发现的空白)*
**跟踪想法之间的关联** — 概念图使得这一点成为可能:
- "在 MLE 有意义之前,我需要理解什么?追溯先决条件。"
- "正则化在哪些讲座中出现过,处理方法有什么不同?"
- "在我的课程中,与偏差-方差权衡相关的所有内容都显示出来。"
**作业和代码**
- "作业 3 实际上要求什么?将它分解成步骤。" *(读取作业提示 PDF)*
- "哪个讲座涵盖了问题 2 需要的方法?"
- "找到我们从头开始实现梯度下降的笔记本,并解释循环的工作原理。"
如果某些内容不在您的同步材料中,它会告诉您,而不是编造答案。当它出错时,引用会告诉您要检查哪张幻灯片。
> 目的是为了理解您自己的材料 — 您如何使用它来完成评估作业取决于您和您的课程的学术诚信政策。
## 隐私、成本和条款
- **您的 Canvas 令牌存储在 `.env` 中**,由该工具读取,不会通过 MCP 或任何模型发送。
- **课程内容在导入过程中发送到 Google 的 Gemini API**(幻灯片、作业提示)。如果您的材料不适合使用此功能,请不要导入。
- **单用户、自行托管设计**。Instructure 的 API 条款禁止与第三方共享您的令牌 — 使用您自己的令牌自行托管是可以的;提供托管服务给其他学生是不允许的。
- 导入成本在 Gemini 的免费层(较慢)下为 $0;聊天成本在 Claude 订阅或免费的 Gemini CLI 上为 $0。
## 开发
```bash
.venv/bin/python -m pytest -q
```
将某门课程的概念图渲染为独立的 SVG(这是上面生成的图像 — 布局已固定,因此重新生成不会产生差异):
```bash
.venv/bin/python tools/graph_svg.py DS4400
.venv/bin/python tools/graph_svg.py DS4400 --size 2000x1100 --label-degree 8 --out docs/ml.svg
```
测试需要构建的仓库会自动跳过。
两个**开发**工具根据手动标记的黄金集评估输出质量。两者都为特定的一门课程编写 — 换成您自己的案例来使用它们:
```bash
.venv/bin/python eval_graph.py # 概念图:链接召回 + 无噪音节点排除
.venv/bin/python eval_retrieval.py # RAG:现实问题场景下的召回率@k 和 MRR
```
它们的存在是因为“输出看起来不错”不是一种衡量标准:提示调整、分割器更改或不同的嵌入模型可以悄悄地降低质量,而这些正是它们所能检测到的。
## 许可证
MIT — 见 [LICENSE](LICENSE)。