From e22da1879de016ef00f591904101bef5d13b9a58 Mon Sep 17 00:00:00 2001 From: potaser Date: Sun, 9 Aug 2026 11:38:03 +0800 Subject: [PATCH] Agent language switching --- apps/web/vue3/index_ml.html | 10 ++-- apps/web/vue3/main.py | 31 +++++++++++-- docs/agent-language-switching-plan.md | 66 +++++++++++++++++++++++++++ 3 files changed, 101 insertions(+), 6 deletions(-) create mode 100644 docs/agent-language-switching-plan.md diff --git a/apps/web/vue3/index_ml.html b/apps/web/vue3/index_ml.html index ef98e8dcd..936fd3ad9 100644 --- a/apps/web/vue3/index_ml.html +++ b/apps/web/vue3/index_ml.html @@ -7216,7 +7216,8 @@ RNA-seq KEGG/fgsea 会优先使用物种级 KEGG gene set;没有物种级缓 session_id: this.sessionId, job_id: window.parentJobInfo ? window.parentJobInfo.job_id : 'default_job', prefix: normalizedPrefix(window.parentParams ? window.parentParams.prefix : ''), - model: 'kimi' + model: 'kimi', + language: window.__CROPAI_LANG__ || 'zh' }) }); @@ -9548,7 +9549,8 @@ compare: [[flesh, peel]]`; include_previous: true, mode: 'comprehensive', use_rag_support: Boolean(options.useRagSupport), - refresh_rag: Boolean(options.refreshRag) + refresh_rag: Boolean(options.refreshRag), + language: currentLang.value }) }); const data = await res.json(); @@ -22548,7 +22550,8 @@ compare: [[flesh, peel]]`; model: selectedModel.value, session_id: currentConversationId.value || sessionId.value, job_id: jobInfo.job_id, - history: agentSession.messages.slice(-10) + history: agentSession.messages.slice(-10), + language: currentLang.value }); if (data.success && data.intent) { @@ -22647,6 +22650,7 @@ compare: [[flesh, peel]]`; job_id: jobInfo.job_id, // 传递当前 job_id 用于查询已完成阶段 prefix: normalizedPrefix(params.prefix || ''), history: agentSession.messages.slice(-10), + language: currentLang.value, intent: data.intent // 传递 chat 返回的 intent,避免重复调用 IntentAgentV2 }) }, 180000, '多智能体规划'); diff --git a/apps/web/vue3/main.py b/apps/web/vue3/main.py index f1f63a684..a01e5f74e 100644 --- a/apps/web/vue3/main.py +++ b/apps/web/vue3/main.py @@ -56,6 +56,8 @@ GF_MAS_ROOT = Path(os.environ.get("GF_MAS_ROOT", "/home/xzq/gene-family-mas-v2") if GF_MAS_ROOT.exists() and str(GF_MAS_ROOT) not in sys.path: sys.path.insert(0, str(GF_MAS_ROOT)) PLANTAI_RAG_ROOT = Path(os.environ.get("PLANTAI_RAG_ROOT", "/home/xzq/PlantAI-rag")).expanduser() + +DEFAULT_AGENT_LANG = "zh" if PLANTAI_RAG_ROOT.exists() and str(PLANTAI_RAG_ROOT) not in sys.path: sys.path.insert(0, str(PLANTAI_RAG_ROOT)) @@ -16427,6 +16429,7 @@ class AgentChatRequest(BaseModel): session_id: str = "" job_id: str = "" # 当前 job_id,用于查询已完成 stages history: list = [] + language: str = DEFAULT_AGENT_LANG # "zh" or "en" methodology_sandbox_registry_cache: Dict[str, str] = {} @@ -17139,6 +17142,11 @@ 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" @@ -17444,6 +17452,7 @@ async def _run_agent_chat( "history": list(request.history or []), "session_id": session_id, "job_id": request.job_id, + "language": request.language, } if router_goal_ledger is not None: payload["goal_ledger"] = router_goal_ledger @@ -18204,6 +18213,7 @@ class AgentExecuteRequest(BaseModel): model: str = "kimi" history: list = [] auto_execute: bool = True # 是否自动执行分析 + language: str = DEFAULT_AGENT_LANG # "zh" or "en" class AgentStatusResponse(BaseModel): """Agent状态响应""" @@ -18223,12 +18233,20 @@ async def agent_execute(request: AgentExecuteRequest, background_tasks: Backgrou 与/agent/chat不同,此接口会实际执行分析流程 """ task_id = f"agent_task_{int(time.time())}_{uuid.uuid4().hex[:8]}" - + + # Inject language instruction for non-Chinese languages + message = request.message + lang = getattr(request, "language", DEFAULT_AGENT_LANG) + if lang and lang != DEFAULT_AGENT_LANG: + lang_map = {"en": "Please respond in English."} + lang_instruction = lang_map.get(lang, f"Please respond in {lang}.") + message = f"{lang_instruction}\n\n{request.message}" + # 初始化任务状态 agent_tasks[task_id] = { "task_id": task_id, "status": "pending", - "message": request.message, + "message": message, "job_id": request.job_id, "prefix": request.prefix, "current_stage": "", @@ -18267,7 +18285,7 @@ async def agent_execute(request: AgentExecuteRequest, background_tasks: Backgrou # 提交任务到Celery队列 celery_task = execute_agent_task.delay( task_id=task_id, - message=request.message, + message=message, session_id=request.session_id or task_id, job_id=request.job_id, # 传入选中的 job_id history=request.history if request.history else [], @@ -18494,6 +18512,7 @@ class AgentPlanRequest(BaseModel): prefix: str = "" history: list = [] intent: dict = {} # 前端可传入已有的intent,避免重复调用IntentAgentV2 + language: str = DEFAULT_AGENT_LANG # "zh" or "en" SUBFAMILY_PREDICTION_PLAN_STAGES = [ @@ -20485,6 +20504,11 @@ async def agent_plan(request: AgentPlanRequest): """ try: 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}" methodology_plan, methodology_intent, methodology_params = _load_methodology_plan_for_agent_request(request) if methodology_plan == {}: return { @@ -23746,6 +23770,7 @@ class GenerateInterpretationRequest(BaseModel): strict_identity: bool = True use_rag_support: bool = False refresh_rag: bool = False + language: str = DEFAULT_AGENT_LANG # "zh" or "en" @app.post("/api/agent/interpret") async def generate_interpretation(request: GenerateInterpretationRequest): diff --git a/docs/agent-language-switching-plan.md b/docs/agent-language-switching-plan.md new file mode 100644 index 000000000..73244dba3 --- /dev/null +++ b/docs/agent-language-switching-plan.md @@ -0,0 +1,66 @@ +# Plan: Agent 跟随页面语言切换 + +## Context + +[页面已有中英文切换](apps/web/vue3/index_ml.html#L1250)(`currentLang` + `switchLang()`),但内嵌的 Agent 对话始终使用默认中文回复。需要在语言切换后将语言参数传递给后端,让 Agent 用对应语言回应。 + +## 受影响的 Agent 入口 + +**前端 fetch 调用([index_ml.html](apps/web/vue3/index_ml.html)):** + +| 调用点 | 行号 | 端点 | 用途 | +|--------|------|------|------| +| RAG Agent `sendMessage` | ~7211 | `/api/agent/execute` | RAG Agent 组件对话 | +| `requestAgentChatWithProgress` | ~22179 | `/api/agent/chat/stream` | 主 Agent 流式对话 | +| `showInterpretationModal` | ~9533 | `/api/agent/interpret` | 深度解读报告 | + +**后端 Request 模型([main.py](apps/web/vue3/main.py)):** + +| 模型 | 行号 | 对应端点 | +|------|------|----------| +| `AgentChatRequest` | 16424 | `/api/agent/chat`, `/api/agent/chat/stream` | +| `AgentExecuteRequest` | 18198 | `/api/agent/execute` | +| `GenerateInterpretationRequest` | 23733 | `/api/agent/interpret` | + +(`AgentPlanRequest` 18489 和 `AgentRunRequest` 22814 也可顺带加上,但影响较小。) + +## 实现方案 + +### 前端(1 个文件) + +在每个 Agent fetch payload 中加一行: +```js +language: currentLang.value // "zh" 或 "en" +``` + +位置: +1. `sendMessage()` body ~7215 +2. `requestAgentChatWithProgress()` 的 payload ~22546 +3. `showInterpretationModal()` body ~9535 + +### 后端(1 个文件) + +**Step 1** — 三个 Request 模型各加一个字段: +```python +language: str = "zh" # "zh" or "en" +``` + +**Step 2** — 在核心处理函数中注入语言指令。当 `language == "en"` 时,在用户消息前拼接指令: + +- `_run_agent_chat()` (~17131):在传给 RouterAgent 的 `payload["query"]` 前拼接英文指令 +- `agent_execute()` (~18218):在 `request.message` 存入 task 之前拼接,或存入 task state 供后续 LLM 调用使用 +- `generate_interpretation()` (~23751):在构造 LLM 提示词时根据 `request.language` 追加语言指令 + +## 修改文件 + +| 文件 | 改动数 | +|------|--------| +| [index_ml.html](apps/web/vue3/index_ml.html) | ~3 行新增 | +| [main.py](apps/web/vue3/main.py) | ~3 个 model 字段 + ~3 处语言注入逻辑 | + +## 验证 + +1. 启动服务,页面切换到英文 +2. 在主 Agent 输入框发送消息,确认 Agent 用英文回复 +3. 切回中文,发送消息,确认恢复中文回复 +4. 测试 RAG Agent 组件和深度解读同样生效