Перейти к основному содержимому

Мост Cordis

Мост Cordis встраивает DeepSeek Harness (DSH) в бэкенд Libre WebUI. DSH работает как дерево плагинов в среде Cordis, размещённой Libre WebUI, поэтому возможности предоставляются сервисами Cordis, а не импортируемыми модулями.

Мост по умолчанию выключен. Описанное здесь действует только после включения оператором; см. Конфигурацию Cordis.

Почему мост, а не прямая интеграция

Импорт пакетов DSH из сервисов Libre WebUI был бы короче, но хуже: движок стал бы зависимостью времени компиляции. Смена адаптера модели, цикла агента или удаление движка требовали бы изменения и повторного развёртывания Libre WebUI.

Мост обращает зависимость. Libre WebUI использует один абстрактный контракт, а документ композиции Cordis выбирает его реализацию:

  • Смена провайдера без пересборки. Композиция — YAML-файл, поэтому другая модель провайдера выбирается настройкой.
  • Настройка возможностей. Каждая возможность — строка Loader. Изменения композиции оператора вступают в силу при следующем запуске хоста.
  • Удаление без следов. Все сервисы, обработчики и эффекты движка принадлежат корневому fiber. Его освобождение откатывает их, позволяя остановить движок без перезапуска Libre WebUI.

Слои

Конкретные зависимости DSH остаются внутри backend/src/cordis/dsh/. Маршруты и сервисы приложения используют контракты моста. У драйвера Work отдельная композиция в памяти, которая никогда не загружает плагины файловой системы хоста.

Контракты

Контракт находится в backend/src/cordis/contracts.ts и намеренно ограничен структурами, нужными API Libre WebUI, без внутренней терминологии движка.

КонтрактНазначение
DshEngine.status()Состояние жизненного цикла сервисов (pending / ready / failed)
DshEngine.modelConfiguration()Модель и провайдер по умолчанию работающей композиции
DshEngine.listSessions()Сводки сеансов от новых к старым
DshEngine.getSession(id)Сеанс с проекцией сообщений
DshEngine.createSession(opts)Резервирование ID сеанса и рабочего каталога
DshEngine.updateSessionSettings(id, settings)Сохранение реальной модели и нативного режима файловых разрешений в простое
DshEngine.decideApproval(id, approvalId, decision)Решение ожидающего согласования инструмента для его сеанса
DshEngine.deleteSession(id)Завершение сеанса и освобождение агента
DshEngine.listAgents()Активные агенты с пометкой корневого или дочернего
DshEngine.listTools()Зарегистрированные инструменты, видимые модели
DshEngine.sendMessage(id, txt)Начало хода и получение дескриптора потока
DshEngine.cancel(id)Отмена активного хода сеанса

Контракт публикуется как сервис Cordis libreDshEngine; потребитель читает его через ctx.get('libreDshEngine'), не импортируя модуль моста.

EngineStreamChunk передаёт text, reasoning, tool-call, tool-result, approval-request, approval-decision, error и done. Живые кадры маршрутизируются по владеющим агенту и сеансу; соответствующее сохранённое сообщение ассистента не отправляется повторно. sendMessage возвращает дескриптор, чей subscribe воспроизводит уже выданное. Поэтому быстрый первый токен не теряется между началом хода и подключением HTTP-обработчика.

Последовательность одного хода чата

Используется NDJSON, поскольку ход после запроса — единая последовательность от сервера к клиенту. Сохранение её в POST исключает второе рукопожатие, билет и протокол переподключения и удерживает весь ход в одном аутентифицированном запросе.

DONE и PENDING

Cordis активирует плагин после появления объявленных сервисов, поэтому строка некоторое время находится в состояниях до запуска. Путаница между следующими понятиями — наиболее частая причина молчащего движка.

Состояние записи Loader. Каждая строка проходит PENDING → LOADING → ACTIVE либо FAILED. Без объявленных сервисов она бесконечно ожидает вместо ошибки, поэтому неполная композиция может запуститься, но ничего не обслуживать.

Доступность сервиса. Хост сообщает каждый ожидаемый сервис так:

СостояниеЗначениеПричина
pendingНе зарегистрирован в контекстеПредоставляющая строка не активирована или выключена
readyЗарегистрирован и доступенПредоставляющая строка активирована
failedОбъявлен, но недоступенСообщается со строкой detail

host.status() показывает доступность и отсутствующие обязательные сервисы; GET /api/cordis/health возвращает те же сведения. Отсутствие обязательного сервиса прерывает запуск, а не публикует движок с пустыми ответами.

В двух цепочках легко ошибиться:

  • dsh-tools не запускается без systemPrompt.
  • dsh-agent-loop ждёт agents, sessions, llm, tools, systemPrompt и sessionProjections.

Если чего-то не хватает, хранилище сеансов может работать, но движок никогда не отвечает на сообщения.

Конфигурация провайдера

Поставляемая строка libre-webui-llm-adapter обслуживает провайдеров, настроенных в Libre WebUI. Селектор страницы Движка выбирает модель для сеанса, не заменяя строку.

