Режим фокусировки

Создание адаптера для ИИ-ассистента

В статье используются следующие термины:

В статье описаны принципы работы, пошаговая инструкция и API модуля для создания собственного адаптера ИИ-ассистента. Как настроить работу с ИИ-ассистентом в интерфейсе платформы см. в статье Работа с ИИ-ассистентом.  

Что такое адаптер

Адаптер — модуль, который связывает чат с внешним LLM-провайдером (OpenAI-совместимый API, Yandex GPT, собственный сервис и т.п.). Сервер чата не обращается к провайдеру напрямую: он передаёт запрос зарегистрированному адаптеру, а адаптер возвращает ответ в едином внутреннем формате.

Возможности механизма адаптеров:

  • подключение произвольного LLM без изменения кода сервера;
  • использование нескольких адаптеров в одном экземпляре чата. Например, для разных провайдеров можно использовать различные адаптеры;
  • поддержка потоковой (SSE) и стандартной генерации ответа;
  • управление моделями: адаптер может опционально передавать список поддерживаемых моделей для выбора в настройках BILLmanager;
  • реализация собственной логики работы с LLM. Например, вызов функций (function calling), фильтрация запросов и т.д;
  • передача API-ключа, URL-адреса и системного промпта из настроек проекта.

Собственные адаптеры загружаются автоматически при старте сервера чата из каталога /app/server/adapters / внутри Docker-контейнера. Пересборка образа не требуется — достаточно разместить файл адаптера в контейнере и перезапустить контейнер.

Устройство работы чата
Устройство работы чата

Встроенные адаптеры

После установки модуля "ИИ-ассистент" в платформе доступны следующие адаптеры:

ИмяОписание
openaiАдаптер для OpenAI-совместимых LLM. Например, YandexGPT, GigaChat.
deep-seekАдаптер для общения с DeepSeek.

Создание адаптера

Ниже приведена инструкция по созданию адаптера для общения с LLM через пакет OpenAI. В примере использовано название адаптера my-llm. Вместо my-llm вы можете указать другое имя.
Чтобы создать адаптер для общения с LLM через пакет OpenAI:

  1. Создайте файл адаптера [имя-адаптера].adapter.js. Например, my-llm.adapter.js.
  2. Создайте класс адаптера и реализуйте необходимые методы:
    • getName — получение имени адаптера;
    • getCompletion — получение ответа от ИИ без SSE;
    • getCompletionStream — получение ответа от ИИ с SSE.
    Подробное описание API для входных и выходных параметров см. ниже;
    Пример создания адаптера
  3. Скопируйте файл адаптера в каталог /app/server/adapters работающего контейнера чата. Чтобы сохранить добавленные адаптеры после пересоздания образа, создайте свой образ на базе Docker-образа чата.

    Пример создания образа с помощью Dockerfile
    FROM docker-registry.ispsystem.com/ispsystem5/chat:1.0.0
    
    WORKDIR /app
    COPY ./my-llm.adapter.js ./server/adapters/my-llm.adapter.js
    Где:
    • docker-registry.ispsystem.com/ispsystem5/chat:1.0.0 — адрес к публичному образу контейнера чата. 
  4. Создайте образ из Dockerfile:
     docker build . -t my-llm 
  5. Укажите тег созданного образа в скрипте для управления образами чата /usr/local/mgr5/etc/docker/services/aichat.sh. Замените переменную SERVICE_IMAGE на тег нового образа SERVICE_IMAGE="my-llm"
  6. Перезапустите контейнер чата:
    sh etc/docker/services/aichat.sh restart 
  7. В веб-интерфейсе BILLmanager перейдите в раздел ИИ-ассистентНастройки → выберите ИИ-ассистента → кнопка Изменить.
  8. В блоке Основное в поле Адаптер выберите новый адаптер.
  9. Задайте необходимые настройки: API URL, API-ключ. Подробнее см. статью Работа с ИИ-ассистентом.
  10. Сохраните изменения.

После настройки чат готов к использованию. Запросы будут отправляться через созданный адаптер.

Написание адаптера с внешними зависимостями

Сервис чата импортирует файлы, оканчивающиеся на  .adapter.js . Если в этом файле нужно использовать зависимости (например, директорию node_modules) или пакеты, их нужно также перенести внутрь контейнера в директорию /app/server/adapters/.
При перемещении каталога node_modules внутрь контейнера зависимости становятся доступны через импорт require.

Пример импорта
// Библиотека OpenAI доступна для импорта по умолчанию.
const OpenAI = require('openai');
// Пример импорта зависимости при переносе node_modules с необходимыми зависимостями в директорию /app/server/adapters/
const customPackage = require('custom-package');

class MyLLMAdapter {
  ...
}

module.exports = { MyLLMAdapter };

API адаптера

Описание функций

getName()

Возвращает уникальное строковое имя адаптера.

ПринимаетНет
Возвращаетstring — уникальный идентификатор адаптера. Сервис чата использует по умолчанию адаптеры deep-seek и openai.
ОбязательныйДа

getCompletion(params)

Синхронная генерация ответа без потока.

ПринимаетAdapterCompletionParams
Возвращает

Promise<ModelResponse>

ОбязательныйДа

getCompletionStream(params)

Потоковая генерация ответа для режима SSE.

ПринимаетAdapterCompletionParams
Возвращает

AsyncIterable<ModelStreamEvent> (функция должна быть асинхронным генератором )

ОбязательныйДа

getModels()

Возвращает список моделей, доступных для выбора в настройках.

ПринимаетНет
Возвращает

string[]

ОбязательныйНет

Описание интерфейсов

ModelStreamEvent

Событие потока (для SSE) — один из следующих типов:

text_delta — промежуточный фрагмент текста.

ПолеТипОбязательноеОписание
typetext_deltaДа

Тип события.

deltastringДаДобавленный фрагмент текста.

completed — завершение генерации (обязательное финальное событие).

ПолеТипОбязательноеОписание
typecompletedДа

Тип события.

responseModelResponseДаИтоговый ответ.

tool_call — уведомление о вызове инструмента.

ПолеТипОбязательноеОписание
typetool_callДа

Тип события.

toolstringДаИдентификатор инструмента для отображения в интерфейсе.

AdapterCompletionParams

ПолеТипОбязательноеОписание
apiKeystringДа

API-ключ провайдера из настроек.

inputModelMessageДаИстория сообщений для LLM.
abortSignalAbortSignalДаСигнал для отмены запросов при разрыве соединения с клиентом.
apiUrlstringНетБазовый URL-адрес API из настроек.
modelstringНетИмя модели из настроек чата.
systemPromptstringНетСистемный промпт из настроек.

ModelMessage

ПолеТипОбязательноеОписание
roleuser | assistant | systemДа

Роль автора сообщения.

contentstringДаТекст сообщения.

ModelResponse

ПолеТипОбязательноеОписание
outputTextstringДаПолный текст ответа ассистента.
output

ModelResponseOutputMessage[]

Да

Структурированный вывод.

relatedContent

ModelResponseRelatedContent

Нет

Дополнительный контент, относящийся к ответу.

ModelResponseOutputMessage
ПолеТипОбязательноеОписание
roleassistant | userДаРоль автора сообщения.
content

{type: 'text', text: string}[]

Да

Фрагменты текста.

status

string

Нет

Статус генерации (например, completed ).

ModelResponseRelatedContent
ПолеТипОбязательноеОписание
links{ title: string, url: string }[]НетИсточники, которые LLM использовала при генерации ответа. Выводите их в виде ссылок под текстом сообщения.

Пример адаптера с вызовом функций (function calling)


Пример адаптера