18 KiB
EPUB 审校器长期维护手册
目标与边界
本工具服务于中日双语 EPUB 的网页审校:长篇连续阅读、逐段修改中文译文、标记翻译问题、导出反馈与修订 EPUB。它也提供可选的书架入口 AI 翻译模块,用于对已入书架的日语 EPUB 发起 API 辅助首译,并生成中日双语或纯中文 EPUB。翻译质量规则仍由项目根目录的 AGENTS.md、rules/translation_rules.md、当前系列的 series-style.md 与术语表共同约束。
AI 翻译模块是可选辅助首译入口,不替代长期项目里的候选词确认、复审、QA 与正式交付流程。用户在网页中留下的编辑、问题备注、长期规则建议,需要由 Codex 后续读取反馈文件后再沉淀到系列风格或候选术语中。网页术语表编辑是用户显式操作正式术语表的入口,保存时会直接写入配置路径指向的 JSON 文件。
数据模型与会话目录
--review-root 是唯一需要备份的运行数据目录。典型结构:
epub_review_sessions/
app_state.json
gpt_config.json
translation_jobs/
<job-id>/
job.json
translations.json
translated_outputs/
*_AI翻译_*.epub
<session-id>/
source.epub
extracted/
review_state/
epub_entries.json
rows.json
session.json
state.json
feedback/
translation_feedback.jsonl
feedback_for_codex.md
exports/
兼容要求:
rows.json是初始解析结果,不应因用户编辑而重写。state.json.edits保存用户修改与标记,字段缺省时必须能回退到原始译文。state.json.reading_position保存当前阅读位置,字段包括mode、scope、chapter_id、part_id、image_item_id、row_id、scroll_top、offset、updated_at;缺省时必须回退到首页/首章。session.json与app_state.json只保存会话定位信息。gpt_config.json只属于本地运行环境,不写入 EPUB、不写入导出文件、不通过 API 回传密钥。gpt_config.json可保存本地提示词设置与术语表路径,但仍不得写入 EPUB 或导出文件。translation_jobs/<job-id>/job.json保存整书 AI 翻译任务状态、进度、输出路径和日志;不得保存 API Key。translated_outputs/保存 AI 翻译生成的 EPUB;生成后应创建独立 session,使/api/sessions和书架自然可见。- 项目根目录的
打开EPUB审校器.cmd是用户一键入口,委托tools/epub_review_editor/open_editor.ps1创建.venv、安静检测 Flask 依赖、缺失时自动安装依赖、复用同版本已运行服务或启动server.py --daemon并打开浏览器;该入口不得硬编码用户私有路径。
目录与插图
结构接口 /api/structure 应保持向后兼容:
- text chapter:
kind: "text"、parts、row_count、touched_count、marked_count - image item: 优先作为 text chapter 的
items子项返回,字段为kind: "image"、images、parts: []、row_count: 0;没有所属文字章时才保留顶层 image chapter - 统计字段同时保留总数和细分:
chapter_count、text_chapter_count、image_chapter_count、top_level_image_chapter_count、image_count、asset_count。其中image_chapter_count表示目录里的图片条目数,不一定是顶层章节数 - part 标题默认使用
part0000形式,真实标题可保留在source_title - 插图通过
/api/asset/<entry>读取,必须继续使用extracted_path()防止路径穿越;只把 raster 图片纳入/api/structure与/api/asset白名单,不能任意读取解包目录文件
章节来源优先级:
- EPUB 内部目录页的中文条目
nav.xhtml/toc.xhtml- 正文
<h1-6>标题 - 文件名 fallback
不要把正文首段或第一句对白作为章节标题 fallback。宁可显示 part0000,也不要让目录被正文内容污染。
阅读与侧栏交互
验收口径:
- 顶栏左上角必须保留“书架”入口;书架是独立页面,列出
--review-root下历史打开过的审校书籍/会话。 - 点击书架条目必须通过会话打开流程进入对应审校页面,并恢复该书上次阅读位置;从审校页进入书架或切换书籍前必须保存当前阅读位置并沿用未保存编辑保护。
- 没有历史书籍时,书架显示空状态并提供“打开新的 EPUB”入口;开始页/书架页顶栏保持正常显示,不套用审校页沉浸隐藏。
- 初始进入审校时,左侧侧栏隐藏,正文占主要空间。
- 顶部“目录”只打开目录,“检索”只打开搜索/段落列表,不合并成一个常驻面板。
- 目录可展开/收起章节,插图作为所属章节的次级目录条目可点击阅读,文字 part 可点击定位。
- 点击目录章节、part 或插图条目后,侧栏不得自动收起;用户仍可点“隐藏”或按
Escape关闭。 - 检索必须保留“当前范围 / 全书全局”切换;当前范围随当前章节或 part 更新,全局检索可跨章节返回并跳转。
- 阅读模式不显示每段编号、快速编辑、快速标记按钮。
- 阅读区提供默认、护眼、夜间背景切换,并允许调整字号与行距;设置可保存在浏览器本地,不要求写入 EPUB 或 session。
- 审校页顶部标题栏与阅读标题栏默认收起,鼠标移到页面上方或键盘焦点进入标题栏时再显示;开始页保持正常显示;展开后不得遮住侧栏“隐藏”或审校工具“关闭”等面板操作。
- 鼠标停留在顶栏或阅读标题栏时,沉浸顶栏不得因自动计时过早收起;前端脚本和样式发布时应带版本参数或等效缓存失效机制,避免浏览器继续执行旧入口代码。
- 阅读区“上一节 / 下一节”必须按 spine/目录内的小章节顺序移动,覆盖文字 part 与插图条目,不得只跳顶层大章节。
- 点击正文段落打开右侧精修与重翻面板。
- 右侧面板打开或关闭导致正文宽度变化时,应恢复用户原来正在看的段落位置。
- 刷新页面、重启服务或从已有会话重新打开 EPUB 时,应恢复到上次阅读的章节/part/插图、段落和滚动位置。
- 移动窄屏下,侧栏和右侧面板应以抽屉/底部面板方式显示,不遮死主要操作。
编辑、反馈与导出
保存本段时必须:
- sanitize 中文 HTML,但允许
ruby/rb/rt/rp/mark/span等必要内联标签。 - 记录
before_cn_html、after_cn_html、问题类型、严重程度、标签、备注、长期规则建议。 - 更新
feedback/translation_feedback.jsonl。
生成反馈时必须:
- 写入
feedback_for_codex.md。 - 只汇总已编辑、已标记或有备注的段落。
- 保留日文原文、修改前、修改后,便于后续翻译规则复盘。
导出 EPUB 前必须先写回已保存的译文修改,插图文件保持原样。
GPT 重翻
GPT 能力是可选增强,不应影响离线审校。
安全与配置:
- 支持 OpenAI 兼容的
base_url、model、api_key。 - 支持单独设置
translation_prompt、format_prompt、character_prompt;缺省时必须回退到内置预设。 - API Key 优先读取
gpt_config.json,也支持环境变量OPENAI_API_KEY。 /api/gpt/config不得返回 API Key。/api/gpt/config可以返回提示词与预设,但不得返回密钥。/api/glossary读取和保存配置路径指向的mingcibiao.json;默认路径应由当前打开 EPUB 对应的系列目录或用户保存的配置决定,不应在通用维护口径中硬编码某个具体系列。- 未保存配置时,可在项目根目录下唯一的一级系列目录
mingcibiao.json中自动推断默认术语表;若无法唯一推断,则回退到项目根目录mingcibiao.json并允许用户在网页中显式设置。 - 术语表保存应保持 JSON 对象结构:key 为源语或匹配词,value 为译名,可继续使用
#作为备注分隔。 - 重翻时必须实时读取术语表,按目标段落日文纯文本、日文 HTML 与 ruby/rt 命中相关条目,不得只在提示词里泛泛要求“遵守术语表”。
- 未配置 Key 时,重翻接口应返回可读错误,不影响编辑保存。
- 重翻请求不得临时覆盖
base_url或model后继续复用已保存密钥;要更换端点或模型必须先保存配置,避免把密钥发往未确认端点。
单段重翻流程:
- 用户点击阅读段落,打开右侧面板。
- 用户可填写额外重翻要求。
/api/row/<row_id>/retranslate使用提示词、目标语句、实时命中的术语表条目、同文件前后一段少量上下文、角色口吻提示生成候选。- 候选只显示在面板里,不自动覆盖。
- 用户点击“应用重翻”后写入编辑框,再点击保存才进入审校记录。
- 候选事件写入 jsonl,便于后续根据精修反馈优化提示词;当前版本不自动改写提示词。
术语实时命中实现口径:
- 每次
/api/row/<row_id>/retranslate请求都必须读取当前配置的 glossary 文件,不使用服务启动时的缓存作为唯一来源。 - 单段重翻的术语命中范围只包括目标段落日文纯文本和日文 HTML 中的可见文本/ruby/rb/rt 信息;当前中文译文不能反向触发新术语,避免把旧误译或幻觉带入 prompt。
- 传给 GPT 的目标日文 HTML 只保留必要内联标签和 ruby 结构,不携带 class/style/data-* 等属性,避免属性值污染术语命中或 prompt。
- 前后一段上下文只用于语气、指代和连贯性参考,不参与单段重翻的术语命中;单段重翻的
glossary_matches只由目标段落自身触发。 - 正式术语表 value 如包含
#,#前是指定译名,#后只是备注,不写入正文。 - 未命中术语时,重翻 prompt 不得塞入无关术语兜底规则;例如只有原文出现
ファルシオン或Falchion时,才可命中“大砍刀”。 - 读取旧版保存提示词时,若只剩“优先服从给定名词表”这类泛泛规则,应回退到当前内置预设,避免用户继续沿用已淘汰的全局术语设计。
- 任何术语特例都不得写入全局 prompt。Falchion / ファルシオン / 大砍刀 只能由
glossary_matches动态生成;未命中时,重翻 messages 中不得出现这些字符串。 - 命中结果必须随候选事件写入 jsonl,字段至少包含
glossary_path与glossary_matches;未命中时记录空数组,便于排查 prompt。
Prompt 第一性原理:
- prompt 必须只携带目标段落、少量上下文、命中术语、系列风格摘要和输出格式;单段重翻不携带完整术语表。
- prompt 不得只泛泛要求遵守术语表,必须列出本段命中的具体术语;未命中的术语不得进入提示。
- prompt 必须要求只输出修订后的中文 HTML,不输出说明。
- prompt 必须要求不合并、不拆分段落,不删除段落编号或必要标签。
- prompt 必须要求保留有意义 ruby 为真实
<ruby><rb>...</rb><rt>...</rt></ruby>。
Prompt 硬规则:
- 输出简体中文轻小说文风。
- 意义优先,不贴日语语序硬译。
- 不合并、不拆分段落。
- 有意义 ruby 必须保留为真实
<ruby><rb>...</rb><rt>...</rt></ruby>,不要改成括号。 - 标点统一为简中出版格式:对话引号用「」,省略号用……,破折号用——,问号用?,感叹号用!,问叹连用用?!/!?。
整书 AI 翻译
整书 AI 翻译从书架页进入,只处理已经上传或打开过、因此已有 session 的 EPUB。它必须作为后台任务运行,前端只负责创建任务、轮询进度、展示日志和打开输出 session。
后端接口口径:
GET /api/translation/defaults返回当前公开 GPT 配置、默认提示词、默认术语表路径、默认范围和输出模式,不返回 API Key。GET /api/session/<session_id>/translation-source只读取指定 session,统计可翻译日文段落和样例,不切换 active session。POST /api/translation/start创建后台任务并快速返回job_id;请求不得临时覆盖base_url或model后复用已保存密钥,要更换端点或模型必须先保存配置。GET /api/translation/jobs/<job_id>返回任务状态、进度、日志、输出 EPUB 和输出 session id,不返回 API Key。
翻译输入与提示词:
- 日语源 EPUB 需要新的源段落抽取器,不复用只识别双语灰字的
build_rows()。 - 默认范围必须是前 N 段试译,当前默认 20 段;全书翻译必须由用户显式选择。
- 每段请求只携带目标段落日文 HTML、前后一段少量上下文、该段实时命中的术语、翻译提示词、格式提示词与角色口吻提示词。
- 不得把完整术语表塞进 prompt;未命中的术语不得进入 prompt。
- 输出只接受当前段中文 HTML 片段,随后进行中文标点规整、HTML sanitize 与术语 ruby 括号形式修复。
输出与书架:
bilingual模式在原日文<p>后插入中文<p>,并把原日文段落标为灰色,方便进入现有双语审校器逐段审校。translated模式用中文替换原日文段落;该输出会加入书架,但由于没有日中成对段落,可能没有逐段双语审校行。- 图片、样式、OPF、spine 和非正文文件应原样保留。
- 翻译失败的单段必须写入可见占位和任务日志,不能让整本输出静默缺段。
- 输出 EPUB 创建 session 时不应自动切换用户当前 active session;只有用户点击“打开输出审校”才切换。
任务安全与恢复:
- 后台 job 不能在网络调用期间持有全局锁;只在写状态文件时短暂加锁或原子写入。
job.json和中间translations.json必须原子写入,避免前端轮询读到半截 JSON。- 日志、状态、EPUB 元数据和反馈文件不得包含 API Key。
- 任务失败时应将状态置为
failed并写清楚错误,不能让前端永久停在 running。 - 后续如加入暂停/取消/恢复,必须保持现有
job.json字段向后兼容。
版本规则
版本号使用 version = "0.1.0" 格式:
| 层级 | 触发条件 |
|---|---|
| PATCH | 修复 bug 或兼容性小修 |
| MINOR | 新增向后兼容能力、API 字段或可选配置 |
| MAJOR | 破坏兼容的 API、配置或行为变更 |
发版时必须同步:
version.pystatic/index.html顶部版本徽标 fallbackREADME.md当前版本与版本说明- 必要时同步
MAINTENANCE.md中的维护口径
版本文档同步边界:
- 审校器自身行为、API、配置、前端交互变化,同步本文件。
- 会影响整个翻译项目的规则变化,同步项目根目录
AGENTS.md与rules/translation_rules.md,不要只写在审校器维护手册中。 - 对外最小包内容、启动方式或发布说明变化,同步 README。
回归清单
每轮改动至少检查:
python -m py_compile tools/epub_review_editor/server.py tools/epub_review_editor/version.pynode --check tools/epub_review_editor/static/app.jspowershell -NoProfile -ExecutionPolicy Bypass -File tools/epub_review_editor/open_editor.ps1能启动服务并打印 URL- 从项目根目录执行
打开EPUB审校器.cmd能启动服务;失败时窗口停留并显示错误;成功时浏览器打开书架/上传页 - 重复执行一键入口时优先复用同版本已运行服务;如需新起服务,端口探测不得允许多个服务同时监听同一端口
git diff --check/api/version/api/session/api/structure/api/gpt/config- 首个插图
/api/asset/...返回 200 且浏览器可显示 - 非白名单或非 raster asset 不能被
/api/asset/...读取 - 目录按钮、检索按钮、隐藏侧栏、点击段落打开右侧面板
- 阅读段落下方没有编号和“快速编辑/快速标记”
- 未配置 API Key 时重翻返回清晰错误
- 配置保存后
/api/gpt/config不包含密钥字段 /api/glossary能读取术语表;新增、修改、删除条目后保存,再读取内容一致- 重翻候选事件包含
glossary_path与glossary_matches - 无关段落的重翻 messages 不得包含
大砍刀、Falchion或ファルシオン;含ファルシオン或 rubyFalchion的段落必须命中“大砍刀” - 顶栏复杂操作收敛在“审校工具”入口;未选择段落时仍可配置 AI、术语表并执行反馈与交付,段落编辑/重翻按钮禁用
/api/reading-position能保存/读取位置;刷新或重启后回到上次阅读处- 阅读外观默认/护眼/夜间、字号、行距切换即时生效,刷新后仍保留;夜间模式下正文、原文、按钮和侧栏文本可读
- 审校页顶部栏和阅读标题栏默认不遮挡正文,鼠标移到页面上方后可操作;开始页顶栏不隐藏;目录侧栏和审校工具打开时,其顶部按钮仍可直接点击
- 鼠标停留在顶栏按钮区域时,书架/打开 EPUB/审校工具按钮能连续点击,不会在点击前自动收起;浏览器重新加载后应使用当前版本 app.js/style.css
- “上一节 / 下一节”在相邻 part 和章节内插图之间移动,不再按顶层大章节跳转
- 顶栏左上角“书架”能打开独立书架页;书架列出历史会话,空状态可进入上传页,点击书籍可进入对应审校页并恢复阅读位置
- 书架卡片“翻译”按钮能进入独立翻译页,且不会触发卡片的“进入审校”
/api/translation/defaults不返回 API Key,提示词和术语表路径可读取/api/session/<session_id>/translation-source能统计日语 EPUB 段落;非日语或无正文时返回清晰空状态- 未配置 API Key 时,
/api/translation/start返回清晰错误,不影响阅读、编辑、导出和单段重翻 - 启动整书翻译任务后,
/api/translation/jobs/<job_id>可看到 pending/running/completed/failed、总段数、已完成数、当前文件和日志 - 默认翻译范围是前 20 段试译;全书翻译必须由用户显式切换并确认
- 整书翻译每段只注入该段源文/ruby 命中的术语,不注入无关术语或全表
- 整书翻译完成后,输出 EPUB 写入
translated_outputs/并自动出现在书架;中日双语输出打开后row_count > 0 - 纯中文输出可以加入书架,且文档说明其不适合逐段双语审校
- 离开翻译页、返回书架或打开输出审校时,前端进度轮询会停止