Перейти к содержимому
GitRiverGitRiver
EN
Навигация

Помощник на основе языковой модели

Помощник на языковых моделях: какие задачи решает, к каким моделям подключается и что уходит наружу

Разбор упавших заданий CI по журналу и разбор изменений запроса на слияние. Модель выбирает администратор установки: свой узел внутри контура или облачный поставщик с OpenAI-совместимым интерфейсом. Входит в редакцию Pro.

Настройка: Администрирование → Помощник ИИ (/admin/ai).

Граница контура

Это первое, что нужно знать при развёртывании в закрытом контуре.

Ненастроенный или выключенный помощник не делает ни одного исходящего запроса. Отказ выдаётся до того, как собран запрос к модели: содержимое журнала никуда не уходит. Установка без настроенного поставщика ведёт себя ровно так, как если бы помощника в продукте не было.

Настроенный помощник обращается ровно по одному адресу — тому, что задал администратор в поле «Адрес интерфейса». Автоматические переходы по 3xx запрещены: ответ чужого сервера не может увести содержимое запроса на другой адрес.

Тип поставщика определяет сетевую политику:

Поставщик Схема Адрес Куда уходит содержимое
Свой узел http или https любой, включая приватные диапазоны внутрь контура
Облачный только https обязан указывать на публичный адрес (защита от SSRF) внешнему поставщику

Приватные диапазоны разрешены только поставщику «Свой узел» — например, http://ollama:11434/v1 в сети docker. Облачному поставщику http запрещён, чтобы исходный код не уходил наружу открытым текстом.

Куда именно уходят запросы, видно в интерфейсе: в настройках и у кнопки разбора показывается адрес поставщика, а для облачного — предупреждение, что содержимое уходит внешнему поставщику.

Секреты CI в журнал не попадают: значения секретных переменных вырезаются при записи журнала ([MASKED]), то есть ещё до того, как журнал может быть отдан модели.

Поставщики

Подходит любой сервер с интерфейсом POST {base_url}/chat/completions:

  • Ollama — http://узел:11434/v1, модель вида qwen2.5-coder:7b;
  • vLLM, llama.cpp server, LM Studio — свой адрес, модель по имени;
  • облачные поставщики с тем же интерфейсом — https://…/v1 и ключ доступа.

Ключ доступа хранится зашифрованным (AES-256-GCM) и не возвращается ни в одном ответе API: интерфейс показывает лишь признак «задан». При правке настроек пустое поле ключа означает «оставить прежний», а кнопка «Удалить ключ» рядом с полем — «убрать» (своему узлу ключ обычно не нужен). После замены jwt_secret установки сохранённый ключ доступа перестаёт читаться, и помощник не ответит, пока ключ не введут заново.

Кнопка «Проверить связь» опрашивает GET {base_url}/models и, если поставщик отдаёт перечень, сверяет с ним имя модели, а затем делает пробный обмен сообщением — одно слово и до шестнадцати токенов ответа. Так проверяются сразу адрес, ключ и модель, в том числе у рассуждающей модели. Проверку можно выполнить до сохранения настроек.

Сколько разрешено ждать модель

«Ожидание ответа» в настройках — от 1 до 600 секунд, по умолчанию 180; столько помощник ждёт ответа поставщика. Рассуждающей модели и локальной модели на процессоре на разбор журнала задания легко нужны минуты — умолчание рассчитано на это.

Если модель не уложилась, участник видит именно это — «модель не ответила за столько-то секунд», а не «поставщик недоступен»: связь в порядке, и нужно поднять ожидание в настройках помощника.

На запросы, ожидающие модель (разбор задания, разбор запроса на слияние, проверка связи), общий срок запроса API в 30 секунд не распространяется: их срок задаёт ai_http_timeout_secs (GITRIVER_AI_HTTP_TIMEOUT_SECS, по умолчанию 660 секунд — потолок ожидания плюс минута на сборку запроса). Ставить его меньше заданного ожидания не нужно: запрос оборвётся раньше, и вместо разбора участник увидит 504.

Рассуждающая модель: когда ответа нет вовсе

