feat:1.数据库存储功能添加(初版)2.后端REST API版本代码清理

This commit is contained in:
liailing1026
2026-02-25 10:55:51 +08:00
parent f736cd104a
commit 2140cfaf92
35 changed files with 3912 additions and 2981 deletions

View File

@@ -49,7 +49,6 @@ def LLM_Completion(
messages: list[dict], stream: bool = True, useGroq: bool = True,model_config: dict = None
) -> str:
if model_config:
print_colored(f"Using model config: {model_config}", "blue")
return _call_with_custom_config(messages,stream,model_config)
if not useGroq or not FAST_DESIGN_MODE:
force_gpt4 = True
@@ -125,7 +124,7 @@ def _call_with_custom_config(messages: list[dict], stream: bool, model_config: d
#print(colored(full_reply_content, "blue", "on_white"), end="")
return full_reply_content
except Exception as e:
print_colored(f"Custom API error for model {api_model} :{str(e)}","red")
print_colored(f"[API Error] Custom API error: {str(e)}", "red")
raise
@@ -166,11 +165,11 @@ async def _achat_completion_stream_custom(messages:list[dict], temp_async_client
except httpx.RemoteProtocolError as e:
if attempt < max_retries - 1:
wait_time = (attempt + 1) *2
print_colored(f"⚠️ Stream connection interrupted (attempt {attempt+1}/{max_retries}). Retrying in {wait_time}s...", text_color="yellow")
print_colored(f"[API Warn] Stream connection interrupted, retrying in {wait_time}s...", "yellow")
await asyncio.sleep(wait_time)
continue
except Exception as e:
print_colored(f"Custom API stream error for model {api_model} :{str(e)}","red")
print_colored(f"[API Error] Custom API stream error: {str(e)}", "red")
raise
@@ -181,7 +180,6 @@ async def _achat_completion_stream_groq(messages: list[dict]) -> str:
max_attempts = 5
for attempt in range(max_attempts):
print("Attempt to use Groq (Fase Design Mode):")
try:
response = await groq_client.chat.completions.create(
messages=messages,
@@ -363,7 +361,7 @@ async def _achat_completion_stream(messages: list[dict]) -> str:
return full_reply_content
except Exception as e:
print_colored(f"OpenAI API error in _achat_completion_stream: {str(e)}", "red")
print_colored(f"[API Error] OpenAI API stream error: {str(e)}", "red")
raise
@@ -383,7 +381,7 @@ def _chat_completion(messages: list[dict]) -> str:
return content
except Exception as e:
print_colored(f"OpenAI API error in _chat_completion: {str(e)}", "red")
print_colored(f"[API Error] OpenAI API error: {str(e)}", "red")
raise

View File

@@ -77,7 +77,6 @@ def generate_AbilityRequirement(General_Goal, Current_Task):
),
},
]
print(messages[1]["content"])
return read_LLM_Completion(messages)["AbilityRequirement"]
@@ -106,6 +105,7 @@ def generate_AgentSelection(General_Goal, Current_Task, Agent_Board):
while True:
candidate = read_LLM_Completion(messages)["AgentSelectionPlan"]
# 添加调试打印
if len(candidate) > MAX_TEAM_SIZE:
teamSize = random.randint(2, MAX_TEAM_SIZE)
candidate = candidate[0:teamSize]
@@ -113,7 +113,6 @@ def generate_AgentSelection(General_Goal, Current_Task, Agent_Board):
continue
AgentSelectionPlan = sorted(candidate)
AgentSelectionPlan_set = set(AgentSelectionPlan)
# Check if every item in AgentSelectionPlan is in agentboard
if AgentSelectionPlan_set.issubset(agentboard_set):
break # If all items are in agentboard, break the loop

View File

@@ -85,7 +85,6 @@ class BaseAction():
# Handle missing agent profiles gracefully
model_config = None
if agentName not in AgentProfile_Dict:
print_colored(text=f"Warning: Agent '{agentName}' not found in AgentProfile_Dict. Using default profile.", text_color="yellow")
agentProfile = f"AI Agent named {agentName}"
else:
# agentProfile = AgentProfile_Dict[agentName]

View File

