feat: add Main Agent core service, tools, and Celery tasks (Phase 2)
数字员工大脑 — 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>
This commit is contained in:
132
backend/app/tasks/goal_tasks.py
Normal file
132
backend/app/tasks/goal_tasks.py
Normal file
@@ -0,0 +1,132 @@
|
||||
"""
|
||||
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()
|
||||
Reference in New Issue
Block a user