No description
  • TypeScript 47.4%
  • Python 46%
  • Shell 4.4%
  • CSS 1.8%
  • Dockerfile 0.3%
  • Other 0.1%
Find a file
李仕博 d174f1290d 意见反馈:右上角入口 + 独立页面,落库并邮件通知
右上角与「使用说明」并列一个「意见反馈」,点进独立页面填写,
提交后是一整屏致谢而不是一个 toast —— 用户花时间写的东西,
值得一个停留得住的回应。

核心设计:先落库,再发邮件。
邮件通道随时可能不通(SMTP 没配、授权码过期、QQ 邮箱风控),
而反馈是用户认真写的,不能因此丢掉。所以:
- feedback 表存全量(内容、联系方式、来源页、UA、投递状态)
- 邮件失败只记 error 字段并打日志,用户侧照常显示「收到了」
  —— 因为确实收到了,只是在库里
- SMTP 完全没配置时功能也完整可用,管理员从库里捞即可

配置全部走 env,收件邮箱不写死:
FEEDBACK_EMAIL / SMTP_HOST / SMTP_PORT / SMTP_USER /
SMTP_PASSWORD / SMTP_FROM / SMTP_SSL,两个 .env.example 都补了说明
(含 QQ 邮箱要填「授权码」而非登录密码这个坑)。

邮件用标准库 smtplib,不引第三方依赖;smtplib 是阻塞的,
丢进 asyncio.to_thread —— QQ 邮箱偶尔要几秒才响应,
卡在事件循环里会拖累整个服务。

验证 7 项,其中一条是写测试时逮到的真 bug:
min_length=1 只数字符数,"   " 能通过,会存下一条空反馈;
已加 field_validator 在 strip 后校验。
2026-08-07 16:52:04 +08:00
backend 意见反馈:右上角入口 + 独立页面,落库并邮件通知 2026-08-07 16:52:04 +08:00
frontend 意见反馈:右上角入口 + 独立页面,落库并邮件通知 2026-08-07 16:52:04 +08:00
.env.prod.example 意见反馈:右上角入口 + 独立页面,落库并邮件通知 2026-08-07 16:52:04 +08:00
.gitignore feat: 生产部署 —— 准入控制、速率限制、单体镜像、自动 HTTPS 2026-08-03 15:48:46 +08:00
Caddyfile feat: 阿里云 ECS 部署 —— 一键脚本 + 无域名降级模式 2026-08-04 19:16:23 +08:00
DEPLOY.md fix: 数据库两个隐蔽坑 —— WAL 不落主文件、相对路径读错库 2026-08-05 11:13:30 +08:00
deploy.sh feat: 阿里云 ECS 部署 —— 一键脚本 + 无域名降级模式 2026-08-04 19:16:23 +08:00
docker-compose.yml feat: 阿里云 ECS 部署 —— 一键脚本 + 无域名降级模式 2026-08-04 19:16:23 +08:00
Dockerfile feat: 生产部署 —— 准入控制、速率限制、单体镜像、自动 HTTPS 2026-08-03 15:48:46 +08:00
install.sh install.sh: 自检会拿 301 冒充成功;HTTPS 判断与实际部署脱节 2026-08-05 23:49:45 +08:00
README.md README: 顶部加上在线体验地址(阶梯计划.top + 备用 IP) 2026-08-06 12:51:44 +08:00
start.sh feat: 阶梯 —— 以疑问为原子单位的学习工作台 2026-08-03 14:59:20 +08:00
update.sh update.sh: GitHub 拉不通时自动退到镜像,失败也不吞掉本地改动 2026-08-05 20:49:54 +08:00

阶梯计划

畅享丝滑 AI 学习

一个以疑问为原子单位的学习工作台。

🌐 在线体验:阶梯计划.top

打开点「游客模式进入」,免注册直接体验
备用地址:http://47.100.80.178


你问出的每一个问题,都不会白问。

AI 学习产品的通病是:内容生成得很丝滑,学完什么都没留下。阶梯把逻辑反过来—— 课程、文档只是内容的两种入口,真正沉淀下来的是你在学习过程中产生的问题卡片。 它们连成链、织成图,最后成为只属于你、别处拿不到的个人认知地图

