大模型
dev.to ·
@龚旭东
用 LLM 翻译整本书:长上下文的分块策略
作者在构建 AI 图书翻译服务 LectuLibre 时发现,即便 Claude 3.5 Sonnet 有 200k token 上下文窗口,一本小说约 10 万至 15 万 token,一次性翻译仍面临成本高、长上下文对前文注意力衰减、限流超时和失败难恢复等问题。其方案是将书切分为带重叠的块,并为每块附带包含术语、人物语气和风格一致性的上下文摘要,以保留跨章节连贯性。
AI推荐理由 给出用 LLM 翻译整本书的具体工程方案:滑动窗口分块加上下文摘要,并说明成本、长上下文注意力衰减、限流与断点续传等实际约束,对做长文本 LLM 应用的开发者有直接参考价值;但为个人项目经验分享,未提供评测数据。
62
正文
How we built a pipeline that preserves context across 100k+ token books using Python, FastAPI, and Claude's context window.
The Problem: Books Don't Fit in a Prompt
When we started building LectuLibre, our AI-powered book translation service, we assumed we could just send an entire book to a large language model and get a translation back. After all, Claude 3.5 Sonnet advertises a 200k token context window. But a typical novel is 80,000–120,000 words, which is roughly 100,000–150,000 tokens. That's technically within limits, but...
Cost: translating a 100k token book in one shot is expensive.
Quality: LLMs lose attention to early chapters when processing long contexts.
Rate limits and timeouts.
Hard to resume if fails.
So we needed a chunking strategy that preserves cross-chapter context: terminology, character voice, consistent style.
Our Approach: Sliding Window Chunks + Context Summary
We split the book into overlapping chunks, translate each with a context buffer containing:
A running glossary of terms and character names
A summary of previous chapters
The current chunk's raw text
The pipeline:
Parse EPUB/PDF into plain text with chapter metadata.
Split text into token-aware chunks with overlap.
For each chunk, fetch glossary + previous summary from PostgreSQL.
Call LLM to translate with system prompt.
Extract new terms from translation, update glossary.
Generate a concise summary of the translated chunk, store for next chunk.
Assemble final translated book.
Code: Token-Aware Chunking
We tried LangChain's RecursiveCharacterTextSplitter first, but it works on characters, not tokens, so some chunks exceeded the model's token limit. We switched to tiktoken for exact token counting.
import tiktoken
from typing import List, Tuple
def chunk_text_by_tokens(
text: str,
target_tokens: int = 3000,
overlap_tokens: int = 500,
model: str = "claude-3-5-sonnet-20240620"
) -> List[str]:
# Use cl100k_base for Claude? Claude uses its own tokenizer but tiktoken's
# cl100k_base approximates well enough for chunk sizing.
enc = tiktoken.get_encoding("cl100k_base")
tokens = enc.encode(text)
chunks = []
start = 0
while start str:
glossary_str = "\n".join([f"{k} -> {v}" for k, v in glossary.items()])
prompt = SYSTEM_PROMPT_TEMPLATE.format(
source_lang=source_lang,
target_lang=target_lang,
chapter_summary=chapter_summary,
glossary=glossary_str
)
response = client.messages.create(
model=model,
max_tokens=4096,
temperature=0.3,
system=prompt,
messages=[{"role": "user", "content": chunk_text}]
)
return response.content[0].text
Enter fullscreen mode
Exit fullscreen mode
We use tenacity for retries due to occasional API timeouts. The glossary is a simple Python dict stored as JSONB in PostgreSQL via SQLAlchemy. We update it after each chunk:
def update_glossary(session, glossary, source_chunk, translated_chunk):
# Simplified extraction: ask LLM to list new terms
extraction_prompt = f"Extract proper nouns, technical terms, and repeated phrases from this translated text. Return a JSON list of objects with 'source' and 'translation'."
# ... call Claude or DeepSeek ...
new_terms = extract_terms(translated_chunk)
for term in new_terms:
glossary[term['source']] = term['translation']
session.execute(
text("UPDATE books SET glossary = :glossary WHERE id = :book_id"),
{"glossary": json.dumps(glossary), "book_id": book_id}
)
Enter fullscreen mode
Exit fullscreen mode
We maintain a chapter summary by asking the LLM to summarize the translated chunk in 2-3 sentences, then concatenate with previous summary but keep it under 500 tokens.
Results and Trade-offs
For a 120,000-word novel (≈150k tokens), our pipeline creates about 60 chunks of 3000 tokens with 500 overlap. Translation using Claude 3.5 Sonnet costs around $12–15 per book, and takes about 20–30 minutes. Before chunking, a single-shot translation often produced inconsistencies (e.g., a character's name changed midway) and occasionally timed out. Chunking reduced errors significantly; we measured a 40% drop in user-reported translation errors.
But it wasn't perfect. Early on we set overlap to 100 tokens (about 3%), and translators noticed context breaks at chunk boundaries: a sentence referencing "the previous chapter's event" would be mistranslated because the LLM didn't have that context. We increased overlap to 500 tokens (16-17% of chunk size), which helped.
Another failure: we initially included the full glossary plus all previous summaries in every prompt, which bloated the context and sometimes confused the model. We now cap the glossary to the 50 most recent terms and keep the summary under 500 tokens.
Lessons Learned
Token-aware chunking is essential. Don't rely on character counts; use tiktoken or the model's tokenizer.
Overlap matters more than you think. Start with 15-20% overlap and test boundaries.
Maintain a running glossary, but prune it. Too much context can hurt as much as too little.
Use summarization to bridge long-range context. A short summary of prior chapters is often more useful than raw text.
Choose your model per chunk. We use Claude for literary quality and DeepSeek for more mechanical sections (table of contents, footnotes) to save cost.
What's Next?
We're exploring hierarchical translation: first translate chapter summaries, then use those as context for translating full chapters. That might improve coherence for very long books with complex plots.
Takeaway: Translating entire books with LLMs is feasible if you treat it as a context-management problem, not a single-prompt problem. Chunking with overlap, glossary, and summary bridges the gap between the model's context window and a book's length.
Open question for the community: How do you handle idiomatic expressions or cultural references that need adaptation rather than literal translation? Any techniques beyond a glossary?