状态:已弃用(Deprecated) — 本文档记录该系统的完整设计与实现细节,作为技术归档保留。弃用原因见第七节。

一、项目概述

translating-articles 是一套基于 Claude Agent SDK 的长文分片翻译系统,用于将 Clippings/totranslate/ 中的外文文章翻译为中文,输出至 Clippings/translated/

系统解决的核心问题是:LLM 上下文窗口有限,无法在保证翻译质量的前提下一次性处理超长文本(如播客文字稿、长篇访谈)。方案是将原文按章节切分为多个片段,逐片翻译,通过术语表接力和重叠段落保持跨片一致性。

技术栈

  • Python 3.10+,async/await
  • claude_agent_sdkquery + ClaudeAgentOptions
  • PyYAML、pathlib、hashlib
  • 运行环境:henri_env conda 环境

文件结构

.claude/skills/translating-articles/
├── SKILL.md                  # Skill 定义(Agent 直接翻译模式的指令)
└── scripts/
    ├── translate.py           # 分片翻译主脚本
    └── add_speakers.py        # 说话人标注后处理脚本

tools/translate-chunks/{slug}/ # 运行时工作目录
├── meta.json                  # 元数据 + chunk manifest
├── glossary.json              # 累积术语表
├── chunk-01-translated.json   # 各片 checkpoint
├── chunk-02-translated.json
└── ...

二、架构设计

2.1 双模式架构

模式适用场景驱动方式上下文
Agent 直接翻译短文(<30KB)SKILL.md 指令,Agent 在会话中直接翻译共享 Agent 上下文
脚本分片翻译长文(>30KB),播客/访谈文字稿translate.py 独立进程每片独立 LLM 调用

Agent 模式由 SKILL.md 描述翻译原则、格式规范和处理流程,Agent 读取原文后直接在会话中完成翻译。该模式简单直接,但受限于 Agent 的上下文窗口。

脚本模式是本系统的核心,下文重点描述。

2.2 分片翻译的核心理念

片间隔离:每片翻译是一次独立的 claude_agent_sdk.query() 调用,拥有完整的系统提示和独立上下文,不存在上下文溢出的风险。

一致性保证:片间通过两个机制维持翻译一致性:

  1. 术语表接力:每片翻译的输出中包含 GLOSSARY: 段,脚本解析后注入下一片的 prompt。术语表滚雪球式积累,确保同一术语在全文中译法统一。
  2. 重叠段落:取前一片译文的末尾 3 段作为"衔接上下文"注入下一片 prompt,供模型参考语气和行文风格,但明确标注"不要重复翻译这部分"。

断点续传:每片翻译完成后立即写入 checkpoint 文件(含原文 hash),中断后可从断点继续,无需重新翻译已完成的片段。

三、处理流水线

3.1 预处理阶段

原文 .md → 解析 frontmatter → 提取元数据 → 自动检测 → 确定翻译策略

Frontmatter 解析:提取 titleauthorsource_urldatetags 等字段。日期字段支持多种来源(published > date > created)和格式(datetime、isoformat、字符串)。

视频脚本自动检测detect_video_transcript):扫描正文,统计以下模式的出现次数:

  • 粗体时间戳:**0:00**
  • 裸时间戳独占一行:0:00
  • 方括号时间戳:[00:01:23]
  • 括号章节目录:(00:00)

出现 ≥3 次即判定为视频脚本。此外,source_url 匹配 youtu.be 也直接视为视频脚本。

章节标题密度分析detect_heading_density):统计 ##### 标题的频率,返回三档:

密度判定条件翻译策略
rich平均每 80 行 ≥ 1 个标题翻译已有标题,适当优化
sparse有标题但密度不足翻译已有 + 在话题切换处补充标题
none无标题按话题切换自动插入 ### 标题

3.2 切分策略

主切分split_into_chunks):以 ## 标题为边界切分。相邻短 section 合并,直到累积字符数接近 max_chars 上限。

超长 section 的逐级回退_split_oversized):当单个 section 超过上限时,按以下顺序尝试拆分:

\n\n(段落) → \n(行) → 句末标点(.!?。!?) → 强制按字符数截断(在空格处断开)

每一级都检查拆分后的最大片段是否 ≤ max_chars,满足则停止回退。

Chunk Manifest:切分结果被序列化为 manifest(包含每片的 text、headings、text_hash、char_count),保存在 meta.json 中。续传时直接从 manifest 恢复,而非重新切分原文。

3.3 翻译执行

每片的 prompt 由以下模块拼合(按顺序):

