add docker

This commit is contained in:
朱潮 2025-10-07 17:33:35 +08:00
parent 7b538d4967
commit 9245864314
7 changed files with 168 additions and 93 deletions

56
.dockerignore Normal file
View File

@ -0,0 +1,56 @@
# Git
.git
.gitignore
# Python
__pycache__
*.pyc
*.pyo
*.pyd
.Python
env
pip-log.txt
pip-delete-this-directory.txt
.tox
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.log
.git
.mypy_cache
.pytest_cache
.hypothesis
# Virtual environments
venv/
env/
ENV/
# IDE
.vscode/
.idea/
*.swp
*.swo
*~
# OS
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
# Project specific
logs/
projects/_cache/
*.zip
# Docker
Dockerfile
docker-compose.yml
.dockerignore

44
Dockerfile Normal file
View File

@ -0,0 +1,44 @@
# 使用Python 3.12官方镜像作为基础镜像
FROM python:3.12-slim
# 设置工作目录
WORKDIR /app
# 设置环境变量
ENV PYTHONPATH=/app
ENV PYTHONUNBUFFERED=1
ENV AGENT_POOL_SIZE=1
# 安装系统依赖
RUN sed -i 's|http://deb.debian.org|http://mirrors.aliyun.com|g' /etc/apt/sources.list.d/debian.sources && \
apt-get update && apt-get install -y \
curl \
wget \
git \
nodejs \
npm \
ripgrep \
&& rm -rf /var/lib/apt/lists/*
# 复制requirements文件并安装Python依赖
COPY requirements.txt .
RUN pip install --no-cache-dir -i https://mirrors.aliyun.com/pypi/simple/ -r requirements.txt
# 复制应用代码
COPY . .
# 创建必要的目录
RUN mkdir -p /app/projects
# 设置权限
RUN chmod +x /app/mcp/json_reader_server.py
# 暴露端口
EXPOSE 8000
# 健康检查
HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8000/ || exit 1
# 启动命令
CMD ["python", "fastapi_app.py"]

24
docker-compose.yml Normal file
View File

@ -0,0 +1,24 @@
version: "3.8"
services:
qwen-agent:
build: .
container_name: qwen-agent-api
ports:
- "8000:8000"
environment:
# 应用配置
- PYTHONPATH=/app
- PYTHONUNBUFFERED=1
- AGENT_POOL_SIZE=2
volumes:
# 挂载项目数据目录
- ./projects:/app/projects
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s

View File

@ -1,6 +1,7 @@
import json import json
import os import os
from typing import AsyncGenerator, Dict, List, Optional, Union from typing import AsyncGenerator, Dict, List, Optional, Union
from contextlib import asynccontextmanager
import uvicorn import uvicorn
from fastapi import BackgroundTasks, FastAPI, HTTPException from fastapi import BackgroundTasks, FastAPI, HTTPException
@ -42,12 +43,40 @@ from agent_pool import (get_agent_from_pool, init_global_agent_pool,
from gbase_agent import init_agent_service_universal, update_agent_llm from gbase_agent import init_agent_service_universal, update_agent_llm
from zip_project_handler import zip_handler from zip_project_handler import zip_handler
app = FastAPI(title="Database Assistant API", version="1.0.0")
# 全局助手实例池,在应用启动时初始化 # 全局助手实例池,在应用启动时初始化
agent_pool_size = int(os.getenv("AGENT_POOL_SIZE", "1")) agent_pool_size = int(os.getenv("AGENT_POOL_SIZE", "1"))
@asynccontextmanager
async def lifespan(app: FastAPI):
"""应用生命周期管理"""
# 启动时初始化助手实例池
print(f"正在启动FastAPI应用初始化助手实例池大小: {agent_pool_size}...")
try:
def agent_factory():
return init_agent_service_universal()
await init_global_agent_pool(pool_size=agent_pool_size, agent_factory=agent_factory)
print("助手实例池初始化完成!")
yield
except Exception as e:
print(f"助手实例池初始化失败: {e}")
raise
# 关闭时清理实例池
print("正在关闭应用,清理助手实例池...")
from agent_pool import get_agent_pool
pool = get_agent_pool()
if pool:
await pool.shutdown()
print("助手实例池清理完成!")
app = FastAPI(title="Database Assistant API", version="1.0.0", lifespan=lifespan)
class Message(BaseModel): class Message(BaseModel):
role: str role: str
content: str content: str
@ -279,32 +308,6 @@ async def cleanup_cache():
raise HTTPException(status_code=500, detail=f"缓存清理失败: {str(e)}") raise HTTPException(status_code=500, detail=f"缓存清理失败: {str(e)}")
@app.on_event("startup")
async def startup_event():
"""应用启动时初始化助手实例池"""
print(f"正在启动FastAPI应用初始化助手实例池大小: {agent_pool_size}...")
try:
def agent_factory():
return init_agent_service_universal()
await init_global_agent_pool(pool_size=agent_pool_size, agent_factory=agent_factory)
print("助手实例池初始化完成!")
except Exception as e:
print(f"助手实例池初始化失败: {e}")
raise
@app.on_event("shutdown")
async def shutdown_event():
"""应用关闭时清理实例池"""
print("正在关闭应用,清理助手实例池...")
from agent_pool import get_agent_pool
pool = get_agent_pool()
if pool:
await pool.shutdown()
print("助手实例池清理完成!")
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -21,7 +21,6 @@ import os
from typing import Dict, List, Optional, Union from typing import Dict, List, Optional, Union
from qwen_agent.agents import Assistant from qwen_agent.agents import Assistant
from qwen_agent.gui import WebUI
from qwen_agent.llm.oai import TextChatAtOAI from qwen_agent.llm.oai import TextChatAtOAI
from qwen_agent.llm.schema import ASSISTANT, FUNCTION, Message from qwen_agent.llm.schema import ASSISTANT, FUNCTION, Message
from qwen_agent.utils.output_beautify import typewriter_print from qwen_agent.utils.output_beautify import typewriter_print
@ -169,67 +168,3 @@ def test(query="数据库里有几张表"):
final_response = responses[-1][-1] # 取最后一个响应作为最终结果 final_response = responses[-1][-1] # 取最后一个响应作为最终结果
print("Answer:", final_response["content"]) print("Answer:", final_response["content"])
def app_tui():
# Define the agent - 使用通用初始化
bot = init_agent_service_universal()
# Chat
messages = []
while True:
# Query example: 数据库里有几张表
query = input("user question: ")
# File example: resource/poem.pdf
file = input("file url (press enter if no file): ").strip()
if not query:
print("user question cannot be empty")
continue
if not file:
messages.append({"role": "user", "content": query})
else:
messages.append(
{"role": "user", "content": [{"text": query}, {"file": file}]}
)
response = []
for response in bot.run(messages):
print("bot response:", response)
messages.extend(response)
def app_gui():
# Define the agent - 使用通用初始化
bot = init_agent_service_universal()
chatbot_config = {
"prompt.suggestions": [
"数据库里有几张表",
"创建一个学生表包括学生的姓名、年龄",
"增加一个学生名字叫韩梅梅今年6岁",
]
}
WebUI(
bot,
chatbot_config=chatbot_config,
).run()
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="数据库助手")
parser.add_argument(
"--query", type=str, default="数据库里有几张表", help="用户问题"
)
parser.add_argument(
"--mode",
type=str,
choices=["test", "tui", "gui"],
default="test",
help="运行模式",
)
args = parser.parse_args()
if args.mode == "test":
test(args.query)
elif args.mode == "tui":
app_tui()
elif args.mode == "gui":
app_gui()

13
requirements.txt Normal file
View File

@ -0,0 +1,13 @@
# FastAPI和Web服务器
fastapi==0.116.1
uvicorn==0.35.0
# HTTP客户端
requests==2.32.5
# Qwen Agent框架
qwen-agent[mcp]==0.0.29
# 数据处理
pydantic==2.10.5
python-dateutil==2.8.2