1. 项目概述:一个为AI智能体设计的D2L Brightspace只读命令行工具

如果你是一名学生,同时又是开发者,或者你正在尝试用AI助手(比如Claude Code、Cursor的AI)来提升学习效率,那你肯定对在D2L Brightspace(国内常称为“学习通”的国际版)里反复点击、翻找课程资料、成绩和作业截止日期的过程感到厌倦。 d2l-cli 这个项目,就是为解决这个痛点而生的。它本质上是一个Python编写的命令行工具,核心功能是帮你把D2L平台上的所有只读信息——成绩、作业、课程内容、大纲、讨论区——统统拉到本地,并以结构化的方式(表格、JSON、Markdown)呈现出来。但它的真正精髓在于,它是 专为AI智能体(AI Agent)设计的 。这意味着,你不用再手动敲命令,而是可以直接对你的AI助手说:“帮我看看这周数据结构的作业是什么?”或者“我这学期微积分的成绩趋势怎么样?”,AI会理解你的意图,并自动调用 d2l-cli 来获取答案。

我最初接触这个工具,是因为受够了在多个课程页面间切换查看截止日期。手动整理太耗时,而现有的浏览器插件要么功能不全,要么不适合自动化。 d2l-cli 吸引我的地方在于它的“API化”思路:它通过逆向工程D2L的官方API(尽管是只读的),提供了一个干净、一致的命令行接口。这对于希望将学习数据接入自己工作流(比如生成学习周报、自动备份资料)的开发者来说,是一个极佳的起点。更重要的是,它的输出格式(特别是 --md --json )是天然为AI对话设计的,使得AI助手能轻易地解析信息并生成对人类友好的总结。

2. 核心设计思路:为什么是“只读CLI”加“AI Agent就绪”?

2.1 定位解析:解决信息获取的“最后一公里”问题

D2L Brightspace作为一个功能完备的LMS(学习管理系统),其前端界面是为人类交互设计的,而非机器。虽然它提供官方API,但直接使用其REST API对于普通学生甚至开发者来说,门槛依然不低:需要处理OAuth2认证、理解复杂的API端点结构、解析嵌套的JSON响应。 d2l-cli 所做的,就是封装了所有这些复杂性。

它的设计定位非常清晰:

  1. 只读(Read-Only) :这是一个关键的安全与伦理设计。工具明确声明不执行任何POST、PUT、DELETE操作。这意味着它无法替你提交作业、发布帖子或修改任何数据,完全避免了误操作或学术不端行为的风险。从技术实现上看,它的所有请求都严格限定为HTTP GET方法。
  2. 命令行优先(CLI-First) :CLI是自动化的基石。通过命令行,你可以轻松地将它集成到Shell脚本、定时任务(Cron)或其他程序中。例如,你可以写一个脚本,每天上午8点运行 d2l due --days 1 ,将当天截止的作业列表发送到你的Telegram或邮箱。
  3. AI Agent就绪(AI-Agent-Ready) :这是项目最具前瞻性的部分。它不仅仅是提供了一个CLI,还包含了完整的AI Agent集成指南( AGENTS.md )和针对特定Agent(如Claude Code)的技能文件(Skill File)。这相当于为AI助手预装了一个“D2L信息查询”的插件,让AI具备了直接与你的学习数据对话的能力。

2.2 技术选型与架构考量

项目采用Python实现,这是一个明智的选择。Python拥有丰富的网络请求( requests , httpx )、HTML解析( beautifulsoup4 )和浏览器自动化( playwright )库生态,非常适合完成此类“网页抓取+API调用”的混合任务。

其架构大致可以分为三层:

  1. 认证层 :处理最棘手的D2L Bearer Token(JWT)获取。它提供了两种方式:基于Playwright的浏览器自动化登录(模拟用户登录流程截取Token)和手动配置Token。前者用户体验好,后者更适合无头服务器环境。
  2. API客户端层 :封装了对D2L Brightspace REST API的各种调用。这里需要仔细研究网络请求,找到正确的API端点(如 /d2l/api/lp/(version)/enrollments/myenrollments/ 用于获取课程列表)。项目代码需要处理分页、错误重试、速率限制等细节。
  3. 命令层 :使用像 click argparse 这样的库来构建命令行界面,将不同的功能( grades , assignments , download )映射为子命令,并处理参数解析、输出格式化(文本表格、JSON、Markdown)。