@@ -83,7 +83,6 @@ def executePlan(plan, num_StepToRun, RehearsalLog, AgentProfile_Dict):
}
# start the group chat
util.print_colored(TaskDescription, text_color="green")
ActionHistory = []
action_count = 0
total_actions = len(TaskProcess)
@@ -93,9 +92,6 @@ def executePlan(plan, num_StepToRun, RehearsalLog, AgentProfile_Dict):
actionType = ActionInfo["ActionType"]
agentName = ActionInfo["AgentName"]
# 添加进度日志
util.print_colored(f"🔄 Executing action {action_count}/{total_actions}: {actionType} by {agentName}", text_color="yellow")
if actionType in Action.customAction_Dict:
currentAction = Action.customAction_Dict[actionType](
info=ActionInfo,
@@ -126,11 +122,4 @@ def executePlan(plan, num_StepToRun, RehearsalLog, AgentProfile_Dict):
stepLogNode["ActionHistory"] = ActionHistory
# Return Output
print(
colored(
"$Run " + str(StepRun_count) + "step$",
color="black",
on_color="on_white",
)
)
return RehearsalLog

View File

@@ -107,7 +107,6 @@ def get_parallel_batches(TaskProcess: List[Dict], dependency_map: Dict[int, List
# 避免死循环
remaining = [i for i in range(len(TaskProcess)) if i not in completed]
if remaining:
print(colored(f"警告: 检测到循环依赖,强制串行执行: {remaining}", "yellow"))
ready_to_run = remaining[:1]
else:
break
@@ -188,7 +187,8 @@ async def execute_step_async_streaming(
KeyObjects: Dict,
step_index: int,
total_steps: int,
execution_id: str = None
execution_id: str = None,
RehearsalLog: List = None # 用于追加日志到历史记录
) -> Generator[Dict, None, None]:
"""
异步执行单个步骤,支持流式返回
@@ -276,8 +276,9 @@ async def execute_step_async_streaming(
total_actions = len(TaskProcess)
completed_actions = 0
# 步骤开始日志
util.print_colored(
f"📋 步骤 {step_index + 1}/{total_steps}: {StepName} ({total_actions} 个动作, 分 {len(batches)}并行执行)",
f"📋 步骤 {step_index + 1}/{total_steps}: {StepName} ({total_actions} 个动作, 分 {len(batches)} 批执行)",
text_color="cyan"
)
@@ -286,11 +287,11 @@ async def execute_step_async_streaming(
# 在每个批次执行前检查暂停状态
should_continue = await execution_state_manager.async_check_pause(execution_id)
if not should_continue:
util.print_colored("🛑 用户请求停止执行", "red")
return
batch_size = len(batch_indices)
# 批次执行日志
if batch_size > 1:
util.print_colored(
f"🚦 批次 {batch_index + 1}/{len(batches)}: 并行执行 {batch_size} 个动作",
@@ -298,7 +299,7 @@ async def execute_step_async_streaming(
)
else:
util.print_colored(
f"🔄 动作 {completed_actions + 1}/{total_actions}: 串行执行",
f"🔄 批次 {batch_index + 1}/{len(batches)}: 串行执行",
text_color="yellow"
)
@@ -353,12 +354,21 @@ async def execute_step_async_streaming(
objectLogNode["content"] = KeyObjects[OutputName]
stepLogNode["ActionHistory"] = ActionHistory
# 收集该步骤使用的 agent去重
assigned_agents_in_step = list(set(Agent_List)) if Agent_List else []
# 追加到 RehearsalLog因为 RehearsalLog 是可变对象,会反映到原列表)
if RehearsalLog is not None:
RehearsalLog.append(stepLogNode)
RehearsalLog.append(objectLogNode)
yield {
"type": "step_complete",
"step_index": step_index,
"step_name": StepName,
"step_log_node": stepLogNode,
"object_log_node": objectLogNode,
"assigned_agents": {StepName: assigned_agents_in_step}, # 该步骤使用的 agent
}
@@ -394,8 +404,6 @@ def executePlan_streaming_dynamic(
execution_state_manager.start_execution(execution_id, general_goal)
print(colored(f"⏸️ 执行状态管理器已启动,支持暂停/恢复execution_id={execution_id}", "green"))
# 准备执行
KeyObjects = existingKeyObjects.copy() if existingKeyObjects else {}
finishedStep_index = -1
@@ -406,9 +414,6 @@ def executePlan_streaming_dynamic(
if logNode["LogNodeType"] == "object":
KeyObjects[logNode["NodeId"]] = logNode["content"]
if existingKeyObjects:
print(colored(f"📦 使用已存在的 KeyObjects: {list(existingKeyObjects.keys())}", "cyan"))
# 确定要运行的步骤范围
if num_StepToRun is None:
run_to = len(plan["Collaboration Process"])
@@ -421,9 +426,6 @@ def executePlan_streaming_dynamic(
if execution_id:
# 初始化执行管理器使用传入的execution_id
actual_execution_id = dynamic_execution_manager.start_execution(general_goal, steps_to_run, execution_id)
print(colored(f"🚀 开始执行计划(动态模式),共 {len(steps_to_run)} 个步骤执行ID: {actual_execution_id}", "cyan"))
else:
print(colored(f"🚀 开始执行计划(流式推送),共 {len(steps_to_run)} 个步骤", "cyan"))
total_steps = len(steps_to_run)
@@ -443,7 +445,7 @@ def executePlan_streaming_dynamic(
# 检查暂停状态
should_continue = await execution_state_manager.async_check_pause(execution_id)
if not should_continue:
print(colored("🛑 用户请求停止执行", "red"))
util.print_colored("🛑 用户请求停止执行", "red")
await queue.put({
"type": "error",
"message": "执行已被用户停止"
@@ -466,29 +468,24 @@ def executePlan_streaming_dynamic(
# 如果没有步骤在队列中queue_total_steps为0立即退出
if queue_total_steps == 0:
print(colored(f"⚠️ 没有步骤在队列中,退出执行", "yellow"))
break
# 如果所有步骤都已完成,等待可能的新步骤
if completed_steps >= queue_total_steps:
if empty_wait_count >= max_empty_wait_cycles:
# 等待超时,退出执行
print(colored(f"✅ 所有步骤执行完成,等待超时", "green"))
break
else:
# 等待新步骤追加
print(colored(f"⏳ 等待新步骤追加... ({empty_wait_count}/{max_empty_wait_cycles})", "cyan"))
await asyncio.sleep(1)
continue
else:
# 还有步骤未完成,继续尝试获取
print(colored(f"⏳ 等待步骤就绪... ({completed_steps}/{queue_total_steps})", "cyan"))
await asyncio.sleep(0.5)
empty_wait_count = 0 # 重置等待计数
continue
else:
# 执行信息不存在,退出
print(colored(f"⚠️ 执行信息不存在,退出执行", "yellow"))
break
# 重置等待计数
@@ -506,7 +503,8 @@ def executePlan_streaming_dynamic(
KeyObjects,
step_index,
current_total_steps, # 使用动态更新的总步骤数
execution_id
execution_id,
RehearsalLog # 传递 RehearsalLog 用于追加日志
):
if execution_state_manager.is_stopped(execution_id):
await queue.put({
@@ -533,7 +531,7 @@ def executePlan_streaming_dynamic(
for step_index, stepDescrip in enumerate(steps_to_run):
should_continue = await execution_state_manager.async_check_pause(execution_id)
if not should_continue:
print(colored("🛑 用户请求停止执行", "red"))
util.print_colored("🛑 用户请求停止执行", "red")
await queue.put({
"type": "error",
"message": "执行已被用户停止"
@@ -547,7 +545,8 @@ def executePlan_streaming_dynamic(
KeyObjects,
step_index,
total_steps,
execution_id
execution_id,
RehearsalLog # 传递 RehearsalLog 用于追加日志
):
if execution_state_manager.is_stopped(execution_id):
await queue.put({

View File

@@ -63,10 +63,7 @@ class DynamicExecutionManager:
# 初始化待执行步骤索引
self._pending_steps[execution_id] = list(range(len(initial_steps)))
print(f"🚀 启动执行: {execution_id}")
print(f"📊 初始步骤数: {len(initial_steps)}")
print(f"📋 待执行步骤索引: {self._pending_steps[execution_id]}")
print(f"[Execution] 启动执行: {execution_id}, 步骤数={len(initial_steps)}")
return execution_id
def add_steps(self, execution_id: str, new_steps: List[Dict]) -> int:
@@ -82,7 +79,6 @@ class DynamicExecutionManager:
"""
with self._lock:
if execution_id not in self._step_queues:
print(f"⚠️ 警告: 执行ID {execution_id} 不存在,无法追加步骤")
return 0
current_count = len(self._step_queues[execution_id])
@@ -95,14 +91,9 @@ class DynamicExecutionManager:
self._pending_steps[execution_id].extend(new_indices)
# 更新总步骤数
old_total = self._executions[execution_id]["total_steps"]
self._executions[execution_id]["total_steps"] = len(self._step_queues[execution_id])
new_total = self._executions[execution_id]["total_steps"]
print(f" 追加了 {len(new_steps)} 个步骤到 {execution_id}")
print(f"📊 步骤总数: {old_total} -> {new_total}")
print(f"📋 待执行步骤索引: {self._pending_steps[execution_id]}")
print(f"[Execution] 追加步骤: +{len(new_steps)}, 总计={self._executions[execution_id]['total_steps']}")
return len(new_steps)
def get_next_step(self, execution_id: str) -> Optional[Dict]:
@@ -117,7 +108,6 @@ class DynamicExecutionManager:
"""
with self._lock:
if execution_id not in self._pending_steps:
print(f"⚠️ 警告: 执行ID {execution_id} 不存在")
return None
# 获取第一个待执行步骤的索引
@@ -128,7 +118,6 @@ class DynamicExecutionManager:
# 从队列中获取步骤
if step_index >= len(self._step_queues[execution_id]):
print(f"⚠️ 警告: 步骤索引 {step_index} 超出范围")
return None
step = self._step_queues[execution_id][step_index]
@@ -137,9 +126,7 @@ class DynamicExecutionManager:
self._executed_steps[execution_id].add(step_index)
step_name = step.get("StepName", "未知")
print(f"🎯 获取下一个步骤: {step_name} (索引: {step_index})")
print(f"📋 剩余待执行步骤: {len(self._pending_steps[execution_id])}")
print(f"[Execution] 获取步骤: {step_name} (索引: {step_index})")
return step
def mark_step_completed(self, execution_id: str):
@@ -154,9 +141,7 @@ class DynamicExecutionManager:
self._executions[execution_id]["completed_steps"] += 1
completed = self._executions[execution_id]["completed_steps"]
total = self._executions[execution_id]["total_steps"]
print(f"📊 步骤完成进度: {completed}/{total}")
else:
print(f"⚠️ 警告: 执行ID {execution_id} 不存在")
print(f"[Execution] 步骤完成: {completed}/{total}")
def get_execution_info(self, execution_id: str) -> Optional[Dict]:
"""

View File

@@ -129,7 +129,6 @@ class ExecutionStateManager:
state['goal'] = goal
state['should_pause'] = False
state['should_stop'] = False
print(f"🚀 [DEBUG] start_execution: execution_id={execution_id}, 状态设置为 RUNNING, goal={goal}")
def pause_execution(self, execution_id: str) -> bool:
"""
@@ -143,19 +142,13 @@ class ExecutionStateManager:
"""
state = self._get_state(execution_id)
if state is None:
# 打印当前所有活跃的 execution_id帮助调试
active_ids = list(self._states.keys())
print(f"⚠️ [DEBUG] pause_execution: execution_id={execution_id} 不存在")
print(f" 当前活跃的 execution_id 列表: {active_ids}")
return False
with self._get_lock(execution_id):
if state['status'] != ExecutionStatus.RUNNING:
print(f"⚠️ [DEBUG] pause_execution: execution_id={execution_id}, 当前状态是 {state['status']},无法暂停")
return False
state['status'] = ExecutionStatus.PAUSED
state['should_pause'] = True
print(f"⏸️ [DEBUG] pause_execution: execution_id={execution_id}, 状态设置为PAUSED")
return True
def resume_execution(self, execution_id: str) -> bool:
@@ -170,16 +163,13 @@ class ExecutionStateManager:
"""
state = self._get_state(execution_id)
if state is None:
print(f"⚠️ [DEBUG] resume_execution: execution_id={execution_id} 不存在")
return False
with self._get_lock(execution_id):
if state['status'] != ExecutionStatus.PAUSED:
print(f"⚠️ [DEBUG] resume_execution: 当前状态不是PAUSED而是 {state['status']}")
return False
state['status'] = ExecutionStatus.RUNNING
state['should_pause'] = False
print(f"▶️ [DEBUG] resume_execution: execution_id={execution_id}, 状态设置为RUNNING, should_pause=False")
return True
def stop_execution(self, execution_id: str) -> bool:
@@ -194,17 +184,14 @@ class ExecutionStateManager:
"""
state = self._get_state(execution_id)
if state is None:
print(f"⚠️ [DEBUG] stop_execution: execution_id={execution_id} 不存在")
return False
with self._get_lock(execution_id):
if state['status'] in [ExecutionStatus.IDLE, ExecutionStatus.STOPPED]:
print(f"⚠️ [DEBUG] stop_execution: 当前状态是 {state['status']}, 无法停止")
return False
state['status'] = ExecutionStatus.STOPPED
state['should_stop'] = True
state['should_pause'] = False
print(f"🛑 [DEBUG] stop_execution: execution_id={execution_id}, 状态设置为STOPPED")
return True
def reset(self, execution_id: str):
@@ -215,12 +202,10 @@ class ExecutionStateManager:
state['goal'] = None
state['should_pause'] = False
state['should_stop'] = False
print(f"🔄 [DEBUG] reset: execution_id={execution_id}, 状态重置为IDLE")
def cleanup(self, execution_id: str):
"""清理指定 execution_id 的所有状态"""
self._cleanup_state(execution_id)
print(f"🧹 [DEBUG] cleanup: execution_id={execution_id} 的状态已清理")
async def async_check_pause(self, execution_id: str):
"""
@@ -248,7 +233,6 @@ class ExecutionStateManager:
# 检查停止标志
if should_stop:
print("🛑 [DEBUG] async_check_pause: execution_id={}, 检测到停止信号".format(execution_id))
return False
# 检查暂停状态
@@ -262,7 +246,6 @@ class ExecutionStateManager:
should_stop = state['should_stop']
if not should_pause:
print("▶️ [DEBUG] async_check_pause: execution_id={}, 从暂停中恢复!".format(execution_id))
continue
if should_stop:
return False

View File

@@ -113,7 +113,6 @@ class GenerationStateManager:
state['status'] = GenerationStatus.GENERATING
state['goal'] = goal
state['should_stop'] = False
print(f"🚀 [GenerationState] start_generation: generation_id={generation_id}, 状态设置为 GENERATING")
def stop_generation(self, generation_id: str) -> bool:
"""
@@ -127,26 +126,21 @@ class GenerationStateManager:
"""
state = self._get_state(generation_id)
if state is None:
print(f"⚠️ [GenerationState] stop_generation: generation_id={generation_id} 不存在")
return True # 不存在也算停止成功
with self._get_lock(generation_id):
if state['status'] == GenerationStatus.STOPPED:
print(f"✅ [GenerationState] stop_generation: generation_id={generation_id} 已经是 STOPPED 状态")
return True # 已经停止也算成功
if state['status'] == GenerationStatus.COMPLETED:
print(f"✅ [GenerationState] stop_generation: generation_id={generation_id} 已经 COMPLETED视为停止成功")
return True # 已完成也视为停止成功
if state['status'] == GenerationStatus.IDLE:
print(f"⚠️ [GenerationState] stop_generation: generation_id={generation_id} 是 IDLE 状态,无需停止")
return True # 空闲状态也视为无需停止
# 真正需要停止的情况
state['status'] = GenerationStatus.STOPPED
state['should_stop'] = True
print(f"🛑 [GenerationState] stop_generation: generation_id={generation_id}, 状态设置为STOPPED")
return True
def complete_generation(self, generation_id: str):
@@ -154,12 +148,10 @@ class GenerationStateManager:
state = self._ensure_state(generation_id)
with self._get_lock(generation_id):
state['status'] = GenerationStatus.COMPLETED
print(f"✅ [GenerationState] complete_generation: generation_id={generation_id}")
def cleanup(self, generation_id: str):
"""清理指定 generation_id 的所有状态"""
self._cleanup_state(generation_id)
print(f"🧹 [GenerationState] cleanup: generation_id={generation_id} 的状态已清理")
def should_stop(self, generation_id: str) -> bool:
"""检查是否应该停止"""

View File

@@ -94,6 +94,7 @@ def remove_render_spec(duty_spec):
return duty_spec
def read_LLM_Completion(messages, useGroq=True):
for _ in range(3):
text = LLM_Completion(messages, useGroq=useGroq)

25
backend/db/__init__.py Normal file
View File

@@ -0,0 +1,25 @@
"""
AgentCoord 数据库模块
提供 PostgreSQL 数据库连接、模型和 CRUD 操作
基于 DATABASE_DESIGN.md 设计
"""
from .database import get_db, get_db_context, test_connection, engine, text
from .models import MultiAgentTask, UserAgent, TaskStatus
from .crud import MultiAgentTaskCRUD, UserAgentCRUD
__all__ = [
# 连接管理
"get_db",
"get_db_context",
"test_connection",
"engine",
"text",
# 模型
"MultiAgentTask",
"UserAgent",
"TaskStatus",
# CRUD
"MultiAgentTaskCRUD",
"UserAgentCRUD",
]

404
backend/db/crud.py Normal file
View File

@@ -0,0 +1,404 @@
"""
数据库 CRUD 操作
封装所有数据库操作方法 (基于 DATABASE_DESIGN.md)
"""
import copy
import uuid
from datetime import datetime, timezone
from typing import List, Optional
from sqlalchemy.orm import Session
from .models import MultiAgentTask, UserAgent
class MultiAgentTaskCRUD:
"""多智能体任务 CRUD 操作"""
@staticmethod
def create(
db: Session,
task_id: Optional[str] = None, # 可选,如果为 None 则自动生成
user_id: str = "",
query: str = "",
agents_info: list = [],
task_outline: Optional[dict] = None,
assigned_agents: Optional[list] = None,
agent_scores: Optional[dict] = None,
result: Optional[str] = None,
) -> MultiAgentTask:
"""创建任务记录"""
task = MultiAgentTask(
task_id=task_id or str(uuid.uuid4()), # 如果没传则生成新的
user_id=user_id,
query=query,
agents_info=agents_info,
task_outline=task_outline,
assigned_agents=assigned_agents,
agent_scores=agent_scores,
result=result,
)
db.add(task)
db.commit()
db.refresh(task)
return task
@staticmethod
def get_by_id(db: Session, task_id: str) -> Optional[MultiAgentTask]:
"""根据任务 ID 获取记录"""
return db.query(MultiAgentTask).filter(MultiAgentTask.task_id == task_id).first()
@staticmethod
def get_by_user_id(
db: Session, user_id: str, limit: int = 50, offset: int = 0
) -> List[MultiAgentTask]:
"""根据用户 ID 获取任务记录"""
return (
db.query(MultiAgentTask)
.filter(MultiAgentTask.user_id == user_id)
.order_by(MultiAgentTask.created_at.desc())
.offset(offset)
.limit(limit)
.all()
)
@staticmethod
def get_recent(
db: Session, limit: int = 20, offset: int = 0
) -> List[MultiAgentTask]:
"""获取最近的任务记录"""
return (
db.query(MultiAgentTask)
.order_by(MultiAgentTask.created_at.desc())
.offset(offset)
.limit(limit)
.all()
)
@staticmethod
def update_result(
db: Session, task_id: str, result: list
) -> Optional[MultiAgentTask]:
"""更新任务结果"""
task = db.query(MultiAgentTask).filter(MultiAgentTask.task_id == task_id).first()
if task:
task.result = result if result else []
db.commit()
db.refresh(task)
return task
@staticmethod
def update_task_outline(
db: Session, task_id: str, task_outline: dict
) -> Optional[MultiAgentTask]:
"""更新任务大纲"""
task = db.query(MultiAgentTask).filter(MultiAgentTask.task_id == task_id).first()
if task:
task.task_outline = task_outline
db.commit()
db.refresh(task)
return task
@staticmethod
def update_assigned_agents(
db: Session, task_id: str, assigned_agents: dict
) -> Optional[MultiAgentTask]:
"""更新分配的智能体(步骤名 -> agent列表"""
task = db.query(MultiAgentTask).filter(MultiAgentTask.task_id == task_id).first()
if task:
task.assigned_agents = assigned_agents
db.commit()
db.refresh(task)
return task
@staticmethod
def update_agent_scores(
db: Session, task_id: str, agent_scores: dict
) -> Optional[MultiAgentTask]:
"""更新智能体评分(合并模式,追加新步骤的评分)"""
task = db.query(MultiAgentTask).filter(MultiAgentTask.task_id == task_id).first()
if task:
# 合并现有评分数据和新评分数据
existing_scores = task.agent_scores or {}
merged_scores = {**existing_scores, **agent_scores} # 新数据覆盖/追加旧数据
task.agent_scores = merged_scores
db.commit()
db.refresh(task)
return task
@staticmethod
def update_status(
db: Session, task_id: str, status: str
) -> Optional[MultiAgentTask]:
"""更新任务状态"""
task = db.query(MultiAgentTask).filter(MultiAgentTask.task_id == task_id).first()
if task:
task.status = status
db.commit()
db.refresh(task)
return task
@staticmethod
def increment_execution_count(db: Session, task_id: str) -> Optional[MultiAgentTask]:
"""增加任务执行次数"""
task = db.query(MultiAgentTask).filter(MultiAgentTask.task_id == task_id).first()
if task:
task.execution_count = (task.execution_count or 0) + 1
db.commit()
db.refresh(task)
return task
@staticmethod
def update_generation_id(
db: Session, task_id: str, generation_id: str
) -> Optional[MultiAgentTask]:
"""更新生成 ID"""
task = db.query(MultiAgentTask).filter(MultiAgentTask.task_id == task_id).first()
if task:
task.generation_id = generation_id
db.commit()
db.refresh(task)
return task
@staticmethod
def update_execution_id(
db: Session, task_id: str, execution_id: str
) -> Optional[MultiAgentTask]:
"""更新执行 ID"""
task = db.query(MultiAgentTask).filter(MultiAgentTask.task_id == task_id).first()
if task:
task.execution_id = execution_id
db.commit()
db.refresh(task)
return task
@staticmethod
def update_rehearsal_log(
db: Session, task_id: str, rehearsal_log: list
) -> Optional[MultiAgentTask]:
"""更新排练日志"""
task = db.query(MultiAgentTask).filter(MultiAgentTask.task_id == task_id).first()
if task:
task.rehearsal_log = rehearsal_log if rehearsal_log else []
db.commit()
db.refresh(task)
return task
@staticmethod
def append_rehearsal_log(
db: Session, task_id: str, log_entry: dict
) -> Optional[MultiAgentTask]:
"""追加排练日志条目"""
task = db.query(MultiAgentTask).filter(MultiAgentTask.task_id == task_id).first()
if task:
current_log = task.rehearsal_log or []
if isinstance(current_log, list):
current_log.append(log_entry)
else:
current_log = [log_entry]
task.rehearsal_log = current_log
db.commit()
db.refresh(task)
return task
@staticmethod
def update_branches(
db: Session, task_id: str, branches
) -> Optional[MultiAgentTask]:
"""更新任务分支数据
支持两种格式:
- list: 旧格式,直接覆盖
- dict: 新格式 { flow_branches: [...], task_process_branches: {...} }
两个 key 独立保存,互不干扰。
"""
import copy
task = db.query(MultiAgentTask).filter(MultiAgentTask.task_id == task_id).first()
if task:
if isinstance(branches, dict):
# 新格式:字典,独立保存两个 key互不干扰
# 使用深拷贝避免引用共享问题
existing = copy.deepcopy(task.branches) if task.branches else {}
if isinstance(existing, dict):
# 如果只更新 flow_branches保留已有的 task_process_branches
if 'flow_branches' in branches and 'task_process_branches' not in branches:
branches['task_process_branches'] = existing.get('task_process_branches', {})
# 如果只更新 task_process_branches保留已有的 flow_branches
if 'task_process_branches' in branches and 'flow_branches' not in branches:
branches['flow_branches'] = existing.get('flow_branches', [])
task.branches = branches
else:
# 旧格式:列表
task.branches = branches if branches else []
db.commit()
db.refresh(task)
return task
@staticmethod
def get_branches(db: Session, task_id: str) -> Optional[list]:
"""获取任务分支数据"""
task = db.query(MultiAgentTask).filter(MultiAgentTask.task_id == task_id).first()
if task:
return task.branches or []
return []
@staticmethod
def get_by_status(
db: Session, status: str, limit: int = 50, offset: int = 0
) -> List[MultiAgentTask]:
"""根据状态获取任务记录"""
return (
db.query(MultiAgentTask)
.filter(MultiAgentTask.status == status)
.order_by(MultiAgentTask.created_at.desc())
.offset(offset)
.limit(limit)
.all()
)
@staticmethod
def get_by_generation_id(
db: Session, generation_id: str
) -> List[MultiAgentTask]:
"""根据生成 ID 获取任务记录"""
return (
db.query(MultiAgentTask)
.filter(MultiAgentTask.generation_id == generation_id)
.all()
)
@staticmethod
def get_by_execution_id(
db: Session, execution_id: str
) -> List[MultiAgentTask]:
"""根据执行 ID 获取任务记录"""
return (
db.query(MultiAgentTask)
.filter(MultiAgentTask.execution_id == execution_id)
.all()
)
@staticmethod
def delete(db: Session, task_id: str) -> bool:
"""删除任务记录"""
task = db.query(MultiAgentTask).filter(MultiAgentTask.task_id == task_id).first()
if task:
db.delete(task)
db.commit()
return True
return False
class UserAgentCRUD:
"""用户智能体配置 CRUD 操作"""
@staticmethod
def create(
db: Session,
user_id: str,
agent_name: str,
agent_config: dict,
) -> UserAgent:
"""创建用户智能体配置"""
agent = UserAgent(
id=str(uuid.uuid4()),
user_id=user_id,
agent_name=agent_name,
agent_config=agent_config,
)
db.add(agent)
db.commit()
db.refresh(agent)
return agent
@staticmethod
def get_by_id(db: Session, agent_id: str) -> Optional[UserAgent]:
"""根据 ID 获取配置"""
return db.query(UserAgent).filter(UserAgent.id == agent_id).first()
@staticmethod
def get_by_user_id(
db: Session, user_id: str, limit: int = 50
) -> List[UserAgent]:
"""根据用户 ID 获取所有智能体配置"""
return (
db.query(UserAgent)
.filter(UserAgent.user_id == user_id)
.order_by(UserAgent.created_at.desc())
.limit(limit)
.all()
)
@staticmethod
def get_by_name(
db: Session, user_id: str, agent_name: str
) -> List[UserAgent]:
"""根据用户 ID 和智能体名称获取配置"""
return (
db.query(UserAgent)
.filter(
UserAgent.user_id == user_id,
UserAgent.agent_name == agent_name,
)
.all()
)
@staticmethod
def update_config(
db: Session, agent_id: str, agent_config: dict
) -> Optional[UserAgent]:
"""更新智能体配置"""
agent = db.query(UserAgent).filter(UserAgent.id == agent_id).first()
if agent:
agent.agent_config = agent_config
db.commit()
db.refresh(agent)
return agent
@staticmethod
def delete(db: Session, agent_id: str) -> bool:
"""删除智能体配置"""
agent = db.query(UserAgent).filter(UserAgent.id == agent_id).first()
if agent:
db.delete(agent)
db.commit()
return True
return False
@staticmethod
def upsert(
db: Session,
user_id: str,
agent_name: str,
agent_config: dict,
) -> UserAgent:
"""更新或插入用户智能体配置(根据 user_id + agent_name 判断唯一性)
如果已存在相同 user_id 和 agent_name 的记录,则更新配置;
否则创建新记录。
"""
existing = (
db.query(UserAgent)
.filter(
UserAgent.user_id == user_id,
UserAgent.agent_name == agent_name,
)
.first()
)
if existing:
# 更新现有记录
existing.agent_config = agent_config
db.commit()
db.refresh(existing)
return existing
else:
# 创建新记录
agent = UserAgent(
id=str(uuid.uuid4()),
user_id=user_id,
agent_name=agent_name,
agent_config=agent_config,
)
db.add(agent)
db.commit()
db.refresh(agent)
return agent

95
backend/db/database.py Normal file
View File

@@ -0,0 +1,95 @@
"""
数据库连接管理模块
使用 SQLAlchemy ORM支持同步操作
"""
import os
import yaml
from typing import Generator
from contextlib import contextmanager
import json
from sqlalchemy import create_engine, text
from sqlalchemy.orm import sessionmaker, declarative_base
from sqlalchemy.pool import QueuePool
from sqlalchemy.dialects.postgresql import dialect as pg_dialect
# 读取配置
yaml_file = os.path.join(os.getcwd(), "config", "config.yaml")
try:
with open(yaml_file, "r", encoding="utf-8") as file:
config = yaml.safe_load(file).get("database", {})
except Exception:
config = {}
def get_database_url() -> str:
"""获取数据库连接 URL"""
# 优先使用环境变量
host = os.getenv("DB_HOST", config.get("host", "localhost"))
port = os.getenv("DB_PORT", config.get("port", "5432"))
user = os.getenv("DB_USER", config.get("username", "postgres"))
password = os.getenv("DB_PASSWORD", config.get("password", ""))
dbname = os.getenv("DB_NAME", config.get("name", "agentcoord"))
return f"postgresql://{user}:{password}@{host}:{port}/{dbname}"
# 创建引擎
DATABASE_URL = get_database_url()
engine = create_engine(
DATABASE_URL,
poolclass=QueuePool,
pool_size=config.get("pool_size", 10),
max_overflow=config.get("max_overflow", 20),
pool_pre_ping=True,
echo=False,
# JSONB 类型处理器配置
json_serializer=lambda obj: json.dumps(obj, ensure_ascii=False),
json_deserializer=lambda s: json.loads(s) if isinstance(s, str) else s
)
# 创建会话工厂
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
# 基础类
Base = declarative_base()
def get_db() -> Generator:
"""
获取数据库会话
用法: for db in get_db(): ...
"""
db = SessionLocal()
try:
yield db
finally:
db.close()
@contextmanager
def get_db_context() -> Generator:
"""
上下文管理器方式获取数据库会话
用法: with get_db_context() as db: ...
"""
db = SessionLocal()
try:
yield db
db.commit()
except Exception as e:
db.rollback()
raise
finally:
db.close()
def test_connection() -> bool:
"""测试数据库连接"""
try:
with engine.connect() as conn:
conn.execute(text("SELECT 1"))
return True
except Exception as e:
return False

22
backend/db/init_db.py Normal file
View File

@@ -0,0 +1,22 @@
"""
数据库初始化脚本
运行此脚本创建所有表结构
基于 DATABASE_DESIGN.md 设计
"""
import sys
import os
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from db.database import engine, Base
from db.models import MultiAgentTask, UserAgent
def init_database():
"""初始化数据库表结构"""
Base.metadata.create_all(bind=engine)
if __name__ == "__main__":
init_database()

104
backend/db/models.py Normal file
View File

@@ -0,0 +1,104 @@
"""
SQLAlchemy ORM 数据模型
对应数据库表结构 (基于 DATABASE_DESIGN.md)
"""
import uuid
from datetime import datetime, timezone
from enum import Enum as PyEnum
from sqlalchemy import Column, String, Text, DateTime, Integer, Enum, Index, ForeignKey
from sqlalchemy.dialects.postgresql import JSONB
from sqlalchemy.orm import relationship
from .database import Base
class TaskStatus(str, PyEnum):
"""任务状态枚举"""
GENERATING = "generating" # 生成中 - TaskProcess 生成阶段
EXECUTING = "executing" # 执行中 - 任务执行阶段
STOPPED = "stopped" # 已停止 - 用户手动停止执行
COMPLETED = "completed" # 已完成 - 任务正常完成
def utc_now():
"""获取当前 UTC 时间"""
return datetime.now(timezone.utc)
class MultiAgentTask(Base):
"""多智能体任务记录模型"""
__tablename__ = "multi_agent_tasks"
task_id = Column(String(64), primary_key=True)
user_id = Column(String(64), nullable=False, index=True)
query = Column(Text, nullable=False)
agents_info = Column(JSONB, nullable=False)
task_outline = Column(JSONB)
assigned_agents = Column(JSONB)
agent_scores = Column(JSONB)
result = Column(JSONB)
status = Column(
Enum(TaskStatus, name="task_status_enum", create_type=False),
default=TaskStatus.GENERATING,
nullable=False
)
execution_count = Column(Integer, default=0, nullable=False)
generation_id = Column(String(64))
execution_id = Column(String(64))
rehearsal_log = Column(JSONB)
branches = Column(JSONB) # 任务大纲探索分支数据
created_at = Column(DateTime(timezone=True), default=utc_now)
updated_at = Column(DateTime(timezone=True), default=utc_now, onupdate=utc_now)
__table_args__ = (
Index("idx_multi_agent_tasks_status", "status"),
Index("idx_multi_agent_tasks_generation_id", "generation_id"),
Index("idx_multi_agent_tasks_execution_id", "execution_id"),
)
def to_dict(self) -> dict:
"""转换为字典"""
return {
"task_id": self.task_id,
"user_id": self.user_id,
"query": self.query,
"agents_info": self.agents_info,
"task_outline": self.task_outline,
"assigned_agents": self.assigned_agents,
"agent_scores": self.agent_scores,
"result": self.result,
"status": self.status.value if self.status else None,
"execution_count": self.execution_count,
"generation_id": self.generation_id,
"execution_id": self.execution_id,
"rehearsal_log": self.rehearsal_log,
"branches": self.branches,
"created_at": self.created_at.isoformat() if self.created_at else None,
"updated_at": self.updated_at.isoformat() if self.updated_at else None,
}
class UserAgent(Base):
"""用户保存的智能体配置模型 (可选表)"""
__tablename__ = "user_agents"
id = Column(String(64), primary_key=True)
user_id = Column(String(64), nullable=False, index=True)
agent_name = Column(String(100), nullable=False)
agent_config = Column(JSONB, nullable=False)
created_at = Column(DateTime(timezone=True), default=utc_now)
__table_args__ = (
Index("idx_user_agents_user_created", "user_id", "created_at"),
)
def to_dict(self) -> dict:
"""转换为字典"""
return {
"id": self.id,
"user_id": self.user_id,
"agent_name": self.agent_name,
"agent_config": self.agent_config,
"created_at": self.created_at.isoformat() if self.created_at else None,
}

70
backend/db/schema.sql Normal file
View File

@@ -0,0 +1,70 @@
-- AgentCoord 数据库表结构
-- 基于 DATABASE_DESIGN.md 设计
-- 执行方式: psql -U postgres -d agentcoord -f schema.sql
-- =============================================================================
-- 表1: multi_agent_tasks (多智能体任务记录)
-- 状态枚举: pending/planning/generating/executing/completed/failed
-- =============================================================================
CREATE TABLE IF NOT EXISTS multi_agent_tasks (
task_id VARCHAR(64) PRIMARY KEY,
user_id VARCHAR(64) NOT NULL,
query TEXT NOT NULL,
agents_info JSONB NOT NULL,
task_outline JSONB,
assigned_agents JSONB,
agent_scores JSONB,
result JSONB,
status VARCHAR(20) DEFAULT 'pending',
execution_count INTEGER DEFAULT 0,
generation_id VARCHAR(64),
execution_id VARCHAR(64),
rehearsal_log JSONB,
branches JSONB, -- 任务大纲探索分支数据
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
-- 索引
CREATE INDEX IF NOT EXISTS idx_multi_agent_tasks_user_id ON multi_agent_tasks(user_id);
CREATE INDEX IF NOT EXISTS idx_multi_agent_tasks_created_at ON multi_agent_tasks(created_at DESC);
CREATE INDEX IF NOT EXISTS idx_multi_agent_tasks_status ON multi_agent_tasks(status);
CREATE INDEX IF NOT EXISTS idx_multi_agent_tasks_generation_id ON multi_agent_tasks(generation_id);
CREATE INDEX IF NOT EXISTS idx_multi_agent_tasks_execution_id ON multi_agent_tasks(execution_id);
-- =============================================================================
-- 表2: user_agents (用户保存的智能体配置) - 可选表
-- =============================================================================
CREATE TABLE IF NOT EXISTS user_agents (
id VARCHAR(64) PRIMARY KEY,
user_id VARCHAR(64) NOT NULL,
agent_name VARCHAR(100) NOT NULL,
agent_config JSONB NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_user_agents_user_id ON user_agents(user_id);
-- =============================================================================
-- 更新时间触发器函数
-- =============================================================================
CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = CURRENT_TIMESTAMP;
RETURN NEW;
END;
$$ language 'plpgsql';
-- 为 multi_agent_tasks 表创建触发器
CREATE TRIGGER update_multi_agent_tasks_updated_at
BEFORE UPDATE ON multi_agent_tasks
FOR EACH ROW
EXECUTE FUNCTION update_updated_at_column();
DO $$
BEGIN
RAISE NOTICE '✅ PostgreSQL 数据库表结构创建完成!';
RAISE NOTICE '表: multi_agent_tasks (多智能体任务记录)';
RAISE NOTICE '表: user_agents (用户智能体配置)';
END $$;

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,3 @@
import request from '@/utils/request'
import websocket from '@/utils/websocket'
import type { Agent, IApiStepTask, IRawPlanResponse, IRawStepTask } from '@/stores'
import { withRetry } from '@/utils/retry'
@@ -30,7 +29,7 @@ export type IExecuteRawResponse = {
}
/**
* SSE 流式事件类型
* WebSocket 流式事件类型
*/
export type StreamingEvent =
| {
@@ -77,45 +76,36 @@ export interface IFillAgentSelectionRequest {
}
class Api {
// 默认使用WebSocket
private useWebSocketDefault = true
setAgents = (
data: Pick<Agent, 'Name' | 'Profile' | 'apiUrl' | 'apiKey' | 'apiModel'>[],
useWebSocket: boolean = this.useWebSocketDefault,
) => {
// 如果启用WebSocket且已连接使用WebSocket
if (useWebSocket && websocket.connected) {
return websocket.send('set_agents', data)
// 提取响应数据的公共方法
private extractResponse<T>(raw: any): T {
return (raw.data || raw) as T
}
// 否则使用REST API
return request({
url: '/setAgents',
data,
method: 'POST',
})
// 颜色向量转 HSL 字符串
private vec2Hsl = (color: number[]): string => {
const [h, s, l] = color
return `hsl(${h}, ${s}%, ${l}%)`
}
setAgents = (data: Pick<Agent, 'Name' | 'Profile' | 'apiUrl' | 'apiKey' | 'apiModel'>[]) =>
websocket.send('set_agents', data)
getAgents = (user_id: string = 'default_user') => websocket.send('get_agents', { user_id })
generateBasePlan = (data: {
goal: string
inputs: string[]
apiUrl?: string
apiKey?: string
apiModel?: string
useWebSocket?: boolean
onProgress?: (progress: {
status: string
stage?: string
message?: string
[key: string]: any
}) => void
}) => {
const useWs = data.useWebSocket !== undefined ? data.useWebSocket : this.useWebSocketDefault
// 如果启用WebSocket且已连接使用WebSocket
if (useWs && websocket.connected) {
return websocket.send(
}) =>
websocket.send(
'generate_base_plan',
{
'General Goal': data.goal,
@@ -127,51 +117,6 @@ class Api {
undefined,
data.onProgress,
)
}
// 否则使用REST API
return request<unknown, IRawPlanResponse>({
url: '/generate_basePlan',
method: 'POST',
data: {
'General Goal': data.goal,
'Initial Input Object': data.inputs,
apiUrl: data.apiUrl,
apiKey: data.apiKey,
apiModel: data.apiModel,
},
})
}
executePlan = (plan: IRawPlanResponse) => {
return request<unknown, IExecuteRawResponse[]>({
url: '/executePlan',
method: 'POST',
data: {
RehearsalLog: [],
num_StepToRun: null,
plan: {
'Initial Input Object': plan['Initial Input Object'],
'General Goal': plan['General Goal'],
'Collaboration Process': plan['Collaboration Process']?.map((step) => ({
StepName: step.StepName,
TaskContent: step.TaskContent,
InputObject_List: step.InputObject_List,
OutputObject: step.OutputObject,
AgentSelection: step.AgentSelection,
Collaboration_Brief_frontEnd: step.Collaboration_Brief_frontEnd,
TaskProcess: step.TaskProcess.map((action) => ({
ActionType: action.ActionType,
AgentName: action.AgentName,
Description: action.Description,
ID: action.ID,
ImportantInput: action.ImportantInput,
})),
})),
},
},
})
}
/**
* 优化版流式执行计划(支持动态追加步骤)
@@ -182,23 +127,25 @@ class Api {
onMessage: (event: StreamingEvent) => void,
onError?: (error: Error) => void,
onComplete?: () => void,
useWebSocket?: boolean,
_useWebSocket?: boolean,
existingKeyObjects?: Record<string, any>,
enableDynamic?: boolean,
onExecutionStarted?: (executionId: string) => void,
executionId?: string,
restartFromStepIndex?: number, // 新增:从指定步骤重新执行的索引
rehearsalLog?: any[], // 新增:传递截断后的 RehearsalLog
restartFromStepIndex?: number,
rehearsalLog?: any[],
TaskID?: string,
) => {
const useWs = useWebSocket !== undefined ? useWebSocket : this.useWebSocketDefault
// eslint-disable-next-line @typescript-eslint/no-unused-vars
void _useWebSocket // 保留参数位置以保持兼容性
const data = {
RehearsalLog: rehearsalLog || [], // 使用传递的 RehearsalLog
RehearsalLog: rehearsalLog || [], // 使用传递的 RehearsalLog
num_StepToRun: null,
existingKeyObjects: existingKeyObjects || {},
enable_dynamic: enableDynamic || false,
execution_id: executionId || null,
restart_from_step_index: restartFromStepIndex ?? null, // 新增:传递重新执行索引
task_id: TaskID || null, // 任务唯一标识,用于写入数据库
plan: {
'Initial Input Object': plan['Initial Input Object'],
'General Goal': plan['General Goal'],
@@ -220,8 +167,6 @@ class Api {
},
}
// 如果启用WebSocket且已连接使用WebSocket
if (useWs && websocket.connected) {
websocket.subscribe(
'execute_plan_optimized',
data,
@@ -262,61 +207,6 @@ class Api {
onError?.(error)
},
)
return
}
// 否则使用原有的SSE方式
fetch('/api/executePlanOptimized', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
})
.then(async (response) => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`)
}
const reader = response.body?.getReader()
const decoder = new TextDecoder()
if (!reader) {
throw new Error('Response body is null')
}
let buffer = ''
while (true) {
const { done, value } = await reader.read()
if (done) {
onComplete?.()
break
}
buffer += decoder.decode(value, { stream: true })
const lines = buffer.split('\n')
buffer = lines.pop() || ''
for (const line of lines) {
if (line.startsWith('data: ')) {
const data = line.slice(6)
try {
const event = JSON.parse(data)
onMessage(event)
} catch (e) {
// Failed to parse SSE data
}
}
}
}
})
.catch((error) => {
onError?.(error)
})
}
/**
@@ -329,19 +219,14 @@ class Api {
Baseline_Completion: number
initialInputs: string[]
goal: string
useWebSocket?: boolean
onProgress?: (progress: {
status: string
stage?: string
message?: string
[key: string]: any
}) => void
}) => {
const useWs = data.useWebSocket !== undefined ? data.useWebSocket : this.useWebSocketDefault
// 如果启用WebSocket且已连接使用WebSocket
if (useWs && websocket.connected) {
return websocket.send(
}) =>
websocket.send(
'branch_plan_outline',
{
branch_Number: data.branch_Number,
@@ -354,22 +239,6 @@ class Api {
undefined,
data.onProgress,
)
}
// 否则使用REST API
return request<unknown, IRawPlanResponse>({
url: '/branch_PlanOutline',
method: 'POST',
data: {
branch_Number: data.branch_Number,
Modification_Requirement: data.Modification_Requirement,
Existing_Steps: data.Existing_Steps,
Baseline_Completion: data.Baseline_Completion,
'Initial Input Object': data.initialInputs,
'General Goal': data.goal,
},
})
}
/**
* 分支任务流程
@@ -381,19 +250,14 @@ class Api {
Baseline_Completion: number
stepTaskExisting: any
goal: string
useWebSocket?: boolean
onProgress?: (progress: {
status: string
stage?: string
message?: string
[key: string]: any
}) => void
}) => {
const useWs = data.useWebSocket !== undefined ? data.useWebSocket : this.useWebSocketDefault
// 如果启用WebSocket且已连接使用WebSocket
if (useWs && websocket.connected) {
return websocket.send(
}) =>
websocket.send(
'branch_task_process',
{
branch_Number: data.branch_Number,
@@ -406,28 +270,12 @@ class Api {
undefined,
data.onProgress,
)
}
// 否则使用REST API
return request<unknown, BranchAction[][]>({
url: '/branch_TaskProcess',
method: 'POST',
data: {
branch_Number: data.branch_Number,
Modification_Requirement: data.Modification_Requirement,
Existing_Steps: data.Existing_Steps,
Baseline_Completion: data.Baseline_Completion,
stepTaskExisting: data.stepTaskExisting,
'General Goal': data.goal,
},
})
}
fillStepTask = async (data: {
goal: string
stepTask: any
generation_id?: string
useWebSocket?: boolean
TaskID?: string
onProgress?: (progress: {
status: string
stage?: string
@@ -435,72 +283,31 @@ class Api {
[key: string]: any
}) => void
}): Promise<IRawStepTask> => {
const useWs = data.useWebSocket !== undefined ? data.useWebSocket : this.useWebSocketDefault
// 定义实际的 API 调用逻辑
const executeRequest = async (): Promise<any> => {
if (useWs && websocket.connected) {
return await websocket.send(
const rawResponse = await withRetry(
() =>
websocket.send(
'fill_step_task',
{
'General Goal': data.goal,
stepTask: data.stepTask,
generation_id: data.generation_id || '',
task_id: data.TaskID || '',
},
undefined,
data.onProgress,
)
}
// 否则使用REST API
return await request<
),
{
'General Goal': string
stepTask: any
},
{
AgentSelection?: string[]
Collaboration_Brief_FrontEnd?: {
template: string
data: Record<string, { text: string; color: number[] }>
}
InputObject_List?: string[]
OutputObject?: string
StepName?: string
TaskContent?: string
TaskProcess?: Array<{
ID: string
ActionType: string
AgentName: string
Description: string
ImportantInput: string[]
}>
}
>({
url: '/fill_stepTask',
method: 'POST',
data: {
'General Goal': data.goal,
stepTask: data.stepTask,
},
})
}
// 使用重试机制执行请求
const rawResponse = await withRetry(executeRequest, {
maxRetries: 3,
initialDelayMs: 2000,
onRetry: (error, attempt, delay) => {
console.warn(`⚠️ [fillStepTask] 第${attempt}次重试,等待 ${delay}ms...`, error?.message)
console.warn(` [fillStepTask] 第${attempt}次重试,等待 ${delay}ms...`, error?.message)
},
})
},
)
// WebSocket 返回格式: { data: {...}, generation_id, execution_id }
// REST API 返回格式: {...}
const response = rawResponse.data || rawResponse
const vec2Hsl = (color: number[]): string => {
const [h, s, l] = color
return `hsl(${h}, ${s}%, ${l}%)`
let response = this.extractResponse<any>(rawResponse)
if (response?.filled_stepTask) {
response = response.filled_stepTask
}
const briefData: Record<string, { text: string; style?: Record<string, string> }> = {}
@@ -509,15 +316,12 @@ class Api {
briefData[key] = {
text: (value as { text: string; color: number[] }).text,
style: {
background: vec2Hsl((value as { text: string; color: number[] }).color),
background: this.vec2Hsl((value as { text: string; color: number[] }).color),
},
}
}
}
/**
* 构建前端格式的 IRawStepTask
*/
return {
StepName: response.StepName || '',
TaskContent: response.TaskContent || '',
@@ -536,7 +340,7 @@ class Api {
goal: string
stepTask: IApiStepTask
agents: string[]
useWebSocket?: boolean
TaskID?: string
onProgress?: (progress: {
status: string
stage?: string
@@ -544,15 +348,13 @@ class Api {
[key: string]: any
}) => void
}): Promise<IApiStepTask> => {
const useWs = data.useWebSocket !== undefined ? data.useWebSocket : this.useWebSocketDefault
// 定义实际的 API 调用逻辑
const executeRequest = async (): Promise<any> => {
if (useWs && websocket.connected) {
return await websocket.send(
const rawResponse = await withRetry(
() =>
websocket.send(
'fill_step_task_process',
{
'General Goal': data.goal,
task_id: data.TaskID || undefined,
stepTask_lackTaskProcess: {
StepName: data.stepTask.name,
TaskContent: data.stepTask.content,
@@ -560,76 +362,26 @@ class Api {
OutputObject: data.stepTask.output,
AgentSelection: data.agents,
},
agents: data.agents,
},
undefined,
data.onProgress,
)
}
// 否则使用REST API
return await request<
),
{
'General Goal': string
stepTask_lackTaskProcess: {
StepName: string
TaskContent: string
InputObject_List: string[]
OutputObject: string
AgentSelection: string[]
}
},
{
StepName?: string
TaskContent?: string
InputObject_List?: string[]
OutputObject?: string
AgentSelection?: string[]
TaskProcess?: Array<{
ID: string
ActionType: string
AgentName: string
Description: string
ImportantInput: string[]
}>
Collaboration_Brief_FrontEnd?: {
template: string
data: Record<string, { text: string; color: number[] }>
}
}
>({
url: '/fill_stepTask_TaskProcess',
method: 'POST',
data: {
'General Goal': data.goal,
stepTask_lackTaskProcess: {
StepName: data.stepTask.name,
TaskContent: data.stepTask.content,
InputObject_List: data.stepTask.inputs,
OutputObject: data.stepTask.output,
AgentSelection: data.agents,
},
},
})
}
// 使用重试机制执行请求
const rawResponse = await withRetry(executeRequest, {
maxRetries: 3,
initialDelayMs: 2000,
onRetry: (error, attempt, delay) => {
console.warn(
`⚠️ [fillStepTaskTaskProcess] 第${attempt}次重试,等待 ${delay}ms...`,
`[fillStepTaskTaskProcess] 第${attempt}次重试,等待 ${delay}ms...`,
error?.message,
)
},
})
},
)
// WebSocket 返回格式: { data: {...}, generation_id, execution_id }
// REST API 返回格式: {...}
const response = rawResponse.data || rawResponse
const vec2Hsl = (color: number[]): string => {
const [h, s, l] = color
return `hsl(${h}, ${s}%, ${l}%)`
let response = this.extractResponse<any>(rawResponse)
if (response?.filled_stepTask) {
response = response.filled_stepTask
}
const briefData: Record<string, { text: string; style: { background: string } }> = {}
@@ -638,7 +390,7 @@ class Api {
briefData[key] = {
text: (value as { text: string; color: number[] }).text,
style: {
background: vec2Hsl((value as { text: string; color: number[] }).color),
background: this.vec2Hsl((value as { text: string; color: number[] }).color),
},
}
}
@@ -672,7 +424,7 @@ class Api {
agentSelectModifyInit = async (data: {
goal: string
stepTask: any
useWebSocket?: boolean
TaskID?: string
onProgress?: (progress: {
status: string
stage?: string
@@ -680,78 +432,54 @@ class Api {
[key: string]: any
}) => void
}): Promise<Record<string, Record<string, { reason: string; score: number }>>> => {
const useWs = data.useWebSocket !== undefined ? data.useWebSocket : this.useWebSocketDefault
// 调试日志:打印请求参数
const requestPayload = {
'General Goal': data.goal,
stepTask: {
Id: data.stepTask.Id || data.stepTask.id,
StepName: data.stepTask.StepName || data.stepTask.name,
TaskContent: data.stepTask.TaskContent || data.stepTask.content,
InputObject_List: data.stepTask.InputObject_List || data.stepTask.inputs,
OutputObject: data.stepTask.OutputObject || data.stepTask.output,
},
task_id: data.TaskID || '',
}
console.log('🔍 [agentSelectModifyInit] 请求参数:', {
goal: requestPayload['General Goal'],
stepTaskName: requestPayload.stepTask.StepName,
stepTaskContentLength: requestPayload.stepTask.TaskContent?.length,
useWebSocket: useWs && websocket.connected,
wsConnected: websocket.connected,
})
// 定义实际的 API 调用逻辑
const executeRequest = async (): Promise<
Record<string, Record<string, { Reason: string; Score: number }>>
> => {
if (useWs && websocket.connected) {
return await websocket.send(
const rawResponse = await withRetry(
() =>
websocket.send(
'agent_select_modify_init',
requestPayload,
undefined,
data.onProgress,
)
}
// 否则使用REST API
return await request<
),
{
'General Goal': string
stepTask: any
},
Record<string, Record<string, { Reason: string; Score: number }>>
>({
url: '/agentSelectModify_init',
method: 'POST',
data: requestPayload,
})
}
// 使用重试机制执行请求
const rawResponse = await withRetry(executeRequest, {
maxRetries: 3,
initialDelayMs: 2000,
onRetry: (error, attempt, delay) => {
console.warn(
`⚠️ [agentSelectModifyInit] 第${attempt}次重试,等待 ${delay}ms...`,
`[agentSelectModifyInit] 第${attempt}次重试,等待 ${delay}ms...`,
error?.message,
)
},
})
},
)
// WebSocket 返回格式: { data: {...}, generation_id, execution_id }
// REST API 返回格式: {...}
const response = rawResponse.data || rawResponse
let response = this.extractResponse<any>(rawResponse)
if (response?.scoreTable) {
response = response.scoreTable
}
const transformedData: Record<string, Record<string, { reason: string; score: number }>> = {}
// 确保 response 存在且是有效对象
if (!response || typeof response !== 'object' || Array.isArray(response)) {
console.warn('[agentSelectModifyInit] 后端返回数据格式异常:', response)
return transformedData
}
for (const [aspect, agents] of Object.entries(response)) {
for (const [agentName, scoreInfo] of Object.entries(agents as Record<string, { Reason: string; Score: number }> || {})) {
for (const [agentName, scoreInfo] of Object.entries(
(agents as Record<string, { Reason: string; Score: number }>) || {},
)) {
if (!transformedData[agentName]) {
transformedData[agentName] = {}
}
@@ -770,7 +498,14 @@ class Api {
*/
agentSelectModifyAddAspect = async (data: {
aspectList: string[]
useWebSocket?: boolean
stepTask?: {
Id?: string
StepName?: string
TaskContent?: string
InputObject_List?: string[]
OutputObject?: string
}
TaskID?: string
onProgress?: (progress: {
status: string
stage?: string
@@ -781,53 +516,33 @@ class Api {
aspectName: string
agentScores: Record<string, { score: number; reason: string }>
}> => {
const useWs = data.useWebSocket !== undefined ? data.useWebSocket : this.useWebSocketDefault
let response: Record<string, Record<string, { Reason: string; Score: number }>>
// 如果启用WebSocket且已连接使用WebSocket
if (useWs && websocket.connected) {
const rawResponse = await websocket.send(
'agent_select_modify_add_aspect',
{
aspectList: data.aspectList,
stepTask: data.stepTask,
task_id: data.TaskID || '',
},
undefined,
data.onProgress,
)
// WebSocket 返回格式: { data: {...}, generation_id, execution_id }
response = rawResponse.data || rawResponse
} else {
// 否则使用REST API
response = await request<
{
aspectList: string[]
},
Record<string, Record<string, { Reason: string; Score: number }>>
>({
url: '/agentSelectModify_addAspect',
method: 'POST',
data: {
aspectList: data.aspectList,
},
})
}
/**
* 获取新添加的维度
*/
const response = this.extractResponse<any>(rawResponse)
const newAspect = data.aspectList[data.aspectList.length - 1]
if (!newAspect) {
throw new Error('aspectList is empty')
}
const newAspectAgents = response[newAspect]
const scoreTable = response.scoreTable || response
const newAspectAgents = scoreTable[newAspect]
const agentScores: Record<string, { score: number; reason: string }> = {}
if (newAspectAgents) {
for (const [agentName, scoreInfo] of Object.entries(newAspectAgents)) {
for (const [agentName, scoreInfo] of Object.entries(newAspectAgents as Record<string, { Score?: number; score?: number; Reason?: string; reason?: string }>)) {
agentScores[agentName] = {
score: scoreInfo.Score,
reason: scoreInfo.Reason,
score: scoreInfo.Score || scoreInfo.score || 0,
reason: scoreInfo.Reason || scoreInfo.reason || '',
}
}
}
@@ -867,12 +582,126 @@ class Api {
})),
})
// WebSocket 返回格式: { data: {...}, generation_id, execution_id }
// REST API 返回格式: {...}
const response = (rawResponse.data || rawResponse) as { added_count: number }
const response = this.extractResponse<{ added_count: number }>(rawResponse)
return response?.added_count || 0
}
/**
* 保存任务分支数据
* @param taskId 任务ID
* @param branches 分支数据数组
* @returns 是否保存成功
*/
saveBranches = async (taskId: string, branches: any[]): Promise<boolean> => {
if (!websocket.connected) {
throw new Error('WebSocket未连接')
}
try {
const rawResponse = await websocket.send('save_branches', {
task_id: taskId,
branches,
})
const response = this.extractResponse<{ status: string }>(rawResponse)
return response?.status === 'success' || false
} catch (error) {
console.error('保存分支数据失败:', error)
return false
}
}
/**
* 保存任务过程分支数据
* @param TaskID 大任务ID数据库主键
* @param branches 任务过程分支数据Map结构转对象
* @returns 是否保存成功
*/
saveTaskProcessBranches = async (
TaskID: string,
branches: Record<string, Record<string, any[]>>,
): Promise<boolean> => {
if (!websocket.connected) {
throw new Error('WebSocket未连接')
}
try {
const rawResponse = await websocket.send('save_task_process_branches', {
task_id: TaskID,
branches,
})
const response = this.extractResponse<{ status: string }>(rawResponse)
return response?.status === 'success' || false
} catch (error) {
console.error('保存任务过程分支数据失败:', error)
return false
}
}
/**
* 更新任务大纲数据
* @param taskId 任务ID
* @param taskOutline 完整的大纲数据
* @returns 是否更新成功
*/
updateTaskOutline = async (taskId: string, taskOutline: any): Promise<boolean> => {
if (!websocket.connected) {
throw new Error('WebSocket未连接')
}
try {
const rawResponse = await websocket.send('save_task_outline', {
task_id: taskId,
task_outline: taskOutline,
})
const response = this.extractResponse<{ status: string }>(rawResponse)
return response?.status === 'success' || false
} catch (error) {
console.error('更新任务大纲失败:', error)
return false
}
}
/**
* 更新指定步骤的 assigned_agents
* @param params 参数对象
* @returns 是否更新成功
*/
updateAssignedAgents = async (params: {
task_id: string // 大任务ID数据库主键
step_id: string // 步骤级ID小任务UUID
agents: string[] // 选中的 agent 列表
confirmed_groups?: string[][] // 可选:确认的 agent 组合列表
agent_combinations?: Record<string, { process: any; brief: any }> // 可选agent 组合的 TaskProcess 数据
}): Promise<boolean> => {
if (!websocket.connected) {
throw new Error('WebSocket未连接')
}
try {
const rawResponse = await websocket.send('update_assigned_agents', {
task_id: params.task_id,
step_id: params.step_id,
agents: params.agents,
confirmed_groups: params.confirmed_groups,
agent_combinations: params.agent_combinations,
})
const response = this.extractResponse<{ status: string; error?: string }>(rawResponse)
if (response?.status === 'success') {
console.log('更新 assigned_agents 成功:', params)
return true
}
console.warn('更新 assigned_agents 失败:', response?.error)
return false
} catch (error) {
console.error('更新 assigned_agents 失败:', error)
return false
}
}
}
export default new Api()

View File

@@ -1,13 +1,15 @@
<script setup lang="ts">
import { ref, onMounted, onUnmounted, computed, reactive, nextTick } from 'vue'
import SvgIcon from '@/components/SvgIcon/index.vue'
import { useAgentsStore, useConfigStore } from '@/stores'
import { useAgentsStore, useConfigStore, useSelectionStore } from '@/stores'
import api from '@/api'
import websocket from '@/utils/websocket'
import { changeBriefs } from '@/utils/collaboration_Brief_FrontEnd.ts'
import { useNotification } from '@/composables/useNotification'
import AssignmentButton from './TaskTemplate/TaskSyllabus/components/AssignmentButton.vue'
import HistoryList from './TaskTemplate/HistoryList/index.vue'
import { withRetry } from '@/utils/retry'
import { Clock } from '@element-plus/icons-vue'
const emit = defineEmits<{
(e: 'search-start'): void
(e: 'search', value: string): void
@@ -15,6 +17,7 @@ const emit = defineEmits<{
const agentsStore = useAgentsStore()
const configStore = useConfigStore()
const selectionStore = useSelectionStore()
const { success, warning, error: notifyError } = useNotification()
const searchValue = ref('')
const triggerOnFocus = ref(true)
@@ -26,6 +29,16 @@ const isStopping = ref(false)
const isStopPending = ref(false)
const currentStepAbortController = ref<{ cancel: () => void } | null>(null)
const currentGenerationId = ref('')
const currentTaskID = ref('') // 后端数据库主键
// 监听 currentTaskID 变化,同步到全局变量(供分支保存使用)
watch(currentTaskID, (newVal) => {
;(window as any).__CURRENT_TASK_ID__ = newVal || ''
console.log('♻️ [Task] currentTaskID 同步到全局变量:', (window as any).__CURRENT_TASK_ID__)
})
const currentPlanOutline = ref<any>(null) // 用于恢复的完整大纲数据
const historyDialogVisible = ref(false) // 历史记录弹窗控制
// 解析URL参数
function getUrlParam(param: string): string | null {
@@ -73,7 +86,7 @@ function handleBlur() {
}
// 预加载所有任务的智能体评分数据
async function preloadAllTaskAgentScores(outlineData: any, goal: string) {
async function preloadAllTaskAgentScores(outlineData: any, goal: string, TaskID: string) {
const tasks = outlineData['Collaboration Process'] || []
if (tasks.length === 0) {
@@ -96,11 +109,13 @@ async function preloadAllTaskAgentScores(outlineData: any, goal: string) {
const agentScores = await api.agentSelectModifyInit({
goal: goal,
stepTask: {
Id: task.Id, // 小任务步骤ID用于数据库存储
StepName: task.StepName,
TaskContent: task.TaskContent,
InputObject_List: task.InputObject_List,
OutputObject: task.OutputObject
}
},
TaskID: TaskID // 后端数据库主键大任务ID
})
// 提取维度列表并存储
@@ -205,14 +220,26 @@ async function handleSearch() {
inputs: []
})
// WebSocket 返回格式: { data: {...}, generation_id, execution_id }
// WebSocket 返回格式: { data: { task_id, generation_id, basePlan }, generation_id, execution_id }
// REST API 返回格式: {...}
const outlineData = response.data || response
// 保存 generation_id
if (response && response.generation_id) {
currentGenerationId.value = response.generation_id
console.log('📋 保存 generation_id:', currentGenerationId.value)
// 提取真正的outline数据
let outlineData: any
if (response?.data?.basePlan) {
// WebSocket新格式数据嵌套在basePlan中
outlineData = response.data.basePlan
currentGenerationId.value = response.data.generation_id || ''
currentTaskID.value = response.data.task_id || ''
console.log('📋 WebSocket格式: 从basePlan提取数据, generation_id:', currentGenerationId.value, 'TaskID:', currentTaskID.value)
} else if (response?.data) {
// 可能是WebSocket旧格式或REST API格式
outlineData = response.data
currentGenerationId.value = response.generation_id || ''
currentTaskID.value = ''
console.log('📋 直接格式: generation_id:', currentGenerationId.value)
} else {
outlineData = response
currentGenerationId.value = ''
currentTaskID.value = ''
}
// 处理简报数据格式
@@ -223,15 +250,22 @@ async function handleSearch() {
emit('search', searchValue.value)
// 预加载所有任务的智能体评分数据
preloadAllTaskAgentScores(outlineData, searchValue.value)
preloadAllTaskAgentScores(outlineData, searchValue.value, currentTaskID.value)
// 填充步骤详情
isFillingSteps.value = true
const steps = outlineData['Collaboration Process'] || []
// 保存 generation_id 到本地变量,用于 fillStepTask 调用
// 保存 generation_id 和 TaskID 到本地变量,用于 fillStepTask 调用
// 这样即使前端停止时清空了 currentGenerationId当前的 fillStepTask 仍能正确停止
const fillTaskGenerationId = currentGenerationId.value
const fillTaskTaskID = currentTaskID.value
console.log('📋 开始填充步骤详情', {
generationId: fillTaskGenerationId,
TaskID: fillTaskTaskID,
stepsCount: steps.length
})
// 串行填充所有步骤的详情
try {
@@ -243,6 +277,7 @@ async function handleSearch() {
await withRetry(
async () => {
console.log(`📤 调用 fillStepTask: 步骤=${step.StepName}, TaskID=${fillTaskTaskID}`)
const detailedStep = await api.fillStepTask({
goal: searchValue.value,
stepTask: {
@@ -250,9 +285,12 @@ async function handleSearch() {
TaskContent: step.TaskContent,
InputObject_List: step.InputObject_List,
OutputObject: step.OutputObject,
Id: step.Id, // 传递步骤ID用于后端定位
},
generation_id: fillTaskGenerationId,
TaskID: fillTaskTaskID, // 后端数据库主键
})
console.log(`📥 fillStepTask 返回完成: 步骤=${step.StepName}`)
updateStepDetail(step.StepName, detailedStep)
},
{
@@ -326,6 +364,145 @@ onMounted(() => {
onUnmounted(() => {
websocket.off('generation_stopped', onGenerationStopped)
})
// 打开历史记录弹窗
const openHistoryDialog = () => {
historyDialogVisible.value = true
}
// 处理历史任务恢复
const handleRestorePlan = (plan: any) => {
console.log('♻️ [Task] 恢复历史任务:', plan)
// 1. 关闭弹窗
historyDialogVisible.value = false
// 2. 设置搜索值为历史任务的目标
searchValue.value = plan.general_goal
// 3. 设置当前任务的 task_id供分支保存使用
currentTaskID.value = plan.id || ''
if (plan.id) {
;(window as any).__CURRENT_TASK_ID__ = plan.id
console.log('♻️ [Task] 已设置 currentTaskID:', currentTaskID.value)
}
// 4. 设置分支初始化标记,告诉 PlanModification.vue 这是从历史记录恢复
// 这样 initializeFlow 会走"恢复分支"逻辑,而不是"首次初始化"
sessionStorage.setItem('plan-modification-branches-initialized', 'true')
// 4. 保存完整的任务大纲数据
currentPlanOutline.value = plan.task_outline || null
// 4. 如果有 agents_info更新到 store
if (plan.agents_info && Array.isArray(plan.agents_info)) {
agentsStore.setAgents(plan.agents_info)
}
// 5. 如果有智能体评分数据,更新到 store
if (plan.agent_scores && currentPlanOutline.value) {
const tasks = currentPlanOutline.value['Collaboration Process'] || []
console.log('🔍 [Task] 所有步骤名称:', tasks.map((t: any) => t.StepName))
console.log('🔍 [Task] agent_scores 的 key:', Object.keys(plan.agent_scores))
for (const task of tasks) {
// 使用 task.Id 作为 key
const taskId = task.Id
console.log(`🔍 [Task] 步骤使用 taskId: ${taskId}`)
console.log(`🔍 [Task] plan.agent_scores[taskId] =`, plan.agent_scores[taskId])
if (taskId && plan.agent_scores[taskId]) {
const stepScores = plan.agent_scores[taskId]
console.log(`✅ [Task] 找到步骤 ${taskId} 的评分数据`)
// 后端返回格式: { aspectList: [...], agentScores: { "Agent1": { "Aspect1": {...} } } }
// 解构获取 aspectList 和 agentScores
const { aspectList, agentScores } = stepScores
// agentScores 格式: { "Agent1": { "Aspect1": { score, reason } } }
// 直接使用,无需转换(已经是前端期望的格式)
console.log(`🔍 [Task] 步骤 ${taskId} 的 agentScores:`, agentScores)
console.log(`🔍 [Task] 步骤 ${taskId} 的维度列表:`, aspectList)
// 使用 taskId 作为 key 存储
console.log(`🔍 [Task] 调用 setTaskScoreData: taskId=${taskId}`)
agentsStore.setTaskScoreData(taskId, {
aspectList,
agentScores
})
console.log(`🔍 [Task] setTaskScoreData 完成,检查存储结果:`)
const storedData = agentsStore.getTaskScoreData(taskId)
console.log(` taskId=${taskId} 的存储结果:`, storedData)
} else {
console.log(`❌ [Task] 未找到步骤 "${taskId}" 的评分数据,尝试用 StepName 查找`)
// 兼容处理:如果找不到,尝试用 StepName
const stepName = task.StepName
if (stepName && plan.agent_scores[stepName]) {
console.log(`✅ [Task] 兼容模式:使用 StepName ${stepName} 找到评分数据`)
const stepScores = plan.agent_scores[stepName]
// 后端返回格式: { aspectList: [...], agentScores: {...} }
const { aspectList, agentScores } = stepScores
agentsStore.setTaskScoreData(taskId, {
aspectList,
agentScores
})
}
}
}
}
// 6. 将完整的大纲数据设置到 store跳过重新生成
if (currentPlanOutline.value) {
agentsStore.setAgentRawPlan({
data: currentPlanOutline.value,
loading: false
})
}
// 7. 如果有分支数据,恢复到 selection store
if (plan.branches && Array.isArray(plan.branches)) {
// 旧格式:数组,恢复任务大纲探索分支
console.log('♻️ [Task] 恢复分支数据:', { count: plan.branches.length })
selectionStore.restoreBranchesFromDB(plan.branches)
} else if (plan.branches && typeof plan.branches === 'object') {
// 新格式:对象,可能包含 flow_branches 和 task_process_branches
// 恢复任务大纲探索分支
if (plan.branches.flow_branches && Array.isArray(plan.branches.flow_branches)) {
console.log('♻️ [Task] 恢复任务大纲探索分支:', { count: plan.branches.flow_branches.length })
selectionStore.restoreBranchesFromDB(plan.branches.flow_branches)
}
// 恢复任务过程分支
if (plan.branches.task_process_branches && typeof plan.branches.task_process_branches === 'object') {
console.log('♻️ [Task] 恢复任务过程分支:', { stepCount: Object.keys(plan.branches.task_process_branches).length })
selectionStore.restoreTaskProcessBranchesFromDB(plan.branches.task_process_branches)
}
}
// 8. 如果有 agent 组合数据,恢复到 selection store
if (plan.assigned_agents && typeof plan.assigned_agents === 'object') {
console.log('♻️ [Task] 恢复 agent 组合数据:', plan.assigned_agents)
selectionStore.restoreAgentCombinationsFromDB(plan.assigned_agents, plan.id)
}
// 9. 如果有 rehearsal_log恢复执行结果
if (plan.rehearsal_log && Array.isArray(plan.rehearsal_log)) {
console.log('♻️ [Task] 恢复执行结果:', { count: plan.rehearsal_log.length })
// rehearsal_log 本身就是 IExecuteRawResponse 格式的数组
agentsStore.setExecutePlan(plan.rehearsal_log)
}
// 10. 触发搜索事件
emit('search', plan.general_goal)
success('成功', '已恢复历史任务')
}
// 暴露给父组件
defineExpose({
currentTaskID,
openHistoryDialog
})
</script>
<template>
@@ -389,8 +566,29 @@ onUnmounted(() => {
</el-button>
</div>
<AssignmentButton v-if="planReady" @click="openAgentAllocationDialog" />
<!-- 历史记录按钮 -->
<el-button
class="history-button"
circle
:title="'历史记录'"
@click.stop="openHistoryDialog"
>
<el-icon size="18px"><Clock /></el-icon>
</el-button>
</div>
</el-tooltip>
<!-- 历史记录弹窗 -->
<el-dialog
v-model="historyDialogVisible"
title="历史任务"
width="600px"
:close-on-click-modal="true"
destroy-on-close
append-to-body
>
<HistoryList @restore="handleRestorePlan" />
</el-dialog>
</template>
<style scoped lang="scss">
@@ -529,4 +727,21 @@ onUnmounted(() => {
}
}
}
// 历史记录按钮
.history-button {
position: absolute;
right: 70px;
top: 28px;
transform: translateY(-50%);
z-index: 999;
background: var(--color-bg-taskbar);
border: 1px solid var(--color-border);
color: var(--color-text-taskbar);
&:hover {
background: var(--color-bg-hover);
color: var(--color-text-hover);
}
}
</style>

View File

@@ -36,6 +36,7 @@ const handleFileSelect = (event: Event) => {
if (input.files && input.files[0]) {
const file = input.files[0]
readFileContent(file)
input.value = ''
}
}

View File

@@ -0,0 +1,376 @@
<template>
<div class="history-list">
<div class="header">
<h3>📋 历史任务</h3>
<el-button type="primary" link @click="fetchPlans">
<el-icon><Refresh /></el-icon>
刷新
</el-button>
</div>
<!-- 加载状态 -->
<div v-if="loading" class="loading">
<el-icon class="is-loading"><Loading /></el-icon>
<span>加载中...</span>
</div>
<!-- 空状态 -->
<el-empty v-else-if="plans.length === 0" description="暂无历史任务" />
<!-- 任务列表 -->
<div v-else class="plan-list">
<div
v-for="plan in plans"
:key="plan.id"
class="plan-item"
:class="{ active: selectedPlanId === plan.id }"
@click="selectPlan(plan)"
>
<div class="plan-info">
<div class="plan-goal">{{ plan.general_goal || '未知任务' }}</div>
<div class="plan-meta">
<el-tag size="small" :type="getStatusType(plan.status)">
{{ getStatusText(plan.status) }}
</el-tag>
<span class="plan-time">{{ formatTime(plan.created_at) }}</span>
</div>
<div class="plan-stats">
<span>执行次数: {{ plan.execution_count }}</span>
</div>
</div>
<div class="plan-actions">
<el-button
type="primary"
size="small"
@click.stop="restorePlan(plan)"
:disabled="restoring"
>
恢复
</el-button>
<el-button
type="danger"
size="small"
link
@click.stop="deletePlan(plan)"
>
<el-icon><Delete /></el-icon>
</el-button>
</div>
</div>
</div>
<!-- 分页 -->
<div v-if="plans.length > 0" class="pagination">
<span> {{ total }} 个任务</span>
</div>
<!-- 删除确认对话框 -->
<el-dialog
v-model="dialogVisible"
title="删除确认"
width="400px"
:close-on-click-modal="false"
>
<span>确定要删除任务 "{{ planToDelete?.general_goal }}" 此操作不可恢复</span>
<template #footer>
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="danger" @click="confirmDelete" :loading="deleting">确定删除</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue'
import { ElMessage } from 'element-plus'
import { Refresh, Loading, Delete } from '@element-plus/icons-vue'
import websocket from '@/utils/websocket'
// 事件定义
const emit = defineEmits<{
(e: 'restore', plan: PlanInfo): void
(e: 'close'): void
}>()
// 数据类型
interface PlanInfo {
id: string // 对应数据库 task_id
general_goal: string // 对应数据库 query
status: string
execution_count: number
created_at: string
// 完整恢复数据
task_outline?: any
assigned_agents?: any
agent_scores?: any
agents_info?: any[]
}
// 响应式数据
const plans = ref<PlanInfo[]>([])
const loading = ref(false)
const restoring = ref(false)
const selectedPlanId = ref<string | null>(null)
const total = ref(0)
const isConnected = ref(false)
// 删除对话框相关
const dialogVisible = ref(false)
const planToDelete = ref<PlanInfo | null>(null)
const deleting = ref(false)
// 生成唯一请求ID
let requestIdCounter = 0
const generateRequestId = () => `ws_req_${Date.now()}_${++requestIdCounter}`
// WebSocket 监听器引用(用于清理)
const historyUpdatedHandler = () => {
console.log('📡 收到历史列表更新通知,自动刷新')
fetchPlans()
}
// 获取任务列表
const fetchPlans = async () => {
loading.value = true
const reqId = generateRequestId()
try {
const result = await websocket.send('get_plans', { id: reqId })
plans.value = (result.data || []) as PlanInfo[]
total.value = plans.value.length
isConnected.value = true
} catch (error) {
console.error('获取任务列表失败:', error)
ElMessage.error('获取任务列表失败')
isConnected.value = false
} finally {
loading.value = false
}
}
// 选择任务
const selectPlan = (plan: PlanInfo) => {
selectedPlanId.value = plan.id
}
// 恢复任务
const restorePlan = async (plan: PlanInfo) => {
if (restoring.value) return
console.log('🔍 [HistoryList] 恢复计划:', plan)
console.log('🔍 [HistoryList] plan.id:', plan.id)
if (!plan.id) {
ElMessage.error('任务 ID 为空,无法恢复')
return
}
restoring.value = true
selectedPlanId.value = plan.id
const reqId = generateRequestId()
try {
const result = await websocket.send('restore_plan', {
id: reqId,
data: { plan_id: plan.id }
})
const planData = (result.data || result) as any
// 发送恢复事件
emit('restore', {
...plan,
...planData
})
ElMessage.success('任务已恢复')
} catch (error) {
console.error('恢复任务失败:', error)
ElMessage.error('恢复任务失败')
} finally {
restoring.value = false
}
}
// 删除任务
const deletePlan = (plan: PlanInfo) => {
planToDelete.value = plan
dialogVisible.value = true
}
// 确认删除
const confirmDelete = async () => {
if (!planToDelete.value) return
deleting.value = true
const reqId = generateRequestId()
try {
await websocket.send('delete_plan', {
id: reqId,
data: { plan_id: planToDelete.value.id }
})
dialogVisible.value = false
ElMessage.success('删除成功')
// 删除成功后会自动通过 history_updated 事件刷新列表
} catch (error) {
console.error('删除任务失败:', error)
ElMessage.error('删除任务失败')
} finally {
deleting.value = false
}
}
// 格式化时间
const formatTime = (timeStr: string | undefined) => {
if (!timeStr) return ''
try {
const date = new Date(timeStr)
return date.toLocaleString('zh-CN', {
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit'
})
} catch {
return timeStr
}
}
// 获取状态类型
const getStatusType = (status: string): string => {
const statusMap: Record<string, string> = {
'generating': 'warning',
'executing': 'warning',
'completed': 'success',
'stopped': 'danger'
}
return statusMap[status] || 'info'
}
// 获取状态文本
const getStatusText = (status: string): string => {
const statusMap: Record<string, string> = {
'generating': '生成中',
'executing': '执行中',
'completed': '已完成',
'stopped': '已停止'
}
return statusMap[status] || status
}
// 生命周期
onMounted(() => {
fetchPlans()
// 监听历史列表更新事件(多标签页实时同步)
websocket.on('history_updated', historyUpdatedHandler)
})
onUnmounted(() => {
// 移除事件监听
websocket.off('history_updated', historyUpdatedHandler)
})
</script>
<style scoped lang="scss">
.history-list {
padding: 16px;
height: 100%;
display: flex;
flex-direction: column;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
h3 {
margin: 0;
font-size: 16px;
font-weight: 600;
}
}
.loading {
display: flex;
align-items: center;
justify-content: center;
padding: 40px;
color: #909399;
gap: 8px;
}
.plan-list {
flex: 1;
overflow-y: auto;
}
.plan-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px;
margin-bottom: 8px;
border-radius: 8px;
background: #f5f7fa;
cursor: pointer;
transition: all 0.2s;
&:hover {
background: #ecf5ff;
}
&.active {
background: #ecf5ff;
border-left: 3px solid #409eff;
}
}
.plan-info {
flex: 1;
min-width: 0;
}
.plan-goal {
font-size: 14px;
font-weight: 500;
margin-bottom: 6px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.plan-meta {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 4px;
}
.plan-time {
font-size: 12px;
color: #909399;
}
.plan-stats {
font-size: 12px;
color: #606266;
}
.plan-actions {
display: flex;
gap: 8px;
margin-left: 12px;
}
.pagination {
display: flex;
justify-content: center;
margin-top: 16px;
color: #909399;
font-size: 13px;
}
</style>

View File

@@ -7,6 +7,7 @@ import {
useAgentsStore,
useSelectionStore,
type IRawStepTask,
type TaskProcess,
type IApiAgentAction
} from '@/stores'
import { getAgentMapIcon, getActionTypeDisplay } from '@/layout/components/config.ts'
@@ -32,9 +33,6 @@ const branchInputRef = ref<InstanceType<typeof HTMLInputElement>>()
// 分支加载状态
const branchLoading = ref(false)
// Mock 数据配置
const USE_MOCK_DATA = false
// 节点和边数据
const nodes = ref<Node[]>([])
const edges = ref<Edge[]>([])
@@ -51,6 +49,191 @@ const BRANCHES_INIT_KEY_PREFIX = 'plan-task-branches-initialized-'
//最后选中的分支ID
const LAST_SELECTED_BRANCH_KEY = 'plan-task-last-selected-branch'
// ==================== 公共函数提取 ====================
// 获取 agent 个人简介
const getAgentProfile = (agentName: string): string => {
return (
agentsStore.agents.find((agent: any) => agent.Name === agentName)?.Profile || '暂无个人简介'
)
}
// 创建 VueFlow 边的通用函数
const createFlowEdge = (
source: string,
target: string,
sourceHandle: string = 'right',
targetHandle: string = 'left',
strokeColor: string = '#43a8aa',
isFirstNode: boolean = false
): Edge => {
return {
id: `edge-${source}-${target}`,
source,
target,
sourceHandle,
targetHandle,
type: 'smoothstep',
animated: true,
style: {
stroke: strokeColor,
strokeWidth: 2,
...(isFirstNode ? { strokeDasharray: '5,5' } : {})
},
markerEnd: {
type: 'arrow' as any,
color: '#43a8aa',
width: 20,
height: 20,
strokeWidth: 2
}
}
}
// 创建分支 agent 节点的通用函数
const createBranchAgentNode = (
agentNodeId: string,
action: IApiAgentAction,
position: { x: number; y: number }
): Node => {
const agentInfo = getAgentMapIcon(action.agent)
const actionTypeInfo = getActionTypeDisplay(action.type)
const agentProfile = getAgentProfile(action.agent)
return {
id: agentNodeId,
type: 'agent',
position,
data: {
agentName: action.agent,
agentIcon: agentInfo.icon,
agentColor: agentInfo.color,
actionTypeColor: actionTypeInfo?.color || '#909399',
agentDescription: action.description || '暂无描述',
agentProfile: agentProfile,
actionTypeName: actionTypeInfo?.name || '未知职责',
actionTypeKey: actionTypeInfo?.key || 'unknown',
isRoot: false,
isBranchTask: true
}
}
}
// 保存分支到 store 的通用函数
const saveBranchToStore = (
branchType: 'root' | 'task',
newBranchNodes: Node[],
newBranchEdges: Edge[],
branchTasks: any[]
) => {
if (newBranchNodes.length === 0) return
const taskStepId = currentTask.value?.Id || ''
const currentAgents = currentTask.value?.AgentSelection || []
isSyncing = true
selectionStore.addTaskProcessBranch(taskStepId, currentAgents, {
parentNodeId: addingBranchNodeId.value!,
branchContent: branchInput.value.trim(),
branchType,
nodes: JSON.parse(JSON.stringify(newBranchNodes)),
edges: JSON.parse(JSON.stringify(newBranchEdges)),
tasks: JSON.parse(JSON.stringify(branchTasks))
})
setTimeout(() => {
isSyncing = false
}, 100)
saveTaskProcessBranchesToDB()
}
// 解析分支 API 响应的通用函数
const parseBranchResponse = (response: any): IApiAgentAction[] => {
const newAgentActions: IApiAgentAction[] = []
const responseData = response.data || response
if (responseData && responseData.length > 0) {
const firstBranch = responseData[0]
firstBranch.forEach((action: any) => {
newAgentActions.push({
id: action.ID || uuidv4(),
type: action.ActionType,
agent: action.AgentName,
description: action.Description,
inputs: action.ImportantInput || []
})
})
}
return newAgentActions
}
// 获取主流程节点 ID 列表的通用函数
const getMainProcessNodeIds = (nodeList?: Node[]): string[] => {
const targetNodes = nodeList || nodes.value
return targetNodes.filter(n => !n.data.isBranchTask && n.id !== 'root').map(n => n.id)
}
// 设置主流程高亮的通用函数
const highlightMainProcess = (nodeList?: Node[]) => {
const mainProcessNodes = getMainProcessNodeIds(nodeList)
selectedNodeIds.value = new Set(mainProcessNodes)
}
// 创建分支节点和边的通用函数
const createBranchNodesAndEdges = (
newAgentActions: IApiAgentAction[],
branchStartX: number,
branchStartY: number,
parentNodeId: string,
strokeColor: string = '#67c23a'
): { nodes: Node[]; edges: Edge[] } => {
const newBranchNodes: Node[] = []
const newBranchEdges: Edge[] = []
const timestamp = Date.now()
const agentNodeIds: string[] = []
newAgentActions.forEach((action, index) => {
const agentNodeId = `branch-agent-${parentNodeId}-${timestamp}-${index}`
agentNodeIds.push(agentNodeId)
// 计算位置:横向排列
const nodeX = branchStartX + (parentNodeId === 'root' ? 100 : 0) + index * 120
const nodeY = branchStartY
// 创建 agent 节点
const newAgentNode = createBranchAgentNode(agentNodeId, action, { x: nodeX, y: nodeY })
nodes.value.push(newAgentNode)
newBranchNodes.push(newAgentNode)
// 创建连接边
if (index === 0) {
// 第一个 agent 连接到父节点
const newEdge = createFlowEdge(parentNodeId, agentNodeId, 'bottom', 'left', strokeColor, true)
edges.value.push(newEdge)
newBranchEdges.push(newEdge)
} else {
// 后续 agent 连接到前一个 agent
const prevAgentNodeId = agentNodeIds[index - 1]!
const newEdge = createFlowEdge(
prevAgentNodeId,
agentNodeId,
'right',
'left',
strokeColor,
false
)
edges.value.push(newEdge)
newBranchEdges.push(newEdge)
}
})
return { nodes: newBranchNodes, edges: newBranchEdges }
}
// ==================== 原有函数保持不变 ====================
// 获取分支的所有节点
const getAllBranchNodes = (startNodeId: string): string[] => {
const visited = new Set<string>()
@@ -272,14 +455,44 @@ const initializeFlow = () => {
)}`
const branchesInitialized = sessionStorage.getItem(branchesInitKey) === 'true'
if (branchesInitialized && savedBranches.length > 0) {
nodes.value = []
edges.value = []
// 【修复】优先检查 store 中是否有数据,而不是依赖 sessionStorage 标记
// 原逻辑if (branchesInitialized && savedBranches.length > 0)
// 问题从历史记录恢复时store 有数据但 sessionStorage 无标记,导致重复创建"初始流程"分支
if (savedBranches.length > 0) {
// 先创建根节点和主流程节点(作为分支的基础)
nodes.value = newNodes
edges.value = newEdges
savedBranches.forEach(branch => {
// 恢复节点
branch.nodes.forEach(node => {
nodes.value.push(JSON.parse(JSON.stringify(node)))
// 找出非初始流程的分支(只处理真正需要偏移的分支)
const actualBranches = savedBranches.filter(branch => branch.branchContent !== '初始流程')
// 统计每个父节点下有多少个分支,用于计算垂直偏移
const parentBranchIndex: Record<string, number> = {}
actualBranches.forEach(branch => {
parentBranchIndex[branch.parentNodeId] = parentBranchIndex[branch.parentNodeId] || 0
})
// 恢复分支时,按顺序处理,同一父节点的分支依次向下偏移
actualBranches.forEach(branch => {
const parentNodeId = branch.parentNodeId
// 获取父节点的Y坐标
const parentNode = nodes.value.find(n => n.id === parentNodeId)
const parentNodeY = parentNode?.position.y || 150
// 获取该分支在同一父节点下的索引
const branchIndex = parentBranchIndex[parentNodeId] ?? 0
parentBranchIndex[parentNodeId] = branchIndex + 1
// 计算分支起始Y坐标父节点Y + 200 + 分支索引 * 250
const branchStartY = parentNodeY + 200 + branchIndex * 250
// 恢复节点计算新的Y坐标
branch.nodes.forEach((node, nodeIndex) => {
const restoredNode = JSON.parse(JSON.stringify(node))
// 计算新位置分支起始Y + 节点索引 * 120横向排列
restoredNode.position.y = branchStartY
nodes.value.push(restoredNode)
})
// 恢复边
branch.edges.forEach(edge => {
@@ -287,6 +500,11 @@ const initializeFlow = () => {
})
})
// 【修复】补充标记已初始化,避免后续重复创建"初始流程"分支
if (!branchesInitialized) {
sessionStorage.setItem(branchesInitKey, 'true')
}
// 恢复最后选中的分支
const lastSelectedBranchId = sessionStorage.getItem(LAST_SELECTED_BRANCH_KEY)
if (lastSelectedBranchId) {
@@ -297,9 +515,7 @@ const initializeFlow = () => {
if (lastSelectedBranch.branchContent === '初始流程') {
// 初始流程高亮所有主流程节点从恢复的nodes中获取
nodesToHighlight = nodes.value
.filter(n => !n.data.isBranchTask && n.id !== 'root')
.map(n => n.id)
nodesToHighlight = getMainProcessNodeIds()
} else {
// 其他分支:高亮主流程路径 + 分支节点(支持多级分支)
const firstBranchNode = lastSelectedBranch.nodes[0]
@@ -311,7 +527,7 @@ const initializeFlow = () => {
let currentNode = nodes.value.find(n => n.id === incomingEdge.source)
while (currentNode && currentNode.id !== 'root') {
nodesToHighlight.unshift(currentNode.id)
const prevEdge = edges.value.find(e => e.target === currentNode.id)
const prevEdge = edges.value.find(e => e.target === currentNode?.id)
currentNode = prevEdge
? nodes.value.find(n => n.id === prevEdge.source)
: undefined
@@ -330,17 +546,11 @@ const initializeFlow = () => {
selectedNodeIds.value = new Set(nodesToHighlight)
} else {
// 找不到最后选中的分支,默认选中初始流程的高亮状态
const mainProcessNodes = nodes.value
.filter(n => !n.data.isBranchTask && n.id !== 'root')
.map(n => n.id)
selectedNodeIds.value = new Set(mainProcessNodes)
highlightMainProcess()
}
} else {
// 没有保存的选中分支,默认选中初始流程的高亮状态
const mainProcessNodes = nodes.value
.filter(n => !n.data.isBranchTask && n.id !== 'root')
.map(n => n.id)
selectedNodeIds.value = new Set(mainProcessNodes)
highlightMainProcess()
}
} else {
// 首次初始化:设置节点和边,并保存为"初始流程"分支
@@ -362,14 +572,14 @@ const initializeFlow = () => {
// 标记已初始化(针对该任务步骤和 agent 组合)
sessionStorage.setItem(branchesInitKey, 'true')
// 保存任务过程分支到数据库
saveTaskProcessBranchesToDB()
// 首次初始化时,设置初始流程为当前选中分支
selectionStore.setActiveTaskProcessBranch(taskStepId, currentAgents, initialBranchId)
// 默认选中"初始流程"的高亮状态
const mainProcessNodes = newNodes
.filter(n => !n.data.isBranchTask && n.id !== 'root')
.map(n => n.id)
selectedNodeIds.value = new Set(mainProcessNodes)
highlightMainProcess(newNodes)
}
}
}
@@ -426,7 +636,7 @@ const onNodeClick = (event: any) => {
let topBranchNodeId: string | null = null
if (branchParentChain.length > 0) {
// 取父节点链的最后一个(最顶层的分支节点)
topBranchNodeId = branchParentChain[branchParentChain.length - 1]
topBranchNodeId = branchParentChain[branchParentChain.length - 1]!
} else {
// 如果没有分支父节点,当前节点就是最顶层
topBranchNodeId = nodeId
@@ -460,8 +670,12 @@ const onNodeClick = (event: any) => {
if (currentTask.value) {
const completeTaskProcess: any[] = []
// 类型守卫:检查是否为 TaskProcess 类型(有 Description 字段)
const isTaskProcess = (data: any): data is TaskProcess =>
data && 'Description' in data
// 从 store 中获取"初始流程"副本(而不是使用 taskProcess.value
const taskStepId = currentTask.value.Id
const taskStepId = currentTask.value.Id!
const currentAgents = currentTask.value.AgentSelection || []
const branches = selectionStore.getTaskProcessBranches(taskStepId, currentAgents)
const initialBranch = branches.find(branch => branch.branchContent === '初始流程')
@@ -478,7 +692,7 @@ const onNodeClick = (event: any) => {
// 主流程节点:从初始流程数据中获取
const originalIndex = node.data.originalIndex
const processData = mainProcessData[originalIndex]
if (processData && processData.ID && processData.AgentName && processData.Description) {
if (processData && isTaskProcess(processData) && processData.ID && processData.AgentName && processData.Description) {
completeTaskProcess.push(processData)
}
} else if (node.data.isBranchTask) {
@@ -490,7 +704,7 @@ const onNodeClick = (event: any) => {
const nodeIndex = parentBranch.nodes.findIndex(n => n.id === nodeId)
if (nodeIndex !== -1 && parentBranch.tasks[nodeIndex]) {
const taskData = parentBranch.tasks[nodeIndex]
if (taskData.ID && taskData.AgentName && taskData.Description) {
if (isTaskProcess(taskData) && taskData.ID && taskData.AgentName && taskData.Description) {
completeTaskProcess.push(taskData)
}
}
@@ -508,7 +722,7 @@ const onNodeClick = (event: any) => {
}
selectionStore.setActiveTaskProcessData(
currentTask.value.Id,
taskStepId,
currentAgents,
completeTaskProcess
)
@@ -521,11 +735,7 @@ const onNodeClick = (event: any) => {
}
} else {
// 点击的是主流程节点,高亮所有主流程节点(初始流程)
const mainProcessNodes = nodes.value
.filter(n => !n.data.isBranchTask && n.id !== 'root')
.map(n => n.id)
selectedNodeIds.value = new Set(mainProcessNodes)
highlightMainProcess()
// 点击主流程节点时,从 store 读取"初始流程"分支的副本
if (currentTask.value) {
@@ -542,7 +752,7 @@ const onNodeClick = (event: any) => {
selectionStore.setActiveTaskProcessData(taskStepId, currentAgents, initialBranch.tasks)
// 同步更新 currentTask.TaskProcess实现全局数据联动
agentsStore.setCurrentTaskProcess(initialBranch.tasks)
agentsStore.setCurrentTaskProcess(initialBranch.tasks as unknown as TaskProcess[])
// 保存选中的分支ID到 sessionStorage
sessionStorage.setItem(LAST_SELECTED_BRANCH_KEY, initialBranch.id)
@@ -626,44 +836,7 @@ const submitBranch = async () => {
// 判断是根节点还是 agent 节点
if (parentNodeId === 'root') {
// 根节点分支
let newAgentActions: IApiAgentAction[] = []
if (USE_MOCK_DATA) {
// 使用 Mock API
const generalGoal = agentsStore.agentRawPlan.data?.['General Goal'] || ''
// 根节点分支:从零开始生成完整方案
// Baseline_Completion = 0 表示没有已完成的部分,需要生成所有阶段
// Existing_Steps 传空数组,不传递初始流程信息
const response = await api.mockBranchTaskProcess({
branch_Number: 1,
Modification_Requirement: branchContent,
Existing_Steps: [], // ← 根节点分支不传递现有步骤
Baseline_Completion: 0, // ← 从零开始
stepTaskExisting: currentTask.value,
goal: generalGoal
})
// 后端返回格式: [[action1, action2], [action3, action4]]
// 取第一个分支
if (response && response.length > 0) {
const firstBranch = response[0]
// 直接遍历 action 数组
firstBranch.forEach((action: any) => {
// 直接使用接口返回的 ActionType
newAgentActions.push({
id: action.ID || uuidv4(),
type: action.ActionType,
agent: action.AgentName,
description: action.Description,
inputs: action.ImportantInput || []
})
})
}
ElMessage.success('[Mock] 任务流程分支创建成功')
} else {
// 调用真实 API
const generalGoal = agentsStore.agentRawPlan.data?.['General Goal'] || ''
@@ -681,126 +854,30 @@ const submitBranch = async () => {
// WebSocket 返回格式: { data: [[action1, action2], [action3, action4]], ... }
// REST API 返回格式: [[action1, action2], [action3, action4]]
const responseData = response.data || response
// 后端返回格式: [[action1, action2], [action3, action4]]
// 取第一个分支
if (responseData && responseData.length > 0) {
const firstBranch = responseData[0]
// 直接遍历 action 数组
firstBranch.forEach((action: any) => {
// 直接使用接口返回的 ActionType
newAgentActions.push({
id: action.ID || uuidv4(),
type: action.ActionType,
agent: action.AgentName,
description: action.Description,
inputs: action.ImportantInput || []
})
})
}
// 使用通用函数解析 API 响应
const newAgentActions = parseBranchResponse(response)
ElMessage.success('任务流程分支创建成功')
}
// 创建新的 agent 节点
if (newAgentActions.length > 0) {
// 计算分支起始位置:在根节点下方(固定位置)
const branchStartX = parentNode.position.x
const branchStartY = parentNode.position.y + 200 // 固定位置父节点下方200px
const timestamp = Date.now()
const agentNodeIds: string[] = []
const branchStartY = parentNode.position.y + 200
// 为每个动作创建一个 agent 节点
newAgentActions.forEach((action, index) => {
const agentNodeId = `branch-agent-${parentNodeId}-${timestamp}-${index}`
agentNodeIds.push(agentNodeId)
const agentInfo = getAgentMapIcon(action.agent)
const actionTypeInfo = getActionTypeDisplay(action.type)
// 从 agentsStore 中获取 agent 的个人简介
const agentProfile =
agentsStore.agents.find((agent: any) => agent.Name === action.agent)?.Profile ||
'暂无个人简介'
// 计算位置:横向排列
const nodeX = branchStartX + 100 + index * 120
const nodeY = branchStartY
// 创建 agent 节点
const newAgentNode: Node = {
id: agentNodeId,
type: 'agent',
position: { x: nodeX, y: nodeY },
data: {
agentName: action.agent,
agentIcon: agentInfo.icon,
agentColor: agentInfo.color,
actionTypeColor: actionTypeInfo?.color || '#909399',
agentDescription: action.description || '暂无描述',
agentProfile: agentProfile,
actionTypeName: actionTypeInfo?.name || '未知职责',
actionTypeKey: actionTypeInfo?.key || 'unknown',
isRoot: false,
isBranchTask: true // 标记为分支任务
}
}
nodes.value.push(newAgentNode)
newBranchNodes.push(newAgentNode)
// 创建连接边
if (index === 0) {
// 第一个 agent 连接到父节点(根节点)
const newEdge: Edge = {
id: `edge-${parentNodeId}-${agentNodeId}`,
source: parentNodeId,
target: agentNodeId,
sourceHandle: 'bottom',
targetHandle: 'left',
type: 'smoothstep',
animated: true,
style: { stroke: '#67c23a', strokeWidth: 2, strokeDasharray: '5,5' },
markerEnd: {
type: 'arrow' as any,
color: '#43a8aa',
width: 20,
height: 20,
strokeWidth: 2
}
}
edges.value.push(newEdge)
newBranchEdges.push(newEdge)
} else {
// 后续 agent 连接到前一个 agent
const prevAgentNodeId = agentNodeIds[index - 1]
const newEdge: Edge = {
id: `edge-${prevAgentNodeId}-${agentNodeId}`,
source: prevAgentNodeId,
target: agentNodeId,
sourceHandle: 'right',
targetHandle: 'left',
type: 'smoothstep',
animated: true,
style: { stroke: '#67c23a', strokeWidth: 2 },
markerEnd: {
type: 'arrow' as any,
color: '#43a8aa',
width: 20,
height: 20,
strokeWidth: 2
}
}
edges.value.push(newEdge)
newBranchEdges.push(newEdge)
}
})
// 使用通用函数创建分支节点和边
const result = createBranchNodesAndEdges(
newAgentActions,
branchStartX,
branchStartY,
parentNodeId,
'#67c23a' // 根节点分支颜色
)
newBranchNodes.push(...result.nodes)
newBranchEdges.push(...result.edges)
// 保存分支数据到 store
if (newBranchNodes.length > 0) {
// 将 IApiAgentAction 转换为 TaskProcess 格式用于存储
// 与 fill-step-task-mock.ts 中的 TaskProcess 格式保持一致
const branchTasks = newAgentActions.map(action => ({
ID: action.id || uuidv4(),
ActionType: action.type,
@@ -809,113 +886,14 @@ const submitBranch = async () => {
ImportantInput: action.inputs || []
}))
// 使用任务过程分支存储
// 注意:需要对 nodes 和 edges 进行深拷贝,避免保存响应式引用
const taskStepId = currentTask.value?.Id || ''
const currentAgents = currentTask.value?.AgentSelection || []
isSyncing = true // 设置标志,避免 watch 触发重复同步
selectionStore.addTaskProcessBranch(taskStepId, currentAgents, {
parentNodeId: parentNodeId,
branchContent: branchContent,
branchType: 'root',
nodes: JSON.parse(JSON.stringify(newBranchNodes)),
edges: JSON.parse(JSON.stringify(newBranchEdges)),
tasks: JSON.parse(JSON.stringify(branchTasks))
})
setTimeout(() => {
isSyncing = false
}, 100)
saveBranchToStore('root', newBranchNodes, newBranchEdges, branchTasks)
}
}
} else {
// Agent 节点分支
const parentIsBranchTask = parentNode.data.isBranchTask || false
const parentOriginalIndex = parentNode.data.originalIndex ?? 0
let newAgentActions: IApiAgentAction[] = []
if (USE_MOCK_DATA) {
// 使用 Mock API
const generalGoal = agentsStore.agentRawPlan.data?.['General Goal'] || ''
const currentTaskProcess = taskProcess.value || []
// 根据父节点类型构建 existingSteps
let existingSteps: any[] = []
let baselineCompletion = 100
if (!parentIsBranchTask) {
// 父节点是主流程节点:传递主流程从 0 到父节点的步骤
baselineCompletion =
currentTaskProcess.length > 0
? Math.round(((parentOriginalIndex + 1) / currentTaskProcess.length) * 100)
: 100
existingSteps = currentTaskProcess.slice(0, parentOriginalIndex + 1).map((p: any) => ({
ID: p.ID,
ActionType: p.ActionType,
AgentName: p.AgentName,
Description: p.Description,
ImportantInput: p.ImportantInput || []
}))
} else {
// 父节点是分支节点:从分支数据中获取步骤
const taskStepId = currentTask.value?.Id || ''
const currentAgents = currentTask.value?.AgentSelection || []
const branches = selectionStore.getTaskProcessBranches(taskStepId, currentAgents)
// 找到父节点所属的分支
const parentBranch = branches.find(branch =>
branch.nodes.some(n => n.id === parentNode.id)
)
if (parentBranch && parentBranch.tasks) {
// 获取分支中从第一个节点到父节点的所有步骤
const parentIndexInBranch = parentBranch.nodes.findIndex(n => n.id === parentNode.id)
existingSteps = parentBranch.tasks.slice(0, parentIndexInBranch + 1).map((p: any) => ({
ID: p.ID,
ActionType: p.ActionType,
AgentName: p.AgentName,
Description: p.Description,
ImportantInput: p.ImportantInput || []
}))
// 分支节点的基线完成度:根据主流程进度计算
baselineCompletion =
currentTaskProcess.length > 0
? Math.round(((parentOriginalIndex + 1) / currentTaskProcess.length) * 100)
: 100
}
}
// 调用 Mock API
const response = await api.mockBranchTaskProcess({
branch_Number: 1,
Modification_Requirement: branchContent,
Existing_Steps: existingSteps,
Baseline_Completion: baselineCompletion,
stepTaskExisting: currentTask.value,
goal: generalGoal
})
// 后端返回格式: [[action1, action2], [action3, action4]]
// 取第一个分支
if (response && response.length > 0) {
const firstBranch = response[0]
// 直接遍历 action 数组
firstBranch.forEach((action: any) => {
// 直接使用接口返回的 ActionType
newAgentActions.push({
id: action.ID || uuidv4(),
type: action.ActionType,
agent: action.AgentName,
description: action.Description,
inputs: action.ImportantInput || []
})
})
}
ElMessage.success('[Mock] 任务流程分支创建成功')
} else {
// 调用真实 API
const generalGoal = agentsStore.agentRawPlan.data?.['General Goal'] || ''
const currentTaskProcess = taskProcess.value || []
@@ -945,9 +923,7 @@ const submitBranch = async () => {
const branches = selectionStore.getTaskProcessBranches(taskStepId, currentAgents)
// 找到父节点所属的分支
const parentBranch = branches.find(branch =>
branch.nodes.some(n => n.id === parentNode.id)
)
const parentBranch = branches.find(branch => branch.nodes.some(n => n.id === parentNode.id))
if (parentBranch && parentBranch.tasks) {
// 获取分支中从第一个节点到父节点的所有步骤
@@ -977,123 +953,27 @@ const submitBranch = async () => {
goal: generalGoal
})
// WebSocket 返回格式: { data: [[action1, action2], [action3, action4]], ... }
// REST API 返回格式: [[action1, action2], [action3, action4]]
const responseData = response.data || response
// 后端返回格式: [[action1, action2], [action3, action4]]
// 取第一个分支
if (responseData && responseData.length > 0) {
const firstBranch = responseData[0]
// 直接遍历 action 数组
firstBranch.forEach((action: any) => {
// 直接使用接口返回的 ActionType
newAgentActions.push({
id: action.ID || uuidv4(),
type: action.ActionType,
agent: action.AgentName,
description: action.Description,
inputs: action.ImportantInput || []
})
})
}
// 使用通用函数解析 API 响应
const newAgentActions = parseBranchResponse(response)
ElMessage.success('任务流程分支创建成功')
}
// 创建新的 agent 节点
if (newAgentActions.length > 0) {
// 计算分支起始位置:在父 agent 节点右下方(固定位置)
const branchStartX = parentNode.position.x + 150
const branchStartY = parentNode.position.y + 200 // 固定位置父节点下方200px
const timestamp = Date.now()
const agentNodeIds: string[] = []
const branchStartY = parentNode.position.y + 200
// 为每个动作创建一个 agent 节点
newAgentActions.forEach((action, index) => {
const agentNodeId = `branch-agent-${parentNodeId}-${timestamp}-${index}`
agentNodeIds.push(agentNodeId)
const agentInfo = getAgentMapIcon(action.agent)
const actionTypeInfo = getActionTypeDisplay(action.type)
// 从 agentsStore 中获取 agent 的个人简介
const agentProfile =
agentsStore.agents.find((agent: any) => agent.Name === action.agent)?.Profile ||
'暂无个人简介'
// 计算位置:横向排列
const nodeX = branchStartX + index * 120
const nodeY = branchStartY
// 创建 agent 节点
const newAgentNode: Node = {
id: agentNodeId,
type: 'agent',
position: { x: nodeX, y: nodeY },
data: {
agentName: action.agent,
agentIcon: agentInfo.icon,
agentColor: agentInfo.color,
actionTypeColor: actionTypeInfo?.color || '#909399',
agentDescription: action.description || '暂无描述',
agentProfile: agentProfile,
actionTypeName: actionTypeInfo?.name || '未知职责',
actionTypeKey: actionTypeInfo?.key || 'unknown',
isRoot: false,
isBranchTask: true // 标记为分支任务
}
}
nodes.value.push(newAgentNode)
newBranchNodes.push(newAgentNode)
// 创建连接边
if (index === 0) {
// 第一个 agent 连接到父节点
const newEdge: Edge = {
id: `edge-${parentNodeId}-${agentNodeId}`,
source: parentNodeId,
target: agentNodeId,
sourceHandle: 'bottom',
targetHandle: 'left',
type: 'smoothstep',
animated: true,
style: { stroke: '#409eff', strokeWidth: 2, strokeDasharray: '5,5' },
markerEnd: {
type: 'arrow' as any,
color: '#43a8aa',
width: 20,
height: 20,
strokeWidth: 2
}
}
edges.value.push(newEdge)
newBranchEdges.push(newEdge)
} else {
// 后续 agent 连接到前一个 agent
const prevAgentNodeId = agentNodeIds[index - 1]
const newEdge: Edge = {
id: `edge-${prevAgentNodeId}-${agentNodeId}`,
source: prevAgentNodeId,
target: agentNodeId,
sourceHandle: 'right',
targetHandle: 'left',
type: 'smoothstep',
animated: true,
style: { stroke: '#409eff', strokeWidth: 2 },
markerEnd: {
type: 'arrow' as any,
color: '#43a8aa',
width: 20,
height: 20,
strokeWidth: 2
}
}
edges.value.push(newEdge)
newBranchEdges.push(newEdge)
}
})
// 使用通用函数创建分支节点和边
const result = createBranchNodesAndEdges(
newAgentActions,
branchStartX,
branchStartY,
parentNodeId,
'#409eff' // Agent 节点分支颜色
)
newBranchNodes.push(...result.nodes)
newBranchEdges.push(...result.edges)
// 保存分支数据到 store
if (newBranchNodes.length > 0) {
@@ -1105,22 +985,7 @@ const submitBranch = async () => {
ImportantInput: action.inputs || []
}))
// 使用任务过程分支存储
// 注意:需要对 nodes 和 edges 进行深拷贝,避免保存响应式引用
const taskStepId = currentTask.value?.Id || ''
const currentAgents = currentTask.value?.AgentSelection || []
isSyncing = true // 设置标志,避免 watch 触发重复同步
selectionStore.addTaskProcessBranch(taskStepId, currentAgents, {
parentNodeId: parentNodeId,
branchContent: branchContent,
branchType: 'task',
nodes: JSON.parse(JSON.stringify(newBranchNodes)),
edges: JSON.parse(JSON.stringify(newBranchEdges)),
tasks: JSON.parse(JSON.stringify(branchTasks))
})
setTimeout(() => {
isSyncing = false
}, 100)
saveBranchToStore('task', newBranchNodes, newBranchEdges, branchTasks)
}
}
}
@@ -1154,6 +1019,20 @@ const handleBranchKeydown = (event: KeyboardEvent) => {
}
onConnect(params => addEdges(params))
// 保存任务过程分支到数据库
const saveTaskProcessBranchesToDB = async () => {
const TaskID = (window as any).__CURRENT_TASK_ID__
if (TaskID) {
try {
await selectionStore.saveTaskProcessBranchesToDB(TaskID)
} catch (error) {
console.error('保存任务过程分支数据失败:', error)
}
} else {
console.warn('[saveTaskProcessBranchesToDB] 未找到 TaskID跳过保存')
}
}
</script>
<template>

View File

@@ -1,183 +0,0 @@
// 模拟后端原始返回格式的 Mock 数据 - fill_stepTask_TaskProcess 接口
// 后端返回格式: IRawStepTask { StepName, TaskContent, InputObject_List, OutputObject, AgentSelection, TaskProcess, Collaboration_Brief_frontEnd }
import type { IRawStepTask } from '@/stores'
// TaskProcess 项格式
interface RawTaskProcessItem {
ID: string
ActionType: string
AgentName: string
Description: string
ImportantInput: string[]
}
// Collaboration_Brief_frontEnd 数据项格式
interface RawBriefDataItem {
text: string
color: number[] // [h, s, l]
}
// 后端返回的完整数据格式
export interface RawAgentTaskProcessResponse {
StepName: string
TaskContent: string
InputObject_List: string[]
OutputObject: string
AgentSelection: string[]
TaskProcess: RawTaskProcessItem[]
Collaboration_Brief_frontEnd?: {
template: string
data: Record<string, RawBriefDataItem>
}
}
// 模拟后端返回的原始数据结构(与后端缓存数据格式一致)
// 使用与 AgentAssignmentBackendMock 相同的 agent 列表
export const mockBackendAgentTaskProcessData: RawAgentTaskProcessResponse = {
StepName: '腐蚀类型识别',
TaskContent: '分析船舶制造中常见的材料腐蚀类型及其成因。',
InputObject_List: [],
OutputObject: '腐蚀类型及成因列表',
AgentSelection: ['腐蚀机理研究员', '实验材料学家', '防护工程专家'],
TaskProcess: [
{
ID: 'action_101',
ActionType: 'Propose',
AgentName: '腐蚀机理研究员',
Description: '分析海洋环境下的腐蚀机理,确定关键防护要素',
ImportantInput: ['海洋环境参数', '防护性能指标'],
},
{
ID: 'action_102',
ActionType: 'Critique',
AgentName: '实验材料学家',
Description: '基于腐蚀机理分析结果,设计涂层材料的基础配方',
ImportantInput: ['腐蚀机理分析结果', '涂层材料配方'],
},
{
ID: 'action_103',
ActionType: 'Improve',
AgentName: '防护工程专家',
Description: '筛选适用于防护涂层的二维材料,评估其性能潜力',
ImportantInput: ['材料配方设计', '涂层材料配方'],
},
{
ID: 'action_104',
ActionType: 'Finalize',
AgentName: '实验材料学家',
Description: '制定涂层材料性能测试实验方案,包括测试指标和方法',
ImportantInput: ['二维材料筛选结果', '防护性能指标'],
},
{
ID: 'action_105',
ActionType: 'Critique',
AgentName: '防护工程专家',
Description: '模拟海洋流体环境对涂层材料的影响,优化涂层结构',
ImportantInput: ['实验方案', '海洋环境参数'],
},
{
ID: 'action_106',
ActionType: 'Improve',
AgentName: '腐蚀机理研究员',
Description: '综合评估涂层材料的防护性能,提出改进建议',
ImportantInput: ['流体力学模拟结果', '实验材料学测试结果', '二维材料性能数据'],
},
{
ID: 'action_107',
ActionType: 'Improve',
AgentName: '实验材料学家',
Description: '整理研发数据和测试结果,撰写完整的研发报告',
ImportantInput: ['综合性能评估', '所有研发数据'],
},
],
Collaboration_Brief_frontEnd: {
template: '基于!<0>!、!<1>!和!<2>!!<3>!、!<4>!、!<5>!和!<6>!执行!<7>!任务,以获得!<8>!。',
data: {
'0': {
text: '涂层材料配方',
color: [120, 60, 70], // hsl(120, 60%, 70%)
},
'1': {
text: '海洋环境参数',
color: [120, 60, 70], // hsl(120, 60%, 70%)
},
'2': {
text: '防护性能指标',
color: [120, 60, 70], // hsl(120, 60%, 70%)
},
'3': {
text: '腐蚀机理研究员',
color: [0, 0, 90], // hsl(0, 0%, 90%)
},
'4': {
text: '先进材料研发员',
color: [0, 0, 90], // hsl(0, 0%, 90%)
},
'5': {
text: '二维材料科学家',
color: [0, 0, 90], // hsl(0, 0%, 90%)
},
'6': {
text: '实验材料学家',
color: [0, 0, 90], // hsl(0, 0%, 90%)
},
'7': {
text: '研发适用于海洋环境的耐腐蚀防护涂层材料,并进行性能测试与评估',
color: [0, 0, 87], // hsl(0, 0%, 87%)
},
'8': {
text: '防护涂层材料研发报告',
color: [30, 100, 80], // hsl(30, 100%, 80%)
},
},
},
}
// 模拟后端API调用 - fill_stepTask_TaskProcess
export const mockBackendFillAgentTaskProcess = async (
goal: string,
stepTask: any,
agents: string[],
): Promise<RawAgentTaskProcessResponse> => {
// 模拟网络延迟 500ms
await new Promise((resolve) => setTimeout(resolve, 500))
// 在真实场景中,后端会根据传入的 goal、stepTask 和 agents 生成不同的 TaskProcess
// 这里我们直接返回预设的 Mock 数据
// 可以根据传入的 agents 动态修改 AgentSelection 和 TaskProcess
// 确保 agents 数组不为空
const safeAgents = agents.length > 0 ? agents : ['腐蚀机理研究员']
const responseData: RawAgentTaskProcessResponse = {
...mockBackendAgentTaskProcessData,
AgentSelection: agents,
TaskProcess: mockBackendAgentTaskProcessData.TaskProcess.map((action, index) => ({
...action,
AgentName: safeAgents[index % safeAgents.length],
})),
Collaboration_Brief_frontEnd: mockBackendAgentTaskProcessData.Collaboration_Brief_frontEnd
? {
template: mockBackendAgentTaskProcessData.Collaboration_Brief_frontEnd.template,
data: { ...mockBackendAgentTaskProcessData.Collaboration_Brief_frontEnd.data },
}
: undefined,
}
// 更新 Collaboration_Brief_frontEnd.data 中的 agent 引用
if (responseData.Collaboration_Brief_frontEnd?.data) {
const agentCount = Math.min(safeAgents.length, 4) // 最多4个agent
for (let i = 0; i < agentCount; i++) {
const key = String(i + 3) // agent从索引3开始
if (responseData.Collaboration_Brief_frontEnd.data[key]) {
responseData.Collaboration_Brief_frontEnd.data[key] = {
...responseData.Collaboration_Brief_frontEnd.data[key],
text: safeAgents[i]!,
}
}
}
}
return responseData
}

View File

@@ -14,9 +14,15 @@ import websocket from '@/utils/websocket'
import Notification from '@/components/Notification/Notification.vue'
import { useNotification } from '@/composables/useNotification'
// 定义组件 props
const props = defineProps<{
TaskID?: string // 任务唯一标识,用于写入数据库
}>()
// 定义组件事件
const emit = defineEmits<{
(e: 'refreshLine'): void
(el: 'setCurrentTask', task: IRawStepTask): void
(e: 'setCurrentTask', task: IRawStepTask): void
}>()
const agentsStore = useAgentsStore()
@@ -411,7 +417,10 @@ async function executeBatchSteps(readySteps: IRawStepTask[]) {
isStreaming.value = true
currentExecutionId.value = executionId
},
currentExecutionId.value || undefined
currentExecutionId.value || undefined,
undefined,
undefined,
props.TaskID || undefined
)
})
}
@@ -961,7 +970,8 @@ async function restartFromStep(stepIndex: number) {
},
newExecutionId, // 传入前端生成的 execution_id
stepIndex,
truncatedLog
truncatedLog,
props.TaskID || undefined // 传入 TaskID 用于更新数据库
)
success('重新执行', `正在从步骤 ${stepIndex + 1} 重新执行...`)

View File

@@ -1,12 +1,5 @@
<template>
<div class="plan-modification">
<!-- 全局加载提示 -->
<div v-if="branchFillingStatus.isFilling" class="branch-loading-hint">
<el-icon class="is-loading"><Loading /></el-icon>
<span>{{ branchFillingStatus.message }}</span>
<span class="progress">{{ branchFillingStatus.current }}/{{ branchFillingStatus.total }}</span>
</div>
<div
v-loading="agentsStore.agentRawPlan.loading || branchLoading"
class="flow-wrapper"
@@ -43,6 +36,9 @@
@add-branch="handleAddBranch"
@start-add-branch="handleStartAddBranch"
@cancel-add-branch="handleCancelAddBranch"
@save-task="handleSaveTask"
@edit-task="handleEditTask"
@cancel-edit="handleCancelEdit"
/>
</template>
</VueFlow>
@@ -56,30 +52,18 @@ import { VueFlow, useVueFlow } from '@vue-flow/core'
import type { Node, Edge } from '@vue-flow/core'
import { useAgentsStore, useSelectionStore, type IRawStepTask } from '@/stores'
import { ElMessage } from 'element-plus'
import { Loading } from '@element-plus/icons-vue'
import api from '@/api'
import { useNotification } from '@/composables/useNotification'
import '@vue-flow/core/dist/style.css'
import '@vue-flow/core/dist/theme-default.css'
import '@vue-flow/minimap/dist/style.css'
import '@vue-flow/controls/dist/style.css'
// 节点组件
import RootNode from './components/RootNode.vue'
import TaskNode from './components/TaskNode.vue'
const agentsStore = useAgentsStore()
const selectionStore = useSelectionStore()
// Mock 数据开关
const USE_MOCK_DATA = false
// 分支详情填充状态
const branchFillingStatus = ref({
isFilling: false,
current: 0,
total: 0,
message: ''
})
const { progress, updateProgress, removeNotification } = useNotification()
// 获取协作流程数据
const collaborationProcess = computed(() => {
@@ -427,6 +411,9 @@ const initializeFlow = () => {
})
sessionStorage.setItem(BRANCHES_INIT_KEY, 'true')
// 保存分支数据到数据库
saveBranchesToDB()
}
// 从 store 恢复分支
@@ -497,19 +484,8 @@ watch(
})
const nodeIndex = nodes.value.findIndex(n => n.id === node.id)
// if (nodeIndex !== -1) {
// const updatedNode = {
// ...nodes.value[nodeIndex],
// data: {
// ...nodes.value[nodeIndex].data,
// task: { ...newTask },
// updateKey: newUpdateKey
// }
// }
// nodes.value.splice(nodeIndex, 1, updatedNode)
// }
if (nodeIndex !== -1) {
const existingNode = nodes.value[nodeIndex]
const existingNode = nodes.value[nodeIndex]!
const existingData = existingNode?.data || {}
const updatedNode = {
...existingNode,
@@ -544,7 +520,7 @@ onMounted(() => {
const nodeIndex = nodes.value.findIndex(n => n.id === node.id)
if (nodeIndex !== -1) {
const existingNode = nodes.value[nodeIndex]
const existingNode = nodes.value[nodeIndex]!
const existingData = existingNode?.data || {}
const updatedNode = {
...existingNode,
@@ -608,7 +584,7 @@ const onNodeClick = (event: any) => {
let topBranchNodeId: string | null = null
if (branchParentChain.length > 0) {
topBranchNodeId = branchParentChain[branchParentChain.length - 1]
topBranchNodeId = branchParentChain[branchParentChain.length - 1]!
} else {
topBranchNodeId = nodeId
}
@@ -696,6 +672,71 @@ const handleCancelAddBranch = () => {
currentAddingBranchNodeId.value = null
}
// 开始编辑任务
const handleEditTask = (nodeId: string) => {
// 更新节点的编辑状态
updateNode(nodeId, {
data: {
...findNode(nodeId)?.data,
isEditing: true
}
})
}
// 保存任务编辑
const handleSaveTask = async (nodeId: string, newContent: string) => {
const node = findNode(nodeId)
if (!node) return
// 更新节点的 TaskContent
const updatedData = {
...node.data,
task: {
...node.data.task,
TaskContent: newContent
},
isEditing: false
}
updateNode(nodeId, { data: updatedData })
// 找到当前节点所属的分支,同步更新
const allBranches = selectionStore.getAllFlowBranches()
for (const branch of allBranches) {
const branchNode = branch.nodes.find(n => n.id === nodeId)
if (branchNode) {
// 找到分支中的对应任务并更新
const taskIndex = branch.tasks.findIndex(t => t.Id === node.data.task.Id)
if (taskIndex !== -1) {
branch.tasks[taskIndex]!.TaskContent = newContent
console.log('[handleSaveTask] 已更新分支中的任务内容:', {
branchContent: branch.branchContent,
taskName: node.data.task.StepName
})
}
break
}
}
// 保存到数据库
saveBranchesToDB()
}
// 取消编辑任务
const handleCancelEdit = (nodeId: string) => {
const node = findNode(nodeId)
if (!node) return
// 恢复编辑状态
updateNode(nodeId, {
data: {
...node.data,
isEditing: false
}
})
}
// 添加分支
const handleAddBranch = async (taskId: string, branchContent: string) => {
// 获取父节点数据
@@ -744,45 +785,6 @@ const handleAddBranch = async (taskId: string, branchContent: string) => {
const newBranchNodes: Node[] = []
const newBranchEdges: Edge[] = []
if (USE_MOCK_DATA) {
// 使用 Mock API
const generalGoal = agentsStore.agentRawPlan.data?.['General Goal'] || ''
const initialInput = agentsStore.agentRawPlan.data?.['Initial Input Object'] || []
// 根节点分支:从零开始生成完整方案
const response = await api.mockBranchPlanOutline({
branch_Number: 1,
Modification_Requirement: branchContent,
Existing_Steps: [],
Baseline_Completion: 0,
initialInputs: Array.isArray(initialInput) ? initialInput : [initialInput],
goal: generalGoal
})
// 从响应中获取协作流程数据
newTasks = response['Collaboration Process'] || []
// ========== 填充每个任务的 TaskProcess ==========
for (let i = 0; i < newTasks.length; i++) {
const task = newTasks[i]
if (!task) continue
try {
const filledTask = await api.mockFillStepTask({
goal: generalGoal,
stepTask: task
})
// 更新任务的 AgentSelection 和 TaskProcess
newTasks[i]!.AgentSelection = filledTask.AgentSelection
newTasks[i]!.TaskProcess = filledTask.TaskProcess
newTasks[i]!.Collaboration_Brief_frontEnd = filledTask.Collaboration_Brief_frontEnd
} catch (error) {
console.error(error)
}
}
ElMessage.success('任务大纲分支创建成功')
} else {
// 调用真实 API
const generalGoal = agentsStore.agentRawPlan.data?.['General Goal'] || ''
const initialInput = agentsStore.agentRawPlan.data?.['Initial Input Object'] || []
@@ -865,7 +867,7 @@ const handleAddBranch = async (taskId: string, branchContent: string) => {
edges.value.push(newEdge)
newBranchEdges.push(newEdge)
} else {
const prevTaskNodeId = taskNodeIds[index - 1]
const prevTaskNodeId = taskNodeIds[index - 1]!
const newEdge: Edge = {
id: `edge-${prevTaskNodeId}-${taskNodeId}`,
source: prevTaskNodeId,
@@ -900,6 +902,9 @@ const handleAddBranch = async (taskId: string, branchContent: string) => {
ElMessage.success('任务大纲分支创建成功')
// 保存分支数据到数据库
saveBranchesToDB()
// 适应视图,让用户先看到大纲
nextTick(() => {
setTimeout(() => {
@@ -911,15 +916,14 @@ const handleAddBranch = async (taskId: string, branchContent: string) => {
branchLoading.value = false
// ========== 异步填充详情(后台进行)==========
branchFillingStatus.value = {
isFilling: true,
current: 0,
total: newTasks.length,
message: '正在生成分支任务协作流程...'
}
const notificationId = progress('正在生成分支任务协作流程...', 0, newTasks.length)
// 带重试的填充函数
const fillStepWithRetry = async (task: IRawStepTask, index: number, retryCount = 0): Promise<void> => {
const fillStepWithRetry = async (
task: IRawStepTask,
index: number,
retryCount = 0
): Promise<void> => {
const maxRetries = 2
try {
@@ -934,7 +938,7 @@ const handleAddBranch = async (taskId: string, branchContent: string) => {
newTasks[index]!.Collaboration_Brief_frontEnd = filledTask.Collaboration_Brief_frontEnd
// 更新节点数据
const taskNodeId = taskNodeIds[index]
const taskNodeId = taskNodeIds[index]!
updateNode(taskNodeId, {
data: {
...findNode(taskNodeId)?.data,
@@ -944,10 +948,12 @@ const handleAddBranch = async (taskId: string, branchContent: string) => {
})
// 更新进度
branchFillingStatus.value.current = index + 1
updateProgress(notificationId, index + 1, newTasks.length)
} catch (error) {
console.error(`填充分支任务 ${task.StepName} 详情失败 (尝试 ${retryCount + 1}/${maxRetries + 1}):`, error)
console.error(
`填充分支任务 ${task.StepName} 详情失败 (尝试 ${retryCount + 1}/${maxRetries + 1}):`,
error
)
if (retryCount < maxRetries) {
// 延迟后重试
@@ -966,13 +972,17 @@ const handleAddBranch = async (taskId: string, branchContent: string) => {
// 更新 store 中的任务数据
const allBranches = selectionStore.getAllFlowBranches()
const currentBranch = allBranches.find(b => b.branchContent === branchContent && b.parentNodeId === taskId)
const currentBranch = allBranches.find(
b => b.branchContent === branchContent && b.parentNodeId === taskId
)
if (currentBranch) {
currentBranch.tasks = JSON.parse(JSON.stringify(newTasks))
}
branchFillingStatus.value.isFilling = false
}
// 填充完成后再次保存分支数据到数据库(确保 TaskProcess 等完整数据被保存)
saveBranchesToDB()
removeNotification(notificationId)
}
} else {
// ========== 任务节点级别分支 ==========
@@ -985,95 +995,6 @@ const handleAddBranch = async (taskId: string, branchContent: string) => {
// 判断父节点是否是分支节点
const parentIsBranchTask = parentNode.data.isBranchTask || false
if (USE_MOCK_DATA) {
// 使用 Mock API
const generalGoal = agentsStore.agentRawPlan.data?.['General Goal'] || ''
const initialInput = agentsStore.agentRawPlan.data?.['Initial Input Object'] || []
// 根据父节点类型构建 existingSteps
let existingSteps: any[] = []
let baselineCompletion = 0
if (!parentIsBranchTask) {
// 父节点是主流程节点:传递主流程从 0 到父节点的步骤
const parentTaskIndex = collaborationProcess.value.findIndex(
task => task.StepName === parentNode.data.task.StepName
)
const parentIndex = parentTaskIndex >= 0 ? parentTaskIndex : 0
existingSteps = collaborationProcess.value.slice(0, parentIndex + 1).map(step => ({
StepName: step.StepName,
TaskContent: step.TaskContent,
InputObject_List: step.InputObject_List || [],
OutputObject: step.OutputObject || ''
}))
baselineCompletion =
collaborationProcess.value.length > 0
? Math.round(((parentIndex + 1) / collaborationProcess.value.length) * 100)
: 0
} else {
// 父节点是分支节点:从分支数据中获取步骤
const branches = selectionStore.getAllFlowBranches()
// 找到父节点所属的分支
const parentBranch = branches.find(branch =>
branch.nodes.some(n => n.id === parentNode.id)
)
if (parentBranch && parentBranch.tasks) {
// 获取分支中从第一个节点到父节点的所有步骤
const parentIndexInBranch = parentBranch.nodes.findIndex(n => n.id === parentNode.id)
existingSteps = parentBranch.tasks.slice(0, parentIndexInBranch + 1).map(step => ({
StepName: step.StepName,
TaskContent: step.TaskContent,
InputObject_List: step.InputObject_List || [],
OutputObject: step.OutputObject || ''
}))
// 分支节点的基线完成度:根据主流程进度计算
const parentTaskIndex = collaborationProcess.value.findIndex(
task => task.StepName === parentNode.data.task.StepName
)
const parentIndex = parentTaskIndex >= 0 ? parentTaskIndex : 0
baselineCompletion =
collaborationProcess.value.length > 0
? Math.round(((parentIndex + 1) / collaborationProcess.value.length) * 100)
: 0
}
}
const response = await api.mockBranchPlanOutline({
branch_Number: 1,
Modification_Requirement: branchContent,
Existing_Steps: existingSteps,
Baseline_Completion: baselineCompletion,
initialInputs: Array.isArray(initialInput) ? initialInput : [initialInput],
goal: generalGoal
})
// 从响应中获取协作流程数据
newTasks = response['Collaboration Process'] || []
for (let i = 0; i < newTasks.length; i++) {
const task = newTasks[i]
if (!task) continue
try {
const filledTask = await api.mockFillStepTask({
goal: generalGoal,
stepTask: task
})
// 更新任务的 AgentSelection 和 TaskProcess
newTasks[i]!.AgentSelection = filledTask.AgentSelection
newTasks[i]!.TaskProcess = filledTask.TaskProcess
newTasks[i]!.Collaboration_Brief_frontEnd = filledTask.Collaboration_Brief_frontEnd
} catch (error) {
console.error(error)
}
}
ElMessage.success('任务大纲分支创建成功')
} else {
// 调用真实 API
const generalGoal = agentsStore.agentRawPlan.data?.['General Goal'] || ''
const initialInput = agentsStore.agentRawPlan.data?.['Initial Input Object'] || []
@@ -1105,9 +1026,7 @@ const handleAddBranch = async (taskId: string, branchContent: string) => {
const branches = selectionStore.getAllFlowBranches()
// 找到父节点所属的分支
const parentBranch = branches.find(branch =>
branch.nodes.some(n => n.id === parentNode.id)
)
const parentBranch = branches.find(branch => branch.nodes.some(n => n.id === parentNode.id))
if (parentBranch && parentBranch.tasks) {
// 获取分支中从第一个节点到父节点的所有步骤
@@ -1247,6 +1166,9 @@ const handleAddBranch = async (taskId: string, branchContent: string) => {
ElMessage.success('任务大纲分支创建成功')
// 保存分支数据到数据库
saveBranchesToDB()
// 适应视图,让用户先看到大纲
nextTick(() => {
setTimeout(() => {
@@ -1258,15 +1180,14 @@ const handleAddBranch = async (taskId: string, branchContent: string) => {
branchLoading.value = false
// ========== 异步填充详情(后台进行)==========
branchFillingStatus.value = {
isFilling: true,
current: 0,
total: newTasks.length,
message: '正在生成分支任务协作流程...'
}
const notificationId = progress('正在生成分支任务协作流程...', 0, newTasks.length)
// 带重试的填充函数
const fillStepWithRetry = async (task: IRawStepTask, index: number, retryCount = 0): Promise<void> => {
const fillStepWithRetry = async (
task: IRawStepTask,
index: number,
retryCount = 0
): Promise<void> => {
const maxRetries = 2
try {
@@ -1291,10 +1212,12 @@ const handleAddBranch = async (taskId: string, branchContent: string) => {
})
// 更新进度
branchFillingStatus.value.current = index + 1
updateProgress(notificationId, index + 1, newTasks.length)
} catch (error) {
console.error(`填充分支任务 ${task.StepName} 详情失败 (尝试 ${retryCount + 1}/${maxRetries + 1}):`, error)
console.error(
`填充分支任务 ${task.StepName} 详情失败 (尝试 ${retryCount + 1}/${maxRetries + 1}):`,
error
)
if (retryCount < maxRetries) {
// 延迟后重试
@@ -1313,14 +1236,23 @@ const handleAddBranch = async (taskId: string, branchContent: string) => {
// 更新 store 中的任务数据
const allBranches = selectionStore.getAllFlowBranches()
const currentBranch = allBranches.find(b => b.branchContent === branchContent && b.parentNodeId === taskId)
const currentBranch = allBranches.find(
b => b.branchContent === branchContent && b.parentNodeId === taskId
)
if (currentBranch) {
currentBranch.tasks = JSON.parse(JSON.stringify(newTasks))
}
branchFillingStatus.value.isFilling = false
// 填充完成后再次保存分支数据到数据库(确保 TaskProcess 等完整数据被保存)
saveBranchesToDB()
removeNotification(notificationId)
}
}
} catch (error) {
ElMessage.error('添加分支失败,请重试')
} finally {
branchLoading.value = false
}
// 重置当前添加分支的节点 ID
@@ -1332,12 +1264,19 @@ const handleAddBranch = async (taskId: string, branchContent: string) => {
fit({ padding: 100, duration: 300 })
}, 100)
})
}
// 保存分支数据到数据库
const saveBranchesToDB = async () => {
const taskId = (window as any).__CURRENT_TASK_ID__
if (taskId) {
try {
await selectionStore.saveBranchesToDB(taskId)
} catch (error) {
ElMessage.error('添加分支失败,请重试')
// 重置当前添加分支的节点 ID
currentAddingBranchNodeId.value = null
} finally {
branchLoading.value = false
console.error('保存分支数据失败:', error)
}
} else {
console.warn('[saveBranchesToDB] 未找到 taskId跳过保存')
}
}
@@ -1419,38 +1358,4 @@ defineExpose({
:deep(.vue-flow__edge.selected .vue-flow__edge-path) {
stroke: #409eff;
}
// 分支加载提示样式
.branch-loading-hint {
position: fixed;
top: 80px;
right: 20px;
background: var(--el-bg-color);
border: 1px solid var(--el-border-color);
border-radius: 8px;
padding: 12px 16px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
display: flex;
align-items: center;
gap: 8px;
z-index: 1000;
animation: slideInRight 0.3s ease-out;
.progress {
color: var(--el-color-primary);
font-weight: bold;
margin-left: 4px;
}
}
@keyframes slideInRight {
from {
opacity: 0;
transform: translateX(20px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
</style>

View File

@@ -1,155 +0,0 @@
// branch_TaskProcess 接口的 Mock 数据和 Mock API
export interface BranchAction {
ID: string
ActionType: string
AgentName: string
Description: string
ImportantInput: string[]
}
export type BranchTaskProcessResponse = BranchAction[][]
// Mock 数据模拟后端返回的原始任务流程数据2D 数组)
// 格式:[[action1, action2], [action3, action4]]
const mockBranchTaskProcessDataRaw: BranchAction[][] = [
// 第一个任务分支方案
[
{
ID: 'agent3',
ActionType: 'Critique',
AgentName: '实验材料学家',
Description: '详细分析用户需求文档',
ImportantInput: ['agent2'],
},
{
ID: 'agent4',
ActionType: 'Critique',
AgentName: '腐蚀机理研究员',
Description: '设计系统整体架构',
ImportantInput: ['agent3'],
},
{
ID: 'agent5',
ActionType: 'Improve',
AgentName: '防护工程专家',
Description: '实现系统核心功能',
ImportantInput: ['agent4'],
},
{
ID: 'agent6',
ActionType: 'Finalize',
AgentName: '实验材料学家',
Description: '进行系统集成测试',
ImportantInput: ['agent5'],
},
],
// 第二个任务分支方案
[
{
ID: 'agent7',
ActionType: 'Critique',
AgentName: '实验材料学家',
Description: '深入分析用户需求和技术约束',
ImportantInput: ['agent2'],
},
{
ID: 'agent8',
ActionType: 'Critique',
AgentName: '防护工程专家',
Description: '设计系统技术架构和数据流',
ImportantInput: ['agent8'],
},
{
ID: 'agent9',
ActionType: 'Improve',
AgentName: '腐蚀机理研究员',
Description: '评估系统安全性',
ImportantInput: ['agent4'],
},
{
ID: 'agent10',
ActionType: 'Finalize',
AgentName: '实验材料学家',
Description: '完成系统安全测试',
ImportantInput: ['agent9'],
},
],
//第三个任务分支方案
[
{
ID: 'agent12',
ActionType: 'Critique',
AgentName: '腐蚀机理研究员',
Description: '设计系统整体架构',
ImportantInput: ['agent11'],
},
{
ID: 'agent13',
ActionType: 'Improve',
AgentName: '防护工程专家',
Description: '实现系统核心功能',
ImportantInput: ['agent12'],
},
{
ID: 'agent14',
ActionType: 'Finalize',
AgentName: '实验材料学家',
Description: '进行系统集成测试',
ImportantInput: ['agent13'],
},
],
]
/**
* Mock API模拟后端 branch_TaskProcess 接口调用
*
* @param branch_Number - 分支数量
* @param Modification_Requirement - 修改需求
* @param Existing_Steps - 现有步骤列表(包含完整信息的对象数组)
* @param Baseline_Completion - 基线完成度
* @param stepTaskExisting - 现有任务
* @param General_Goal - 总体目标
* @returns Promise<BranchAction[][]> - 返回 2D 数组,与后端格式完全一致
*/
export const mockBranchTaskProcessAPI = async (params: {
branch_Number: number
Modification_Requirement: string
Existing_Steps: BranchAction[]
Baseline_Completion: number
stepTaskExisting: any
General_Goal: string
}): Promise<BranchAction[][]> => {
// 模拟网络延迟 800ms
await new Promise((resolve) => setTimeout(resolve, 800))
console.log('[Mock API] branch_TaskProcess 调用参数:', params)
// 🆕 使用轮询方式选择分支方案(依次循环使用所有分支方案)
const totalBranches = mockBranchTaskProcessDataRaw.length
const sessionKey = `branch-task-process-index-${params.stepTaskExisting?.Id || 'default'}`
// 获取上一次的选择索引
let lastIndex = parseInt(sessionStorage.getItem(sessionKey) || '0')
// 计算本次的选择索引(轮询到下一个分支)
const selectedBranchIndex = (lastIndex + 1) % totalBranches
// 保存本次的选择索引
sessionStorage.setItem(sessionKey, selectedBranchIndex.toString())
const rawBranchData = mockBranchTaskProcessDataRaw[selectedBranchIndex] || []
console.log(
'[Mock API] branch_TaskProcess 选择分支方案:',
selectedBranchIndex + 1,
'/',
totalBranches,
)
console.log('[Mock API] branch_TaskProcess 返回数据:', rawBranchData)
// 直接返回 2D 数组,与后端格式完全一致
return [rawBranchData]
}
export default mockBranchTaskProcessAPI

View File

@@ -83,8 +83,23 @@ const handleSubmit = async () => {
}
try {
isAddingDimension.value = true
// 获取大任务ID和小任务信息
const dbTaskId = (window as any).__CURRENT_TASK_ID__
const stepTask = currentTask.value
? {
Id: currentTask.value.Id,
StepName: currentTask.value.StepName,
TaskContent: currentTask.value.TaskContent,
InputObject_List: currentTask.value.InputObject_List,
OutputObject: currentTask.value.OutputObject
}
: undefined
const response = await api.agentSelectModifyAddAspect({
aspectList: [...scoreDimensions.value, newDimension]
aspectList: [...scoreDimensions.value, newDimension],
stepTask: stepTask,
TaskID: dbTaskId
})
scoreDimensions.value.push(response.aspectName)
@@ -103,9 +118,10 @@ const handleSubmit = async () => {
})
//异步更新 store等前端显示完成后再更新避免触发重新初始化
await nextTick()
// 使用 Id 作为 key
const taskId = currentTask.value?.Id
//更新按任务ID的存储
//更新按任务Id的存储
if (taskId) {
const existingTaskData = agentsStore.getTaskScoreData(taskId)
if (existingTaskData) {
@@ -172,7 +188,28 @@ const confirmAgentSelection = async () => {
selectedAgents.value = new Set(existingGroup)
return
}
// 获取大任务ID
const dbTaskId = (window as any).__CURRENT_TASK_ID__
// 添加到内存 store
agentsStore.addConfirmedAgentGroup(currentTask.value.Id, agentArray)
// 🆕 保存 confirmed_groups 到数据库
if (dbTaskId && currentTask.value.Id) {
try {
const updatedGroups = agentsStore.getConfirmedAgentGroups(currentTask.value.Id)
await api.updateAssignedAgents({
task_id: dbTaskId,
step_id: currentTask.value.Id,
agents: agentArray,
confirmed_groups: updatedGroups
})
} catch (error) {
console.error('❌ 保存 confirmed_groups 失败:', error)
}
}
try {
isLoadingConfirm.value = true
const stepTaskForApi = agentsStore.createStepTaskForApi(agentArray)
@@ -186,7 +223,8 @@ const confirmAgentSelection = async () => {
const filledTask = await api.fillStepTaskTaskProcess({
goal,
stepTask: stepTaskForApi,
agents: agentArray
agents: agentArray,
TaskID: dbTaskId
})
selectionStore.setAgentTaskProcess(currentTask.value.Id, agentArray, filledTask)
} catch (error) {
@@ -212,6 +250,7 @@ const selectAgentGroup = async (agentNames: string[]) => {
if (currentTask.value?.Id && agentNames.length > 0) {
let taskProcessData = selectionStore.getAgentTaskProcess(currentTask.value.Id, agentNames)
// 如果 store 中没有数据,调用 API 生成
if (!taskProcessData) {
try {
isLoadingSelectGroup.value = true
@@ -225,9 +264,17 @@ const selectAgentGroup = async (agentNames: string[]) => {
const filledTask = await api.fillStepTaskTaskProcess({
goal,
stepTask: stepTaskForApi,
agents: agentNames
agents: agentNames,
TaskID: (window as any).__CURRENT_TASK_ID__
})
// 🆕 先存储到 store确保 getAgentCombinations 能获取到
// 注意:需要转换为后端期望的格式 { process, brief }
const groupKey = selectionStore.getAgentGroupKey(agentNames)
selectionStore.setAgentTaskProcess(currentTask.value.Id, agentNames, {
process: filledTask.process || [],
brief: filledTask.brief || {}
})
selectionStore.setAgentTaskProcess(currentTask.value.Id, agentNames, filledTask)
taskProcessData = filledTask
} catch (error) {
console.error('❌ 加载 TaskProcess 数据失败:', error)
@@ -239,10 +286,16 @@ const selectAgentGroup = async (agentNames: string[]) => {
if (taskProcessData) {
const convertedTaskProcess = convertToTaskProcess(taskProcessData.process || [])
agentsStore.updateCurrentAgentSelection(
// 🆕 获取所有 agent combinations包含新存储的数据
// getAgentCombinations 现在使用 stepId 作为第一层 key
const agentCombinations = selectionStore.getAgentCombinations(currentTask.value.Id)
await agentsStore.updateCurrentAgentSelection(
[...agentNames],
convertedTaskProcess,
taskProcessData.brief || currentTask.value.Collaboration_Brief_frontEnd
taskProcessData.brief || currentTask.value.Collaboration_Brief_frontEnd,
agentCombinations
)
}
@@ -345,9 +398,9 @@ const calculateAgentAverage = (agentData: AgentHeatmapData, selectedDimensions?:
// API调用 - 获取智能体评分数据
const fetchAgentScores = async () => {
// 使用 Id 作为 key
const taskId = currentTask.value?.Id
if (!taskId) {
console.warn('⚠️ fetchAgentScores: 当前任务没有 Id')
return null
}
//先检查 store 中是否有该任务的评分数据
@@ -369,14 +422,15 @@ const fetchAgentScores = async () => {
// 检查是否已停止
if (agentsStore.isStopping || agentsStore.hasStoppedFilling) {
console.log('检测到停止信号,跳过获取智能体评分')
return null
}
// 调用 API 获取评分(如果没有缓存或预加载)
const dbTaskId = (window as any).__CURRENT_TASK_ID__
const agentScores = await api.agentSelectModifyInit({
goal: agentsStore.agentRawPlan.data?.['General Goal'] || '',
stepTask: currentTask.value
stepTask: currentTask.value,
TaskID: dbTaskId
})
// 再次检查是否已停止API 调用后)
@@ -388,9 +442,7 @@ const fetchAgentScores = async () => {
const firstAgent = Object.keys(agentScores)[0]
const aspectList = firstAgent ? Object.keys(agentScores[firstAgent] || {}) : []
console.log('✅ 获取到的维度列表:', aspectList)
// 🆕 保存到 store按任务ID存储
// 🆕 保存到 store按 Id 存储)
agentsStore.setTaskScoreData(taskId, {
aspectList,
agentScores
@@ -438,7 +490,6 @@ const initHeatmapData = async () => {
// 获取接口数据
const scoreData = await fetchAgentScores()
if (!scoreData) {
console.warn('⚠️ initHeatmapData: 没有获取到评分数据')
return
}

View File

@@ -1,116 +0,0 @@
// Mock数据 - 用于agentSelectModifyAddAspect接口
// 模拟用户输入新维度后所有agent在该维度上的评分数据
import { vueAgentList } from './AgentAssignmentMock'
// 类型定义
export interface NewDimensionScore {
score: number
reason: string
}
export type NewDimensionScoreData = Record<string, NewDimensionScore>
// 模拟接口返回的数据结构
export interface AgentAddAspectResponse {
aspectName: string // 新添加的维度名称
agentScores: NewDimensionScoreData // 所有agent在该维度上的评分
}
// 生成指定维度名称的mock评分数据
export const generateMockDimensionScores = (dimensionName: string): AgentAddAspectResponse => {
const agentScores: NewDimensionScoreData = {}
vueAgentList.forEach((agent) => {
// 随机生成1-5的评分
const score = Math.floor(Math.random() * 5) + 1
// 根据评分生成不同的原因描述
let reason = ''
switch (score) {
case 5:
reason = `在"${dimensionName}"方面表现卓越,展现出杰出的能力和深刻的理解`
break
case 4:
reason = `在"${dimensionName}"方面表现优秀,具有良好的专业能力和执行力`
break
case 3:
reason = `在"${dimensionName}"方面表现合格,能够完成相关任务`
break
case 2:
reason = `在"${dimensionName}"方面表现一般,仍有提升空间`
break
case 1:
reason = `在"${dimensionName}"方面需要加强,建议进一步提升相关能力`
break
}
agentScores[agent] = { score, reason }
})
return {
aspectName: dimensionName,
agentScores,
}
}
// 预设的一些常用维度及其评分数据
export const presetDimensionScores: Record<string, AgentAddAspectResponse> = {
创新性: generateMockDimensionScores('创新性'),
技术能力: generateMockDimensionScores('技术能力'),
沟通技巧: generateMockDimensionScores('沟通技巧'),
问题解决: generateMockDimensionScores('问题解决'),
团队协作: generateMockDimensionScores('团队协作'),
学习能力: generateMockDimensionScores('学习能力'),
执行力: generateMockDimensionScores('执行力'),
责任心: generateMockDimensionScores('责任心'),
适应性: generateMockDimensionScores('适应性'),
领导力: generateMockDimensionScores('领导力'),
}
// 模拟API调用函数用于前端测试
export const mockAgentAddAspectApi = async (
aspectList: string[],
): Promise<AgentAddAspectResponse[]> => {
// 获取新增的维度(最后一个)
const newAspect = aspectList[aspectList.length - 1]
// 模拟网络延迟 500ms
await new Promise((resolve) => setTimeout(resolve, 20000))
// 如果是预设维度,返回预设数据
if (presetDimensionScores[newAspect]) {
return [presetDimensionScores[newAspect]]
}
// 否则动态生成新的评分数据
return [generateMockDimensionScores(newAspect)]
}
// Vue Composition API 兼容的hook
export const useAgentAddAspectMock = () => {
const addNewDimension = async (dimensionName: string) => {
const response = await mockAgentAddAspectApi([dimensionName])
return response[0]
}
const getMultipleDimensions = async (dimensionNames: string[]) => {
const responses: AgentAddAspectResponse[] = []
for (const dimension of dimensionNames) {
if (presetDimensionScores[dimension]) {
responses.push(presetDimensionScores[dimension])
} else {
responses.push(generateMockDimensionScores(dimension))
}
}
return responses
}
return {
addNewDimension,
getMultipleDimensions,
generateMockDimensionScores,
}
}

View File

@@ -1,192 +0,0 @@
// 模拟后端原始返回格式的Mock数据 - 维度 -> agent -> { Reason, Score }
import { vueAgentList, vueAspectList } from './AgentAssignmentMock'
// 后端返回的评分项格式
export interface BackendScoreItem {
Reason: string
Score: number
}
// 后端返回的完整数据格式
export type BackendAgentScoreResponse = Record<string, Record<string, BackendScoreItem>>
// 模拟后端返回的原始数据结构(维度 -> agent -> { Reason, Score }
export const mockBackendAgentScoreData: BackendAgentScoreResponse = {
: {
: { Reason: '展现出卓越的创造力和创新思维', Score: 4 },
: { Reason: '展现出杰出的创造性问题解决能力', Score: 5 },
: { Reason: '具有中等创造技能,有待提升', Score: 3 },
: { Reason: '在大多数情况下展现较强的创造性思维', Score: 4 },
: { Reason: '展现出胜任的创造能力', Score: 3 },
: { Reason: '具有较强的创造性表达能力', Score: 4 },
: { Reason: '擅长创新性思维方法', Score: 5 },
: { Reason: '展现出卓越的创造性思维和创新能力', Score: 5 },
: { Reason: '展现出良好的创造性问题解决能力', Score: 4 },
: { Reason: '展现出卓越的创造性问题解决能力', Score: 5 },
: { Reason: '展现出平衡的创造能力', Score: 4 },
: { Reason: '展现出卓越的创造天赋', Score: 5 },
: { Reason: '展现出胜任的创造性思维', Score: 3 },
: { Reason: '展现出较强的创造性主动性', Score: 4 },
: { Reason: '具有发展中的创造技能', Score: 3 },
: { Reason: '展现出卓越的创造愿景', Score: 5 },
: { Reason: '展现出卓越的创造性执行力', Score: 4 },
: { Reason: '具有较强的创造性问题解决能力', Score: 4 },
: { Reason: '展现出胜任的创造能力', Score: 3 },
},
: {
: { Reason: '展现出卓越的共情能力和社会意识', Score: 5 },
: { Reason: '具有较强的情绪调节和人际交往技能', Score: 4 },
: { Reason: '展现出卓越的情感智力', Score: 5 },
: { Reason: '在大多数情况下展现平均的情感智力', Score: 3 },
: { Reason: '具有良好的情绪意识和沟通能力', Score: 4 },
: { Reason: '在情绪意识方面偶尔表现不足', Score: 3 },
: { Reason: '具有较强的情绪理解能力', Score: 4 },
: { Reason: '展现出卓越的共情能力和社交技能', Score: 5 },
: { Reason: '具有良好的情绪调节能力', Score: 4 },
: { Reason: '展现出卓越的情感智力和社会意识', Score: 5 },
: { Reason: '具有发展中的情绪意识', Score: 3 },
: { Reason: '擅长人际交往和建立关系', Score: 5 },
: { Reason: '展现出平衡的情感智力', Score: 4 },
: { Reason: '具有基本的情绪理解能力', Score: 3 },
: { Reason: '展现出良好的情绪调节能力', Score: 4 },
: { Reason: '展现出卓越的社会意识', Score: 5 },
: { Reason: '在情感智力方面需要提升', Score: 3 },
: { Reason: '具有较强的共情能力', Score: 4 },
: { Reason: '具有良好的情绪沟通技能', Score: 4 },
},
: {
: { Reason: '展现出胜任的哲学推理技能', Score: 3 },
: { Reason: '展现出卓越的逻辑推理和分析能力', Score: 5 },
: { Reason: '展现出深刻的哲学洞察力和批判性思维', Score: 2 },
: { Reason: '展现出良好的哲学理解能力', Score: 1 },
: { Reason: '具有基础哲学推理能力,存在一些局限', Score: 3 },
: { Reason: '展现出较强的分析思维能力', Score: 4 },
: { Reason: '展现出卓越的哲学深度', Score: 5 },
: { Reason: '展现出卓越的专业分析和推理能力', Score: 5 },
: { Reason: '具有良好的批判性思维能力', Score: 4 },
: { Reason: '具有较强的专业性分析和推理能力', Score: 4 },
: { Reason: '展现出卓越的逻辑推理能力', Score: 5 },
: { Reason: '具有基础的哲学理解能力', Score: 3 },
: { Reason: '展现出平衡的哲学推理能力', Score: 4 },
: { Reason: '需要在哲学思维方面发展', Score: 3 },
: { Reason: '展现出良好的分析技能', Score: 4 },
: { Reason: '具有较强的哲学洞察力', Score: 4 },
: { Reason: '擅长批判性思维和分析', Score: 5 },
: { Reason: '具有基础哲学推理能力', Score: 3 },
: { Reason: '展现出卓越的哲学才能', Score: 5 },
},
: {
: { Reason: '在任务完成方面展现出卓越的效率', Score: 4 },
: { Reason: '擅长高效的工作流程管理', Score: 5 },
: { Reason: '展现出平均的效率,有提升空间', Score: 3 },
: { Reason: '具有良好的时间管理技能', Score: 4 },
: { Reason: '展现出胜任的效率', Score: 3 },
: { Reason: '展现出卓越的生产力', Score: 5 },
: { Reason: '具有强大的任务执行能力', Score: 4 },
: { Reason: '具有良好的工作效率和时间管理', Score: 4 },
: { Reason: '展现出良好的工作流程优化能力', Score: 4 },
: { Reason: '展现出卓越的效率和工作流程管理', Score: 5 },
: { Reason: '展现出足够的效率', Score: 3 },
: { Reason: '擅长快速完成任务', Score: 5 },
: { Reason: '展现出良好的生产力', Score: 4 },
: { Reason: '具有中等的效率水平', Score: 3 },
: { Reason: '具有较强的任务效率', Score: 4 },
: { Reason: '具有良好的执行速度', Score: 4 },
: { Reason: '展现出卓越的效率', Score: 5 },
: { Reason: '展现出平均的生产力', Score: 3 },
: { Reason: '在执行方面具有良好的效率', Score: 4 },
},
: {
: { Reason: '展现出卓越的细节关注度', Score: 5 },
: { Reason: '展现出卓越的准确性和精确度', Score: 5 },
: { Reason: '展现出卓越的精确度', Score: 5 },
: { Reason: '展现出良好的准确性', Score: 4 },
: { Reason: '展现出中等的准确性,有提升空间', Score: 3 },
: { Reason: '具有较强的细节关注度', Score: 4 },
: { Reason: '在精确度和准确性方面表现卓越', Score: 5 },
: { Reason: '展现出卓越的细节导向和精确度', Score: 5 },
: { Reason: '展现出平均的准确性', Score: 3 },
: { Reason: '展现出卓越的准确性和精确技能', Score: 5 },
: { Reason: '展现出卓越的准确性', Score: 5 },
: { Reason: '展现出较强的精确度', Score: 4 },
: { Reason: '展现出中等的准确性', Score: 3 },
: { Reason: '具有良好的细节导向能力', Score: 4 },
: { Reason: '在准确性和精确度方面表现卓越', Score: 5 },
: { Reason: '展现出较强的细节关注度', Score: 4 },
: { Reason: '展现出平均的准确性水平', Score: 3 },
: { Reason: '在工作中具有良好的精确度', Score: 4 },
: { Reason: '展现出卓越的准确性', Score: 5 },
},
: {
: { Reason: '展现出卓越的协作技能', Score: 4 },
: { Reason: '在团队合作和协作方面表现卓越', Score: 5 },
: { Reason: '具有较强的协作能力', Score: 4 },
: { Reason: '具有中等的协作技能', Score: 3 },
: { Reason: '展现出良好的团队合作精神', Score: 4 },
: { Reason: '具有较强的合作能力', Score: 4 },
: { Reason: '展现出平均的协作技能', Score: 3 },
: { Reason: '在团队协作方面表现卓越', Score: 5 },
: { Reason: '展现出良好的合作工作能力', Score: 4 },
: { Reason: '在团队协作和合作方面表现卓越', Score: 5 },
: { Reason: '具有中等的协作水平', Score: 3 },
: { Reason: '展现出良好的协作技能', Score: 4 },
: { Reason: '在协调和团队合作方面表现卓越', Score: 5 },
: { Reason: '具有较强的合作能力', Score: 4 },
: { Reason: '展现出平均的协作水平', Score: 3 },
: { Reason: '展现出良好的团队合作精神', Score: 4 },
: { Reason: '具有较强的协作技能', Score: 4 },
: { Reason: '在团队协作方面表现卓越', Score: 5 },
: { Reason: '具有中等的协作能力', Score: 3 },
},
}
// 模拟后端API调用 - agentSelectModifyInit
export const mockBackendAgentSelectModifyInit = async (): Promise<BackendAgentScoreResponse> => {
// 模拟网络延迟 300ms
await new Promise(resolve => setTimeout(resolve, 300))
return mockBackendAgentScoreData
}
// 模拟后端API调用 - agentSelectModifyAddAspect添加新维度
export const mockBackendAgentSelectModifyAddAspect = async (
aspectList: string[]
): Promise<BackendAgentScoreResponse> => {
// 模拟网络延迟 500ms
await new Promise(resolve => setTimeout(resolve, 500))
// 获取新添加的维度(最后一个)
const newAspect = aspectList[aspectList.length - 1]
if (!newAspect) {
return {}
}
// 生成该维度下所有agent的评分
const aspectData: Record<string, BackendScoreItem> = {}
vueAgentList.forEach(agent => {
const score = Math.floor(Math.random() * 5) + 1
let reason = ''
switch (score) {
case 5:
reason = `在"${newAspect}"方面表现卓越,展现出杰出的能力和深刻的理解`
break
case 4:
reason = `在"${newAspect}"方面表现优秀,具有良好的专业能力和执行力`
break
case 3:
reason = `在"${newAspect}"方面表现合格,能够完成相关任务`
break
case 2:
reason = `在"${newAspect}"方面表现一般,仍有提升空间`
break
case 1:
reason = `在"${newAspect}"方面需要加强,建议进一步提升相关能力`
break
}
aspectData[agent] = { Reason: reason, Score: score }
})
return {
[newAspect]: aspectData
}
}

View File

@@ -1,314 +0,0 @@
// Vue兼容的mock数据 - 6个维度19个智能体
export const vueAgentList = [
'船舶设计师',
'防护工程专家',
'病理生理学家',
'药物化学家',
'制剂工程师',
'监管事务专家',
'物理学家',
'实验材料学家',
'计算模拟专家',
'腐蚀机理研究员',
'先进材料研发员',
'肾脏病学家',
'临床研究协调员',
'中医药专家',
'药物安全专家',
'二维材料科学家',
'光电物理学家',
'机器学习专家',
'流体动力学专家',
]
export const vueAspectList = ['能力', '可用性', '专业性', '效率', '准确性', '协作性']
// 类型定义
export type AgentName = (typeof vueAgentList)[number]
export type AspectName = (typeof vueAspectList)[number]
export interface AgentScore {
score: number
reason: string
}
export type IAgentSelectModifyAddRequest = Record<AspectName, Record<AgentName, AgentScore>>
// Vue友好的数据结构 - agent -> 维度 -> 评分(与后端返回格式一致)
export const vueAgentScoreData: Record<AgentName, Record<AspectName, AgentScore>> = {
: {
: { score: 4, reason: '展现出卓越的创造力和创新思维' },
: { score: 5, reason: '展现出卓越的共情能力和社会意识' },
: { score: 3, reason: '展现出胜任的哲学推理技能' },
: { score: 4, reason: '在任务完成方面展现出卓越的效率' },
: { score: 5, reason: '展现出卓越的细节关注度' },
: { score: 4, reason: '展现出卓越的协作技能' },
},
: {
: { score: 5, reason: '展现出杰出的创造性问题解决能力' },
: { score: 4, reason: '具有较强的情绪调节和人际交往技能' },
: { score: 5, reason: '展现出卓越的逻辑推理和分析能力' },
: { score: 5, reason: '擅长高效的工作流程管理' },
: { score: 5, reason: '展现出卓越的准确性和精确度' },
: { score: 5, reason: '在团队合作和协作方面表现卓越' },
},
: {
: { score: 3, reason: '具有中等创造技能,有待提升' },
: { score: 5, reason: '展现出卓越的情感智力' },
: { score: 2, reason: '展现出深刻的哲学洞察力和批判性思维' },
: { score: 3, reason: '展现出平均的效率,有提升空间' },
: { score: 5, reason: '展现出卓越的精确度' },
: { score: 4, reason: '具有较强的协作能力' },
},
: {
: { score: 4, reason: '在大多数情况下展现较强的创造性思维' },
: { score: 3, reason: '在大多数情况下展现平均的情感智力' },
: { score: 1, reason: '展现出良好的哲学理解能力' },
: { score: 4, reason: '具有良好的时间管理技能' },
: { score: 4, reason: '展现出良好的准确性' },
: { score: 3, reason: '具有中等的协作技能' },
},
: {
: { score: 3, reason: '展现出胜任的创造能力' },
: { score: 4, reason: '具有良好的情绪意识和沟通能力' },
: { score: 3, reason: '具有基础哲学推理能力,存在一些局限' },
: { score: 3, reason: '展现出胜任的效率' },
: { score: 3, reason: '展现出中等的准确性,有提升空间' },
: { score: 4, reason: '展现出良好的团队合作精神' },
},
: {
: { score: 4, reason: '具有较强的创造性表达能力' },
: { score: 3, reason: '在情绪意识方面偶尔表现不足' },
: { score: 4, reason: '展现出较强的分析思维能力' },
: { score: 5, reason: '展现出卓越的生产力' },
: { score: 4, reason: '具有较强的细节关注度' },
: { score: 4, reason: '具有较强的合作能力' },
},
: {
: { score: 5, reason: '擅长创新性思维方法' },
: { score: 4, reason: '具有较强的情绪理解能力' },
: { score: 5, reason: '展现出卓越的哲学深度' },
: { score: 4, reason: '具有强大的任务执行能力' },
: { score: 5, reason: '在精确度和准确性方面表现卓越' },
: { score: 3, reason: '展现出平均的协作技能' },
},
: {
: { score: 5, reason: '展现出卓越的创造性思维和创新能力' },
: { score: 5, reason: '展现出卓越的共情能力和社交技能' },
: { score: 5, reason: '展现出卓越的专业分析和推理能力' },
: { score: 4, reason: '具有良好的工作效率和时间管理' },
: { score: 5, reason: '展现出卓越的细节导向和精确度' },
: { score: 5, reason: '在团队协作方面表现卓越' },
},
: {
: { score: 4, reason: '展现出良好的创造性问题解决能力' },
: { score: 4, reason: '具有良好的情绪调节能力' },
: { score: 4, reason: '具有良好的批判性思维能力' },
: { score: 4, reason: '展现出良好的工作流程优化能力' },
: { score: 3, reason: '展现出平均的准确性' },
: { score: 4, reason: '展现出良好的合作工作能力' },
},
: {
: { score: 5, reason: '展现出卓越的创造性问题解决能力' },
: { score: 5, reason: '展现出卓越的情感智力和社会意识' },
: { score: 4, reason: '具有较强的专业性分析和推理能力' },
: { score: 5, reason: '展现出卓越的效率和工作流程管理' },
: { score: 5, reason: '展现出卓越的准确性和精确技能' },
: { score: 5, reason: '在团队协作和合作方面表现卓越' },
},
: {
: { score: 4, reason: '展现出平衡的创造能力' },
: { score: 3, reason: '具有发展中的情绪意识' },
: { score: 5, reason: '展现出卓越的逻辑推理能力' },
: { score: 3, reason: '展现出足够的效率' },
: { score: 5, reason: '展现出卓越的准确性' },
: { score: 3, reason: '具有中等的协作水平' },
},
: {
: { score: 5, reason: '展现出卓越的创造天赋' },
: { score: 5, reason: '擅长人际交往和建立关系' },
: { score: 3, reason: '具有基础的哲学理解能力' },
: { score: 5, reason: '擅长快速完成任务' },
: { score: 4, reason: '展现出较强的精确度' },
: { score: 4, reason: '展现出良好的协作技能' },
},
: {
: { score: 3, reason: '展现出胜任的创造性思维' },
: { score: 4, reason: '展现出平衡的情感智力' },
: { score: 4, reason: '展现出平衡的哲学推理能力' },
: { score: 4, reason: '展现出良好的生产力' },
: { score: 3, reason: '展现出中等的准确性' },
: { score: 5, reason: '在协调和团队合作方面表现卓越' },
},
: {
: { score: 4, reason: '展现出较强的创造性主动性' },
: { score: 3, reason: '具有基本的情绪理解能力' },
: { score: 3, reason: '需要在哲学思维方面发展' },
: { score: 3, reason: '具有中等的效率水平' },
: { score: 4, reason: '具有良好的细节导向能力' },
: { score: 4, reason: '具有较强的合作能力' },
},
: {
: { score: 3, reason: '具有发展中的创造技能' },
: { score: 4, reason: '展现出良好的情绪调节能力' },
: { score: 4, reason: '展现出良好的分析技能' },
: { score: 4, reason: '具有较强的任务效率' },
: { score: 5, reason: '在准确性和精确度方面表现卓越' },
: { score: 3, reason: '展现出平均的协作水平' },
},
: {
: { score: 5, reason: '展现出卓越的创造愿景' },
: { score: 5, reason: '展现出卓越的社会意识' },
: { score: 4, reason: '具有较强的哲学洞察力' },
: { score: 4, reason: '具有良好的执行速度' },
: { score: 4, reason: '展现出较强的细节关注度' },
: { score: 4, reason: '展现出良好的团队合作精神' },
},
: {
: { score: 4, reason: '展现出卓越的创造性执行力' },
: { score: 3, reason: '在情感智力方面需要提升' },
: { score: 5, reason: '擅长批判性思维和分析' },
: { score: 5, reason: '展现出卓越的效率' },
: { score: 3, reason: '展现出平均的准确性水平' },
: { score: 4, reason: '具有较强的协作技能' },
},
: {
: { score: 4, reason: '具有较强的创造性问题解决能力' },
: { score: 4, reason: '具有较强的共情能力' },
: { score: 3, reason: '具有基础哲学推理能力' },
: { score: 3, reason: '展现出平均的生产力' },
: { score: 4, reason: '在工作中具有良好的精确度' },
: { score: 5, reason: '在团队协作方面表现卓越' },
},
: {
: { score: 3, reason: '展现出胜任的创造能力' },
: { score: 4, reason: '具有良好的情绪沟通技能' },
: { score: 5, reason: '展现出卓越的哲学才能' },
: { score: 4, reason: '在执行方面具有良好的效率' },
: { score: 5, reason: '展现出卓越的准确性' },
: { score: 3, reason: '具有中等的协作能力' },
},
}
// Vue友好的智能体选择配置
export const vueAgentSelections = {
balanced: { agents: ['船舶设计师', '防护工程专家', '病理生理学家'] },
creative: { agents: ['防护工程专家', '物理学家', '二维材料科学家'] },
emotional: { agents: ['船舶设计师', '病理生理学家', '实验材料学家'] },
philosophical: { agents: ['病理生理学家', '物理学家', '光电物理学家'] },
mixed: { agents: ['药物化学家', '先进材料研发员', '肾脏病学家', '机器学习专家'] },
}
export const vueCurrentAgentSelection = 'balanced'
// Vue兼容的工具函数
export const vueCalculateAgentAverages = () => {
const averages: Record<string, number> = {}
vueAgentList.forEach((agent) => {
let total = 0
let count = 0
vueAspectList.forEach((aspect) => {
// 数据结构agentScores[agent][aspect]
const scoreData = vueAgentScoreData[agent]?.[aspect]
if (scoreData) {
total += scoreData.score
count++
}
})
averages[agent] = count > 0 ? Number((total / count).toFixed(2)) : 0
})
return averages
}
// 获取按平均分排序的智能体列表
export const vueGetSortedAgentsByAverage = () => {
const averages = vueCalculateAgentAverages()
return [...vueAgentList].sort((a, b) => {
return averages[b] - averages[a]
})
}
// Vue Composition API 兼容的hook
export const useAgentMockData = () => {
// 在Vue中可以使用ref或reactive包装数据
const agentScores = vueAgentScoreData
const agentSelections = vueAgentSelections
const currentSelection = vueCurrentAgentSelection
// 计算平均分的响应式函数
const calculateAverages = () => {
return vueCalculateAgentAverages()
}
// 获取特定维度的评分(返回该维度下所有智能体的评分)
const getScoresByAspect = (aspect: AspectName) => {
const result: Record<AgentName, AgentScore> = {}
// 数据结构agentScores[agent][aspect]需要遍历所有agent提取指定aspect
vueAgentList.forEach((agent) => {
if (agentScores[agent]?.[aspect]) {
result[agent] = agentScores[agent][aspect]!
}
})
return result
}
// 获取特定智能体的所有维度评分
const getScoresByAgent = (agent: AgentName) => {
// 数据结构agentScores[agent][aspect]直接返回agent的所有维度评分
return agentScores[agent] || {}
}
return {
agentScores,
agentSelections,
currentSelection,
calculateAverages,
getScoresByAspect,
getScoresByAgent,
getSortedAgents: vueGetSortedAgentsByAverage,
}
}
// Vue 2.x 兼容的选项式API版本
export const vueMockMixin = {
data() {
return {
vueAgentScores: vueAgentScoreData,
vueAgentSelections: vueAgentSelections,
vueCurrentSelection: vueCurrentAgentSelection,
vueAgentList: vueAgentList,
vueAspectList: vueAspectList,
}
},
computed: {
vueAgentAverages() {
return vueCalculateAgentAverages()
},
vueSortedAgents() {
return vueGetSortedAgentsByAverage()
},
},
methods: {
vueGetScoresByAspect(aspect: AspectName) {
const agentScores = (this as any).vueAgentScores
const agentList = (this as any).vueAgentList
// 数据结构agentScores[agent][aspect]遍历所有agent提取指定aspect
const result: Record<string, { score: number; reason: string }> = {}
agentList.forEach((agent: string) => {
if (agentScores[agent]?.[aspect]) {
result[agent] = agentScores[agent][aspect]
}
})
return result
},
vueGetScoresByAgent(agent: AgentName) {
const agentScores = (this as any).vueAgentScores
// 数据结构agentScores[agent][aspect]直接返回agent的所有维度评分
return agentScores[agent] || {}
},
},
}

View File

@@ -3,6 +3,7 @@ import SvgIcon from '@/components/SvgIcon/index.vue'
import { getAgentMapIcon } from '@/layout/components/config.ts'
import { type ConnectArg, Jsplumb } from '@/layout/components/Main/TaskTemplate/utils.ts'
import { type IRawStepTask, useAgentsStore } from '@/stores'
// import api from '@/api'
import { computed, nextTick, watch, onMounted } from 'vue'
import { AnchorLocations } from '@jsplumb/browser-ui'
import { Loading } from '@element-plus/icons-vue'
@@ -109,7 +110,7 @@ watch(
)
// 保存编辑内容
const handleContentSave = (taskId: string, content: string) => {
const handleContentSave = async (taskId: string, content: string) => {
const taskToUpdate = collaborationProcess.value.find(item => item.Id === taskId)
if (taskToUpdate && content !== taskToUpdate.TaskContent) {
taskToUpdate.TaskContent = content
@@ -118,6 +119,18 @@ const handleContentSave = (taskId: string, content: string) => {
if (stepIndex >= 0) {
agentsStore.addModifiedStep(stepIndex)
}
//暂时注释掉直接保存 task_outline 的操作,避免覆盖整个主流程
// 如果需要持久化,应该只更新变化的字段或使用专门的 API
// const dbTaskId = (window as any).__CURRENT_TASK_ID__
// if (dbTaskId && agentsStore.agentRawPlan.data) {
// try {
// await api.updateTaskOutline(dbTaskId, agentsStore.agentRawPlan.data)
// console.log('[handleContentSave] 主流程已保存到数据库')
// } catch (error) {
// console.error('[handleContentSave] 保存主流程失败:', error)
// }
// }
}
}

View File

@@ -6,6 +6,12 @@ import { Jsplumb } from './utils.ts'
import { type IRawStepTask, useAgentsStore } from '@/stores'
import { BezierConnector } from '@jsplumb/browser-ui'
import { ref } from 'vue'
// 定义组件 props
const props = defineProps<{
TaskID?: string // 任务唯一标识,用于写入数据库
}>()
const agentsStore = useAgentsStore()
// 智能体库
@@ -100,6 +106,7 @@ defineExpose({
<div class="flex-1 h-full">
<TaskResult
ref="taskResultRef"
:TaskID="props.TaskID"
@refresh-line="taskResultJsplumb.repaintEverything"
@set-current-task="handleTaskResultCurrentTask"
/>

View File

@@ -3,6 +3,7 @@ import Task from './Task.vue'
import TaskTemplate from './TaskTemplate/index.vue'
import { nextTick, ref } from 'vue'
const taskRef = ref<{ currentTaskID: string }>()
const taskTemplateRef = ref<{ changeTask: () => void; clear: () => void }>()
function handleSearch() {
@@ -10,12 +11,21 @@ function handleSearch() {
taskTemplateRef.value?.changeTask()
})
}
// 获取当前任务的 TaskID
function getTaskID(): string {
return taskRef.value?.currentTaskID || ''
}
defineExpose({
getTaskID
})
</script>
<template>
<div class="p-[24px] h-[calc(100%-60px)]">
<Task @search="handleSearch" @search-start="taskTemplateRef?.clear" />
<TaskTemplate ref="taskTemplateRef" />
<Task ref="taskRef" @search="handleSearch" @search-start="taskTemplateRef?.clear" />
<TaskTemplate ref="taskTemplateRef" :TaskID="taskRef?.currentTaskID" />
</div>
</template>

View File

@@ -1,14 +1,38 @@
<script setup lang="ts">
import Header from './components/Header.vue'
import Main from './components/Main/index.vue'
import { ref, onMounted } from 'vue'
import { ref, onMounted, computed } from 'vue'
import FloatWindow from './components/Main/TaskTemplate/TaskSyllabus/components/FloatWindow.vue'
import PlanModification from './components/Main/TaskTemplate/TaskSyllabus/Branch/PlanModification.vue'
import { useAgentsStore } from '@/stores/modules/agents'
import PlanTask from './components/Main/TaskTemplate/TaskProcess/components/PlanTask.vue'
import AgentAllocation from './components/Main/TaskTemplate/TaskSyllabus/components/AgentAllocation.vue'
const isDarkMode = ref(false)
const agentsStore = useAgentsStore()
const mainRef = ref<{ currentTaskID: string } | null>(null)
// 获取当前的 taskId
const currentTaskId = computed(() => {
return mainRef.value?.currentTaskID || ''
})
onMounted(() => {
// 等待 Main 组件挂载后获取 taskId
setTimeout(() => {
const mainEl = document.querySelector('.main-container')
if (mainEl) {
// 通过 parent 获取
const mainParent = mainEl.parentElement
if (mainParent) {
const mainComponent = mainParent.querySelector('[class*="main"]')
if (mainComponent) {
// 尝试从 Vue 组件实例获取
}
}
}
}, 100)
})
// 初始化主题
const initTheme = () => {
const savedTheme = localStorage.getItem('theme')

View File

@@ -5,6 +5,7 @@ import { getAgentMapIcon } from '@/layout/components/config'
import { store } from '../index'
import { useStorage } from '@vueuse/core'
import type { IExecuteRawResponse } from '@/api'
import api from '@/api'
import { useConfigStore } from '@/stores/modules/config.ts'
export interface Agent {
Name: string
@@ -102,14 +103,14 @@ export interface IRawPlanResponse {
* @returns TaskProcess 数组
*/
export function convertToTaskProcess(
actions: { id: string; type: string; agent: string; description: string; inputs?: string[] }[]
actions: { id: string; type: string; agent: string; description: string; inputs?: string[] }[],
): TaskProcess[] {
return actions.map(action => ({
return actions.map((action) => ({
ID: action.id,
ActionType: action.type,
AgentName: action.agent,
Description: action.description,
ImportantInput: action.inputs || []
ImportantInput: action.inputs || [],
}))
}
@@ -129,16 +130,16 @@ export const useAgentsStore = defineStore('agents', () => {
agents.value = agent
}
// 🆕 新的按任务ID存储的评分数据
// 新的按任务ID存储的评分数据
const taskScoreDataMap = useStorage<Record<string, ITaskScoreData>>(
`${storageKey}-task-score-data`,
{},
)
// 🆕 预加载状态追踪(用于避免重复预加载)
// 预加载状态追踪(用于避免重复预加载)
const preloadingTaskIds = ref<Set<string>>(new Set())
// 🆕 获取指定任务的评分数据按任务ID获取
// 获取指定任务的评分数据按任务ID获取
function getTaskScoreData(taskId: string): IAgentSelectModifyAddRequest | null {
if (!taskId) {
console.warn('⚠️ getTaskScoreData: taskId 为空')
@@ -147,7 +148,9 @@ export const useAgentsStore = defineStore('agents', () => {
const taskScoreData = taskScoreDataMap.value[taskId]
if (taskScoreData) {
console.log(`✅ 使用任务 ${taskId} 的缓存评分数据,维度数: ${taskScoreData.aspectList.length}`)
console.log(
`✅ 使用任务 ${taskId} 的缓存评分数据,维度数: ${taskScoreData.aspectList.length}`,
)
return {
aspectList: taskScoreData.aspectList,
agentScores: taskScoreData.agentScores,
@@ -284,6 +287,11 @@ export const useAgentsStore = defineStore('agents', () => {
confirmedAgentGroupsMap.value.clear()
}
// 设置指定任务的确认的agent组合列表用于从数据库恢复
function setConfirmedAgentGroups(taskId: string, groups: string[][]) {
confirmedAgentGroupsMap.value.set(taskId, groups)
}
const planModificationWindow = ref(false)
const planTaskWindow = ref(false)
const agentAllocationDialog = ref(false)
@@ -350,7 +358,7 @@ export const useAgentsStore = defineStore('agents', () => {
function setCurrentTask(task: IRawStepTask) {
const existingTask = currentTask.value
// 🆕 智能判断:如果是同一个任务,保留用户修改过的数据AgentSelection、TaskProcess、Collaboration_Brief_frontEnd
// 智能判断:如果是同一个任务,保留用户修改过的数据
if (existingTask && existingTask.Id === task.Id) {
currentTask.value = {
...task,
@@ -360,7 +368,7 @@ export const useAgentsStore = defineStore('agents', () => {
existingTask.Collaboration_Brief_frontEnd || task.Collaboration_Brief_frontEnd,
}
// 🆕 同步更新主流程数据(让执行结果卡片和任务大纲都能联动)
// 同步更新主流程数据(让执行结果卡片和任务大纲都能联动)
syncCurrentTaskToMainProcess(currentTask.value)
console.log('🔄 setCurrentTask: 保留同一任务的分支数据', {
@@ -431,10 +439,11 @@ export const useAgentsStore = defineStore('agents', () => {
// 🆕 更新当前任务的 AgentSelection 和 TaskProcess用于在 AgentAllocation 中切换 agent 组合)
// 此函数专门用于强制更新,不会被 setCurrentTask 的"智能保留"逻辑阻止
function updateCurrentAgentSelection(
async function updateCurrentAgentSelection(
agentSelection: string[],
taskProcess: TaskProcess[],
collaborationBrief: any,
agentCombinations?: Record<string, { process: any; brief: any }>,
) {
if (currentTask.value) {
// 直接更新 currentTask不保留旧数据
@@ -453,7 +462,40 @@ export const useAgentsStore = defineStore('agents', () => {
taskName: currentTask.value.StepName,
newAgentSelection: agentSelection,
taskProcessLength: taskProcess.length,
agentCombinationsKeys: agentCombinations ? Object.keys(agentCombinations) : undefined,
})
// 🆕 保存 assigned_agents 到数据库
const dbTaskId = (window as any).__CURRENT_TASK_ID__
const stepId = currentTask.value?.Id
// 获取已确认的 agent 组合列表
const confirmedGroups = stepId ? getConfirmedAgentGroups(stepId) : []
console.log('[updateCurrentAgentSelection] 保存到数据库:', {
dbTaskId,
stepId,
agents: agentSelection,
confirmedGroupsCount: confirmedGroups?.length,
agentCombinationsKeys: agentCombinations ? Object.keys(agentCombinations) : undefined,
})
if (dbTaskId && stepId) {
try {
console.log('[updateCurrentAgentSelection] 调用 APIconfirmedGroups:', confirmedGroups)
await api.updateAssignedAgents({
task_id: dbTaskId,
step_id: stepId,
agents: agentSelection,
confirmed_groups: confirmedGroups,
agent_combinations: agentCombinations,
})
} catch (error) {
console.error('❌ 保存 assigned_agents 到数据库失败:', error)
}
} else {
console.warn('[updateCurrentAgentSelection] dbTaskId 或 stepId 为空,跳过保存', {
dbTaskId,
stepId,
})
}
}
}
@@ -542,9 +584,9 @@ export const useAgentsStore = defineStore('agents', () => {
agents,
brief: currentTask.value?.Collaboration_Brief_frontEnd || {
template: '',
data: {}
data: {},
},
process: []
process: [],
}
}
@@ -602,6 +644,7 @@ export const useAgentsStore = defineStore('agents', () => {
addConfirmedAgentGroup,
clearConfirmedAgentGroups,
clearAllConfirmedAgentGroups,
setConfirmedAgentGroups,
// 停止填充状态
hasStoppedFilling,
setHasStoppedFilling,

View File

@@ -4,6 +4,7 @@ import { v4 as uuidv4 } from 'uuid'
import { store } from '../index'
import type { IRawStepTask, IApiStepTask } from './agents'
import type { Node, Edge } from '@vue-flow/core'
import { useAgentsStoreHook } from './agents'
/**
* 分支数据接口
@@ -243,6 +244,107 @@ export const useSelectionStore = defineStore('selection', () => {
}
}
// ==================== 数据库持久化方法 ====================
/**
* 从数据库恢复分支数据
* @param dbBranches 从数据库读取的分支数据数组
*/
function restoreBranchesFromDB(dbBranches: IBranchData[]) {
// 清除现有分支
clearFlowBranches()
// 恢复分支数据到 store
dbBranches.forEach((branch) => {
if (branch && branch.id && branch.tasks) {
addFlowBranch({
parentNodeId: branch.parentNodeId,
branchContent: branch.branchContent,
branchType: branch.branchType,
nodes: branch.nodes || [],
edges: branch.edges || [],
tasks: branch.tasks,
})
}
})
}
/**
* 保存分支数据到数据库
* @param taskId 任务ID
* @returns Promise<boolean> 是否保存成功
*/
async function saveBranchesToDB(taskId: string): Promise<boolean> {
// 导入 api避免循环导入问题
const { default: api } = await import('@/api')
const branches = getAllFlowBranches()
const result = await api.saveBranches(taskId, branches)
return result
}
/**
* 保存任务过程分支数据到数据库
* @param TaskID 大任务ID数据库主键
* @returns Promise<boolean> 是否保存成功
* @description 存储结构: Map<taskStepId, Map<agentGroupKey, IBranchData[]>>
*/
async function saveTaskProcessBranchesToDB(TaskID: string): Promise<boolean> {
// 导入 api避免循环导入问题
const { default: api } = await import('@/api')
// 将 Map 转换为普通对象,便于序列化
const branchesMap = getAllTaskProcessBranches()
const branchesObj: Record<string, Record<string, any[]>> = {}
for (const [taskStepId, agentMap] of branchesMap.entries()) {
branchesObj[taskStepId] = {}
for (const [agentGroupKey, branches] of agentMap.entries()) {
branchesObj[taskStepId][agentGroupKey] = branches
}
}
const result = await api.saveTaskProcessBranches(TaskID, branchesObj)
return result
}
/**
* 从数据库恢复任务过程分支数据
* @param dbBranches 从数据库读取的任务过程分支数据
* @param TaskID 大任务ID用于获取全局 __CURRENT_TASK_ID__
* @description 数据格式: { stepId小任务UUID: { agentGroupKey: [IBranchData...] } }
*/
function restoreTaskProcessBranchesFromDB(dbBranches: Record<string, Record<string, any[]>>) {
// 清除现有数据
taskProcessBranchesMap.value.clear()
if (!dbBranches) {
return
}
// 恢复数据
for (const [taskStepId, agentMap] of Object.entries(dbBranches)) {
if (typeof agentMap !== 'object' || agentMap === null) {
continue
}
// 获取或创建该步骤的 Map
if (!taskProcessBranchesMap.value.has(taskStepId)) {
taskProcessBranchesMap.value.set(taskStepId, new Map())
}
const stepMap = taskProcessBranchesMap.value.get(taskStepId)!
// 恢复 agent combinations
for (const [agentGroupKey, branches] of Object.entries(agentMap)) {
if (Array.isArray(branches)) {
stepMap.set(agentGroupKey, branches)
}
}
}
}
// ==================== Agent 组合 TaskProcess 数据存储 ====================
/**
* Agent 组合 TaskProcess 数据映射
@@ -270,20 +372,34 @@ export const useSelectionStore = defineStore('selection', () => {
/**
* 存储 agent 组合的 TaskProcess 数据
* @param taskId 任务 ID
* @param stepId 步骤 ID小任务 UUID用于作为 agentTaskProcessMap 的第一层主键)
* @param agents Agent 列表
* @param taskProcess TaskProcess 数据
* @param taskProcess TaskProcess 数据(支持完整格式或简化格式)
* @description 存储结构: Map<stepId, Map<agentGroupKey, { process, brief }>>
* agentTaskProcessMap = { stepId: { agentGroupKey: { process, brief } } }
* 注意:简化格式 { process, brief } 与后端数据库格式一致
*/
function setAgentTaskProcess(taskId: string, agents: string[], taskProcess: IApiStepTask) {
function setAgentTaskProcess(
stepId: string,
agents: string[],
taskProcess: IApiStepTask | { process: any; brief: any },
) {
const groupKey = getAgentGroupKey(agents)
// 获取或创建该任务的 Map
if (!agentTaskProcessMap.value.has(taskId)) {
agentTaskProcessMap.value.set(taskId, new Map())
// 获取或创建该步骤的 Map
if (!agentTaskProcessMap.value.has(stepId)) {
agentTaskProcessMap.value.set(stepId, new Map())
}
const stepMap = agentTaskProcessMap.value.get(stepId)!
// 统一转换为简化格式 { process, brief },与后端期望格式一致
const simplifiedData = {
process: (taskProcess as IApiStepTask).process || taskProcess.process || [],
brief: (taskProcess as IApiStepTask).brief || taskProcess.brief || {},
}
// 存储该 agent 组合的 TaskProcess 数据
agentTaskProcessMap.value.get(taskId)!.set(groupKey, taskProcess)
stepMap.set(groupKey, simplifiedData)
}
/**
@@ -321,6 +437,97 @@ export const useSelectionStore = defineStore('selection', () => {
agentTaskProcessMap.value.clear()
}
/**
* 获取指定步骤的所有 agent 组合的 TaskProcess 数据
* @param stepId 步骤 ID小任务 UUID
* @returns agent_combinations 对象格式 { "[\"AgentA\",\"AgentB\"]": { process, brief }, ... }
* @description 存储结构: Map<stepId, Map<agentGroupKey, { process, brief }>>
* 注意:简化设计后,第一层 key 就是 stepId
*/
function getAgentCombinations(
stepId: string,
): Record<string, { process: any; brief: any }> | undefined {
// 直接使用 stepId 作为第一层 key
const stepMap = agentTaskProcessMap.value.get(stepId)
if (!stepMap) {
return undefined
}
// 将 Map 转换为普通对象
const result: Record<string, { process: any; brief: any }> = {}
for (const [key, value] of stepMap.entries()) {
result[key] = value
}
return result
}
// ==================== 数据库持久化恢复方法 ====================
/**
* 从数据库 assigned_agents 字段恢复 agent 组合数据
* @param assignedAgents 从数据库读取的 assigned_agents 数据
* @param taskId 大任务 ID此参数已不再使用因为简化后直接用 stepId 作为 key
* @description 数据格式: { step_id小任务UUID: { current: [...], confirmed_groups: [...], agent_combinations: {...} } }
* 存储结构: Map<stepId, Map<agentGroupKey, IApiStepTask>>
* agentTaskProcessMap = { stepId: { agentGroupKey: data } }
*/
function restoreAgentCombinationsFromDB(assignedAgents: Record<string, any>, taskId: string) {
// 获取 agents store 实例
const agentsStore = useAgentsStoreHook()
// 清除现有数据
clearAllAgentTaskProcess()
agentsStore.clearAllConfirmedAgentGroups()
if (!assignedAgents) {
return
}
// 🆕 简化版本:直接使用 stepId 作为第一层 key
// 遍历每个步骤的数据
for (const [stepId, stepData] of Object.entries(assignedAgents)) {
if (typeof stepData !== 'object' || stepData === null) {
continue
}
// 获取或创建该步骤的 Map
if (!agentTaskProcessMap.value.has(stepId)) {
agentTaskProcessMap.value.set(stepId, new Map<string, IApiStepTask>())
}
const stepMap = agentTaskProcessMap.value.get(stepId)!
// 恢复 agent_combinations 到 stepMap
// 格式: { agentGroupKey: { process, brief } }
if (stepData.agent_combinations) {
for (const [agentGroupKey, combinationData] of Object.entries(
stepData.agent_combinations,
)) {
stepMap.set(agentGroupKey, combinationData as IApiStepTask)
}
}
// 恢复 confirmed_groups
if (stepData.confirmed_groups) {
agentsStore.setConfirmedAgentGroups(stepId, stepData.confirmed_groups)
}
// 恢复 current当前选中的 agent 组合)
if (stepData.current) {
agentsStore.setSelectedAgentGroup(stepId, stepData.current)
// 同步更新 agentRawPlan 中对应步骤的 AgentSelection
const planData = agentsStore.agentRawPlan.data
if (planData && planData['Collaboration Process']) {
const process = planData['Collaboration Process']
const step = process.find((s: any) => s.Id === stepId)
if (step) {
step.AgentSelection = stepData.current
}
}
}
}
}
// ==================== 当前生效的任务过程分支 ====================
/**
* 当前生效的任务过程分支映射
@@ -450,7 +657,15 @@ export const useSelectionStore = defineStore('selection', () => {
getAgentTaskProcess,
hasAgentTaskProcess,
clearAgentTaskProcess,
getAgentCombinations,
clearAllAgentTaskProcess,
// ==================== 数据库持久化方法 ====================
restoreBranchesFromDB,
saveBranchesToDB,
restoreAgentCombinationsFromDB,
saveTaskProcessBranchesToDB,
restoreTaskProcessBranchesFromDB,
}
})