数字员工大脑 — Main Agent 核心实现: - MainAgentService: 目标分解(LLM)、任务调度、进度监控、失败重试、自主循环 - 4个 Main Agent 专有工具: create_task / assign_task / check_progress / notify_user - Celery 异步任务: decompose_goal / execute_goal / execute_task / autonomy_tick - Goal API 增强: decompose / execute-async / replan 端点 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
133 lines
4.0 KiB
Python
133 lines
4.0 KiB
Python
"""
|
||
Goal/Task 异步任务 — Celery 任务定义
|
||
|
||
Main Agent 的目标分解、任务执行、自主循环等重量级操作通过 Celery 异步执行。
|
||
"""
|
||
from app.core.tools_bootstrap import ensure_builtin_tools_registered
|
||
|
||
ensure_builtin_tools_registered()
|
||
|
||
from app.core.celery_app import celery_app
|
||
from app.core.database import SessionLocal
|
||
from app.models.goal import Goal
|
||
from app.models.execution import Execution
|
||
from app.services.main_agent_service import MainAgentService
|
||
import asyncio
|
||
import logging
|
||
import time
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
|
||
@celery_app.task(bind=True)
|
||
def decompose_goal_task(self, goal_id: str):
|
||
"""
|
||
异步分解目标:使用 LLM 将 Goal 分解为 Task 树。
|
||
|
||
在 Celery Worker 中执行,避免 API 请求超时。
|
||
"""
|
||
db = SessionLocal()
|
||
try:
|
||
service = MainAgentService(db)
|
||
goal = asyncio.run(service.decompose_goal(goal_id))
|
||
|
||
return {
|
||
"status": "completed",
|
||
"goal_id": goal_id,
|
||
"goal_title": goal.title,
|
||
}
|
||
except Exception as e:
|
||
logger.error(f"Goal decomposition failed: {e}", exc_info=True)
|
||
db.rollback()
|
||
return {"status": "failed", "goal_id": goal_id, "error": str(e)}
|
||
finally:
|
||
db.close()
|
||
|
||
|
||
@celery_app.task(bind=True)
|
||
def execute_goal_task(self, goal_id: str):
|
||
"""
|
||
异步执行 Goal:启动 Main Agent 管理目标全生命周期。
|
||
|
||
1. 分解目标(如果尚未分解)
|
||
2. 持续执行 task 直到完成或阻塞
|
||
3. 更新 Goal 状态
|
||
"""
|
||
db = SessionLocal()
|
||
start_time = time.time()
|
||
try:
|
||
goal = db.query(Goal).filter(Goal.id == goal_id).first()
|
||
if not goal:
|
||
return {"status": "failed", "goal_id": goal_id, "error": "目标不存在"}
|
||
|
||
# 更新状态
|
||
goal.status = "active"
|
||
db.commit()
|
||
|
||
service = MainAgentService(db)
|
||
result = asyncio.run(service.start_goal_execution(goal_id))
|
||
|
||
elapsed = int((time.time() - start_time) * 1000)
|
||
return {
|
||
"status": "completed",
|
||
"goal_id": goal_id,
|
||
"elapsed_ms": elapsed,
|
||
**result,
|
||
}
|
||
except Exception as e:
|
||
logger.error(f"Goal execution failed: {e}", exc_info=True)
|
||
goal = db.query(Goal).filter(Goal.id == goal_id).first()
|
||
if goal:
|
||
goal.status = "failed"
|
||
db.commit()
|
||
return {"status": "failed", "goal_id": goal_id, "error": str(e)}
|
||
finally:
|
||
db.close()
|
||
|
||
|
||
@celery_app.task(bind=True)
|
||
def execute_task_celery(self, task_id: str):
|
||
"""
|
||
异步执行单个 Task。
|
||
|
||
Main Agent 创建 Execution 记录后将任务交给 Celery Worker 执行。
|
||
"""
|
||
db = SessionLocal()
|
||
start_time = time.time()
|
||
try:
|
||
service = MainAgentService(db)
|
||
result = asyncio.run(service.execute_task(task_id))
|
||
|
||
elapsed = int((time.time() - start_time) * 1000)
|
||
return {
|
||
"status": result.get("status", "completed"),
|
||
"task_id": task_id,
|
||
"elapsed_ms": elapsed,
|
||
**result,
|
||
}
|
||
except Exception as e:
|
||
logger.error(f"Task execution failed: {e}", exc_info=True)
|
||
return {"status": "failed", "task_id": task_id, "error": str(e)}
|
||
finally:
|
||
db.close()
|
||
|
||
|
||
@celery_app.task(bind=True)
|
||
def autonomy_tick_task(self, goal_id: str):
|
||
"""
|
||
自主循环单次心跳:检查进度 → 执行可运行任务 → 处理失败 → 通知。
|
||
|
||
由 Celery Beat 定时调度(根据 Goal.autonomy_config.check_interval_minutes)。
|
||
"""
|
||
db = SessionLocal()
|
||
try:
|
||
service = MainAgentService(db)
|
||
result = asyncio.run(service.autonomy_tick(goal_id))
|
||
logger.info(f"Autonomy tick for goal {goal_id}: {result.get('status', 'unknown')}")
|
||
return {"status": "completed", "goal_id": goal_id, **result}
|
||
except Exception as e:
|
||
logger.error(f"Autonomy tick failed for goal {goal_id}: {e}", exc_info=True)
|
||
return {"status": "failed", "goal_id": goal_id, "error": str(e)}
|
||
finally:
|
||
db.close()
|