本文最后更新于 2026年7月12日 晚上
下载了个新模型,LM Studio 一跑就报错。折腾半天,最后发现只需要改一个单词。
翻车现场
最近在模型服务器上部署 Qwen3.6-35B-A3B-Uncensored,模型下好了,LM Studio 加载也正常,结果一发消息就炸了:
1 2 3 4 5 6 7
| 1 2
AI_ProviderSpecificError: Unknown StringValue filter: safe Error rendering prompt with jinja template: "Unknown StringValue filter: safe"
|
模型明明没问题,为什么连 prompt 都渲染不了?
根因定位
GGUF 文件里内嵌了一段 Jinja 模板(chat_template),负责把对话历史转成模型能理解的文本格式。问题出在这行:
1 2 3 4 5
| 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 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
| 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
""" 修复 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())
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)
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'...")
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 9 10 11 12 13 14 15 16 17 18 19
| 1 2 3 4 5 6 7 8
python3 fix_gguf_safe.py /path/to/your/model.gguf
rm -f ~/.lmstudio/.internal/gguf-metadata-cache.json
systemctl restart lmstudio
|
实际效果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| 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 8 9 10 11 12 13 14 15 16 17
| 1 2 3 4 5 6 7
strings model.gguf | grep -o '| safe' | wc -l
strings model.gguf | grep 'tojson | trim'
|
以后怎么预防
下载新模型后先查一下:
1 2 3 4 5
| 1
strings model.gguf | grep '| safe' && echo "⚠️ 需要修复" || echo "✅ 兼容"
|
或者优先选择 lmstudio-community 发布的模型版本——他们已经做过兼容性处理。
总结
方案
效果
持久性
改缓存
有效但不稳定
❌ 重启失效
model.yaml
不一定生效
⚠️ 看版本
改 GGUF
彻底解决
✅ 永久
本质就是:模型作者用了一个 LM Studio 不支持的 Jinja 过滤器。等长字符串替换,原地改一个单词,问题消失。
参考资料:GGUF 格式规范 · Jinja2 模板引擎 · LM Studio 官方文档