核心组件: - Orchestrator: 意图理解、任务拆分、竞标管理、结果验证 - Worker: 竞标任务、执行交付 - TaskBoard: 状态管理、信息存储 - BidEvaluator: 竞标评估算法 - ExecutionMonitor: 执行监控、超时处理 - LLMClient: 大模型接口调用 功能特性: - 竞标机制:Agent主动竞争任务 - 动态调度:串行/并行任务智能调度 - 智能容错:超时切换、验证重试 - 质量保证:结果验证、历史追踪 Web界面:首页、请求列表、任务列表、Agent管理 API接口:请求/任务/Agent管理、测试接口 端口:19015
501 lines
16 KiB
Python
501 lines
16 KiB
Python
"""
|
||
规划Agent (Orchestrator) - 意图理解、任务拆分、竞标管理、结果验证
|
||
"""
|
||
|
||
import time
|
||
import json
|
||
from typing import Dict, List, Optional, Any
|
||
from .models import (
|
||
Task, TaskType, TaskStatus, TaskGraph, Bid, Execution,
|
||
Attempt, ValidationResult, UserRequest, AgentProfile
|
||
)
|
||
from .task_board import TaskBoard
|
||
from .bid_evaluator import BidEvaluator
|
||
from .execution_monitor import ExecutionMonitorManager
|
||
from .llm_client import LLMClient, default_client
|
||
|
||
|
||
class Orchestrator:
|
||
"""规划Agent - 核心调度引擎"""
|
||
|
||
def __init__(
|
||
self,
|
||
task_board: TaskBoard,
|
||
llm_client: LLMClient = None
|
||
):
|
||
self.task_board = task_board
|
||
self.llm = llm_client or default_client
|
||
|
||
self.bid_evaluator = BidEvaluator()
|
||
self.execution_manager = ExecutionMonitorManager()
|
||
|
||
# 配置
|
||
self.bidding_window = 30 # 竞标窗口时间(秒)
|
||
self.max_retry_per_agent = 1 # 每个Agent最大重试次数
|
||
self.max_total_attempts = 5 # 总尝试次数上限
|
||
self.max_total_time = 1800 # 最大总执行时间(秒)
|
||
|
||
# === 意图理解 ===
|
||
|
||
def understand_intent(self, user_request: str) -> Dict:
|
||
"""
|
||
意图理解
|
||
|
||
Args:
|
||
user_request: 用户原始请求
|
||
|
||
Returns:
|
||
意图分析结果
|
||
"""
|
||
try:
|
||
intent = self.llm.analyze_intent(user_request)
|
||
return intent
|
||
except Exception as e:
|
||
# 简化处理
|
||
return {
|
||
"intent": user_request,
|
||
"keywords": [],
|
||
"need_clarification": False,
|
||
"questions": []
|
||
}
|
||
|
||
# === 任务拆分 ===
|
||
|
||
def split_tasks(self, intent: Dict, user_request: str) -> TaskGraph:
|
||
"""
|
||
任务拆分
|
||
|
||
Args:
|
||
intent: 意图分析结果
|
||
user_request: 用户原始请求
|
||
|
||
Returns:
|
||
TaskGraph
|
||
"""
|
||
try:
|
||
task_list = self.llm.split_tasks(intent, user_request)
|
||
|
||
graph = TaskGraph()
|
||
|
||
for t in task_list:
|
||
task_type = TaskType.PARALLEL if t.get('type') == 'parallel' else TaskType.SERIAL
|
||
task = Task(
|
||
type=task_type,
|
||
description=t.get('description', ''),
|
||
input_data=t.get('input_schema', {}),
|
||
output_schema=t.get('output_schema', {}),
|
||
dependencies=t.get('dependencies', [])
|
||
)
|
||
graph.add_task(task)
|
||
|
||
return graph
|
||
|
||
except Exception as e:
|
||
# 简化:创建单个任务
|
||
graph = TaskGraph()
|
||
task = Task(
|
||
type=TaskType.INDEPENDENT,
|
||
description=user_request
|
||
)
|
||
graph.add_task(task)
|
||
return graph
|
||
|
||
# === 任务发布 ===
|
||
|
||
def publish_task(self, task: Task) -> List[AgentProfile]:
|
||
"""
|
||
发布任务
|
||
|
||
Args:
|
||
task: 任务
|
||
|
||
Returns:
|
||
能处理该任务的Agent列表
|
||
"""
|
||
# 更新状态
|
||
task.status = TaskStatus.PUBLISHED
|
||
self.task_board.add_task(task)
|
||
|
||
# 找能处理的Agent
|
||
capable_agents = self.task_board.find_capable_agents(task)
|
||
|
||
return capable_agents
|
||
|
||
# === 竞标收集 ===
|
||
|
||
def collect_bids(
|
||
self,
|
||
task: Task,
|
||
agents: List[AgentProfile],
|
||
window_seconds: int = None
|
||
) -> List[Bid]:
|
||
"""
|
||
收集竞标
|
||
|
||
Args:
|
||
task: 任务
|
||
agents: Agent列表
|
||
window_seconds: 竞标窗口时间
|
||
|
||
Returns:
|
||
竞标列表
|
||
"""
|
||
window = window_seconds or self.bidding_window
|
||
task.status = TaskStatus.BIDDING
|
||
|
||
bids = []
|
||
|
||
# 让每个Agent生成竞标
|
||
for agent in agents:
|
||
try:
|
||
bid_data = self.llm.generate_bid(task.to_dict(), agent.to_dict())
|
||
|
||
bid = Bid(
|
||
task_id=task.id,
|
||
agent_id=agent.id,
|
||
capability_match=float(bid_data.get('capability_match', 0.5)),
|
||
estimated_time=int(bid_data.get('estimated_time', 60)),
|
||
confidence=float(bid_data.get('confidence', 0.5)),
|
||
approach=bid_data.get('approach', ''),
|
||
prerequisites=bid_data.get('prerequisites', []),
|
||
alternative_approaches=bid_data.get('alternative_approaches', [])
|
||
)
|
||
|
||
bids.append(bid)
|
||
self.task_board.add_bid(bid)
|
||
|
||
except Exception as e:
|
||
# Agent无法竞标,跳过
|
||
pass
|
||
|
||
return bids
|
||
|
||
# === 竞标评估 ===
|
||
|
||
def evaluate_bids(self, task: Task, bids: List[Bid]) -> tuple:
|
||
"""
|
||
评估竞标
|
||
|
||
Args:
|
||
task: 任务
|
||
bids: 竞标列表
|
||
|
||
Returns:
|
||
(selected_agent, selected_bid, backup_agents)
|
||
"""
|
||
agents_dict = {
|
||
a.id: a for a in self.task_board.list_agents()
|
||
}
|
||
|
||
best_bid, best_agent, scores = self.bid_evaluator.select_best_bid(bids, agents_dict)
|
||
|
||
if best_agent:
|
||
backup_agents = self.bid_evaluator.get_backup_agents(
|
||
bids, agents_dict, best_agent.id
|
||
)
|
||
|
||
return (best_agent, best_bid, backup_agents)
|
||
|
||
return (None, None, [])
|
||
|
||
# === 任务执行 ===
|
||
|
||
def assign_and_execute(
|
||
self,
|
||
task: Task,
|
||
agent: AgentProfile,
|
||
bid: Bid,
|
||
backup_agents: List[AgentProfile] = None
|
||
) -> Execution:
|
||
"""
|
||
分配并执行任务
|
||
|
||
Args:
|
||
task: 任务
|
||
agent: 执行Agent
|
||
bid: 竞标书
|
||
backup_agents: 备选Agent
|
||
|
||
Returns:
|
||
Execution对象
|
||
"""
|
||
task.status = TaskStatus.ASSIGNED
|
||
|
||
# 定义执行函数
|
||
def execute_func(t, a, b):
|
||
try:
|
||
result = self.llm.execute_task(t.to_dict(), b.approach)
|
||
return result.get('result') or result
|
||
except Exception as e:
|
||
raise e
|
||
|
||
# 创建监控器
|
||
monitor = self.execution_manager.create_monitor(
|
||
task=task,
|
||
agent=agent,
|
||
bid=bid,
|
||
callbacks={
|
||
'complete': lambda exec: self._on_execution_complete(exec, task),
|
||
'timeout': lambda exec: self._on_execution_timeout(exec, task, backup_agents),
|
||
'error': lambda exec, e: self._on_execution_error(exec, task, e)
|
||
}
|
||
)
|
||
|
||
# 添加备选
|
||
if backup_agents:
|
||
for backup in backup_agents:
|
||
monitor.add_backup_agent(backup)
|
||
|
||
# 启动执行
|
||
task.status = TaskStatus.EXECUTING
|
||
execution = monitor.start(execute_func)
|
||
|
||
self.task_board.add_execution(execution)
|
||
|
||
return execution
|
||
|
||
def _on_execution_complete(self, execution: Execution, task: Task):
|
||
"""执行完成回调"""
|
||
task.status = TaskStatus.VALIDATING
|
||
|
||
# 验证结果
|
||
validation = self.validate_result(task, execution.result)
|
||
|
||
if validation.passed:
|
||
task.status = TaskStatus.COMPLETED
|
||
self.task_board.update_task_status(task.id, task.status)
|
||
|
||
# 更新Agent统计
|
||
duration = execution.end_time - execution.start_time
|
||
self.task_board.update_agent_stats(
|
||
execution.agent_id,
|
||
success=True,
|
||
duration=duration,
|
||
quality_score=validation.score
|
||
)
|
||
else:
|
||
# 验证失败,尝试重试
|
||
self._handle_validation_failure(task, execution, validation)
|
||
|
||
def _on_execution_timeout(self, execution: Execution, task: Task, backup_agents: List[AgentProfile]):
|
||
"""执行超时回调"""
|
||
# 记录失败
|
||
attempt = Attempt(
|
||
task_id=task.id,
|
||
agent_id=execution.agent_id,
|
||
execution=execution,
|
||
success=False,
|
||
failure_type='timeout',
|
||
duration=execution.end_time - execution.start_time
|
||
)
|
||
self.task_board.add_attempt(attempt)
|
||
|
||
# 尝试备选Agent
|
||
if backup_agents and self.task_board.get_attempt_count(task.id) < self.max_total_attempts:
|
||
# 找一个备选Agent重新执行
|
||
for backup in backup_agents:
|
||
attempts = [a for a in self.task_board.get_attempts(task.id) if a.agent_id == backup.id]
|
||
if len(attempts) < self.max_retry_per_agent:
|
||
# 创建新的竞标
|
||
bid = Bid(
|
||
task_id=task.id,
|
||
agent_id=backup.id,
|
||
capability_match=0.5,
|
||
estimated_time=60,
|
||
confidence=0.5,
|
||
approach="备选执行"
|
||
)
|
||
self.assign_and_execute(task, backup, bid, [])
|
||
return
|
||
|
||
# 无备选或已达上限,任务失败
|
||
task.status = TaskStatus.FAILED
|
||
self.task_board.update_task_status(task.id, task.status)
|
||
|
||
def _on_execution_error(self, execution: Execution, task: Task, error: Exception):
|
||
"""执行错误回调"""
|
||
# 记录失败
|
||
attempt = Attempt(
|
||
task_id=task.id,
|
||
agent_id=execution.agent_id,
|
||
execution=execution,
|
||
success=False,
|
||
failure_type='error',
|
||
duration=time.time() - execution.start_time,
|
||
feedback=str(error)
|
||
)
|
||
self.task_board.add_attempt(attempt)
|
||
|
||
task.status = TaskStatus.FAILED
|
||
self.task_board.update_task_status(task.id, task.status)
|
||
|
||
def _handle_validation_failure(self, task: Task, execution: Execution, validation: ValidationResult):
|
||
"""处理验证失败"""
|
||
# 记录失败
|
||
attempt = Attempt(
|
||
task_id=task.id,
|
||
agent_id=execution.agent_id,
|
||
execution=execution,
|
||
validation=validation,
|
||
success=False,
|
||
failure_type='validation',
|
||
duration=execution.end_time - execution.start_time,
|
||
feedback=json.dumps(validation.issues)
|
||
)
|
||
self.task_board.add_attempt(attempt)
|
||
|
||
# 检查重试次数
|
||
agent_attempts = [a for a in self.task_board.get_attempts(task.id) if a.agent_id == execution.agent_id]
|
||
|
||
if len(agent_attempts) < self.max_retry_per_agent:
|
||
# 给予重试机会
|
||
task.status = TaskStatus.RETRY
|
||
self.task_board.update_task_status(task.id, task.status)
|
||
|
||
# 重新执行(带上反馈)
|
||
# 简化处理,直接失败
|
||
task.status = TaskStatus.FAILED
|
||
self.task_board.update_task_status(task.id, task.status)
|
||
else:
|
||
# 已达重试上限,任务失败
|
||
task.status = TaskStatus.FAILED
|
||
self.task_board.update_task_status(task.id, task.status)
|
||
|
||
# === 结果验证 ===
|
||
|
||
def validate_result(self, task: Task, result: Any) -> ValidationResult:
|
||
"""
|
||
验证结果
|
||
|
||
Args:
|
||
task: 任务
|
||
result: 执行结果
|
||
|
||
Returns:
|
||
ValidationResult
|
||
"""
|
||
try:
|
||
validation_data = self.llm.validate_result(task.to_dict(), result)
|
||
|
||
validation = ValidationResult(
|
||
passed=validation_data.get('passed', False),
|
||
issues=validation_data.get('issues', []),
|
||
score=float(validation_data.get('score', 0.5))
|
||
)
|
||
|
||
return validation
|
||
|
||
except Exception as e:
|
||
# 简化:假设通过
|
||
return ValidationResult(passed=True, score=0.5)
|
||
|
||
# === 主流程 ===
|
||
|
||
def process_request(self, user_request: str) -> UserRequest:
|
||
"""
|
||
处理用户请求(完整流程)
|
||
|
||
Args:
|
||
user_request: 用户原始请求
|
||
|
||
Returns:
|
||
UserRequest对象
|
||
"""
|
||
# 1. 创建请求记录
|
||
request = self.task_board.create_request(user_request)
|
||
|
||
try:
|
||
# 2. 意图理解
|
||
intent = self.understand_intent(user_request)
|
||
|
||
# 3. 如果需要澄清
|
||
if intent.get('need_clarification'):
|
||
request.status = 'clarification_needed'
|
||
request.error = json.dumps(intent.get('questions', []))
|
||
self.task_board.update_request(request)
|
||
return request
|
||
|
||
# 4. 任务拆分
|
||
task_graph = self.split_tasks(intent, user_request)
|
||
request.task_graph = task_graph
|
||
request.status = 'processing'
|
||
self.task_board.update_request(request)
|
||
|
||
# 5. 按执行顺序处理任务
|
||
execution_order = task_graph.get_execution_order()
|
||
completed = set()
|
||
results = {}
|
||
|
||
for layer_tasks in execution_order:
|
||
# 并行处理当前层的任务
|
||
layer_results = {}
|
||
|
||
for task in layer_tasks:
|
||
# 发布任务
|
||
capable_agents = self.publish_task(task)
|
||
|
||
if not capable_agents:
|
||
task.status = TaskStatus.FAILED
|
||
continue
|
||
|
||
# 收集竞标
|
||
bids = self.collect_bids(task, capable_agents)
|
||
|
||
if not bids:
|
||
task.status = TaskStatus.FAILED
|
||
continue
|
||
|
||
# 评估竞标
|
||
agent, bid, backup = self.evaluate_bids(task, bids)
|
||
|
||
if not agent:
|
||
task.status = TaskStatus.FAILED
|
||
continue
|
||
|
||
# 执行任务
|
||
execution = self.assign_and_execute(task, agent, bid, backup)
|
||
|
||
# 等待执行完成(简化:直接等待)
|
||
# 实际应该用异步等待或轮询
|
||
import time
|
||
max_wait = task.timeout + 10
|
||
waited = 0
|
||
while waited < max_wait:
|
||
exec_record = self.task_board.get_execution(execution.id)
|
||
if exec_record and exec_record.status in ['completed', 'failed', 'timeout']:
|
||
break
|
||
time.sleep(1)
|
||
waited += 1
|
||
|
||
# 获取结果
|
||
if task.status == TaskStatus.COMPLETED:
|
||
layer_results[task.id] = execution.result
|
||
completed.add(task.id)
|
||
else:
|
||
# 任务失败,整个请求失败
|
||
request.status = 'failed'
|
||
request.error = f"任务 {task.id} 执行失败"
|
||
self.task_board.update_request(request)
|
||
return request
|
||
|
||
results.update(layer_results)
|
||
|
||
# 6. 整合结果
|
||
request.final_result = results
|
||
request.status = 'completed'
|
||
self.task_board.update_request(request)
|
||
|
||
return request
|
||
|
||
except Exception as e:
|
||
request.status = 'failed'
|
||
request.error = str(e)
|
||
self.task_board.update_request(request)
|
||
return request
|
||
|
||
def get_request_status(self, request_id: str) -> Dict:
|
||
"""获取请求状态"""
|
||
request = self.task_board.get_request(request_id)
|
||
if request:
|
||
return request.to_dict()
|
||
return None |