LM Studio 报 Unknown StringValue filter: safe?一个脚本原地修复 GGUF

本文最后更新于 2026年5月20日 晚上

下载了个新模型,LM Studio 一跑就报错。折腾半天,最后发现只需要改一个单词。

翻车现场

最近在模型服务器上部署 Qwen3.6-35B-A3B-Uncensored,模型下好了,LM Studio 加载也正常,结果一发消息就炸了:

1
2
AI_ProviderSpecificError: Unknown StringValue filter: safe
Error rendering prompt with jinja template: "Unknown StringValue filter: safe"

模型明明没问题,为什么连 prompt 都渲染不了?

根因定位

GGUF 文件里内嵌了一段 Jinja 模板(chat_template),负责把对话历史转成模型能理解的文本格式。问题出在这行:

1
{%- set args_value = args_value | string if args_value is string else args_value | tojson | safe %}

模型作者用了 | safe 这个 Jinja2 过滤器。| safe 在 Web 开发里很常见——告诉模板引擎”这段 HTML 别转义”。但在 LLM prompt 渲染里完全没必要。

关键是:LM Studio 用的精简版 Jinja 引擎不支持 | safe 直接就报错了。

这不是模型的 bug,是 LM Studio 的兼容性问题。

三条路,两条走不通

❌ 方案一:改缓存

LM Studio 会把模型元数据缓存到 gguf-metadata-cache.json。我直接改这个文件,删掉 | safe

有效——但重启 LM Studio 后缓存重建,问题复现。

❌ 方案二:model.yaml 覆盖

LM Studio 支持 model.yaml 配置文件,理论上可以覆盖内置模板。

实际:写进去了,LM Studio 压根不认。可能是版本问题。

✅ 方案三:直接改 GGUF 文件

最暴力,也最彻底。但 19GB 的文件,全读全写?

关键发现:| safe| trim 都是 6 个字符。

等长替换,不需要移动任何数据,直接在二进制里原地改就行。文件大小不变,GGUF 结构不变。

为什么用 | trim 替换

| 对比项 | | safe | | trim |
|——–|———-|———-|
| 字符数 | 6 | 6 |
| Jinja 标准 | ✅ | ✅ |
| LM Studio 支持 | ❌ | ✅ |
| 对 JSON 输出的影响 | 标记不转义 | 去首尾空白 |

| trim 去除字符串首尾空白,对 tojson 的输出不会有任何副作用。完美的等价替换。

修复脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#!/usr/bin/env python3
"""
修复 GGUF 文件中的 chat_template - 将 | safe 替换为 | trim
等长替换,不改变文件大小,直接原地修改。
"""
import sys
import os
import struct

def main():
if len(sys.argv) < 2:
print("Usage: python3 fix_gguf_safe.py <gguf_file>")
sys.exit(1)

gguf_path = sys.argv[1]

if not os.path.exists(gguf_path):
print(f"Error: File not found: {gguf_path}")
sys.exit(1)

print(f"Reading: {gguf_path}")
print(f"File size: {os.path.getsize(gguf_path) / (1024**3):.2f} GB")

# 读取整个文件到内存
with open(gguf_path, 'rb') as f:
data = bytearray(f.read())

# 定位 tokenizer.chat_template 键
key_marker = b'tokenizer.chat_template'

try:
key_idx = data.index(key_marker)
except ValueError:
print("Error: tokenizer.chat_template not found in file")
sys.exit(1)

# 解析 GGUF 结构,找到 chat_template 的值区域
# GGUF 结构: [key_length(8B)][key][value_type(4B)][value_length(8B)][value]
key_len = struct.unpack('<Q', bytes(data[key_idx-8:key_idx]))[0]
type_off = key_idx + key_len
val_len_off = type_off + 4
val_len = struct.unpack('<Q', bytes(data[val_len_off:val_len_off+8]))[0]
val_start = val_len_off + 8
val_end = val_start + val_len

# 读取原始模板
original_template = bytes(data[val_start:val_end]).decode('utf-8')
print(f"chat_template: offset={val_start}, length={val_len}")

# 检查是否需要修复
safe_count = original_template.count('| safe')
if safe_count == 0:
print("✅ Template is already clean (no | safe found)")
return

print(f"Found {safe_count} occurrence(s) of '| safe', replacing with '| trim'...")

# 等长替换 | safe → | trim
fixed_template = original_template.replace('| safe', '| trim')
fixed_bytes = fixed_template.encode('utf-8')
original_bytes = original_template.encode('utf-8')

# 安全检查:长度必须一致
if len(fixed_bytes) != len(original_bytes):
print(f"ERROR: Length mismatch! Original: {len(original_bytes)}, Fixed: {len(fixed_bytes)}")
sys.exit(1)

# 原地替换
data[val_start:val_end] = fixed_bytes

# 写回文件
with open(gguf_path, 'wb') as f:
f.write(data)

print(f"✅ Successfully patched: {gguf_path}")
print(f" Replaced {safe_count} occurrence(s) of '| safe' → '| trim'")

if __name__ == '__main__':
main()

使用方法

1
2
3
4
5
6
7
8
# 1. 运行修复脚本
python3 fix_gguf_safe.py /path/to/your/model.gguf

# 2. 清理 LM Studio 缓存(重要!)
rm -f ~/.lmstudio/.internal/gguf-metadata-cache.json

# 3. 重启 LM Studio
systemctl restart lmstudio

实际效果

1
2
3
4
5
6
Reading: Qwen3.6-35B-A3B-Uncensored-Q4_K_M.gguf
File size: 19.71 GB
chat_template: offset=10934766, length=7764
Found 1 occurrences of '| safe', replacing with '| trim'...
✅ Successfully patched!
Replaced 1 occurrence(s) of '| safe''| trim'

19GB 的文件,不到一分钟搞定。

验证

1
2
3
4
5
6
7
# 确认没有残留的 | safe
strings model.gguf | grep -o '| safe' | wc -l
# 输出: 0

# 确认 | trim 已替换
strings model.gguf | grep 'tojson | trim'
# 输出: ... args_value | tojson | trim ...

以后怎么预防

下载新模型后先查一下:

1
strings model.gguf | grep '| safe' && echo "⚠️ 需要修复" || echo "✅ 兼容"

或者优先选择 lmstudio-community 发布的模型版本——他们已经做过兼容性处理。

总结

方案 效果 持久性
改缓存 有效但不稳定 ❌ 重启失效
model.yaml 不一定生效 ⚠️ 看版本
改 GGUF 彻底解决 ✅ 永久

本质就是:模型作者用了一个 LM Studio 不支持的 Jinja 过滤器。等长字符串替换,原地改一个单词,问题消失。


参考资料GGUF 格式规范 · Jinja2 模板引擎 · LM Studio 官方文档


LM Studio 报 Unknown StringValue filter: safe?一个脚本原地修复 GGUF
https://www.normdist.com/2026/05/20/ND-20260520-002-lmstudio-gguf-safe-filter-fix/
作者
小瑞
发布于
2026年5月20日
许可协议