""" FastAPI 主入口 提供 RESTful API 和 SSE 流式接口 """ import asyncio import json from datetime import datetime from typing import Dict, Any, Optional from contextlib import asynccontextmanager from fastapi import FastAPI, HTTPException, Request from fastapi.responses import StreamingResponse, JSONResponse, HTMLResponse from fastapi.middleware.cors import CORSMiddleware from pydantic import BaseModel, Field from crew_factory import CrewFactory, run_multi_agent_task from stream_manager import stream_manager, create_sse_generator # ==================== 数据模型 ==================== class RunTaskRequest(BaseModel): """运行任务请求体""" user_requirement: str = Field( ..., description="用户需求描述", example="开发一个在线商城系统,支持用户注册、商品浏览、购物车和支付功能" ) skip_confirmation: bool = Field( default=True, description="是否跳过 Coordinator 的人工确认环节" ) class RunTaskResponse(BaseModel): """运行任务响应体""" task_id: str status: str message: Optional[str] = None class SSEEvent(BaseModel): """SSE 事件数据结构""" agent: str type: str content: str timestamp: str task_id: str # ==================== 生命周期管理 ==================== @asynccontextmanager async def lifespan(app: FastAPI): """应用生命周期管理""" # 启动时执行 print("🚀 Multi-Agent System 启动中...") print("📡 SSE 流服务已就绪") # 启动后台任务:定期清理旧流 asyncio.create_task(cleanup_streams_periodically()) yield # 关闭时清理 print("👋 正在关闭所有 SSE 流...") # 可以在这里添加清理逻辑 # ==================== FastAPI 应用 ==================== app = FastAPI( title="Multi-Agent Software Delivery System", description=""" 基于 CrewAI + Qwen3.5-flash 的多智能体软件交付系统 ## 核心功能 - **产品需求分析**: ProductManager Agent 分析用户需求,生成 PRD - **测试计划制定**: QAEngineer Agent 设计测试策略和用例 - **技术方案设计**: SoftwareDeveloper Agent 输出架构设计和代码框架 - **质量审核**: Coordinator Agent 审核所有产出物并生成交付报告 ## 实时通信 支持 SSE (Server-Sent Events) 协议,实时推送每个 Agent 的思考过程和任务状态 ## API 端点 - `POST /api/run_task` - 启动新任务 - `GET /api/stream/{task_id}` - 订阅任务执行日志(SSE) - `GET /api/task/{task_id}/status` - 查询任务状态 - `GET /test-ui` - 测试页面 """, version="1.0.0", lifespan=lifespan, ) # CORS 中间件(允许跨域) app.add_middleware( CORSMiddleware, allow_origins=["*"], # 生产环境应限制具体域名 allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # ==================== 后台任务 ==================== async def cleanup_streams_periodically(interval: int = 300): """定期清理旧的流(每 5 分钟)""" while True: await asyncio.sleep(interval) try: await stream_manager.cleanup_old_streams(max_age_seconds=3600) except Exception as e: print(f"清理流失败:{e}") # ==================== API 路由 ==================== @app.post( "/api/run_task", response_model=RunTaskResponse, summary="启动多智能体任务", description="接收用户需求,启动 CrewAI 流程,异步执行并立即返回 task_id" ) async def run_task(request: RunTaskRequest): """ 启动新的多智能体任务 - **user_requirement**: 用户需求描述 - **skip_confirmation**: 是否跳过人工确认(默认 True) 返回 task_id 用于后续 SSE 流订阅 """ try: # 生成 task_id 并启动任务 task_id = await run_multi_agent_task( user_requirement=request.user_requirement, skip_confirmation=request.skip_confirmation ) return RunTaskResponse( task_id=task_id, status="started", message="任务已启动,请通过 /api/stream/{task_id} 订阅执行日志" ) except Exception as e: raise HTTPException( status_code=500, detail=f"启动任务失败:{str(e)}" ) @app.get( "/api/stream/{task_id}", summary="订阅任务执行日志 (SSE)", description="建立 SSE 连接,实时接收任务执行过程中的所有事件" ) async def stream_task_logs(task_id: str): """ SSE 端点 - 实时推送任务执行日志 事件类型包括: - **start**: 任务开始 - **agent_start**: Agent 开始执行 - **thought**: Agent 思考过程 - **action**: Agent 执行动作 - **output**: Agent 输出结果 - **step_end**: 步骤完成 - **end**: 任务结束 - **error**: 发生错误 数据格式: ```json { "agent": "ProductManager", "type": "thought", "content": "正在分析用户需求...", "timestamp": "2024-01-01T12:00:00", "task_id": "uuid" } ``` """ # 检查任务是否存在 stream = await stream_manager.get_stream(task_id) if stream is None: # 任务不存在,返回错误 return StreamingResponse( iter([f"data: {json.dumps({'error': 'Task not found', 'task_id': task_id})}\n\n"]), media_type="text/event-stream" ) # 创建 SSE 流 return StreamingResponse( create_sse_generator(task_id), media_type="text/event-stream", headers={ "Cache-Control": "no-cache", "Connection": "keep-alive", "X-Accel-Buffering": "no", # Nginx 禁用缓冲 } ) @app.get( "/api/task/{task_id}/status", summary="查询任务状态", description="获取任务的当前状态和基本信息" ) async def get_task_status(task_id: str): """查询任务状态""" stream = await stream_manager.get_stream(task_id) if stream is None: return {"task_id": task_id, "status": "not_found"} return { "task_id": task_id, "status": "closed" if stream.is_closed else "running", "queue_size": stream.queue.qsize() if hasattr(stream, 'queue') else 0, } @app.get( "/api/streams", summary="列出所有活跃流", description="查看当前所有正在执行的任务" ) async def list_active_streams(): """列出所有活跃的 SSE 流""" streams = stream_manager.list_active_streams() return { "total": len(streams), "streams": streams } @app.get( "/health", summary="健康检查", description="检查服务是否正常运行" ) async def health_check(): """健康检查端点""" return { "status": "healthy", "timestamp": datetime.now().isoformat(), "service": "Multi-Agent Software Delivery System" } # ==================== 测试页面 ==================== @app.get( "/test-ui", response_class=HTMLResponse, summary="测试 UI 页面", description="一个简单的 HTML 页面,用于测试 SSE 流功能" ) async def test_ui(): """返回测试页面""" return HTMLResponse(content=get_test_page_html()) def get_test_page_html() -> str: """生成测试页面 HTML""" return """ 多智能体系统 - 测试 UI

🤖 多智能体软件交付系统

示例需求:
等待中...
Task ID: -

📊 实时事件日志

暂无事件,点击上方"启动任务"按钮开始
""" # ==================== 主程序入口 ==================== if __name__ == "__main__": import uvicorn # 加载环境变量 try: from dotenv import load_dotenv load_dotenv() except ImportError: pass uvicorn.run( "main:app", host="0.0.0.0", port=8000, reload=True, log_level="info" )