# bucoid-heretic-27b 严格 system 模板修复（GPU 机 llama.cpp 服务）

> 日期：2026-09-26
> 机器：GPU 机 192.168.31.31
> 服务：`llama-bucoid-heretic.service`（`llama-server` @ 8080，模型 `bucoid-heretic-27b`）
> 触发来源：DSH 机酒馆（SillyTavern）联调时报 500

---

## 1. 症状

任何客户端（酒馆 / Open WebUI / 脚本）只要把 **system 消息放在非首位**，就收到 HTTP 500：

```
Chat Completion API
While executing CallExpression at line 106, column 32 in source: ...first %}
raise_exception('System message must be at the beginning.'
Error: Jinja Exception: System message must be at the beginning.
```

## 2. 根因

模型内嵌 Jinja 模板里有一段硬检查（`/props` 取回的 `chat_template` 第 104-107 行）：

```jinja
{%- for message in messages %}
    {%- if message.role == "system" %}
        {%- if not loop.first %}
            {{- raise_exception('System message must be at the beginning.') }}
        {%- endif %}
```

**只允许第 0 条是 system**，出现第二条就 500。这是 Qwen3.x 系模板的已知通病
（[llama.cpp #27367](https://github.com/ggml-org/llama.cpp/issues/27367)、
[unsloth/Qwen3.8-27B-GGUF 讨论](https://huggingface.co/unsloth/Qwen3.8-27B-GGUF/discussions/10)）。

**为什么酒馆必踩**：酒馆默认 `squash_system_messages = false`，会把「主提示 / 角色描述 / 性格 / 场景 / 世界书前后 / 对话示例」
作为**多条连续 system** 逐条发送 → 第 2 条就炸。世界书 at-depth 条目（角色写死默认 system）与 Author's Note 同理。

## 3. 修复（改 1 行模板 + 加 1 个 flag，不动模型文件）

```
/home/zyw/models/bucoid-heretic-27b/chat_template-fixed.jinja   ← 补丁模板（同目录另有 chat_template-orig.jinja 原件）
```

补丁内容（仅第 106 行；首条 system 仍由模板前面的分支渲染，故保留 `not loop.first` 守卫不会重复输出）：

```diff
     {%- if message.role == "system" %}
         {%- if not loop.first %}
-            {{- raise_exception('System message must be at the beginning.') }}
+            {{- '<|im_start|>system\n' + content + '<|im_end|>' + '\n' }}
         {%- endif %}
```

单元 `ExecStart` 末尾追加：

```
--chat-template-file /home/zyw/models/bucoid-heretic-27b/chat_template-fixed.jinja
```

```bash
sudo systemctl daemon-reload          # ⚠️ 改 ExecStart 后必须 daemon-reload
sudo systemctl restart llama-bucoid-heretic
```

## 4. 验证（改前全部 500 → 改后全部通过）

| 消息形态 | 改前 | 改后 |
|---|---|---|
| 2 条连续 system + user | ❌ 500 | ✅ 正常出正文 |
| system + user + 尾部 system | ❌ 500 | ✅ 正常出正文 |
| system + user + assistant + **中间** system（世界书 at-depth） | ❌ 500 | ✅ 且**正确用上注入的世界书内容** |
| 常规 1 条 system（回归） | ✅ | ✅ |
| `/health` + `/v1/models` | — | ✅ `n_ctx=65536`、`IQ4_XS`，重启后 5s 内就绪 |
| `ps -eo cmd` 确认进程带该 flag | — | ✅ |

> 该单元注释自己写的教训：**只验 `/health` 不够，必须实发一次生成请求**。本次已按此执行，输出内容与质量均正常。

## 5. 回滚（30 秒）

```bash
sudo cp /etc/systemd/system/llama-bucoid-heretic.service.bak-20260926-pre-template \
        /etc/systemd/system/llama-bucoid-heretic.service
sudo systemctl daemon-reload && sudo systemctl restart llama-bucoid-heretic
```

## 6. 注意与影响面

- **速度/显存不受影响**：只换模板来源，MTP、KV4（`-ctk/-ctv q4_0`）、64K 上下文、`--fit off` 等参数**全未改动**。
- **影响面是服务级的**：所有连 8080 的客户端一并受益（酒馆、Open WebUI、CLI 脚本），不必各自打补丁。
- **重启不回退补丁**：补丁在单元里，`systemctl restart` 依旧生效；只有还原单元文件才会回退。
- **该单元 `Restart=no`**，重启需手动；与 ComfyUI / LM Studio 的显存互斥关系不变。
- 若日后换模型（`switch-llm.sh` 切 ridge / ko-ridge 等），**这个模板文件只适配 bucoid**；
  别的模型若也报同样错误，需照此流程各自取模板打补丁。酒馆侧保留的
  `squash_system_messages=true` + `jailbreak/nsfw → user` 就是为这种情况留的保险。

## 7. 关联产物

- 详细报告：`dl-hub/55-SillyTavern酒馆部署/SillyTavern酒馆部署报告-20260926.md` §9 / §10
- 归档文件（`dl-hub/55-SillyTavern酒馆部署/`）：`chat_template-orig.jinja`、`chat_template-fixed.jinja`、`llama-bucoid-heretic.service.patched`
