多语言散落补全

This commit is contained in:
potaser
2026-08-10 00:31:32 +08:00
parent 87c158efe5
commit fe5f7b9d6e
5 changed files with 434 additions and 176 deletions
+29 -15
View File
@@ -390,48 +390,57 @@ class DiagnosisAgent(BaseAgent):
ERROR_PATTERNS = {
"missing_input_file": {
"patterns": [r"文件不存在", r"FileNotFoundError", r"No such file", r"找不到文件"],
"suggestion": "检查输入文件路径是否正确,确保文件已上传到input目录",
"suggestion_zh": "检查输入文件路径是否正确,确保文件已上传到input目录",
"suggestion_en": "Check if input file path is correct; ensure files are uploaded to the input directory",
"auto_fix": False
},
"hmm_not_found": {
"patterns": [r"required_pfams is empty", r"No HMM file", r"找不到HMM"],
"suggestion": "请上传HMM模型文件或指定--required-pfams参数",
"suggestion_zh": "请上传HMM模型文件或指定--required-pfams参数",
"suggestion_en": "Please upload HMM model file or specify --required-pfams parameter",
"auto_fix": False
},
"blast_db_error": {
"patterns": [r"BLAST Database error", r"makeblastdb failed"],
"suggestion": "Identify BLAST 固定用目标物种全蛋白 FASTA 建库;请优先检查目标全蛋白 FASTA 是否存在、非空且为有效蛋白FASTA格式,再检查参考家族蛋白 query 文件。",
"suggestion_zh": "Identify BLAST 固定用目标物种全蛋白 FASTA 建库;请优先检查目标全蛋白 FASTA 是否存在、非空且为有效蛋白FASTA格式,再检查参考家族蛋白 query 文件。",
"suggestion_en": "Identify BLAST uses target species full protein FASTA for database; check that target FASTA exists, is non-empty and valid, then check reference family protein query file.",
"auto_fix": False
},
"memory_error": {
"patterns": [r"MemoryError", r"out of memory", r"内存不足"],
"suggestion": "减少--cpu参数值或分批处理,建议使用--cpu 4",
"suggestion_zh": "减少--cpu参数值或分批处理,建议使用--cpu 4",
"suggestion_en": "Reduce --cpu parameter or use batch processing; recommend --cpu 4",
"auto_fix": True,
"fix_action": "reduce_cpu"
},
"rnaseq_library_strandness_error": {
"patterns": [r"library_strandness", r"链特异性", r"rna-strandness", r"featureCounts.*-s"],
"suggestion": "请在 samples.yaml 的 rnaseq.library_strandness 或每个 samples.<sample>.library_strandness 中明确填写 non_stranded / forward / reverse;混合链特异性样本当前不能用单次 featureCounts 定量。",
"suggestion_zh": "请在 samples.yaml 的 rnaseq.library_strandness 或每个 samples.<sample>.library_strandness 中明确填写 non_stranded / forward / reverse;混合链特异性样本当前不能用单次 featureCounts 定量。",
"suggestion_en": "Please specify non_stranded / forward / reverse in samples.yaml rnaseq.library_strandness or each samples.<sample>.library_strandness; mixed strand-specific samples cannot be quantified in a single featureCounts run.",
"auto_fix": False
},
"timeout_error": {
"patterns": [r"Timeout", r"timed out", r"超时"],
"suggestion": "增加超时时间或检查输入数据大小",
"suggestion_zh": "增加超时时间或检查输入数据大小",
"suggestion_en": "Increase timeout or check input data size",
"auto_fix": False
},
"conda_env_error": {
"patterns": [r"conda", r"Environment", r"找不到命令"],
"suggestion": "检查conda环境是否正确激活,确保工具已安装",
"suggestion_zh": "检查conda环境是否正确激活,确保工具已安装",
"suggestion_en": "Check that conda environment is activated and tools are installed",
"auto_fix": False
},
"permission_error": {
"patterns": [r"Permission denied", r"权限不足"],
"suggestion": "检查文件权限,确保有读写权限",
"suggestion_zh": "检查文件权限,确保有读写权限",
"suggestion_en": "Check file permissions; ensure read/write access",
"auto_fix": False
},
"invalid_parameter": {
"patterns": [r"unrecognized arguments", r"invalid parameter", r"参数错误"],
"suggestion": "检查参数名称是否正确,注意使用短横线而非下划线",
"suggestion_zh": "检查参数名称是否正确,注意使用短横线而非下划线",
"suggestion_en": "Check parameter name; use hyphens instead of underscores",
"auto_fix": True,
"fix_action": "fix_parameter_format"
},
@@ -448,7 +457,8 @@ class DiagnosisAgent(BaseAgent):
r"RuntimeError: MAFFT失败.*No such file",
r"RuntimeError:.*失败.*No such file or directory"
],
"suggestion": "生物信息学工具未安装或未添加到PATH。建议:1) 激活conda环境: conda activate test4py311 2) 安装缺失工具: conda install -c bioconda mafft blast iqtree hmmer",
"suggestion_zh": "生物信息学工具未安装或未添加到PATH。建议:1) 激活conda环境: conda activate test4py311 2) 安装缺失工具: conda install -c bioconda mafft blast iqtree hmmer",
"suggestion_en": "Bioinformatics tools not installed or not in PATH. Suggestions: 1) activate conda environment: conda activate test4py311 2) install missing tools: conda install -c bioconda mafft blast iqtree hmmer",
"auto_fix": False,
"severity": "high"
},
@@ -461,7 +471,8 @@ class DiagnosisAgent(BaseAgent):
r"requires.*stage.*not completed",
r"dependency.*not satisfied"
],
"suggestion": "前置分析阶段未完成,系统将自动先执行依赖阶段",
"suggestion_zh": "前置分析阶段未完成,系统将自动先执行依赖阶段",
"suggestion_en": "Prerequisite analysis stage not completed; system will execute dependency stage first automatically",
"auto_fix": True,
"fix_action": "execute_dependencies",
"severity": "high"
@@ -601,6 +612,7 @@ class DiagnosisAgent(BaseAgent):
prepare_patch_workspace: bool = False,
scope_id: str = "",
params: Optional[Dict[str, Any]] = None,
language: str = "zh",
) -> Dict[str, Any]:
"""只读审计某个 job/stage 的产物、manifest、日志和输入配置。
@@ -930,6 +942,7 @@ class DiagnosisAgent(BaseAgent):
gate: str = "preflight",
project_root: Path = PROJECT_ROOT,
params: Optional[Dict[str, Any]] = None,
language: str = "zh",
) -> Dict[str, Any]:
"""Global QA gate for stage preflight/postflight/failure.
@@ -4490,10 +4503,10 @@ class DiagnosisAgent(BaseAgent):
except Exception as exc:
print(f"[DiagnosisAgent] Failed to remember LLM diagnosis case: {exc}")
def _match_error_pattern(self, error_text: str) -> Optional[Dict]:
def _match_error_pattern(self, error_text: str, language: str = "zh") -> Optional[Dict]:
"""匹配错误模式"""
error_lower = error_text.lower()
priority_order = [
"tool_not_found",
"dependency_failed",
@@ -4507,15 +4520,16 @@ class DiagnosisAgent(BaseAgent):
"permission_error",
"timeout_error"
]
for error_type in priority_order:
if error_type in self.ERROR_PATTERNS:
pattern_info = self.ERROR_PATTERNS[error_type]
for pattern in pattern_info["patterns"]:
if re.search(pattern, error_text, re.IGNORECASE):
suggestion_key = "suggestion_en" if language == "en" else "suggestion_zh"
return {
"error_type": error_type,
"suggestion": pattern_info["suggestion"],
"suggestion": pattern_info.get(suggestion_key, pattern_info.get("suggestion_zh", "")),
"auto_fix": pattern_info.get("auto_fix", False),
"fix_action": pattern_info.get("fix_action"),
"severity": pattern_info.get("severity", "medium")
+85 -17
View File
@@ -29,6 +29,74 @@ WORKSPACE_DIR = WORKSPACE_ROOT
class Orchestrator:
# 双语消息映射表:中文 → English(用于用户可见消息)
_MSG = {
# analysis_node — response 消息
"✅ 所有分析阶段已完成!": "✅ All analysis stages completed!",
"📋 自动规划剩余分析阶段": "📋 Auto-planned remaining analysis stages",
"**已完成**": "**Completed**",
"无": "None",
"**待执行**": "**Pending**",
"个阶段)": "stage(s))",
"请确认是否按顺序执行以上分析阶段?": "Confirm to execute the above analysis stages in order?",
"我将帮您执行RNA-seq": "I will help you execute RNA-seq",
"分析。请确认参数后开始。": "analysis. Please confirm parameters to start.",
"RNA-seq分析将通过前端异步执行。": "RNA-seq analysis will be executed asynchronously via frontend.",
" 分析。PlanningAgentV2 已根据当前状态生成完整执行计划。": " analysis. PlanningAgentV2 has generated a complete execution plan based on current state.",
"我将帮您执行": "I will help you execute",
"分析。": "analysis.",
"请提供基因家族蛋白序列文件(.fa/.fasta),用于MEME保守基序预测。": "Please provide gene family protein sequence file (.fa/.fasta) for MEME conserved motif prediction.",
"请提供config.xlsx及基因组/GFF/CDS/PEP等原始文件,用于完整数据准备。": "Please provide config.xlsx and genome/GFF/CDS/PEP files for complete data preparation.",
"请提供基因家族蛋白序列文件,用于Pfam/InterPro结构域注释。": "Please provide gene family protein sequence file for Pfam/InterPro domain annotation.",
"请提供基因家族的GFF注释文件和基因组序列。": "Please provide gene family GFF annotation file and genome sequence.",
"请提供基因家族成员的启动子序列(上游2000bp)或全基因组序列+GFF。": "Please provide gene family promoter sequences (upstream 2000bp) or whole genome + GFF.",
"请提供物种基因组序列(.fasta)和GFF注释文件。": "Please provide species genome sequence (.fasta) and GFF annotation file.",
"请提供多个物种的基因组序列和GFF注释文件,以及物种间的同源关系。": "Please provide genome sequences and GFF annotation files for multiple species, along with inter-species orthology.",
"请提供染色体长度信息和基因位置信息。": "Please provide chromosome length and gene position information.",
"请提供拟南芥同源基因映射文件(ortholog_pairs.txt,格式:拟南芥ID\t苦荞ID),用于STRING数据库互作预测。如未提供,请先运行同源基因识别分析。": "Please provide Arabidopsis ortholog mapping file (ortholog_pairs.txt, format: AtID\tTargetID) for STRING interaction prediction. If unavailable, run ortholog identification first.",
"请提供目标物种和参考物种(如拟南芥At)的蛋白序列文件,用于同源基因识别。": "Please provide target and reference species (e.g., Arabidopsis At) protein FASTA for ortholog identification.",
"请提供物种间同源基因对(ortholog pairs,格式:基因A\t基因B)或共线性分析结果。": "Please provide inter-species ortholog pairs (format: GeneA\tGeneB) or synteny analysis results.",
"请提供RNA-seq表达矩阵(FPKM/TPM/Count)或原始测序数据。": "Please provide RNA-seq expression matrix (FPKM/TPM/Count) or raw sequencing data.",
"请提供差异表达基因列表(DEGs)或目标基因集。": "Please provide differentially expressed gene list (DEGs) or target gene set.",
"请提供基因家族蛋白序列文件,用于理化性质分析。": "Please provide gene family protein sequence file for physicochemical property analysis.",
"请提供基因家族HMM文件和蛋白序列文件,将自动执行标准11阶段分析。": "Please provide gene family HMM file and protein FASTA; standard 11-stage analysis will be executed automatically.",
"请提供RNA-seq原始测序数据(FASTQ格式)。": "Please provide RNA-seq raw sequencing data (FASTQ format).",
"请提供质控后的clean reads(FASTQ格式)和参考基因组。": "Please provide QC-filtered clean reads (FASTQ format) and reference genome.",
"请提供比对后的BAM文件或参考基因组注释GFF。": "Please provide aligned BAM file or reference genome annotation GFF.",
"请提供featureCounts计数矩阵和样本分组表,用于VST、PCA、样本相关性和聚类。": "Please provide featureCounts count matrix and sample grouping table for VST, PCA, sample correlation and clustering.",
"请提供多时相表达矩阵(如不同发育阶段/处理时间点)。": "Please provide multi-temporal expression matrix (e.g., different developmental stages/treatment time points).",
"请提供VST/log2表达矩阵或count矩阵和样本性状表,用于共表达网络构建。": "Please provide VST/log2 expression matrix or count matrix and sample trait table for co-expression network construction.",
"请提供差异表达结果和gene2go背景文件,或先执行背景注释。": "Please provide differential expression results and gene2go background file, or run background annotation first.",
"请提供差异表达结果和gene2ko背景文件,或先执行背景注释。": "Please provide differential expression results and gene2ko background file, or run background annotation first.",
"请提供差异表达排序统计量和gene2ko/KEGG背景,用于fgsea排序富集。": "Please provide differential expression ranking statistics and gene2ko/KEGG background for fgsea ranked enrichment.",
"请提供差异表达结果和目标物种蛋白FASTA;若已有InterProScan TSV会优先复用。": "Please provide differential expression results and target species protein FASTA; existing InterProScan TSV will be reused if available.",
"请提供表达矩阵(样本数≥6),用于共表达网络构建。": "Please provide expression matrix (samples ≥ 6) for co-expression network construction.",
# knowledge_node — system prompt / response 消息
"你是BotanicalBaseAgent基因家族分析系统的知识助手。基于提供的文献资料,生成专业、详细的回答。": "You are a knowledge assistant for the BotanicalBaseAgent gene family analysis system. Generate professional, detailed answers based on provided literature.",
"📚 基于知识库的回答:": "📚 Knowledge base answer:",
"**参考文献**:": "**References**:",
"(来源: 本地LLM回答 + RAG辅助检索)": "(Source: local LLM answer + RAG-assisted retrieval)",
"⚠️ (本地LLM回答,未使用RAG文献检索)": "⚠️ (local LLM answer, RAG retrieval not used)",
"找到": "Found",
"条相关文献,正在生成本地LLM回答...": " related references; generating local LLM answer...",
"未找到相关文献,生成一般回答": "No related references found; generating general answer",
"你是一个专业的基因家族分析意图识别器。": "You are a professional gene family analysis intent recognizer.",
"你是一个基因家族分析专家。用户想要进行基因家族分析。": "You are a gene family analysis expert. The user wants to perform gene family analysis.",
}
@classmethod
def _l(cls, zh_text: str, language: str = "zh") -> str:
"""Return language-appropriate text from the bilingual map."""
if language == "zh":
return zh_text
en = cls._MSG.get(zh_text)
if en is not None:
return en
# fallback: try partial match for template strings
for zh_key, en_val in cls._MSG.items():
if zh_key in zh_text:
return zh_text.replace(zh_key, en_val)
return zh_text
def __init__(
self,
llm_manager: LLMManager,
@@ -313,6 +381,7 @@ class Orchestrator:
async def _router_node(self, state: GraphState) -> dict:
query = state["query"]
language = state.get("language", "zh")
# 使用 RouterAgent 进行意图路由
print(f"[RouterNode] Using RouterAgent to classify query: {query[:50]}...")
@@ -378,6 +447,7 @@ class Orchestrator:
async def _knowledge_node(self, state: GraphState) -> dict:
query = state["query"]
language = state.get("language", "zh")
try:
# 优先调用当前 Dual-KB QA 服务
@@ -578,7 +648,7 @@ class Orchestrator:
"status": "completed",
"mode": "analysis",
"final_output": {
"response": f"✅ 所有分析阶段已完成!\n\n已完成: {', '.join(completed_stages)}",
"response": self._l("✅ 所有分析阶段已完成!", language) + "\n\n" + self._l("**已完成**", language) + ": " + ", ".join(completed_stages),
"analysis_plan": {
"gene_family": gene_family,
"species": species,
@@ -595,14 +665,7 @@ class Orchestrator:
"status": "completed",
"mode": "analysis",
"final_output": {
"response": f"""📋 自动规划剩余分析阶段
**已完成**: {', '.join(completed_stages) if completed_stages else '无'}
**待执行** ({len(remaining_stages)}个阶段):
{stages_text}
请确认是否按顺序执行以上分析阶段?""",
"response": f"{self._l('📋 自动规划剩余分析阶段', language)}\n\n{self._l('**已完成**', language)}: {', '.join(completed_stages) if completed_stages else self._l('无', language)}\n\n{self._l('**待执行**', language)} ({len(remaining_stages)}{self._l('个阶段)', language)}:\n{stages_text}\n\n{self._l('请确认是否按顺序执行以上分析阶段?', language)}",
"analysis_plan": {
"gene_family": gene_family,
"species": species,
@@ -630,6 +693,7 @@ class Orchestrator:
async def _analysis_node(self, state: GraphState) -> dict:
query = state["query"]
files = state.get("files", [])
language = state.get("language", "zh")
print(f"[_analysis_node] Entered with query: {query[:50]}...", flush=True)
try:
@@ -821,7 +885,7 @@ class Orchestrator:
"status": "completed",
"mode": "analysis",
"final_output": {
"response": f"我将帮您执行RNA-seq {analysis_type}分析。请确认参数后开始。",
"response": self._l("我将帮您执行RNA-seq", language) + " " + analysis_type + self._l("分析。请确认参数后开始。", language),
"analysis_plan": {
"gene_family": gene_family,
"species": species,
@@ -831,7 +895,7 @@ class Orchestrator:
"prefix": f"{species}.{gene_family}",
"cpu": 8
},
"guidance": "RNA-seq分析将通过前端异步执行。"
"guidance": self._l("RNA-seq分析将通过前端异步执行。", language)
}
}
}
@@ -878,7 +942,7 @@ class Orchestrator:
"status": "completed",
"mode": "analysis",
"final_output": {
"response": f"我将帮您执行RNA-seq {analysis_type} 分析。PlanningAgentV2 已根据当前状态生成完整执行计划。",
"response": self._l("我将帮您执行RNA-seq", language) + " " + analysis_type + self._l(" 分析。PlanningAgentV2 已根据当前状态生成完整执行计划。", language),
"analysis_plan": plan_result.get("final_output", {}).get("analysis_plan", {})
}
}
@@ -889,7 +953,7 @@ class Orchestrator:
"status": "completed",
"mode": "analysis",
"final_output": {
"response": f"我将帮您执行RNA-seq {analysis_type} 分析。请确认参数后开始。",
"response": self._l("我将帮您执行RNA-seq", language) + " " + analysis_type + self._l(" 分析。请确认参数后开始。", language),
"analysis_plan": {
"gene_family": gene_family,
"species": species,
@@ -900,21 +964,25 @@ class Orchestrator:
"prefix": f"{species}.{gene_family}",
"cpu": 8
},
"guidance": f"RNA-seq {analysis_type} 分析将通过前端异步执行。"
"guidance": self._l("RNA-seq " + analysis_type + " 分析将通过前端异步执行。", language)
}
}
}
# Ka/Ks分析:走通用规划流程(返回analysis_plan让前端执行)
# 注意:不再直接调用_execute_kaks,而是通过PlanningAgentV2规划完整流程
guidance = guidance_map.get(analysis_type, f"请上传进行{analysis_type}分析所需的文件,或确认使用系统内置数据。")
guidance = guidance_map.get(analysis_type)
if guidance:
guidance = self._l(guidance, language)
else:
guidance = self._l("请上传进行", language) + analysis_type + self._l("分析所需的文件,或确认使用系统内置数据。", language)
return {
"current_agent": "analysis",
"status": "completed",
"mode": "analysis",
"final_output": {
"response": f"我将帮您执行{analysis_type}分析。",
"response": self._l("我将帮您执行", language) + analysis_type + self._l("分析。", language),
"analysis_plan": {
"gene_family": gene_family,
"species": species,