Files
filam3d/backend/app/services/ai_advisor.py
2026-03-22 12:40:33 +03:00

105 lines
4.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import json
import logging
import time
from google import genai
from google.genai import types
from app.config import settings
logger = logging.getLogger("app.services.ai_advisor")
SYSTEM_PROMPT = """
Ты — эксперт по 3D-печати из инженерных пластиков по технологии FDM.
Твоя задача — рекомендовать оптимальный материал для печати на основе описания задачи клиента.
Доступные материалы:
{materials_json}
Правила:
1. Всегда рекомендуй один основной материал и 1-2 альтернативы.
2. Учитывай: температурный режим, механические нагрузки, химическое воздействие, UV, влажность.
3. Если задача не подходит для FDM-печати (слишком мелкие детали, высокая точность) — честно скажи об этом.
4. Отвечай кратко, по делу, на русском языке.
5. Если клиент не указал критичные параметры — задай уточняющие вопросы.
Формат ответа — строго JSON:
{{
"recommended_material_id": <int>,
"recommended_material_name": "<str>",
"reasoning": "<обоснование на русском>",
"alternatives": [{{"material_id": <int>, "name": "<str>", "why": "<причина>"}}],
"questions": ["<вопрос, если нужна доп. информация>"]
}}
"""
async def get_material_recommendation(
task_description: str,
materials_data: list[dict],
budget_preference: str = "optimal",
file_info: dict | None = None,
) -> dict:
"""Get material recommendation from Google Gemini API."""
logger.info("=== AI Advisor request ===")
logger.info("Task: %s", task_description)
logger.info("Budget preference: %s", budget_preference)
logger.info("File info: %s", file_info)
logger.info("Materials count: %d", len(materials_data))
if not settings.GOOGLE_API_KEY:
logger.error("GOOGLE_API_KEY is not configured")
raise ValueError("GOOGLE_API_KEY not configured")
materials_json = json.dumps(materials_data, ensure_ascii=False, indent=2)
system = SYSTEM_PROMPT.format(materials_json=materials_json)
logger.debug("System prompt length: %d chars", len(system))
user_message = f"Описание задачи: {task_description}\nПредпочтение по бюджету: {budget_preference}"
if file_info:
user_message += f"\nИнформация о модели: {json.dumps(file_info, ensure_ascii=False)}"
logger.debug("User message: %s", user_message)
logger.info("Sending request to Gemini API (model: gemini-2.0-flash)...")
start_time = time.time()
client = genai.Client(api_key=settings.GOOGLE_API_KEY)
response = await client.aio.models.generate_content(
model="gemini-3-flash-preview",
contents=user_message,
config=types.GenerateContentConfig(
system_instruction=system,
max_output_tokens=1024,
temperature=0.3,
),
)
elapsed = time.time() - start_time
logger.info("Gemini API responded in %.2f seconds", elapsed)
response_text = response.text
logger.debug("Raw response (%d chars): %s", len(response_text), response_text[:500])
try:
result = json.loads(response_text)
logger.info("Response parsed as JSON successfully")
logger.info("Recommended material: id=%s, name=%s",
result.get("recommended_material_id"), result.get("recommended_material_name"))
logger.info("Alternatives: %d, Questions: %d",
len(result.get("alternatives", [])), len(result.get("questions", [])))
logger.info("=== AI Advisor complete ===")
return result
except json.JSONDecodeError:
logger.warning("Direct JSON parse failed, trying to extract JSON from response...")
start = response_text.find("{")
end = response_text.rfind("}") + 1
if start != -1 and end > start:
extracted = response_text[start:end]
logger.debug("Extracted JSON substring [%d:%d]: %s", start, end, extracted[:300])
result = json.loads(extracted)
logger.info("Extracted JSON parsed successfully")
logger.info("=== AI Advisor complete ===")
return result
logger.error("Failed to extract JSON from AI response: %s", response_text[:200])
raise ValueError("AI вернул невалидный JSON")