Изменения строк композиции применяются при следующем старте хоста. Перезапустите бэкенд либо выключите и включите Cordis, если переключатель администратора разблокирован. Сохранённые сеансы остаются в настроенном хранилище и возобновляются через текущую композицию.

Доверенный код интеграции может напрямую использовать API жизненного цикла Loader. Мост не предоставляет конечной точки замены адаптера и автоматически не восстанавливает предыдущий адаптер при сбое нового.

Откат

Освобождение корневого fiber хоста удаляет всё установленное движком. Эта связь владения и составляет гарантию:

  • Сервисы регистрируют плагины, и они отзываются вместе с их fiber.
  • Подписки session/event создаются в конструкторе моста и принадлежат fiber его строки.
  • Мост отслеживает дескрипторы агентов и освобождает их своим завершающим эффектом.
  • Хост освобождает корневой контекст, владеющий всеми строками.

stopCordisHost() идемпотентен и включён в завершение бэкенда: таймеры и файловые дескрипторы освобождаются явно, а не остаются до выхода процесса.

Идентичность и сохранение сеансов

Страница Движка резервирует непрозрачный ID при создании. При включённом сохранении заголовок сразу записывается, поэтому даже пустой сеанс переживает перезапуск. Мост перечисляет сохранённые и активные сеансы, читает логи через проверяемый API хранения DSH и возобновляет агента на том же ID для следующего сообщения. Новые сообщения пользователя используют конструктор DSH с идентификатором.

Удаление сеанса сначала отменяет и освобождает агента, затем удаляет его файл. Локальный адаптер JSONL проверяет пути хранилища и сеанса и отвергает символьные ссылки. Пользовательские хранилища без поддержки удаления возвращают ошибку, а не объявляют данные удалёнными.

Отмена достигает нативного агента, запроса модели и инструментов. Отключение клиента отменяет ход; готовые сообщения остаются читаемыми. Повторная выдача буфера потока ограничена и сохраняет быстрый ответ до подключения читателя.

Движок хоста — функция solo с одной репликой. Развёртывания team не могут загрузить локальный JSONL-runtime. Work в песочнице использует существующие SQL-репозитории задач, запусков, сообщений, согласований и событий.

HTTP-интерфейс

МетодПутьНазначение
GET/api/cordis/healthСостояние моста; без аутентификации
GET/api/cordis/sessionsСписок сеансов
POST/api/cordis/sessionsСоздание сеанса
GET/api/cordis/sessions/:idЧтение сеанса с сообщениями
DELETE/api/cordis/sessions/:idЗавершение сеанса
POST/api/cordis/sessions/:id/messagesОтправка сообщения и поток NDJSON
POST/api/cordis/sessions/:id/cancelОтмена активного хода
GET/api/cordis/agentsСписок активных агентов
GET/api/cordis/toolsСписок зарегистрированных инструментов

Все маршруты, кроме /health, требуют аутентифицированный сеанс администратора. Пока мост не может обслуживать запросы, они отвечают 503 с code равным CORDIS_DISABLED, CORDIS_STARTING или CORDIS_UNAVAILABLE.

Страница Движка Cordis Libre WebUI со списком сеансов, зарегистрированными инструментами и потоковой беседой.

Страница — frontend/src/pages/CordisPage.tsx, доступна по /cordis из боковой панели. Она перечисляет сеансы и инструменты, создаёт сеансы и добавляет потоковый ход в историю. Если мост выключен или не запускается, показывается причина, а не пустой список: иначе «нет сеансов» неотличимо от «нет движка».

