←  首页案例研究获取应用开源项目 · 2026EN

独立产品 — 设计、开发与发布 — 2026年7月 →

Moodle
Agent

一个无人值守跑完整件事的 agent:每一份课件、习题和作业说明被归进正确的周,每一节录播被读成文字,成绩一发布就被记下 —— 于是等着你的,是一个语言模型可以直接拿来干活的文件夹。

PythonPlaywrightBeautifulSoupCustomTkinterPyInstallerLLM APIs
01问题

课程材料总是 来得晚,而且 从不集中在一处。

每门课都把文件发布在十几个层层嵌套的板块里,而且很少一次性发完 —— 课件在上课前、习题在本周内、答案在几天之后。想在本地保留一份干净的副本,就意味着每周重新点开每门课,还得记住哪些已经下过了。

现有的浏览器插件只是把点击变少了:学生仍然要打开每一个课程页面,自己决定抓哪些东西。真正的问题从来不是下载速度 —— 而是这件事必须靠人记得去做,而且每节课有一半内容被锁在一段没有任何工具读得了的视频里。

01分批发布
答案和补充材料在课后几天才出现 —— 一门课只扫一遍永远不够。
02深度嵌套的结构
「周」是某个父板块下的子板块,其下还有更深的子板块;课程主页上什么都没有。
03认证摩擦
任何自动化都必须扛住单点登录和多因素认证,同时绝不持有密码。
04机器读不了
最有价值的内容是讲出来的,藏在录播里 —— 在它变成文字之前,对任何工具都没用。
02调研

先读懂系统, 再去自动化它

第一版爬虫什么也没抓到。课程主页上根本没有文件链接:这套 Moodle 用的是自定义版式,「周」是 Learning 板块下的子板块,而每一周下面还有更深的子板块,材料真正放在那里。

把结构摸清楚,靠的是真的去抓页面、读 HTML。现在的爬虫按广度优先遍历板块链接,构建父子树,让子节点继承父节点的周次。课程发现则复用了 Moodle 面板自己调用的那个 AJAX 接口 —— 它顺便还暴露了学生标星了哪些课,所以工具可以直接列出清单,而不是让人去复制课程 ID。

假设中 — 扁平

course/view.php
Week 1
Week 2

抓到 0 个文件

实际上 — 嵌套

course/view.php
section 3 · Learning
section 7 · Week 1
Own-time / Real-time / Wrap-up
section 11 · Week 2

板块编号并不连续。子节点继承父节点的周次。

03录播

把讲出来的那一半 变成文字

课件只承载了一节课的一半,剩下的一半是讲出来的。每一份录播都会被读成一份文档 —— 全程不下载视频,只取它的字幕 —— 而正是这一步,让后面所有事情成为可能。

难点从来不是下载。老师发布录播有四种不同方式,而且四种都有人在用,所以只处理最显眼那一种的程序,会悄无声息地漏掉其余三种。Panopto 还会对每一份录播 单独 授权 —— 一份从未被打开过播放器的录播,会报告「没有字幕」,而不是报错。正是这一个行为,导致那些明明标着「含字幕」的视频被判定成没有字幕。

一份录播出现的四种方式

  1. 01

    板块页面上的一个链接

  2. 02

    直接写在页面正文里的裸 URL

  3. 03

    嵌在页面中的 iframe

  4. 04

    包着以上任意一种的 page 或 url 活动

抓不到的时候 —— 说明原因

01没有字幕
会写进当周的说明里,让你知道它存在。
02被限流
会在之后的同步中重试。
03需要登录
只多要一次登录,且只要一次。
04已排队
刻意押后,以免超出接口限制。
04AI 工作流

产出的东西, 是模型 读得懂的

一份文字稿不是终点,而是学生真正想做的那件事的输入:问一个概念、拼一份复习提纲、找某个知识点讲在哪儿。这个 agent 真正的产物,是一整周已经处理成「可以直接粘给模型」的材料。

用哪个模型,刻意不由工具替你决定。不填密钥就不生成摘要,八家服务商任选,而且不填密钥整条管线照样跑 —— 你拿到的只是文字稿而不是摘要。工具里不内置任何人的密钥,所以没人替别人付账;而一个完全不想让模型介入的学生,手上仍然是一个能用的工具。

谁的模型,谁的账单

01自带密钥
支持八家服务商;不内置、也不转售任何额度。
02默认关闭
没有配置密钥时,不会有一个字节被发给任何模型。
03只降级,不罢工
没有密钥就没有摘要 —— 文字稿照样到位。
04用人会粘贴的格式
Word 和纯文本,因为那才是聊天工具真正接受的东西。

