| Q86 | How would you structure a prompt to ensure the LLM output is in a specific format, like JSON
1️⃣ 考察意图
面试官想考察的不是你会不会写“请输出JSON”,而是你对LLM输出格式控制的工程级理解。这是典型的系统设计+工程取舍题,刁钻点在于:LLM天然是概率生成器,强制结构化输出本质上是对抗其生成惯性。答好了能展示你对prompt engineering、后处理容错、以及工具层(function calling / JSON mode)的实战认知,而非纸上谈兵。
2️⃣ 标准答
确保LLM输出JSON,需要从prompt设计、工具层约束、后处理兜底三个层面递进控制。以下是我在线上系统里的标准做法:
1. Prompt层:显式模板 + 角色锚定
- 给完整JSON模板:在prompt末尾直接贴一个带占位符的JSON结构,要求“只填充
<value>,不要额外文字”。例如:
{**"name": "<string>", "age": <int>, "skills": ["<string>"] }
- 角色设定:用system message锚定“你是一个JSON生成器,输出必须严格符合RFC 8259标准,禁止markdown代码块包裹”。
- 负面示例:给一个错误输出(如用单引号、多行注释)并标注“这是错误的”,强化LLM对格式的边界认知。
- 工具层:优先使用JSON mode / Function Calling**
- JSON mode(如GPT-4 Turbo / Claude 3):在API参数中设置
response_format={"type": "json_object"},强制模型只输出合法JSON。代价:模型会牺牲部分推理质量(因为内部加了约束),且不支持流式输出。 - Function Calling:定义一个
output_json函数,参数schema就是目标JSON结构。模型会以结构化参数返回,天然保证格式正确。工程取舍:Function Calling的token消耗比纯prompt高约15-20%,但格式错误率从~30%降到<1%。 - 开源方案:用
lm-format-enforcer或outlines库,在推理时对logits做mask,只允许生成符合JSON schema的token序列。适合自部署场景,但增加推理延迟约10-20ms。
3. 后处理层:容错与修复
- 正则提取:用
re.search(r'\{.*\}', text, re.DOTALL)抓取第一个花括号块,再用json.loads()解析。坑:LLM可能在JSON前加“Here is your JSON:”,正则必须跳过前缀。 - 修复策略:当
json.loads()抛异常时,用json_repair库(基于Levenshtein距离)尝试自动修复常见错误(如多余逗号、未闭合引号)。实测修复率约60-70%。 - 重试机制:如果修复失败,用原prompt+错误信息重新调用一次,并加一句“上次输出格式错误,请严格按模板输出”。重试2次后格式正确率可达99%+。
实际落地的坑 + 解法:
- 坑:LLM在JSON里插入注释(
//或/* */),导致解析失败。解法:在prompt里明确“JSON中禁止任何注释”,并在后处理中用正则//.*$和/\*.*?\*/预清理。 - 坑:嵌套JSON时,模型容易在深层字段漏掉引号。解法:用
pydantic定义schema,在后处理时做类型校验,不匹配的字段用默认值填充,而不是直接抛错。
总结:纯prompt控制只能解决80%问题,必须结合工具层约束(JSON mode / Function Calling)和后处理容错(修复+重试)才能达到生产级可靠性。
3️⃣ 答题模板(30 秒电梯版)
“这个问题我从prompt设计、工具层约束、后处理兜底三个层面回答。prompt层给完整JSON模板并加负面示例;工具层优先用Function Calling或JSON mode,牺牲少量性能换取格式确定性;后处理层用正则提取+json_repair修复+重试机制兜底。总结一句:生产级方案必须三层联动,单靠prompt指令不可靠。”
4️⃣ 高频追问 & 应对
追问 1:如果用户要求输出嵌套JSON(如包含数组和对象),你的prompt怎么设计?
嵌套场景下,模板要分层缩进,并在每个字段后标注类型。例如:
同时加一条约束:“数组元素必须用逗号分隔,最后一个元素后不能有逗号”。如果模型仍出错,在后处理中用递归解析器(如
json.loads自带递归)逐层校验,并在重试时只回传错误路径(如“orders[0].items格式错误”),减少模型认知负担。
追问 2:你的方案在流式输出(streaming)场景下怎么处理?
流式场景下,JSON mode和Function Calling都不支持,只能用纯prompt+后处理。做法:在客户端缓存流式token,直到遇到闭合花括号
}才尝试解析。如果解析失败,用ijson库做增量解析,边流边校验。取舍:流式场景下格式错误率会从1%升到5-8%,所以我会在prompt里加“输出必须一行一个JSON字段”来降低解析复杂度,并接受偶尔的格式错误,用前端容错(如显示“解析中”)代替重试。
追问 3:你如何评估不同方案(prompt vs. Function Calling vs. JSON mode)的性价比?
我会用三个指标:格式正确率、平均延迟、token消耗。实测数据(基于GPT-4):纯prompt正确率约70%,延迟0.5s,token消耗低;JSON mode正确率95%,延迟0.6s,token消耗略高(因为内部加约束);Function Calling正确率99%+,延迟0.8s,token消耗高15-20%。取舍:对延迟敏感的场景(如实时对话)用JSON mode;对正确率要求极高(如API输出)用Function Calling;对成本敏感用纯prompt+重试。没有银弹,必须根据业务SLA选型。
5️⃣ 避坑 · 常见错误答法
- ❌ 说“在prompt里写‘请输出JSON’就够了” → ✅ 必须给完整模板+负面示例+角色锚定,否则模型会输出markdown代码块或额外解释文字。
- ❌ 说“用正则提取JSON后直接解析,出错就抛异常” → ✅ 必须加修复和重试机制,生产环境不能容忍一次失败就中断流程。
- ❌ 说“JSON mode是万能的,所有场景都用它” → ✅ JSON mode不支持流式输出,且牺牲推理质量,必须根据场景选型。
6️⃣ 简历呼应
- 如果你有RAG项目:从“结构化输出用于知识图谱构建”切入,讲如何用JSON mode将LLM抽取的实体关系直接转为图数据库schema,并对比纯prompt的格式错误率下降数据。
- 如果你只做过传统NLP:用“序列标注 vs. JSON生成”类比,讲传统CRF输出固定标签序列,而LLM输出自由文本,所以需要更严格的约束机制,引出Function Calling的schema设计。
- 如果你是校招无项目:聚焦“复现OpenAI JSON mode论文”,讲如何用
lm-format-enforcer在本地实现类似效果,并给出100条测试用例的格式正确率对比表。 - 《A Prompt Pattern Catalog to Enhance Prompt Engineering with ChatGPT》 - 结构化prompt模式
- OpenAI Cookbook: “How to use JSON mode” - 官方最佳实践
lm-format-enforcerGitHub仓库 - 开源logits mask实现- 《Function Calling vs. JSON Mode: A Practical Comparison》 - 博客对比分析
json_repairPyPI包 - 后处理修复工具