Files
crewai/main.py

232 lines
6.5 KiB
Python
Raw Normal View History

2026-03-13 14:20:58 +08:00
"""
2026-03-13 21:09:44 +08:00
SDLC Agent Demo - FastAPI 主服务轮询版本
2026-03-13 18:12:31 +08:00
多智能体端到端软件交付协同系统
2026-03-13 14:20:58 +08:00
"""
import json
2026-03-13 18:12:31 +08:00
import uuid
2026-03-13 20:00:07 +08:00
import asyncio
2026-03-13 20:53:44 +08:00
import threading
2026-03-13 20:00:07 +08:00
from typing import Dict, Optional, AsyncGenerator
2026-03-13 14:20:58 +08:00
from datetime import datetime
2026-03-13 18:12:31 +08:00
from fastapi import FastAPI, HTTPException
from fastapi.staticfiles import StaticFiles
2026-03-13 14:20:58 +08:00
from fastapi.middleware.cors import CORSMiddleware
2026-03-13 18:12:31 +08:00
from fastapi.responses import StreamingResponse, RedirectResponse, JSONResponse
2026-03-13 14:20:58 +08:00
from pydantic import BaseModel, Field
2026-03-13 18:12:31 +08:00
import uvicorn
2026-03-13 21:09:44 +08:00
from concurrent.futures import ThreadPoolExecutor
2026-03-13 14:20:58 +08:00
2026-03-13 18:12:31 +08:00
from crews.sdlc_crew import SDLCCrew
from models.qwen_config import get_qwen_config
2026-03-13 14:20:58 +08:00
2026-03-13 18:12:31 +08:00
# ========== FastAPI 应用初始化 ==========
app = FastAPI(
title="SDLC Agent Demo",
description="多智能体端到端软件交付协同系统 - 基于 CrewAI + Qwen3.5-flash",
version="1.0.0"
)
2026-03-13 14:20:58 +08:00
2026-03-13 21:09:44 +08:00
# 添加 CORS 中间件
2026-03-13 18:12:31 +08:00
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
2026-03-13 14:20:58 +08:00
2026-03-13 18:12:31 +08:00
# 挂载静态文件目录
app.mount("/static", StaticFiles(directory="static"), name="static")
2026-03-13 14:20:58 +08:00
2026-03-13 18:12:31 +08:00
# ========== 数据模型 ==========
class StartRequest(BaseModel):
"""启动请求模型"""
requirement: str = Field(..., description="用户需求描述", min_length=10)
2026-03-13 14:20:58 +08:00
2026-03-13 18:12:31 +08:00
# ========== 任务管理(内存存储) ==========
class TaskManager:
"""任务管理器 - 负责任务状态持久化"""
2026-03-13 14:20:58 +08:00
2026-03-13 18:12:31 +08:00
def __init__(self):
self.tasks: Dict[str, Dict] = {}
2026-03-13 20:53:44 +08:00
self.task_events: Dict[str, list] = {} # 存储任务的所有事件
2026-03-13 14:20:58 +08:00
2026-03-13 18:12:31 +08:00
def create_task(self, requirement: str) -> str:
"""创建新任务"""
task_id = str(uuid.uuid4())
2026-03-13 20:00:07 +08:00
self.tasks[task_id] = {
"task_id": task_id,
"requirement": requirement,
"status": "pending",
"created_at": datetime.now().isoformat(),
"updated_at": datetime.now().isoformat()
}
2026-03-13 20:53:44 +08:00
# 创建事件列表用于存储所有事件
self.task_events[task_id] = []
2026-03-13 18:12:31 +08:00
return task_id
2026-03-13 14:20:58 +08:00
2026-03-13 18:12:31 +08:00
def update_task_status(self, task_id: str, status: str):
"""更新任务状态"""
2026-03-13 20:00:07 +08:00
if task_id in self.tasks:
self.tasks[task_id]["status"] = status
self.tasks[task_id]["updated_at"] = datetime.now().isoformat()
2026-03-13 20:53:44 +08:00
def add_event(self, task_id: str, event: dict):
"""添加事件到任务"""
if task_id in self.task_events:
self.task_events[task_id].append(event)
2026-03-13 14:20:58 +08:00
2026-03-13 20:53:44 +08:00
def get_events(self, task_id: str, last_index: int = 0) -> Dict:
"""获取任务的新事件"""
if task_id not in self.task_events:
return {"events": [], "has_more": False}
events = self.task_events[task_id]
new_events = events[last_index:]
task = self.tasks.get(task_id, {})
has_more = task.get("status") not in ["completed", "failed", "pending"]
return {
"events": new_events,
"has_more": has_more,
"status": task.get("status", "unknown")
}
2026-03-13 14:20:58 +08:00
2026-03-13 18:12:31 +08:00
def get_task(self, task_id: str) -> Optional[Dict]:
"""获取任务信息"""
2026-03-13 20:00:07 +08:00
return self.tasks.get(task_id)
2026-03-13 14:20:58 +08:00
2026-03-13 18:12:31 +08:00
# 全局任务管理器
task_manager = TaskManager()
2026-03-13 14:20:58 +08:00
2026-03-13 21:09:44 +08:00
# 线程池
executor = ThreadPoolExecutor(max_workers=5)
2026-03-13 14:20:58 +08:00
2026-03-13 18:12:31 +08:00
# ========== API 端点 ==========
@app.post("/api/v1/sdlc/start", response_model=Dict[str, str])
async def start_sdlc_process(request: StartRequest):
"""
2026-03-13 20:53:44 +08:00
启动 SDLC 流程使用线程池异步执行
2026-03-13 14:20:58 +08:00
"""
2026-03-13 18:12:31 +08:00
# 验证配置
try:
get_qwen_config()
except ValueError as e:
raise HTTPException(status_code=500, detail=str(e))
# 创建任务
task_id = task_manager.create_task(request.requirement)
2026-03-13 14:20:58 +08:00
2026-03-13 20:53:44 +08:00
# 在线程池中异步执行 SDLC 流程
loop = asyncio.get_event_loop()
loop.run_in_executor(executor, execute_sdlc_flow, task_id, request.requirement)
2026-03-13 14:20:58 +08:00
2026-03-13 18:12:31 +08:00
return {
"task_id": task_id,
"status": "processing"
}
2026-03-13 20:53:44 +08:00
def execute_sdlc_flow(task_id: str, requirement: str):
2026-03-13 18:12:31 +08:00
"""
2026-03-13 20:53:44 +08:00
在线程池中执行 SDLC 流程将事件保存到任务列表
2026-03-13 20:00:07 +08:00
Args:
task_id: 任务 ID
requirement: 用户需求
2026-03-13 14:20:58 +08:00
"""
2026-03-13 18:12:31 +08:00
task_manager.update_task_status(task_id, "processing")
2026-03-13 14:20:58 +08:00
try:
2026-03-13 20:53:44 +08:00
# 添加启动事件
task_manager.add_event(task_id, {
2026-03-13 20:00:07 +08:00
"event": "task_started",
"data": {
"status": "starting",
"message": "SDLC 流程已启动",
"timestamp": datetime.now().isoformat()
}
})
2026-03-13 18:12:31 +08:00
2026-03-13 20:53:44 +08:00
# 直接执行 CrewAI (同步阻塞)
2026-03-13 20:00:07 +08:00
crew = SDLCCrew()
for event in crew.execute(requirement):
2026-03-13 20:53:44 +08:00
# 添加事件到任务
task_manager.add_event(task_id, event)
2026-03-13 20:00:07 +08:00
2026-03-13 20:53:44 +08:00
# 更新状态
2026-03-13 20:00:07 +08:00
event_type = event.get("event", "")
if event_type == "final_result":
task_manager.update_task_status(task_id, "completed")
elif event_type == "error":
task_manager.update_task_status(task_id, "failed")
2026-03-13 14:20:58 +08:00
except Exception as e:
2026-03-13 18:12:31 +08:00
task_manager.update_task_status(task_id, "failed")
2026-03-13 20:53:44 +08:00
task_manager.add_event(task_id, {
2026-03-13 18:12:31 +08:00
"event": "error",
"data": {
"error": str(e),
"timestamp": datetime.now().isoformat()
}
})
2026-03-13 14:20:58 +08:00
2026-03-13 20:53:44 +08:00
@app.get("/api/v1/sdlc/poll/{task_id}")
async def poll_task_events(task_id: str, last_index: int = 0):
"""
轮询任务事件替代 SSE
Args:
task_id: 任务 ID
last_index: 最后已知的 event 索引
"""
result = task_manager.get_events(task_id, last_index)
return result
2026-03-13 18:12:31 +08:00
@app.get("/api/v1/sdlc/status/{task_id}")
def get_task_status(task_id: str):
"""
获取任务状态非流式
"""
task = task_manager.get_task(task_id)
if not task:
raise HTTPException(status_code=404, detail="Task not found")
2026-03-13 14:20:58 +08:00
return {
2026-03-13 18:12:31 +08:00
"task_id": task["task_id"],
"status": task["status"],
"created_at": task["created_at"],
2026-03-13 20:00:07 +08:00
"updated_at": task["updated_at"]
2026-03-13 14:20:58 +08:00
}
2026-03-13 18:12:31 +08:00
@app.get("/")
def root():
"""根路径重定向到测试页面"""
return RedirectResponse(url="/static/index.html")
2026-03-13 14:20:58 +08:00
2026-03-13 18:12:31 +08:00
@app.get("/health")
def health_check():
"""健康检查端点"""
return {"status": "healthy", "version": "1.0.0"}
2026-03-13 14:20:58 +08:00
2026-03-13 18:12:31 +08:00
# ========== 主程序入口 ==========
2026-03-13 14:20:58 +08:00
if __name__ == "__main__":
uvicorn.run(
"main:app",
host="0.0.0.0",
2026-03-13 20:53:44 +08:00
port=8080,
2026-03-13 20:00:07 +08:00
reload=False,
2026-03-13 14:20:58 +08:00
log_level="info"
)