┌─────────────────────────────────────────┐
│ 系统指令:翻译原则(信达雅、直译优先等) │  ~300 tokens
├─────────────────────────────────────────┤
│ 视频脚本指令(如适用)                   │  ~150 tokens
├─────────────────────────────────────────┤
│ 章节标题策略(rich/sparse/none 三选一)  │  ~50 tokens
├─────────────────────────────────────────┤
│ 已确定的术语表(累积)                   │  ~50-300 tokens
├─────────────────────────────────────────┤
│ 前文衔接段落(前一片末尾 3 段)          │  ~200-400 tokens
├─────────────────────────────────────────┤
│ 待翻译原文                               │  主体内容
├─────────────────────────────────────────┤
│ 输出格式指令(GLOSSARY 段要求)          │  ~50 tokens
└─────────────────────────────────────────┘

翻译原则要点

  • 原文→中文直译优先,不经英文中介转写
  • 避免过度补全(不补出原文没有的主语、因果、转折)
  • 专业术语首次出现时:中文翻译(原文术语)
  • 人名仅国际知名人物使用中文译名,其余保留原文
  • 不要将翻译写成摘要或释义

视频脚本特殊处理

  • 移除所有时间戳
  • 合并相邻短句为自然段落(3-5 句为一段)
  • 口语转书面语
  • 跳过广告/赞助商推广段落
  • 说话人识别:根据上下文推断并标注 **说话人名字:**

重试与降级机制

第 1 次 → 正常翻译
第 2 次 → 等待 10s 后重试
第 3 次 → 如片段 >5000 字符,拆为 5K 子片段分别翻译后拼合

术语表解析:翻译结果中如包含 GLOSSARY: 段,脚本按 原文 → 中文 格式逐行解析,merge 入全局术语表,传递给后续片段。

3.4 后处理

Markdown 排版修复fix_markdown_formatting):

  • ## 正文章节降为 ###(避免与文档标题冲突)
  • #### 统一为 ###
  • 标题前后确保空行
  • 压缩连续空行(最多保留一个)

AI 生成导读generate_intro):取译文前 3000 字作为上下文,调用 LLM 生成约 200 字的导读摘要,放在标题与正文之间。

输出文件生成

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
---
title: "中文标题"
original_title: "原文标题"
author: "作者"
date: YYYY-MM-DD
tags: ["translated", ...]
source_url: "URL"
original_file: "原文件名"
language: "zh-CN"
---

# 中文标题

{导读}

---

{译文正文}

## 术语表

| 原文 | 中文 |
|------|------|
| ... | ... |

---
*此文章由 AI 翻译*

原文自动归档:翻译完成后,原文件从 totranslate/ 移至 _archived/translated/

四、断点续传机制

断点续传是本系统应对长文翻译中断(网络超时、进程终止、token 限额)的核心能力。

4.1 Checkpoint 结构

每片翻译完成后,立即写入 chunk-{i:02d}-translated.json

1
2
3
4
5
6
7
8
9
{
  "index": 1,
  "headings": ["## 章节标题"],
  "source_text": "原文内容...",
  "source_hash": "sha256...",
  "source_char_count": 4871,
  "translation": "译文内容...",
  "glossary": {"term": "译法"}
}

4.2 续传流程

启动 → 读取 meta.json → 校验原文 hash
  ├─ hash 不匹配 → 警告并重新切分(旧 checkpoint 不再适用)
  └─ hash 匹配 → 从 manifest 恢复切分
       ├─ 全部片段有 checkpoint → 直接合并输出
       ├─ 部分完成 + chunk-size 未变 → 跳过已完成,继续翻译
       └─ 部分完成 + chunk-size 变化 → 保留已完成片段,
           剩余文本按新 chunk-size 重切,清理旧的未完成 checkpoint

4.3 自动续传

系统无需 --resume 标志。每次运行自动检测工作目录中的已有 checkpoint:

  • 已完成的片段:校验 source_hash 一致后直接跳过
  • 未完成的片段:正常翻译
  • chunk-size 变更:仅对未翻译的剩余文本按新尺寸重切

五、辅助工具

