СОЗДАНИЕ ДС С ЛОКАЛЬНОЙ LLM. ЧАСТЬ 5: ВЗАИМОДЕЙСТВИЕ С API

Мы уже обсудили данные, грамматику и промпты. Теперь поговорим о транспорте. Как именно наш Python-скрипт передает информацию локальной модели и получает ответ?

Это пятая часть большой серии, где я опишу подробно каждый раздел из общей части. Подписывайся на меня в телеграм, если тоже любишь автоматизировать юридические процессы.

В отличие от облачных решений (ChatGPT, Claude), где мы зависим от интернет-соединения и биллинга, локальная модель работает на нашем железе. Но чтобы не переписывать код под каждую новую нейросеть, мы используем стандартный протокол.

1. ЛОКАЛЬНЫЙ СЕРВЕР (OPENAI-COMPATIBLE)

Большинство современных инструментов для запуска локальных моделей (LM Studio, vLLM, Ollama, llama.cpp) умеют притворяться сервером OpenAI. Это значит, что они поднимают локальный веб-сервер (обычно `http://localhost:1234` или `8000`) и принимают запросы в том же формате, что и официальный API OpenAI.

Это огромное преимущество. В коде не нужно использовать специфические библиотеки для `torch` или `transformers`. Мы просто отправляем обычный POST-запрос с JSON.

2. РЕАЛИЗАЦИЯ В КОДЕ (БЕЗ ЛИШНИХ БИБЛИОТЕК)

Вместо установки тяжелой библиотеки `openai` (которая тянет за собой кучу зависимостей), я использую стандартный легковесный модуль `requests`. Это делает скрипт более портативным и простым для аудита.

Вот ключевая функция `call_llm` из моего проекта:

def call_llm(contract_json, hints=None): payload = { "model": "qwen3-vl-30b-a3b-thinking", # Имя модели, загруженной в сервер "messages": [ {"role": "system", "content": SYSTEM_PROMPT}, {"role": "user", "content": USER_PROMPT_WITH_CONTEXT} ], "temperature": 0.0, # Ноль креативности, максимум логики "max_tokens": -1, # Не ограничиваем длину ответа "response_format": ADDENDUM_SCHEMA # Та самая JSON-схема } # Отправка запроса r = requests.post("http://localhost:1234/v1/chat/completions", json=payload, timeout=900.0) r.raise_for_status() # Парсинг ответа data = r.json() content = data["choices"][0]["message"]["content"] return json.loads(content)

3. УПРАВЛЕНИЕ НАДЕЖНОСТЬЮ (RETRIES)

Локальный сервер — штука капризная. Модель может "захлебнуться" в памяти, видеокарта может перегреться, или генерация может занять слишком много времени. Чтобы скрипт не падал при первой ошибке, я обернул вызов в цикл повторных попыток (retries).

for i in range(5): try: # ... попытка запроса ... return json.loads(content) except Exception as e: if i == 4: # Последняя попытка провалилась print(f"LLM error: {e}") return None time.sleep(2 * (i + 1)) # Экспоненциальная задержка

Если сервер временно перегружен, скрипт подождет 2 секунды, потом 4, потом 6... Это позволяет "протолкнуть" тяжелые запросы даже при высокой нагрузке на GPU.

4. ТАЙМАУТЫ

Обратите внимание на `timeout=900.0` (15 минут). Локальные модели, особенно большие (как Qwen 30B) и особенно в режиме "Thinking" (когда они рассуждают перед ответом), могут думать долго. Обычный таймаут в 30-60 секунд здесь не подойдет. Лучше подождать лишнюю минуту, чем оборвать соединение, когда ответ был уже почти готов.

5. ПОДГОТОВКА КОНТЕКСТА

Важный нюанс: мы не отправляем модели PDF-файл или DOCX. Мы отправляем чистый JSON с текстом договора. Это экономит количество токенов (контекстное окно) и ускоряет обработку. В переменную `contract_json` мы кладем только то, что действительно нужно модели: список глав, текст пунктов, реквизиты. Лишний мусор отсекается на этапе подготовки данных.

В заключительной части мы соберем всё вместе и посмотрим, как полученный JSON превращается в красивый документ Word.

Кому, как и мне, интересно автоматизировать юридические процессы, присоединяйтесь ко мне в телеграме, там я пишу "человеческим языком", а не вот это вот всё)

Начать дискуссию