Files
aiagent/backend/app/tasks/goal_tasks.py
renjianbo 926ec6c0a1 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>
2026-05-08 19:58:53 +08:00

133 lines
4.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
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()