feat(brain): add OUTPUT_LANGUAGE reply-language lock

Add an optional OUTPUT_LANGUAGE env var that forces every reply into a
single language. When set, output_language_directive() injects a "respond
only in <language>" instruction (also forbidding other scripts) into the
chat loop's system prompt, next to the existing TTS English-only lock.
Empty (default) keeps the multilingual "reply in the user's language"
behaviour, so upstream is unaffected.

For the Korean-only deployment this also suppresses the occasional trailing
CJK/Hanja fragment qwen2.5:3b leaks on free-form chit-chat.

- system_prompt.py: language-agnostic output_language_directive() helper
- engine.py: read OUTPUT_LANGUAGE, append directive in _build_initial_system_message
- docker-compose.yml + .env.example: document/pass the new var
- docs/llm_contexts.md: note the new gating on the main reply context
- tests: cover unset/set/agnostic/whitespace cases
This commit is contained in:
javis-bot
2026-06-12 21:08:44 +09:00
parent 40877b65b3
commit 006a32276a
6 changed files with 77 additions and 3 deletions

View File

@@ -5,10 +5,11 @@ Handles memory enrichment, tool planning and execution.
"""
from __future__ import annotations
import os
from typing import Optional, TYPE_CHECKING
from ..utils.redact import redact
from ..system_prompt import build_system_prompt
from ..system_prompt import build_system_prompt, output_language_directive
from ..tools.registry import run_tool_with_retries, generate_tools_description, generate_tools_json_schema, BUILTIN_TOOLS
from ..tools.builtin.stop import STOP_SIGNAL
from ..debug import debug_log
@@ -1441,6 +1442,14 @@ def run_reply_engine(db: "Database", cfg, tts: Optional[Any],
"Always respond in English regardless of the language the user speaks in."
)
# Deployment-level output-language lock (OUTPUT_LANGUAGE). When set,
# force every reply into that single language and forbid stray
# characters from other scripts. Empty (default) keeps the
# multilingual behaviour of replying in the user's own language.
_lang_directive = output_language_directive(os.environ.get("OUTPUT_LANGUAGE"))
if _lang_directive:
guidance.append(_lang_directive)
if warm_profile_block:
# Pre-query, query-agnostic user context. Lives OUTSIDE the
# conversation-history section because it isn't a history