Files
llm-library/CODEBASE.md

11 KiB
Raw Permalink Blame History

📘 LLM 论文图书馆 — 代码全景 & 维护手册

最后更新2026-06-09 | 馆长必读。每次改代码前先看这里。 本文档是代码的"说明书"——解释每个文件做什么、怎么改、踩过什么坑。


🗺️ 整体架构

用户浏览器 → Nginx (443, OpenResty) → Docker (127.0.0.1:8741→8000)
                                          └── FastAPI (uvicorn)
                                               ├── /api/*     → REST API
                                               ├── /papers/*  → PDF 本地代理
                                               └── /           → 静态前端

数据流: papers.json 是唯一数据源。前端通过 /api/modules/{id} 拉取完整模块数据(含论文列表),在浏览器端渲染。


📁 文件清单 & 职责

后端 — api/

文件 行数 职责 改这里时注意
server.py ~600 FastAPI 主程序路由、鉴权、PDF 代理、翻译触发 核心文件。 所有 API 在此。修改路由/鉴权/PDF 逻辑都在这里
downloader.py ~200 批量下载 arXiv + HuggingFace PDF 到本地缓存 调用 httpx,有重试机制。下载到 papers/arxiv/papers/hf/
backfill.py ~50 快速补全缺失的 arXiv PDF 比 downloader 轻量,用 wget。适合 cron 定期跑
batch_translate.py ~70 用 pdf2zh + DeepSeek 批量翻译 papers.json 读取论文,跳过已有译文。输出到 papers/translated/
parse_papers.py ~110 llm_library.html 解析论文数据 → papers.json 旧版工具,用于从原始 HTML 模板提取数据
extract_data.py ~105 备用提取脚本(与 parse_papers 功能相似) 路径硬编码了 /app/working/workspaces/default/
check_trans.py ~15 测试翻译 API 连通性 仅开发调试用

前端 — static/

文件 行数 职责 改这里时注意
index.html ~330 HTML 壳header + search + grid + overlay modal 注意: index_template.html 和它内容相同。修改时两个都要同步?→ 实际上只有 index.html 被服务。index_template.html 是旧版模板
app.js ~285 全部前端逻辑 ES6 模块风格(但不是 module无框架依赖,纯 vanilla JS。
style.css ~315 暗色主题 CSS 9 种模块配色 + 卡片光效 + PDF 阅读器样式
pdf.min.js ~40K PDF.js 库 外部依赖,不要手动改

数据 — data/

文件 说明
papers.json 唯一数据源。 结构:{ module_id: { name, icon, desc, areas: [{ id, name, mainline[], branches[], forward[] }] } }

配置 & 部署

文件 说明
Dockerfile Python 3.11-slim安装 poppler-utils + torch CPU + pdf2zh
start.sh 容器启动脚本:检查 API Key、安装依赖、启动 uvicorn
requirements.txt FastAPI + uvicorn + httpx + pydantic + tqdm
nginx.conf 参考配置(非容器内使用,宿主机 Nginx 反向代理)
.env.example 环境变量模板
proxy_conf.txt 代理配置备注

🔌 API 路由速查

读操作(无需鉴权)

方法 路径 返回 备注
GET /api/health {"status":"ok"} 健康检查
GET /api/stats 模块数/领域数/论文总数 首页统计
GET /api/modules 模块列表(含 paper_count 用于渲染首页卡片
GET /api/modules/{id} 完整模块数据(含所有论文) 点击卡片后调用
GET /api/papers?q=...&module=...&tag=... 搜索结果列表 搜索栏 / 标签过滤
GET /papers/arxiv/{id}.pdf PDF 文件 本地缓存代理。也支持无 .pdf 后缀
GET /papers/hf/{name}.pdf PDF 文件 HuggingFace 缓存代理
GET /papers/translated/{id}.pdf 翻译 PDF 中文译文
GET /api/translated/{id} {"exists":bool} 检查译文是否存在
GET /api/translate/{id} 段落级翻译 JSON MyMemory 免费 API文本翻译
GET /api/translate/{id}/status 缓存状态 检查段落翻译缓存
GET /api/translate/status 正在翻译的论文列表 pdf2zh 翻译队列

写操作(需 API KeyBearer 头 或 ?api_key= 参数)

方法 路径 说明
POST /api/papers 新增论文
PUT /api/papers?module_id=...&area_id=...&title=... 修改论文
DELETE /api/papers?module_id=...&area_id=...&title=... 删除论文
POST /api/translate/{paper_id} 触发 pdf2zh 翻译
POST /api/download/{arxiv_id} 按需下载单篇 PDF

🧩 前端逻辑流程

1. init()
   ├── buildStatusBar()          → 底部连通性检测条
   ├── fetch('/api/modules')     → 获取模块列表
   ├── renderCards(mods)         → 渲染 9 张卡片
   ├── attachGlowTracking()      → 鼠标光效
   └── checkSources()            → ping arxiv + hf

2. 用户点击卡片
   └── openModule(modId)
       ├── fetch('/api/modules/{id}')  → 获取完整数据(缓存到 moduleData
       └── renderPapers(area)
           ├── section-label: 主线论文 / 支线论文 / 前瞻探索
           └── paper-item: 年份 | 标题 | 作者 | venue | tags | 按钮

3. 用户点击「📄 阅读」
   └── openPdfBtn(btn)
       └── openPdf(url, title)
           ├── 创建/复用 #pdfOverlay
           ├── getLocalPdfUrl() → 优先走本地缓存 /papers/arxiv/{id}.pdf
           └── iframe 加载 PDF失败时回退到 arXiv 直链

4. 用户点击「📖 译文」
   └── 同上URL 指向 /papers/translated/{id}.pdf

🔒 安全机制

机制 位置 说明
API Key 鉴权 server.py:verify_api_key() 所有 POST/PUT/DELETE 需 Bearer token 或 ?api_key=
路径遍历防护 server.py:safe_paper_id() 正则 ^[a-zA-Z0-9_.\-]+$,禁止 ..
PDF 路径校验 server.py:safe_pdf_path() resolve 后检查是否在 base 目录内
CORS 全局 allow_origins=["*"] 当前宽松,如需收紧改这里
速率限制 nginx.conf 宿主机 Nginx 层 limit_req zone=api:10m rate=10r/s

⚠️ 已知问题 & 注意事项

1. index.html vs index_template.html 2026-06-09 已修复)

已修复: index.html 原来包含 286 行内联 JS导致 app.js 的修改完全不生效。已改为 <script src="/app.js?v=20260609"></script>HTML 和 JS 完全分离。index_template.html 保持为早期模板(也引用外部 script用于备用。

2. MyMemory 免费翻译 vs pdf2zh

GET /api/translate/{arxiv_id} 用的是 MyMemory 免费 API质量一般而真正的翻译是通过 POST /api/translate/{paper_id} 触发 pdf2zh + DeepSeek生成中文 PDF。两者是不同的翻译通道不要混淆。

3. 按需下载实际跑全量

POST /api/download/{arxiv_id} 目前的实现是运行 downloader.py 下载所有未缓存的论文,而不是只下载请求的那一篇。这是个已知的粗糙实现。

4. 翻译覆盖率

翻译 PDF 存储在容器内 /app/papers/translated/,通过 volume 挂载持久化。翻译 API (POST /api/translate/{paper_id}) 会检查是否已翻译,避免重复。

5. 搜索实现2026-06-09 已修复)

搜索已在 2026-06-09 升级为实时下拉结果面板。输入 ≥2 字符后200ms debounce 后调用 /api/papers?q=,在下拉面板中显示匹配论文(标题高亮、模块/领域、年份、标签)。卡片同步绿框高亮。点击结果项跳转到对应模块弹窗。点外部或 Escape 关闭。

6. OpenResty 缓存问题

宿主机 Nginx/OpenResty1Panel 管理,容器名 1Panel-openresty-mHac)可能缓存静态文件响应。已通过 FastAPI 中间件添加 Cache-Control: no-cache, no-store, must-revalidate 头来阻止。如果仍有缓存问题,运行:

docker restart 1Panel-openresty-mHac

另外,app.jsstyle.css 的引用使用了版本号查询参数(?v=20260609),修改后递增版本号即可绕过所有缓存。 papers.json 是唯一数据源。新增论文通过 API 写操作 → 自动更新 JSON → 前端下次请求时自动获取最新数据。不要手动编辑 production 上的 papers.json 绕过 API,可能导致并发写入问题。


🔄 修改代码后的部署流程

1. 本地修改代码(在 paper_librarian/llm-library/ 下)
2. git add . && git commit -m "..." && git push origin main
3. scp 到宿主机scp -i hk_server_key -P 16844 <file> root@103.112.185.16:/opt/llm-library/<path>
4. docker cp 到容器ssh ... "docker cp /opt/llm-library/<path> llm-library:/app/<path>"
5. 重启容器ssh ... "docker restart llm-library"
6. 用无头浏览器验证网站各功能正常

注意: API 文件(server.py)改动后需要重启容器。静态文件(*.html, *.js, *.css)也需要重启才能生效(因为被 FastAPI StaticFiles 挂载)。


🧪 无头浏览器验证清单

每次部署后,用 headless browser 验证以下功能:

  • 首页加载9 张模块卡片正常渲染
  • 连通性检测条arXiv 和 HF 状态显示正常
  • 点击任意模块卡片 → 弹窗打开tabs 切换正常
  • 论文列表:主线/支线/前瞻 三个 section 显示正确
  • 📄 阅读」按钮PDF iframe 能加载(至少一篇测试)
  • 📖 译文」按钮:翻译 PDF 能加载(如果存在)
  • 搜索功能:输入关键词能返回结果
  • Esc 键关闭弹窗 / PDF
  • 底部栏链接spdis.space 可点击

📊 性能基线

指标 当前值 备注
论文总数 ~189 papers.json ~200KB
API 响应时间 <50ms 纯内存 JSON 读取
PDF 代理延迟 <100ms 本地文件系统读取
翻译耗时 3-8 min/篇 pdf2zh + DeepSeek取决于论文长度
容器镜像 ~4.3GB 含 PyTorch CPU + ONNX 模型

🔧 常见维护操作

新增论文

curl -X POST https://llmlibrary.spdis.space/api/papers?api_key=KEY \
  -H "Content-Type: application/json" \
  -d '{"module_id":"arch","area_id":"attention","section":"mainline","title":"...","authors":"...","year":2026,"venue":"arXiv","arxiv":"2601.01234","tags":["前沿"]}'

修改论文标签

curl -X PUT "https://llmlibrary.spdis.space/api/papers?api_key=KEY&module_id=arch&area_id=attention&title=Attention Is All You Need" \
  -H "Content-Type: application/json" \
  -d '{"tags":["起点","关键节点"]}'

触发翻译

curl -X POST "https://llmlibrary.spdis.space/api/translate/1706.03762?api_key=KEY"

检查翻译状态

# 查看所有翻译 PDF 数量
ssh ... "docker exec llm-library ls /app/papers/translated/ | wc -l"
# 查看翻译队列
curl https://llmlibrary.spdis.space/api/translate/status