5.1 说话人标注(add_speakers.py

为访谈/播客的已翻译文本后置添加说话人标注。读取 translate-chunks/{slug}/ 中的已翻译 chunks,逐片调用 LLM 推断说话人身份,在每次说话人切换时插入 **说话人名字:** 标记。

规则约束:不修改任何翻译内容,仅插入标注。使用前一片末尾 3 段作为上下文判断当前说话人。

5.2 多模型支持

通过 --provider 参数切换模型。配置文件位于 ~/.configanthropic/keys/{provider},格式为环境变量导出(export KEY=VALUE)。

Provider模型用途
defaultClaude Sonnet 4.6主力翻译模型
glm智谱 GLM备选
minimaxMiniMax备选

六、翻译理念与 Prompt 设计

6.1 非中介翻译

明确要求"不要先改写成英文或借助其他中介语言理解后再翻译为中文"。这是针对 LLM 常见行为的约束——模型倾向于先将非英语原文理解为英文,再从英文翻译为中文,导致原文的语序、省略和修辞特征丢失。

6.2 直译优先的层次

直译(保留原文语序、语气、修辞、信息重心) 
  > 顺译(调整语序使中文通顺)
    > 意译(为可读性牺牲部分原文结构)

明确禁止的行为:

  • 过度补全(补出原文没有的主语、因果、转折)
  • 把翻译写成摘要或释义
  • 重新组织原文结构

6.3 术语处理策略

  • 首次出现:中文翻译(原文术语)
  • 后续出现:直接使用中文译法
  • 术语表全文统一,通过 GLOSSARY 机制跨片传递
  • 人名:仅国际知名人物用公认译名(如 Euler → 欧拉),其余保留原文

6.4 视频脚本处理哲学

视频脚本(播客、访谈、演讲)是本系统处理最多的文体。其特殊性在于:

  • 时间戳噪声:原文中密布的时间戳需要完全移除
  • 碎片化句子:口语转录按时间切割,单句往往不完整,需合并为自然段落
  • 口语冗余:“you know”、“like”、“I mean” 等填充词需在转书面语时自然消除
  • 广告插入:YouTube 视频常含赞助商推广,需直接跳过
  • 多人对话:需根据上下文推断说话人身份并标注

七、成本分析与弃用原因

7.1 Token 消耗分析

每片翻译的固定开销约 700-1200 tokens(系统指令 + 术语表 + 衔接段落),与原文内容无关。

以一篇 70K 字符的播客文字稿为例:

切分方式片数固定开销合计相对增量
10K/片7~5,600 tokens基准
5K/片14~11,200 tokens+100%
3K/片24~19,200 tokens+243%

术语表随翻译进行不断膨胀,后期片段的固定开销更高。此外,每片的输出端也因独立的格式指令而产生额外 token。

7.2 弃用原因

  1. Token 消耗大:分片架构的固有开销——每片都要重复系统指令、术语表、衔接段落,总 token 消耗显著高于一次性翻译
  2. 速度慢:串行逐片翻译,无并发。一篇 13 片的文章需要 13 次独立 API 调用,加上重试和导读生成,耗时可达数十分钟
  3. 性价比不足:与直接在 AI Studio 等交互式工具中翻译相比,翻译质量没有明显优势,但开发维护成本和运行成本都高得多
  4. 复杂度高:断点续传、chunk manifest、动态重切等机制增加了系统复杂度,调试和维护负担重

7.3 适用场景反思

分片翻译架构适合的场景:原文极长(>100K)且需要高度一致的术语体系。对于大多数文章(<50K),现代 LLM 的上下文窗口(128K-1M tokens)已足以一次性处理,分片的必要性大为降低。

八、命令行接口

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# 基本用法
python translate.py <input.md> [--provider default|glm|minimax] [--chunk-size N]

# 示例
python translate.py 'Clippings/totranslate/article.md'
python translate.py 'Clippings/totranslate/article.md' --chunk-size 5000
python translate.py 'Clippings/totranslate/article.md' --provider glm

# 说话人标注
python add_speakers.py <work-dir-slug> [--provider default|glm|minimax]

--resume 参数已废弃,系统现在自动检测断点续传。

附录:可复用的翻译提示词

以下提示词从本系统的 Prompt 设计中提炼而来,适用于 AI Studio 等支持长上下文的工具,整篇一次性翻译,无需分片。

请将以下文本翻译为中文。

## 翻译原则

- 直译优先:保留原文语序、语气、修辞和信息重心;实在不通顺再调整语序
- 不要中介转写:直接从原文翻译为中文,不要先理解为英文再转中文
- 不要过度补全:不补出原文没有的主语、因果、转折或解释
- 不要写成摘要或释义,不要重新组织原文结构
- 专业术语首次出现时:中文翻译(原文术语),后续直接用中文
- 人名:仅国际知名人物用公认译名(如 Euler → 欧拉),其余保留原文
- 保持 Markdown 格式,代码/命令不翻译

## 如果是视频脚本/播客/访谈

- 移除所有时间戳
- 合并相邻短句为自然段落(3-5 句一段),口语转书面语但保持流畅
- 跳过广告和赞助商推广段落
- 说话人识别:根据上下文在每次说话人切换时标注 **说话人名字:**,同一人连续发言不重复标注
- 在话题切换处插入 ### 三级标题,简洁概括该段核心话题

## 输出格式

1. 译文正文
2. 文末附术语表:

| 原文 | 中文 |
|------|------|
| term | 译法 |

---

## 待翻译内容

(粘贴原文)