Skip to Content
Руководство для разработчиковРуководство по интеграции через REST API

Руководство по интеграции через REST API

Для команд, встраивающих Qwen Code в собственный продукт поверх HTTP: запустите qwen serve как бэкенд и управляйте им из собственного фронтенда.

Эта страница — отправная точка. Полный справочник маршрутов находится в qwen-serve-protocol.md; внутреннее устройство описано в углублённом руководстве по демону; рабочий пример на TypeScript — в examples/daemon-client-quickstart.md.

Какие пути существуют

Шесть способов построения на базе демона, разделённых одним вопросом — какую часть фронтенда вы контролируете?

ПутьВы контролируетеСтатус
демон + встроенный Web Shellничего — используете как естьдоступен сейчас (руководство пользователя)
демон --no-web + ваш собственный UIвесь фронтенд целикомдоступен сейчас — эта страница
демон + брендированный Web Shellбрендинг, а не кодне реализовано (#11357 )
демон + самостоятельно собранный Web Shellсборку фронтендане реализовано (#11358 )
демон через SDK DaemonClientклиентский код, без сырого HTTPдоступен сейчас (TS, Java) — Python SDK работает только с процесс-транспортом и не имеет клиента демона, поэтому Python-интеграция использует путь 2 через сырой HTTP
демон через MCP bridgeничего — другой агент управляет импоставляется как qwen-serve-mcp в @qwen-code/sdk — см. README бриджа; QWEN_BRIDGE_ALLOW_GLOBAL_SCOPE опционально разрешает мутации в глобальной области

Headless qwen -p и ACP через stdio для редакторов — это отдельные пути интеграции. Каналы и расширения также могут работать через демон; см. руководство по каналам и справочник расширений.

Что нужно знать перед проектированием

Демон не выполняет инференс внутри своего процесса. Он порождает дочерние процессы qwen --acp и выступает посредником между ними и HTTP. Он запускает входной скрипт CLI под тем же бинарём Node, используя QWEN_CLI_ENTRY или, иначе, process.argv[1]. Встраиваемый Node-бэкенд должен указывать в QWEN_CLI_ENTRY путь к установленному входному скрипту Qwen CLI; поиск qwen в PATH не производится. Отсутствие точки входа проявляется как MissingCliEntryError.

В установившемся режиме существует один дочерний процесс на активное рабочее пространство, а не по одному на сессию. Каждая сессия в рабочем пространстве мультиплексируется на этот дочерний процесс и разделяет его процесс, состояние OAuth, файловый кэш и разбор иерархической памяти. Таким образом, домен отказа — это рабочее пространство: если дочерний процесс завершается, все мультиплексированные на него сессии уничтожаются вместе. Рассчитывайте контейнер на демон плюс один дочерний процесс на зарегистрированное рабочее пространство, с запасом на один дополнительный дочерний процесс на среду выполнения при переключении канала. Когда сессии должны отказывать независимо, запускайте отдельные демоны — --max-sessions ограничивает параллелизм, а не радиус поражения.

Аутентификация рассчитана на одного оператора. Runtime bearer-токен предоставляет доступ ко всему bearer-защищённому API, а доверенный loopback-вызывающий получает полные полномочия, включая выполнение кода от имени пользователя демона. Модели принципала для каждого конечного пользователя нет. Если вы размещаете это за мультипользовательским продуктом, ваш бэкенд отвечает за идентификацию пользователей и не должен передавать токен демона в браузеры. Контейнеризированное и мультитенантное развёртывание явно отложено — см. «v0.16-alpha known limits» в руководстве пользователя.

Настроенный вход webhook канала (POST /channels/:channelName/webhooks/:source) использует собственную аутентификацию x-qwen-webhook-secret до bearer-аутентификации; он неактивен, пока не настроен источник webhook канала.

Запуск демона

export QWEN_SERVER_TOKEN="$(openssl rand -hex 32)" qwen serve --no-web --require-auth \ --hostname 0.0.0.0 --port 4170 \ --workspace /srv/project

--no-web сохраняет перечисленные ниже маршруты, но отключает ресурсы Web Shell и зависимые поверхности: на macOS — маршруты /live/* и сокет /live/host, а на каждой платформе — GET /mcp-app-sandbox. Передавайте токен через переменную окружения, а не через --token, который доступен любому локальному пользователю через /proc/<pid>/cmdline.

Примеры Bash ниже передают заголовок Authorization через файловый дескриптор с помощью встроенной команды printf оболочки, не включая токен в аргументы curl.

Маршруты, которые реально использует интеграция

Большая часть того, что регистрирует демон, существует для работы Web Shell — операции git, установка расширений, доверие к рабочему пространству, голос, запланированные задачи — и меняется вместе с этим UI. Нижеприведённое подмножество на порядок меньше.

Это то, что нужно REST-интеграции. Остальное считайте внутренним.

Обнаружение

МаршрутНазначение
GET /healthLiveness-проба
GET /capabilitiesПредварительный запрос — прочитайте workspaceCwd и policy.permission до всего остального

Жизненный цикл сессии

МаршрутНазначение
POST /sessionСоздание. Отправьте sessionScope: "thread" для независимого разговора
DELETE /session/:idЗакрытие. Сохранённая сессия остаётся и может быть перезагружена
POST /session/:id/load · /resumeВосстановление сохранённой сессии
POST /session/:id/heartbeatОтсрочка idle-утилизатора
PATCH /session/:id/metadataМетаданные сессии
POST /session/:id/modelПереключение модели в пределах привязанной службы
GET /session/:id/statusСтатус среды выполнения — отдельного раздела справочника пока нет

Промптинг и потоковая передача

МаршрутНазначение
POST /session/:id/promptОтправка. Возвращает 202 при допуске, а не при завершении
POST /session/:id/cancelОтмена только активного промпта
GET /session/:id/eventsПоток SSE. Подписывайтесь до отправки промпта
GET /session/:id/transcriptИстория разговора
GET /session/:id/contextИспользование окна контекста
GET /session/:id/export · GET /session/:id/pending-promptsОтдельных разделов справочника пока нет

Разрешения

МаршрутНазначение
POST /session/:id/permission/:requestIdОтвет на permission_request. Маршрутизируется к среде выполнения, владеющей сессией, поэтому работает корректно при любой конфигурации рабочего пространства — отдельного раздела пока нет
POST /permission/:requestIdПроцесс-глобальная форма, подключённая только к бриджу основного рабочего пространства: возвращает 404 для сессии, принадлежащей другой зарегистрированной среде выполнения, с тем же телом, что и пропавший голос при политике first-responder по умолчанию — поэтому 404 здесь сам по себе не означает, что запрос уже был отвечен

Контекст рабочего пространства только для чтения

МаршрутНазначение
GET /file · /file/bytesЧтение файла или диапазона байт
GET /stat · GET /list · GET /globМетаданные пути, список каталога, glob — отдельных разделов пока нет
GET /workspace/toolsИнструменты, сообщённые активным дочерним ACP-процессом; без него ответ содержит acpChannelLive: false, tools: [] и ошибку not_startedотдельного раздела пока нет

Покрытие справочника. 17 из 25 вышеперечисленных маршрутов имеют отдельные разделы. Из 8 отмеченных иначе некоторые упомянуты лишь вскользь, а три отсутствуют полностью: GET /session/:id/pending-prompts, POST /session/:id/permission/:requestId и GET /workspace/tools. Закрытие этого пробела отслеживается в #11359 .

Минимальный поток

1. Предварительный запрос. Прочитайте workspaceCwd (чтобы не передавать cwd при создании) и policy.permission (чтобы знать, кто может отвечать на запросы разрешений).

curl -sH @<(printf 'Authorization: Bearer %s\n' "$QWEN_SERVER_TOKEN") http://daemon:4170/capabilities

2. Создание сессии. Используйте sessionScope: "thread", если вызывающие не должны разделять один разговор — значение по умолчанию "single" заставляет повторное создание в том же рабочем пространстве переиспользовать существующую сессию, сериализуя независимых вызывающих через одну очередь.

curl -sX POST http://daemon:4170/session \ -H @<(printf 'Authorization: Bearer %s\n' "$QWEN_SERVER_TOKEN") -H 'Content-Type: application/json' \ -d '{"sessionScope":"thread"}' # → {"sessionId":"…","workspaceCwd":"/srv/project","attached":false}

3. Подписка до отправки промпта. Last-Event-ID: 0 воспроизводит с самого старого сохранённого события — так вы ловите события, возникшие между созданием и подпиской, в частности model_switch_failed. При подключении (значение по умолчанию sessionScope: "single" переиспользует существующую сессию) это событие — единственный сигнал того, что некорректный modelServiceId был отклонён, потому что ошибка намеренно не передаётся как HTTP-ошибка. При новом создании с modelServiceId — чего тело шага 2 не содержит — тело 200 также содержит modelApplied, false если переключение было отклонено, и это детерминированный сигнал для действия, в отличие от события на кольце с ограниченным буфером. Создание без modelServiceId вообще не имеет ключа modelApplied.

curl -N http://daemon:4170/session/$SID/events \ -H @<(printf 'Authorization: Bearer %s\n' "$QWEN_SERVER_TOKEN") \ -H 'Accept: text/event-stream' -H 'Last-Event-ID: 0'

Каждая строка data: — это полный Envelope на одной строке; type Envelope совпадает со строкой event:.

Воспроизведение ограничено --event-ring-size и фиксированным байтовым бюджетом 8 МиБ на подписку. Если поток выдаёт state_resync_required с reason: "replay_budget_exceeded", восстановитесь через POST /session/:id/load, а не считайте воспроизведение завершённым.

4. Промптинг. 202 означает допуск, а не завершение. Соотнесите turn_complete / turn_error в потоке по promptId. Читайте stopReason в turn_complete; в turn_error читайте message и необязательные code / errorKind — см. POST /session/:id/prompt.

curl -sX POST http://daemon:4170/session/$SID/prompt \ -H @<(printf 'Authorization: Bearer %s\n' "$QWEN_SERVER_TOKEN") -H 'Content-Type: application/json' \ -d '{"prompt":[{"type":"text","text":"What does src/main.ts do?"}]}' # → 202 {"promptId":"…","lastEventId":42}

5. Ответы на запросы разрешений. Когда агент хочет выполнить инструмент и его режим одобрения требует подтверждения, он выдаёт permission_request, и ход блокируется, пока кто-то не ответит или вы не отмените — по умолчанию таймаута нет (--permission-response-timeout-ms по умолчанию 0 = ждать бесконечно), поэтому неотвеченный запрос продолжает занимать слот в очереди промптов сессии, пока вы не отмените или не закроете сессию. Установите собственный дедлайн, если потоку он нужен.

Режим — это собственная настройка Qwen дочернего процесса tools.approvalMode, разрешаемая из настроек хоста демона и каталога --workspace; демон ничего не фиксирует при запуске. По умолчанию auto, который одобряет один класс вызовов инструментов без спрашивания — те не выдают permission_request вообще — и продолжает спрашивать для остальных. Недоверенная папка рабочего пространства принудительно переключается на default (спрашивать), поэтому одна инсталляция видит эти события, а другая нет, и GET /capabilities сообщает политику посредничества голосов, а не режим одобрения, так что предварительный запрос не скажет, в какой позиции вы находитесь. Если ваша интеграция зависит от гейтинга одобрения, зафиксируйте tools.approvalMode явно и решите заранее, как он отвечает: автоодобрение уже может действовать без того, чтобы кто-либо его выбирал.

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

curl -sX POST http://daemon:4170/session/$SID/permission/$REQUEST_ID \ -H @<(printf 'Authorization: Bearer %s\n' "$QWEN_SERVER_TOKEN") -H 'Content-Type: application/json' \ -d '{"outcome":{"outcome":"selected","optionId":"proceed_once"}}'

6. Закрытие. DELETE /session/$SID204. Сессия на диске сохраняется.

Эксплуатация

ЗаботаГде
Ограничения параллелизма--max-sessions, --max-total-sessions; создания сверх лимита возвращают 503 с Retry-After
Ограничение частоты--rate-limit и флаги --rate-limit-* для каждого класса
Очистка неактивных--session-idle-timeout-ms; поддержка активности через POST /session/:id/heartbeat
Память--child-heap-mode — только наблюдение. --memory-budget-mb управляет адаптивным пулом роста live-журнала для POST /session/:id/load, а не для воспроизведения SSE; фиксация --max-journal-bytes или --max-journal-events отключает рост. Ни один флаг не определяет размер дочерних процессов и не отказывает в запуске, и не управляет их фактическим потолком кучи (--max-old-space-size, производный от памяти хоста). См. Конфигурация для расчёта бюджета. Воспроизведение SSE отдельно ограничено --event-ring-size и фиксированным бюджетом 8 МиБ на подписку; пропущенный хвост выдаёт state_resync_required с reason: "replay_budget_exceeded"
Дедлайны промптов--prompt-deadline-ms; истечение выдаёт turn_error
ОшибкиКлассификация ошибок
НаблюдаемостьНаблюдаемость
Полный список флаговКонфигурация
Last updated on