把 AI 输出接进业务系统,最怕的就是"格式不固定"。DeepSeek 提供了
response_format: json_object强制输出 JSON,但真正落地时坑不少。这篇把我踩过的坑一次说清。
一、先跑通:JSON 模式怎么开
curl https://api.deepseek.com/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $DEEPSEEK_API_KEY" \ -d '{ "model": "deepseek-chat", "messages": [ {"role": "system", "content": "你是一个输出 JSON 的助手"}, {"role": "user", "content": "分析这句话的情感,输出 json"} ], "response_format": {"type": "json_object"} }'返回结果里的choices[0].message.content就是一段 JSON 字符串。看着简单,下面每个坑都藏在这里。
二、坑1:prompt 里没有 "json" 这个词,直接翻车
这是 DeepSeek 官方文档特意强调、也最容易踩的坑。
现象:明明设置了response_format: {"type": "json_object"},模型却返回空内容,或者返回一段不带 JSON 的纯文本。
原因:DeepSeek 要求消息里必须出现 "json" 这个单词(不区分大小写),否则即使你声明了 response_format,模型也可能不认。
正例:
{"role": "system", "content": "你是一个输出 JSON 格式的助手"} {"role": "user", "content": "把下面这段文字转成 json 输出"}反例(会翻车):
{"role": "system", "content": "你是一个结构化输出助手"} {"role": "user", "content": "分析这句话的情感"}一句话记住:别用"结构化"代替"json",prompt 里老老实实写 json。
三、坑2:max_tokens 太小,JSON 被腰斩
现象:返回的 JSON 明显不完整,结尾是{"name": "张三", "tags": ["Java",就没了,解析必抛异常。
原因:JSON 模式默认输出比纯文本长,字段名、引号、逗号、花括号都占 token。之前按纯文本习惯设的 max_tokens 不够。
解决:给足预算。单次结构化输出建议max_tokens至少 1024,字段多、内容长直接给 2048 或 4096。
四、坑3:模型爱给 JSON 套 markdown 代码块
现象:content拿回来长这样:
```json {"name": "张三", "age": 30} ```直接JSON.parse会报错。
解决:解析前先清洗,去掉 ``` 包裹和前后空白:
private String cleanJson(String content) { String s = content.trim(); if (s.startsWith("```")) { s = s.replaceFirst("```[a-zA-Z]*\\s*", ""); s = s.replaceFirst("```\\s*$", ""); } return s.trim(); }五、坑4:字段类型漂移,数字变字符串
现象:同一个字段,这次返回"count": 3,下次返回"count": "3"。字段缺失也常见,这次有tags,下次没有。
原因:LLM 不保证类型稳定,尤其是没给示例时。
解决两条路:
prompt 里给一个完整的输出示例,模型会照着抄:
输出示例:{"sentiment": "正面", "score": 0.9, "tags": ["服务", "价格"]}拿到结果后做类型归一,读值时对类型做兜底处理。
实战建议:示例优先,兜底其次。示例能解决 90% 的类型漂移。
六、坑5:字符串值里夹了未转义的换行和引号
现象:让模型总结一段文本放进 JSON 字段,结果文本里的换行、双引号没转义,产出非法 JSON:
{"summary": "他说:"服务很好"。 体验不错。"}这个 JSON 直接解析必挂。
原因:模型输出的是"看起来像 JSON"的文本,不是真正经过序列化的 JSON,特殊字符转义不可靠。
解决:
prompt 里明确要求:
字符串内的换行请用 \n 表示,双引号请转义解析失败时重试一次,把报错信息回喂给模型让它修正(这是最有效的兜底)
七、Java 侧稳健封装(可直接抄)
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import lombok.extern.slf4j.Slf4j; import org.springframework.http.*; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; import java.util.HashMap; import java.util.List; import java.util.Map; @Service @Slf4j public class DeepSeekJsonService { private static final String API_URL = "https://api.deepseek.com/chat/completions"; private static final String API_KEY = "sk-xxxxxxxx"; private final RestTemplate restTemplate = new RestTemplate(); /** * 调用 DeepSeek 强制输出 JSON,返回清洗后的合法 JSON 字符串 */ public String chatForJson(String userPrompt) { Map<String, Object> body = new HashMap<>(); body.put("model", "deepseek-chat"); body.put("max_tokens", 2048); body.put("response_format", Map.of("type", "json_object")); body.put("messages", List.of( Map.of("role", "system", "content", "你是 JSON 输出助手,只输出合法 JSON,不要 markdown 代码块"), Map.of("role", "user", "content", userPrompt + " 请用 json 格式输出") )); HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); headers.set("Authorization", "Bearer " + API_KEY); String resp = restTemplate.postForObject( API_URL, new HttpEntity<>(body, headers), String.class); JSONObject json = JSON.parseObject(resp); String content = json.getJSONArray("choices") .getJSONObject(0) .getJSONObject("message") .getString("content"); return cleanJson(content); } private String cleanJson(String content) { String s = content.trim(); if (s.startsWith("```")) { s = s.replaceFirst("```[a-zA-Z]*\\s*", ""); s = s.replaceFirst("```\\s*$", ""); } return s.trim(); } }要点都封装进去了:
system + user 两个消息都带了 "json" 字样
max_tokens给到 2048 防截断cleanJson统一去 markdown 包裹
八、总结
| 坑 | 一句话解法 |
|---|---|
| prompt 没有 "json" | 消息里老老实实写 json |
| JSON 被截断 | max_tokens 给足 2048 |
| markdown 代码块包裹 | 解析前 cleanJson 清洗 |
| 字段类型漂移 | prompt 给输出示例 |
| 特殊字符没转义 | 明确转义要求 + 失败重试 |
结构化输出不是开了response_format就万事大吉,真正稳的是一套"提示词约束 + 清洗 + 兜底重试"的组合拳。
关于作者
独立开发者,主业 Java 后端。一个人用 SpringBoot + AI 交付过企业级管理平台和微信小程序,业余接外包。
代码和架构图放 Gitee 了:https://gitee.com/yao113088/jiguang-dev
微信/邮箱:luckluffy
顺手推荐
小程序"面试刷题狮"是我用 SpringBoot + DeepSeek 一个人做的 AI 面试刷题工具,本文的response_format: json_object就是它的核心实现。微信搜索"面试刷题狮"就能搜到,免费刷题 + AI 定制面试。