三分钟了解

模块 一句话
课程 给个主题AI 生成大纲,逐节懒生成讲解;长连接流式,不卡不崩
卡片 在正文或 AI 的回答里划词,就地浮出卡片;卡片里再划词,无限套娃
双图谱 概念图讲「这个领域该怎么学」,问题图讲「你是怎么想的」,进度图讲「你啃到哪了」
第二大脑 只回答你自己学过的东西,答案可溯源到原始卡片;记忆网络可视化
复习 FSRS 间隔重复,快忘的卡自己浮出来;出题、判分、重排程全自动
文档 PDF / EPUB / arXiv 导入,段落级对照翻译,同样能划词建卡

卡片系统:产品的灵魂

传统 chat 是一维时间线,卡片是二维空间。四条铁律,一条不让:

正文:……通过 softmax 归一化后得到权重分布……

① 划中「softmax」        → 就地浮出卡片 C1
② 在 C1 的回答里划「归一化」→ 生成子卡 C2两卡之间自动连线
③ C2 答案里还有不懂的     → 再划 → C3 …… 可无限套娃
④ C1 C2 C3 同屏可见,原文始终没有被关闭、覆盖、顶走
⑤ 回看这条链:「我真正卡住的是归一化,不是 softmax」
  1. AI 的回答里必须也能划词建卡——否则就退化成了普通多轮对话
  2. 多张卡同时可见——换内容就等于退回一维 chat
  3. 卡与卡之间有可见连线——没有线就看不出思维轨迹
  4. 原文永远不被遮挡——遮住原文就没人愿意用卡片了

卡片套娃天然产生 1 / 1a / 1a1 / 1a1b 的编号结构。链追得深了, 系统会反问一句:要不要把这个疑问链提炼成一节专项课?——从问题反向驱动课程生成。

双图谱:两张图,两种人格

  • 概念图客观AI 从正文抽出的领域结构。前置 关系撑起分层,上→下就是学习顺序。 数据本身不是树,也不该砍成树——多前置、跨概念的关联都是真实知识,保留图、只排层。
  • 问题图(主观):你的卡片和追问链。左→右是你钻研的深度,每棵追问树独立生长; 跨树的 real link 用琥珀色大弧线飞越结构——意外关联是第二大脑里最值钱的东西。
  • 进度图(叠加):同一个领域结构,叠上你的学习状态。空心球 = 空白、蓝 = 提过问题、 绿 = 写过己见(背过 ≠ 想过)。空白概念一键生成强化课。

第二大脑

我不会用通用知识给你兜底。检索不到就直说「你的学习记录里还没有涉及这部分」。

GraphRAG-lite 四路召回jieba 全文 + 向量 + 图扩散 + 结构加权RRF 融合后 LLM 重排。 每次召回过程都会实时演在记忆网络上——四路信号依次点亮、沿突触扩散, 你能亲眼看到它是怎么「想起来」的。

记忆网络的每个神经元是一张卡,不透明度来自 FSRS 记忆强度:快忘掉的会自己淡下去。 拖一下时间轴,可以回放你的知识网络从第一张卡长到今天的全过程。

快速开始

cp backend/.env.example backend/.env    # 填入 LLM API key
./start.sh

打开 http://localhost:5173,注册一个账号即可。

前置条件:Node.js 18+uv(自动装 Python 3.12)。 不需要 Docker不需要单独装数据库——SQLite 单文件就是整个数据库。

部署到服务器

git clone https://github.com/nuronly/Ascend.git ladder && cd ladder
sudo ./install.sh     # 裸机uv + systemd + Nginx不需要 Docker
# 或
./deploy.sh           # 容器Docker Compose + Caddy 自动 HTTPS
  • 支持域名 + HTTPS只有 IP 两种模式,脚本自动识别并配置
  • 已有手工配的 HTTPS 不会被覆盖GitHub 拉不通自动退镜像Node 损坏自动重装
  • 更新一条命令:sudo ./update.sh
  • 阿里云 ECS 逐步操作与故障排查:DEPLOY.md

