LLM Tool Call 的 JSON 自愈机制

做 LLM Tool Calling 时,一个很现实的问题是:

模型输出的 JSON,经常不是合法 JSON。

尤其是本地模型、量化模型、Ollama 系列,经常出现:

{"a":1,}

或者:

{
  "text": "hello
world"
}

甚至直接:

None

如果直接:

json.loads(raw_args)

那么 Tool Runtime 很容易:

HTTP 400
invalid tool call arguments

因此生产环境里,通常都会在 Tool Runtime 前增加一层:

LLM
 ↓
Repair Layer
 ↓
Tool Runtime

核心目标不是“严格正确”,而是:

尽可能修复,保证系统别崩。


修复流程

整个 repair pipeline 基本可以概括为:

1. 空值修复
2. 宽松解析
3. 结构修复
4. 字符修复
5. 最终兜底

1. 空值修复

处理:

""
"   "
None

统一转成:

{}

避免最基础的解析错误。


2. 宽松 JSON 解析(最核心)

核心代码:

json.loads(raw, strict=False)

这是整个 repair 的关键。

因为很多模型的问题,其实不是结构错误,而是字符串污染:

{
  "text": "hello
world"
}

strict=False 可以容忍:

  • 未转义换行

  • tab

  • control chars

然后再:

json.dumps(parsed)

重新序列化为标准 JSON:

{"text":"hello\nworld"}

本质上:

用“宽松解析 + 标准重编码”完成 JSON 标准化。


3. 结构修复

如果还是解析失败,就进入字符串级修复。

删除尾随逗号

修复:

{"a":1,}

变成:

{"a":1}

补全缺失括号

修复:

{"a":[1,2

自动补:

{"a":[1,2]}

删除多余闭合符

修复:

{"a":1}}}

循环删除尾部 }


4. 非法字符修复

很多模型会输出非法控制字符:

\r
\t
\x00

需要统一转义:

\n → \\n
\t → \\t

否则 JSON 仍然无法解析。


5. 最终兜底

如果所有修复都失败:

return "{}"

这里体现的是一种很典型的 AI Runtime 思路:

宁可丢参数,也不要让整个 Session 崩掉。


核心思想

传统系统默认:

输入是可信的

但 LLM 系统必须默认:

模型输出永远不可信

所以 Tool Calling 真正重要的,不只是 Function Calling,而是:

Repair Layer
Validation Layer
Retry Layer

否则:

json.loads()

就足以让整个 Runtime 不稳定。


一句话总结

这个 repair 函数本质上是在做:

“把 LLM 输出的半结构化脏数据,尽可能修复成可执行 JSON,并保证 Tool Runtime 永不因解析失败而崩溃。”


LLM Tool Call 的 JSON 自愈机制
https://blog.cikaros.cn/archives/llm-tool-call-de-json-zi-yu-ji-zhi
作者
Cikaros
发布于
2026年05月08日
许可协议