Клиент браузера — frontend/src/utils/api/cordisApi.ts. Он обращается только к этому интерфейсу, не импортируя типы бэкенда или пакеты @deepseek-ai/*; движок можно заменить без изменения фронтенда. Ход читается через sendMessage(sessionId, text, { onChunk }); клиент сам разбирает JSON по строкам и поддерживает фрагменты, разделённые между сетевыми чтениями.

Управление чатом Движка

Страница показывает Markdown, таблицы и код с подсветкой, с кнопками копирования ответов и кода. Системные промпты и внедрённый контекст среды находятся в свёрнутом разделе Контекст сеанса, а не изображаются сообщениями пользователя. Доступные рассуждения и активность инструментов имеют отдельные раскрываемые секции; результаты после перезагрузки остаются связаны с правильными операциями.

Выберите настоящую модель провайдера. Селектор показывает доступные локальные и плагинные модели вошедшего администратора вместе с провайдером. Персоны и агенты Чата не являются ID моделей и не внедряют инструкции в беседу Движка. Старые ошибочные заголовки с моделью персоны игнорируются как подсказки по умолчанию без изменения сохранённого лога.

У каждого сеанса свой режим Только чтение или Запись в рабочей области, обеспечиваемый файловой политикой DSH и канонической границей моста. Поле ввода показывает область действия. Настройки сохраняются нативными событиями и переживают рестарт; во время хода изменения отклоняются.

Нативный запрос расширения прав появляется карточкой Разрешить один раз / Отклонить у операции. Согласие действует только для этого запроса и не меняет постоянный режим. Устаревшие или отменённые запросы нельзя одобрить; headless-Чат отклоняет вопросы, которые не может показать. Мост не предлагает неограниченный доступ к хосту.

Дополнительные конечные точки администратора:

МетодПутьНазначение
GET/api/cordis/modelsДоступные модели провайдеров и текущая реальная модель по умолчанию
PATCH/api/cordis/sessions/:id/settingsВыбор модели или режима разрешений сеанса
POST/api/cordis/sessions/:id/approvals/:approvalIdРешение ожидающего запроса через allowed-once или rejected

Использование движка в Чате

Включите Доступ и политики → Модели агентов CLI и Движок Cordis. Администраторы смогут выбрать DeepSeek Harness в Чате. Каждый запрос получает новый временный сеанс движка с переданной этим запросом историей. Обычная база Чата остаётся источником истины: независимые разговоры, ветки и повторы не делят невидимую историю движка. Временный лог удаляется после завершения или отмены и не отображается на странице Движка.

Стандартная композиция провайдеров также показывает DeepSeek Harness · модель (провайдер) в Агентах. Сохранённые ID оборачивают тот же квалифицированный маршрут, что у страницы Движка: dsh:lwui:ollama:<model> или dsh:lwui:plugin:<plugin>:<model>, с процентным кодированием компонентов. Необязательное локальное подключение нативного DSH добавляет dsh:native:<provider>:<model> из актуального каталога инстанции, повторно используя её настройки и учётные данные.

Установите отдельный пакет Apache-2.0 из libre-webui/dsh-native-provider либо подготовьте bundle из дистрибутива Libre WebUI. Оба используют имя @libre-webui/dsh-native-provider и хранят ключи в нативном DSH. Требуются один Unix-хост и одна учётная запись ОС с приватным Unix-сокетом; приложения под одной учётной записью не изолируются. Доступен только инференс модели, без нативных сеансов агента и выполнения инструментов. Установка, перезапуски профиля, обновление и удаление описаны в руководстве конфигурации. Недоступное соединение или выбранная модель вызывают ошибку без смены провайдера. Нативные вызовы также видны в Использование провайдера с моделью, сообщёнными токенами, задержкой и результатом.

Базовый профиль dsh сохраняет модель по умолчанию работающей композиции. Композиции с собственным адаптером предоставляют этот профиль, не предлагая неподдерживаемые переопределения провайдеров Libre WebUI.

Заголовки и итоги размышлений разрешают выбор DSH в лежащий под ним провайдер и напрямую запрашивают текст без инструментов или сеанса агента. Базовый профиль читает настройки работающего движка, включая переопределения строки моста, а не угадывает их по каталогу. Собственным адаптерам нужна явно выбранная модель задач Ollama или плагина. Недоступный провайдер даёт обычную ошибку либо локальное превью заголовка, не запрос другому провайдеру.

Используются настройки и учётные данные аутентифицированного администратора. Данные других администраторов не выбираются неявно. Настроенная область Cordis остаётся стандартной; Чат не подменяет её домашним каталогом пользователя сервера.

Work в песочнице

При включённом Cordis Work предлагает отдельный Движок: Libre WebUI или DeepSeek Harness. Селектор сохраняет обычные имена моделей и провайдеров. Для провайдеров LWUI выбор DSH хранится как dsh:<model>; для нативных — как providerType: dsh, точный ID провайдера и исходный ID модели. Обычные проверки доступа и поддержки инструментов остаются; нативные данные дополнительно требуют активного администратора.

Каждый запуск создаёт отдельный цикл DSH в памяти. Адаптер получает текущую историю Work, метаданные провайдера, изображения и схемы инструментов. Тела инструментов только ждут результатов Work; они не читают файлы хоста и не запускают его процессы.

Work проверяет аргументы, запрашивает согласия, выполняет инструменты в рабочей среде, сохраняет результаты и состояние воспроизведения провайдера в SQL, применяет бюджеты и публикует события. Отказ даёт обычный результат отказа. Отмена освобождает DSH и следует обычной очистке контейнера Work. После восстановления воркера новый драйвер получает восстановленный контекст без повторения завершённых эффектов инструментов.

Интеграции не нужны композиция Движка хоста и хранилище JSONL. Действуют правила Work для Docker/Kubernetes и развёртывания, включая общую постоянную память режима team.

Граница безопасности

Страница Движка и агент Чата на хосте доступны только администраторам. Сеансы — общая административная консоль, включая системные промпты, а не личные рабочие области. Обычные аккаунты не могут читать, создавать, менять или отменять их через API.

Поставляемые файловые инструменты ограничивают чтение и запись настроенной областью с каноническими путями и разрешением символьных ссылок. Переопределённые рабочие каталоги сеансов обязаны оставаться внутри. Действуют нативная политика изменений DSH и одноразовые согласия Движка. Плагины композиции оператора — доверенный серверный код, способный предоставлять дополнительные возможности. Согласования Движка отделены от согласований Work и выполнения в контейнерах.

Драйвер Work отдельный: он не загружает плагины файлов, оболочки или хранения хоста и выполняет действия только через авторизацию и песочницу Work. Удалённые модели остаются необязательными и используют настроенный маршрут выбранного аккаунта.