打开网易新闻 查看精彩图片

去年Q3,Lovai的翻译功能挂了47分钟。不是模型翻得烂,是三个反引号把整件事搞砸了。

用户刷着日料帖子,突然满屏空白。监控报警炸锅,根因定位花了11分钟——模型"贴心"地给JSON包了一层Markdown代码围栏。```json开头,```结尾。JSON.parse()当场窒息,整个翻译链路陪葬。

这事发生在OpenRouter上。我用它跑日英双向翻译,处理用户生成的菜谱标题、摘要和多段正文。生产环境跑了大半年,第一次真正理解什么叫"结构化输出"的脆弱性。

本文按故障发生顺序,还原我堆的三层防御。JSON格式问题是主战场,重试降级和语言检测顺带提,但工程时间大头全花在让JSON不崩上。

第一层:response_format是底线,不是可选项

第一层:response_format是底线,不是可选项

最初实现根本没开JSON Mode。系统提示写得很随意:"请返回JSON格式"。没加response_format参数,纯靠模型自觉。

这事在轻负载时跑得挺顺。直到某天模型静默更新,或者OpenRouter流量洪峰触发了某种行为漂移——没人知道具体是哪边变的——响应突然开始带代码围栏。

实际返回长这样:

```json
{"__title": "Built Translation with OpenRouter"}
```

前端拿到这串东西,JSON.parse()直接抛异常。用户看到空白块,以为是翻译失败,其实是解析失败。质量差还能忍,功能下线忍不了。

修复第一步:强制加response_format: { type: 'json_object' }。这行代码约束模型输出合法JSON,去掉Markdown包装。OpenRouter文档里写得明白,但不是所有模型都支持,得去模型详情页查兼容性矩阵。

关键认知:生产环境跑LLM输出进JSON.parse(),不开json_objectjson_schema等于裸奔。提示词指令会在模型更新或服务负载变化时无声断裂,你甚至收不到告警。

第二层:response-healing是补丁,不是万能药

第二层:response-healing是补丁,不是万能药

打开网易新闻 查看精彩图片

开了JSON Mode之后,我以为高枕无忧。两周后又崩了一次,这次是被max_tokens截断的半截JSON。

OpenRouter有个叫response-healing的插件,自动修复格式问题。接入方式很直白:

plugins: [{ id: 'response-healing' }]

它和json_object/json_schema叠加使用,能处理不少边缘情况。但文档里埋了两个限制:只支持非流式响应,且修不了token上限导致的截断。

第二次故障就是踩了第二个坑。用户发了一篇超长菜谱,正文拆成6个block,模型翻完第4个就撞墙了。返回的JSON缺了右大括号和后半截数组,response-healing摇头说修不了。

这个插件是保险丝,不是发电机。它能兜住格式漂移,兜不住资源硬限制。我后来给长内容加了分段策略,但那是另一个话题。

第三层:应用层解析器是最后一张底牌

第三层:应用层解析器是最后一张底牌

即便叠了JSON Mode和response-healing,我还是在应用层留了防御性解析器。代码不复杂,但逻辑很硬:

遇到不完整或畸形数据,直接拒绝,不进业务逻辑。不尝试"智能修复",不猜用户意图,不返回半成品。失败就触发降级流程,走备用模型或标记人工复核。

这个设计来自第三次惊吓。某次OpenRouter的API响应头里Content-Type是对的,但body里混进了调试日志的HTML片段。概率极低,但一旦发生,没有这层过滤就会污染数据库。

三层防御的优先级很明确:协议层约束(response_format)→ 服务层修复(response-healing)→ 应用层熔断(自定义解析器)。越往上成本越低,越往下兜底能力越强。

整个系统现在跑在日英双向翻译上,日均处理1.2万条用户内容。JSON解析失败率从早期的3.7%压到0.02%,剩下的全是模型不支持结构化输出的边缘机型,正在逐步下线。

最后留个细节:response-healing的修复日志默认不开,得在OpenRouter dashboard里手动启用审计。我第一次排查故障时,花了20分钟才发现这个开关。现在它是我检查列表的第二项,排在"确认模型支持JSON Mode"之后。

你的生产环境里,LLM输出进解析器之前,有几层过滤?