У рассуждающих моделей (GLM, Qwen с включённым размышлением, DeepSeek-R1 и прочих) предел длины ответа тратится сначала на размышление. При уровне рассуждений «максимум» тысяча токенов — умолчание настройки — уходит на него целиком, ответ обрывается по длине, и помощник получает пустоту.

Отказ в этом случае называет причину прямо: «модель израсходовала предел длины ответа на размышление (столько-то токенов) и до ответа не дошла». Чинится одним из двух:

  • поднять длину ответа в настройках (потолок — 32 768 токенов);
  • погасить размышление дополнительными полями запроса (ниже).

Дополнительные поля запроса

Объект JSON, который подмешивается в тело запроса к модели как есть. Здесь задаётся то, чего нет отдельным полем настройки: рычаг размышления, temperature, top_p, любые расширения вашего сервера.

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

Сервер Что вписать
OpenAI-совместимые (и свежие vLLM) {"reasoning_effort": "low"}
vLLM, SGLang с GLM и Qwen {"chat_template_kwargs": {"enable_thinking": false}}
шлюзы под интерфейс Anthropic {"thinking": {"type": "disabled"}}
подстройка выдачи {"temperature": 0.2, "top_p": 0.9}

Поля model, messages, max_tokens и stream помощник задаёт сам, и переопределить их здесь нельзя: настройка отвергается с указанием поля. Пустое поле — ничего не добавляется. Размер — до 8 КиБ.

Ключи доступа сюда не вписывают. Ключ поставщика хранится зашифрованным в своём поле; дополнительные поля хранятся как есть и возвращаются администратору в ответе настроек — вписанный сюда ключ окажется сохранён и виден открытым текстом.

Размышление, пришедшее не отдельным полем, а прямо в тексте ответа (между <think> и </think> — так отдают llama.cpp и часть шаблонов GLM и Qwen), помощник снимает: участнику показывается разбор, а не черновик к нему. Если предел длины кончился внутри размышления и закрывающего тега нет, ответа нет вовсе — и помощник скажет об этом, а не покажет обрывок.

«Проверить связь» отправляет пробный запрос с теми же дополнительными полями, поэтому поле, которого сервер поставщика не знает, обнаружится уже при проверке.

Разбор упавшего задания CI

На странице конвейера у упавшего задания появляется кнопка «Разобрать сбой». Помощник получает контекст задания (репозиторий, ветка, имя, этап, код возврата, образ) и хвост журнала, а возвращает разбор: причину, объяснение и что сделать.

  • Ответ помечен как машинный (machine_generated) — читателю видно, что текст написан не человеком, а также какой моделью.
  • Журнал отдаётся хвостом: причина падения почти всегда в конце, а начало занято установкой зависимостей. Объём хвоста задаётся настройкой «Журнала на разбор». Если журнал не поместился целиком, об этом сказано и модели, и читателю.
  • Разбор доступен тем, у кого есть право записи в репозиторий; права на чтение недостаточно, потому что обращение расходует месячный предел владельца репозитория.
  • Успешное задание на разбор не отправляется.

Разбор изменений запроса на слияние

На странице запроса на слияние участник с правом записи нажимает «Разобрать изменения». Модель получает заголовок, описание, ветки и текст изменений, а ответ публикуется обычным комментарием с пометкой «Сгенерировано моделью».

  • Как обычный комментарий, разбор виден в ленте обсуждения, в ветках обсуждения и в счётчике нерешённых. Пометку ставит сервер; объявить свой комментарий машинным клиент не может.
  • Автор комментария — тот, кто запросил разбор: видно, по чьей просьбе он появился, а права на правку и удаление остаются обычными.
  • В подписи под разбором указаны модель и сколько файлов разобрано.
  • Файлы отдаются модели целиком и по порядку, пока хватает объёма («Изменений на разбор» в настройках); файл, который целиком не помещается, не обрезается. Двоичные и не поместившиеся файлы перечисляются отдельно — и модели, и читателю видно, что разобрано не всё.
  • Запрос без изменений на разбор не отправляется.

Учёт расхода и пределы

