diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..a2e4e89 --- /dev/null +++ b/.dockerignore @@ -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 \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..acee6f8 --- /dev/null +++ b/Dockerfile @@ -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"] diff --git a/__pycache__/gbase_agent.cpython-312.pyc b/__pycache__/gbase_agent.cpython-312.pyc index 53f1281..e2649d8 100644 Binary files a/__pycache__/gbase_agent.cpython-312.pyc and b/__pycache__/gbase_agent.cpython-312.pyc differ diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..2b48421 --- /dev/null +++ b/docker-compose.yml @@ -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 diff --git a/fastapi_app.py b/fastapi_app.py index 691e6e1..2d87f2e 100644 --- a/fastapi_app.py +++ b/fastapi_app.py @@ -1,6 +1,7 @@ import json import os from typing import AsyncGenerator, Dict, List, Optional, Union +from contextlib import asynccontextmanager import uvicorn 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 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")) +@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): role: str content: str @@ -279,32 +308,6 @@ async def cleanup_cache(): 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__": diff --git a/gbase_agent.py b/gbase_agent.py index 53ecc9d..678ed35 100644 --- a/gbase_agent.py +++ b/gbase_agent.py @@ -21,7 +21,6 @@ import os from typing import Dict, List, Optional, Union from qwen_agent.agents import Assistant -from qwen_agent.gui import WebUI from qwen_agent.llm.oai import TextChatAtOAI from qwen_agent.llm.schema import ASSISTANT, FUNCTION, Message from qwen_agent.utils.output_beautify import typewriter_print @@ -169,67 +168,3 @@ def test(query="数据库里有几张表"): final_response = responses[-1][-1] # 取最后一个响应作为最终结果 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() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..752b45e --- /dev/null +++ b/requirements.txt @@ -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