对于SimpleSyllabus(一个常见的课程大纲插件)的集成,项目采用了另一种思路。因为大纲数据可能来自独立的第三方服务,所以它没有直接使用D2L API,而是通过分析SimpleSyllabus网站自身的API(通过浏览器开发者工具捕获),实现了直接的数据获取。这体现了开发者解决实际问题的灵活性和对细节的把握。

3. 从零开始:环境配置与初次认证实战

3.1 基础环境搭建

假设你使用的是macOS或Linux系统(Windows用户请将 bin 替换为 Scripts ),我们从头开始。

# 1. 克隆仓库
git clone https://github.com/Aaryan-Kapoor/d2l-cli.git
cd d2l-cli

# 2. 创建并激活虚拟环境(强烈推荐,避免污染全局Python环境)
python -m venv .venv
source .venv/bin/activate  # 激活后,命令行提示符前通常会出现 (.venv)

# 3. 安装核心依赖
pip install -e .

pip install -e . 中的 -e 代表“可编辑模式”安装。这会将当前目录以“开发模式”链接到Python的site-packages中。这样,你后续在 src/ 目录下修改任何代码,都会立即生效,无需重新安装。

注意 :项目要求Python 3.10+。请务必使用 python --version 确认版本。如果系统默认版本过低,你可能需要先安装更高版本的Python,或者使用 python3 命令。

3.2 关键配置详解

安装完成后,不要急着运行。首先需要配置你的学校信息。核心配置文件在 src/d2l/config.py

# 打开并编辑这个文件
LMS_HOST = "https://your-school.view.usg.edu"  # 你的D2L Brightspace登录地址
TENANT_ID = "your-tenant-id-here"               # 租户ID,需要从浏览器中获取
  • LMS_HOST :这个就是你的学校D2L主页的URL。通常以 https://[学校名].brightspace.com 或类似形式出现。 千万不要带结尾的斜杠
  • TENANT_ID :这是D2L多租户架构的标识。获取它需要一点技巧:
    1. 登录你的D2L网站。
    2. 打开浏览器开发者工具(F12),切换到“网络”(Network)标签页。
    3. 刷新页面,在网络请求列表中,找到一个指向 *.api.brightspace.com 域名的请求。
    4. 点击该请求,在“标头”(Headers)部分,找到请求URL。你会在URL路径中看到类似 .../versions/...?tenantId=123456 的参数,或者直接在请求头里找。 TENANT_ID 就是那个数字ID。如果找不到,也可以尝试在页面HTML源码中搜索 tenantId

如果你的学校使用了SimpleSyllabus,还需要配置 src/d2l/commands/syllabus.py 中的URL:

SYLLABUS_SEARCH_URL = "https://your-school.simplesyllabus.com/api2/syllabus-search"
SYLLABUS_FULL_URL = "https://your-school.simplesyllabus.com/api2/doc-full-page-get"

your-school 替换为你的学校子域名。

3.3 认证实战:两种获取Token的方法

D2L API使用Bearer Token(JWT)进行认证,这个Token大约每小时过期一次。

方法A:浏览器自动捕获(推荐给个人用户)

首先,需要安装额外的登录支持包和浏览器驱动。

pip install -e ".[login]"  # 安装playwright等依赖
playwright install chromium  # 安装Chromium浏览器,用于无头操作

然后运行登录命令:

d2l login

这个命令会:

  1. 启动一个Chromium浏览器实例。
  2. 导航到你配置的 LMS_HOST
  3. 模拟登录流程(你需要手动输入用户名密码,完成学校的SSO认证,如Duo Push等)。
  4. 登录成功后,工具会监控浏览器与D2L API的通信,自动捕获 Authorization: Bearer ... 这个请求头,并将Token及相关信息(过期时间、用户ID等)保存到 ~/.d2l/token.json 文件中。

