
其实让大模型输出一个 JSON 很简单Prompt 里写一句请以 JSON 格式输出大多数模型就能给你吐出来。但你真正在项目里用过就知道能输出和稳定输出完全不是一个难度级别的事。我刚开始做 LLM 相关开发的时候第一反应就是疯狂改 Prompt各种花式叮嘱严格按照 JSON 输出。 不要输出任何解释。 不要使用 Markdown。这些写法有没有用有用。但本质上你只是在引导模型并不是在约束模型。模型仍然随时可能给你整出花活前面加一句好的下面是结果用json 包一层 Markdown 代码块少字段、多字段字段类型不对该给数字给了字符串JSON 格式本身就有语法错误直接解析失败测试环境跑 50 条数据全部完美上线后结果生产环境每隔几个小时就蹦一个JSONParseException。Prompt 可以提高成功率但永远不能保证稳定性要控制模型稳定输出必须多管齐下~Prompt 把输出结构描述清楚太多人写 Prompt 的时候太随意了就丢一句返回姓名、年龄、地址。这种描述模糊得一批模型看到以后全靠自由发挥。它可能返回{姓名: 张三}也可能返回{name: 张三}你根本无法预期。更好的做法是直接在 Prompt 里给出目标 JSON 结构例如{ name: , age: 0, address: }或者用更清晰的字段描述name: string用户姓名 age: integer用户年龄 address: string用户地址模型有了明确的参考模板输出通常会稳定不少。我一般还会补一句狠话只输出纯 JSON 文本不要输出任何解释文字、Markdown 标记或代码块。但是说实话Prompt 写得再严格该翻车还是会翻车。这就好比你跟同事交代工作哪怕说了三遍别加注释他还是可能顺手写两行注释上去。毕竟大模型的底层是概率生成不是指令执行。优先使用模型原生的结构化输出能力如果项目要投入生产千万不要只依赖 Prompt。现在主流的大模型基本都提供了结构化输出的能力常见的有三种Structured Output结构化输出JSON SchemaFunction Calling也叫 Tool Calling这三种方案的共同点是不是在语义层面上劝模型遵守格式而是在生成阶段就按照你指定的 Schema 进行硬约束。举个例子你定义了这样一个 Schema{ score: { type: integer } }在 Structured Output 模式下模型一般不会返回{ score: 90 }因为字段类型在底层就被锁死了它想输出字符串都输出不了。Structured Output 和 Function Calling 怎么选其实很简单看你的场景如果你只是想让模型返回固定的数据结构比如从一段文本里提取几个字段Structured Output 通常是首选。你给它一个 JSON Schema它严格按照这个 Schema 生成干净利落。如果后续还要调用数据库、搜索接口、天气 API 等外部能力那 Function Calling 更合适。因为它不仅规范了参数格式还能直接驱动工具调用。模型在 Function Calling 模式下会意识到自己是在填写参数表而不是在跟人聊天输出的规范性会好很多。以 OpenAI 的接口为例用 Function Calling 大概长这样tools [{ type: function, function: { name: extract_user_info, description: 从文本中提取用户信息, parameters: { type: object, properties: { name: {type: string, description: 用户姓名}, age: {type: integer, description: 用户年龄}, address: {type: string, description: 用户地址} }, required: [name, age, address] } } }] response client.chat.completions.create( modelgpt-4o, messages[{role: user, content: 张三28岁住在北京市朝阳区}], toolstools, tool_choice{type: function, function: {name: extract_user_info}} )后端拿到的直接就是一个标准的函数调用对象不是一段带有 Markdown 包裹的文本用 Java 或者 Go 直接反序列化就完事了。能用模型原生能力约束的就别靠 Prompt 去求模型。这条原则在 AI 工程化落地中怎么强调都不为过。不要让模型一次干太多活这是一个特别容易被忽略的坑。很多人写 Prompt 的时候恨不得把所有事情一股脑塞进去阅读下面这篇文章 → 总结核心观点 → 提取关键词 → 判断情绪倾向 → 翻译成英文 → 最后输出 JSON。任务链越长模型越容易在中间某个环节跑偏到最后输出的 JSON 就开始变形。要么丢字段要么格式错乱甚至直接变成一段自然语言的总结报告。模型不是流水线工人它是一个概率生成器。你给它的任务越复杂它需要同时兼顾的约束就越多出错概率就越高。实际开发中我更倾向于拆成多个步骤第一步内容理解 → 输入原始文章让模型做摘要和关键词提取输出自然语言即可 第二步结构化输出 → 把第一步的结果喂给模型让它严格按照 JSON Schema 输出结构化数据虽然多调用了一次模型多花了几分钱 Token 费但整体稳定性通常会显著提高而且出了问题也更容易定位是第一步理解错了还是第二步格式化出了问题一目了然。这跟写代码是一个道理一个函数只干一件事永远比一个函数干五件事更可靠。程序一定要做校验和兜底永远不要假设模型一定会返回合法 JSON哪怕你已经用了 Structured Output哪怕你已经用了 Function Calling程序也必须做好兜底。原因很简单线上环境什么妖蛾子都可能出——模型版本升级、接口超时返回了半截响应、网络抖动导致 JSON 被截断……一个成熟的 AI 应用对模型的输出至少要做以下校验import json def validate_model_output(raw_output: str, required_fields: list) - dict: # 1. JSON 是否能解析 try: data json.loads(raw_output) except json.JSONDecodeError: # 尝试修复常见问题去掉 Markdown 代码块包裹 cleaned raw_output.strip() if cleaned.startswith(): cleaned cleaned.split(\n, 1)[-1].rsplit(, 1)[0] try: data json.loads(cleaned) except json.JSONDecodeError: raise ValueError(模型输出无法解析为 JSON) # 2. 必填字段是否缺失 for field in required_fields: if field notin data: raise ValueError(f缺少必填字段: {field}) # 3. 字段类型是否正确根据业务定义校验 # 4. 枚举值是否合法 # 5. 是否需要填充默认值 return data除了校验还有一个非常重要的机制自动重试。模型输出有一定的随机性这次格式不对重新调一次可能就对了。给关键链路加一个 2~3 次的重试机制配合指数退避能覆盖掉绝大多数偶发性的格式异常。import time def call_with_retry(func, max_retries3): for attempt in range(max_retries): try: result func() return validate_model_output(result, [name, age]) except (ValueError, KeyError) as e: if attempt max_retries - 1: raise time.sleep(2 ** attempt) # 指数退避1s, 2s, 4s想做一个能跑在线上的 AI 应用不能假想模型特别可靠必须保证应用能处理各种异常情况。这跟你调第三方接口是一个道理。你不会裸调支付宝的接口不加任何异常处理吧对待模型的输出也该有同样的防御心态。说在最后总结一下目前比较成熟的一套做法其实就是以下四种Prompt 明确描述输出结构减少模型的理解偏差。优先使用 Structured Output 或 Function Calling在生成层面做硬约束而不是只靠 Prompt 软约束。拆分复杂任务让模型一次只专注一件事降低出错概率。程序侧做校验和兜底对解析失败、字段异常等情况做好重试和容错。一般刚接触大模型开发的人会把大量时间花在反复修改 Prompt 上希望把成功率从 90% 提高到 99%。但真做过线上应用就会发现Prompt 只是第一步。