
人看太累机器看不懂开启JSON Mode让大模型吐出纯结构化数据前面九篇文章我们一步步把大模型调教得越来越听话了。我们用System Prompt给它立了规矩用Few-shot教了它套路。现在它吐出来的分析报告既有条理又专业老板看了直竖大拇指。但是呢软件工程里有个永恒的矛盾人看着爽的东西机器看着就费劲。老板看Markdown格式的报告觉得层次分明重点突出。可是如果你想把这份报告接进自动化系统里比如让脚本读完后自动发个钉钉报警或者自动重启服务那脚本看到Markdown就傻眼了。脚本不认识什么是二级标题什么是粗体。它只认识结构化的数据。以前我们为了让机器能读懂大模型的话经常去写正则表达式。从一大段文本里抠关键字。那日子简直不是人过的。大模型稍微换个说法正则就挂了。今天这篇我们要用一个大招彻底解决这个问题。这就是JSON Mode。我们要逼着大模型别整那些虚头巴脑的自然语言了直接给我吐机器最爱吃的纯JSON。一、 以前输出Markdown挺好的为啥突然要折腾JSON1.1 人看报告和机器看报告的需求是不一样的我们先复盘一下需求。当你只是做个Copilot也就是辅助工具的时候。人看一眼结果自己决定怎么操作。这时候输出Markdown、甚至输出一坨散文都没关系。人脑能过滤掉废话抓住重点。但是当你做的是Agent是自动化闭环的时候。大模型是整个流水线里的一环。它的输出是下游代码的输入。下游代码可能是一个Python脚本可能是一个钉钉机器人的发消息接口。这时候下游代码需要什么它需要确切的字段。比如它需要知道“异常级别是高还是低”、“异常指标叫什么”、“建议动作是重启还是忽略”。如果你给下游丢一段Markdown“## 异常概要级别高指标CPU”下游脚本得写多少正则才能把“高”和“CPU”抠出来而且万一哪天大模型调皮输出成了“级别为高”正则直接匹配不上整个自动化流水线就断了。1.2 正则表达式解析大模型的文本就是在给自己挖坑我以前带过个项目就是用正则去解析大模型输出的结论。当时试了各种提示词让大模型按格式输出。它大部分时候是听话的。但是总有那么百分之五的概率它会在字段后面多打个冒号或者把换行符给吞了。结果就是正则匹配失败脚本报错。半夜三更被叫起来查bug一查又是大模型输出格式飘了。这种锅背得人想撞墙。所以要想让系统稳定就必须在协议层把格式锁死。不允许大模型有任何自由发挥格式的空间。这就是JSON Mode的意义。二、 什么是JSON Mode凭啥它能让大模型乖乖就范2.1 大白话解释给大模型的嘴巴上加个过滤器JSON Mode通俗点说就是API网关给你加了个狠活。大模型在后台脑子里其实还是在那预测下一个词的概率。它可能还是想输出“好的分析结果是…”。但是当JSON Mode打开的时候API网关就像个监工死死盯着它吐出来的每一个Token。一旦大模型想吐出一个不符合JSON语法的字符比如想吐个中文逗号或者想打个回车监工直接就把这个Token给屏蔽了不让你发出来。逼着它只能换一个符合JSON语法的Token比如英文双引号或者冒号。也就是说不管大模型脑子里想得多飞起最终落到你手里的必定是一个符合JSON语法的字符串。如果它实在编不出合法的JSONAPI直接就报错返回个400之类的告诉你这次生成失败了也不会给你一坨乱码让你去猜。这就从根本上杜绝了格式崩坏的问题。2.2response_format参数闪亮登场在兼容OpenAI规范的API里开启JSON Mode非常简单。你只需要在发请求的payload里加一个参数就行。payload{model:timecho-model,messages:messages,response_format:{type:json_object}# 就是这一句}你看加上response_format这个字段指定类型为json_object。这就相当于你跟API签了协议这次我只要JSON别的烂杂碎我一概不要。三、 想用JSON ModeSystem Prompt必须得改这是JSON Mode里最大的一个坑。无数新手在这个地方翻车然后跑来跟我抱怨说这功能是骗人的。3.1 官方硬性要求System里必须出现JSON字样当你开启了json_object模式的时候大模型内部会做一次检查。它会去看你给的messages列表里有没有明确提到你要JSON输出。如果你只是闷头加了response_format但在System Prompt或者User提问里一个“JSON”字眼都没提。大模型会非常懵逼。它不知道你要啥格式的JSON是只要一个空对象{}还是要里面包一堆字段为了防止它摆烂输出个{}你必须在System Prompt里清清楚楚地告诉它你要的JSON长什么样。3.2 把之前的格式要求改成JSON Schema描述我们把上一篇的System Prompt拿来彻底改头换面一下。把Markdown的规矩换成JSON的规矩。system_prompt 你是一个企业层级里的时序数据运维专家。 你必须以合法的JSON格式输出你的分析结果。 JSON的Schema必须如下 { anomaly_detected: boolean, anomaly_time: string | 如果没异常则为空字符串, anomaly_metric: string | 指标名, anomaly_value: number | 异常数值, root_cause_guess: string | 推测的原因如果无法推断则填unknown, suggestion: string | 建议操作 } 严禁输出JSON以外的任何字符。不要用Markdown代码块包裹JSON。 你看这段System。首先第一句话我就点了题告诉它要合法JSON。然后我详细定义了Schema。我告诉它必须要有个anomaly_detected字段这是布尔值用来给下游脚本判断到底有没有异常。还要有anomaly_time等字符串字段。如果推断不出原因必须填unknown不能瞎编。最后那句“严禁输出JSON以外的任何字符”特别要紧。有些大模型手贱它虽然输出了JSON但它会在JSON前面加上json这种Markdown标记。这会导致json.loads()解析失败。必须在System里把这条路堵死。四、 动手改造代码从请求到解析一气呵成4.1 构造带response_format的Payload我们把整个请求的代码串起来。这次我们只关注核心逻辑假设数据已经拿到了。importrequestsimportjsondefask_for_json_analysis(real_data_text,api_key):urlhttps://ai.timecho.com/v1/chat/completionsheaders{Content-Type:application/json,Authorization:fBearer{api_key}}# 刚才定义的Systemsystem_prompt...messages[{role:system,content:system_prompt},{role:user,content:f分析数据\n{real_data_text}}]payload{model:timecho-model,messages:messages,response_format:{type:json_object}# 开启JSON Mode}try:responserequests.post(url,headersheaders,jsonpayload,timeout30)response.raise_for_status()resultresponse.json()# 拿到模型吐出来的内容现在它是个纯JSON字符串了content_strresult[choices][0][message][content]returncontent_strexceptExceptionase:returnNone这段代码发出去等它回来content_str里面装的就是纯粹的JSON没有任何杂质感。4.2 大模型吐出来的东西长啥样为了让你有个直观感受我贴一下大模型在JSON Mode下返回的真实内容{anomaly_detected:true,anomaly_time:2023-10-25 10:02:00,anomaly_metric:CPU,anomaly_value:89.5,root_cause_guess:内存泄漏导致频繁GC,suggestion:建议排查Java应用堆内存使用日志}你看干干净净。没有废话没有Markdown标题。这就是我们下游脚本最爱的食物。4.3 用json.loads解析并安全提取字段拿到这个字符串后我们就可以放心大胆地把它转成字典了。但是依然要做好兜底因为大模型有时候会漏字段。content_strask_for_json_analysis(my_data,my_key)ifcontent_str:try:# 把JSON字符串变成Python字典report_dictjson.loads(content_str)# 安全提取字段如果没给就默认给个兜底值is_anomalyreport_dict.get(anomaly_detected,False)metricreport_dict.get(anomaly_metric,未知)causereport_dict.get(root_cause_guess,unknown)print(f解析成功发现异常:{is_anomaly}, 指标:{metric}, 原因:{cause})exceptjson.JSONDecodeError:print(严重错误开了JSON Mode竟然还返回了非法JSON)else:print(请求失败)你看这解析逻辑是不是比写正则清爽一万倍我直接用.get()方法去取值不怕KeyError。就算大模型偶尔抽风少给个字段我的脚本也不会崩只会拿到个默认值继续往下跑。这就是结构化输出的底气。你的系统健壮性从经常崩溃变成了稳如老狗。五、 跑一下感受自动化对接的快乐5.1 根据解析出来的字段自动决定要不要发报警我们接着上面的代码往下写。既然拿到了结构化的report_dict我们就可以写业务逻辑了。# 如果检测到异常并且原因不是unknown就发高级报警ifis_anomaly:ifcause!unknown:send_p2_alert(metric,cause)# 发P2级报警else:send_p3_alert(metric)# 原因不明发P3级报警else:log_info(指标正常无需操作)你看这套逻辑是完全自动化的。不需要人去看报告不需要人去判断严重程度。脚本自己看了JSON自己做了决策自己发了报警。这才叫真正的AI赋能业务不好意思用了个禁止词汇划掉这才叫真正的把AI用在了刀刃上。大模型成了系统的一个计算节点而不是一个只会聊天的玩具。5.2 再也不用担心大模型多嘴多舌破坏格式了你回过头去想想如果没有JSON Mode你要写这个自动报警逻辑有多难你得写正则去判断“分析报告里有没有包含‘异常’两个字”。万一大模型说“未发现明显异常”正则匹配到了“异常”直接发了个假报警大半夜把你叫起来你气不气有了JSON Mode这个布尔值字段anomaly_detected是大模型自己算好的。它是True就是真异常是False就是没异常。黑就是黑白就是白没有任何歧义。这就叫确定性。在工程里确定性比啥都重要。六、 JSON Mode的局限性有时候它太死板了当然世间万物都是双刃剑。JSON Mode虽好但也有它难受的地方。6.1 不支持流式输出Stream这是目前JSON Mode最大的痛点。当你开启了response_format: json_object的时候你是不能同时开stream: True的。为什么你想想看流式输出是一个Token一个Token往外蹦的。但是在JSON没输出完之前你根本不知道它是不是合法的。比如它刚输出个{ key: val这就不是合法JSON。如果这时候流式推给你你根本没法解析。API网关为了保证最终给你的JSON一定是完整的它只能憋着。等大模型把所有的Token全想完了网关校验了一遍发现是个完整的JSON才一口气全返回给你。也就是说开了JSON Mode你就退回到最开始那种傻等的状态了。转圈圈的时间又回来了。这在需要用户体验的聊天界面上是不可接受的。所以通常的做法是如果是对接机器脚本没人在乎等待体验就开JSON Mode如果是做给人看的聊天窗口就开Stream不要JSON Mode。6.2 如果推理逻辑特别长JSON字段塞不下还有一个情况。大模型有时候推理过程很复杂需要想个几百字才能得出结论。但是在JSON Mode下它只能把推理过程硬塞到某个字符串字段里比如塞到root_cause_guess里。如果这个字段内容太长整个JSON就会很大。大模型有可能还没想完就触发了最大Token限制被强行截断了。一旦截断JSON就不完整了少了后半个大括号}json.loads()直接报错。这种截断导致的解析失败是JSON Mode里最容易翻车的地方。解决办法也很简单粗暴把最大输出Token设大点比如设成2048。给它留足余量确保它想完了也不会被截断。这个参数叫max_tokens在payload里加上就行。七、 去官方查文档确认支持情况7.1 确认是否兼容OpenAI的response_format规范JSON Mode 这个特性各大模型厂商支持的程度不一样。有些是原生的有些是套壳自己做的。你在用之前一定要去开发文档https://ai.timecho.com/docs/ 里仔细翻翻。看看官方有没有说response_format参数怎么用。如果文档里压根没提这茬那大概率是不支持。如果不支持你强行传这个参数过去API可能直接给你忽略掉还是给你返回Markdown你的解析代码就全废了。7.2 看看有没有更高级的 Structured Outputs 功能有些更先进的模型除了基础的json_object模式还支持一种叫 Structured Outputs 的功能。也就是说你可以直接把一个JSON Schema传给API。API不仅保证输出是合法JSON还保证输出一定符合你给的那个Schema。比如你要求某个字段必须是数字大模型就绝对不敢给你传个字符串进去。如果TimechoAI支持这种高级功能那你连System Prompt里写格式要求都可以省了。直接传Schema更省心。这个在文档里一般叫json_schema模式感兴趣的朋友可以自己去挖一挖。八、 总结与下期预告8.1 从人话到机语大模型真正成了系统的一环今天这篇我们完成了大模型输出形态的一次大跃迁。从自然语言Markdown变成了纯结构化JSON。这看似只是换了个格式但本质上是把大模型从“聊天机器人”变成了“计算服务”。聊天对象是人计算服务的对象是代码。一旦它能稳定吐出JSON我们的自动化闭环就彻底打通了。大模型查数据、出结论下游脚本读结论、发报警、执行动作。整条链路不见人工干预全靠代码驱动。这才是时序大模型在企业层级里真正该有的样子。8.2 下期我们讲讲怎么把Prompt也存进数据库做个配置化运营中心到今天为止我们的代码已经相当复杂了。System Prompt写了一大坨Few-shot样例也有一大坨。这些提示词全硬编码在Python文件里。如果哪天运维团队换了个人他想改一下报警的规矩他不会写Python他改不了代码这系统就僵住了。所以下期我们要把这些硬编码的提示词给拔出来。我们也把它们存进数据库做个配置化页面。让不懂代码的人也能调教大模型。我们下篇见。