From 3d869fd1fd9d1256bb56764f36dfc917fa8378e0 Mon Sep 17 00:00:00 2001 From: LaoWang <257199637@qq.com> Date: Tue, 9 Jun 2026 15:46:55 +0000 Subject: [PATCH] =?UTF-8?q?docs:=20add=20CODEBASE.md=20=E2=80=94=20?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E5=85=A8=E6=99=AF=E7=BB=B4=E6=8A=A4=E6=89=8B?= =?UTF-8?q?=E5=86=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CODEBASE.md | 229 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 229 insertions(+) create mode 100644 CODEBASE.md diff --git a/CODEBASE.md b/CODEBASE.md new file mode 100644 index 0000000..4fbdb72 --- /dev/null +++ b/CODEBASE.md @@ -0,0 +1,229 @@ +# 📘 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 Key:Bearer 头 或 `?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` 双胞胎 +两个文件内容几乎相同。`index_template.html` 是早期版本,被 `extract_data.py` 引用但路径硬编码为 `/app/working/workspaces/default/llm_library.html`。**实际服务的是 `index.html`。** 如果改前端 HTML 结构,只需改 `index.html`。 + +### 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. 搜索是简单子串匹配 +`GET /api/papers?q=...` 用 Python `in` 操作符在 title + authors 中做子串匹配。不支持模糊搜索、中文分词或 relevance ranking。对于 189 篇论文规模够用,但超过 500 篇可能需要改进。 + +### 6. 数据一致性 +`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 root@103.112.185.16:/opt/llm-library/ +4. docker cp 到容器:ssh ... "docker cp /opt/llm-library/ llm-library:/app/" +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 模型 | + +--- + +## 🔧 常见维护操作 + +### 新增论文 +```bash +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":["前沿"]}' +``` + +### 修改论文标签 +```bash +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":["起点","关键节点"]}' +``` + +### 触发翻译 +```bash +curl -X POST "https://llmlibrary.spdis.space/api/translate/1706.03762?api_key=KEY" +``` + +### 检查翻译状态 +```bash +# 查看所有翻译 PDF 数量 +ssh ... "docker exec llm-library ls /app/papers/translated/ | wc -l" +# 查看翻译队列 +curl https://llmlibrary.spdis.space/api/translate/status +```