diff --git a/apps/web/vue3/index_ml.html b/apps/web/vue3/index_ml.html
index 0f5967946..adc74f6de 100644
--- a/apps/web/vue3/index_ml.html
+++ b/apps/web/vue3/index_ml.html
@@ -2158,7 +2158,7 @@
{{ ragProgress.current || '正在知识库中查找相关内容……' }}
- 停止等待
+ {{ t('stop-waiting') }}
diff --git a/gfa_core/mas/agents/conversation_tools.py b/gfa_core/mas/agents/conversation_tools.py
index 6e4bcc4b4..7c253712e 100644
--- a/gfa_core/mas/agents/conversation_tools.py
+++ b/gfa_core/mas/agents/conversation_tools.py
@@ -264,6 +264,7 @@ class RecognizeAnalysisIntentArgs(BaseModel):
query: str
job_id: str = ""
prefix: str = ""
+ language: str = "zh"
_job_id_is_local = field_validator("job_id")(_validate_job_id)
_prefix_is_literal = field_validator("prefix")(_validate_prefix)
@@ -1604,21 +1605,29 @@ def build_plantai_conversation_tools(
*,
workflow_id: str | None = None,
guidance: bool = False,
+ language: str = "zh",
) -> str:
"""Adapt MainAgent memory to the original IntentAgent text contract."""
text = str(query or "").strip()
+ en = language == "en"
+ if (
+ text.startswith("执行数据准备(genome_prepare)")
+ and "当前业务动作已由数据准备契约唯一确定" in text
+ ):
+ return text
if goal_ledger is None:
return text
terms = goal_ledger.user_terms
context: list[str] = []
family = str(terms.get("gene_family") or "").strip()
if family:
- context.append(f"基因家族 {family}")
- for key, label in (
- ("target_species", "目标物种"),
- ("reference_species", "参考物种"),
- ("comparative_species", "比较物种"),
+ context.append(f"Gene family: {family}" if en else f"基因家族 {family}")
+ for key, label_zh, label_en in (
+ ("target_species", "目标物种", "Target species"),
+ ("reference_species", "参考物种", "Reference species"),
+ ("comparative_species", "比较物种", "Comparative species"),
):
+ label = label_en if en else label_zh
values = terms.get(key) or ()
if isinstance(values, (list, tuple)):
cleaned = [
@@ -1638,21 +1647,39 @@ def build_plantai_conversation_tools(
if mode == "whole_genome":
enabled_designs = "、".join(business_context["enabled_designs"])
disabled_designs = "、".join(business_context["disabled_designs"])
- context.append(
- f"RNA-seq业务模式 全基因组;{experiment_design} 是实验设计权威输入;"
- f"可解释设计为 {enabled_designs},{disabled_designs} 本轮禁用;"
- "设计或 batch 降级不得静默发生"
- )
+ if en:
+ context.append(
+ f"RNA-seq mode: whole genome; {experiment_design} is the authoritative experimental design input; "
+ f"interpretable designs: {enabled_designs}, {disabled_designs} disabled this round; "
+ "design or batch downgrades must not happen silently"
+ )
+ else:
+ context.append(
+ f"RNA-seq业务模式 全基因组;{experiment_design} 是实验设计权威输入;"
+ f"可解释设计为 {enabled_designs},{disabled_designs} 本轮禁用;"
+ "设计或 batch 降级不得静默发生"
+ )
else:
- context.append(
- "RNA-seq业务模式 基因家族成员;使用已成功的全基因组差异表达结果"
- "和精确确认的基因家族成员进行投影;不得自动选择首个成员文件,"
- "不得隐式重跑差异表达"
- )
+ if en:
+ context.append(
+ "RNA-seq mode: gene family member; use successful whole-genome differential expression results "
+ "and precisely confirmed gene family members for projection; must not auto-select the first member file, "
+ "must not implicitly re-run differential expression"
+ )
+ else:
+ context.append(
+ "RNA-seq业务模式 基因家族成员;使用已成功的全基因组差异表达结果"
+ "和精确确认的基因家族成员进行投影;不得自动选择首个成员文件,"
+ "不得隐式重跑差异表达"
+ )
elif rna_route == "whole_genome":
- context.append("RNA-seq模式 全基因组")
+ context.append(
+ "RNA-seq mode: whole genome" if en else "RNA-seq模式 全基因组"
+ )
elif rna_route == "family_member":
- context.append("RNA-seq模式 基因家族成员")
+ context.append(
+ "RNA-seq mode: gene family member" if en else "RNA-seq模式 基因家族成员"
+ )
job_id = bound_job_id or str(terms.get("active_job_id") or "").strip()
formal_config_exists = bool(
job_id and (project_root / "input" / job_id / "config.xlsx").is_file()
@@ -1686,15 +1713,62 @@ def build_plantai_conversation_tools(
and family_matches
):
subfamily_confirm_observed = True
- if config_confirmed:
- context.append("正式 config.xlsx 已确认")
+ genome_prepare_observed = False
+ canonical_request = None
+ if config_confirmed and not genome_prepare_observed:
+ if en:
+ context.append(
+ "Formal config.xlsx confirmed; this indicates configuration has been verified, "
+ "not that data preparation is complete. No data preparation completion evidence in current job; "
+ "specific analysis intent and stages still determined by the existing analysis system"
+ )
+ else:
+ context.append(
+ "正式 config.xlsx 已确认;这只表示配置已核对,不等于数据准备已经完成,"
+ "当前 job 尚无数据准备完成证据;具体分析意图和阶段仍由现有分析系统判断"
+ )
+ if guidance and workflow_id in {
+ "data_preparation",
+ "gene_family",
+ "rnaseq_whole_genome",
+ "rnaseq_family_member",
+ }:
+ contract = load_canonical_workflow_contract("data_preparation")
+ transition = next(
+ item
+ for item in contract.transitions
+ if item.from_state_id == "config_confirmed"
+ )
+ actions = {item.id: item for item in contract.actions}
+ target_stage = actions[transition.action_id].target_stage
+ if en:
+ canonical_request = (
+ f"Execute data preparation ({target_stage})\n"
+ "Business action uniquely determined by data preparation contract"
+ )
+ else:
+ canonical_request = (
+ f"执行数据准备({target_stage})\n"
+ "当前业务动作已由数据准备契约唯一确定"
+ )
if family and subfamily_confirm_observed:
- context.append(
- "当前 job 的 subfamily_confirm 已成功完成,家族成员和亚家族分类表已确认,"
- "这是当前项目状态事实;具体分析意图和阶段仍由现有分析系统判断"
- )
+ if en:
+ context.append(
+ "Current job's subfamily_confirm completed successfully; "
+ "family members and subfamily classification table confirmed. "
+ "This is a current project state fact; specific analysis intent and stages still determined by the existing analysis system"
+ )
+ else:
+ context.append(
+ "当前 job 的 subfamily_confirm 已成功完成,家族成员和亚家族分类表已确认,"
+ "这是当前项目状态事实;具体分析意图和阶段仍由现有分析系统判断"
+ )
+ if canonical_request:
+ return canonical_request
if not context:
return text
+ if en:
+ return f"{text}\nConfirmed context: {'; '.join(context)}."
return f"{text}\n已确认上下文:{';'.join(context)}。"
registry.set_analysis_handoff_resolver(intent_query_with_confirmed_context)
@@ -1711,7 +1785,7 @@ def build_plantai_conversation_tools(
receiver="intent_v2",
message_type="recognize_intent",
payload={
- "query": intent_query_with_confirmed_context(args.query),
+ "query": intent_query_with_confirmed_context(args.query, language=args.language),
"job_id": job_id,
"prefix": args.prefix,
"workspace": str(project_root / "workspace" / job_id) if job_id else "",