三个硬约束,提前知道能省一晚上:

  1. Serverless 走不通——大纲 ~90 秒、正文 ~70 秒的 SSE 长连接,超过所有平台的超时上限
  2. Nginx 必须 proxy_buffering off——漏了就是「卡住一分钟然后内容全部涌出」(脚本已配好)
  3. 上线前先配成本闸——别人用你的 key 花你的钱。ALLOW_REGISTRATION / INVITE_CODE / MAX_USERS / DAILY_TOKEN_QUOTA / RATE_LIMIT_ENABLED,至少开一道

技术栈

选型
前端 React + TypeScript + Vite + Tailwind v4
卡片画布 React Flow · 自定义节点即卡片
图谱 Cytoscape.js + dagre 分层布局
记忆网络 Canvas 2D 手绘(深浅底双调色板)
后端 FastAPI + Python 3.12 + SQLAlchemy 2.0 async
数据库 SQLite 现在 / PostgreSQL + pgvector 切换只改一行
检索 jieba + FTS5 · 向量 · 图扩散 · RRF 融合
间隔重复 FSRS
鉴权 argon2id + JWT httpOnly cookie

LLM 走 provider 抽象层 + 分级路由:大纲用旗舰、正文用中档、打标用小模型; 主模型失败自动降级、可跨供应商;正文与 embedding 按内容 hash 缓存,同一段文本永不重复付费。 每次调用都记账,「设置 → AI 用量」随时可查。

验证

cd frontend && npm test                        # 67 项:渲染 / XSS / 流式半截内容 / 布局 / 配色对比度
cd backend && .venv/bin/python -m pytest       # JSON 截断修复等边界
cd backend && .venv/bin/python scripts/smoke.py  # 端到端冒烟(真实调 LLM约 5 分钟)

测试里钉的都是踩过的真实坑:

  • react-markdown 用 hasOwnProperty 查组件——值是 undefined 也照取, 每个 <p> 变成 createElement(undefined) 整页白屏
  • 大纲被 max_tokens 截断就整份报废——现在有括号栈修复器,救回前面完整的章节
  • 配色里写了 var(--x),而 cytoscape 走 Canvas 渲染根本解析不了 CSS 变量
  • 空白节点与背景只差 5% 亮度,一张全是空白概念的图集体隐形

项目结构

backend/
  app/
    core/       配置 · 数据库 · ★ 数据隔离UserScope 逐跳校验)
    llm/        ★ provider 抽象层 · 分级路由 · 降级 · 缓存 · 成本日志
    models/     ★ 全量 schema一次埋齐历史数据补不回来
    services/   course · card · brain · review · prompts
    api/        HTTP 接口 · SSE
  scripts/      smoke.py · preflight.py · check_providers.py · backup.py
  tests/
frontend/
  src/
    components/ CardNode ★ · CardSpace ★ · useSelection ★ · NeuralNetwork
    lib/        graphLayout · graphTheme · neural · streamingMarkdown
    pages/      Section ★(原文+卡片分栏) · Graph · Brain · Vault · Review …
install.sh      裸机部署systemd + Nginx防 HTTPS 被覆盖、Node 失效自愈)
update.sh       一键更新GitHub 不通自动退镜像,本地改动自动暂存恢复)

设计原则

  • 界面灰阶,语义上色——全局克制的灰阶,颜色只用来编码语义 real/potential、己见/AI 原生、空白/提过/有己见)。图谱统一浅底 + 彩色球。
  • 数据不做绑架——JSON 全量导出Markdown zip 按 Luhmann 编号命名、 双链写成 [[编号]],直接扔进 Obsidian 就能用。
  • 不静默编造——降级链全挂就明确报错;残缺大纲会告诉你「这份大纲不完整」,而不是假装成功。
  • 己见率 > 学习时长——AI 一键生成的卡本质是摘抄堆积。入库前鼓励用自己的话改一句, 改过的卡在图上有不同标记。这是比打卡时长诚实得多的深度指标。

状态

已完成v0.1 卡片核心闭环 · v0.2 双图谱 · v0.3 文档模式 · v0.4 第二大脑 + FSRS · v0.5 勋章墙

进行中Workspace 草稿画布前端界面(后端 API 已就绪)· 扫描件 PDF 的 OCR


课程与文档只是两种投喂内容的入口。
你的问题,才是产品。