/
maks20352
/
pankihoy_hackaton
Обзор
Документация
Войти
/
maks20352
/
pankihoy_hackaton
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
code.py
179 строк
9 KB
maks20352
update code.py
28 окт 2025, 11:37
28 окт 2025, 11:37
c1d7b51
Код
Авторство
О чём код?
import os import uvicorn from fastapi import FastAPI, HTTPException from pydantic import BaseModel from typing import List from openai import OpenAI import pandas as pd import json # Инициализация FastAPI app = FastAPI() class QueryRequest(BaseModel): name: str manufacturer: str article: str class ResponseModel(BaseModel): analogs: List[str] # Константы EXCEL_FILE_PATH = "/home/jovyan/Systeme_Electric.xlsx" API_KEY = os.getenv("API_KEY") if not API_KEY: raise RuntimeError("Переменная окружения API_KEY не задана") BASE_URL = "https://foundation-models.api.cloud.ru/v1" PROMPT_TEMPLATE = prompt = """Проанализируй строку наименования от производителя и извлеки из неё только те характеристики, которые явно указаны или однозначно подразумеваются согласно общепринятой в электротехнике маркировке и типовым обозначениям. Используй только следующие поля (в точности как перечислены ниже). Если значение не упомянуто и не может быть достоверно выведено — ставь null. Поля и правила их заполнения: Тип устройства: Тип устройства в полном или сокращенном виде. Кол-во полюсов: Определяй по явным указаниям: «1P», «1п», «1Р», «2P», «3P», «4P», «1п+N», «3P+N» и т.п. Учитывай, что «1п+N» = «1P+N», «3Р» = «3P». Для дифавтоматов типа «1P+N» — указывай именно «1P+N». Связан с Типом контактов, т.е. если 2НО или 2НЗ то 2P, 2НО+2НЗ это 4P и т.д. Возможные значения: ['1P', '1P+N', '2P', '2P+PE', '3P', '3P+N', '4P']. Ном. ток, А: Извлекай цифровое значение тока (в амперах), указанное рядом с «А», «Амп», «Iн=», или в формате «C16», «D25» и т.п. Допустимые значения: ['1', '1.6', '2', '3', '4', '5', '6', '8', '10', '13', '16', '20', '25', '32', '40', '50', '63', '80', '100', '125']. Если есть Ном. ток AC-7a, то Ном. ток, А нужно пропускать Откл. способность, кА: Ищешь значения «4,5кА», «6кА», «10кА» и т.п. Преобразуй в числовой формат из списка: ['4.5', '6', '10'] → но в JSON используй строку: '4.5', '6', '10' только если указано явно. Иначе — null. Тип кривой: Извлекай букву после упоминания автомата или дифавтомата: «B», «C», «D» (например, «характеристика C», «тип D», «C16»). Возможные значения: ['B', 'C', 'D']. Чувствительность, мА: Ищешь значение утечки: «10мА», «30мА», «100мА», «300мА» — только для УЗО и дифавтоматов. Возможные значения: ['10', '30', '100', '300']. Тип ВДТ: Указывается как «тип A», «тип AC», «электронный тип A» и т.п. Возможные значения: ['A', 'AC']. Если не указан — null. Селективный ВДТ: Только если прямо сказано «селективный» или есть маркировка типа «S» — ставь 'Да'. Иначе — 'Нет' только если это УЗО/дифавтомат, иначе — null. Ном. рабочее напряжение расцепителя, В пер. ток: Для контакторов и реле — напряжение катушки («AC220-240В», «230В» и т.п.). Приводи к ближайшему из: ['12-24', '220-240', '230', '100-415']. Для автоматов и УЗО — null. Ном. ток AC-7a, А и Ном. ток AC-7b, А: Применимо только к контакторам. Если указан общий ток (например, «16А») и тип устройства — контактор, то AC-7a = номинальный ток, а AC-7b = null (если не указано иное). Значения берутся из соответствующих списков. Тип контактов: Только для контакторов и реле. Расшифровывай по обозначению: /20 → «2НО», /11 → «1НО+1НЗ», /40 → «4НО» и т.д. Возможные значения: ['2НО', '2НЗ', '1НО+1НЗ', '4НО', ...]. Кол-во модулей (кратно 9 мм): Для модульных устройств (автоматы, УЗО, дифавтоматы, контакторы): 1P / 1P+N → обычно 1.0 или 2.0 (для дифавтоматов — часто 2.0) 2P → 2.0 3P → 3.0 4P → 4.0 Для контакторов — смотри по модели (например, NCH8-16M/20 = 2 модуля → 2.0). Используй только значения из списка: ['1.0', '2.0', ..., '16.0']. Остальные поля (Цвет индикатора, Класс ОПН, Uc, Imax и др.): Заполняй только если устройство — ОПН, УЗИП или имеет индикатор. Для автоматов, дифавтоматов, контакторов — ставь null. Важно: Не придумывай значения. Не интерполируй отсутствующие данные. Не используй внешние знания, кроме общепринятых стандартов маркировки (IEC, ГОСТ, типовые обозначения IEK/EKF/CHINT/Schneider и др.). Если поле не относится к типу устройства (например, «Чувствительность» для автомата без УЗО) — ставь null.""" # Загрузка Excel один раз при старте try: df_global = pd.read_excel(EXCEL_FILE_PATH) except Exception as e: raise RuntimeError(f"Не удалось загрузить Excel-файл: {e}") # Инициализация клиента OpenAI client = OpenAI(api_key=API_KEY, base_url=BASE_URL) @app.post("/predict", response_model=ResponseModel) def predict(request: QueryRequest): query = request.name.strip() if not query: raise HTTPException(status_code=400, detail="Поле 'name' не может быть пустым") try: # Формируем промт content = f"Инструкция: {PROMPT_TEMPLATE}\n\nЗапрос: {query}" # Вызов модели response = client.chat.completions.create( model="GigaChat/GigaChat-2-Max", max_tokens=2500, temperature=0.7, presence_penalty=0, top_p=0.95, response_format={"type": "json_object"}, messages=[{"role": "user", "content": content}] ) json_str = response.choices[0].message.content # Очистка от markdown-обёртки json_str = json_str.replace("```json", "").replace("```", "").strip() json_data = json.loads(json_str) # Фильтрация критериев criteria = {} skip_keys = {"Тип устройства", "Кол-во модулей (кратно 9 мм)"} for key, value in json_data.items(): if key in skip_keys: continue if value is not None: criteria[key] = value if not criteria: return {"analogs": []} # Подсчёт совпадений match_counts = [] for _, row in df_global.iterrows(): count = 0 for col, crit_value in criteria.items(): if col not in df_global.columns: continue cell_value = row[col] if pd.isna(cell_value) and pd.isna(crit_value): count += 1 elif pd.isna(cell_value) or pd.isna(crit_value): continue else: if str(cell_value).strip() == str(crit_value).strip(): count += 1 match_counts.append(count) df_global['Совпадений'] = match_counts min_matches = len(criteria) filtered = df_global[df_global['Совпадений'] >= min_matches] analogs = ( filtered['Артикул (Аналог)'] .dropna() .astype(str) .tolist() ) return {"analogs": analogs} except json.JSONDecodeError: raise HTTPException(status_code=500, detail="Ошибка парсинга JSON от модели") except Exception as e: raise HTTPException(status_code=500, detail=f"Внутренняя ошибка: {str(e)}") @app.get("/") def probe(): return {"ping": "pong"} if __name__ == "__main__": print("111") uvicorn.run(app, host="0.0.0.0", port=7777)