GLM-5系モデル(zai-orgのGLM-5、GLM-5.1等)を使っていると、会話の途中で出力が突然中国語に切り替わることがある。日本語で会話を始め、何ターンか問題なく進んだあと、あるターンで返ってくるのが完全に中国語になる——こういうバグだ。
システムプロンプトで「日本語で回答せよ」と指示すれば頻度は下がるが、ゼロにはならない。そして最も厄介なのは、その「中国語になる問題の報告すら中国語で出力される」という再帰的な皮肉だ。
ソフトガードの限界
最初に試したのは、Hermes Agentのenvironment_hint設定で、システムプロンプトに「Always respond in Japanese. Never output in Chinese unless the user explicitly requests it.」という明示的な指示を埋め込む方法だ。
ゲートウェイを再起動して設定を反映し、「反映を確認して」と依頼した。返ってきたのはこういう回答だった。
已确认。所有项目均已正常。 ① config.yaml 的值 ✅ ② 网关重启 ✅ — Telegram 连接完成
environment_hintを設定した直後のセッションで、中国語が出力されている。プロンプトへの指示は「ドアに『立ち入り禁止』と書札を貼る」ようなものだ。礼儀正しい人は読むが、バグは読まない。
発想の転換:内側から外側へ
ここで必要なのは、プロンプトの改善ではなく、アーキテクチャの変更だ。モデルの内部挙動(なぜ中国語に切り替わるのか)に依存せず、外側のレイヤーで「ユーザーに届く前に日本語であることを保証する」仕組みが必要になる。
設計したのは3層構造のLanguage Guardだ。
第1層:Unicode範囲分析による検出
中国語と日本語は、どちらもCJK統合漢字(U+4E00–U+9FFF)を使う。だが決定的な違いがある。日本語には必ず仮名が含まれる。ひらがな(U+3040–U+309F)かカタカナ(U+30A0–U+30FF)のどちらかだ。中国語には含まれない。
この性質を利用して、テキスト中のCJK漢字数に対する仮名の比率を計算する。漢字が一定数以上あり、仮名の比率が閾値を下回れば、中国語と判定できる。
def _detect_chinese_ratio(text):
cjk = kana = 0
for ch in str(text):
cp = ord(ch)
if 0x4E00 <= cp <= 0x9FFF: cjk += 1
elif 0x3040 <= cp <= 0x30FF: kana += 1
if cjk == 0: return 0.0
kana_ratio = kana / (cjk + kana + 1)
if cjk >= 10 and kana_ratio < 0.05: return 1.0
if cjk >= 5 and kana_ratio < 0.10: return 0.8
return 0.0
この判定は純粋に数値的な操作で、モデルのAPIを追加で呼ぶ必要がない。コストゼロだ。
第2層:非GLMモデルによる書き換え
中国語と判定されたテキストは、別のモデルに渡して日本語に書き換える。ここで重要なのは、原因となったモデルと同じ系統のモデルを使わないことだ。GLMが中国語に切り替わるなら、書き換えにはGoogleのモデル(Gemini Flash)を使う。失敗時はOpenRouter経由で別系統のモデルにフォールバックする。
第3層:パイプラインへの統合
Hermes Agentのゲートウェイには、チャットサーフェスへメッセージを送る直前に呼ばれる_sanitize_gateway_final_responseという関数がある。この関数の最後、ユーザーへ送信する直前のreturnの前に、Language Guardの呼び出しを挟む。
# 既存の最終サニタイズ処理の後、returnの直前
if _looks_like_gateway_provider_error(redacted):
return _gateway_provider_error_reply(redacted)
# 硬い言語ガード: 中国語出力を日本語に書き換え
redacted = _language_guard(redacted)
return redacted
これにより、モデルがどれだけ中国語に逃げても、最終的にユーザーに届くテキストは日本語であることが保証される。プロンプトへの信任ではなく、構造への信任だ。
パッチの永続性
この種のゲートウェイパッチは、エージェントフレームワークのアップデート(hermes update等)で上書きされる。そのため、パッチはスクリプト化し、冪等性(既に適用済みかチェックしてから適用)を保証した上で、リポジトリに保存しておく。アップデート後に再実行すれば、常に最新のゲートウェイコードに対してパッチが再適用される。
設計原則としての「硬い壁」
このアプローチは、LLMエージェントに限らない普遍的な設計原則を含んでいる。
- 意図ではなく構造を信頼する。システムプロンプトで「中国語を出力するな」と指示するのは、コードに「バグを書くな」とコメントするようなものだ。守れない人間(モデル)は守れない。代わりに、出力が通るパイプラインの最後に関所を設ける。
- 検出は軽量に、書き換えは別系統で。検出ロジックはコストゼロの数値演算(Unicode範囲分析)に徹し、重い処理(LLM呼び出し)は中国語と判定された場合のみ実行する。書き換えに使うモデルは、原因モデルと同じバグを共有しない別系統にする。
- 中間プロセスの言語は妥協する。モデルのthinkingや推論プロセスが中国語で行われるのは許容する。ユーザーに見えるのは最終成果物だけだ。完璧主義を捨て、実用主義で線を引く。
GLM-5系モデルは、コストパフォーマンスに優れた強力なモデルだ。コーディングタスクや複雑な推論で優れた結果を出す。だが、中国語への言語切り替えという既知のバグがあり、それがユーザー体験を著しく損なう。
モデルを変えることもできる。だが、優秀なモデルをバグ一つで手放すのはもったいない。必要なのは、そのバグをアーキテクチャで囲い込むことだ。Language Guardは、モデルの長所を活かしながら短所を構造的に補う一つの実践例である。