Claude API JSON 输出格式不对?完整诊断与解决方案指南
遇到 Claude API 返回的 JSON 格式错误?先别急着换模型。这篇文章整理了一套完整的诊断思路和分层解决方案,帮你快速找到问题出在哪,然后对症下药。
一、先搞清楚:你遇到的是哪类问题?
JSON 输出格式出问题,原因其实就那么几种。花两三分钟对照下面这张表,基本能定位到你的具体情况:
| 问题类型 | 典型表现 | 怎么排查 |
|---|---|---|
| 提示词没说清楚格式 | 模型有时返回 JSON,有时返回一段自然语言,或者字段结构对不上 | 翻一下你的 system prompt,有没有明确要求 JSON 格式? |
| Markdown 混进来了 | 返回内容被 ```包裹,或者 JSON 里的引号、换行没有正确转义 | 看看原始响应,JSON 是不是被代码块套着? |
| 请求体本身就有问题 | 收到 400 错误,提示 “deserialize” 或 “invalid JSON” | 检查请求体,重点看特殊字符和 Unicode 编码 |
| 输出被截断了 | JSON 结构不完整,末尾少了 } 或 ] |
看看 JSON 是不是在某个奇怪的地方突然断掉了 |
对应的快速处理思路:
- 提示词问题 → 用结构化输出 API(最稳),或者在提示词里给出 JSON Schema 示例
- Markdown 混入 → 启用结构化输出 API,或者在系统提示里明确禁止 markdown 包装
- 请求体问题 → 升级 SDK,确认编码是 UTF-8,特殊字符记得转义
- 输出截断 → 把 max_tokens 调大,或者把数据拆成小批次处理

二、四类根源逐一拆解
问题一:提示词没有明确告诉模型你要什么格式
怎么表现出来的: 模型有时给你 JSON,有时给你一段文字;或者虽然是 JSON,但字段名和结构跟你想要的完全不一样。
为什么会这样: Claude 默认会根据上下文自己判断该用什么格式输出。如果你的提示词没说清楚期望的结构,模型就会按自己的理解来,结果自然不稳定。
解决方案,按复杂度分三档:
最简单的做法(快速修复)
在系统提示里加一句明确的指令:
你必须用 JSON 格式回复,不要包含任何其他文本。
这能提高 JSON 输出的概率,但没法保证字段结构每次都一样。
稍微完整一点(给出 Schema 示例)
直接把你想要的结构写出来:
请以以下 JSON 格式回复:
{
"title": "字符串",
"summary": "字符串",
"tags": ["字符串数组"],
"confidence": 0.0
}
只返回 JSON,不要其他内容。
结构一致性会好很多,但碰到复杂嵌套或字段验证还是可能出问题。
最稳的方案(结构化输出 API)
这是 Claude 官方推荐的做法,下面第三部分会详细说。简单来说,通过 API 层面的 JSON Schema 约束,可以做到格式 100% 一致。
问题二:输出混入了 Markdown,转义也不完整
怎么表现出来的: 响应内容被 ```代码块包着,或者 JSON 里的引号、换行符没有正确转义,直接导致解析失败。
为什么会这样: Claude 在回复时习惯用 markdown 代码块来展示 JSON,尤其是提示词没有明确禁止的时候。另外,如果 JSON 里包含特殊字符,模型也不一定会正确转义。
解决方案 A:结构化输出 API(首选)
这个方案在 Claude 3.5 Sonnet 及更新版本里可用,直接在 API 层面强制模型返回符合 JSON Schema 的纯数据,彻底杜绝 markdown 混入:
import anthropic
import json
client = anthropic.Anthropic(api_key="your_api_key")
schema = {
"type": "object",
"properties": {
"title": {"type": "string"},
"content": {"type": "string"},
"tags": {
"type": "array",
"items": {"type": "string"}
}
},
"required": ["title", "content", "tags"]
}
response = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=1024,
messages=[
{
"role": "user",
"content": "请分析这段文本并提取标题、内容摘要和标签。\n文本:[你的内容]"
}
],
structured_outputs={
"type": "json_schema",
"json_schema": {
"name": "TextAnalysis",
"schema": schema,
"strict": True
}
}
)
# 直接拿到 JSON 对象,不用手动清洗
result = json.loads(response.content[0].text)
print(result)
这个方案的好处很明显:输出 100% 符合 Schema,没有 markdown 包装,字段类型和必填项自动验证,性能和 token 消耗跟传统方案差不多。
解决方案 B:提示词约束(备选方案)
如果因为模型版本限制用不了结构化输出,可以在提示词里这样写:
你的回复必须满足以下要求:
1. 只返回有效的 JSON,不要任何其他文本
2. 不要用 markdown 代码块包裹 JSON
3. JSON 中的特殊字符(如引号、换行)必须正确转义
4. 不要在 JSON 前后添加任何说明文字
解决方案 C:后处理清洗(应急用)
实在没办法,可以在客户端做一层清洗,但这只是救急,不建议作为常规做法:
import re
import json
def extract_json_from_markdown(response_text):
# 先尝试提取 markdown 代码块里的 JSON
match = re.search(r'```(?:json)?\s*([\s\S]*?)\s*```', response_text)
if match:
json_str = match.group(1)
else:
json_str = response_text
try:
return json.loads(json_str)
except json.JSONDecodeError as e:
print(f"JSON 解析失败: {e}")
return None
result = extract_json_from_markdown(response.content[0].text)
问题三:请求体本身有问题(Unicode 或转义错误)
怎么表现出来的: 发请求时直接收到 400 错误,提示 “Failed to deserialize” 或 “invalid JSON”。
常见原因有这几个: 请求体里的字符串没有正确转义(尤其是包含引号、反斜杠、换行的内容);特殊 Unicode 字符编码不对;SDK 版本太旧,对某些字符的处理有 bug。
排查步骤:
首先检查 SDK 版本够不够新:
# Python
pip show anthropic
# 版本需要 >= 0.7.0
# Node.js
npm list @anthropic-ai/sdk
# 版本需要 >= 0.9.0
然后确认字符编码是 UTF-8:
import sys
print(sys.getdefaultencoding()) # 应该输出 'utf-8'
再看看特殊字符的处理方式。其实用 SDK 的话,它会自动处理转义,不需要你手动操心:
# 直接传字符串给 SDK 就好,它会自动转义
content = 'Please return {"name": "value"}'
response = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=1024,
messages=[{"role": "user", "content": content}]
)
如果问题还在,直接升级 SDK:
pip install --upgrade anthropic
问题四:输出被截断(token 不够用)
怎么表现出来的: 返回的 JSON 结构不完整,末尾少了闭合的 } 或 ]。
怎么确认: 看 API 响应里的 stop_reason 字段,如果是 "max_tokens",那就是 token 不够导致截断了。
两种处理方式:
直接调大 max_tokens:
response = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=4096, # 根据实际输出大小调整
messages=[{"role": "user", "content": "..."}]
)
数据量太大的话,拆成小批次处理:
def process_large_data(items, batch_size=10):
results = []
for i in range(0, len(items), batch_size):
batch = items[i:i+batch_size]
response = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=2048,
messages=[{
"role": "user",
"content": f"处理这些项目:{json.dumps(batch)}"
}]
)
results.extend(json.loads(response.content[0].text))
return results
三、结构化输出 API 完全指南
结构化输出是目前最稳的 JSON 格式保证方案,Claude 3.5 Sonnet 及更新版本都支持。
它到底是什么
简单说,就是在 API 请求里附上一个 JSON Schema,然后模型会被强制返回符合这个 Schema 的纯 JSON 数据。跟"在提示词里要求 JSON"不同,这是在 API 层面做的约束,所以:
- 格式 100% 一致
- 字段类型、必填项自动验证
- 不会有 markdown 包装
- 性能开销很小
什么情况下用
- 必须用的场景: 需要稳定 JSON 输出、字段结构固定、生产环境关键业务
- 推荐用的场景: 数据提取、内容分类、表单验证、API 集成
- 可以用也可以不用: 简单的 JSON 输出,对格式容错度比较高
Schema 怎么写
来看一个比较完整的例子:
schema = {
"type": "object",
"properties": {
# 字符串字段
"title": {
"type": "string",
"description": "内容标题"
},
# 数字字段,带范围限制
"score": {
"type": "number",
"minimum": 0,
"maximum": 100,
"description": "评分(0-100)"
},
# 枚举字段,限定可选值
"category": {
"type": "string",
"enum": ["技术", "生活", "娱乐"],
"description": "内容分类"
},
# 数组字段
"tags": {
"type": "array",
"items": {"type": "string"},
"description": "标签列表"
},
# 嵌套对象
"author": {
"type": "object",
"properties": {
"name": {"type": "string"},
"email": {"type": "string"}
},
"required": ["name"]
}
},
"required": ["title", "category"]
}
几个关键字段说明一下:type 指定字段类型(string、number、integer、boolean、array、object 都支持);required 列出必填字段;enum 限定枚举值;minimum/maximum 设置数字范围;description 写字段说明,模型会参考这个来理解你的需求,建议写清楚一点。
完整代码示例
import anthropic
import json
def extract_article_info(article_text):
client = anthropic.Anthropic(api_key="your_api_key")
schema = {
"type": "object",
"properties": {
"title": {"type": "string", "description": "文章标题"},
"summary": {"type": "string", "description": "100字以内的摘要"},
"main_points": {
"type": "array",
"items": {"type": "string"},
"description": "3-5个主要观点"
},
"sentiment": {
"type": "string",
"enum": ["positive", "neutral", "negative"],
"description": "情感倾向"
},
"confidence": {
"type": "number",
"minimum": 0,
"maximum": 1,
"description": "分析置信度"
}
},
"required": ["title", "summary", "main_points", "sentiment"]
}
response = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=1024,
messages=[
{
"role": "user",
"content": f"请分析以下文章:\n\n{article_text}"
}
],
structured_outputs={
"type": "json_schema",
"json_schema": {
"name": "ArticleAnalysis",
"schema": schema,
"strict": True
}
}
)
result = json.loads(response.content[0].text)
return result
article = "这是一篇关于 AI 发展的文章..."
analysis = extract_article_info(article)
print(f"标题: {analysis['title']}")
print(f"情感: {analysis['sentiment']}")
加上重试机制
偶发失败在所难免,建议在生产环境里加上重试逻辑:
def call_with_retry(prompt, schema, max_retries=3):
client = anthropic.Anthropic()
for attempt in range(max_retries):
try:
response = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=1024,
messages=[{"role": "user", "content": prompt}],
structured_outputs={
"type": "json_schema",
"json_schema": {
"name": "Output",
"schema": schema,
"strict": True
}
}
)
return json.loads(response.content[0].text)
except Exception as e:
if attempt == max_retries - 1:
raise
print(f"第 {attempt + 1} 次尝试失败: {e},重试中...")
四、常见错误速查表
| 错误信息 | 根本原因 | 怎么解决 |
|---|---|---|
invalid_request_error: Could not deserialize JSON body |
请求体 JSON 格式有问题,通常是特殊字符没转义 | 升级 SDK,检查字符编码,用 SDK 自动转义 |
400 Bad Request |
请求格式错误,可能是 headers、body 或参数问题 | 确认 Content-Type 是 application/json,检查 API key 格式 |
stop_reason: "max_tokens" + JSON 不完整 |
token 不够,输出被截断了 | 调大 max_tokens,或者拆批处理 |
| JSON 被 markdown 包裹 | 模型习惯用 ```包装 JSON | 用结构化输出 API,或在提示词里明确禁止 markdown |
"no low surrogate in string" |
Unicode 转义错误,代理对处理有问题 | 确保 UTF-8 编码,升级 SDK |
| 字段类型不匹配 | 模型理解有偏差,返回了错误类型的数据 | 在 Schema 的 description 里把字段要求写得更具体 |
五、工程实践建议
提示词怎么写: 明确说明期望的 JSON 结构(给示例或 Schema 都行),解释每个字段的含义和类型,同时禁止不需要的格式,比如 markdown 或者自然语言混排。
请求体构造注意事项: 用最新版本的 SDK,确保所有字符串是 UTF-8 编码,不要在请求体里手动拼接未转义的 JSON,交给 SDK 处理就好。
响应验证和错误处理: 每次都检查 stop_reason,确认输出是完整的;解析 JSON 之前做格式校验;加上重试机制应对偶发失败。
调试时的日志:
import logging
logging.basicConfig(level=logging.DEBUG)
response = client.messages.create(...)
print(f"Stop reason: {response.stop_reason}")
print(f"Content: {response.content[0].text}")
六、完整实战案例:从用户评论中提取结构化信息
import anthropic
import json
def analyze_user_reviews(reviews):
client = anthropic.Anthropic()
schema = {
"type": "object",
"properties": {
"reviews": {
"type": "array",
"items": {
"type": "object",
"properties": {
"original_text": {"type": "string"},
"sentiment": {
"type": "string",
"enum": ["positive", "neutral", "negative"]
},
"rating": {
"type": "integer",
"minimum": 1,
"maximum": 5
},
"key_points": {
"type": "array",
"items": {"type": "string"}
}
},
"required": ["original_text", "sentiment", "rating"]
}
}
},
"required": ["reviews"]
}
response = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=2048,
messages=[{
"role": "user",
"content": f"请分析以下用户评论,提取情感、评分和关键点:\n{json.dumps(reviews, ensure_ascii=False)}"
}],
structured_outputs={
"type": "json_schema",
"json_schema": {
"name": "ReviewAnalysis",
"schema": schema,
"strict": True
}
}
)
return json.loads(response.content[0].text)
# 跑一下试试
reviews = [
"这个产品太棒了,质量很好",
"一般般,没什么特别的",
"质量很差,不值得买"
]
result = analyze_user_reviews(reviews)
for review in result["reviews"]:
print(f"评论: {review['original_text']}")
print(f"情感: {review['sentiment']},评分: {review['rating']}")
总结
遇到 Claude API JSON 输出格式问题,处理思路其实很清晰:先用第一部分的表格定位是哪类问题,然后选对应的解决方案——结构化输出 API 是最优解,提示词约束是备选;实施的时候参考代码示例,记得检查 stop_reason 和响应格式;后续根据实际情况调整 max_tokens、Schema 定义或提示词。
对于生产环境,强烈建议用 Claude 3.5 Sonnet 及更新版本的结构化输出 API,稳定性和可维护性都是最好的。
更多推荐


所有评论(0)