分治哲学:一个 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 一步翻译?

因为好的翻译是一个分治问题,不是一个模式匹配问题。它需要:

  1. 数据分治:把长文拆成可管理的片段
  2. 流程分治:把翻译拆成职责单一的步骤
  3. 能力分治:把步骤分配给最擅长的模型
  4. 状态分治:让全局一致性和局部灵活性共存
  5. 容错分治:让每一步都有独立的降级路径

这五层分治不是独立存在的,它们层层嵌套、相互支撑,共同构成了一个可观测、可恢复、可扩展的 LLM 翻译系统。

分治不仅是一种架构策略,更是一种工程哲学:承认复杂性,然后系统性地化解它

posted @ 2026-06-09 12:35  黄忠  阅读(32)  评论(0)    收藏  举报