最后一版
This commit is contained in:
@@ -33,11 +33,11 @@ dist/
|
||||
Dockerfile
|
||||
.dockerignore
|
||||
|
||||
# Documentation
|
||||
README.md
|
||||
*.md
|
||||
|
||||
# Test
|
||||
.pytest_cache/
|
||||
.coverage
|
||||
htmlcov/
|
||||
|
||||
# Logs
|
||||
logs/
|
||||
*.log
|
||||
|
||||
147
DOCKER.md
Normal file
147
DOCKER.md
Normal file
@@ -0,0 +1,147 @@
|
||||
# Docker 部署指南
|
||||
|
||||
## 快速启动
|
||||
|
||||
### 方式一:使用 Docker Compose (推荐)
|
||||
|
||||
1. **配置环境变量**
|
||||
```bash
|
||||
# 复制环境变量文件
|
||||
cp .env.example .env
|
||||
|
||||
# 编辑 .env 文件,填入你的 DashScope API Key
|
||||
DASHSCOPE_API_KEY=your_api_key_here
|
||||
```
|
||||
|
||||
2. **启动服务**
|
||||
```bash
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
3. **查看日志**
|
||||
```bash
|
||||
docker-compose logs -f
|
||||
```
|
||||
|
||||
4. **访问应用**
|
||||
- API 文档:http://localhost:8080/docs
|
||||
- 前端界面:http://localhost:8080/static/index.html
|
||||
|
||||
5. **停止服务**
|
||||
```bash
|
||||
docker-compose down
|
||||
```
|
||||
|
||||
### 方式二:使用 Docker 构建
|
||||
|
||||
1. **构建镜像**
|
||||
```bash
|
||||
docker build -t sdlc-agent-demo:latest .
|
||||
```
|
||||
|
||||
2. **运行容器**
|
||||
```bash
|
||||
docker run -d \
|
||||
-p 8080:8080 \
|
||||
-e DASHSCOPE_API_KEY=your_api_key_here \
|
||||
-v $(pwd)/logs:/app/logs \
|
||||
--name sdlc-agent \
|
||||
sdlc-agent-demo:latest
|
||||
```
|
||||
|
||||
3. **查看日志**
|
||||
```bash
|
||||
docker logs -f sdlc-agent
|
||||
```
|
||||
|
||||
4. **停止并删除容器**
|
||||
```bash
|
||||
docker stop sdlc-agent && docker rm sdlc-agent
|
||||
```
|
||||
|
||||
## 环境变量
|
||||
|
||||
| 变量名 | 说明 | 默认值 | 必需 |
|
||||
|--------|------|--------|------|
|
||||
| `DASHSCOPE_API_KEY` | 阿里云 DashScope API Key | 无 | ✅ |
|
||||
| `PYTHONUNBUFFERED` | Python 输出缓冲设置 | 1 | ❌ |
|
||||
|
||||
## 端口说明
|
||||
|
||||
| 端口 | 说明 |
|
||||
|------|------|
|
||||
| 8080 | HTTP 服务端口 |
|
||||
|
||||
## 数据卷
|
||||
|
||||
| 路径 | 说明 |
|
||||
|------|------|
|
||||
| `./logs` | 日志文件存储目录 |
|
||||
|
||||
## 常见问题
|
||||
|
||||
### 1. 构建失败
|
||||
|
||||
如果遇到依赖安装失败,尝试使用国内镜像:
|
||||
|
||||
```dockerfile
|
||||
# 在 Dockerfile 开头添加
|
||||
FROM registry.cn-hangzhou.aliyuncs.com/library/python:3.11-slim
|
||||
```
|
||||
|
||||
### 2. API Key 配置
|
||||
|
||||
确保在 `.env` 文件中正确配置了 DashScope API Key:
|
||||
|
||||
```bash
|
||||
DASHSCOPE_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxx
|
||||
```
|
||||
|
||||
### 3. 网络问题
|
||||
|
||||
如果容器无法访问外网,检查 Docker 网络配置:
|
||||
|
||||
```bash
|
||||
docker network inspect sdlc-network
|
||||
```
|
||||
|
||||
## 生产环境建议
|
||||
|
||||
1. **使用 secrets 管理敏感信息**
|
||||
```yaml
|
||||
secrets:
|
||||
dashscope_api_key:
|
||||
external: true
|
||||
|
||||
services:
|
||||
sdlc-agent:
|
||||
secrets:
|
||||
- dashscope_api_key
|
||||
```
|
||||
|
||||
2. **添加资源限制**
|
||||
```yaml
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '2'
|
||||
memory: 2G
|
||||
```
|
||||
|
||||
3. **配置日志轮转**
|
||||
```yaml
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "3"
|
||||
```
|
||||
|
||||
4. **使用持久化存储**
|
||||
```yaml
|
||||
volumes:
|
||||
- sdlc-data:/app/logs
|
||||
|
||||
volumes:
|
||||
sdlc-data:
|
||||
```
|
||||
42
Dockerfile
42
Dockerfile
@@ -1,46 +1,36 @@
|
||||
# 多阶段构建 - SDLC Agent Demo
|
||||
FROM python:3.11-slim as builder
|
||||
# SDLC Agent Demo - Dockerfile
|
||||
# 基于 CrewAI + Qwen3.5-flash + FastAPI 的多智能体软件交付系统
|
||||
|
||||
# 设置工作目录
|
||||
WORKDIR /app
|
||||
FROM python:3.11-slim
|
||||
|
||||
# 设置环境变量
|
||||
ENV PYTHONDONTWRITEBYTECODE=1 \
|
||||
PYTHONUNBUFFERED=1 \
|
||||
PIP_NO_CACHE_DIR=1 \
|
||||
PIP_DISABLE_PIP_VERSION_CHECK=1
|
||||
|
||||
# 安装依赖
|
||||
COPY requirements.txt .
|
||||
RUN pip install --user --no-cache-dir -r requirements.txt
|
||||
|
||||
# 运行阶段
|
||||
FROM python:3.11-slim
|
||||
PIP_DISABLE_PIP_VERSION_CHECK=1 \
|
||||
PYTHONPATH=/app
|
||||
|
||||
# 设置工作目录
|
||||
WORKDIR /app
|
||||
|
||||
# 从 builder 阶段复制安装的包
|
||||
COPY --from=builder /root/.local /root/.local
|
||||
# 安装系统依赖
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
gcc \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# 确保脚本路径在 PATH 中
|
||||
ENV PATH=/root/.local/bin:$PATH \
|
||||
PYTHONPATH=/app
|
||||
# 安装 Python 依赖
|
||||
COPY requirements.txt .
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
# 复制应用代码
|
||||
COPY . .
|
||||
|
||||
# 创建非 root 用户
|
||||
RUN useradd -m -u 1000 appuser && \
|
||||
chown -R appuser:appuser /app
|
||||
USER appuser
|
||||
|
||||
# 暴露端口
|
||||
EXPOSE 8000
|
||||
EXPOSE 8080
|
||||
|
||||
# 健康检查
|
||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
||||
CMD python -c "import httpx; httpx.get('http://localhost:8000/health')" || exit 1
|
||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=10s --retries=3 \
|
||||
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8080/docs')" || exit 1
|
||||
|
||||
# 启动命令
|
||||
CMD ["python", "-m", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
|
||||
CMD ["python", "main.py"]
|
||||
|
||||
41
README.md
41
README.md
@@ -95,7 +95,24 @@ uvicorn main:app --reload --host 0.0.0.0 --port 8000
|
||||
|
||||
打开浏览器访问:http://localhost:8000/static/index.html
|
||||
|
||||
### 方法二:Docker 运行
|
||||
### 方法二:使用 Docker Compose (推荐)
|
||||
|
||||
```bash
|
||||
# 1. 配置环境变量
|
||||
cp .env.example .env
|
||||
# 编辑 .env 文件,填入你的 DashScope API Key
|
||||
|
||||
# 2. 启动服务
|
||||
docker-compose up -d
|
||||
|
||||
# 3. 查看日志
|
||||
docker-compose logs -f
|
||||
|
||||
# 4. 停止服务
|
||||
docker-compose down
|
||||
```
|
||||
|
||||
### 方法三:使用 Docker 构建
|
||||
|
||||
#### 1. 构建镜像
|
||||
|
||||
@@ -106,16 +123,34 @@ docker build -t sdlc-agent-demo .
|
||||
#### 2. 运行容器
|
||||
|
||||
```bash
|
||||
# 使用 docker-compose (推荐)
|
||||
docker-compose up -d
|
||||
|
||||
# 或单独运行容器
|
||||
docker run -d \
|
||||
-p 8000:8000 \
|
||||
-p 8080:8080 \
|
||||
-e DASHSCOPE_API_KEY=your_api_key \
|
||||
-v $(pwd)/logs:/app/logs \
|
||||
--name sdlc-demo \
|
||||
sdlc-agent-demo
|
||||
```
|
||||
|
||||
#### 3. 访问服务
|
||||
|
||||
http://localhost:8000/static/index.html
|
||||
- 前端界面:http://localhost:8080/static/index.html
|
||||
- API 文档:http://localhost:8080/docs
|
||||
|
||||
#### 4. 查看日志
|
||||
|
||||
```bash
|
||||
docker logs -f sdlc-demo
|
||||
```
|
||||
|
||||
#### 5. 停止服务
|
||||
|
||||
```bash
|
||||
docker stop sdlc-demo && docker rm sdlc-demo
|
||||
```
|
||||
|
||||
## 📡 API 接口
|
||||
|
||||
|
||||
33
docker-compose.yml
Normal file
33
docker-compose.yml
Normal file
@@ -0,0 +1,33 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
sdlc-agent:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
container_name: sdlc-agent-demo
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "8080:8080"
|
||||
environment:
|
||||
- DASHSCOPE_API_KEY=${DASHSCOPE_API_KEY:sk-616332b2afa94699b4572d0fe6ac370a}
|
||||
- PYTHONUNBUFFERED=1
|
||||
env_file:
|
||||
- .env
|
||||
volumes:
|
||||
- ./logs:/app/logs
|
||||
networks:
|
||||
- sdlc-network
|
||||
healthcheck:
|
||||
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8080/docs')"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 10s
|
||||
labels:
|
||||
- "com.sdlc.agent.name=SDLC Agent Demo"
|
||||
- "com.sdlc.agent.version=1.0"
|
||||
|
||||
networks:
|
||||
sdlc-network:
|
||||
driver: bridge
|
||||
131
main.py
131
main.py
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
SDLC Agent Demo - FastAPI 主服务(异步版本)
|
||||
SDLC Agent Demo - FastAPI 主服务(轮询版本)
|
||||
多智能体端到端软件交付协同系统
|
||||
"""
|
||||
|
||||
@@ -7,7 +7,6 @@ import json
|
||||
import uuid
|
||||
import asyncio
|
||||
import threading
|
||||
import queue
|
||||
from typing import Dict, Optional, AsyncGenerator
|
||||
from datetime import datetime
|
||||
from fastapi import FastAPI, HTTPException
|
||||
@@ -16,6 +15,7 @@ from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.responses import StreamingResponse, RedirectResponse, JSONResponse
|
||||
from pydantic import BaseModel, Field
|
||||
import uvicorn
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
|
||||
from crews.sdlc_crew import SDLCCrew
|
||||
from models.qwen_config import get_qwen_config
|
||||
@@ -27,7 +27,7 @@ app = FastAPI(
|
||||
version="1.0.0"
|
||||
)
|
||||
|
||||
# 启用 CORS
|
||||
# 添加 CORS 中间件
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"],
|
||||
@@ -104,6 +104,9 @@ class TaskManager:
|
||||
# 全局任务管理器
|
||||
task_manager = TaskManager()
|
||||
|
||||
# 线程池
|
||||
executor = ThreadPoolExecutor(max_workers=5)
|
||||
|
||||
|
||||
# ========== API 端点 ==========
|
||||
@app.post("/api/v1/sdlc/start", response_model=Dict[str, str])
|
||||
@@ -130,13 +133,6 @@ async def start_sdlc_process(request: StartRequest):
|
||||
}
|
||||
|
||||
|
||||
import threading
|
||||
import asyncio
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
|
||||
# 线程池
|
||||
executor = ThreadPoolExecutor(max_workers=5)
|
||||
|
||||
def execute_sdlc_flow(task_id: str, requirement: str):
|
||||
"""
|
||||
在线程池中执行 SDLC 流程,将事件保存到任务列表
|
||||
@@ -195,42 +191,6 @@ async def poll_task_events(task_id: str, last_index: int = 0):
|
||||
return result
|
||||
|
||||
|
||||
@app.get("/api/v1/sdlc/stream/{task_id}")
|
||||
async def stream_task_progress(task_id: str):
|
||||
"""
|
||||
SSE流式输出任务进度
|
||||
|
||||
直接在异步函数中使用 async for 和 yield,
|
||||
FastAPI 会自动将其转换为 StreamingResponse
|
||||
"""
|
||||
# 验证任务存在
|
||||
task = task_manager.get_task(task_id)
|
||||
if not task:
|
||||
raise HTTPException(status_code=404, detail="Task not found")
|
||||
|
||||
# 直接使用 async for 和 yield
|
||||
while True:
|
||||
event = await task_manager.get_event(task_id, timeout=120.0)
|
||||
|
||||
if event is None:
|
||||
# 超时,检查任务状态
|
||||
task_data = task_manager.get_task(task_id)
|
||||
if task_data and task_data["status"] in ["completed", "failed"]:
|
||||
yield f"event: end\ndata: {json.dumps({'status': task_data['status']}, ensure_ascii=False)}\n\n"
|
||||
break
|
||||
continue
|
||||
|
||||
# 格式化 SSE事件
|
||||
event_type = event.get("event", "message")
|
||||
event_data = event.get("data", {})
|
||||
|
||||
yield f"event: {event_type}\ndata: {json.dumps(event_data, ensure_ascii=False)}\n\n"
|
||||
|
||||
# 如果是结束事件,断开连接
|
||||
if event_type in ["final_result", "error"]:
|
||||
break
|
||||
|
||||
|
||||
@app.get("/api/v1/sdlc/status/{task_id}")
|
||||
def get_task_status(task_id: str):
|
||||
"""
|
||||
@@ -248,85 +208,6 @@ def get_task_status(task_id: str):
|
||||
}
|
||||
|
||||
|
||||
@app.get("/api/v1/sdlc/result/{task_id}")
|
||||
def get_task_result(task_id: str):
|
||||
"""
|
||||
获取任务完整结果
|
||||
"""
|
||||
task = task_manager.get_task(task_id)
|
||||
if not task:
|
||||
raise HTTPException(status_code=404, detail="Task not found")
|
||||
|
||||
if task["status"] != "completed":
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail=f"Task not completed yet. Status: {task['status']}"
|
||||
)
|
||||
|
||||
return task
|
||||
|
||||
|
||||
@app.get("/api/v1/sdlc/download/{task_id}")
|
||||
def download_result(task_id: str):
|
||||
"""
|
||||
打包下载任务结果(ZIP 文件)
|
||||
"""
|
||||
import zipfile
|
||||
import io
|
||||
from fastapi.responses import StreamingResponse
|
||||
|
||||
task = task_manager.get_task(task_id)
|
||||
if not task:
|
||||
raise HTTPException(status_code=404, detail="Task not found")
|
||||
|
||||
if task["status"] != "completed":
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail=f"Task not completed yet. Status: {task['status']}"
|
||||
)
|
||||
|
||||
# 创建 ZIP 文件
|
||||
zip_buffer = io.BytesIO()
|
||||
|
||||
with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zip_file:
|
||||
# 1. SRS 文档
|
||||
srs_content = ""
|
||||
# 注意:异步模式下需要从 events 中获取,这里简化处理
|
||||
zip_file.writestr("01_SRS_需求规格说明书.md", "需求文档内容")
|
||||
|
||||
# 2. 测试用例
|
||||
zip_file.writestr("02_Test_测试用例.md", "测试用例内容")
|
||||
|
||||
# 3. 代码实现
|
||||
zip_file.writestr("03_Code_代码实现.md", "代码实现内容")
|
||||
|
||||
# 4. 项目摘要
|
||||
summary = f"""# SDLC 项目交付摘要
|
||||
|
||||
## 项目信息
|
||||
- 任务 ID: {task['task_id']}
|
||||
- 创建时间:{task['created_at']}
|
||||
- 完成时间:{task['updated_at']}
|
||||
- 原始需求:{task['requirement']}
|
||||
|
||||
## 生成说明
|
||||
本项目由 SDLC Agent Demo 自动生成
|
||||
基于 CrewAI + Qwen3.5-flash + FastAPI
|
||||
"""
|
||||
zip_file.writestr("README_项目摘要.md", summary)
|
||||
|
||||
# 准备下载
|
||||
zip_buffer.seek(0)
|
||||
|
||||
return StreamingResponse(
|
||||
zip_buffer,
|
||||
media_type="application/zip",
|
||||
headers={
|
||||
"Content-Disposition": f"attachment; filename=SDLC_Result_{task_id[:8]}.zip"
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@app.get("/")
|
||||
def root():
|
||||
"""根路径重定向到测试页面"""
|
||||
|
||||
@@ -226,21 +226,6 @@
|
||||
<div class="space-y-6" v-show="results.length > 0">
|
||||
<div class="flex items-center justify-between">
|
||||
<h2 class="text-lg font-semibold text-gray-900">4. 输出结果</h2>
|
||||
<button
|
||||
@click="downloadResults"
|
||||
:disabled="!isCompleted"
|
||||
:class="[
|
||||
'px-4 py-2 rounded-lg font-medium text-white transition-all duration-200 flex items-center',
|
||||
isCompleted
|
||||
? 'bg-green-600 hover:bg-green-700 shadow-md'
|
||||
: 'bg-gray-400 cursor-not-allowed'
|
||||
]"
|
||||
>
|
||||
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"/>
|
||||
</svg>
|
||||
{{ isCompleted ? '打包下载结果' : '执行完成后下载' }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-for="(result, index) in results" :key="index" class="bg-white rounded-lg shadow-md overflow-hidden">
|
||||
@@ -355,16 +340,6 @@
|
||||
},
|
||||
|
||||
methods: {
|
||||
/**
|
||||
* 下载打包结果
|
||||
*/
|
||||
downloadResults() {
|
||||
if (!this.taskId || !this.isCompleted) return;
|
||||
|
||||
const url = `/api/v1/sdlc/download/${this.taskId}`;
|
||||
window.open(url, '_blank');
|
||||
},
|
||||
|
||||
/**
|
||||
* 启动 SDLC 流程
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user