📌 内容摘要
- Claude 输出不完整有两种截然不同的原因——弄清楚是哪种,才能用对解决方法。
- 7个具体技巧从 Prompt 层、参数层、架构层三个维度解决截断问题,覆盖网页端和 API 两种使用场景。
- 附 stop_reason 判断代码和自动续写实现,API 用户可以直接复制使用。
- 文末说明什么时候截断是”正确行为”,不需要修复。
一、先搞清楚:是哪种截断?
遇到输出不完整,第一步不是去改设置,而是判断是哪种截断——两种截断的原因和解决方法完全不同:
| 类型 | 表现 | 原因 | 解决方向 |
|---|---|---|---|
| 硬截断 | 句子中途断开,代码只写了一半,列表只有前几条 | 达到 max_tokens 上限 | 加大 max_tokens 或分段生成 |
| 软截断 | 结构完整但内容省略,如”……以下类似,不再赘述” | Claude 主动省略,认为已经够了 | Prompt 层明确要求完整输出 |
API 用户如何快速判断:检查响应的 stop_reason 字段。
import anthropic
client = anthropic.Anthropic()
response = client.messages.create(
model = "claude-sonnet-4-6",
max_tokens = 512,
messages = [{"role": "user", "content": "写一篇2000字的文章"}]
)
if response.stop_reason == "max_tokens":
print("❌ 硬截断:达到 max_tokens 上限,需要加大或分段")
print(f" 已用 {response.usage.output_tokens} tokens,设置上限是 512")
elif response.stop_reason == "end_turn":
print("✅ 自然结束:Claude 认为已完整")
print(" 如果内容不完整,是软截断,需要改 Prompt")
print(response.content[0].text[-100:]) # 看结尾是否完整
技巧一:加大 max_tokens(解决硬截断最直接的方法)
如果 stop_reason == "max_tokens",最直接的解法就是加大上限。但不是无脑加大——先估算任务实际需要多少 token,设一个合理的值:
"""
输出 token 估算参考:
- 中文:约 1.5 token/字,1000字文章 ≈ 1500 tokens
- 英文:约 1.3 token/词,1000词文章 ≈ 1300 tokens
- 代码:约 1 token/3-4字符,200行代码 ≈ 800-1000 tokens
- JSON:约 1 token/3字符,取决于字段数量
任务 → 建议 max_tokens:
- 一句话回答 → 64
- 短段落(<200字)→ 512
- 完整文章(<2000字) → 4096
- 长篇内容(>2000字) → 8192,或分段生成
- 完整代码文件 → 4096-8192
"""
# 自动根据任务类型选择 max_tokens
TASK_TOKENS = {
"short_answer": 128,
"paragraph": 512,
"article": 4096,
"long_article": 8192,
"code": 4096,
"analysis": 2048,
}
def smart_complete(prompt: str, task_type: str = "article") -> str:
max_tok = TASK_TOKENS.get(task_type, 2048)
resp = client.messages.create(
model = "claude-sonnet-4-6",
max_tokens = max_tok,
messages = [{"role": "user", "content": prompt}]
)
if resp.stop_reason == "max_tokens":
print(f"⚠️ 仍然触发截断,当前设置 {max_tok},建议升级任务类型或用分段生成")
return resp.content[0].text
技巧二:Prompt 明确禁止省略(解决软截断)
软截断的本质是 Claude 认为”这里不用写那么详细”。解决方法是在 Prompt 里明确告诉它不能省略:
“列出所有方法” → Claude 可能写5个就加”……等其他方法”
“写完整代码” → Claude 可能写到一半加”# 其余部分类似,省略”
“列出所有方法,每一个都要写,不允许用’等’或’以此类推’代替”
“写完整的、可直接运行的代码,不要省略任何函数实现,不要写’# TODO’或’# 类似'”
"""
防省略的关键词汇总,按场景选用:
列表/枚举任务:
"列出全部X,一个不漏"
"不允许用'等'、'以此类推'、'……'代替任何条目"
"即使有很多,也要逐条列出"
代码任务:
"写出完整可运行的代码,不省略任何函数"
"不要写 # TODO、# 省略、pass 占位"
"每个函数都要有完整实现,不能只写函数签名"
文章/分析任务:
"完整展开每个部分,不要因为字数多而省略"
"如果内容很长,继续写,不要总结收尾"
"每个章节都要有实质内容,不能只有标题"
"""
# 针对代码任务的防省略 Prompt 模板
CODE_PROMPT = """请写一个完整的 {功能描述} 的 Python 实现。
严格要求:
- 所有函数必须有完整实现,不能只有 pass 或注释占位
- 不要写"# 其他方法类似"或"# TODO"
- 包含完整的错误处理
- 代码可以直接复制运行,不需要任何补充
{具体需求}"""
技巧三:告诉 Claude 预期的输出长度
Claude 在不知道”应该写多长”的时候,容易保守地少写。明确给出长度预期,能让它更自信地展开:
""" 有效的长度指定方式(从具体到模糊,效果依次递减): 最有效:指定具体字数/行数 "写一篇1500-2000字的文章" "列出至少20个示例,每个一句话描述" "代码不少于100行,覆盖所有边界情况" 次有效:指定结构深度 "每个章节至少300字,不少于5个子要点" "每个函数都要有实现、注释和测试用例" 较弱:模糊的"详细"要求 "详细介绍" → Claude 对"详细"的理解因人而异,容易偏短 """
技巧四:分段生成(长内容的根本解法)
当内容确实很长(比如完整的代码文件、长篇文章),单次生成不可避免会有截断风险。正确的做法是分段生成,每段控制在合理的 token 范围内:
def generate_long_content(
topic: str,
sections: list[str],
words_each: int = 500,
) -> str:
"""
分段生成长文章
先生成大纲确认结构,再逐段展开
避免单次生成过长导致截断
"""
all_parts = []
# 第一步:生成大纲(确保结构完整)
outline_resp = client.messages.create(
model = "claude-sonnet-4-6",
max_tokens = 512,
messages = [{
"role": "user",
"content": f"为文章《{topic}》生成详细大纲,包含以下章节:{', '.join(sections)}。每章节列3-5个要点。"
}]
)
outline = outline_resp.content[0].text
all_parts.append(f"# {topic}\n\n")
# 第二步:逐章节展开
for section in sections:
print(f"正在生成:{section}...")
section_resp = client.messages.create(
model = "claude-sonnet-4-6",
max_tokens = int(words_each * 2), # 中文每字约1.5token,留余量
messages = [
{
"role": "user",
"content": f"""根据以下大纲,完整展开文章《{topic}》的【{section}】章节。
大纲参考:
{outline}
要求:
- 只写【{section}】这一章节,约{words_each}字
- 内容完整,不省略任何要点
- 不要写其他章节的内容"""
}
]
)
section_text = section_resp.content[0].text
# 检查是否截断
if section_resp.stop_reason == "max_tokens":
print(f"⚠️ {section} 章节被截断,考虑减少每章字数或进一步拆分")
all_parts.append(f"\n## {section}\n\n{section_text}\n")
return "".join(all_parts)
# 使用示例
article = generate_long_content(
topic = "Python异步编程完全指南",
sections = ["基础概念", "async/await语法", "并发模式", "实战案例", "性能优化"],
words_each = 400,
)
技巧五:自动续写(检测截断后继续)
对于不方便提前分段的场景,可以在检测到截断后自动触发续写:
def complete_with_continuation(
prompt: str,
max_tokens: int = 2048,
max_continuations: int = 3,
model: str = "claude-sonnet-4-6",
) -> tuple[str, int]:
"""
自动续写:检测到截断后自动继续生成
返回 (完整内容, 实际续写次数)
"""
messages = [{"role": "user", "content": prompt}]
full_content = []
continuations = 0
for attempt in range(max_continuations + 1):
resp = client.messages.create(
model = model,
max_tokens = max_tokens,
messages = messages,
)
chunk = resp.content[0].text
full_content.append(chunk)
# 自然结束,不需要续写
if resp.stop_reason != "max_tokens":
break
# 达到上限,触发续写
if attempt < max_continuations:
continuations += 1
print(f"检测到截断,触发第 {continuations} 次续写...")
# 把已有内容加入对话,让 Claude 接着写
messages.append({"role": "assistant", "content": chunk})
messages.append({
"role": "user",
"content": "请继续,从你停下的地方接着写,不要重复已有内容,不要重新开头。"
})
else:
print(f"已达最大续写次数({max_continuations}),停止")
return "".join(full_content), continuations
# 使用示例
content, times = complete_with_continuation(
prompt = "写一篇完整的Python装饰器教程,包含原理、语法、常见用法和实战案例",
max_tokens = 1500, # 故意设小,演示续写
max_continuations = 3,
)
print(f"生成完成,续写了 {times} 次,总字数约 {len(content)}")
技巧六:代码生成的专项处理
代码截断比文章截断更麻烦——文章截断还能读,代码截断直接报错。代码任务有几个专项技巧:
"""
代码任务防截断的三个专项方法:
"""
# 方法 A:先生成框架,再逐函数实现
FRAMEWORK_FIRST = """
第一步:先给我类和函数的签名列表(只有定义,不要实现)
第二步:我会逐个告诉你要实现哪个函数,你完整实现那一个
现在请给我以下功能的类框架:{需求}
"""
# 方法 B:指定每次生成的范围
SCOPED_CODE = """
只写 {函数名} 这一个函数的完整实现。
其他函数不需要写,只需要这一个函数。
函数要完整可运行,包含错误处理和注释。
"""
# 方法 C:检测代码是否完整(简单启发式)
def is_code_complete(code: str) -> bool:
"""粗略检查代码是否完整(不是精确解析)"""
code = code.strip()
# 检查括号是否配对
if code.count("{") != code.count("}"):
return False
if code.count("(") != code.count(")"):
return False
if code.count("[") != code.count("]"):
return False
# 检查是否有常见截断标志
truncation_signs = [
"# TODO", "# ...", "pass #", "...\n",
"# 其他", "# 类似", "# 省略", "# etc"
]
for sign in truncation_signs:
if sign in code:
return False
return True
def generate_complete_code(requirement: str) -> str:
"""生成完整代码,自动检测并修复截断"""
resp = client.messages.create(
model = "claude-sonnet-4-6",
max_tokens = 4096,
system = "你是一个代码生成专家。生成的代码必须完整可运行,不允许使用 TODO、pass 占位或省略号。",
messages = [{"role": "user", "content": requirement}]
)
code = resp.content[0].text
if resp.stop_reason == "max_tokens" or not is_code_complete(code):
# 代码不完整,要求继续
messages = [
{"role": "user", "content": requirement},
{"role": "assistant", "content": code},
{"role": "user", "content": "代码还不完整,请继续补全,从你停下的位置接着写。不要重复已有代码。"}
]
cont_resp = client.messages.create(
model = "claude-sonnet-4-6",
max_tokens = 2048,
messages = messages,
)
code += "\n" + cont_resp.content[0].text
return code
技巧七:流式输出时监控截断
使用流式输出时,截断发生在流结束时,需要在流完成后检查:
async def stream_with_truncation_check(
prompt: str,
max_tokens: int = 2048,
on_text: callable = None,
) -> dict:
"""
流式生成并在完成后检查是否截断
返回 {"content": str, "truncated": bool, "tokens": int}
"""
full_text = []
input_tok = 0
output_tok = 0
async with client.messages.stream(
model = "claude-sonnet-4-6",
max_tokens = max_tokens,
messages = [{"role": "user", "content": prompt}],
) as stream:
async for text in stream.text_stream:
full_text.append(text)
if on_text:
on_text(text) # 实时回调(用于前端展示)
# 流结束后获取完整统计
final = await stream.get_final_message()
input_tok = final.usage.input_tokens
output_tok= final.usage.output_tokens
truncated = final.stop_reason == "max_tokens"
content = "".join(full_text)
if truncated:
# 通知前端/调用方内容被截断
print(f"⚠️ 流式输出被截断({output_tok}/{max_tokens} tokens)")
return {
"content": content,
"truncated": truncated,
"tokens": {"input": input_tok, "output": output_tok},
}
什么时候截断是"正确行为",不需要修复
有时候 stop_reason == "max_tokens" 但不需要处理,因为你的业务就是需要截断到特定长度:
- 文本摘要:要求"不超过100字的摘要",设 max_tokens=150,命中了是符合预期的
- 标题生成:生成标题不需要很长,截断通常意味着已经生成了一个完整标题
- 分类/判断任务:输出只需要一个词,max_tokens=16,不可能也不应该更长
- 预算控制:有意设置 max_tokens 来控制每次调用成本,截断是设计行为
判断标准:看内容是否完整表达了你需要的信息,而不是看有没有达到 max_tokens。
常见问题
Q:claude.ai 网页端输出省略了怎么办?
网页端没有 stop_reason 可看,只能从内容判断。如果 Claude 写了"……以下类似"或提前结束,直接在对话里回复:"请继续,把刚才省略的部分完整写出来,不要再省略。"如果是硬截断(句子中途断了),说"请从[最后一句话]处继续"效果比"继续"更好。
Q:设了很大的 max_tokens(比如 8192),为什么还是截断?
检查是否是软截断——stop_reason 是 end_turn 但内容不完整,说明 Claude 认为已经写完了。这不是 max_tokens 的问题,是 Prompt 没有充分表达"需要更多内容"的意图。用技巧二(明确禁止省略)和技巧三(指定长度)来解决。
Q:续写时 Claude 会重复之前的内容怎么处理?
续写 Prompt 里加上"不要重复已有内容,直接从你停下的地方继续",同时在对话历史里把已生成的内容作为 assistant 消息传入(本文技巧五的代码已经这样处理)。如果重复仍然严重,可以在续写请求里附上最后50个字:"请从以下内容之后继续:'……[最后几句话]'"。
总结
解决截断问题的决策路径:先用 stop_reason 判断是硬截断还是软截断;硬截断(max_tokens 到顶)用技巧一(加大上限)或技巧四(分段生成);软截断(Claude 主动省略)用技巧二(Prompt 明确禁止省略)和技巧三(指定预期长度);对于一定会很长的内容,技巧四(分段生成)是最稳的根本解法,不依赖单次生成一次成功。