Files
PlantAI-dev/gfa_core/mas/agents/planning.py
T
2026-08-03 18:42:47 +08:00

395 lines
16 KiB
Python
Executable File

"""
PlanningAgent V2 - 增强版任务规划
支持根据用户意图生成合理的分析流程
集成Knowledge Agent获取参数推荐
"""
import json
import re
from typing import Dict, List, Optional
from .base import AgentMessage, AgentState, BaseAgent
from gfa_core.mas.llm import LLMManager
class PlanningAgent(BaseAgent):
"""
任务规划Agent V2
根据用户意图和可用资源,生成最优的分析执行计划
"""
name = "planning"
role = "Workflow Planning Specialist"
# 标准分析流程定义
STANDARD_FLOWS = {
"prepare": ["genome_prepare"],
"basic": ["identify"],
"identify_refine": ["identify_refine"],
"phylogeny": ["identify", "phylogeny"],
"structure": ["identify", "phylogeny", "motif", "domain", "structure"],
"promoter": ["identify", "phylogeny", "promoter"],
"synteny": ["identify", "phylogeny", "synteny"],
"karyotype": ["identify", "phylogeny", "karyotype"],
"interspecies": ["identify", "phylogeny", "interspecies_synteny"],
"expression": ["identify", "phylogeny", "expression"],
"complete": [
"identify", "phylogeny", "motif", "domain",
"structure", "promoter", "synteny", "expression"
],
"rnaseq": [
"rnaseq_qc",
"rnaseq_align",
"rnaseq_quant",
"rnaseq_sample_qc",
"rnaseq_de",
"rnaseq_go_enrich",
"rnaseq_kegg_enrich",
"rnaseq_fgsea",
"rnaseq_wgcna",
],
"evolution": ["identify", "phylogeny", "kaks"],
"interaction": ["identify", "ppi"]
}
# 阶段依赖关系
STAGE_DEPENDENCIES = {
"genome_prepare": [],
"identify_refine": [],
"phylogeny": ["identify"],
"motif": ["identify"],
"domain": ["identify"],
"structure": ["identify"],
"promoter": ["identify", "phylogeny"],
"synteny": ["identify"],
"karyotype": ["identify"],
"interspecies_synteny": ["identify"],
"expression": ["identify"],
"kaks": ["identify", "phylogeny"],
"ppi": ["identify"],
"rnaseq_align": ["rnaseq_qc"],
"rnaseq_quant": ["rnaseq_align"],
"rnaseq_sample_qc": ["rnaseq_quant"],
"rnaseq_wgcna": ["rnaseq_sample_qc"],
"rnaseq_de": ["rnaseq_sample_qc"],
"rnaseq_go_enrich": ["rnaseq_de"],
"rnaseq_kegg_enrich": ["rnaseq_de"],
"rnaseq_fgsea": ["rnaseq_de"],
"rnaseq_enrich": ["rnaseq_de"],
"rnaseq_candidate_family": ["rnaseq_de", "rnaseq_go_enrich", "rnaseq_kegg_enrich", "rnaseq_fgsea"]
}
# 阶段描述
STAGE_DESCRIPTIONS = {
"genome_prepare": "数据准备总流程 - 多物种文件整理、GFF/FASTA标准化、最长转录本和参考序列合并",
"identify_refine": "修订鉴定成员 - 根据人工修订ID列表重建final_members.id和final_members.pep.fa",
"identify": "基因家族鉴定 - 使用HMM和BLAST识别基因家族成员",
"phylogeny": "系统发育分析 - 构建进化树和亚家族分类",
"motif": "Motif分析 - 识别保守的蛋白基序",
"domain": "结构域分析 - 分析蛋白结构域组成",
"structure": "结构预测 - 预测蛋白三维结构",
"promoter": "启动子分析 - 分析顺式作用元件",
"synteny": "共线性分析 - 分析基因组共线性关系",
"karyotype": "核型分析 - 生成染色体分布图",
"interspecies_synteny": "物种间共线性 - 跨物种比较分析",
"expression": "表达分析 - 分析基因表达模式",
"kaks": "Ka/Ks分析 - 计算进化速率和选择压力",
"ppi": "PPI分析 - 蛋白互作网络分析",
"rnaseq_qc": "RNA-seq质控 - FastQC质量评估",
"rnaseq_align": "RNA-seq比对 - reads比对到基因组",
"rnaseq_quant": "RNA-seq定量 - 基因表达量计算",
"rnaseq_sample_qc": "RNA-seq样本质量评估 - VST、PCA、样本相关性与聚类",
"rnaseq_wgcna": "RNA-seq WGCNA - 加权共表达网络与模块性状相关性",
"rnaseq_de": "差异表达 - DESeq2差异分析",
"rnaseq_go_enrich": "RNA-seq GO富集分析 - 独立使用 gene2go 背景",
"rnaseq_kegg_enrich": "RNA-seq KEGG富集分析 - 独立使用 gene2ko/KO 背景",
"rnaseq_fgsea": "RNA-seq fgsea - 基于排序基因和 KEGG 基因集的 GSEA",
"rnaseq_enrich": "RNA-seq富集分析 - 兼容旧入口;规划中优先使用 GO/KEGG 子阶段"
}
def __init__(self, llm_manager: LLMManager):
super().__init__(llm_manager)
async def execute(self, state: AgentState, message: AgentMessage) -> AgentState:
"""
执行任务规划
根据用户意图生成分析执行计划
"""
query = message.payload.get("query", "")
intent = state.results.get("results", {}).get("intent", {}) or state.results.get("intent", {})
# 提取关键信息
gene_family = intent.get("gene_family", "") or self._extract_gene_family(query)
species = intent.get("species", "") or self._extract_species(query)
stage = intent.get("stage", "")
# 确定分析流程
flow = self._determine_flow(query, stage)
# 生成执行计划
plan = await self._generate_plan(query, gene_family, species, flow)
# 更新状态
state.results["task_plan"] = plan
state.results["planning_info"] = {
"gene_family": gene_family,
"species": species,
"flow_type": flow,
"total_stages": len(plan.get("stages", []))
}
state.messages.append({
"role": "planning",
"content": f"📋 已生成分析计划: {len(plan.get('stages', []))}个阶段",
})
return state
def _extract_gene_family(self, query: str) -> str:
"""从查询中提取基因家族名称"""
# 常见基因家族模式
patterns = [
r'(\w+)\s*基因家族',
r'基因家族\s*(\w+)',
r'\b(SPL|NAC|WRKY|MYB|bHLH|WD40|C2H2|C3H|NBS|AP2|ARF|AUX|B3|BZIP|C2C2|CCAAT|CO-like|CPP|CSD|CTF|CYP|Dof|E2F|ERF|FAR1|G2-like|GATA|GeBP|GRAS|GRF|HB|HMA|HMG|HRT|HSF|JUMONJI|KNOX|LBD|LFY|LIM|LOB|LSD|MADS|MULE|MYB|NAC|ND|Nin|NLP|NPR|OFP|Orphan|Ots|PHD|PLATZ|PPR|Pseudo|RWP|S1Fa|SAP|SBP|Sigma|SMC|SRS|TALE|TCP|Tify|TRAF|Trihelix|TST|TUB|ULT|VOZ|Whirly|WOX|WRKY|XAL2|YABBY|ZF)\b'
]
for pattern in patterns:
match = re.search(pattern, query, re.IGNORECASE)
if match:
return match.group(1).upper()
return "SPL" # 默认
def _extract_species(self, query: str) -> str:
"""从查询中提取物种信息"""
species_map = {
'ft': 'Ft', 'tartary': 'Ft', '苦荞': 'Ft',
'at': 'At', 'arabidopsis': 'At', '拟南芥': 'At',
'os': 'Os', 'rice': 'Os', '水稻': 'Os',
'zm': 'Zm', 'maize': 'Zm', '玉米': 'Zm',
'gm': 'Gm', 'soybean': 'Gm', '大豆': 'Gm',
'sl': 'Sl', 'tomato': 'Sl', '番茄': 'Sl'
}
query_lower = query.lower()
for key, value in species_map.items():
if key in query_lower:
return value
return "Ft" # 默认
def _determine_flow(self, query: str, stage: str) -> str:
"""确定分析流程类型"""
query_lower = query.lower()
# RNA-seq关键词
if any(kw in query_lower for kw in ['rnaseq', 'rna-seq', '转录组', '表达谱']):
return "rnaseq"
# 进化分析关键词
if any(kw in query_lower for kw in ['kaks', 'ka/ks', '进化', '选择压力']):
return "evolution"
# PPI关键词
if any(kw in query_lower for kw in ['ppi', '互作', 'interaction', 'string']):
return "interaction"
# 根据明确指定的stage
if stage:
flow_map = {
"identify": "basic",
"phylogeny": "phylogeny",
"motif": "structure",
"domain": "structure",
"structure": "structure",
"promoter": "promoter",
"synteny": "synteny",
"karyotype": "karyotype",
"interspecies_synteny": "interspecies",
"expression": "expression",
"kaks": "evolution",
"ppi": "interaction"
}
return flow_map.get(stage, "complete")
# 完整分析关键词
if any(kw in query_lower for kw in ['完整', '全套', '全部', 'complete', 'full']):
return "complete"
return "complete" # 默认完整分析
async def _generate_plan(self, query: str, gene_family: str,
species: str, flow: str) -> dict:
"""生成详细执行计划"""
# 获取流程阶段列表
stages = self.STANDARD_FLOWS.get(flow, self.STANDARD_FLOWS["complete"])
# 构建阶段详情
stage_details = []
for i, stage_id in enumerate(stages):
stage_info = {
"id": stage_id,
"name": self.STAGE_DESCRIPTIONS.get(stage_id, stage_id),
"description": self.STAGE_DESCRIPTIONS.get(stage_id, ""),
"order": i + 1,
"dependencies": self.STAGE_DEPENDENCIES.get(stage_id, []),
"params": await self._get_default_params(stage_id, gene_family, species)
}
stage_details.append(stage_info)
# 构建执行计划
plan = {
"query": query,
"gene_family": gene_family,
"species": species,
"flow_type": flow,
"prefix": f"{species}.{gene_family}",
"stages": stage_details,
"total_stages": len(stages),
"estimated_time": self._estimate_time(stages),
"required_inputs": self._get_required_inputs(stages),
"outputs": self._get_outputs(stages, gene_family, species)
}
return plan
async def _get_default_params(self, stage_id: str, gene_family: str, species: str) -> dict:
"""获取阶段的默认参数,并尝试从Knowledge Agent获取推荐"""
default_params = {
"identify": {"cpu": 8, "dom_i_evalue_max": 0.001, "hmm_cov_min": 0.3, "blast_evalue": "1e-05"},
"phylogeny": {"cpu": 8, "bootstrap": 1000, "model": "MFP"},
"motif": {"cpu": 8, "nmotifs": 10, "min_width": 6, "max_width": 200},
"domain": {"cpu": 8, "e_value": 1e-5},
"structure": {"cpu": 8},
"promoter": {"cpu": 8},
"synteny": {"cpu": 8},
"karyotype": {"cpu": 8},
"interspecies_synteny": {"cpu": 8},
"expression": {"cpu": 8},
"kaks": {"cpu": 8, "model": "YN00"},
"ppi": {"species": "ath", "score": 200},
"rnaseq_qc": {"cpu": 8},
"rnaseq_align": {"cpu": 8},
"rnaseq_quant": {"cpu": 8},
"rnaseq_de": {"cpu": 8},
"rnaseq_enrich": {"cpu": 8, "top_pathways": 20}
}
params = default_params.get(stage_id, {"cpu": 8}).copy()
params["prefix"] = f"{species}.{gene_family}"
# 尝试从 Knowledge Agent 获取参数推荐
try:
recommended = await self._get_params_from_knowledge(stage_id, gene_family, species)
if recommended:
# 合并推荐参数(推荐参数优先)
params.update(recommended)
print(f"[PlanningAgent] 从Knowledge获取到{stage_id}阶段参数推荐")
except Exception as e:
print(f"[PlanningAgent] Knowledge参数推荐失败: {e},使用默认参数")
return params
async def _get_params_from_knowledge(self, stage_id: str, gene_family: str, species: str) -> dict:
"""从Knowledge Agent获取参数推荐"""
from .knowledge import create_knowledge_agent
from gfa_core.mas.llm import LLMManager
# 创建 Knowledge Agent
llm_manager = LLMManager()
knowledge_agent = create_knowledge_agent(llm_manager)
# 构建查询
query = f"推荐{stage_id}阶段的分析参数,基因家族:{gene_family},物种:{species}"
# 构建消息
from .base import AgentMessage, AgentState
import uuid
message = AgentMessage(
sender="planning",
receiver="knowledge",
message_type="query",
payload={"query": query, "mode": "consultation"}
)
state = AgentState(
session_id=f"planning_{uuid.uuid4().hex[:8]}",
results={},
messages=[]
)
# 执行查询
result_state = await knowledge_agent.execute(state, message)
# 解析推荐参数
answer = result_state.results.get("knowledge_response", "")
# 尝试从回答中提取参数(简化版)
recommended = {}
if "cpu" in answer.lower():
# 提取CPU推荐
import re
cpu_match = re.search(r'(\d+)\s*核', answer)
if cpu_match:
recommended["cpu"] = int(cpu_match.group(1))
return recommended
def _estimate_time(self, stages: list) -> str:
"""估算总执行时间"""
time_map = {
"identify": 5, "phylogeny": 30, "motif": 20, "domain": 15,
"structure": 30, "promoter": 20, "synteny": 30, "karyotype": 10,
"interspecies_synteny": 40, "expression": 15, "kaks": 20, "ppi": 10,
"rnaseq_qc": 30, "rnaseq_align": 60, "rnaseq_quant": 30,
"rnaseq_sample_qc": 10, "rnaseq_de": 20, "rnaseq_enrich": 15,
"rnaseq_go_enrich": 15, "rnaseq_kegg_enrich": 15,
"rnaseq_fgsea": 10, "rnaseq_wgcna": 30
}
total_minutes = sum(time_map.get(s, 10) for s in stages)
if total_minutes < 60:
return f"约{total_minutes}分钟"
else:
hours = total_minutes // 60
mins = total_minutes % 60
return f"约{hours}小时{mins}分钟" if mins > 0 else f"约{hours}小时"
def _get_required_inputs(self, stages: list) -> list:
"""获取所需输入文件"""
inputs = []
if "identify" in stages:
inputs.extend([
{"name": "HMM模型文件", "type": "hmm", "required": True, "example": "SPL_Ath.hmm"},
{"name": "蛋白质序列", "type": "fasta", "required": True, "example": "Ft.pep.fasta"}
])
if any(s.startswith("rnaseq") for s in stages):
inputs.extend([
{"name": "FASTQ文件", "type": "fastq", "required": True, "example": "*.fastq.gz"},
{"name": "样本配置", "type": "yaml", "required": True, "example": "samples.yaml"}
])
return inputs
def _get_outputs(self, stages: list, gene_family: str, species: str) -> list:
"""获取预期输出"""
prefix = f"{species}.{gene_family}"
outputs = []
output_map = {
"identify": [{"name": "最终成员列表", "file": f"{prefix}.final_members.id"}],
"phylogeny": [{"name": "进化树", "file": f"{prefix}.tree.nwk"}],
"kaks": [{"name": "Ka/Ks结果", "file": f"{prefix}.kaks_results.csv"}],
"ppi": [{"name": "PPI网络", "file": f"{prefix}.ppi_network.tsv"}]
}
for stage in stages:
if stage in output_map:
outputs.extend(output_map[stage])
return outputs
def create_planning_agent(llm_manager):
"""创建PlanningAgent实例"""
return PlanningAgent(llm_manager)