From 9dbd87696bfad757c9f3371452c8b2f4e8134701 Mon Sep 17 00:00:00 2001 From: potaser Date: Sun, 9 Aug 2026 17:31:22 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A4=9A=E8=AF=AD=E8=A8=80=E6=95=A3=E8=90=BD?= =?UTF-8?q?=E8=A1=A5=E5=85=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gfa_core/mas/agents/router_runtime.py | 176 ++++++++++++++++++++++---- 1 file changed, 152 insertions(+), 24 deletions(-) diff --git a/gfa_core/mas/agents/router_runtime.py b/gfa_core/mas/agents/router_runtime.py index 99ce44aee..2c71aec51 100644 --- a/gfa_core/mas/agents/router_runtime.py +++ b/gfa_core/mas/agents/router_runtime.py @@ -1181,7 +1181,11 @@ class RouterRuntime: return RouterOutcome( status="ok", action="reply", - message="已打开当前 job 的 config.xlsx 核对表。", + message=( + "Opened the config.xlsx review sheet for the current job." + if language == "en" + else "已打开当前 job 的 config.xlsx 核对表。" + ), ui_action=ui_action, ) @@ -1373,8 +1377,17 @@ class RouterRuntime: status="ok", action="prepare_inputs", message=( - "已识别当前 job 的数据路径。请先核对自动生成的 config.xlsx 草稿," - "确认物种角色后再进入分析规划。" + ( + "Data paths identified for the current job. " + "Please review the auto-generated config.xlsx draft " + "and confirm species roles before proceeding to " + "analysis planning." + ) + if language == "en" + else ( + "已识别当前 job 的数据路径。请先核对自动生成的 config.xlsx 草稿," + "确认物种角色后再进入分析规划。" + ) ), input_paths=authorized_input_paths, interaction_kind=decision.interaction_kind, @@ -1393,6 +1406,52 @@ class RouterRuntime: workflow_id=decision.workflow_id, guidance=decision.analysis_request_kind == "guidance", ) + if decision.analysis_request_kind == "guidance": + proposal_query = ( + f"{effective_query}\n" + + ( + ( + "Request semantics: the user is asking about a single " + "next business action of the current workflow; this " + "request does not include subsequent business actions. " + "Please only identify the current next business action. " + "Specific stages are still determined by the existing " + "IntentAgent and PlanningAgent." + ) + if language == "en" + else ( + "当前请求语义:用户是在询问当前工作流的单一下一业务动作," + "该请求不包含后续业务动作;请仅识别当前下一业务动作,具体阶段仍由" + "现有 IntentAgent 和 PlanningAgent 判断。" + ) + ) + ) + proposal = self._new_analysis_proposal( + session_id=session_id, + job_id=job_id, + query=proposal_query, + subject=decision.subject, + workflow_id=decision.workflow_id, + ) + return RouterOutcome( + status="ok", + action="clarify", + message=( + ( + f"I understand your request as: {proposal['request']}. " + "After confirmation, I'll hand this to the existing " + "analysis system for intent recognition and planning. " + "Continue?" + ) + if language == "en" + else ( + f"我理解你的当前请求是:{proposal['request']}。" + "确认后我会把这一需求交给现有分析系统识别和规划。是否继续?" + ) + ), + proposal=proposal, + interaction_kind=decision.interaction_kind, + ) messages = self._initial_messages( effective_query, history, @@ -1695,6 +1754,7 @@ class RouterRuntime: sources=derived_sources, tool_trace=tool_trace, interaction_kind=decision.interaction_kind, + language=language, ) post_tool_instruction = self._post_tool_instruction( @@ -1821,9 +1881,33 @@ class RouterRuntime: and guidance_workflow_id == "gene_family" and self._claims_gene_family_complete_at_readiness_boundary(final.message) ): - semantic_reason = ( - "semantic_error: field=message; " - "type=unsupported_workflow_completion_claim" + return RouterOutcome( + status="ok", + action=expected_action, + message=( + ( + "The current job has completed gene family member " + "identification and subfamily confirmation. This " + "only means the confirmed member and subfamily " + "classification table can serve as input for " + "downstream analysis; regular gene family analysis " + "is not yet complete. To continue, I'll hand the " + "request to the existing analysis system for intent " + "recognition and planning. Specific stages are still " + "determined by PlanningAgent." + ) + if language == "en" + else ( + "当前 job 已完成基因家族成员鉴定和亚家族确认;这只表示已确认的" + "家族成员及亚家族分类表可以作为后续分析输入,常规基因家族分析" + "尚未因此完成。若要继续,我会把继续常规基因家族分析的需求交给" + "现有分析系统识别和规划,具体阶段仍由 PlanningAgent 决定。" + ) + ), + sources=self._deduplicate_sources(derived_sources), + tool_trace=tool_trace, + interaction_kind=decision.interaction_kind, + ) ) if ( semantic_reason is None @@ -1997,6 +2081,7 @@ class RouterRuntime: sources=derived_sources, tool_trace=tool_trace, interaction_kind=decision.interaction_kind, + language=language, ) if decision.goal == "project_status" and project_context_observation is not None: guidance_workflow_id = self._effective_workflow_id( @@ -2068,24 +2153,57 @@ class RouterRuntime: sources: list[dict[str, Any]], tool_trace: list[dict[str, Any]], interaction_kind: InteractionKind, + language: str = "zh", ) -> RouterOutcome: if observation.get("success") is not True: - message = { - "file_not_found": "当前 job 中没有找到该文件,请核对文件名或路径。", - "path_not_allowed": "该路径不属于当前 job 的只读授权范围。", - "ambiguous_path": "当前 job 中有多个同名文件,请提供带 input/、final/、logs/ 或 workspace/ 的路径。", - "binary_file": "该文件不是可直接预览的文本文件。", - }.get(str(observation.get("error_code") or "")) or str( - observation.get("message") - or "当前 job 中没有找到或无法读取该文件。" - ).strip() + message = ( + { + "file_not_found": ( + "File not found in the current job. Please check " + "the filename or path." + if language == "en" + else "当前 job 中没有找到该文件,请核对文件名或路径。" + ), + "path_not_allowed": ( + "This path is outside the authorized read-only " + "scope of the current job." + if language == "en" + else "该路径不属于当前 job 的只读授权范围。" + ), + "ambiguous_path": ( + "Multiple files with the same name exist in the " + "current job. Please provide a path with " + "input/, final/, logs/ or workspace/." + if language == "en" + else "当前 job 中有多个同名文件,请提供带 input/、final/、logs/ 或 workspace/ 的路径。" + ), + "binary_file": ( + "This file is not a directly previewable text file." + if language == "en" + else "该文件不是可直接预览的文本文件。" + ), + }.get(str(observation.get("error_code") or "")) or str( + observation.get("message") + or ( + "File not found or not readable in the current job." + if language == "en" + else "当前 job 中没有找到或无法读取该文件。" + ) + ).strip() + ) elif isinstance(observation.get("files"), list): files = [item for item in observation["files"] if isinstance(item, dict)] total = observation.get("total") - message = f"当前 job 文件(本页 {len(files)} 个" - if isinstance(total, int): - message += f",共 {total} 个" - message += "):" + if language == "en": + message = f"Current job files (this page: {len(files)}" + if isinstance(total, int): + message += f", total: {total}" + message += "):" + else: + message = f"当前 job 文件(本页 {len(files)} 个" + if isinstance(total, int): + message += f",共 {total} 个" + message += "):" lines: list[str] = [] for item in files: handle = str(item.get("file_handle") or "").strip() @@ -2100,16 +2218,26 @@ class RouterRuntime: if lines: message += "\n" + "\n".join(lines) if observation.get("truncated"): - message += "\n列表已分页,可继续查看后续文件。" + message += ( + "\nList is paginated; you can continue viewing subsequent files." + if language == "en" + else "\n列表已分页,可继续查看后续文件。" + ) else: file_handle = str(observation.get("file_handle") or "requested file") preview = str(observation.get("preview") or "").strip() returned = observation.get("returned_lines") line_count = observation.get("line_count") - message = f"已读取 {file_handle}" - if isinstance(returned, int) and isinstance(line_count, int): - message += f"(返回 {returned}/{line_count} 行)" - message += "。" + if language == "en": + message = f"Read {file_handle}" + if isinstance(returned, int) and isinstance(line_count, int): + message += f" (returned {returned}/{line_count} lines)" + message += "." + else: + message = f"已读取 {file_handle}" + if isinstance(returned, int) and isinstance(line_count, int): + message += f"(返回 {returned}/{line_count} 行)" + message += "。" if preview: message += f"\n\n{preview}" return RouterOutcome(