共计 3389 个字符,预计需要花费 9 分钟才能阅读完成。
最近在用 Claude API 做长文本生成时,经常遇到 api error: claude's response exceeded the 6000 output token maximum 这个报错。刚开始遇到时还挺头疼的,后来摸索出几种解决方案,这里做个记录和分享。

问题场景
当我们需要处理以下场景时,特别容易触发这个限制:
- 生成长篇内容(如技术文档、小说章节)
- 构建多轮对话系统
- 处理大型文档的摘要或翻译
这个限制意味着单次 API 响应不能超过 6000 个 token,对于需要处理大量文本的场景来说确实是个挑战。
解决方案一:分块处理
基本思路
把大文本按语义拆分成多个小块,每个块独立请求 API,最后合并结果。关键在于如何拆分才能保持语义连贯。
实现细节
- 文本拆分算法
def split_text_by_semantic(text, max_tokens=5000):
"""
按段落边界拆分文本,确保每个块不超过 max_tokens
保留段落完整性优于严格 token 计数
"""paragraphs = text.split('\n\n') # 假设双换行是段落分隔
chunks = []
current_chunk = []
current_token_count = 0
for para in paragraphs:
para_token_count = estimate_token_count(para) # 需要实现的 token 估算函数
# 如果当前段落太大需要进一步拆分
if para_token_count > max_tokens:
sentences = split_into_sentences(para) # 按句子拆分
for sent in sentences:
sent_token_count = estimate_token_count(sent)
if current_token_count + sent_token_count > max_tokens:
chunks.append(' '.join(current_chunk))
current_chunk = [sent]
current_token_count = sent_token_count
else:
current_chunk.append(sent)
current_token_count += sent_token_count
else:
if current_token_count + para_token_count > max_tokens:
chunks.append(' '.join(current_chunk))
current_chunk = [para]
current_token_count = para_token_count
else:
current_chunk.append(para)
current_token_count += para_token_count
if current_chunk:
chunks.append(' '.join(current_chunk))
return chunks
-
上下文保持技巧
-
每个块的开头添加上文摘要
- 对对话系统,保留最近 3 - 5 轮对话历史
-
使用特殊的 [CONTINUE] 标记连接分块
-
完整调用示例
import logging
from tenacity import retry, stop_after_attempt, wait_exponential
@retry(stop=stop_after_attempt(3),
wait=wait_exponential(multiplier=1, min=4, max=10),
reraise=True
)
def call_claude_with_chunking(prompt, max_retries=3):
chunks = split_text_by_semantic(prompt)
full_response = []
for i, chunk in enumerate(chunks):
try:
# 如果是后续块,添加上下文提示
if i > 0:
chunk = f"上文摘要: {summarize_previous(full_response[-1])}\n\n{chunk}"
response = claude_api_call(chunk)
full_response.append(response)
except Exception as e:
logging.error(f"处理第 {i} 个块时出错: {str(e)}")
if i == 0 or len(full_response) == 0:
raise # 第一个块失败直接抛出
# 否则尝试用更小的块重试
smaller_chunks = split_text_by_semantic(chunk, max_tokens=2000)
for small_chunk in smaller_chunks:
retry_response = claude_api_call(small_chunk)
full_response.append(retry_response)
return ' '.join(full_response)
解决方案二:流式响应
基本思路
利用 API 的流式返回特性,边接收边处理,避免一次性加载全部响应。
实现细节
- 异步流式处理示例
import aiohttp
import asyncio
async def stream_claude_response(prompt):
"""流式处理 API 响应,适用于实时性要求高的场景"""
buffer = []
buffer_token_count = 0
buffer_size = 2000 # 控制内存使用
async with aiohttp.ClientSession() as session:
async with session.post(
API_ENDPOINT,
json={"prompt": prompt, "stream": True},
headers=API_HEADERS
) as resp:
async for chunk in resp.content:
decoded_chunk = chunk.decode('utf-8')
token_count = estimate_token_count(decoded_chunk)
# 缓冲控制,防止内存暴涨
if buffer_token_count + token_count > buffer_size:
processed = process_buffer(buffer)
yield processed
buffer = []
buffer_token_count = 0
buffer.append(decoded_chunk)
buffer_token_count += token_count
if buffer:
yield process_buffer(buffer)
async def process_buffer(buffer):
"""处理缓冲区的数据,可以在这里加入业务逻辑"""
return ''.join(buffer)
-
背压 (backpressure) 处理
-
设置合理的缓冲区大小
- 监控处理速度,动态调整请求速率
- 使用 asyncio 的信号量控制并发
方案对比
| 维度 | 分块处理 | 流式响应 |
|---|---|---|
| 实现复杂度 | 中等 | 较高 |
| 延迟 | 较高(需要等待所有块完成) | 低(实时输出) |
| 内存使用 | 较高 | 低 |
| 适用场景 | 需要完整上下文的生成任务 | 实时性要求高的交互场景 |
| 错误恢复 | 容易(可重试单个块) | 困难(需要重新建立连接) |
性能优化建议
- 分块大小调优
- 建议初始设置为 4000-5000 token(留出安全边际)
-
根据实际响应时间动态调整
-
监控指标
# 示例监控指标收集 metrics = {'chunk_processing_time': [], 'retry_count': 0, 'total_tokens_processed': 0 } -
内存优化技巧
- 流式处理时及时释放已处理的数据
- 使用生成器而非列表保存中间结果
生产环境陷阱
- 上下文丢失问题
- 现象:分块后响应不连贯
-
解决:在块之间添加足够的上下文提示
-
速率限制叠加
- 现象:分块导致 API 调用次数暴增
-
解决:实现请求队列和速率控制
-
错误处理不充分
- 现象:中间块失败导致整个任务失败
- 解决:实现块级别的重试和跳过机制
总结思考
实际项目中,我通常会根据业务特点选择方案:
- 内容生成类任务:分块处理 + 上下文保持
- 实时对话系统:流式响应 + 背压控制
- 超高可靠性场景:两者结合,流式为主,分块为备
最关键的还是理解业务需求,没有放之四海皆准的方案。希望这些实践经验对你有帮助!
正文完
发表至: 技术分享
六天前
