# Local Model Service Protocol (LMSP) 1.0, Черновик > Made for Agent публикует открытые машиночитаемые протоколы, которые позволяют ИИ-агентам, моделям и инструментам находить друг друга и общаться. Начинаем с LMSP, Local Model Service Protocol. Source: https://madeforagent.com/ru/llms-full.txt Updated: 2026-11-08 --- # Протокол сервиса локальных моделей (LMSP) > LMSP — открытый протокол для обнаружения и вызова языковых моделей, работающих на вашем устройстве. Определяет обнаружение сервисов, каталог моделей, потоковую передачу, устойчивые задачи и совместимость с OpenAI. Source: https://madeforagent.com/ru/lmsp/ Language: ru Updated: 2026-11-08 ## Что делает LMSP {#what} LMSP (Local Model Service Protocol) — открытый протокол для программ, которые запускают языковые модели на устройстве пользователя или в его локальной сети, и для клиентов, которые пользуются этими программами. Резидентный локальный сервис описывает себя по well-known-адресу и перечисляет свои модели. На запросы генерации он отвечает в формате JSON или событиями, отправляемыми сервером, а долгие генерации выполняет как сохраняемые задачи, которые можно отменить и снова отследить после разрыва соединения. Тот же сервис говорит и на API OpenAI, поэтому существующие сегодня клиенты продолжают работать. LMSP не заменяет API OpenAI, Ollama или MCP. Он объединяет их сильные стороны в слое протокола, рассчитанном на резидентные локальные сервисы. ## Коротко {#glance} | Тема | Кратко | | --- | --- | | Адрес по умолчанию | `http://127.0.0.1:17832` | | Описание сервиса | `GET /.well-known/lmsp.json` | | Основной интерфейс | `/lmsp/v1`, JSON поверх HTTP, потоковая передача событиями, отправляемыми сервером | | Интерфейсы совместимости | `/v1` для клиентов OpenAI, необязательный `/api` для клиентов Ollama | | Обнаружение | Адрес вручную, проверка локальных адресов, необязательные mDNS и DNS-SD (`_lmsp._tcp.local.`) | | Аутентификация | Bearer-токены, полученные при локальном сопряжении | | Хранение | Встроенная SQLite, задачи переживают перезапуск | | Контракт | OpenAPI 3.2 | ## Попробуйте {#try} Если на вашем компьютере запущен сервис LMSP, весь сценарий выглядит так. Сначала выполните сопряжение и получите токен, как описано в [разделе 10.2](https://madeforagent.com/ru/lmsp/1.0/security.md#pairing), и сохраните его в `LMSP_TOKEN`. ```sh # 1. Обнаружить сервис curl http://127.0.0.1:17832/.well-known/lmsp.json # 2. Получить список локальных моделей curl http://127.0.0.1:17832/lmsp/v1/models \ -H "Authorization: Bearer $LMSP_TOKEN" # 3. Сгенерировать ответ curl http://127.0.0.1:17832/lmsp/v1/responses \ -H "Authorization: Bearer $LMSP_TOKEN" \ -H "Content-Type: application/json" \ -d '{"model":"qwen3:14b-q4_k_m","input":[{"type":"message","role":"user","content":[{"type":"input_text","text":"Hello"}]}]}' ``` ## Как LMSP соотносится с другими протоколами {#related} | Протокол | Что охватывает | Связь с LMSP | | --- | --- | --- | | API OpenAI | Размещённые модели. Responses API — его новейший интерфейс. | LMSP следует структуре Responses в основном интерфейсе, а сервис предлагает рядом с ним интерфейс, совместимый с OpenAI. | | Ollama | Локальная среда выполнения, которая уже предоставляет эндпоинты, совместимые с OpenAI. | Может стоять за сервисом LMSP в роли адаптера, а LMSP определяет необязательные эндпоинты, совместимые с Ollama. | | MCP | Связывает агентов с инструментами и контекстом. | LMSP связывает агентов с моделями. Оба протокола предпочитают обнаружение без состояния обязательным сессиям. | | A2A | Взаимодействие между агентами. | LMSP заимствует карточки сервисов и явные состояния задач для описания сервиса и для своих задач. | ## Ресурсы {#resources} - [Спецификация, версия 1.0 (черновик)](https://madeforagent.com/ru/lmsp/1.0.md). - Контракт OpenAPI в формате [JSON](https://madeforagent.com/lmsp/1.0/openapi.json) или [YAML](https://madeforagent.com/lmsp/1.0/openapi.yaml). - [llms.txt](https://madeforagent.com/ru/llms.txt) и [llms-full.txt](https://madeforagent.com/ru/llms-full.txt) для языковых моделей. - Каждая страница доступна в формате Markdown. Замените завершающий слэш в её адресе на `.md`, например [`/ru/lmsp/1.0/tasks.md`](https://madeforagent.com/ru/lmsp/1.0/tasks.md). ## Часто задаваемые вопросы ### Что такое LMSP? LMSP, Local Model Service Protocol (протокол сервиса локальных моделей), — открытый протокол для сервисов языковых моделей, которые работают на собственном устройстве пользователя или в его локальной сети. Он определяет, как клиенты обнаруживают сервис, получают список его моделей, запрашивают ответы, получают потоковый вывод и запускают долгие задачи. ### Заменяет ли LMSP API OpenAI, Ollama или MCP? Нет. LMSP опирается на них. Его основной интерфейс повторяет структуру OpenAI Responses, сервис может предоставлять эндпоинты, совместимые с OpenAI и Ollama, а от MCP протокол берёт обнаружение без состояния, от A2A — состояния задач. ### Могут ли существующие клиенты OpenAI пользоваться сервисом LMSP? Обычно да. Им нужен другой базовый URL и токен LMSP в качестве API-ключа. Сервис они автоматически не находят, потому что для обнаружения нужен клиент с поддержкой LMSP. ### Как клиент находит локальный сервис LMSP? Он пробует три способа по порядку. Сначала адрес, введённый вручную, затем стандартный локальный адрес, по которому запрашивает /.well-known/lmsp.json, и наконец, по желанию, поиск _lmsp._tcp.local. через mDNS и DNS-SD в локальной сети. ### Безопасен ли сервис моделей на localhost? Не автоматически, потому что веб-страницы могут пытаться обращаться к локальным портам. Сервисы LMSP по умолчанию привязываются к loopback, требуют Bearer-токены, полученные при локальном сопряжении, проверяют заголовки Origin и Host и требуют аутентификации в режиме LAN. ### В каком состоянии спецификация? Версия 1.0 — первый публичный черновик, и она ещё может измениться несовместимо. Нормативным является английский текст, а переводы предоставляются для удобства. --- # Обзор > Обзор черновика LMSP 1.0. Что такое Local Model Service Protocol, на каких решениях он основан, его слоистая архитектура, как читать спецификацию и какие термины используются во всём тексте. Source: https://madeforagent.com/ru/lmsp/1.0/ Language: ru Specification: LMSP 1.0 (draft) Updated: 2026-11-08 ## 1.1 Аннотация {#abstract} Local Model Service Protocol (LMSP) определяет, как клиенты находят и используют сервисы языковых моделей, работающие на устройстве пользователя или в его локальной сети. Он описывает обнаружение сервисов, каталог моделей, синхронную и потоковую генерацию, а также асинхронные задачи, переживающие перезапуск. Кроме того, он определяет, как один и тот же сервис предлагает интерфейс, совместимый с OpenAI, рядом с интерфейсом LMSP, чтобы существующие клиенты продолжали работать. LMSP не заменяет API OpenAI, Ollama или MCP. Он объединяет их сильные стороны в слое протокола, рассчитанном на резидентный локальный сервис. ## 1.2 Статус документа {#status} Это первый публичный черновик LMSP 1.0, опубликованный 2026-11-08. «LMSP» — рабочее название. Текст открыт для замечаний и может измениться несовместимо до выхода окончательной версии 1.0. Нормативным является английский текст. Переводы предоставляются для удобства; при расхождениях приоритет имеет английский текст. ## 1.3 Основа проектирования {#design-basis} LMSP повторно использует проверенные решения вместо того, чтобы изобретать новые: - **OpenAI Responses API.** Структурированные элементы вывода, семантические события, отправляемые сервером, и фоновое выполнение с запросом состояния и отменой для долгих задач. Это эталон для основного интерфейса диалога LMSP. См. [потоковые ответы](https://developers.openai.com/api/docs/guides/streaming-responses) и [фоновый режим](https://developers.openai.com/api/docs/guides/background). - **Ollama.** Предоставляет эндпоинты `/v1/models`, `/v1/chat/completions` и `/v1/responses`, совместимые с OpenAI, что показывает: один сервис и слой адаптации к OpenAI резко снижают стоимость внедрения. См. [совместимость Ollama с OpenAI](https://docs.ollama.com/api/openai-compatibility). - **Model Context Protocol (MCP).** Его новейшая версия отказывается от обязательной инициализации сессии в пользу обнаружения без состояния: каждый запрос несёт собственные сведения о версии, а клиенты узнают возможности через интерфейс обнаружения. Это подходит для локальных сервисов, где перезапуски, сбои клиентов и обрывы соединений не должны портить состояние протокола. См. [MCP](https://modelcontextprotocol.io/). - **Agent2Agent (A2A).** Карточки сервисов, явные состояния задач, подписка на задачи и их отмена легли в основу описания сервиса и модели асинхронных задач LMSP. См. [A2A](https://a2a-protocol.org/). ## 1.4 Архитектура {#architecture} LMSP состоит из трёх слоёв над адаптерами, которые общаются со средами выполнения моделей: ```text +---------------------------------------------------------------+ | Discovery mDNS / DNS-SD + well-known service document | +---------------------------------------------------------------+ | Core model catalog, responses, SSE, async tasks | +---------------------------------------------------------------+ | Compatibility OpenAI API, optional Ollama API | +---------------------------------------------------------------+ | Adapters llama.cpp, Ollama, vLLM, custom runtimes | +---------------------------------------------------------------+ ``` - **Обнаружение.** mDNS/DNS-SD и well-known-документ с описанием сервиса ([страница 3](https://madeforagent.com/ru/lmsp/1.0/discovery.md)). - **Ядро.** Каталог моделей, синхронные ответы, события, отправляемые сервером, и асинхронные задачи ([страницы 5–7](https://madeforagent.com/ru/lmsp/1.0/models.md)). - **Совместимость.** API, совместимый с OpenAI, и, по желанию, API, совместимый с Ollama ([страница 8](https://madeforagent.com/ru/lmsp/1.0/compatibility.md)). - **Адаптеры.** Мосты к llama.cpp, Ollama, vLLM или собственной среде выполнения. Ведущий принцип — **одно ядро выполнения моделей, два семейства интерфейсов**. Сервис предлагает интерфейс LMSP и интерфейс OpenAI поверх одного и того же ядра, так что есть единственное место, где загружаются модели, выполняются запросы и отслеживаются задачи. ## 1.5 Язык требований {#conformance} Ключевые слова **ДОЛЖЕН/ДОЛЖНЫ** (MUST), **НЕ ДОЛЖЕН/НЕ ДОЛЖНЫ** (MUST NOT), **ОБЯЗАТЕЛЬНО** (REQUIRED), **СЛЕДУЕТ** (SHOULD), **НЕ СЛЕДУЕТ** (SHOULD NOT), **РЕКОМЕНДУЕТСЯ** (RECOMMENDED), **МОЖЕТ/МОГУТ** (MAY) и **НЕОБЯЗАТЕЛЬНО** (OPTIONAL) в этом документе следует понимать так, как описано в [BCP 14](https://www.rfc-editor.org/info/bcp14) ([RFC 2119](https://www.rfc-editor.org/rfc/rfc2119), [RFC 8174](https://www.rfc-editor.org/rfc/rfc8174)), тогда и только тогда, когда они набраны жирным шрифтом заглавными буквами. В остальных случаях это обычные слова. Каждая страница помечена как нормативная или информативная. Нормативные страницы определяют требования; информативные дают рекомендации. Примеры информативны. JSON кодируется в UTF-8, а имена полей используют `snake_case`. ## 1.6 Терминология {#terminology} | Термин | Значение | | --- | --- | | Сервис | Резидентный процесс, который реализует LMSP и запускает модели через адаптеры. | | Клиент | Программа, которая обнаруживает и вызывает сервис: агент, приложение или инструмент. | | Адаптер | Компонент, который соединяет сервис с одной средой выполнения моделей, например llama.cpp, Ollama или vLLM. | | Модель | Локально доступная модель, обозначаемая стабильным `id`. | | Ответ | Результат одного запроса генерации: элементы вывода, статистика использования и статус. | | Задача | Сохраняемое асинхронное выполнение запроса ответа с явным состоянием. | | Событие | Одно событие, отправляемое сервером, с порядковым номером, которое выдаётся во время генерации ответа. | --- # Транспорт и развёртывание > Адреса прослушивания по умолчанию, транспорт, формат потоковой передачи и структура URL сервиса LMSP, а также его работа как службы операционной системы, отдельной от процессов моделей. Source: https://madeforagent.com/ru/lmsp/1.0/transport/ Language: ru Specification: LMSP 1.0 (draft) Updated: 2026-11-08 ## 2.1 Развёртывание по умолчанию {#deployment} По умолчанию сервис **ДОЛЖЕН** слушать loopback-адреса `127.0.0.1` и `::1`. Стандартный порт — `17832`, поэтому базовый адрес по умолчанию — `http://127.0.0.1:17832`. | Параметр | Значение | | --- | --- | | Адреса прослушивания | `127.0.0.1` и `::1` | | Транспорт | HTTP/1.1 или HTTP/2, несущий JSON | | Потоковая передача | События, отправляемые сервером, `Content-Type: text/event-stream` | | Префикс основного интерфейса | `/lmsp/v1` | | Префикс интерфейса, совместимого с OpenAI | `/v1` | | Описание сервиса | `GET /.well-known/lmsp.json` | | Машиночитаемый контракт | `GET /lmsp/v1/openapi.json` | | Необязательный локальный канал | Unix-сокет или именованный канал Windows | Запросы и ответы используют JSON в UTF-8 (`Content-Type: application/json`), кроме потоков (`text/event-stream`) и ошибок (`application/problem+json`). Метки времени — целые числа: секунды с начала эпохи Unix. Идентификаторы — непрозрачные строки; **РЕКОМЕНДУЕТСЯ** использовать префикс типа, например `resp_` или `task_`. Контракт интерфейса — документ OpenAPI 3.2. OpenAPI 3.2 умеет описывать события, отправляемые сервером, JSON Lines и JSON Sequences, поэтому потоковые эндпоинты описаны в том же документе, что и остальные. Контракт этой версии опубликован по адресу [`/lmsp/1.0/openapi.json`](https://madeforagent.com/lmsp/1.0/openapi.json). Сервис **МОЖЕТ** также принимать соединения через Unix-сокет или именованный канал Windows с той же семантикой HTTP. В версии 1.0 способ объявления таких эндпоинтов оставлен расширениям. ## 2.2 Постоянная работа {#operation} Сервис **СЛЕДУЕТ** запускать средствами операционной системы: launchd в macOS, systemd в Linux и диспетчер служб в Windows. Процесс сервиса и процессы моделей **СЛЕДУЕТ** разделять. Сбой модели **СЛЕДУЕТ** изолировать так, чтобы он затрагивал только задачи, использующие эту модель. **НЕ СЛЕДУЕТ** допускать, чтобы из-за него становились недоступны обнаружение сервиса, каталог моделей или запросы о прежних задачах. --- # Обнаружение сервисов > Как клиенты LMSP находят локальный сервис через ручную настройку, проверку loopback-адресов и необязательные mDNS/DNS-SD, и как читать well-known-документ с описанием сервиса. Source: https://madeforagent.com/ru/lmsp/1.0/discovery/ Language: ru Specification: LMSP 1.0 (draft) Updated: 2026-11-08 ## 3.1 Способы обнаружения {#methods} Клиент обнаруживает сервис, пробуя эти способы по порядку: 1. **Ручная настройка.** Пользователь вводит адрес сервиса напрямую. 2. **Проверка стандартных локальных адресов.** Клиент запрашивает `/.well-known/lmsp.json` по адресам-кандидатам. Стандартные кандидаты — `http://127.0.0.1:17832` и `http://[::1]:17832`. 3. **Обнаружение в локальной сети.** Клиент ищет `_lmsp._tcp.local.` через mDNS и DNS-SD. Способ считается успешным, если он даёт корректное описание сервиса ([раздел 3.3](https://madeforagent.com/ru/lmsp/1.0/discovery.md#service-description)). Адрес, введённый пользователем вручную, всегда имеет приоритет над обнаруженными. ## 3.2 Объявление в локальной сети {#mdns} DNS-SD использует записи PTR для поиска экземпляров сервиса, записи SRV для получения хоста и порта и записи TXT для передачи небольшого объёма дополнительных сведений. Вместе с mDNS он обеспечивает обнаружение без настройки в локальной сети ([RFC 6763](https://www.rfc-editor.org/rfc/rfc6763), [RFC 6762](https://www.rfc-editor.org/rfc/rfc6762)). Пример объявления: ```text Service type: _lmsp._tcp.local. Instance name: Workstation LMSP._lmsp._tcp.local. TXT record: v=1 id=svc_01J8M4Y8K6M5E6K6Z7Z0Y8W3M2 path=/ auth=pairing ``` | Ключ TXT | Значение | | --- | --- | | `v` | Версия формата объявления. `1` для LMSP 1.0. | | `id` | Идентификатор сервиса, равный `service.id` в описании сервиса. | | `path` | Путь URL к корню сервиса. Клиенты запрашивают `{path}.well-known/lmsp.json`. | | `auth` | Подсказка об аутентификации. `pairing` означает, что клиент должен пройти сопряжение, прежде чем пользоваться сервисом. | Правила: - Сервис **НЕ ДОЛЖЕН** помещать в запись TXT токены доступа, списки моделей, пути к файлам и любые другие чувствительные сведения. Запись лишь указывает, где находится сервис. Реальные возможности **ДОЛЖНЫ** считываться из описания сервиса. - Сервис **ДОЛЖЕН** по умолчанию держать объявление mDNS выключенным. Оно включается только тогда, когда пользователь явно включил обнаружение в локальной сети. Сервис, который используется только на локальной машине, ничего не объявляет. - Сервис, объявляющий себя в локальной сети, вынужден слушать не только loopback, а это разрешено только в режиме LAN ([раздел 10.5](https://madeforagent.com/ru/lmsp/1.0/security.md#lan)). - Клиент **ДОЛЖЕН** считать запись TXT непроверенной подсказкой и подтверждать сервис, прочитав его описание. ## 3.3 Описание сервиса {#service-description} ```http GET /.well-known/lmsp.json ``` Сервис **ДОЛЖЕН** отдавать этот документ как `application/json` без требования аутентификации. ```json { "protocol": "lmsp", "versions": ["1.0"], "service": { "id": "svc_01J8M4Y8K6M5E6K6Z7Z0Y8W3M2", "name": "Workstation Local Models", "version": "1.0.0" }, "interfaces": [ { "kind": "lmsp", "version": "1.0", "url": "http://127.0.0.1:17832/lmsp/v1", "transport": "http+json" }, { "kind": "openai", "version": "responses+chat", "url": "http://127.0.0.1:17832/v1", "transport": "http+json" } ], "capabilities": { "streaming": true, "tasks": true, "event_resume": true, "conversations": true, "model_management": true }, "authentication": { "type": "bearer", "pairing_url": "http://127.0.0.1:17832/lmsp/v1/pair" }, "openapi": "http://127.0.0.1:17832/lmsp/v1/openapi.json" } ``` | Поле | Значение | | --- | --- | | `protocol` | Всегда `"lmsp"`. | | `versions` | Версии LMSP, которые поддерживает сервис, в виде строк `major.minor`. | | `service` | `id` — стабильный идентификатор сервиса, `name` — отображаемое имя, `version` — версия программного обеспечения сервиса. | | `interfaces` | Предлагаемые интерфейсы. У каждого есть `kind` (`lmsp`, `openai` или `ollama`), `version`, базовый `url` и `transport`. Для `openai` в `version` указаны обслуживаемые семейства API, например `responses+chat`. | | `capabilities` | Булевы флаги возможностей, перечисленные ниже. | | `authentication` | `type` — `bearer`. `pairing_url` — адрес, по которому клиенты обменивают код сопряжения на токен ([раздел 10.2](https://madeforagent.com/ru/lmsp/1.0/security.md#pairing)). | | `openapi` | URL документа OpenAPI, описывающего интерфейс LMSP. | Флаги возможностей: | Флаг | Значение | | --- | --- | | `streaming` | Ответы можно передавать потоком в виде событий, отправляемых сервером. В версии 1.0 флаг **ДОЛЖЕН** быть `true`. | | `tasks` | Доступны асинхронные задачи. В версии 1.0 флаг **ДОЛЖЕН** быть `true`. | | `event_resume` | На потоки событий задач можно подписаться и возобновить их (`GET /lmsp/v1/tasks/{task_id}/events`). | | `conversations` | Сервис хранит ответы, поэтому работают `store` и `previous_response_id`, а `GET /lmsp/v1/responses/{response_id}` доступен. | | `model_management` | Модели можно загружать и выгружать явно. | ## 3.4 Поведение клиента {#client-behavior} Клиент: - **СЛЕДУЕТ** выбирать наибольшую версию из `versions`, которую он тоже поддерживает. - **СЛЕДУЕТ** выбирать из `interfaces` интерфейс LMSP или интерфейс OpenAI в зависимости от того, на каком из них он умеет работать. - **ДОЛЖЕН** использовать только те возможности, которые объявлены в описании. Отсутствующий флаг означает `false`. - **ДОЛЖЕН** игнорировать поля расширений, которых он не знает. - **СЛЕДУЕТ** периодически перечитывать описание сервиса, а также при сбое запроса, а не кэшировать его навсегда. - **СЛЕДУЕТ** запоминать выбранный сервис по `service.id`, а не по адресу, потому что адреса могут меняться. --- # Эндпоинты > Полная таблица эндпоинтов LMSP 1.0 с методом, путём, уровнем требования и назначением, а также соглашения о путях, аутентификации и необязательных эндпоинтах. Source: https://madeforagent.com/ru/lmsp/1.0/endpoints/ Language: ru Specification: LMSP 1.0 (draft) Updated: 2026-11-08 ## 4.1 Таблица эндпоинтов {#table} Соответствующий спецификации сервис **ДОЛЖЕН** реализовывать каждый эндпоинт, помеченный как обязательный. Он **МОЖЕТ** реализовывать необязательные и сообщает о них через флаги возможностей в своём [описании сервиса](https://madeforagent.com/ru/lmsp/1.0/discovery.md#service-description). | Метод | Путь | Требование | Назначение | | --- | --- | --- | --- | | GET | `/.well-known/lmsp.json` | Обязательный | [Обнаружение сервиса и его возможностей](https://madeforagent.com/ru/lmsp/1.0/discovery.md#service-description) | | GET | `/lmsp/v1/openapi.json` | Обязательный | Машиночитаемая спецификация интерфейса | | GET | `/lmsp/v1/health` | Обязательный | [Проверка живости и готовности](https://madeforagent.com/ru/lmsp/1.0/common-rules.md#health) | | POST | `/lmsp/v1/pair` | Обязательный | [Обмен кода сопряжения на Bearer-токен](https://madeforagent.com/ru/lmsp/1.0/security.md#pairing) | | GET | `/lmsp/v1/models` | Обязательный | [Список локальных моделей](https://madeforagent.com/ru/lmsp/1.0/models.md#list) | | GET | `/lmsp/v1/models/{model_id}` | Обязательный | [Получение одной модели](https://madeforagent.com/ru/lmsp/1.0/models.md#operations) | | POST | `/lmsp/v1/responses` | Обязательный | [Синхронная или потоковая генерация](https://madeforagent.com/ru/lmsp/1.0/responses.md#request) | | GET | `/lmsp/v1/responses/{response_id}` | Необязательный | [Получение сохранённого ответа](https://madeforagent.com/ru/lmsp/1.0/responses.md#sync) | | POST | `/lmsp/v1/tasks` | Обязательный | [Создание асинхронной задачи генерации](https://madeforagent.com/ru/lmsp/1.0/tasks.md#create) | | GET | `/lmsp/v1/tasks/{task_id}` | Обязательный | [Получение асинхронной задачи](https://madeforagent.com/ru/lmsp/1.0/tasks.md#create) | | POST | `/lmsp/v1/tasks/{task_id}/cancel` | Обязательный | [Отмена асинхронной задачи](https://madeforagent.com/ru/lmsp/1.0/tasks.md#cancel) | | GET | `/lmsp/v1/tasks/{task_id}/events` | Необязательный | [Подписка на поток событий или его возобновление](https://madeforagent.com/ru/lmsp/1.0/tasks.md#events) | | POST | `/lmsp/v1/models/{model_id}/load` | Необязательный | [Предварительная загрузка модели](https://madeforagent.com/ru/lmsp/1.0/models.md#operations) | | POST | `/lmsp/v1/models/{model_id}/unload` | Необязательный | [Выгрузка модели](https://madeforagent.com/ru/lmsp/1.0/models.md#operations) | Эндпоинты, совместимые с OpenAI, по пути `/v1` и необязательные эндпоинты, совместимые с Ollama, перечислены на [странице 8](https://madeforagent.com/ru/lmsp/1.0/compatibility.md). ## 4.2 Соглашения {#conventions} - **Тела.** Тела запросов и ответов — JSON (`application/json`), кроме потоков событий (`text/event-stream`) и ошибок (`application/problem+json`). - **Параметры пути.** Имя в фигурных скобках, например `{model_id}`, — это подстановка. Значения **ДОЛЖНЫ** быть закодированы с процентным кодированием. Идентификатор модели вида `qwen3:14b-q4_k_m` передаётся как `qwen3%3A14b-q4_k_m`. - **Аутентификация.** Каждый эндпоинт требует `Authorization: Bearer `, кроме `/.well-known/lmsp.json`, `/lmsp/v1/openapi.json`, `/lmsp/v1/health` и `/lmsp/v1/pair`. - **Необязательные эндпоинты.** Необязательный эндпоинт, который сервис не реализует, **ДОЛЖЕН** отвечать `501 Not Implemented` с кодом ошибки `unsupported_capability`. - **Версии и ошибки.** Запросы могут нести заголовки версии, а каждая ошибка использует единый формат. И то и другое определено на [странице 9](https://madeforagent.com/ru/lmsp/1.0/common-rules.md). - **Контракт.** Документ OpenAPI, опубликованный по адресу [`/lmsp/1.0/openapi.json`](https://madeforagent.com/lmsp/1.0/openapi.json), — это машиночитаемая форма данной страницы. При расхождениях нормативен текст. --- # Каталог моделей > Как сервис LMSP перечисляет свои локальные модели, объект модели и его флаги возможностей, состояния жизненного цикла, стабильные идентификаторы моделей и ревизии каталога. Source: https://madeforagent.com/ru/lmsp/1.0/models/ Language: ru Specification: LMSP 1.0 (draft) Updated: 2026-11-08 ## 5.1 Список моделей {#list} ```http GET /lmsp/v1/models?capability=chat&ready_only=false Authorization: Bearer ``` | Параметр запроса | Значение | | --- | --- | | `capability` | Возвращает только модели с указанной возможностью, например `chat`, `embeddings` или `tools`. | | `ready_only` | При значении `true` возвращает только модели, у которых `status` равен `ready`. По умолчанию `false`. | ```json { "object": "list", "catalog_revision": "42", "data": [ { "id": "qwen3:14b-q4_k_m", "object": "model", "display_name": "Qwen3 14B Q4_K_M", "owned_by": "local", "family": "qwen3", "revision": "sha256:8f27c1a4e9b05d36f2a7c8e1b4d90a63c5e2f7b8a1d4c9e60b3f5a7d2c8e1b04", "status": "ready", "capabilities": { "chat": true, "completion": true, "embeddings": false, "tools": true, "vision": false, "structured_output": true, "streaming": true }, "input_modalities": ["text"], "output_modalities": ["text"], "context_window": 32768, "max_output_tokens": 8192, "runtime": { "backend": "llama.cpp", "device": "metal", "load_on_demand": true } } ] } ``` Чтобы клиентам не приходилось раз за разом запрашивать весь каталог, сервису **СЛЕДУЕТ** возвращать заголовок `ETag`, поле `catalog_revision` или и то и другое: - `catalog_revision` — непрозрачная строка, которая меняется при каждом изменении каталога. - Сервису, который отправляет `ETag`, **СЛЕДУЕТ** отвечать на совпадающий `If-None-Match` кодом `304 Not Modified`. ## 5.2 Объект модели {#object} | Поле | Значение | | --- | --- | | `id` | Стабильный идентификатор модели, используемый в каждом запросе, где названа модель. | | `object` | Всегда `"model"`. | | `display_name` | Читаемое имя. Пользователь может его изменить. | | `aliases` | Необязательный список альтернативных имён. Носят только информационный характер. | | `owned_by` | Откуда взялась модель. `local` — для моделей, установленных на этом устройстве. | | `family` | Семейство модели, например `qwen3`. | | `revision` | Дайджест содержимого модели, например `sha256:8f27...`. | | `status` | Состояние жизненного цикла ([раздел 5.3](https://madeforagent.com/ru/lmsp/1.0/models.md#lifecycle)). | | `capabilities` | Булевы флаги: `chat`, `completion`, `embeddings`, `tools`, `vision`, `structured_output` и `streaming`. | | `input_modalities`, `output_modalities` | Виды содержимого, которые модель читает и пишет. В версии 1.0 определена только модальность `text`. | | `context_window` | Максимальное число токенов контекста. | | `max_output_tokens` | Максимальное число токенов в одном ответе. | | `runtime` | `backend` (например, `llama.cpp`), `device` (например, `metal`) и `load_on_demand`. | | `error` | Присутствует, когда `status` равен `error`. Объект проблемы ([раздел 9.3](https://madeforagent.com/ru/lmsp/1.0/common-rules.md#errors)). | Идентификатор `id` модели **ДОЛЖЕН** быть стабильным. Отображаемое имя и алиасы пользователь может менять, поэтому они **НЕ ДОЛЖНЫ** использоваться как первичный ключ. ## 5.3 Жизненный цикл модели {#lifecycle} ```text installed --> loading --> ready --> unloading --> installed | +-----> error ``` | Статус | Значение | | --- | --- | | `installed` | Модель существует, но не загружена. | | `loading` | Выделяется память или инициализируется среда выполнения. | | `ready` | Модель может сразу обрабатывать запросы. | | `unloading` | Ресурсы освобождаются. | | `error` | Загрузка не удалась. Объект модели **СЛЕДУЕТ** снабжать структурированной ошибкой. | Новый запрос на загрузку переводит модель из состояния `error` обратно в `loading`. Если запрос называет модель в состоянии `installed`, у которой `runtime.load_on_demand` равен `true`, сервис загружает её в рамках этого запроса. Если модель нельзя использовать сразу и она не загружается по требованию, сервис отвечает `409` с кодом `model_not_ready`. Если загрузка не удалась, возвращается ошибка `model_load_failed`. ## 5.4 Операции с моделями {#operations} `GET /lmsp/v1/models/{model_id}` возвращает один объект модели. Неизвестный идентификатор даёт `404` с кодом `model_not_found`. Два необязательных эндпоинта управляют загрузкой явно. Они доступны, когда возможность `model_management` равна `true`, и требуют область доступа `models:manage` ([раздел 10.3](https://madeforagent.com/ru/lmsp/1.0/security.md#scopes)): | Эндпоинт | Поведение | | --- | --- | | `POST /lmsp/v1/models/{model_id}/load` | Предварительно загружает модель. Возвращает объект модели с `202 Accepted`, если загрузка началась, или с `200`, если модель уже `ready`. | | `POST /lmsp/v1/models/{model_id}/unload` | Выгружает модель. Возвращает объект модели. Повторный запрос безвреден. | Оба принимают `Idempotency-Key` ([раздел 9.2](https://madeforagent.com/ru/lmsp/1.0/common-rules.md#idempotency)). --- # Ответы и потоковая передача > Основной диалоговый интерфейс LMSP с запросами и элементами вывода в стиле Responses, синхронной и потоковой генерацией, типами событий SSE, идентификаторами событий, переподключением и вызовами инструментов. Source: https://madeforagent.com/ru/lmsp/1.0/responses/ Language: ru Specification: LMSP 1.0 (draft) Updated: 2026-11-08 ## 6.1 Запрос {#request} ```http POST /lmsp/v1/responses Authorization: Bearer Content-Type: application/json Idempotency-Key: 01J8M5T7Z2K4N6Q8S0V3X5B9DG LMSP-Version: 1.0 ``` ```json { "model": "qwen3:14b-q4_k_m", "input": [ { "type": "message", "role": "user", "content": [ { "type": "input_text", "text": "Describe a local model service in one sentence." } ] } ], "stream": false, "max_output_tokens": 512, "temperature": 0.7, "response_format": { "type": "text" }, "store": true } ``` Основной протокол использует структуру `input` и `output` в стиле Responses, а не простой список строк сообщений, чтобы одна форма запроса могла нести: - многоходовые сообщения; - текст, изображения, аудио и файлы; - вызовы инструментов и их результаты; - структурированный вывод; - сводки рассуждений; - мультимодальный вывод. LMSP 1.0 определяет только текстовое содержимое. Типы содержимого для изображений, аудио и файлов зарезервированы для будущих версий. | Поле | Обязательное | Значение | | --- | --- | --- | | `model` | Да | Идентификатор используемой модели. | | `input` | Да | Массив элементов ввода. Элемент `message` имеет `role` (`system`, `user` или `assistant`) и массив частей `content`, например `input_text`. Использование инструментов добавляет элементы `function_call` и `function_call_output` ([раздел 6.5](https://madeforagent.com/ru/lmsp/1.0/responses.md#tools)). | | `stream` | Нет | `true`, чтобы получать события, отправляемые сервером. По умолчанию `false`. | | `max_output_tokens` | Нет | Верхняя граница числа генерируемых токенов. | | `temperature` | Нет | Температура сэмплирования. | | `response_format` | Нет | `{"type": "text"}` (по умолчанию) или `{"type": "json_schema", "json_schema": {"name": "...", "schema": {...}}}` для структурированного вывода. Требует возможности `structured_output`. | | `tools`, `tool_choice` | Нет | Функции-инструменты, которые может вызывать модель ([раздел 6.5](https://madeforagent.com/ru/lmsp/1.0/responses.md#tools)). Требует возможности `tools`. | | `store` | Нет | Просит сервис сохранить ответ, чтобы его можно было получить позже и использовать как `previous_response_id`. По умолчанию `false`. Игнорируется сервисами без возможности `conversations`. | | `previous_response_id` | Нет | Продолжает с сохранённого ответа. Требует возможности `conversations`. | | `extensions` | Нет | Частные расширения ([раздел 9.1](https://madeforagent.com/ru/lmsp/1.0/common-rules.md#versioning)). | Запрос, которому нужна возможность, отсутствующая у модели, отклоняется с `400` и кодом `unsupported_capability`. Запрос, который не помещается в контекстное окно, отклоняется с `400` и кодом `context_length_exceeded`. Сервис, умеющий хранить диалоги, **МОЖЕТ** также поддерживать `previous_response_id`. В противном случае каждый запрос **ДОЛЖЕН** нести весь контекст. Сервис **НЕ ДОЛЖЕН** полагаться на то, что состояние диалога хранится в HTTP-соединении. ## 6.2 Синхронный ответ {#sync} Если `stream` равен `false`, сервис возвращает `200 OK` и объект ответа после завершения генерации. ```json { "id": "resp_01J8M6Q4W2T7N5R3V9X1Z8B0DF", "object": "response", "created_at": 1794200000, "status": "completed", "model": "qwen3:14b-q4_k_m", "output": [ { "id": "msg_01J8M6Q4W5C8H2K9P3S7Y1A6ZE", "type": "message", "role": "assistant", "content": [ { "type": "output_text", "text": "A local model service is a resident interface layer that discovers, manages and calls models on the user's own device." } ] } ], "usage": { "input_tokens": 28, "output_tokens": 31, "total_tokens": 59 }, "error": null, "incomplete_details": null } ``` | Поле | Значение | | --- | --- | | `id` | Идентификатор ответа, например `resp_...`. | | `object` | Всегда `"response"`. | | `created_at` | Время создания в секундах с начала эпохи Unix. | | `status` | `in_progress`, `completed`, `incomplete`, `failed` или `cancelled`. Синхронный ответ возвращается в конечном статусе. | | `model` | Идентификатор модели, создавшей ответ. | | `output` | Массив элементов вывода. | | `usage` | Количество токенов: `input_tokens`, `output_tokens` и `total_tokens`. | | `error` | `null` или объект проблемы ([раздел 9.3](https://madeforagent.com/ru/lmsp/1.0/common-rules.md#errors)), когда `status` равен `failed`. | | `incomplete_details` | `null` или объект с полем `reason`, например `max_output_tokens`, когда `status` равен `incomplete`. | Типы элементов вывода: | Тип элемента | Структура | | --- | --- | | `message` | `id`, `role` (`assistant`) и `content` — массив частей `output_text`, у каждой из которых есть `text`. | | `function_call` | `id`, `call_id`, `name` и `arguments` — строка в формате JSON. | | `reasoning` | `id` и `summary` — массив частей `summary_text`, у каждой из которых есть `text`. | Если генерация не удалась, синхронный запрос получает статус ошибки и документ проблемы ([раздел 9.3](https://madeforagent.com/ru/lmsp/1.0/common-rules.md#errors)), а не объект ответа. Статус `failed` встречается в потоках, задачах и сохранённых ответах. `GET /lmsp/v1/responses/{response_id}` возвращает ответ, сохранённый с `store: true`. Он необязателен и требует возможности `conversations`. Неизвестный идентификатор даёт `404` с кодом `response_not_found`. ## 6.3 Потоковая передача {#streaming} Чтобы получить поток, задайте `stream` в запросе: ```json { "stream": true } ``` Сервис отвечает `200 OK` с `Content-Type: text/event-stream` и отправляет семантические события. У каждого события есть `id`, имя `event` и JSON-объект `data`: ```text id: 1 event: response.created data: {"response_id":"resp_01J8M6Q4W2T7N5R3V9X1Z8B0DF","status":"in_progress"} id: 2 event: response.output_item.added data: {"response_id":"resp_01J8M6Q4W2T7N5R3V9X1Z8B0DF","output_index":0,"item":{"id":"msg_01J8M6Q4W5C8H2K9P3S7Y1A6ZE","type":"message","role":"assistant","content":[]}} id: 3 event: response.output_text.delta data: {"response_id":"resp_01J8M6Q4W2T7N5R3V9X1Z8B0DF","item_id":"msg_01J8M6Q4W5C8H2K9P3S7Y1A6ZE","delta":"A local model "} id: 4 event: response.output_text.delta data: {"response_id":"resp_01J8M6Q4W2T7N5R3V9X1Z8B0DF","item_id":"msg_01J8M6Q4W5C8H2K9P3S7Y1A6ZE","delta":"service is a resident interface layer."} id: 5 event: response.output_item.done data: {"response_id":"resp_01J8M6Q4W2T7N5R3V9X1Z8B0DF","output_index":0,"item":{"id":"msg_01J8M6Q4W5C8H2K9P3S7Y1A6ZE","type":"message","role":"assistant","content":[{"type":"output_text","text":"A local model service is a resident interface layer."}]}} id: 6 event: response.completed data: {"response_id":"resp_01J8M6Q4W2T7N5R3V9X1Z8B0DF","status":"completed","usage":{"input_tokens":28,"output_tokens":12,"total_tokens":40}} ``` Определены как минимум следующие события: | Событие | Значение | Поля данных | | --- | --- | --- | | `response.created` | Ответ создан. | `response_id`, `status` (`in_progress`) | | `response.output_item.added` | Начался новый элемент вывода. | `response_id`, `output_index`, `item` | | `response.output_text.delta` | К элементу-сообщению добавлен текст. | `response_id`, `item_id`, `delta` | | `response.output_item.done` | Элемент вывода завершён. | `response_id`, `output_index`, `item` | | `response.completed` | Ответ завершён. Последнее событие. | `response_id`, `status`, `usage` | | `response.incomplete` | Генерация остановлена досрочно, например по лимиту токенов. Последнее событие. | `response_id`, `status`, `incomplete_details`, `usage` | | `response.failed` | Генерация завершилась ошибкой. Последнее событие. | `response_id`, `status`, `error` | | `response.cancelled` | Ответ отменён. Последнее событие. | `response_id`, `status` | | `error` | Проблема, не связанная с одним ответом, после которой поток продолжаться не может. | `error` — объект проблемы | Сервис закрывает поток после последнего события или события `error`. Сервисы **МОГУТ** отправлять дополнительные события, например для аргументов вызовов инструментов, изображений, аудио и дельт структурированного JSON. Клиенты **ДОЛЖНЫ** игнорировать неизвестные им типы событий. Если клиент закрывает соединение потокового запроса, сервису **СЛЕДУЕТ** прекратить генерацию. ## 6.4 Идентификаторы событий и переподключение {#reconnection} Идентификатор `id` события **ДОЛЖЕН** быть целым порядковым номером, строго возрастающим в пределах ответа и начинающимся с 1. После обрыва соединения клиент возобновляет получение, отправив последний полученный идентификатор. Он делает это через заголовок: ```http Last-Event-ID: 2 ``` или через параметр запроса: ```http GET /lmsp/v1/tasks/{task_id}/events?after=2 ``` Оба варианта направляются на эндпоинт событий задачи ([раздел 7.4](https://madeforagent.com/ru/lmsp/1.0/tasks.md#events)). Сервис повторно отправляет только те события, чей порядковый номер больше 2. Если указаны оба, приоритет у `after`. Если запрошенные события старше срока хранения, сервис отвечает `410 Gone` с ошибкой `event_history_expired`. Тогда клиент получает итоговый результат запросом `GET /lmsp/v1/tasks/{task_id}`. Ответ, переданный потоком напрямую из `POST /lmsp/v1/responses`, принадлежит этому HTTP-соединению и возобновлён быть не может. Клиенту, которому нужен возобновляемый вывод, **СЛЕДУЕТ** создать [задачу](https://madeforagent.com/ru/lmsp/1.0/tasks.md) и следить за её событиями. ## 6.5 Вызовы инструментов {#tools} LMSP 1.0 определяет функции-инструменты, которые выполняет клиент. Сам сервис инструменты не запускает. Область доступа `tools:execute` зарезервирована для расширений, которые это делают ([раздел 10.3](https://madeforagent.com/ru/lmsp/1.0/security.md#scopes)). Сначала клиент перечисляет инструменты. У каждой записи есть `type` со значением `function`, `name`, `description` и `parameters` — объект JSON Schema. `tool_choice` принимает значения `auto` (по умолчанию), `none`, `required` или `{"type": "function", "name": "get_weather"}`. ```json { "model": "qwen3:14b-q4_k_m", "input": [ { "type": "message", "role": "user", "content": [ { "type": "input_text", "text": "What is the weather in Hangzhou?" } ] } ], "tools": [ { "type": "function", "name": "get_weather", "description": "Get the current weather for a city.", "parameters": { "type": "object", "properties": { "city": { "type": "string" } }, "required": ["city"] } } ], "tool_choice": "auto" } ``` Когда модель решает вызвать инструмент, ответ содержит элемент `function_call`. Его `arguments` — строка в формате JSON. В потоке элемент приходит целиком в `response.output_item.done`. ```json { "id": "resp_01J8M7B3N6Q9T2V5X8Z1C4D7FH", "object": "response", "created_at": 1794200050, "status": "completed", "model": "qwen3:14b-q4_k_m", "output": [ { "id": "fc_01J8M7B3N8R1S4W7Y0A3E6G9JK", "type": "function_call", "call_id": "call_01J8M7B3N9T2V5X8Z1C4D7F0HM", "name": "get_weather", "arguments": "{\"city\":\"Hangzhou\"}" } ], "usage": { "input_tokens": 74, "output_tokens": 18, "total_tokens": 92 }, "error": null, "incomplete_details": null } ``` Клиент запускает инструмент и отправляет новый запрос с полным контекстом: прежние элементы ввода, элемент `function_call` в том виде, в каком он был получен, и элемент `function_call_output` с тем же `call_id`. Его `output` — строка, обычно в формате JSON. ```json { "type": "function_call_output", "call_id": "call_01J8M7B3N9T2V5X8Z1C4D7F0HM", "output": "{\"temperature_c\":17,\"condition\":\"light rain\"}" } ``` --- # Асинхронные задачи > Как LMSP выполняет долгие генерации как сохраняемые задачи, с созданием задач, конечным автоматом состояний, идемпотентной отменой, потоками событий и политиками перезапуска для незавершённых задач. Source: https://madeforagent.com/ru/lmsp/1.0/tasks/ Language: ru Specification: LMSP 1.0 (draft) Updated: 2026-11-08 ## 7.1 Создание задачи {#create} Клиент создаёт задачу, отправляя запрос ответа, который хочет выполнить, обёрнутый в конверт задачи. Рекомендуется заголовок `Idempotency-Key` ([раздел 9.2](https://madeforagent.com/ru/lmsp/1.0/common-rules.md#idempotency)). ```http POST /lmsp/v1/tasks Authorization: Bearer Idempotency-Key: 01J8M7C4Y3N5P8R2T6V9X1Z4BH ``` ```json { "type": "response", "input": { "model": "qwen3:14b-q4_k_m", "input": [ { "type": "message", "role": "user", "content": [ { "type": "input_text", "text": "Write a long report." } ] } ] }, "on_restart": "fail" } ``` | Поле | Обязательное | Значение | | --- | --- | --- | | `type` | Да | `response` — единственный тип задачи в версии 1.0. | | `input` | Да | Запрос ответа, точно такой же, как для `POST /lmsp/v1/responses`. Его поле `stream` игнорируется. | | `on_restart` | Нет | `fail` (по умолчанию), `retry` или `resume`. См. [раздел 7.5](https://madeforagent.com/ru/lmsp/1.0/tasks.md#restart). | Сервис **ДОЛЖЕН** сохранить задачу до того, как ответит. Затем он возвращает: ```http HTTP/1.1 202 Accepted ``` ```json { "id": "task_01J8M8R5X3V7B2N4Q9T1W6Y0ZC", "object": "task", "type": "response", "status": "queued", "created_at": 1794200100, "updated_at": 1794200100, "response_id": "resp_01J8M8R5X4B9D2F6H1K3N7Q0ZW", "events_url": "/lmsp/v1/tasks/task_01J8M8R5X3V7B2N4Q9T1W6Y0ZC/events", "on_restart": "fail", "response": null, "error": null } ``` | Поле задачи | Значение | | --- | --- | | `id` | Идентификатор задачи, например `task_...`. | | `object` | Всегда `"task"`. | | `type` | `response`. | | `status` | Текущее состояние ([раздел 7.2](https://madeforagent.com/ru/lmsp/1.0/tasks.md#states)). | | `created_at`, `updated_at` | Время в секундах с начала эпохи Unix. | | `response_id` | Идентификатор ответа, который создаёт задача. Присваивается при создании задачи. | | `events_url` | Путь к потоку событий. Присутствует, когда возможность `event_resume` равна `true`. | | `on_restart` | Политика перезапуска задачи. | | `response` | Объект ответа, когда `status` равен `succeeded`, иначе `null`. | | `error` | Объект проблемы ([раздел 9.3](https://madeforagent.com/ru/lmsp/1.0/common-rules.md#errors)), когда `status` равен `failed`, иначе `null`. | `GET /lmsp/v1/tasks/{task_id}` возвращает объект задачи. Клиенты, которые не используют потоки событий, опрашивают этот эндпоинт. Неизвестный идентификатор даёт `404` с кодом `task_not_found`. Завершённая задача выглядит так: ```json { "id": "task_01J8M8R5X3V7B2N4Q9T1W6Y0ZC", "object": "task", "type": "response", "status": "succeeded", "created_at": 1794200100, "updated_at": 1794200162, "response_id": "resp_01J8M8R5X4B9D2F6H1K3N7Q0ZW", "events_url": "/lmsp/v1/tasks/task_01J8M8R5X3V7B2N4Q9T1W6Y0ZC/events", "on_restart": "fail", "response": { "id": "resp_01J8M8R5X4B9D2F6H1K3N7Q0ZW", "object": "response", "created_at": 1794200100, "status": "completed", "model": "qwen3:14b-q4_k_m", "output": [ { "id": "msg_01J8M8R6A2C5E8G1J4M7P0S3VY", "type": "message", "role": "assistant", "content": [ { "type": "output_text", "text": "Local model services make on-device inference a shared, discoverable utility..." } ] } ], "usage": { "input_tokens": 12, "output_tokens": 1480, "total_tokens": 1492 }, "error": null, "incomplete_details": null }, "error": null } ``` ## 7.2 Конечный автомат {#states} ```text queued --> loading --> running --> succeeded | | | | | +--> failed | | +--> cancelling --> cancelled | +--------------> failed +--------------------------> cancelled ``` | Состояние | Значение | Следующие состояния | | --- | --- | --- | | `queued` | Принята и сохранена, ожидает запуска. | `loading`, `cancelled`, `failed` | | `loading` | Модель загружается. | `running`, `cancelling`, `failed` | | `running` | Модель генерирует. | `succeeded`, `failed`, `cancelling` | | `cancelling` | Отмена запрошена, но бэкенд ещё не остановился. | `cancelled` | | `succeeded` | Конечное. Ответ готов. | нет | | `failed` | Конечное. | нет | | `cancelled` | Конечное. | нет | Конечные состояния — `succeeded`, `failed` и `cancelled`. Сервис **НЕ ДОЛЖЕН** переводить задачу из конечного состояния в другое. Задача, ответ которой заканчивается статусом `incomplete`, например по лимиту токенов, считается `succeeded`. Каждый переход атомарен, поэтому задача никогда не бывает одновременно отменённой и завершённой. Побеждает тот переход, который зафиксирован первым. ## 7.3 Отмена {#cancel} ```http POST /lmsp/v1/tasks/{task_id}/cancel ``` Сервис возвращает `200 OK` и текущий объект задачи. Задача `queued` сразу становится `cancelled`. Задача `loading` или `running` становится `cancelling`, пока бэкенд не остановится, а затем `cancelled`. Сервис **ДОЛЖЕН** обеспечивать идемпотентность отмены. Отмена задачи, которая уже `cancelling` или `cancelled`, возвращает текущую задачу и не порождает новой ошибки. Отмена задачи, которая уже `succeeded` или `failed`, получает ответ `409` с кодом `task_not_cancelable`. Неизвестный идентификатор даёт `404` с кодом `task_not_found`. ## 7.4 Потоки событий {#events} ```http GET /lmsp/v1/tasks/{task_id}/events Accept: text/event-stream ``` Этот эндпоинт необязателен. Он доступен, когда возможность `event_resume` равна `true`. - События — это события ответа из [раздела 6.3](https://madeforagent.com/ru/lmsp/1.0/responses.md#streaming) для ответа, который создаёт задача. Нумерация и возобновление следуют [разделу 6.4](https://madeforagent.com/ru/lmsp/1.0/responses.md#reconnection). - Поток начинается с `response.created`, которое записывается при приёме задачи. Он заканчивается одним последним событием, соответствующим исходу: `response.completed`, `response.incomplete`, `response.failed` или `response.cancelled`. - Клиент, подключившийся поздно, сначала получает пропущенные события, затем живые. После последнего события сервис закрывает поток. - События хранятся в течение срока хранения ([раздел 11.2](https://madeforagent.com/ru/lmsp/1.0/persistence.md#rules)). Более старые события дают `410 Gone` с `event_history_expired`. ## 7.5 Поведение при перезапуске {#restart} `on_restart` определяет, что происходит с незавершённой задачей при перезапуске сервиса: | Значение | Поведение | | --- | --- | | `fail` | По умолчанию. Сервис помечает незавершённую задачу как `failed` с кодом ошибки `server_restarted`. | | `retry` | Сервис запускает задачу заново с начала. | | `resume` | Сервис продолжает со снимка состояния. Это допускается, только если бэкенд явно поддерживает возобновляемые снимки. В противном случае запрос на создание отклоняется с `400` и кодом `unsupported_capability`. | Задача, которая находится в состоянии `cancelling` при перезапуске сервиса, завершает отмену и становится `cancelled` независимо от своей политики. Значение `retry` не рекомендуется использовать по умолчанию, потому что задача могла уже запустить инструменты или вызвать внешние побочные эффекты. Клиент, выбравший `retry`, **ДОЛЖЕН** сочетать его с `Idempotency-Key` и сделать вызовы своих инструментов идемпотентными. --- # Совместимость с OpenAI и Ollama > Как сервис LMSP остаётся совместимым с существующими клиентами OpenAI и Ollama благодаря тонкому слою преобразования данных над единым ядром выполнения и где эта совместимость заканчивается. Source: https://madeforagent.com/ru/lmsp/1.0/compatibility/ Language: ru Specification: LMSP 1.0 (draft) Updated: 2026-11-08 ## 8.1 Эндпоинты, совместимые с OpenAI {#openai} Сервису **СЛЕДУЕТ** предлагать интерфейс, совместимый с OpenAI, рядом с интерфейсом LMSP и объявлять его в `interfaces` с `kind`, равным `openai`. Чтобы охватить существующие клиенты, сервис, который это делает, **ДОЛЖЕН** реализовать как минимум следующие эндпоинты с префиксом `/v1`: | Эндпоинт | Примечания | | --- | --- | | `GET /v1/models` | Перечисляет модели каталога LMSP. Каждый `id` — идентификатор модели LMSP. | | `POST /v1/chat/completions` | Chat Completions, с потоковой передачей и без неё. | | `POST /v1/responses` | Форма OpenAI Responses. | | `POST /v1/embeddings` | В основном интерфейсе LMSP 1.0 нет эндпоинта эмбеддингов. Адаптер обслуживает этот эндпоинт напрямую для моделей, у которых `capabilities.embeddings` равно `true`. | Эти эндпоинты принимают те же Bearer-токены, что и интерфейс LMSP. Клиент, говорящий на протоколе OpenAI, указывает токен LMSP как свой API-ключ. Первая реализация может начать с `/v1/models` и `/v1/chat/completions` ([страница 12](https://madeforagent.com/ru/lmsp/1.0/mvp.md)). ## 8.2 Эндпоинты, совместимые с Ollama {#ollama} Сервису **СЛЕДУЕТ** также предоставлять эти эндпоинты, чтобы работали и клиенты, написанные для Ollama: | Эндпоинт | Примечания | | --- | --- | | `GET /api/tags` | Перечисляет модели каталога LMSP. | | `POST /api/chat` | Чат. | | `POST /api/generate` | Простая генерация текста. | Сервис, который их предоставляет, объявляет интерфейс с `kind`, равным `ollama`. ## 8.3 Единое ядро выполнения {#core} Слой совместимости **ДОЛЖЕН** только преобразовывать данные. Он **НЕ ДОЛЖЕН** создавать второй набор логики управления моделями. Запрос на интерфейсе совместимости отображается на ответ LMSP, выполняется на том же ядре и адаптере, что и нативный запрос, и использует тот же каталог моделей и ту же аутентификацию. ```text OpenAI Chat Completions | отображение запроса v LMSP Response | адаптация к бэкенду v llama.cpp / Ollama / vLLM / другие среды выполнения ``` На эндпоинтах `/v1` и `/api` сервису **СЛЕДУЕТ** сообщать об ошибках в формате, который определяет соответствующий API, чтобы немодифицированные клиенты могли их разобрать. Интерфейс LMSP всегда использует `application/problem+json`. ## 8.4 Границы {#boundaries} У совместимости есть пределы, и о них стоит сказать прямо: - Существующему клиенту, совместимому с OpenAI, обычно нужно лишь изменить `base_url`. Обнаружение LMSP он автоматически не получает. - Только клиенты с поддержкой LMSP могут автоматически обнаруживать сервис, читать его подробные возможности, возобновлять потоки событий и управлять асинхронными задачами. - Никакой серверный протокол не заставит работать клиент, который нельзя настроить и который принимает только официальные облачные адреса. --- # Общие правила > Правила, общие для всех эндпоинтов LMSP, включая заголовки версий и расширения, ключи идемпотентности, формат ошибок problem+json с кодами ошибок и проверку работоспособности. Source: https://madeforagent.com/ru/lmsp/1.0/common-rules/ Language: ru Specification: LMSP 1.0 (draft) Updated: 2026-11-08 ## 9.1 Версии и расширения {#versioning} Каждый запрос LMSP **МОЖЕТ** нести эти заголовки: ```http LMSP-Version: 1.0 LMSP-Client: example-client/1.2.0 ``` `LMSP-Version` — версия протокола, на которую рассчитан клиент. `LMSP-Client` обозначает клиентскую программу для диагностики в виде `name/version`. Сервис **НЕ ДОЛЖЕН** менять поведение протокола в зависимости от этого заголовка. Правила: - `v1` в пути обозначает основную версию. - Запрос, в котором указана неизвестная основная версия, получает ответ `426 Upgrade Required` с кодом ошибки `unsupported_protocol_version`. - Новые необязательные поля **НЕ ДОЛЖНЫ** нарушать совместимость 1.x. - Сервисы и клиенты **ДОЛЖНЫ** игнорировать неизвестные им поля. - Частные расширения помещаются в объект `extensions` и именуются в обратной доменной записи: ```json { "extensions": { "com.example.cache": { "enabled": true } } } ``` ## 9.2 Идемпотентность {#idempotency} Запросы, которые что-то создают, например создание ответа, создание задачи или загрузка модели, принимают ключ идемпотентности: ```http Idempotency-Key: ``` Сервис сохраняет ключ вместе с дайджестом тела запроса и результатом: - Тот же ключ с тем же телом возвращает исходный результат. - Тот же ключ с другим телом получает ответ `409 Conflict` с кодом `idempotency_conflict`. Ключ действует в пределах клиента и эндпоинта. Ключ **СЛЕДУЕТ** делать уникальным значением длиной не более 255 символов, например ULID или UUID. Сервису **СЛЕДУЕТ** хранить ключи не менее 24 часов. Сервис **МОЖЕТ** игнорировать ключ в потоковых запросах. Клиент, которому нужны безопасные повторы, использует [задачу](https://madeforagent.com/ru/lmsp/1.0/tasks.md). ## 9.3 Ошибки {#errors} Каждая ошибка интерфейса LMSP использует `application/problem+json` ([RFC 9457](https://www.rfc-editor.org/rfc/rfc9457)): ```json { "type": "urn:lmsp:error:model-not-found", "title": "Model not found", "status": 404, "code": "model_not_found", "detail": "No local model with id 'qwen3:14b-q4_k_m'", "request_id": "req_01J8M9S6Y4W8C3P5R1V7Z2A0DH", "retryable": false } ``` | Поле | Обязательное | Значение | | --- | --- | --- | | `type` | Да | URI, обозначающий вид ошибки. LMSP использует `urn:lmsp:error:`, за которым следует код с дефисами вместо подчёркиваний. | | `title` | Да | Краткое описание, понятное человеку. | | `status` | Да | Код состояния HTTP. | | `code` | Да | Машиночитаемый код ошибки из списка ниже. | | `detail` | Нет | Понятное человеку пояснение к данному случаю. Сервис **НЕ ДОЛЖЕН** помещать в него секреты, промпты или сгенерированное содержимое. | | `request_id` | Нет | Идентификатор запроса для сопоставления с журналами сервиса. | | `retryable` | Да | Может ли повторный запрос позже завершиться успехом. | Типичные коды ошибок: | Код | HTTP-статус | Можно повторить | Значение | | --- | --- | --- | --- | | `invalid_request` | 400 | Нет | Запрос искажён или содержит недопустимые параметры. | | `unauthorized` | 401 | Нет | Токен отсутствует, недействителен, просрочен или отозван, либо неверен код сопряжения. | | `forbidden` | 403 | Нет | У токена нет нужной области доступа, либо не пройдена проверка `Origin` или `Host`. | | `model_not_found` | 404 | Нет | Ни у одной локальной модели нет такого идентификатора. | | `model_not_ready` | 409 | Да | Модель не загружена и не загружается по требованию. | | `model_load_failed` | 500 | Да | Не удалось загрузить модель. | | `context_length_exceeded` | 400 | Нет | Ввод не помещается в контекстное окно модели. | | `unsupported_capability` | 400 или 501 | Нет | Модель или сервис не поддерживают то, что нужно запросу. Нереализованный необязательный эндпоинт отвечает 501. | | `unsupported_protocol_version` | 426 | Нет | Сервис не поддерживает запрошенную основную версию. | | `response_not_found` | 404 | Нет | Ни у одного сохранённого ответа нет такого идентификатора. | | `task_not_found` | 404 | Нет | Ни у одной задачи нет такого идентификатора. | | `task_not_cancelable` | 409 | Нет | Задача уже завершилась успешно или с ошибкой. | | `idempotency_conflict` | 409 | Нет | Ключ идемпотентности уже использован с другим телом запроса. | | `event_history_expired` | 410 | Нет | Запрошенные события старше срока хранения. | | `rate_limited` | 429 | Да | Слишком много запросов. | | `resource_exhausted` | 503 | Да | На устройстве закончилась память или другой ресурс. | | `backend_unavailable` | 503 | Да | Среда выполнения модели недоступна. | | `server_restarted` | 503 | Да | Сервис перезапустился, пока задача была не завершена. | | `internal_error` | 500 | По-разному | Неожиданный сбой внутри сервиса. | Поле `retryable` является определяющим. В столбце выше показано обычное значение. Сервис **МОЖЕТ** определять дополнительные коды. Клиент **ДОЛЖЕН** обрабатывать неизвестный ему код по его HTTP-статусу и полю `retryable`. ## 9.4 Проверка работоспособности {#health} ```http GET /lmsp/v1/health ``` ```json { "status": "ready", "service_id": "svc_01J8M4Y8K6M5E6K6Z7Z0Y8W3M2", "uptime_seconds": 86420, "loaded_models": 1, "active_tasks": 2, "storage": "ok" } ``` | Поле | Значение | | --- | --- | | `status` | `starting`, `ready`, `degraded` или `stopping`. | | `service_id` | Идентификатор сервиса из описания сервиса. | | `uptime_seconds` | Секунды с момента запуска сервиса. | | `loaded_models` | Число моделей в состоянии `ready`. | | `active_tasks` | Число задач в состоянии `queued`, `loading`, `running` или `cancelling`. | | `storage` | `ok` или `error`. | Статус HTTP — `200 OK`, когда `status` равен `ready` или `degraded`, и `503 Service Unavailable` в остальных случаях. Эндпоинт проверки возвращает только рабочее состояние. Он **НЕ ДОЛЖЕН** возвращать пути к моделям, промпты, токены доступа и другие чувствительные сведения. --- # Локальная безопасность > Требования безопасности к сервисам LMSP, включая привязку к loopback, сопряжение и Bearer-токены, области доступа, проверки Origin и Host против вредоносных веб-страниц, режим LAN и правила журналирования. Source: https://madeforagent.com/ru/lmsp/1.0/security/ Language: ru Specification: LMSP 1.0 (draft) Updated: 2026-11-08 ## 10.1 Базовые меры {#baseline} Прослушивание только localhost — не абсолютная защита: вредоносная веб-страница всё равно может пытаться обратиться к локальным портам. Поэтому LMSP сочетает несколько мер. - Сервис **ДОЛЖЕН** по умолчанию привязываться к loopback-адресам. Он **НЕ ДОЛЖЕН** слушать `0.0.0.0` или любой другой адрес, не являющийся loopback, если не включён режим LAN ([раздел 10.5](https://madeforagent.com/ru/lmsp/1.0/security.md#lan)). - При первом запуске сервису **СЛЕДУЕТ** сгенерировать случайный секрет сервиса с высокой энтропией и использовать его, а не какое-либо встроенное значение по умолчанию, для выпуска и проверки токенов. - Каждый запрос к основному интерфейсу **ДОЛЖЕН** нести действительный Bearer-токен, кроме начальных эндпоинтов, перечисленных в [разделе 4.2](https://madeforagent.com/ru/lmsp/1.0/endpoints.md#conventions). Одного лишь происхождения с loopback никогда не достаточно. ## 10.2 Сопряжение и токены {#pairing} Клиент получает собственный Bearer-токен с помощью локального кода сопряжения. Пользователь просит сервис показать код сопряжения тем способом, который предлагает сервис, и клиент отправляет его: ```http POST /lmsp/v1/pair Content-Type: application/json ``` ```json { "client_name": "example-client", "pairing_code": "K7QD-4M2X", "scopes": ["inference"] } ``` | Поле запроса | Значение | | --- | --- | | `client_name` | Отображаемое имя клиента, которое видит пользователь. | | `pairing_code` | Код, который пользователь получил от сервиса. | | `scopes` | Необязательный список запрашиваемых областей доступа. По умолчанию `["inference"]`. | Сервис отвечает токеном. Значение `token_type` всегда `Bearer`: ```json { "client_id": "cli_01J8MAT3W6X9B2D5F8H1K4N7QZ", "access_token": "lmsp_example_3f9a1c52e7d84b60", "token_type": "Bearer", "scopes": ["inference"] } ``` Затем клиент отправляет `Authorization: Bearer ` с каждым запросом. - Код сопряжения **ДОЛЖЕН** быть одноразовым, а срок его действия **СЛЕДУЕТ** ограничивать 5 минутами. Код **СЛЕДУЕТ** формировать так, чтобы он содержал не менее 40 бит энтропии. Сервис **ДОЛЖЕН** ограничивать частоту неудачных попыток. На неверный или просроченный код сервис отвечает `401` и `unauthorized`. - Токены независимы для каждого клиента, поэтому пользователь может отозвать один клиент, не затрагивая остальные. Сервису **СЛЕДУЕТ** позволять пользователю просматривать список сопряжённых клиентов и отзывать их. - Клиенту **СЛЕДУЕТ** хранить токен в связке ключей или менеджере учётных данных операционной системы. Сервису **СЛЕДУЕТ** хранить только хеш каждого токена. - На отсутствующий, недействительный или отозванный токен сервис отвечает `401`, `unauthorized` и заголовком `WWW-Authenticate: Bearer`. На действительный токен, которому не хватает области доступа, нужной эндпоинту, сервис отвечает `403` и `forbidden`. ## 10.3 Области доступа {#scopes} Разрешения разделены на отдельные области доступа. Скачивание моделей, удаление моделей и выполнение инструментов никогда не входят в область доступа для инференса. | Область доступа | Разрешает | | --- | --- | | `inference` | Чтение каталога моделей, создание ответов и задач, а также чтение и отмену собственных задач клиента. | | `models:manage` | Загрузку и выгрузку моделей. | | `models:install` | Зарезервировано. Скачивание и удаление моделей. | | `tools:execute` | Зарезервировано. Выполнение инструментов на стороне сервиса. | Задачи и сохранённые ответы принадлежат клиенту, который их создал. Сервису **СЛЕДУЕТ** отвечать на запрос другого клиента так, будто их не существует (`task_not_found` или `response_not_found`). ## 10.4 Защита браузера и сети {#browser} - Сервис **ДОЛЖЕН** по умолчанию отклонять кросс-доменные запросы браузера и разрешать только явно настроенные значения `Origin`. На запрос с любым другим `Origin` сервис отвечает `403` и `forbidden`. - Если источник разрешён, сервис отвечает на его предварительные запросы CORS и отправляет `Access-Control-Allow-Origin` ровно с этим источником, но никогда не с `*`. - Сервис **ДОЛЖЕН** проверять заголовок `Host`, чтобы предотвратить DNS rebinding. Он принимает только те имена хостов, которые обслуживает: имена и адреса loopback, а также любые имена, настроенные для режима LAN. На любое другое значение сервис отвечает `403` и `forbidden`. ## 10.5 Режим LAN {#lan} Режим LAN означает прослушивание не только loopback-адресов. - Сервис **НЕ ДОЛЖЕН** включать режим LAN, если пользователь явно его не включил. - В режиме LAN сервис **ДОЛЖЕН** требовать аутентификацию, а также **СЛЕДУЕТ** включать TLS. Клиенту **НЕ СЛЕДУЕТ** отправлять токен по обычному HTTP на адрес, не являющийся loopback, если пользователь явно этого не разрешил. - Объявление mDNS разрешено только в режиме LAN, когда пользователь дополнительно включил обнаружение в локальной сети ([раздел 3.2](https://madeforagent.com/ru/lmsp/1.0/discovery.md#mdns)). ## 10.6 Журналирование и конфиденциальность {#logging} - Пути к моделям, журналы, промпты и сгенерированное содержимое **НЕ ДОЛЖНЫ** попадать в объявления mDNS. - По умолчанию сервису **СЛЕДУЕТ** журналировать только метаданные (время, эндпоинты, коды состояния, идентификаторы, количество токенов и длительности). Полные промпты и сгенерированное содержимое **НЕ СЛЕДУЕТ** журналировать. - Ответы проверки работоспособности и подробности ошибок **НЕ ДОЛЖНЫ** содержать пути к моделям, промпты, токены доступа и другие чувствительные сведения. --- # Постоянное хранение > Как сервис LMSP хранит своё состояние, со встроенной базой SQLite, перечнем таблиц, транзакционными правилами для задач и событий, сроком хранения и восстановлением при запуске. Source: https://madeforagent.com/ru/lmsp/1.0/persistence/ Language: ru Specification: LMSP 1.0 (draft) Updated: 2026-11-08 ## 11.1 Хранилище {#storage} Сервису **СЛЕДУЕТ** хранить своё состояние во встроенной базе данных [SQLite](https://www.sqlite.org/). Предлагаемый набор таблиц: | Таблица | Содержимое | | --- | --- | | `service_config` | Идентичность и конфигурация сервиса. | | `clients` | Сопряжённые клиенты, хеши токенов и области доступа. | | `models` | Метаданные моделей и дайджесты содержимого. | | `responses` | Сохранённые ответы. | | `tasks` | Асинхронные задачи. | | `task_events` | Журнал событий каждой задачи с порядковыми номерами. | | `idempotency_keys` | Ключи идемпотентности, дайджесты запросов и результаты. | Файлы моделей **ДОЛЖНЫ** оставаться вне базы данных. База хранит только метаданные и дайджесты содержимого. Имена таблиц и их структура — решение реализации. То, что клиенты могут наблюдать и что должно быть совместимо, — это поведение, описанное в двух следующих разделах. ## 11.2 Правила {#rules} Какое бы хранилище ни использовал сервис, действуют такие правила: - Сервис **ДОЛЖЕН** сохранять задачу до того, как вернёт `202 Accepted`. - Сервис **ДОЛЖЕН** обновлять порядковые номера событий и статус задачи в одной транзакции. - Сервис **ДОЛЖЕН** делать переход отмены атомарным, чтобы избежать гонки, при которой задача сначала отменяется, а потом завершается. - Сервису **СЛЕДУЕТ** хранить завершённые задачи и события в течение настраиваемого срока хранения. - Сервис **НЕ ДОЛЖЕН** удалять незавершённые задачи при очистке журналов. - Сервис **ДОЛЖЕН** при запуске просматривать оставшиеся задачи `queued`, `loading`, `running` и `cancelling` и применять к каждой её политику перезапуска ([раздел 7.5](https://madeforagent.com/ru/lmsp/1.0/tasks.md#restart)). ## 11.3 Восстановление при запуске {#recovery} В таблице показано состояние, в которое переводится оставшаяся задача в соответствии с её политикой `on_restart`: | Состояние при запуске | `fail` | `retry` | `resume` | | --- | --- | --- | --- | | `queued` | `failed` | `queued` | `queued` | | `loading` | `failed` | `queued` | `queued` | | `running` | `failed` | `queued` | `running`, продолжение со снимка состояния | | `cancelling` | `cancelled` | `cancelled` | `cancelled` | Задача, переведённая в `failed`, получает код ошибки `server_restarted` и событие `response.failed`. Задача, переведённая в `cancelled`, получает событие `response.cancelled`. Новое состояние и его событие записываются в одной транзакции ([раздел 11.2](https://madeforagent.com/ru/lmsp/1.0/persistence.md#rules)). --- # Минимально жизнеспособный объём > Предлагаемый объём первой реализации LMSP с десятью элементами, образующими работоспособный замкнутый цикл, и частями спецификации, которые могут подождать до следующих выпусков. Source: https://madeforagent.com/ru/lmsp/1.0/mvp/ Language: ru Specification: LMSP 1.0 (draft) Updated: 2026-11-08 ## 12.1 Первая версия {#scope} Первой версии не обязательно реализовывать все мультимодальные возможности, загрузку моделей или голос в реальном времени. Она образует работоспособный замкнутый цикл, как только предоставляет: 1. Обнаружение сервиса через `/.well-known/lmsp.json`. 2. Необязательное объявление `_lmsp._tcp.local.` в локальной сети. 3. `GET /lmsp/v1/models`. 4. `POST /lmsp/v1/responses`, как синхронно, так и в виде потока SSE. 5. Асинхронные задачи: создание, опрос и отмена, с хранением в SQLite. 6. Возобновление оборванных потоков с помощью `Last-Event-ID` или `after`. 7. Bearer-токены, локальное сопряжение, а также защиту `Origin` и `Host`. 8. Эндпоинты `/v1/models` и `/v1/chat/completions`, совместимые с OpenAI. 9. Файл контракта OpenAPI 3.2 и межъязыковые тесты соответствия. 10. Как минимум один настоящий бэкенд и проверку того, что задачи корректно восстанавливаются после перезапуска сервиса. Суть замысла: LMSP даёт полный набор возможностей, интерфейс OpenAI даёт готовую совместимость, mDNS и описание сервиса дают автоматическое обнаружение, а модель задач на SQLite даёт локальное хранение. ## 12.2 Что может подождать {#later} Всё, что не упомянуто в списке выше, может появиться в следующих выпусках. Сюда относятся необязательные эндпоинты (сохранённые ответы, а также загрузка и выгрузка моделей), эндпоинты OpenAI `/v1/responses` и `/v1/embeddings`, API, совместимый с Ollama, и все типы содержимого, кроме текста. Этот объём — путь к работоспособному первому выпуску, а не профиль соответствия. Сервис, заявляющий о поддержке LMSP 1.0, по-прежнему реализует все обязательные эндпоинты со [страницы 4](https://madeforagent.com/ru/lmsp/1.0/endpoints.md) и обязательное поведение, описанное на остальных страницах.