分治哲学:一个 LLM 翻译系统的五层分治架构
分治哲学:一个 LLM 翻译系统的五层分治架构
"分治"(Divide and Conquer)是算法领域的经典思想——把大问题拆成小问题,逐个解决,再合并结果。但在 LLM 翻译系统的工程实践中,分治的含义远不止"拆分文本"。
本文以"O小译"系统为案例,从数据分治、流程分治、能力分治、状态分治、容错分治五个层次,剖析分治原则如何贯穿整个系统架构。
一、总论:为什么翻译系统必须用分治?
一个直觉性的问题:为什么不直接把整篇文章丢给 LLM,让它一步到位翻译出来?
三个根本约束决定了分治的必要性:
| 约束 | 说明 |
|---|---|
| 上下文窗口限制 | 即使 128K 的模型,处理 5 万字文档也力不从心,且越长质量越差("Lost in the Middle" 效应) |
| 单一 Prompt 的能力上限 | 让一个 Prompt 同时做好术语统一、准确翻译、自然润色,等于让一个人同时当术语专家、翻译和编辑 |
| 错误爆炸半径 | 单步翻译中一个 token 的错误可能影响整段输出;分治后,一个 Chunk 的失败不影响其他 Chunk |
分治的本质是降低单次 LLM 调用的认知复杂度,让模型在每一步都只专注做好一件事。
二、五层分治架构
┌─────────────────────────────────────────────────────────────────┐
│ 分治架构五层模型 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 第 1 层:数据分治 ── 长文本切分为 Chunk │
│ ↓ │
│ 第 2 层:流程分治 ── 翻译拆为 4 个职责单一的步骤 │
│ ↓ │
│ 第 3 层:能力分治 ── 不同步骤路由到不同模型 │
│ ↓ │
│ 第 4 层:状态分治 ── 全局状态 vs Chunk 局部状态 │
│ ↓ │
│ 第 5 层:容错分治 ── 每层独立兜底,错误不扩散 │
│ │
└─────────────────────────────────────────────────────────────────┘
三、第 1 层:数据分治 —— 文本切分
核心问题
一篇 5000 字的技术文档,如何切分成 LLM 能高质量处理的片段?
三级切分策略
原文(5000 字)
│
▼ 第一级:段落边界(\n\n)切分
│
├── 段落 A(200 字)──→ 合并入 Chunk 1
├── 段落 B(300 字)──→ 合并入 Chunk 1
├── 段落 C(900 字)──→ 超过 800 token 上限
│ │
│ ▼ 第二级:句子边界(。!?)切分
│ │
│ ├── 句子组 1 ──→ Chunk 2
│ └── 句子组 2 ──→ Chunk 3
│
├── 段落 D(400 字)──→ Chunk 4
└── ...
▼ 第三级:重叠窗口(50 token)
│
└── Chunk 1 末尾句 → 注入 Chunk 2 开头(保证语义连贯)
Token 估算——分治的度量衡
def estimate_tokens(text: str) -> int:
chinese_chars = sum(1 for c in text if '\u4e00' <= c <= '\u9fff')
english_chars = sum(1 for c in text if c.isascii() and c.isalnum())
return int(chinese_chars * 1.5 + english_chars * 0.25)
为什么中文 × 1.5、英文 × 0.25? 这不是精确的 token 计数,而是一个分治决策函数——它只需要回答一个问题:"这段文本是否需要进一步切分?"。精确计数需要依赖具体模型的 tokenizer,而我们的估算函数是模型无关的,可以在切分阶段独立运行。
分治的粒度选择:800 token
这个值是在两个目标之间做权衡:
- 太小(如 200 token):Chunk 数过多,上下文丢失严重,聚合时拼接痕迹明显
- 太大(如 4000 token):接近模型窗口上限,LLM 输出质量下降,且单个 Chunk 失败代价大
800 token 约等于一篇中等长度的段落,既保证了单 Chunk 内有足够上下文,又不会因为太长导致"尾部质量衰减"。
四、第 2 层:流程分治 —— 四步翻译法
从"一步到位"到"四步流水线"
传统的 LLM 翻译是一个 Prompt 搞定:
Prompt: "请将以下英文翻译为中文:..." → 输出
这种方式的问题:一个 Prompt 承载了太多相互矛盾的要求。
- 要求准确 → 模型倾向保守,译文生硬
- 要求自然 → 模型倾向自由,可能偏离原文
- 要求术语统一 → 模型可能"忘记"术语表中的某些条目
分治:让每个步骤只做一件事
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Step 1 │ │ Step 2 │ │ Step 3 │ │ Step 4 │
│ 术语识别 │ → │ 直译 │ → │ 自审 │ → │ 意译定稿 │
│ │ │ │ │ │ │ │
│ 角色: 顾问 │ │ 角色: 翻译 │ │ 角色: 审校 │ │ 角色: 编辑 │
│ 温度: 0.2 │ │ 温度: 0.3 │ │ 温度: 0.2 │ │ 温度: 0.5 │
│ 目标: 准确 │ │ 目标: 忠实 │ │ 目标: 严谨 │ │ 目标: 自然 │
└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
每个步骤的角色定位是互斥的:
- 术语识别不需要文采,只需要"这个领域里这个词怎么翻译"的知识
- 直译不需要自然,只需要"一字不差地映射"
- 自审不需要翻译能力,只需要批判性的审阅能力
- 意译不需要忠实,只需要在准确的基础上让文字"活"起来
这就是分治的核心:将矛盾的目标分解到不同步骤,每步只优化一个目标。
流程分治带来的另一个好处:可观测性
四步拆分后,我们可以精确定位问题:
- 术语翻译错了?→ Step 1 的 Prompt 有问题
- 信息遗漏了?→ Step 2 的直译不够忠实
- 表达生硬?→ Step 4 的意译 temperature 不够
如果是一步到位,你只能笼统地说"翻译质量不好",无法定向优化。
五、第 3 层:能力分治 —— 多模型路由
核心洞察
不同 LLM 模型的"认知偏好"不同:
DeepSeek → 逻辑推理强,适合技术文本和精确翻译
百炼 → 中文领域知识广,适合术语识别
Claude → 长文本表达自然,适合文学润色
OpenAI → 通用能力强,适合做兜底
路由矩阵:能力与需求的匹配
terminology literal review polish
(需要知识) (需要忠实) (需要严谨) (需要文采)
─────────────────────────────────────────────────
tech │ 百炼 │ DeepSeek │ DeepSeek │ DeepSeek
legal │ 百炼 │ 百炼 │ DeepSeek │ Claude
medical │ 百炼 │ 百炼 │ DeepSeek │ 百炼
literature│ DeepSeek │ DeepSeek │ Claude │ Claude
general │ DeepSeek │ DeepSeek │ DeepSeek │ DeepSeek
每一格的选择逻辑都是一个分治决策:把这个步骤交给最擅长这个维度的模型。
分治 vs 集成
一个替代方案是集成(Ensemble):每个步骤都调用多个模型,取投票/融合结果。但这会导致:
- 成本 × N(N = 模型数)
- 延迟 × N
- 融合逻辑本身也可能引入错误
分治选择的是精准匹配而非暴力集成——用路由表把每个步骤分配给最优模型,而不是让所有模型做所有事。
六、第 4 层:状态分治 —— 全局 vs 局部
双层状态结构
class TranslateState(TypedDict):
"""全局状态 —— 所有 Chunk 共享"""
session_id: str
global_terminology: Dict # 全局术语表
source_text: str # 完整原文
final_result: str # 最终译文
...
class ChunkState(TypedDict):
"""局部状态 —— 每个 Chunk 独立"""
chunk_id: str
source_text: str # Chunk 原文
terminology: Dict # 局部术语表(全局 + Chunk 特有)
literal_translation: str # Chunk 直译
self_review: Dict # Chunk 审校报告
final_translation: str # Chunk 定稿
status: str # Chunk 独立的状态机
retry_count: int
为什么需要状态分治?
问题1:全局术语表 vs 局部术语表
一篇关于"机器学习"的文档中:
- "gradient descent" 在全文中统一翻译为"梯度下降" → 全局术语
- "learning rate" 在前半部分讨论的是"学习率",后半部分在元学习语境下可能指"元学习率" → 局部术语
状态分治允许每个 Chunk 在全局术语表基础上覆盖局部术语,避免全局一刀切。
问题2:Chunk 独立状态机
每个 Chunk 有自己的 status 字段(pending → running → translated → reviewed → polished),这意味着不同 Chunk 可以处于不同阶段:
Chunk 1: [polished] ✅
Chunk 2: [reviewed] → 等待意译
Chunk 3: [running] → 正在直译
Chunk 4: [pending] → 等待处理
这种异步进度使得并发处理成为可能——不需要等所有 Chunk 都完成直译才开始审校。
问题3:审校回退的局部化
当自审发现 Chunk 2 需要修订时,只有 Chunk 2 的 status 被重置为 pending 并回退到直译——其他 Chunk 不受影响(虽然在当前实现中是整体回退,但状态结构已经为更细粒度的回退做好了准备)。
七、第 5 层:容错分治 —— 每层独立兜底
设计原则:错误不扩散
┌──────────────────────────────────────────────────────────────┐
│ │
│ 术语识别失败 ──→ 使用空术语表 ──→ 直译靠模型自身能力 │
│ │ │
│ └──→ 不影响后续步骤 │
│ │
│ 直译失败 ──→ 使用原文作为直译结果 ──→ 自审发现质量差 │
│ │ │ │
│ └──→ 触发修订循环 └──→ 最多重试2次 │
│ │
│ 自审 JSON 解析失败 ──→ 给默认低分 ──→ 触发修订 │
│ │ │
│ └──→ 修订后仍然可能解析失败 ──→ 强制进入意译 │
│ │
│ 意译失败 ──→ 使用直译结果作为最终译文 │
│ │ │
│ └──→ 质量降级但不丢失 │
│ │
│ 聚合时 Chunk 状态异常 ──→ 使用原文兜底 │
│ │
└──────────────────────────────────────────────────────────────┘
兜底链的层级
理想结果 > 修订后结果 > 直译结果 > 原文
每一层兜底都是一个"降级但不崩溃"的策略。核心思想是:
用户看到一篇有瑕疵的译文,远好于看到一个 500 错误页面。
容错分治的数学模型
假设每步的失败概率为 p,共 n 步,且各步独立:
- 无兜底:系统成功率 = (1-p)^n。当 p=0.05, n=4 时,成功率 = 81%
- 有兜底:每步兜底成功率为 1.0(因为兜底总是能产出结果),系统成功率 = 100%
- 但输出质量的期望会下降:质量 = Σ(每步正常完成的概率 × 正常质量 + 兜底概率 × 兜底质量)
这就是容错分治的精髓:用质量的梯度换取可用性的保障。
八、分治的代价与权衡
分治不是免费的午餐,它带来了三个工程代价:
代价 1:上下文断裂
切分后的 Chunk 之间丢失了上下文关联。比如:
- 前一段说"如前所述",但"前"在另一个 Chunk 里
- 代词指代可能跨 Chunk
缓解方案:重叠窗口(50 token)+ 全局术语表 + 聚合时术语一致性校验
代价 2:延迟增加(可优化)
串行四步的总延迟 = 术语 + 直译 + 自审 + 意译,比一步到位慢 3~4 倍。
缓解方案:
- Chunk 级并发(asyncio.gather):5 个 Chunk 并行处理,单步延迟不变
- 条件边回退只在需要时触发,大多数时候 2 轮就通过
代价 3:系统复杂度
四步流水线 + 条件回退 + 多模型路由 + 双层状态,代码量和调试难度显著增加。
缓解方案:
- LangGraph 的声明式图描述降低了编排复杂度
- 每步的 Prompt 是独立的,可以单独测试和迭代
- Prometheus 指标提供全链路可观测性
九、分治模式的泛化
这套五层分治架构不仅适用于翻译系统。将它泛化,可以应用到所有需要多步 LLM 协作的场景:
| 场景 | 数据分治 | 流程分治 | 能力分治 | 状态分治 | 容错分治 |
|---|---|---|---|---|---|
| RAG 问答 | 文档切块 | 检索→重排→生成 | 向量模型+LLM | 全局知识库+局部上下文 | 检索失败→直接生成 |
| 代码生成 | 按函数拆分 | 分析→设计→编码→测试 | 架构模型+编码模型 | 全局接口+局部实现 | 编译失败→回退修正 |
| 数据分析 | 按表/列拆分 | 理解→查询→可视化→解读 | SQL模型+图表模型 | 全局Schema+局部结果 | 查询失败→简化SQL |
| Agent 编排 | 子任务拆分 | 规划→执行→验证→总结 | 规划模型+执行模型 | 全局计划+局部执行状态 | 执行失败→重新规划 |
分治的本质是不变的:将复杂的认知任务分解为多个简单子任务,每个子任务用最合适的工具独立完成,再通过状态和流程编排合并为最终结果。
十、总结
回到最初的问题:为什么不直接让 LLM 一步翻译?
因为好的翻译是一个分治问题,不是一个模式匹配问题。它需要:
- 数据分治:把长文拆成可管理的片段
- 流程分治:把翻译拆成职责单一的步骤
- 能力分治:把步骤分配给最擅长的模型
- 状态分治:让全局一致性和局部灵活性共存
- 容错分治:让每一步都有独立的降级路径
这五层分治不是独立存在的,它们层层嵌套、相互支撑,共同构成了一个可观测、可恢复、可扩展的 LLM 翻译系统。
分治不仅是一种架构策略,更是一种工程哲学:承认复杂性,然后系统性地化解它。
浙公网安备 33010602011771号