实操心得 :第一次运行 d2l login 可能会因为浏览器弹窗或学校复杂的SSO流程而失败。确保你的系统有图形界面。如果遇到问题,可以尝试 d2l login --headless ,它会在后台无界面模式下运行,但前提是你之前已经成功登录过且浏览器保存了会话Cookie。

方法B:手动获取Token(适用于服务器或无GUI环境)

  1. 在已登录D2L的浏览器中,按F12打开开发者工具。
  2. 进入“网络”标签,筛选 XHR Fetch 请求。
  3. 刷新D2L页面,在请求列表中找到任意一个指向 https://[你的学校].api.brightspace.com/... 的请求。
  4. 点击该请求,在右侧“标头”部分,找到“请求标头”下的 Authorization 。其值类似 Bearer eyJhbGciOiJ... (一串很长的字符串)。
  5. 复制整个 Bearer 后面的Token字符串(不包括单词 Bearer 和空格)。
  6. 在终端中,创建目录和文件:
    mkdir -p ~/.d2l
    
    然后编辑 ~/.d2l/token.json ,填入以下内容(需要计算过期时间戳 exp ,通常捕获时间 captured_at 加3600秒即可):
    {
      "token": "你复制的很长很长的JWT Token",
      "exp": 1741234567,
      "sub": "你的用户ID(可从Token解码或API获取)",
      "tenant": "你的TENANT_ID",
      "captured_at": 1741230967
    }
    
    你可以使用 jwt.io 解码Token(不要泄露给不可信的网站),来获取 sub (用户ID)和 exp (过期时间戳)字段。 captured_at 可以设为当前时间的时间戳。

验证Token : 配置和认证完成后,运行以下命令测试:

d2l token  # 查看Token状态(本地检查,不调用API)
d2l whoami # 调用API获取当前用户信息,验证Token是否有效

如果 whoami 成功返回你的姓名和用户ID,恭喜你,最困难的一步已经完成了。

4. 核心命令深度使用与AI集成指南

4.1 课程与学术信息查询

安装配置好后,你就可以像使用普通命令行工具一样查询信息了。所有命令都支持模糊匹配课程名称。

# 1. 列出本学期所有课程
d2l courses
# 输出是一个清晰的表格,包含课程ID、名称、代码和学期。

# 2. 查看特定课程的成绩(例如课程名包含“data”)
d2l grades "data"
# 默认显示当前成绩项(作业、测验等)的分数和权重。

# 3. 使用 --final 标志查看计算后的课程总评成绩(如果老师已发布)
d2l grades "calculus" --final

# 4. 查看某门课的所有作业
d2l assignments "introduction to programming"
# 会列出作业名称、状态、截止日期、是否已提交等信息。

# 5. 查看所有课程的近期截止事项(默认未来7天)
d2l due
# 这是我最常用的命令之一,它能从所有课程中聚合出即将到期的作业和测验。

# 6. 查看已过期的任务
d2l overdue

# 7. 获取课程大纲(如果学校使用SimpleSyllabus)
d2l syllabus "physics 101"
# 这个命令非常有用,它能直接抓取完整的、格式化的课程大纲PDF或HTML,其中包含了评分政策、课程安排等关键信息。

4.2 内容管理与文件下载

除了查看信息, d2l-cli 还能帮你把资料“搬”下来。

# 1. 查看课程的内容模块结构
d2l content "web development" --toc
# `--toc` 参数会以树状图形式展示模块和主题,让你对课程结构一目了然。

# 2. 下载特定作业的附件(比如老师提供的starter code)
d2l download "data structures" "assignment 5" -o ./ds_a5
# 这个命令会解析指定作业,找到所有附件(ZIP, PDF等)并下载到 `./ds_a5` 目录。

# 3. 下载整个内容模块的文件
d2l download-content "calculus" "week 3 lectures" -o ./calc_notes
# 这会遍历“week 3 lectures”模块下的所有主题,下载其中的PPT、PDF、视频链接等资源。

