From e1bbc921624edc09eabac1db8ff7440fb18dec85 Mon Sep 17 00:00:00 2001 From: ziabric Date: Fri, 23 Jan 2026 10:34:50 +0300 Subject: [PATCH] added fastapi --- .gitignore | 2 +- main.py | 106 ++++++++++++++++++++++++++++++++++++------ mcp_agent.config.yaml | 2 +- 3 files changed, 93 insertions(+), 17 deletions(-) diff --git a/.gitignore b/.gitignore index 36b13f1..cfbddc1 100644 --- a/.gitignore +++ b/.gitignore @@ -173,4 +173,4 @@ cython_debug/ # PyPI configuration file .pypirc - +.idea/** diff --git a/main.py b/main.py index 720c3df..82ab36f 100644 --- a/main.py +++ b/main.py @@ -1,29 +1,105 @@ import asyncio +import json +import urllib.request +from contextlib import asynccontextmanager +from typing import Optional + +from fastapi import FastAPI +from pydantic import BaseModel from mcp_agent.app import MCPApp from mcp_agent.agents.agent import Agent from mcp_agent.workflows.llm.augmented_llm_openai import OpenAIAugmentedLLM -app = MCPApp(name="weather_agent") +OLLAMA_MODEL = "qwen3:1.7b" +OLLAMA_KEEP_ALIVE = "10h" + +AGENT_INSTRUCTION = ( + "Ты ассистент по погоде. " + "Если нужны актуальные данные — используй доступные инструменты." + "ОТВЕЧАЙ ТОЛЬКО НА РУССКОМ ЯЗЫКЕ!" + "Не выводи блоки или внутренние рассуждения, отвечай только итогом." +) + +MCP_SERVER_NAMES = ["weather"] -async def main(): - async with app.run(): - agent = Agent( +def warmup_ollama(model: str, keep_alive: str = "10h") -> None: + payload = { + "model": model, + "prompt": "", + "stream": False, + "keep_alive": keep_alive, + } + req = urllib.request.Request( + "http://localhost:11434/api/generate", + data=json.dumps(payload).encode("utf-8"), + headers={"Content-Type": "application/json"}, + method="POST", + ) + try: + urllib.request.urlopen(req, timeout=5).read() + except Exception: + pass + + +class AskRequest(BaseModel): + question: str + + +class AskResponse(BaseModel): + answer: str + + +app_mcp = MCPApp(name="weather_agent") +_agent: Optional[Agent] = None +_llm: Optional[OpenAIAugmentedLLM] = None +_lock: asyncio.Lock = asyncio.Lock() + + +@asynccontextmanager +async def lifespan(app: FastAPI): + """ + Startup: + - прогреваем Ollama + - поднимаем MCPApp + - создаём Agent и подключаем LLM один раз + Shutdown: + - корректно закрываем agent и MCPApp + """ + global _agent, _llm + + warmup_ollama(OLLAMA_MODEL, keep_alive=OLLAMA_KEEP_ALIVE) + + async with app_mcp.run(): + _agent = Agent( name="weather", - instruction=( - "Ты ассистент по погоде. " - "Если нужны актуальные данные — используй доступные инструменты." - ), - server_names=["weather"], + instruction=AGENT_INSTRUCTION, + server_names=MCP_SERVER_NAMES, ) - async with agent: - llm = await agent.attach_llm(OpenAIAugmentedLLM) - answer = await llm.generate_str("Какая сейчас точная погода в Берлине?") - print(answer) + async with _agent: + _llm = await _agent.attach_llm(OpenAIAugmentedLLM) + yield + +api = FastAPI(title="Weather MCP Agent API", lifespan=lifespan) -if __name__ == "__main__": - asyncio.run(main()) +@api.post("/ask", response_model=AskResponse) +async def ask(req: AskRequest) -> AskResponse: + """ + Принимает вопрос, возвращает краткий ответ на русском. + """ + if _llm is None: + return AskResponse(answer="Сервис не готов. Попробуйте позже.") + + async with _lock: + answer = await _llm.generate_str(req.question) + + return AskResponse(answer=answer) + + +@api.get("/health") +async def health(): + return {"status": "ok"} diff --git a/mcp_agent.config.yaml b/mcp_agent.config.yaml index 3ebbd4f..b01724a 100644 --- a/mcp_agent.config.yaml +++ b/mcp_agent.config.yaml @@ -8,4 +8,4 @@ mcp: openai: base_url: "http://localhost:11434/v1" - default_model: "qwen3:1.7b" + default_model: "llama3.2:1b"