Навигация
Помощник на основе языковой модели
Помощник на языковых моделях: какие задачи решает, к каким моделям подключается и что уходит наружу
Разбор упавших заданий 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.