Расход относится на владельца репозитория — пользователя или группу, ту же сторону, на которую считаются квоты хранилища. Не на того, кто нажал кнопку: разбор сборки командного репозитория — расход команды.

Окно учёта — календарный месяц (с первого числа, UTC): предел обнуляется первого числа каждого месяца.

Уровень Где задаётся
Умолчание установки «Месячный предел на владельца» в настройках помощника
Переопределение владельца таблица расхода, кнопка «Задать предел»

Ноль означает «без ограничения» — и в умолчании, и в переопределении. Переопределение важнее умолчания, поэтому ноль у владельца снимает предел установки, а не запрещает обращения.

Если поставщик не сообщает расход в ответе, он оценивается по длине текстов.

Сверх месячного предела действует предел частоты: 10 обращений в минуту на клиента — общий для всех обращений, которые ждут ответа модели (разбор упавшего задания, разбор изменений и проверка связи с поставщиком из настроек), и отдельный от предела пишущих вызовов. Превышение — 429 с заголовком Retry-After.

Предел мягкий

Предел проверяется до обращения к модели и сверяется с расходом, уже записанным в учёт: сколько токенов потребует очередное обращение, заранее не знает никто, поэтому правило звучит как «начать новое обращение нельзя», а не «оборвать на полуслове». Достигнутый предел (израсходовано ≥ предела) уже считается исчерпанным. Отсюда два следствия:

  • последовательные обращения перебирают предел не более чем на одно обращение (при пределе 300 и обращении в 120 токенов списывается 360, дальше 403);
  • одновременные обращения проверяются по одному и тому же расходу и могут пройти все: 10 параллельных запросов при том же пределе 300 спишут до 1200. Величину такого перебора ограничивает предел частоты — больше 10 обращений в минуту клиент не сделает.

После перебора следующее обращение получает 403 с фактическими числами. Такой предел рассчитан на бюджет, а не на запрет: назначайте его исходя из месячной нормы, а не как жёсткий барьер на каждое обращение.

Что видно в отказе

Подробность отказа поставщика (текст его сообщения) видит только администратор — в проверке связи и в журнале сервера. Тому, кто нажал «Разобрать сбой», возвращается код отказа без подробностей: сообщения поставщиков могут содержать начало ключа доступа и адрес узла модели.

API

Действие Метод и путь
Настройки помощника (без ключа) GET /api/v1/admin/ai
Сохранить настройки PUT /api/v1/admin/ai
Забыть настройки DELETE /api/v1/admin/ai
Проверить связь с поставщиком POST /api/v1/admin/ai/test
Расход по владельцам за месяц GET /api/v1/admin/ai/usage?limit=&offset=
Предел пользователя PUT/DELETE /api/v1/admin/ai/limits/users/{id}
Предел группы PUT/DELETE /api/v1/admin/ai/limits/groups/{id}
Состояние помощника GET /api/v1/ai/status
Разбор упавшего задания POST /api/v1/repos/{owner}/{name}/pipelines/{id}/jobs/{job_id}/explain
Разбор изменений запроса на слияние POST /api/v1/repos/{owner}/{name}/pull_requests/{number}/ai-review

Все пути требуют редакции Pro (или Max). Административные пути (/api/v1/admin/ai/**) местами не меряются: администратор управляет помощником и без места. Обращения от имени участника — состояние помощника и оба разбора — требуют ещё и места у того, кто обращается (см. лицензирование); учётная запись без места работает в объёме редакции Community. Отчёт о расходе выдаётся постранично: total показывает общее число владельцев, поэтому хвост списка не теряется.

Пример: Ollama рядом с GitRiver

# docker-compose.yml — фрагмент
services:
  ollama:
    image: ollama/ollama
    volumes:
      - ollama:/root/.ollama
volumes:
  ollama:
docker compose exec ollama ollama pull qwen2.5-coder:7b

Затем в Администрирование → Помощник ИИ:

  • поставщик — «Свой узел»;
  • адрес интерфейса — http://ollama:11434/v1;
  • модель — qwen2.5-coder:7b;
  • ключ доступа не нужен.

Содержимое журналов не покидает сеть compose.