feat: Release v0.10 - Modular Architecture & External Config

- Refactor codebase into src/ (preprocessing, translation, assembly)
- Add pipeline/ scripts for individual stages
- Externalize configuration to config/config.yaml
- Fix Cover Image preservation
- Update documentation and manuals
This commit is contained in:
谭凯
2026-01-31 22:49:44 +08:00
parent 9ef82393be
commit 7a93c52b42
306 changed files with 30313 additions and 1071 deletions
@@ -0,0 +1,163 @@
# 纯中文模式格式丢失问题 - 深度分析
## 🔴 根本原因:Mode 参数未被正确传递
### 问题概述
当前 `translator.py``translate_epub()` 方法签名只有 3 个参数:
```python
async def translate_epub(self, epub_path: str, test_mode: bool = False, output_dir: str = None) -> str:
```
**缺失 `mode` 参数!** 这意味着:
1. 无论用户指定 `-m chinese` 还是 `-m bilingual`,都走相同的代码路径
2. 永远使用 `BilingualEPUBBuilder`
3. 格式提取功能 (`format_extractor.py`) 从未被调用
---
## 问题链路分析
### main.py 调用
```python
# main.py:55-59
await translator.translate_epub(
args.epub_path,
test_mode=args.test,
output_dir=args.output,
mode=args.mode # ❌ 此参数被忽略!因为 translate_epub 不接受 mode
)
```
### translator.py 未处理 mode
```python
# translator.py:39 - 缺少 mode 参数
async def translate_epub(self, epub_path: str, test_mode: bool = False, output_dir: str = None) -> str:
# translator.py:53 - 提取时未传 mode
self.text_processor.extract_to_manifest(item['content'], item['file_name'], manifest)
# 应该是: extract_to_manifest(..., mode=mode)
# translator.py:72 - 分块时未传 mode
chunks = self.text_processor.create_chunks_from_manifest(manifest)
# 应该是: create_chunks_from_manifest(manifest, mode=mode)
# translator.py:84 - 永远使用 BilingualBuilder
builder = BilingualEPUBBuilder(self.parser.book, self.config)
# 应根据 mode 选择 ChineseEPUBBuilder
```
---
## 完整的格式保留流程 (应有逻辑)
```
┌─────────────────────────────────────────────────────────────────┐
│ 1. 提取阶段 (format_extractor.py) │
│ Input: <p>This is <strong>bold</strong> text.</p> │
│ Output: │
│ - clean_text: "This is bold text." │
│ - text_with_placeholders: "This is φc00001φboldφc00002φ text." │
│ - placeholder_map: {c00001: "<strong>", c00002: "</strong>"} │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ 2. 翻译阶段 (llm_client.py) │
│ Prompt: p_00001 [BODY] This is φc00001φboldφc00002φ text. │
│ LLM Response: p_00001 这是φc00001φ粗体φc00002φ文本。 │
│ ⚠️ 问题:LLM 可能丢失/错放占位符! │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ 3. 还原阶段 (format_restorer.py) │
│ Input: "这是φc00001φ粗体φc00002φ文本。" │
│ Output: "这是<strong>粗体</strong>文本。" │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ 4. 构建阶段 (chinese_builder.py) │
│ 使用 translation_with_original_html 替换原始内容 │
└─────────────────────────────────────────────────────────────────┘
```
**当前状态**: 步骤 1、3、4 从未执行!
---
## LLM 占位符丢失的常见原因
即使修复了 mode 传递问题,LLM 仍可能丢失占位符:
| 原因 | 示例 | 解决方案 |
|------|------|----------|
| 占位符被"翻译" | φc00001φ → φ中00001φ | 强调 Prompt: "φ...φ 是代码,禁止修改" |
| 占位符位置错误 | 原: Aφc1φB → 译: φc1φAB | 修复 Agent (repair_format) |
| 占位符完全丢失 | 原: φc1φ → 译: (无) | 后处理:从原文恢复 |
| 幻觉占位符 | 原: (无) → 译: φc99φ | 忽略未知 ID |
---
## 修复建议
### 修复 1: translator.py 添加 mode 参数
```python
async def translate_epub(self, epub_path: str, test_mode: bool = False,
output_dir: str = None, mode: str = "bilingual") -> str:
# ...
# 提取时传递 mode
self.text_processor.extract_to_manifest(item['content'], item['file_name'], manifest, mode=mode)
# 分块时传递 mode
chunks = self.text_processor.create_chunks_from_manifest(manifest, mode=mode)
# 翻译时传递 mode
await self._translate_concurrently(chunks, manifest, profile, mode=mode)
# 构建时选择正确的 Builder
if mode == "chinese":
builder = ChineseEPUBBuilder(self.parser.book, self.config)
result_file = builder.create_chinese_epub_with_mapping(manifest.get_items(), output_path)
else:
builder = BilingualEPUBBuilder(self.parser.book, self.config)
result_file = builder.create_bilingual_epub_with_mapping(...)
```
### 修复 2: llm_client.py 添加占位符指令
```python
if mode == "chinese":
base_sys_prompt += """
Placeholder Instructions:
1. Text contains φcXXXXXφ placeholders representing HTML formatting.
2. These are CODE tokens - DO NOT translate, modify, or remove them.
3. Keep placeholders in corresponding positions in your translation.
4. Example: "This is φc00001φboldφc00002φ text.""这是φc00001φ粗体φc00002φ文本。"
"""
```
### 修复 3: 增强 format_restorer.py 容错
```python
def restore(self, text_with_placeholders, placeholder_map):
# 如果 LLM 完全丢失占位符,尝试智能合并
if not self.PLACEHOLDER_REGEX.search(text_with_placeholders):
logger.warning("所有占位符丢失,降级为纯文本")
return text_with_placeholders, False
# 现有逻辑...
```
---
## 优先级
| 优先级 | 修复项 | 工作量 |
|--------|--------|--------|
| P0 | translator.py 传递 mode 参数 | 小 |
| P0 | translator.py 选择正确 Builder | 小 |
| P1 | llm_client.py 占位符 Prompt | 小 |
| P2 | format_restorer.py 容错增强 | 中 |
| P2 | repair_format 机制完善 | 中 |