注意事项 :下载功能依赖于D2L内容页面的HTML结构。如果你们学校的D2L界面经过深度定制,下载链接的提取可能会失败。此时需要查看 src/d2l/commands/download.py 中的解析逻辑,可能需要根据实际情况调整CSS选择器或正则表达式。

4.3 输出格式:为人类和机器准备的三种视图

这是 d2l-cli 设计上的一个亮点,它提供了三种输出格式以适应不同场景。

格式 命令示例 适用场景 特点
默认表格 d2l grades 人类在终端快速阅读 对齐的、彩色的(如果终端支持)文本表格,直观美观。
JSON d2l --json grades 脚本处理、二次开发 结构化的数据,方便用 jq 过滤或用Python的 json 库解析。例如:`d2l --json due
Markdown d2l --md grades AI Agent交互 、生成报告 纯文本Markdown格式,包含完整上下文(如课程ID、日期ISO格式)。AI最容易理解和处理这种格式。

为什么 --md 格式对AI如此重要? 当你问AI:“我这学期成绩怎么样?”AI需要调用 d2l-cli 。如果返回的是JSON,AI需要先解析数据结构再组织语言。如果返回的是纯文本表格,AI可能难以精确提取字段。而 --md 格式是两者的折中:它既是人类可读的(有标题、列表),又保留了机器可读的结构化信息(比如用 **Course:** 明确标识字段),极大降低了AI的理解和转换成本。项目中的 AGENTS.md 文件就是教AI如何正确使用这些命令和解析 --md 输出的“说明书”。

4.4 与AI智能体深度集成:以Claude Code为例

这才是 d2l-cli 的“杀手级”应用场景。我们以目前非常流行的Claude Code(集成在Cursor、Windsurf等编辑器中的Claude)为例。

步骤1:为AI准备环境 AI智能体(Agent)需要在它自己的运行时环境中能访问到 d2l 命令。最简单的方法就是在你启动AI对话的项目根目录下,也安装一份 d2l-cli

# 在你的项目目录下
cd /path/to/your/workspace
# 假设d2l-cli仓库在隔壁目录
pip install -e ../d2l-cli
# 确保token已配置(~/.d2l/token.json 已存在且有效)

步骤2:让AI“学会”使用这个工具 d2l-cli 仓库根目录下的 AGENTS.md 文件,就是写给AI看的完整工具文档。你需要让AI知道这个文件的存在。

  • 在Claude Code中 :你可以直接上传 AGENTS.md 文件,或者将它的内容粘贴到对话中,并告诉Claude:“这是你用来查询我D2L学习信息的工具文档,请根据它来回答我的问题。”
  • 更优雅的方式 :对于Claude Code,项目还提供了 .claude/skills/d2l/SKILL.md 文件。你可以将这个技能文件复制到你的项目目录下的 .claude/skills/ 文件夹中。Claude Code会自动加载这个技能,使其成为一个可用的“工具”。

步骤3:开始自然语言对话 完成以上步骤后,你就可以像和助手说话一样提问了:

  • 帮我看看下周有哪些作业要交。
    • AI会理解意图,在后台执行 d2l due --days 7 --md ,解析返回的Markdown,然后生成一个清晰的列表告诉你。
  • 我在‘数据科学与机器学习’这门课上的当前成绩是多少?把每次作业的分数都列出来。
    • AI会执行 d2l --md grades "data science" ,然后从输出中提取表格,并可能帮你计算一下当前加权平均分。
  • 把‘计算机视觉’课程第三周的所有讲义下载到当前目录的 cv_week3 文件夹里。
    • AI会执行 d2l download-content "computer vision" "week 3" -o ./cv_week3 并告诉你下载结果。

步骤4:进阶用法——生成学习周报 你可以给AI更复杂的任务: “ 基于我过去一周的D2L活动,生成一份学习周报。包括:1) 新发布的公告,2) 已提交和待提交的作业,3) 所有课程的最新成绩变化。用Markdown格式输出,并给出下周的学习建议。 ” AI可能会组合调用多个命令:

d2l --json news --since 7
d2l --json assignments --all
d2l --json grades --all

然后综合分析这些JSON数据,生成一份结构化的周报。

4.5 无头服务器与自动化部署

如果你想在云服务器或树莓派上运行一个定时查询任务,就需要“无头”(Headless)环境。

  1. 初始Token获取 :你仍需在一个有图形界面的机器上(比如你的笔记本电脑)运行一次 d2l login 。这会生成 ~/.d2l/token.json ~/.d2l/browser_context (Playwright的浏览器会话数据)。
  2. 迁移数据 :将整个 ~/.d2l/ 目录打包复制到你的服务器上。
  3. 设置定时刷新 :Token一小时过期,但浏览器会话Cookie可能持续数天甚至数周。我们可以设置一个Cron任务,定期用 --headless 模式重新登录(利用已保存的Cookie,无需再次输入密码)。
    # 编辑crontab: crontab -e
    # 每隔45分钟运行一次登录命令(在虚拟环境中)
    */45 * * * * cd /path/to/d2l-cli && /path/to/.venv/bin/d2l login --headless > /tmp/d2l-refresh.log 2>&1
    
    --headless 模式会使用之前保存的浏览器上下文静默刷新Token。
  4. 定时任务示例 :每天早晨8点,查询当天截止的作业,并发送通知。
    # 一个简单的Shell脚本 /home/user/check_due.sh
    #!/bin/bash
    source /path/to/d2l-cli/.venv/bin/activate
    OUTPUT=$(d2l due --days 1 --md)
    if [ -n "$OUTPUT" ]; then
        echo "$OUTPUT" | mail -s "今日D2L截止提醒" your-email@example.com
        # 或者调用Telegram/Bark的API发送到手机
    fi
    
    然后在Cron中添加: 0 8 * * * /bin/bash /home/user/check_due.sh

5. 常见问题排查与实战经验分享

即使按照指南操作,在实际部署和使用中,你仍可能会遇到一些问题。下面是我在深度使用过程中总结的一些典型问题和解决方案。

5.1 认证与登录失败

问题1: d2l login 打开浏览器后,页面卡在学校的SSO登录页,无法自动跳转/捕获Token。

  • 原因 :学校的SSO流程(如使用Duo Mobile、Microsoft Entra ID)可能有额外的安全挑战(点击推送、输入验证码),Playwright脚本无法自动处理这些交互。
  • 解决方案
    1. 使用 d2l login --headless 试试。有时无头模式反而更稳定。
    2. 如果不行,退而求其次,采用 手动获取Token 的方法。虽然麻烦一次,但一劳永逸。
    3. 检查 config.py 中的 LMS_HOST 是否正确。确保它是你实际登录的首页地址,而不是某个深层链接。

问题2:Token频繁过期,即使刚获取不久。

  • 原因 :D2L的JWT Token有效期通常是1小时,但可能因服务器配置而异。另外,如果你在多台设备上同时使用同一个账号,可能会使之前的Token失效。
  • 解决方案
    1. 运行 d2l token 检查Token的过期时间( exp )。确认是否真的过期。
    2. 实施上述的Cron自动刷新方案,确保服务器上的Token始终新鲜。
    3. 考虑编写一个简单的包装脚本,在执行任何 d2l 命令前先检查Token有效期,如果临近过期(如剩余少于5分钟),则自动调用 d2l login --headless 刷新。

5.2 命令执行报错或返回空数据

问题3:运行 d2l courses d2l grades 返回空列表或404错误。

  • 原因A:Tenant ID配置错误。 这是最常见的原因。API请求的URL中需要正确的租户ID。
  • 排查 :运行 d2l whoami 。如果失败,大概率是认证或Tenant ID问题。再次确认 config.py 中的 TENANT_ID 是否与浏览器捕获的一致。
  • 原因B:API端点不匹配。 不同学校或不同版本的D2L,API路径可能略有差异。
  • 排查 :打开浏览器开发者工具,在成功加载课程列表的页面,观察网络请求中真正的API调用地址。与 d2l-cli 代码中(如 src/d2l/api/courses.py )拼接的URL进行对比。你可能需要根据实际情况微调代码中的路径模板。

问题4: d2l syllabus 命令无法获取大纲,返回“Syllabus not found”或类似错误。

  • 原因 :你的学校可能没有使用SimpleSyllabus,或者使用了但域名不同。
  • 解决方案
    1. 手动访问一门课程的大纲页面。
    2. 打开开发者工具,搜索网络请求中包含 syllabus simplesyllabus 的API调用。
    3. 将找到的URL更新到 src/d2l/commands/syllabus.py 的配置中。
    4. 如果学校根本不用SimpleSyllabus,那么这个功能就无法使用。你可以尝试查看 d2l content 命令的输出,大纲有时会作为一个普通的HTML文件或PDF存在于课程内容模块中。

5.3 与AI智能体协作时的调试技巧

问题5:AI助手无法正确调用 d2l 命令,或解析输出有误。

  • 原因 :AI没有获得正确的上下文或指令。
  • 解决方案
    1. 明确指令 :在提问时,更明确地指示AI使用工具。例如:“请使用 d2l-cli 工具,运行 d2l --md due --days 3 来获取我未来三天的截止任务,然后总结给我听。”
    2. 提供技能文件 :确保 AGENTS.md .claude/skills/d2l/SKILL.md 的内容已被AI加载。对于Claude Code,你可以直接在对话中说:“我已为你安装了d2l-cli技能,请查阅你的技能列表并使用它。”
    3. 检查输出格式 :如果AI解析混乱,尝试让AI使用 --json 格式输出,然后你告诉AI:“你刚才返回的是JSON,请用自然语言解读一下其中的 assignments 数组。” 这可以帮助AI(和你)理解数据结构。

问题6:在自动化脚本中, d2l 命令的输出包含颜色代码或进度条,影响解析。

  • 原因 d2l-cli 在输出到终端时会使用颜色美化表格,但这些ANSI转义码在脚本中会成为干扰字符。
  • 解决方案 :在命令中设置环境变量强制禁用颜色。
    NO_COLOR=1 d2l courses --json
    # 或者
    FORCE_COLOR=0 d2l courses --json
    
    这样能确保输出是干净的文本或JSON。

5.4 性能与扩展性考量

问题7: d2l dump 命令运行很慢,或者返回数据不全。

  • 原因 dump 命令会遍历你所有课程的大部分数据(成绩、作业、内容等),如果课程多、内容杂,API调用次数会剧增,导致速度慢。 --shallow 参数可以只获取摘要信息,加快速度。
  • 建议
    • 使用 --course 参数指定单个课程进行深度dump。
    • 使用 --since N 参数只获取最近N小时内的更新。
    • 考虑将全量dump作为低频后台任务(比如每周一次),而高频查询使用 due , news , updates 等针对性命令。

扩展思路 d2l-cli 提供了一个强大的基础。你可以基于它构建自己的应用:

  • 成绩追踪器 :定期运行 d2l --json grades --all ,将JSON数据存入SQLite数据库,并绘制每门课的成绩变化曲线图。
  • 资源归档机器人 :学期初,写个脚本遍历所有课程,用 d2l content --toc 获取目录,然后自动调用 d2l download-content 把所有讲义、视频链接保存下来,并按课程-模块整理好。
  • 个性化仪表盘 :将 d2l --json dump 的数据喂给一个本地Web应用(比如用Flask或Streamlit),生成一个属于你自己的、干净简洁的学习仪表盘,摆脱D2L笨重的界面。

这个项目的价值在于它打通了“封闭的LMS系统”与“开放的个人自动化工具”之间的壁垒。它尊重系统的只读边界,同时又赋予了学生极大的信息掌控权和流程自动化能力。尤其是在AI智能体时代,它将自己变成了一个高效的“信息查询插件”,让AI真正成为了学习的得力助手,而不仅仅是编程的伙伴。

Logo

小龙虾开发者社区是 CSDN 旗下专注 OpenClaw 生态的官方阵地,聚焦技能开发、插件实践与部署教程,为开发者提供可直接落地的方案、工具与交流平台,助力高效构建与落地 AI 应用

更多推荐