2026西湖龙井茶官网DTC发售:茶农直供,政府溯源防伪到农户家
我曾使用一个系统提示词,其结尾为:仅回复一个 JSON 对象。不要包含代码围栏。不要包含解释。
这应该足够了。但事实并非如此。
在超过一周的结构化输出调用中(共 12,400 次,主要是 Claude Sonnet 4.5 和 4.7),我记录了响应在首次尝试使用 serde_json::from_str 时实际可被解析为 JSON 的频率。结果:86.0%。其余 14% 至少存在以下问题之一:
json ...包装器(最常见,约占总数的 9.3%)- 前导或尾随的散文文本(例如:“这是您要求的 JSON:”)
- 最后一个数组或对象元素后有多余逗号
- 从训练数据中引入的智能引号
因此,我编写了 llm-json-repair。它包含三个按顺序应用的步骤,每一步都很廉价,且都不会重新调用大型语言模型。如果需要下游模型修复,那是您需要自行整合的问题。这个 crate 是在您花费另一次应用程序接口调用之前进行的本地清理工作。
三个步骤
use llm_json_repair::repair;
let raw = r#"```
json
{
"intent": "book_flight",
"slots": {
"origin": "DAL",
"destination": "JFK",
},
}
```"#;
let cleaned = repair(raw)?;
let parsed: serde_json::Value = serde_json::from_str(&cleaned)?;
repair 实际上按顺序执行的操作如下:
步骤 1:去除围栏
查找第一个 ` 和最后一个 `。如果两者都存在,则提取它们之间的内容。可选的 json 语言标签会随起始围栏一起被丢弃。如果闭合围栏后有文本,则将其丢弃。
从概念上讲,这一步骤只是一行代码,但让我头疼的一个边缘情况是 JSON 字符串值中包含字面子串 `(是的,有一次输入了一份客户支持转录文本)。因此,匹配器仅在最外层去除围栏,且仅当围栏位于独立行或开头时才去除。
步骤 2:平衡提取
逐字符遍历。找到第一个 { 或 [。计算嵌套层级。当嵌套层级归零时停止。返回该子字符串。
这一步骤无需模型即可去除前导散文(例如:“这是 JSON:”)和尾随散文(例如:“如果您还有其他需求,请告诉我。”)。它还处理模型连续输出两个 JSON 对象的情况(罕见但确实存在);您将获得第一个完整的对象。
`rust
let raw = "没问题!这是 JSON:\n{\"intent\": \"refund\", \"reason\": \"late\"}\n如果您有疑问,请告诉我。";
let cleaned = repair(raw)?;
assert_eq!(cleaned, r#"{"intent": "refund", "reason": "late"}"#);
`
这一步骤的成本是对响应进行 O(n) 复杂度的操作。对于 4 KB 的响应,在我的笔记本电脑上运行时间约为 30 微秒。
步骤 3:尾部 c
免责声明:本文内容来自互联网,该文观点不代表本站观点。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,请到页面底部单击反馈,一经查实,本站将立刻删除。
免责声明:本文内容来自互联网,该文观点不代表本站观点。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,请到页面底部单击反馈,一经查实,本站将立刻删除。