Babel Studio:一套本地优先的长文本翻译与漫画自动嵌字系统
项目名称:Babel Studio(巴别塔智能本地化工作室)
项目类别:生成式 AI / 智能体 / 多模态内容生产
开源仓库:GitHub - rust234a2/babel-studio
1. 背景:翻译完成不等于作品完成
小说和漫画的本地化,麻烦远不止把一句日文换成一句中文。长篇小说要一直维护人名、地名、称谓和世界观设定。章节一多,同一个角色很容易换个上下文就换了译名。漫画还多出竖排 OCR、阅读顺序、气泡检测、原文擦除、背景修复和重新排版。现有工具大多只管其中一截:译文留在聊天窗口,术语表放在另一个文件,嵌字时又得回到图像软件。
所以我想把这些环节收进同一个工作台。用户导入 TXT、Markdown、DOCX、EPUB 或漫画页面后,系统为作品建立档案,接着完成翻译、审校、消字、嵌字和质量复检,中间事件与产物也一并保存。未公开文本尽量不离开本机;公开或已获授权的漫画可以按需调用 Stepfun 视觉服务。
Babel Studio 从一开始就按“模型会失败”来设计。OCR 服务没启动,任务就走本地回退;文字放不进气泡,排版器宁可标记失败,也不能悄悄画出边界;微调 loss 再好看,也得经过固定测试才能谈效果。系统要留下足够的证据,让人能顺着记录找到出错的那一层。
2. 问题分析:为什么这条流水线难做
2.1 长篇文本的问题是记忆和约束
单个段落翻译得自然,不代表整本书都能保持一致。每次都把完整术语表和全部前文塞进 Prompt,上下文成本很快就会上涨,模型还未必听话;每章各管一份词表,人物改名时又得逐章修改。我最后采用的是作品级记忆、按需召回和译后检查,没有继续堆上下文长度。
2.2 漫画的问题横跨文字与图像
OCR 只能回答“图片里可能写了什么”。文字属于哪个气泡、先读哪一列、擦掉原文后怎样补背景、译文放进去会不会挡住人物,都不是 OCR 能单独解决的。日文竖排、多列对白、细长气泡和不规则边框还会继续放大误差,因此区域检测、OCR、图像修复、排版和视觉复检缺一不可。
2.3 本地部署的问题是资源调度
DGX Spark 有 128GB 统一内存,但 Qwen、Step-3.7-Flash、Sakura、Nemotron、向量模型和图像工具还是不适合全按峰值配置常驻。长上下文建库与高并发翻译的负载也不一样。模型装得下,只能说明方案有机会运行;现场能不能稳定,还得看量化方式、常驻服务和运行档位怎么安排。
2.4 团队协作的问题是模型太大
前端队友的普通开发机跑不了几十到上百 GB 的模型。假如 UI 开发必须连着真实推理服务,两个人只能轮流占用同一台模型机。为了解开这个结,项目提供了稳定的接口契约、可回放样例和 Mock/API 双模式,日常前端开发不必等 GPU 空闲。
3. 设计目标、作品特点与核心亮点
Babel Studio 用一套作品档案承接小说和漫画,让文本翻译与漫画嵌字共享术语资产。翻译时只注入当前段落命中的术语,完成后再检查定译、占位符和疑似漏译,既控制上下文长度,也避免同一个人物在不同媒介中出现两套名字。正文、模型权重、训练语料和 checkpoint 默认留在 NVIDIA DGX Spark;漫画视觉理解则保留 Stepfun API 和本地 OCR 两条路线,由用户按素材的敏感程度选择,云端不可用时也能降级运行。翻译、审校、文化适配和视觉处理各用合适的模型,系统只接管重复劳动,不承诺无人值守出版;不规则艺术字、跨格 SFX、严重受损背景和无法安全排版的气泡都会标记为needs_human,生成文件与通过质检也会分别显示。任务执行期间,每个阶段都会写入带递增序号的事件,章节、术语、检测图、消字图、成品页和质量报告则保存为可定位的产物。这样既能把质量问题追到具体章节、页面和气泡,也能在浏览器刷新或网络波动后从最后一条事件继续,不必重新支付一遍模型推理成本。
4. 系统设计:架构思路与模块关系
4.1 这套系统里,各项技术分别做什么
前端使用 React、TypeScript 和 Vite。FastAPI 本身不负责翻译,它把文件上传、任务创建和结果查询封装成 API,再编排后面的智能体。浏览器通过 HTTP 发起任务,通过 SSE 持续接收进度。对于这种服务器不断汇报、浏览器主要负责接收的长任务,单向事件流已经够用。
这里说的智能体并非某种新模型,而是职责、输入输出和工具权限都写清楚的程序。翻译智能体生成初稿,审校智能体负责回译与语义检查,文化适配智能体处理俚语和必要的缩句,编排器安排执行顺序、重试和降级。文字生成交给大语言模型,语义比较交给向量模型,图片与文字的联合理解则交给多模态模型。
漫画侧还会用到 OCR、目标检测和图像修复。OCR 识别字符,检测器寻找文字区域并生成待清除像素的 mask,LaMa 根据四周图像补回被擦除的背景,排版器再计算字体、行宽、方向和标点禁则。这几项工具各管一件事,无法由视觉模型的一次整页回答包办。
本地推理使用启用GGML_CUDA的 llama.cpp,把计算交给 NVIDIA GPU。权重采用 GGUF 格式,Q4、Q6、Q8 等量化方式用少量质量损失换取更低内存占用。KV Cache 保存已经算过的上下文状态,长度和精度都会影响内存与速度。LoRA 只训练少量附加参数,相关实验由 PyTorch CUDA 与 Triton 完成。
4.2 整体架构与设计思路
系统被拆成浏览器、编排器、领域流水线、模型服务和持久化五层:
React / TypeScript / Vite │ HTTP + SSE ▼ FastAPI Orchestrator │ ├── 文档解析 ── 术语召回 ── 翻译 ── 回译审校 │ └── 漫画检测 ── OCR ── 消字 ── 嵌字 ── 视觉复检 │ ├── Qwen / Sakura / Nemotron / bge-m3 ├── Step-3.7-Flash / Stepfun Vision └── comic-text-detector / RapidOCR / LaMa │ ▼ SQLite + JSONL ledger + 图片与报告 Artifact这套架构有两条基本原则。能精确计算的内容交给代码,例如文档解析、气泡坐标、字符预算和事件序号;翻译、文化改写与视觉语境判断才交给模型。另一个原则是用稳定契约隔离变化:模型服务统一提供 OpenAI 兼容接口,流水线事件遵循固定 Schema,产物通过统一标识关联。以后替换模型时,前端和任务记录不用跟着推倒重来。
单个模型很难同时顾好翻译流畅度、术语一致性、文化适配和质量判断。拆开以后,每一步可以选更合适的模型,也可以单独重试或降级。前端始终只面对一套任务协议,不必关心后台刚刚切换了哪条模型路线。
5. 技术实现、技术栈与优化方案
5.1 文档解析与作品记忆
小说链路支持 TXT、Markdown、DOCX 和 EPUB。日文纯文本兼容 CP932、EUC-JP 与 ISO-2022-JP;DOCX 按段落提取,EPUB 按 OPF spine 顺序读取并忽略 ruby 注音。作品术语存入 SQLite,用project_id隔离,不同语言使用各自的候选规则。翻译时只召回当前段落命中的术语,再附上少量前后文帮助模型判断指代和口吻。模型仍被要求只输出当前段落,以免上下文越滚越长。
翻译完成后,代码先检查定译、占位符和疑似漏译。随后由 Nemotron 回译,bge-m3 比较原文与回译文本的语义相似度。低于阈值的片段会进入重译或人工复核。术语也不再是一段用完即丢的 Prompt,而是可以编辑、追踪和重新检查的数据。
5.2 技术栈说明:NVIDIA、Stepfun 与模型路由
模型没有简单按参数量排座次,我按任务分工:
| 类别 | 实际采用的技术 | 在项目中的作用 |
|---|---|---|
| NVIDIA 硬件与 SDK | DGX Spark、GB10 Grace Blackwell、NVIDIA CUDA Toolkit 13.0 | 提供 128GB 统一内存与本地 GPU 推理能力 |
| NVIDIA 大模型 | NVIDIA Nemotron Nano 9B v2 | 回译审校、文化适配和缩句 |
| Stepfun 阶跃星辰模型 | step-1o-turbo-vision、Step-3.7-Flash + mmproj | 云端漫画理解、本地长上下文与视觉复检 |
| 翻译与检索模型 | Qwen3.6-35B-A3B、Sakura-14B-Qwen3-v1.5、bge-m3 | 通用翻译、日译中和语义相似度计算 |
| 漫画视觉工具 | comic-text-detector、OpenCV、ONNX Runtime、manga-ocr、RapidOCR、LaMa | 检测、OCR、消字和背景修复 |
| 应用与数据层 | React、TypeScript、Vite、FastAPI、SQLite、JSONL | UI、任务编排、术语资产和事件记录 |
本地模型都提供 OpenAI 兼容接口。上层智能体只依赖服务地址和统一请求格式,所以 Sakura 没启动时可以退回 Qwen,Nemotron 更换部署方式也不用重写编排逻辑:
qwen=LLMClient("http://localhost:8081","qwen-batch")nemotron=LLMClient("http://localhost:8001","nemotron-local")sakura=LLMClient("http://localhost:8083","sakura")bge=LLMClient("http://localhost:8002","bge-m3")我也评估过用 TensorRT-LLM 的trtllm-serve部署 Nemotron。不过在当前 aarch64 环境中,交付版本最后统一用了 CUDA 版 llama.cpp。应用层可以由 Docker Compose 启动,GPU 模型仍跑在宿主机,目前不依赖 NVIDIA Container Toolkit。这里列的是实际落地方案,而不是所有尝试过的技术名词。
5.3 漫画处理链与嵌字效果
漫画页先经过检测器,得到文字区域和像素级 mask,再由 manga-ocr、RapidOCR 或 Stepfunstep-1o-turbo-vision识别内容。纯色气泡直接填充,复杂背景交给 LaMa 修复。OCR、翻译和排版数据都用bubble_id串联,视觉模型返回的阅读顺序、说话人线索和情绪才能准确落回本地检测框。
嵌字时,排版器从可读字号开始尝试换行和缩放。只有确认溢出且字符预算允许,系统才会调用文化适配智能体缩句;还是放不下,就保留完整译文并转交人工处理。最终页面还可送入 Stepfun Vision 或本地 Step-3.7-Flash + mmproj,检查overflow、occlusion和misplaced。mmproj 是把视觉编码结果映射给语言模型的投影权重。
下面两张图来自同一页测试素材。系统保留了拟声字和线稿,只清除气泡里的原文,再按气泡宽高选择字号与换行。
下面两张图来自同一页测试素材。系统保留了拟声字和线稿,只清除气泡里的原文,再按气泡宽高选择字号与换行。
| 消字后的中间图 | 日译中并重新嵌字后的成品 |
|---|---|
5.4 长任务事件与前端协作
FastAPI 把事件追加到 JSONL ledger,每条都带递增的seq。前端记住最后收到的序号,重连时从after_seq或Last-Event-ID接着收;状态层会丢弃重复事件。订阅端只需要建立一条 EventSource:
constsource=newEventSource(`${baseUrl}/jobs/${jobId}/events?after_seq=${afterSeq}`,);前端支持 Mock 和 API 两种模式。队友平时用小型 fixture 开发书架、任务状态和错误界面,不需要下载模型权重。分支提交后,我再拉到模型机上跑契约校验,连接真实的 FastAPI、SSE 和模型服务。这样,UI 开发不会卡在 GPU 排期上。
5.5 部署说明:利用本地算力部署智能体
项目部署在 NVIDIA DGX Spark 上,硬件是 GB10 Grace Blackwell Superchip 和 128GB 统一内存,CUDA Toolkit 版本为 13.0。Qwen、Sakura、Nemotron、bge-m3 和 Step-3.7-Flash 分别由独立的 llama-server 进程加载;翻译、审校、文化适配等 Python 智能体运行在 FastAPI 编排器中,通过固定端口调用模型。模型进程可以按任务阶段启停,某项服务不可用时也方便切到回退路线。
模型权重、语料和 checkpoint 不进入 Git,由.env或MODEL_DIR指向模型机目录。先在 DGX Spark 上编译启用 CUDA 的 llama.cpp server:
cmake-Bbuild-DGGML_CUDA=ON-DLLAMA_BUILD_SERVER=ON cmake--buildbuild -j$(nproc)端口约定如下:Qwen8081、Sakura8083、Nemotron8001、bge-m38002、Step-3.7-Flash + mmproj8080。常用服务由仓库脚本启动:
MODEL_DIR=/path/to/models ./models/serve-B-qwen.shMODEL_DIR=/path/to/models ./models/serve-nemotron.shMODEL_DIR=/path/to/models ./models/serve-bge.shMODEL_DIR=/path/to/models ./models/serve-sakura.sh模型服务健康后,再启动 FastAPI 编排器和 React 前端:
python3-mvenv .venv .venv/bin/pipinstall-rorchestrator/requirements.txt .venv/bin/uvicorn orchestrator.app:app--host0.0.0.0--port8051npmcicp.env.example .envnpmrun devStepfun 云端视觉路线通过本机环境变量启用,密钥不会提交到 Git:
VITE_DATA_MODE=api VITE_API_BASE_URL=/api/v1 VITE_API_TIMEOUT_MS=15000 STEPFUN_API_KEY=<本地填写> STEPFUN_VLM_MODEL=step-1o-turbo-vision浏览器入口是http://localhost:5173/。在服务器内部,可以用http://localhost:8051/health检查 FastAPI;浏览器端不直接访问这个地址,而是请求同源/api,再由 Vite 转发到后端。用户创建任务后,编排器按源语言和任务阶段选择本地智能体。只有打开视觉增强且素材允许时,漫画页面才会调用 Stepfun API。应用层也能通过 Docker Compose 启动,不过 GPU 模型服务仍留在宿主机,以免 CUDA 环境和大体积权重拖累日常前端联调。
5.6 大模型优化方案
资源方面,Qwen 使用UD-Q4_K_M,Sakura 使用 Q6_K,Step-3.7-Flash 使用 IQ4_XS 和 Q8 KV Cache,bge-m3 使用 Q8_0。这样能为 OCR、审校和图像处理留出内存。吞吐方面,我实测了 8、16 和 32 路并发,最后把 Qwen 定在 16 路,批量聚合吞吐约 235 token/s。继续提高并发,收益没有同比增长,延迟和内存波动反而更大。
调度上,环境分成建库/复核档与翻译量产档:大模型按阶段切换,小模型按需常驻,训练和整本夜跑通过SPARK-LOCK.md预约独占时段。质量优化则依靠固定测试集与位置打乱的第三方盲评。众包参考译文把 LoRA 训坏以后,我没有继续调参,而是换成通过教师门测试的 Qwen 35B 生成蒸馏目标。量化、KV Cache 和并发只是优化的一部分,数据与评估方法同样会决定最后的质量。
5.7 蒸馏模型
通用翻译老师 Qwen3.6-35B-A3B 是 MoE 模型,约有 35B 总参数,每个 token 激活约 3B。它以较低的单 token 活跃计算量换取更大的知识容量,适合在 DGX Spark 上当教师并批量生成数据。但部署时仍要保存和加载完整专家权重,运行时也依赖专家路由及相应算子。
学生 Qwen3-8B 是 Dense 模型,每个 token 都经过同一组 8B 参数。它并不天然比 MoE 质量更高,单 token 的活跃参数也未必更少。选择它主要是出于部署考虑:总权重小、计算路径固定、容易量化,LoRA 合并后还能直接导出成单个 GGUF。边缘设备、个人工作站和要求批延迟稳定的服务,更需要这种可移植性。
| 对比维度 | 35B-A3B MoE 老师 | 8B Dense 学生 |
|---|---|---|
| 主要价值 | 容量大,适合生成高质量蒸馏数据 | 权重小,适合长期部署和分发 |
| 每 token 路径 | 动态选择部分专家 | 固定经过同一组 Dense 层 |
| 内存特点 | 激活参数少,但完整专家权重仍需驻留 | 总权重只有 8B,Q4_K_M 成品为 4.68 GiB(5.03 GB) |
| 微调与导出 | 专家路由和适配模块更复杂 | LoRA、merge、GGUF 量化流程成熟直接 |
| 本项目定位 | 教师与高吞吐量产档 | 可部署的中译英专用学生 |
这次蒸馏要解决的是交付体积,而不是证明 Dense 全面胜过 MoE。教师门测试使用 200 条中译英网文样本,并用位置打乱的 DeepSeek A/B 判读。本地 35B 相对基础 8B 为 124 胜、60 负、16 平,净偏好+32pt,在这批数据上足以担任教师。随后由它生成 5 万条目标,清理 200 条残留 CJK 的输出,留下 49,800 条训练数据。8B 学生用 LoRA 训练 3,113 步,合并后量化为 4.68 GiB(5.03 GB)的 Q4_K_M GGUF。单句冒烟测试约 41 token/s,可独立运行在8084端口:
llama-server\-m/path/to/qwen3-8b-distill-50k-q4km.gguf\--aliasguofeng-zh-en-distill50\-c4096-nglall--jinja--port8084这里要把训练评测和部署评测分开看。后文 200 条 A/B、BLEU 与 chrF++ 的对象是 BF16 基座挂载 LoRA;量化后的 GGUF 最初只做了加载和单句冒烟,后来又用同一个 Q4_K_M 成品跑完 6 章、407 段的长文全链路。它证明了量化模型能在 llama.cpp 下持续完成翻译、术语处理、回译、SSE 和产物生成,也帮我找出了质量门禁的问题。不过,两次评测的输入不同,也没有对 BF16 与 Q4 做逐条配对,不能据此认定二者质量完全相同。
6. 遇到的问题与解决方案
6.1 128GB 内存不等于模型可以全部常驻
最初我想把 Step-3.7-Flash、Qwen、Nemotron 和向量服务全部启动,随时响应不同智能体。实际一跑,长上下文和高并发负载开始争抢内存,现场演示反而更不稳定。最后只能把环境拆成“建库/复核档”和“翻译量产档”,GPU 使用登记在SPARK-LOCK.md。训练、整本夜跑和演示各占明确时段,“一键全开”也不再是目标。
6.2 整页视觉模型无法稳定对应气泡
日文竖排页会漏假名,也会把多列文字拼错顺序。可如果直接把整页交给多模态模型,返回文字又很难与本地气泡逐一对应。我最后保留了两步:先做确定性的区域检测,再把整页图、bubble_id和坐标交给 Stepfun,要求它按 ID 返回内容、阅读顺序和说话人线索。API 不可用时,日文回退到 manga-ocr,并按从上到下、同行从右到左重新排序。
6.3 翻译后存在12 个空泡
46 页漫画第一次跑完后有 12 个空泡。我以为译文太长,于是打开缩句救援重跑。空泡仍是 12 个,缩句成功次数为 0。核对预算后才发现,10 个气泡的译文字数压根没有超限;另外 2 个预算过小,系统为了不把拟声词压坏,拒绝了自动缩句。模型服务没坏,触发条件从一开始就没有成立。
问题出在 CJK 换行器。旧逻辑为了避免省略号和破折号落在行首,会把标点硬塞回上一行;紧接着,宽度校验又把这条超宽行判为不可排。排版器选择安全失败,没有把文字画出气泡,于是出现了“剧本有译文、成品没落字”的空泡。
修复后的规则是:上一行放不下禁则标点,就把末字和标点一起挪到下一行;如果窄到连一个字加标点都放不下,允许标点单独占行。...和...也只在显示层统一成中文……。简化后的核心判断如下:
carry=cur[-1]+chifchinNO_LINE_STARTandlen(cur)>1andfits(carry,max_w):lines.append(cur[:-1])cur=carryelse:lines.append(cur)cur=ch随后,我把这 12 个真实气泡固化成回归样本。复用原译文和消字图重新排版,空泡从 12 个降到 0。以后再遇到类似故障,我会先查规则和数据,最后才考虑模型救援。
6.4 训练 loss 下降,译文却变差了
第一阶段,我用众包参考译文训练 Qwen3-8B LoRA。训练 loss 一直下降,可在同一套第三方 A/B 流程里,三个版本仍比基础模型低约 27 至 30 个百分点。过滤低分样本、清理异常符号也没能扭转结果。抽样复核后,问题变得很直接:参考译文本身经常还不如基础模型。loss 只能说明学生正在拟合目标,无法说明这个目标值得学。
后来我先给教师模型做门测试,再让本地 Qwen 35B 生成蒸馏目标。1 万条版本相对基础 8B 为 86 胜、72 负、42 平,净偏好+7pt;5 万条版本为 93 胜、68 负、39 平,净偏好+12.5pt。50k 与 10k 直接比较时是 75 胜、54 负、71 平,净偏好+10.5pt。结果终于从持续负向转为正向,CJK 泄漏也从基础模型的 9/200 降到 0/200。
这个实验没能把所有因果因素拆开。蒸馏方案同时更换了目标来源、清洗方式和数据分布;从 10k 扩到 50k 时,样本量增加五倍,一个 epoch 的优化步数也从 623 增到 3,113。目前只能说,在这套中译英网文数据和训练配方下,“35B 教师目标 + 清洗 + LoRA”比众包参考训练给出了更可靠的正向信号,50k 也强于 10k。至于提升分别有多少来自数据量、更新步数或某条清洗规则,现有实验还回答不了。
6.5 单句评测通过后,407 段 E2E 仍测出了什么
离线 A/B 能判断输出偏好,却看不到生产链是否安全。所以我用最终的 Q4_K_M 模型,把上传、章节解析、术语抽取、翻译、文化适配、回译审校、SSE、产物 API 和真实前端完整跑了一遍。输入由一本留出作品的 398 个连续正文段落,以及 9 个带跨章专名和占位符的原创段落组成,解析后共 6 章、407 段。
首次运行没有崩溃,407/407 段也都有输出,但这些输出离“可交付”还有一段距离:
| E2E 发现 | 为什么原有检查没拦住 | 解决办法 | 同数据最终复测 |
|---|---|---|---|
“10级学徒?”原样回显中文,却以相似度 1.0 进入final | 回译能复述中文原文,“语义相似”不等于“已经翻译” | 在回译前增加目标文字系统与原文照抄检查;整段重试仍失败时,只翻译残留片段并按字符边界回填,同时保护{player}和 HTML 标签 | CJK 残留段落1 → 0,3 次文字系统违例均自动修复 |
4 个短段被相邻上下文覆盖,1 个输出泄漏【Next Reference】 | 短标题和对白信息少,参考段落反而占据生成注意力 | 用<reference_context translate="false">隔离参考;检测内部标记和相邻原文复述,命中后无上下文重试 | 已知串段4 → 0,上下文标记泄漏1 → 0 |
黎明 => Dawn等误识别候选被当成强制术语,制造生硬译文和失败重试 | 自动候选与人工确认没有权限边界 | 只有confirmed术语进入 glossary 和确定性失败门槛,candidate只进入审核队列 | 候选术语强制违例1 → 0 |
术语命中率 0.9975 仍掩盖Xilan/Ceylan等跨章变体 | 后续章节发现的术语没有回填前文,指标只统计已有 mention | 项目术语汇总后对全书倒排扫描并补写 mention | mentions178 → 202;但别名归并仍待人工确认 |
23 段处于reviewing,项目却显示completed | “流水线处理结束”和“允许发布”共用一个状态 | 只要仍有待审段落,任务终态改为warning,书架和工作台显示“待校对” | 最终为395 final / 12 reviewing,项目终态为warning |
| 前端加载 254/407 的快照后,被旧 SSE 事件回退到 32% | 历史事件无条件覆盖了更新的服务端快照 | 进度百分比和已处理计数按单调递增方式合并 | 最终 82 条 SSE 序号严格递增,单调合并回归测试通过 |
文化适配把fat merchant改成wealthy merchant,改变人物外貌 | 自动“礼貌化”被错误当作文化适配 | 默认切换为 advisory,只把建议和理由写入 ledger;显式开启auto才覆盖正文 | 14 条建议均未自动改写正文 |
最终复测仍得到 407/407 段完整译文,平均回译相似度从 0.8949 升到 0.9012。在 398 个有参考的段落上,BLEU 从 19.94 升到 20.37,chrF++ 从 43.23 升到 43.76。占位符{player}、<color=gold>和</color>全部保留。自动分数只涨了一点,但已知的 CJK 残留、提示标记和明显串段不再悄悄混进最终产物,这个变化更实用。
安全恢复不是免费的。自动恢复调用从 4 次增加到 21 次,吞吐从 28.9 降到 26.31 段/分钟,下降 8.96%。这一轮我接受了约 9% 的吞吐损失,因为错误被看见、还能自动恢复,比表面上的速度更重要。
复测后还有 12 个reviewing,都来自低于 0.75 的回译相似度。其中包括“晦气!”、嘭嘭嘭!这类合理短译,说明固定阈值会误报短句。16 个新术语也都停留在 candidate,虽然没有污染正文,但Metatlin/Metatrin等专名在人工确认和别名归并前不会自动统一。因此,目前的准确定位是“带人工校对的内部 Beta”。
6.6 前后端都在运行,为什么页面还是空白或一直“正在读取”
录制投稿视频前,我在远程服务器上启动了 FastAPI 和 Vite。两个终端都没报错,浏览器标签页也显示着 Babel Studio,正文区域却始终一片空白。试过localhost、服务器局域网地址和 Tailscale 地址,情况都没变。第一反应很容易是 React 挂了,但标题能显示,说明index.html已经到达浏览器。接下来该确认的是前端模块有没有加载,以及浏览器连到的究竟是不是刚启动的服务。
我按下面的顺序排查:
- 先看服务是否真的监听端口。我检查了 Vite 和 FastAPI 进程,并在服务器内部请求前端地址、
/health、/projects、章节、术语和工件接口。API 都能返回数据,生产构建也能通过,问题不在翻译任务或后端存储。 - 然后核对浏览器端口。Docker 旧页面占着
5173,新启动的 Vite 自动换到5174。我一直访问5173,看到的其实是旧容器。服务器的 HTTP 代理还会让普通curl localhost超时,所以本机探测改用curl --noproxy '*',免得把代理问题错认成服务没启动。 - 再检查地址和网络边界。用户电脑里的
127.0.0.1指向用户电脑自己;服务器的192.168.*地址只在服务器所在局域网有效。当时 Tailscale 也没有让两端加入同一个网络。这些地址都代替不了 VS Code Remote 的端口隧道,外部转发域名还可能被 Vite 的allowedHosts拒绝。反复换网址没有用。 - 最后打开 VS Code 的“端口”面板。
5174当时显示为“自动转发”,这条记录却没有把当前浏览器会话稳定接到远程 Vite。我删掉记录,点击“添加端口”,手动输入5174,状态随即变成“用户转发”。从面板点开localhost:5174后,VS Code 通过新隧道在本地浏览器打开页面,React 正常挂载,空白页消失。
页面总算出来了,书架却又一直停在“正在读取”。这次不是 React,也不是 FastAPI:前端客户端把 API 地址写死成了http://localhost:8051。网页虽然运行在远程服务器上,实际打开它的却是本地浏览器,因此这里的localhost指向我的电脑,而不是 DGX Spark。Vite 当时又没有配置/api代理,请求自然到不了服务器里的 FastAPI,界面还会一直等下去。
最后的改法很小。vite.config.ts把同源/api转发到服务器内部的http://127.0.0.1:8051,src/api/client.ts和.env.example的默认地址都改成/api/v1。这样浏览器只访问当前网页的来源,跨到8051的那一步由远程 Vite 完成,不再让本地浏览器猜“localhost 到底是谁”。我还给 API 请求加了 15 秒超时;后端或代理没启动时,页面会直接报连接超时,不会永远停在“正在读取”。
排查期间,Vite 还因为云端机器的 inotify watcher 达到上限而报过ENOSPC,不过它和空白页是两件事。生产构建正常,说明源码仍能编译。录制环境后来改用无 HMR 的生产构建和 preview,避免 watcher 再来添乱。稳定录制时由5175提供生产页面,同源/api转发到服务器内部的8051,VS Code 只需建立一个明确的用户转发端口。
以后再碰到这种问题,我会先看监听和服务器内直连,再检查 HTML、前端模块与 API,接着核对实际端口、请求地址和代理,最后重建远程端口转发。如果服务器内访问正常、浏览器侧异常,就先查浏览器到远程主机的交付路径,尤其留意浏览器眼里的localhost,别急着改业务代码。
7. 实验结果:翻译、长文本、文化适配与嵌字
7.1 总体结果
Babel Studio 目前已经跑通小说和漫画两条主链路。漫画侧用 46 页 OpenMantra 日漫做了整本验证;小说侧覆盖 TXT、Markdown、DOCX 和 EPUB,作品级术语、翻译、回译审校与质量回流也都进入了实际链路。下面这些结果可以复现:
| 验证项 | 调整前 | 调整后或当前结果 |
|---|---|---|
| 漫画空白气泡 | 12 个 | 0 个 |
| CJK 排版回归 | 缺少真实空泡样本 | 12 个问题气泡进入回归,相关后端测试 115 项通过 |
| Qwen 并发推理 | 测试 8、16、32 路 | 选择 16 路,聚合吞吐约 235 token/s |
| 众包参考 LoRA | 相比基础模型低约 27–30pt | 停止沿低质量目标继续调参,改为先验证教师与数据 |
| distill-10k 8B 学生 | 旧 LoRA 持续负向 | 86 胜、72 负、42 平,净偏好 +7pt |
| distill-50k 8B 学生 | 10k 版净偏好 +7pt | 对基础 8B 为 93 胜、68 负、39 平,净偏好 +12.5pt;对 10k 为 75 胜、54 负、71 平 |
| 学生模型可部署性 | 8B BF16 与 LoRA 分离 | 合并并量化为 4.68 GiB(5.03 GB)Q4_K_M,冒烟约 41 token/s |
| Q4_K_M 长文 E2E | 单句冒烟,缺少流水线证据 | 6 章 407 段全部完成;CJK 残留、上下文标记和已知串段均为 0 |
| 人工发布闸门 | 23 个待审段仍显示 completed | 最终 395 final / 12 reviewing,任务以 warning / 待校对结束 |
7.2 多个模型的日译中对比
日译中测试包含 31 个真实漫画气泡。下面几组例子来自同一份 OCR 输入。“通用 Qwen 35B”当时还沿用一套不适合日译中的旧 Prompt,所以这组结果只能说明路由需要细分,不能证明通用模型永远更差。
| 日文原文 | 通用 Qwen 35B | Qwen3-8B 零样本 | Sakura-14B 专用档 | 观察 |
|---|---|---|---|---|
| 大体なんで使い捨てカメラなんだよ! | 因为是大体,所以是 disposable camera 啊! | 大概你是在用一次性相机啊! | 再说为什么是用抛弃式相机啊! | Sakura 正确识别“大体”是转折语气,也没有英文残留 |
| 女か? | 女か? | 是女生吗? | 是女人吗? | 旧通用路由整泡漏译,后两者都可接受 |
| お前もしかして | 你该不会 | — | 你该不会是—— | Sakura 保留漫画对白的悬停语气 |
| マフィア舐めんのも大概にしやがれ | 别他妈把黑手党当软柿子捏。 | — | 别太小看黑手党了。 | 通用模型语气更痞,Sakura 更忠实但偏平,需要文化适配按作品分级裁决 |
逐泡人工判读时,旧通用 Qwen 路线出现 1 处整泡漏译,以及 1 处误解并残留英文;Sakura 没有这两类硬伤。独立 DeepSeek A/B 盲评的方向一致:31 个漫画气泡中,Sakura 16 胜、6 负、9 平,净偏好+32.2pt。样本不大,但足以支持“日译中优先走专用模型”这项路由决定。它还不足以代表所有漫画题材,而且在某些强烈语气下,通用模型的表达反而更生动,风格取舍不能只看总分。
7.3 Dense 蒸馏前后的中译英对比
蒸馏评测使用 200 条固定样本,来自 15 本以整本书为单位、与微调训练集隔离的留出作品。基础模型和学生使用相同 Prompt,关闭 thinking,采用 greedy 解码。A/B 展示位置按固定种子打乱,DeepSeek-chat 只看到中文原文和两份匿名英文译文,再按忠实度与英文自然度判断胜、负或平。这样能控制微调阶段的书籍泄漏和位置偏差,但无法排除基础模型、教师或裁判在预训练阶段见过相关文本。
| 指标 | 基础 Qwen3-8B | distill-10k | distill-50k | 如何解读 |
|---|---|---|---|---|
| 对基础模型 A/B | — | 86 胜 / 72 负 / 42 平 | 93 胜 / 68 负 / 39 平 | 两个蒸馏版都呈正向偏好,50k 信号更强 |
| 净偏好 | — | +7.0pt | +12.5pt | (胜−负)/200,不是“质量提高百分比” |
| 非平局胜率 | — | 54.4% | 57.8% | 只在裁判明确分出胜负的样本中计算 |
| 双侧精确符号检验 | — | p=0.301 | p=0.058 | 忽略平局;50k 接近但未达到常用的p<0.05阈值 |
| BLEU | 16.81 | 17.58 | 17.97 | 参考译文质量有限,只作为方向性辅证 |
| chrF++ | 40.24 | 41.23 | 41.84 | 与盲评方向一致,但差值不大 |
| CJK 泄漏 | 9/200 | 1/200 | 0/200 | 蒸馏配方对未翻译汉字这一硬错误改善明显 |
| 空输出 | 0/200 | 0/200 | 0/200 | 三者持平 |
50k 与 10k 直接 A/B 的结果为 75 胜、54 负、71 平,非平局胜率 58.1%,双侧精确符号检验p=0.078。所以+10.5pt只能描述成扩大数据后出现了进一步改善的信号,尚不能排除抽样波动。不过,50k 在盲评、BLEU、chrF++ 和 CJK 泄漏四项观察上的方向一致,结果也不是靠少数漂亮案例撑起来的。
下面是能直接看到差异的样例:
| 中文原文 | 基础 Qwen3-8B | distill-50k 8B 学生(BF16+LoRA) | 改善点 |
|---|---|---|---|
#<#“混账! | #<#“混账! | Damn it! | 不再整句回吐中文,并清理语料伪影 |
| 比起只开出过精力药剂的初级宝箱,中级宝箱的效果持久而爽。 | Compared to the初级 treasure chest... the中级 treasure chest... | Compared to the basic treasure chest... the intermediate treasure chest... | 消除中英文混杂,术语完整翻译 |
| 是周美琳接的,语气是裴七七没有听过的温柔, | ...a gentleness裴 QiQi had never heard before. | ...a gentleness that Pei Qiqi had never heard before. | 消除汉字泄漏,保留人物拼写 |
这些样例表明,学生学会了一部分“完整翻译、不要回吐训练伪影”的行为。但它们毕竟是从 200 条结果中挑出的可解释案例,不能代替总体统计。现有证据支持的范围是:这套蒸馏配方在留出的中译英网文样本上减少了硬错误,并得到中等强度的正向偏好信号。新闻、技术文档、日译中和其他文学题材是否也会改善,还没有数据。
部署侧还有一条边界:+12.5pt来自 BF16 基座挂载 LoRA,不是最终的 Q4_K_M 文件。量化版已经完成另一套 407 段长文 E2E,持续生成和工程链路都能稳定运行;可长文输入与 200 条盲评不是同一测试集。在 llama.cpp 上重跑同一批 200 条、并和 BF16 输出逐条配对之前,+12.5pt不能直接算在量化成品头上。
当前我把它定位为“可部署候选”,还不是 35B 教师的等价替代。它可以在资源受限的中译英网文链路中承担批量初译,尤其适合减少汉字漏译这类硬错误。碰到文化词、人物口吻或高价值交付,仍然要由 35B 教师复核并配合人工校对。
7.4 长文本效果与长程一致性的边界
只测两三个字的漫画气泡显然不够,我又加入 75 句公版长文本,来自太宰治《走れメロス》和芥川龙之介《蜘蛛の糸》,每句约 15–40 个日文字符。DeepSeek A/B 中,Sakura 取得 50 胜、18 负、7 平,净胜 Qwen3-8B+42.7pt,比漫画短句组的+32.2pt更强。COMETKiwi 在两组上只给出千分位差异,分不出系统好坏,所以没有拿这个单一自动分数作裁判。
不过,句子长一点仍不等于跨章节一致。当前留出测试覆盖多本书和不同章节,可以看到Pei Qiqi等人物名在分散样本中的拼写比较稳定;生产流水线也已有作品级 SQLite 术语库、命中召回、定译检查和跨章节复用。但我还没有完成一本小说从第一章到最后一章的人工一致率标注,所以这里不给出一个凑出来的百分比。后续会分别统计人物名、称谓和世界观术语的首次出现、跨章复现与冲突次数。
7.5 文化适配:有改善,也有尚未迁移的能力
文化适配不等于“把脏话写得更重”。同一句黑帮威胁,通用 Qwen 的“别他妈把黑手党当软柿子捏”更有街头感,Sakura 的“别太小看黑手党了”更忠实,也更适合低年龄分级。Nemotron 因此只做最小改写,并把理由写进 ledger;改写完成后还会重新检查术语,一旦破坏专名就丢弃结果。
蒸馏实验也暴露了文化知识迁移不足。面对“这个认真的男人竟然还如此旺财”,基础 8B 和 distill-50k 都把“旺财”压成lucky,35B 教师给出的auspicious for wealth更接近“招财旺运”。Dense 学生已经明显减少漏译和汉字泄漏,但处理文化词时仍追不上 MoE 教师。目前文化适配有真实案例和保护机制,还缺一套独立、分级、由人类评分的专项测试集。以后需要分别评估忠实度、目标文化自然度、人物口吻和内容分级,不能全塞进一个语义相似度里。
8. 总结与未来计划
8.1 这次做成了什么
Babel Studio 把确定性程序和生成式模型接在了一起。术语库防止模型随意改名,检测框约束视觉模型的猜测范围,排版器在越界时安全失败,ledger 记录每次决策。敏感内容留在 DGX Spark,公开漫画可以借助 Stepfun;云端服务缺席时,还有本地回退。
开发中最耽误时间的,往往不是出错,而是先入为主地猜错原因。12 个空泡和译文长度无关,LoRA 退化也不是因为训练时间不够。没有气泡预算、事件记录、固定测试集和盲评,我可能还在沿着两个错误方向继续优化。Dense 蒸馏则把强教师的一部分行为压进约 5.03GB 的学生模型。+12.5pt净偏好、自动指标和硬错误统计给出了初步的正向证据,但学生没有学全教师处理文化词的能力,样本规模和单一模型裁判也限制了结论强度。
407 段 E2E 证明量化模型可以长时间稳定运行,也提醒我模型质量和交付安全是两回事。目标文字系统检查、上下文污染恢复、术语状态权限、全书回填和人工发布闸门,让已知错误不再静默进入成品。最终仍有 12 段等待人工校对。系统没有把它们藏进completed,而是如实显示为待审。
8.2 仍然存在的边界
不规则艺术字、跨格 SFX 和严重受损的复杂背景仍要人工处理,多模态复检也代替不了最终校对。书架式作品管理与逐气泡编辑器已经有设计和接口基础,但进入多人生产环境前,还要补上权限控制、版本冲突、批量编辑和人工审核闭环。
评测也有欠账。长程术语一致性和文化适配需要正式标注集,不能只靠短句盲评和个别案例外推。长文 E2E 仍有短句回译阈值误报与实体别名归并问题。蒸馏侧还缺更大规模的人类双盲、多裁判、多随机种子复测,以及 BF16+LoRA 与 Q4_K_M 在同一批样本上的等价性评估。
8.3 下一步
下一步先完成书架前端和逐气泡编辑,把needs_human从一个状态变成可以逐项处理的审核队列。短句将采用长度感知或分类阈值;人物别名、源文错别字和人工确认也要合并到同一实体。漫画视觉回归集会继续扩充,OCR、排版和复检误差分开统计。小说侧则要建立整本、跨章节的术语与人物口吻测试。
蒸馏实验先不急着继续堆数据。Q4_K_M 要在与 BF16 相同的 200 条上重跑,然后扩大样本,引入人工与多个裁判复评。只有确认 50k 的正向信号可以重复,才会继续观察 10 万条的边际收益,并尝试把最难的文化样本单独交给强教师生成。
我不打算把 Babel Studio 包装成“全自动翻译机”。它更像一间本地化工作室:做过什么都有记录,失败时留下证据,拿不准的地方就交给人。