Skip to content

Commit 81121eb

Browse files
committed
feat: 實虛詞典 — headword→page-image dictionary lookup for classical Chinese
- 7 new API endpoints: /api/dict/search, /api/dict/page-images, /api/dict/status, /api/dict/chat, etc. - Frontend: dict.html + dict.js (868 lines) + dict.css (828 lines) - Headword index: xuci 1389 entries, changyong 5509 entries (QC 35/35 passed) - Page images served from R2 CDN (1580 webp pages uploaded) - AI chat with dictionary context (verified headwords only) - Docs: implementation notes + launch checklist
1 parent c99ce4f commit 81121eb

9 files changed

Lines changed: 3813 additions & 2 deletions

File tree

backend/main.py

Lines changed: 1143 additions & 1 deletion
Large diffs are not rendered by default.

docs/shixuci_implementation.md

Lines changed: 513 additions & 0 deletions
Large diffs are not rendered by default.

docs/shixuci_launch_checklist.md

Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
# 实虚词典上线前检查单
2+
3+
## 当前结论
4+
5+
- 代码链路已基本打通:
6+
- `/dict.html`
7+
- `GET /api/dict/status`
8+
- `GET /api/dict/search`
9+
- `GET /api/dict/textbook`
10+
- `GET /api/dict/gaokao`
11+
- `GET /api/dict/references`
12+
- `GET /api/dict/page-images`
13+
- `POST /api/dict/chat`
14+
- 运行时页图索引已生成:
15+
- `xuci = 1389`
16+
- `changyong = 5509`
17+
- 当前固定样张质检通过:
18+
- `xuci = 12 / 12`
19+
- `changyong = 23 / 23`
20+
- `changyong` 当前唯一单字头覆盖率约 `82.03%`
21+
- 这不是错页问题,是“部分字头暂未覆盖”
22+
- 前端空状态与外部参考兜底逻辑已存在
23+
24+
## 当前阻塞项
25+
26+
### 阻塞 1:馆藏辞典页图 CDN 仍未上线
27+
28+
当前抽查全部返回 `404`
29+
30+
- `https://img.rdfzer.com/pages/dict_xuci/p188.webp`
31+
- `https://img.rdfzer.com/pages/dict_xuci/p844.webp`
32+
- `https://img.rdfzer.com/pages/dict_changyong/p99.webp`
33+
- `https://img.rdfzer.com/pages/dict_changyong/p101.webp`
34+
35+
结论:
36+
37+
- 代码与页码索引已就绪
38+
- 但学生端上线后会看到“页图待导入”或加载失败
39+
- 在页图上传到 R2/CDN 之前,不能算可上线状态
40+
41+
### 阻塞 2:生产环境尚未部署新页面与新数据
42+
43+
当前只完成本地代码、索引和质检。
44+
45+
仍未确认:
46+
47+
- 生产后端是否已加载新 `main.py`
48+
- 生产前端是否已提供 `/dict.html`
49+
- 生产数据目录是否已同步:
50+
- `dict_headword_pages.json`
51+
- `dict_headword_qc.json`
52+
53+
## 本轮核查覆盖范围
54+
55+
### 前端逻辑
56+
57+
- 导航入口:
58+
- 首页保留原有搜索 / 真题 / 数据 / 图谱
59+
- 新增 `实虚词典` 链接,不干扰原视图切换
60+
- `/dict.html` 页面结构:
61+
- 教材
62+
- 馆藏辞典原页
63+
- 官方与外部参考
64+
- 真题
65+
- AI 多轮对话
66+
- 搜索并发:
67+
- 已补 query token 防止旧请求覆盖新搜索结果
68+
- 已把“自动首轮 AI”从搜索主流程解耦,避免用户被 AI 响应卡住
69+
- 降级逻辑:
70+
- 教材 / 馆藏辞典 / 外部参考 / 真题四路独立容错
71+
- 单路失败不拖垮整页
72+
73+
### 后端逻辑
74+
75+
- 默认启用源已切到:
76+
- `xuci`
77+
- `changyong`
78+
- `dict/status` 已返回:
79+
- `verified_headwords`
80+
- `candidate_headwords`
81+
- `coverage_ratio`
82+
- `student_safe_mode = page_images_only`
83+
- `dict/search` 当前优先走:
84+
- `dict_headword_pages.json`
85+
- 再回退 `dictionary_index.db`
86+
- `dict/references` 已接:
87+
- 教育部《重编国语辞典修订本》
88+
- 教育部《国语辞典简编本》
89+
- 教育部《异体字字典》
90+
- `zi.tools`
91+
- 汉语多功能字库
92+
93+
### 数据逻辑
94+
95+
- `xuci`
96+
- 通过 `pdftotext -raw` 检测字头起始页
97+
- 再按下一个字头起始页推完整页码区间
98+
- `changyong`
99+
- 通过 CSV 单字头顺序底稿 + 页眉 Vision OCR
100+
- 只 OCR 页眉,不 OCR 全页正文
101+
- 学生端统一只展示原页图片,不展示 OCR 正文
102+
103+
## 上线门槛
104+
105+
必须同时满足以下条件,才算可上线:
106+
107+
1. 生产 CDN 上 dict 页图可访问
108+
2. 生产后端已部署新代码
109+
3. 生产数据目录已同步最新索引文件
110+
4. 生产 `/dict.html` 页面可访问
111+
5. 生产 API 烟测通过
112+
6. 页面端到端手工点击通过
113+
114+
任一项不满足,均为 `NO-GO`
115+
116+
## 部署前数据同步清单
117+
118+
必须同步:
119+
120+
- `/Users/ylsuen/textbook_ai_migration/data/index/dict_headword_pages.json`
121+
- `/Users/ylsuen/textbook_ai_migration/data/index/dict_headword_qc.json`
122+
123+
建议一并留档:
124+
125+
- `/Users/ylsuen/textbook_ai_migration/data/index/dict_headword_candidates_xuci.jsonl`
126+
- `/Users/ylsuen/textbook_ai_migration/data/index/dict_headword_candidates_changyong.jsonl`
127+
- `/Users/ylsuen/textbook_ai_migration/data/index/dict_headword_review.tsv`
128+
129+
必须上传到 R2/CDN:
130+
131+
- `pages/dict_xuci/p{N}.webp`
132+
- `pages/dict_changyong/p{N}.webp`
133+
134+
## 部署前代码同步清单
135+
136+
必须同步:
137+
138+
- `/Users/ylsuen/textbook_ai_migration/platform/backend/main.py`
139+
- `/Users/ylsuen/textbook_ai_migration/platform/frontend/dict.html`
140+
- `/Users/ylsuen/textbook_ai_migration/platform/frontend/assets/dict.css`
141+
- `/Users/ylsuen/textbook_ai_migration/platform/frontend/assets/dict.js`
142+
143+
建议随仓保留:
144+
145+
- `/Users/ylsuen/textbook_ai_migration/platform/scripts/build_dict_headword_index.py`
146+
- `/Users/ylsuen/textbook_ai_migration/platform/scripts/vision_ocr.swift`
147+
- `/Users/ylsuen/textbook_ai_migration/scripts/40_dictionary_ocr_qc.py`
148+
149+
## 生产烟测
150+
151+
### 接口烟测
152+
153+
必须通过:
154+
155+
```bash
156+
curl -sS https://sun.bdfz.net/api/dict/status
157+
curl -sS "https://sun.bdfz.net/api/dict/search?q=之"
158+
curl -sS "https://sun.bdfz.net/api/dict/search?q=觇"
159+
curl -sS "https://sun.bdfz.net/api/dict/search?q=长"
160+
curl -sS "https://sun.bdfz.net/api/dict/search?q=所以"
161+
curl -sS "https://sun.bdfz.net/api/dict/references?q=斯民"
162+
curl -sS "https://sun.bdfz.net/api/dict/textbook?q=之"
163+
curl -sS "https://sun.bdfz.net/api/dict/gaokao?q=之"
164+
curl -sS "https://sun.bdfz.net/api/dict/page-images?dict_source=changyong&page=99&context=2"
165+
curl -sS "https://sun.bdfz.net/api/dict/page-images?dict_source=xuci&page=188&context=2"
166+
```
167+
168+
通过标准:
169+
170+
- `status` 返回 `enabled_sources` 包含 `xuci, changyong`
171+
- `search?q=之` 返回 `changyong + xuci`
172+
- `search?q=觇` 返回 `changyong`
173+
- `search?q=所以` 返回 `xuci`
174+
- `references?q=斯民` 返回外部参考卡片
175+
- `page-images` 返回真实 URL,不是空数组
176+
177+
### CDN 烟测
178+
179+
必须通过:
180+
181+
```bash
182+
curl -sSI https://img.rdfzer.com/pages/dict_xuci/p188.webp
183+
curl -sSI https://img.rdfzer.com/pages/dict_xuci/p844.webp
184+
curl -sSI https://img.rdfzer.com/pages/dict_changyong/p99.webp
185+
curl -sSI https://img.rdfzer.com/pages/dict_changyong/p101.webp
186+
```
187+
188+
通过标准:
189+
190+
- HTTP `200`
191+
- `content-type` 为图片类型
192+
193+
### 页面手测
194+
195+
至少手测这些词:
196+
197+
- ``
198+
- ``
199+
- ``
200+
- `所以`
201+
- `斯民`
202+
203+
必须确认:
204+
205+
- 页面能打开 `/dict.html`
206+
- 左栏教材只出现古文 / 古诗词
207+
- 右栏馆藏辞典能打开正确页图
208+
- `` 同时能看到王力本和虚词本
209+
- `斯民` 在馆藏未命中时,右栏外部参考正常出现
210+
- 真题区正常返回
211+
- AI 首轮自动触发
212+
- 新搜索不会被上一次 AI 卡住
213+
214+
## 回滚点
215+
216+
### 最小回滚
217+
218+
- 保留页面,但把后端环境变量 `DICT_ENABLED_SOURCES` 收回 `xuci`
219+
220+
### 代码回滚
221+
222+
- 回退:
223+
- `backend/main.py`
224+
- `frontend/dict.html`
225+
- `frontend/assets/dict.css`
226+
- `frontend/assets/dict.js`
227+
228+
### 数据回滚
229+
230+
- 回退:
231+
- `data/index/dict_headword_pages.json`
232+
- `data/index/dict_headword_qc.json`
233+
234+
## 当前推荐判断
235+
236+
- 代码:`GO`
237+
- 数据索引:`GO`
238+
- 质检:`GO`
239+
- 生产上线:`NO-GO`
240+
241+
当前唯一明确阻塞项是:
242+
243+
- 馆藏辞典页图尚未上传到生产 CDN
244+
245+
在这一步完成之前,不建议部署。

frontend/assets/app.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -584,7 +584,7 @@ async function loadFrontendVersion() {
584584
loadFrontendVersion();
585585

586586
// ── Navigation ────────────────────────────────────────────
587-
document.querySelectorAll('.nav-btn').forEach(btn => {
587+
document.querySelectorAll('.nav-btn[data-view]').forEach(btn => {
588588
btn.addEventListener('click', () => {
589589
document.querySelectorAll('.nav-btn').forEach(b => b.classList.remove('active'));
590590
document.querySelectorAll('.view').forEach(v => v.classList.remove('active'));

0 commit comments

Comments
 (0)