From b8dcb63fa150c648a577d3ad62db754e02eceba7 Mon Sep 17 00:00:00 2001 From: wtx Date: Sun, 9 Aug 2026 16:03:13 +0800 Subject: [PATCH] fix: preserve raw intent across language switching --- apps/web/vue3/main.py | 5 --- gfa_core/mas/agents/router.py | 1 + gfa_core/mas/agents/router_runtime.py | 9 +++++ tests/unit/test_agent_chat_actions.py | 35 +++++++++++++++++++ .../test_router_analysis_handoff_boundary.py | 20 +++++++++++ 5 files changed, 65 insertions(+), 5 deletions(-) diff --git a/apps/web/vue3/main.py b/apps/web/vue3/main.py index a8b89e68d..afb151ee4 100644 --- a/apps/web/vue3/main.py +++ b/apps/web/vue3/main.py @@ -17141,11 +17141,6 @@ async def _run_agent_chat( executes analysis stages here. """ session_id = request.session_id or f"session_{int(time.time())}" - # Inject language instruction for non-Chinese languages - if request.language and request.language != DEFAULT_AGENT_LANG: - lang_map = {"en": "Please respond in English."} - lang_instruction = lang_map.get(request.language, f"Please respond in {request.language}.") - request.message = f"{lang_instruction}\n\n{request.message}" completed_stages: list[str] = [] if request.job_id: final_dir = PROJECT_ROOT / "workspace" / request.job_id / "final" diff --git a/gfa_core/mas/agents/router.py b/gfa_core/mas/agents/router.py index 36875fdfa..2bc665706 100755 --- a/gfa_core/mas/agents/router.py +++ b/gfa_core/mas/agents/router.py @@ -54,6 +54,7 @@ class RouterAgent(BaseAgent): "history": history, "session_id": str(payload.get("session_id") or state.session_id), "job_id": str(payload.get("job_id") or state.job_id), + "language": str(payload.get("language") or "zh"), } if "goal_ledger" in payload: runtime_context["goal_ledger"] = payload.get("goal_ledger") diff --git a/gfa_core/mas/agents/router_runtime.py b/gfa_core/mas/agents/router_runtime.py index f0412fee0..99ce44aee 100644 --- a/gfa_core/mas/agents/router_runtime.py +++ b/gfa_core/mas/agents/router_runtime.py @@ -1174,6 +1174,7 @@ class RouterRuntime: session_id: str, job_id: str, goal_ledger: GoalLedger | None = None, + language: str = "zh", ) -> RouterOutcome: ui_action = self._config_dialog_ui_action(query) if ui_action is not None: @@ -1399,6 +1400,7 @@ class RouterRuntime: job_id, decision, goal_ledger, + language, ) definitions = self._definitions_for_route( decision, @@ -2127,6 +2129,7 @@ class RouterRuntime: job_id: str, decision: RouteDecision, goal_ledger: GoalLedger | None, + language: str = "zh", ) -> list[LLMMessage]: normalized = [] for item in history: @@ -2150,6 +2153,12 @@ class RouterRuntime: f"\nTrusted locked route context: {route_context}." f"\nCurrent session_id={session_id!r}; current job_id={job_id!r}." ) + if str(language or "zh").strip().lower() == "en": + context += ( + "\nRespond to the user in English. Treat the language preference as " + "presentation metadata; do not rewrite the user query or pass the " + "language instruction to intent-recognition tools." + ) if goal_ledger is not None: ledger_context = json.dumps( goal_ledger.model_dump(mode="json", round_trip=True), diff --git a/tests/unit/test_agent_chat_actions.py b/tests/unit/test_agent_chat_actions.py index 18fd49984..962c9063c 100644 --- a/tests/unit/test_agent_chat_actions.py +++ b/tests/unit/test_agent_chat_actions.py @@ -92,6 +92,7 @@ async def test_agent_chat_maps_router_actions_and_passes_context( "history": history, "session_id": "session-7", "job_id": "job-7", + "language": "zh", } ] assert result["success"] is True @@ -108,6 +109,40 @@ async def test_agent_chat_maps_router_actions_and_passes_context( assert result["intent"]["stages"] == [] +@pytest.mark.asyncio +async def test_agent_chat_keeps_english_language_separate_from_business_query( + monkeypatch, + web_main, +): + captured = [] + _install_router( + monkeypatch, + { + "status": "ok", + "action": "reply", + "message": "I can help with that.", + "intent": None, + "sources": [], + "tool_trace": [], + }, + captured, + ) + request = web_main.AgentChatRequest( + message="执行接下来的分析", + language="en", + session_id="english-business-query", + job_id="job-english-business-query", + history=[], + ) + + result = await web_main.agent_chat(request) + + assert result["intent"]["message"] == "I can help with that." + assert request.message == "执行接下来的分析" + assert captured[0]["query"] == "执行接下来的分析" + assert captured[0]["language"] == "en" + + @pytest.mark.asyncio async def test_agent_chat_persists_router_proposal_without_analysis_intent( monkeypatch, diff --git a/tests/unit/test_router_analysis_handoff_boundary.py b/tests/unit/test_router_analysis_handoff_boundary.py index 36fef7f98..c92c34069 100644 --- a/tests/unit/test_router_analysis_handoff_boundary.py +++ b/tests/unit/test_router_analysis_handoff_boundary.py @@ -143,3 +143,23 @@ def test_new_analysis_decision_discards_main_agent_workflow_and_clarification() assert normalized.workflow_id is None assert normalized.clarification_question is None assert normalized.analysis_request_kind == "execution" + + +def test_router_response_language_does_not_rewrite_business_query() -> None: + query = "执行接下来的分析" + messages = RouterRuntime._initial_messages( + query=query, + history=[], + session_id="english-router-session", + job_id="job-english-router", + decision=RouteDecision( + goal="general", + needs=[], + subject=query, + ), + goal_ledger=None, + language="en", + ) + + assert messages[-1].content == query + assert "Respond to the user in English." in messages[0].content