内容
<div align="center">
<img src="assets/logo.png" width="160" />
# 开放式知识学习辅助系统 OpenKLAS
一款开源的 FastAPI 后端,封装 [KLAS](https://klas.kw.ac.kr) — 光云大学的学习管理系统 — 并将其暴露为一个干净的 REST API,并内置 MCP 支持 AI 助手。
> 由 [@univerxe](https://github.com/univerxe) 开发 · 开放贡献
请访问 [openklas.com](https://openklas.com) 了解 OpenKLAS 的功能和设置方法。要将其插入 Claude.ai(或任何 MCP 兼容的助手),请添加一个自定义连接器,网址为 `https://mcp.openklas.com/mcp` — 您将在 OpenKLAS 登录页面上登录一次,整个工具目录便可用。
---
## 技术栈
<p>
<img src="https://skillicons.dev/icons?i=python,fastapi,postgres,redis,docker" />
</p>
<p>
<img src="https://img.shields.io/badge/Claude-Anthropic-D97757?style=for-the-badge&logo=anthropic&logoColor=white" />
<img src="https://img.shields.io/badge/Groq-Whisper-F55036?style=for-the-badge&logo=groq&logoColor=white" />
<img src="https://img.shields.io/badge/Voyage_AI-Embeddings-6C47FF?style=for-the-badge&logoColor=white" />
<img src="https://img.shields.io/badge/Ollama-RAG-000000?style=for-the-badge&logo=ollama&logoColor=white" />
<img src="https://img.shields.io/badge/pgvector-Vector_Search-4169E1?style=for-the-badge&logo=postgresql&logoColor=white" />
</p>
</div>
---
## 主要功能
- **MCP 服务器** — 每个端点都是通过 `fastapi-mcp` 的 Claude 工具;作为自定义 OAuth 连接器连接到 Claude.ai
- **KLAS 身份验证** — RSA 加密登录,双令牌系统(会话令牌 + JWT)
- **作业和讲座** — 代理作业,讲座板,课程大纲,团队项目
- **录制讲座管道** — 下载 → Whisper 转录 → Claude 总结 → 保存到 Obsidian
- **RAG** — 摄入讲座 PDF,查询它们并使用 Voyage AI 嵌入 + pgvector + Ollama
- **自动补全** — 自动完成讲座进度
---
## 使用 Claude 作为 KLAS 代理
将 OpenKLAS 连接到 Claude.ai 一次,并用纯英语询问有关您的课程的任何问题。Claude 使用 MCP 工具查找 KLAS 的实时数据,并立即回复。
### 剩余讲座
一个问题。每个课程。聚合进度。Claude 为您扫描每个录制的讲座板,并报告哪些周在哪些科目中剩余,包括持续时间和截止日期。

### 作业概览
每个截止日期,按重要性排序。询问一次。在单个回复中获取每个科目的待处理、逾期和已提交的作业,按截止日期排序,并标记逾期的项目。

### 作业详情
格式、截止日期、内容清单,全部在一个提示中。停止打开四个 KLAS 标签来了解作业要求。Claude 提取完整的任务详情,并将其显示为一个干净的清单。

### 电子邮件草稿
一个英文句子输入。一个礼貌的韩文电子邮件输出。需要申请延迟提交?Claude 查找作业,找到正确的教授,并草拟一个尊重的韩文电子邮件,准备发送。

### PDF 问答
像对待导师一样与您的讲座 PDF 对话。上传您的幻灯片一次。稍后询问任何问题。Voyage AI 嵌入 + pgvector + Ollama 保持答案的依据,每个用户隔离设计。

### 讲座总结
从一小时的视频到学习-ready 笔记。下载 → Whisper 转录 → Claude 编写的总结,可选保存到您的 Obsidian 保险库。走进课堂,已经准备好了。

---
## 架构
### 系统概述
```mermaid
graph TD
subgraph Clients
A[Claude.ai\nClaude Desktop]
B[Browser\nREST API Client]
end
subgraph OpenKLAS["OpenKLAS · FastAPI"]
MCP[MCP Server\nSSE · /mcp]
OAuth[OAuth 2.0\n/oauth/* routes]
API[REST API\n/api/* routes]
BG[Background Tasks\nsummarize · autocomplete]
end
subgraph Storage["Storage"]
Redis[(Redis\nSession Store)]
PG[(PostgreSQL\n+ pgvector)]
end
subgraph External["External Services"]
KLAS[KLAS LMS\nklas.kw.ac.kr]
KWC[Video CDN\nkwcommons.kw.ac.kr]
Groq[Groq Whisper\nwhisper-large-v3]
Anthropic[Anthropic\nClaude claude-sonnet]
Voyage[Voyage AI\nvoyage-3 · 1024-dim]
end
subgraph Infra["Infrastructure"]
Caddy[Caddy\nReverse Proxy · TLS]
end
A -->|OAuth Bearer / SSE| Caddy
B -->|HTTPS| Caddy
Caddy --> MCP
Caddy --> OAuth
Caddy --> API
MCP --> API
OAuth --> PG
API --> BG
API -->|RSA-encrypted login\nscrape data| KLAS
BG -->|Playwright + httpx\nvideo download| KWC
BG -->|audio file| Groq
BG -->|transcript| Anthropic
API -->|PDF chunks| Voyage
API -->|question| Anthropic
API --> Redis
API --> PG
```
### OAuth + MCP 登录流程
```mermaid
sequenceDiagram
actor User
participant Claude as Claude.ai
participant API as OpenKLAS API
participant KLAS as klas.kw.ac.kr
participant DB as PostgreSQL
participant Redis as Redis
User->>Claude: Add MCP connector\n(mcp.openklas.com/mcp)
Claude->>API: GET /.well-known/oauth-authorization-server
API-->>Claude: OAuth metadata (endpoints, scopes)
Claude->>API: POST /oauth/register
API-->>Claude: client_id / client_secret
Claude->>User: Redirect to /oauth/authorize
User->>API: POST credentials (student_id + password)
API->>KLAS: RSA-encrypted login
KLAS-->>API: Session cookie
API->>Redis: Store KLAS session
API->>DB: Upsert OAuthToken (encrypted credentials)
API-->>Claude: auth code → redirect
Claude->>API: POST /oauth/token (exchange code)
API-->>Claude: long-lived access_token
Claude->>API: MCP tool calls (Bearer access_token)
Note over API,Redis: On token expiry: silent re-login\nusing stored encrypted credentials
```
### 录制讲座总结管道
```mermaid
%%{init: {"themeVariables": {"fontSize": "11px"}, "flowchart": {"nodeSpacing": 20, "rankSpacing": 30}}}%%
flowchart TD
A([POST /summarize\nstart pipeline]) --> B[Validate request\nno job already running]
B --> C[Return 202 Accepted\nbackground job started]
C --> D[Acquire semaphore\nmax 1 pipeline at a time]
D --> E[Browser login\nPlaywright · klas.kw.ac.kr]
E --> F[Navigate to player\nkwcommons · /em/code]
F --> G[Extract cookies\nfrom browser context]
G --> H[Stream download\nhttpx · 1 MB chunks · .mp4]
H --> I[Extract audio\nffmpeg · 16kHz mono MP3]
I --> J[Transcribe audio\nGroq Whisper API]
J --> K[Sanitize transcript\nstrip control characters]
K --> L[Summarize\nClaude claude-sonnet-4-6]
L --> M{Save to Obsidian?}
M -->|yes| N[Write to vault\ncourse/lectures/WN-title.md]
M -->|no| O([Pipeline complete\nstep = done])
N --> O
P([GET /summarize/status\npoll endpoint]) -.->|poll| Q[SummarizeStatus\nstep · transcript · summary]
P2([GET /summarize/status/stream\nSSE endpoint]) -.->|SSE push| Q
```
### RAG 管道
```mermaid
%%{init: {"themeVariables": {"fontSize": "11px"}, "flowchart": {"nodeSpacing": 20, "rankSpacing": 30}}}%%
flowchart TD
subgraph Ingest
A([POST /api/rag/ingest\nupload PDF]) --> B[Extract text\npage by page]
B --> C[Chunk text\nsemantic splitting]
C --> D[Voyage AI embed\nvoyage-3 · 1024-dim]
D --> E[(pgvector store\nDocumentChunks)]
end
subgraph Query
F([POST /api/rag/query\nask a question]) --> G[Embed question\nVoyage AI · query mode]
G --> H[Cosine search\npgvector top-k chunks]
E --> H
H --> I[Build context\ntop-k chunk text]
I --> J[Claude API\ngrounded answer]
J --> K([Return answer\nto client])
end
Ingest --> Query
```
---
## 贡献
欢迎贡献!请随意提出问题或拉取请求。
1. 分叉仓库
2. 创建分支 (`git checkout -b feat/your-feature`)
3. 提交并推送
4. 开启拉取请求