完整的一圈

  1. 01

    收集

    遍历每一个嵌套板块;只抓新增的部分。

  2. 02

    读取

    取出每份录播的字幕,写成一份文档。

  3. 03

    摘要 — 可选

    你的密钥、你的服务商、你说了算。默认关闭。

  4. 04

    交付

    一个装着纯文本和 Word 文件的周文件夹,可以直接粘进任何模型。

05产品

两屏设置, 然后不用再管

分发方式被当成设计的一部分,而不是事后才考虑的事。整个产品就是一个 75 MB 的文件:不装运行时、不要管理员权限、不碰命令行。设置过程只问两个问题,并给出一份它自己找到的课程勾选清单;之后这个窗口存在的唯一意义,就是回答「它还在跑吗」。其余的事 —— 收集、读取、记录成绩 —— 都发生在没人盯着的时候。

现状 · 人工

  1. 01记得去看 Moodle
  2. 02登录,逐门课打开
  3. 03展开每一个周板块
  4. 04和本地文件夹比对
  5. 05下载新增文件
  6. 06拖着看 47 分钟视频

每周、每门课 · 很容易忘 · 录播始终读不了

目标 · agent 化

  1. 01应用自行唤醒
  2. 02静默恢复会话
  3. 03遍历每一个板块
  4. 04比对清单差异
  5. 05录播被读成文字
  6. 06归档、记录,随时可喂给模型

每 3 小时一次 · 什么都不用记 · 材料到手即机器可读

06安全

不需要持有的 东西,就别 持有

向学生索要学校密码,会让这个工具变得非常好写,同时变得不可能被信任。所以登录被放回它该在的地方:一个真实的浏览器窗口,打开学校自己的登录页,多因素认证照走不误。应用自始至终看不到任何凭据。

它保留的是登录之后的会话,存放在项目目录之外的本地位置,因此绝不会被提交进仓库或同步到云端。除非你自己明确配置了模型密钥,否则没有任何东西离开这台机器:没有服务器、没有埋点、没有账号。控制台输出的是相对于下载目录的路径,所以随手分享的截图也不会泄露用户名。

  1. 01

    学校 SSO + MFA

    跑在真实浏览器里 —— 用户自己输入,应用只负责等待完成。

  2. 02

    会话 Cookie

    存在本地,位于仓库之外,也位于云同步目录之外。

  3. 03

    静默无头同步

    下次运行时重新注入。没有窗口、没有密码、没有服务器。

  4. 04

    模型调用需手动开启

    除 Moodle 之外唯一的外发流量,是你自己用密钥打开的那一条。

07结果
0
被存储或传输的密码数量 —— 登录全程不离开学校 SSO

以及基于清单追踪的增量同步,跨多次运行零重复下载

4
一份录播被找到的方式
链接 · 裸 URL · iframe · 包装活动
3h
无人值守同步间隔
外加每次登录时运行一次
13
每门课的周文件夹
外加 Assignments 与 _Other
8
可接入的 AI 服务商
用你自己的密钥 —— 一个都不填也能跑
1
只需下载一个文件,无需安装
不装 Python,不要管理员权限
2
文档语言
英文与简体中文

交付物

//课程结构逆向分析
//广度优先板块爬虫
//SSO 会话持久化
//增量清单同步
//作业与考核信息抓取
//按课程的成绩记录
//桌面 GUI —— 设置页与主面板
//后台自动同步服务
//Panopto 与 YouTube 字幕管线
//多服务商 AI 层,自带密钥
//每门课的每周摘要笔记
//Word 与纯文本文档输出
源码与发布版本 ↗
08收获

这个项目真正教会我的东西 —— 而不是它交付了什么。

01

要消灭的是任务,不是点击

下载得更快,这件苦差事依然留在学生身上。只有无人值守地跑起来,才真正解决了这件苦差事所代表的问题 —— 这是产品决策,不是技术决策。

02

有用的产出必须是机器可读的

把一段视频归好档,什么也没改变;把它读成文字,才改变了学生接下来能做什么。让 AI 工作流成为可能的是文字稿,而不是下载。

03

先读懂系统

第一版爬虫失败,是因为我自动化了一个自己假设出来的结构。真的去抓 HTML、把周次的嵌套关系画清楚,才让后面的一切成为可能。

04

把模型的选择权交给用户

内置一个密钥,意味着替陌生人付账、还替他们选了服务商。自带密钥只多花一个设置页,却同时消掉了这两个问题。

05

信任是一条设计约束

把凭据挡在工具之外让开发变难了,也正是因为这一点,它才能放心交给一个陌生人。是安全决定了架构,而不是反过来。

06

静默跳过会藏住 Bug

失败的录播被悄悄略过,于是一整类故障连续几周都没被发现。现在每一次跳过都会说明原因,并标明它是最终结论还是会重试 —— 其中好几个问题正是这样才被找出来的。