init
This commit is contained in:
104
backend/app/services/ai_advisor.py
Normal file
104
backend/app/services/ai_advisor.py
Normal file
@@ -0,0 +1,104 @@
|
||||
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")
|
||||
Reference in New Issue
Block a user