🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI

学业助手 kw78/academic-agent

DeepSeek Harness
kw785 天前有提交已提供中文文档扫描:含敏感能力

把课程、作业和考试放在一起,少花时间来回查。

31/100
综合分
2
GitHub 星标
—
npm 周下载
—/5
用户评分

数据截至 2026/11/3 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。

安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add kw78/academic-agent
该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:已验证
是什么
dsh 原生插件 · market
装得上吗
本站已在 dsh 0.2.1-rc.2 下真实安装成功
非静态推断
L5 · 运行时冒烟等级说明 →
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 5 天前
档位由哪些信号合成

本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。

验证判据与等级说明 →
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。

可执行外部命令读写/删除本地文件发起外部网络请求
查看扫描证据(共 83 处,展示前 71 条)
  • 发起网络请求
    adapters/fixture-lms/src/index.ts:135
    async fetch(): Promise<AdapterResult> {
  • 读取本地文件
    adapters/fixture-lms/src/index.ts:149
    const raw = JSON.parse(readFileSync(path, 'utf8')) as unknown
  • 发起网络请求
    adapters/ustc-blackboard/src/engine.ts:208
    async fetch(context?: AdapterFetchContext): Promise<AdapterResult> {
  • 写入本地文件
    adapters/ustc-blackboard/src/index.ts:136
    mkdirSync(outputDir, { recursive: true })
  • 写入本地文件
    adapters/ustc-blackboard/src/index.ts:163
    writeFileSync(saved[0]!, html)
  • 写入本地文件
    adapters/ustc-blackboard/src/index.ts:190
    writeFileSync(path, courseHtml)
  • 写入本地文件
    adapters/ustc-blackboard/src/index.ts:209
    writeFileSync(
  • 读取本地文件
    apps/academic-cli/src/main.ts:705
    return JSON.parse(readFileSync(path, 'utf8')) as unknown
  • 发起网络请求
    apps/academic-cli/src/main.ts:847
    const result = await app.webSessions.fetch(sessionId, url)
  • 写入本地文件
    apps/academic-cli/src/main.ts:850
    writeFileSync(out, result.body)
  • 读取本地文件
    apps/academic-cli/src/main.ts:992
    const text = filePath ? readFileSync(filePath, 'utf8') : inline!
  • 发起网络请求
    packages/academic-adapter-runtime/src/runtime.ts:363
    return this.request(url, 'GET')
  • 发起网络请求
    packages/academic-adapter-runtime/src/runtime.ts:547
    const body = await this.request(currentUrl, method, requestBody, {
  • 发起网络请求
    packages/academic-adapter-runtime/src/runtime.ts:675
    async fetch(context?: AdapterFetchContext): Promise<AdapterResult> {
  • 发起网络请求
    packages/academic-adapter-runtime/src/runtime.ts:1180
    const result = await adapter.fetch()
  • 发起网络请求
    packages/academic-agent-tools/src/tools.ts:706
    const result = await dependencies.webSessions!.fetch(
  • 写入本地文件
    packages/academic-application/src/app.ts:488
    mkdirSync(dirname(options.databasePath), { recursive: true })
  • 删除本地文件递归
    packages/academic-application/src/app.ts:2422
    rmSync(materialRoot, { recursive: true, force: true })
  • 读取本地文件
    packages/academic-application/src/backup.ts:21
    if (existsSync(targetPath)) {
  • 写入本地文件
    packages/academic-application/src/backup.ts:24
    mkdirSync(dirname(targetPath), { recursive: true })
  • 读取本地文件
    packages/academic-application/src/backup.ts:29
    if (!existsSync(backupPath)) {
  • 读取本地文件
    packages/academic-application/src/backup.ts:32
    if (existsSync(targetPath)) {
  • 写入本地文件
    packages/academic-application/src/backup.ts:35
    mkdirSync(dirname(targetPath), { recursive: true })
  • 删除本地文件
    packages/academic-application/src/backup.ts:37
    rmSync(`${targetPath}${suffix}`, { force: true })
  • 写入本地文件
    packages/academic-application/src/backup.ts:39
    copyFileSync(backupPath, targetPath)
  • 读取本地文件
    packages/academic-application/src/backup.ts:58
    return createHash('sha256').update(readFileSync(path)).digest('hex')
  • 读取本地文件
    packages/academic-application/src/backup.ts:62
    return { sha256: sha256File(path), bytes: statSync(path).size }
  • 读取本地文件
    packages/academic-application/src/backup.ts:68
    for (const entry of readdirSync(directory, { withFileTypes: true })) {
  • 写入本地文件
    packages/academic-application/src/backup.ts:143
    mkdirSync(targetDir, { recursive: true })
  • 写入本地文件
    packages/academic-application/src/backup.ts:155
    mkdirSync(dirname(target), { recursive: true })
  • 写入本地文件
    packages/academic-application/src/backup.ts:156
    copyFileSync(join(materialsDir, file), target)
  • 删除本地文件递归
    packages/academic-application/src/backup.ts:172
    rmSync(targetDir, { recursive: true, force: true })
  • 删除本地文件递归
    packages/academic-application/src/backup.ts:212
    rmSync(targetDir, { recursive: true, force: true })
  • 执行外部命令固定命令
    packages/academic-connections/src/vault.ts:104
    const child = spawn(
  • 执行外部命令固定命令
    packages/academic-connections/src/vault.ts:162
    const result = spawnSync(
  • 写入本地文件
    packages/academic-connections/src/vault.ts:174
    mkdirSync(this.#directory, { recursive: true })
  • 写入本地文件
    packages/academic-connections/src/vault.ts:175
    writeFileSync(this.#path(reference), encrypted, {
  • 读取本地文件
    packages/academic-connections/src/vault.ts:184
    if (!existsSync(path)) return undefined
  • 读取本地文件
    packages/academic-connections/src/vault.ts:185
    const ciphertext = readFileSync(path, 'utf8').trim()
  • 读取本地文件
    packages/academic-connections/src/vault.ts:193
    if (!existsSync(path)) return false
  • 删除本地文件
    packages/academic-connections/src/vault.ts:194
    rmSync(path, { force: true })
  • 发起网络请求
    packages/academic-connectors/src/connector.ts:215
    const result = await adapter.fetch(credentials ? { credentials } : undefined)
  • 发起网络请求
    packages/academic-connectors/src/connector.ts:321
    adapterResult = await adapter.fetch(credentials ? { credentials } : undefined)
  • 发起网络请求
    packages/academic-contracts/src/connector.ts:30
    fetch(context?: AdapterFetchContext): Promise<AdapterResult>
  • 删除本地文件递归
    packages/academic-dsh-plugin/build.mjs:9
    rmSync(join(packageRoot, 'lib'), { recursive: true, force: true })
  • 删除本地文件递归
    packages/academic-dsh-plugin/build.mjs:10
    rmSync(join(packageRoot, 'migrations'), { recursive: true, force: true })
  • 写入本地文件
    packages/academic-dsh-plugin/build.mjs:11
    mkdirSync(join(packageRoot, 'lib'), { recursive: true })
  • 删除本地文件递归
    packages/academic-dsh-ui/build.mjs:9
    rmSync(lib, { recursive: true, force: true })
  • 写入本地文件
    packages/academic-dsh-ui/build.mjs:10
    mkdirSync(lib, { recursive: true })
  • 读取本地文件
    packages/academic-dsh-ui/build.mjs:41
    const body = readFileSync(clientBodyPath, 'utf8')
  • 写入本地文件
    packages/academic-dsh-ui/build.mjs:52
    writeFileSync(join(lib, 'client.js'), wrapped)
  • 删除本地文件
    packages/academic-dsh-ui/build.mjs:53
    rmSync(clientBodyPath, { force: true })
  • 发起网络请求
    packages/academic-dsh-ui/lib/host.js:21828
    return ok(await app.webSessions.fetch(body.sessionId, body.url));
  • 发起网络请求
    packages/academic-dsh-ui/src/api.ts:1978
    return ok(await app.webSessions.fetch(body.sessionId, body.url))
  • 写入本地文件
    packages/academic-materials/src/library.ts:263
    mkdirSync(this.#rootDir, { recursive: true })
  • 读取本地文件
    packages/academic-materials/src/library.ts:319
    bytes = readFileSync(input.filePath)
  • 发起网络请求
    packages/academic-materials/src/library.ts:340
    const resource = await this.#fetch(input.url, {
  • 发起网络请求
    packages/academic-materials/src/library.ts:490
    const resource = await this.#fetch(input.pageUrl, {
  • 发起网络请求
    packages/academic-materials/src/library.ts:555
    const attachment = await this.#fetch(candidate.url.toString(), {
  • 读取本地文件
    packages/academic-materials/src/library.ts:648
    if (existsSync(absolute)) rmSync(absolute, { force: true })
  • 删除本地文件
    packages/academic-materials/src/library.ts:648
    if (existsSync(absolute)) rmSync(absolute, { force: true })
  • 读取本地文件
    packages/academic-materials/src/library.ts:722
    if (!existsSync(absolute)) {
  • 读取本地文件
    packages/academic-materials/src/library.ts:740
    extraction = this.#extract(row.kind as MaterialKind, readFileSync(absolute))
  • 发起网络请求
    packages/academic-materials/src/library.ts:974
    async #fetch(url: string, options: MaterialFetchOptions): Promise<MaterialFetchedResource> {
  • 写入本地文件
    packages/academic-materials/src/library.ts:1012
    mkdirSync(directory, { recursive: true })
  • 读取本地文件
    packages/academic-materials/src/library.ts:1014
    if (!existsSync(absolute)) {
  • 写入本地文件
    packages/academic-materials/src/library.ts:1016
    writeFileSync(absolute, bytes, { flag: 'wx' })
  • 发起网络请求
    packages/academic-source-browser/src/index.ts:332
    const request = response.request()
  • 读取本地文件
    packages/academic-storage-sqlite/src/database.ts:37
    const files = readdirSync(migrationsDir)
  • 读取本地文件
    packages/academic-storage-sqlite/src/database.ts:44
    const sql = readFileSync(join(migrationsDir, file), 'utf8')
  • 发起网络请求
    packages/academic-web-sessions/src/index.ts:249
    async fetch(sessionId: string, targetUrl: string): Promise<WebSessionFetchResult> {
扫描于 2026/11/2(1 天前) · 范围 全包扫描 · 来源 GitHub 仓库

静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。

给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

本地优先的学术助手,课程、任务、计划采用事件溯源,支持课程资料,并提供 DSH 插件和智能体工具。

README

由 DeepSeek 最新模型翻译生成
Academic Agent | English 把课程、作业和考试放在一起,少花时间来回查。 Academic Agent 是一个在本机运行的学业助手。它汇总学校平台上的课程、作业和考试,保存你的学习进度,并根据空闲时间安排计划。你也可以导入课件和讲义,在需要时请 Agent 查资料、讲题或给复习建议。 日常同步、去重、提醒和基础规划都由程序完成,不调用大模型。遇到尚未支持的网站时,可以在 DSH 对话中让 Agent 帮忙分析页面、生成接入规则;规则经过验证并获准启用后,后续同步就能重复使用。 业务数据默认留在本机,凭据只进系统凭据库;重要变化有来源和历史,用户可以暂停自动化、确认或锁定计划。 接入学校平台 不同学校的网站、接口和页面结构各不相同。Academic Agent 将这些差异保存在可验证的 Manifest(声明式接入规则) 中,让已有引擎承担执行,新平台优先通过配置接入。 1. 观察真实网站:用户完成登录后,Agent 通过受控工具探测页面、有限抓取栏目,寻找列表、字段、分页与接口线索;支持 HTML / JSON,以及显式声明的只读 JSON POST。 2. 生成候选规则:Agent 在 DSH 对话中将发现整理为 Manifest,声明读取哪些主机、路径、课程和学业字段。候选可以暂存,供用户查看。
展开完整文档(README 剩余 1 节)
Academic Agent

| English

把课程、作业和考试放在一起,少花时间来回查。

Academic Agent 是一个在本机运行的学业助手。它汇总学校平台上的课程、作业和考试,保存你的学习进度,并根据空闲时间安排计划。你也可以导入课件和讲义,在需要时请 Agent 查资料、讲题或给复习建议。

日常同步、去重、提醒和基础规划都由程序完成,不调用大模型。遇到尚未支持的网站时,可以在 DSH 对话中让 Agent 帮忙分析页面、生成接入规则;规则经过验证并获准启用后,后续同步就能重复使用。

业务数据默认留在本机,凭据只进系统凭据库;重要变化有来源和历史,用户可以暂停自动化、确认或锁定计划。

接入学校平台

不同学校的网站、接口和页面结构各不相同。Academic Agent 将这些差异保存在可验证的 Manifest(声明式接入规则) 中,让已有引擎承担执行,新平台优先通过配置接入。

1. 观察真实网站:用户完成登录后,Agent 通过受控工具探测页面、有限抓取栏目,寻找列表、字段、分页与接口线索;支持 HTML / JSON,以及显式声明的只读 JSON POST。
2. 生成候选规则:Agent 在 DSH 对话中将发现整理为 Manifest,声明读取哪些主机、路径、课程和学业字段。候选可以暂存,供用户查看。
3. 用样本检验推断:在真实页面 / JSON 样本上离线回放,检查规则结构、字段契约与访问范围;宿主捕获的样本可通过 sampleId 引用,原始响应无需交给模型。
4. 授权后持续复用:只有验证通过的版本才能启用;启用会创建或更新同步源,后续由确定性运行时按规则执行,无需每轮重新推理网站结构。

flowchart LR
A[用户登录与入口 URL] --> B[Agent 探测与生成候选]
B --> C[样本回放与契约验证]
C --> D[授权启用 Manifest 版本]
D --> E[零 LLM 日常同步]
E --> F[任务、计划与提醒]
E -. 同步异常:当前在对话中诊断 .-> B

已有实例:USTC 教务考试接入。 新增一个 Manifest、两个 fixture 和一组集成测试,即可读取考试信息;无需修改内核、Connector 或 UI,也无需新增网站专属引擎。覆盖范围见 真实数据源验证。

自适应也体现在日常安排中:截止时间、任务进度或可用时间改变后,系统可以重新计算计划建议,保留用户锁定的时间块,并说明无法安排的冲突。

网站改版后怎么处理

系统积累的是可验证的接入规则、候选版本和验证记录。网站变化后,可以结合失败信息重新探测、生成新版本、回放验证,再切换生效版本;旧版本保留,可回滚。规则演进与学业事实分开管理,同步失败会保留已有数据和用户进度。

| 环节 | 当前进展 |
| 探测 → 候选 → 验证 → 启用 / 回滚 | 已实现;候选生成由 DSH 对话中的 Agent 完成 |
| 启用 → 真实预览 / 同步 | 已实现;设置页开启“Agent 自动接入”后,Agent 可在授权范围内完成,无需逐次审核 |
| 失败诊断 → 修改规则 → 新版本验证 | 目前由用户发起 Agent 对话,结合现有工具完成 |
| 同步失败自动唤起 Agent 并修复 | 后续目标;尚未实现无人值守的自修复闭环 |

样本回放通过只说明规则能解析给定样本;真实预览、真实同步与重复同步 no-op 需要分别验证。 当前能力与进展详见 MVP 验收 和 路线图。

“Agent 自动接入”默认关闭,用户可随时关闭;开启后也仅允许启用已验证候选、预览和同步声明式数据源。任务修改等其他写入仍需可信宿主逐次授权。Agent 不获得 Cookie / Token,也不会替你提交作业或修改学校账户。

平时可以用它做什么

- 汇总课程和待办:已实测 Blackboard 课程 / 作业与 USTC 教务考试,也可手动添加。重复同步不会重复建课,同学期同名课程复用同一身份。
- 安排学习时间:按截止时间、任务及课程优先级和你设置的空闲时间规划;支持计划确认、锁定和冲突说明。
- 截止前提醒:默认本地扫描 72 小时 / 24 小时提醒,去重并在重启后补偿检查。
- 检索课件和讲义:导入 PDF、PPTX、DOCX、网页或文本后按关键词检索;提供资料标题、片段和可用页码,供 Agent 引用。扫描版 PDF 暂无 OCR。
- 按需查询与讲解:在 DSH 对话中使用课程、任务、计划和资料工具;Tutor skill 约束讲解先查资料、标注来源,缺少资料时明确说明。
- 跨会话保留成果:数据、配置、计划和后台作业可恢复;宿主运行时,例行自动化独立于 Agent 会话。停用 Agent 或 UI 插件仍保留学业事实。

最短上手

要求 Node.js ≥ 22.12;凭据库目前使用 Windows DPAPI。

npm ci
npm run build:dsh
npm run build:ui

用内置演示数据跑通:同步 → 任务 → 计划
ACADEMIC_DB=.data/academic.db npm run demo

第一次完整使用可以按这个顺序:添加课程 → 连接/同步 → list 看任务 → plan --accept 确认计划 → 导入资料。demo 会用内置数据把这条链路跑一遍。

常用操作:

npm run demo -- list # 看今天/近期要做什么
npm run demo -- course add 2026-fall CS101 "Computer Science"
npm run demo -- task add "复习第三章" --minutes 60
npm run demo -- plan --accept # 生成并确认未来 7 天计划
npm run demo -- material add notes.pdf --course <courseId>
npm run demo -- material search "隐函数"
npm run demo -- help

完整命令见 CLI 参考。数据库默认在 .data/academic.db,可用 ACADEMIC_DB 改路径。

在 DSH 里使用(可选)

需要 DSH 0.2.1-rc.3。只装 Host/工具:

dsh plugin --profile academic add <path-to-repo>/packages/academic-dsh-plugin

同时装「学业」界面(Web 或 Desktop):

dsh plugin --profile academic-web add \
<path-to-repo>/packages/academic-dsh-plugin \
<path-to-repo>/packages/academic-dsh-ui

界面分两块:「日常学习」(今天、任务、计划、课程、资料)和「管理」(连接、自动化、设置)。今天页先显示下一学习时段、今日任务与逾期;课程页汇总任务、来源和资料。

智能体默认只读;修改任务、导入/删除资料等写入操作,必须由可信宿主逐次批准。当前没有接入 DSH 审批弹窗时,这些写入会被拒绝,命令行/UI 操作不受影响。上文的「Agent 自动接入」是用户单独开启的有限授权,只覆盖已验证候选启用和声明式源的预览 / 同步。

DSH 没有官方中央市场;插件 Discover 读取用户配置的索引。Academic Agent 索引:

https://kw78.github.io/academic-agent/market/index.json

添加方式见 market/README.md。

安装细节:插件 README、UI README。

数据放在哪里

- 数据库:命令行默认 .data/academic.db;DSH 默认 $DSH_HOME/academic-agent/academic.db。
- 课程资料:数据库同目录的 materials/,按内容哈希保存;SQLite 只存元数据和检索分块。
- 凭据:Windows DPAPI;数据库、日志和智能体结果里没有明文 Cookie/Token。
- 备份:backup 只快照 SQLite;backup-app 连同 materials/ 与 SHA-256 清单一起备份,整库迁移用后者。
- 删除/恢复与故障处理见 运维文档。

已知限制

- 凭据库目前只支持 Windows DPAPI。
- 扫描版 PDF 没有 OCR,只能保存原件。
- 自动附件导入只扫描你给出的单个课程页,不做全站爬虫。
- Blackboard 已覆盖课程/作业/文件夹;真实考试/测验样本仍以脱敏 fixture 验证。
- DSH 文件级 HMR、真实 Session 生命周期和完整审批 UI 尚未专项验证。
- npm 包尚未正式发布;当前从源码或本地 tarball 安装。

更完整的验收边界见 MVP 验收,架构与工程状态见 AGENTS.md。

文档

- CLI 参考
- 运维、备份与故障处理
- 测试与架构矩阵
- 前端验证记录
- 架构决策
- 当前路线图
- v0.2.0 发布说明
- 架构基线:Intent、Architecture Spec

License

Apache License 2.0。见 LICENSE;第三方依赖保留各自许可证。
预览分享卡片长什么样 ↓kw78/academic-agent
分享 · DeepSeek Harness Hub
kw78/academic-agent

把课程、作业和考试放在一起,少花时间来回查。

回到顶部