Исправление ошибок (no content) и утечки тегов в Kimi K2.5 на vLLM и SGLang | AiManual
AiManual Logo Ai / Manual.
28 Янв 2026 Гайд

Kimi K2.5 в vLLM и SGLang: как убить '(no content)' и утечку тегов на H200

Подробное решение проблем с ответами '(no content)' и утечкой XML-тегов при запуске Kimi K2.5 в vLLM и SGLang на H200 GPU. Настройка tool-call-parser и параметр

Когда Kimi K2.5 молчит вместо ответа

Вы развернули Kimi K2.5 на кластере из 8x H200, запустили vLLM или SGLang, отправили запрос и получили в ответ... пустоту. Или странный XML-мусор вроде <thinking> или <function_calls>, который должен был остаться внутри модели. Знакомо? Это не баг вашей настройки - это особенность архитектуры K2.5, которую нужно правильно обойти.

На 28.01.2026 Kimi K2.5 остается одной из самых капризных моделей для развертывания. Ее гибридная архитектура (MoE + reasoning) требует специфичных настроек, которых нет в документации vLLM.

Почему это происходит? Разбираем механизм ошибки

Kimi K2.5 использует внутренние теги для структурирования reasoning и tool calls. В обычном режиме эти теги удаляются перед выводом. Но когда вы запускаете модель через vLLM или SGLang, система постобработки не знает, как работать с кастомными тегами Kimi.

Проблема Причина Когда проявляется
(no content) в ответе Модель генерирует только внутренние теги, которые удаляются постпроцессором При использовании tool-call-parser без правильных стоп-токенов
Утечка XML-тегов Постпроцессор не распознает теги Kimi как служебные Когда skip_special_tokens=True не работает с кастомными тегами
Бесконечная генерация Отсутствие стоп-последовательностей для tool calls В SGLang без настройки stop_tokens

Проблема усугубляется тем, что Kimi K2.5 на 28.01.2026 использует собственную систему tool calling, несовместимую со стандартным OpenAI-форматом. Если вы пытались запустить ее как обычную модель - вот почему получали мусор.

Решение для vLLM: правильная настройка tool-call-parser

Основная ошибка - запуск vLLM с флагом --tool-call-parser, но без указания кастомных тегов Kimi. vLLM по умолчанию ожидает OpenAI-формат, а K2.5 говорит на своем диалекте.

1 Создаем кастомный парсер для Kimi

Вам нужен Python-скрипт, который понимает теги Kimi. Не используйте стандартный tool-call-parser - он сломается.

# kimi_parser.py
import json
import re
from typing import List, Dict, Any
from vllm.transformers_utils.tokenizer import get_tokenizer

class KimiToolCallParser:
    def __init__(self, tokenizer):
        self.tokenizer = tokenizer
        # Теги, специфичные для Kimi K2.5 (актуально на 28.01.2026)
        self.kimi_tags = [
            "",
            "",
            "",
            "",
            "",
            "",
            "",
            "",
            "",
            "",
        ]
        
    def parse_tool_calls(self, text: str) -> List[Dict[str, Any]]:
        """Извлекает tool calls из текста Kimi K2.5"""
        if "" not in text:
            return []
            
        tool_calls = []
        # Ищем все вызовы инструментов
        pattern = r'.*?'
        matches = re.findall(pattern, text, re.DOTALL)
        
        for match in matches:
            # Извлекаем название инструмента
            tool_name_match = re.search(r'(.*?)', match)
            # Извлекаем параметры
            params_match = re.search(r'(.*?)', match, re.DOTALL)
            
            if tool_name_match and params_match:
                try:
                    params = json.loads(params_match.group(1).strip())
                    tool_calls.append({
                        "type": "function",
                        "function": {
                            "name": tool_name_match.group(1).strip(),
                            "arguments": json.dumps(params, ensure_ascii=False)
                        }
                    })
                except json.JSONDecodeError:
                    # Если JSON невалидный, пытаемся починить
                    continue
        
        return tool_calls
    
    def clean_text(self, text: str) -> str:
        """Удаляет теги Kimi из финального ответа"""
        for tag in self.kimi_tags:
            text = text.replace(tag, "")
        # Удаляем множественные пробелы
        text = re.sub(r'\s+', ' ', text).strip()
        return text

2 Запускаем vLLM с правильными параметрами

Теперь запускаем vLLM с нашим парсером и критически важными стоп-токенами:

# Запуск на 8x H200 с правильными настройками
python -m vllm.entrypoints.openai.api_server \
    --model moe-community/Kimi-K2.5-8x7B \
    --tensor-parallel-size 8 \
    --gpu-memory-utilization 0.95 \
    --max-model-len 32768 \
    --served-model-name kimi-k2.5 \
    --trust-remote-code \
    --disable-custom-all-reduce \
    --enforce-eager \
    --stop "" --stop "" --stop "" \
    --stop-token-ids 2  # EOS token для Kimi

# Важно: НЕ используйте --tool-call-parser без кастомной реализации!
💡
Параметр --enforce-eager критически важен для H200 с Kimi K2.5. Графовый режим vLLM иногда конфликтует с архитектурой MoE модели, вызывая артефакты генерации.

3 Интегрируем парсер в клиентский код

При запросах к API используйте наш парсер для постобработки:

import openai
from kimi_parser import KimiToolCallParser

# Инициализируем парсер с токенизатором Kimi
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained(
    "moe-community/Kimi-K2.5-8x7B",
    trust_remote_code=True
)
parser = KimiToolCallParser(tokenizer)

# Запрос к vLLM API
client = openai.OpenAI(
    base_url="http://localhost:8000/v1",
    api_key="token-abc123"
)

response = client.chat.completions.create(
    model="kimi-k2.5",
    messages=[{"role": "user", "content": "Какая погода в Москве?"}],
    temperature=0.7,
    max_tokens=1024
)

# Постобработка ответа
raw_text = response.choices[0].message.content

# Извлекаем tool calls
tool_calls = parser.parse_tool_calls(raw_text)

# Очищаем текст от тегов
clean_text = parser.clean_text(raw_text)

print(f"Tool calls: {tool_calls}")
print(f"Clean response: {clean_text}")

Решение для SGLang: настройка stop_tokens и template

SGLang более гибок, но требует точной настройки шаблона. Основная проблема - SGLang по умолчанию не знает про теги Kimi.

1 Создаем кастомный шаблон для Kimi K2.5

# kimi_sglang.py
from sglang import function, system, user, assistant, gen, set_default_backend
from sglang.backend.runtime_endpoint import RuntimeEndpoint
import re

# Кастомный шаблон для Kimi K2.5
KIMI_TEMPLATE = """{{#system}}Вы - полезный ассистент Kimi.{{/system}}

{{#user}}
{{content}}
{{/user}}

{{#assistant}}
{{gen 'response' stop=['', '', '', '\n\n']}}
{{/assistant}}"""

@function
def kimi_chat(s, question):
    s += system("Вы - полезный ассистент Kimi.")
    s += user(question)
    
    # Критически важно: указываем stop-токены для Kimi
    s += assistant(gen(
        "response",
        max_tokens=1024,
        stop=["", "", "", "\n\n"],
        temperature=0.7
    ))
    
    # Постобработка для удаления тегов
    raw_response = s["response"]
    
    # Удаляем теги Kimi
    kimi_tags = [
        "", "",
        "", "",
        "", "",
        "", "",
        "", "",
    ]
    
    for tag in kimi_tags:
        raw_response = raw_response.replace(tag, "")
    
    # Удаляем множественные пробелы и пустые строки
    raw_response = re.sub(r'\s+', ' ', raw_response).strip()
    
    return raw_response

# Инициализация бэкенда для H200
backend = RuntimeEndpoint(
    "http://localhost:30000",
    tokenizer_path="moe-community/Kimi-K2.5-8x7B"
)
set_default_backend(backend)

# Использование
state = kimi_chat.run("Какая погода в Москве?")
print(f"Response: {state['response']}")

2 Запускаем SGLang сервер с правильными параметрами

# Запуск SGLang сервера для Kimi K2.5 на H200
python -m sglang.launch_server \
    --model-path moe-community/Kimi-K2.5-8x7B \
    --port 30000 \
    --tp-size 8 \
    --trust-remote-code \
    --max-total-tokens 32768 \
    --chat-template ./kimi_template.jinja  # Используем кастомный шаблон

# В kimi_template.jinja:
# {{#system}}Вы - ассистент Kimi.{{/system}}
# {{#user}}{{content}}{{/user}}
# {{#assistant}}{{gen stop=['','']}}{{/assistant}}

Типичные ошибки и как их избежать

Ошибка 1: Использование skip_special_tokens=True с токенизатором по умолчанию. Токенизатор Kimi не знает про ее внутренние теги как "специальные".

Неправильно:

# Так НЕ работает с Kimi K2.5
text = tokenizer.decode(output_ids, skip_special_tokens=True)
# Все равно получите теги в выводе

Правильно:

# Сначала декодируем, потом удаляем теги
raw_text = tokenizer.decode(output_ids)
# Кастомная очистка тегов Kimi
clean_text = re.sub(r'<[^>]+>', '', raw_text)

Ошибка 2: Не указаны stop-токены для tool calls. Модель будет генерировать бесконечно, ожидая закрывающих тегов.

Если вы видите бесконечную генерацию или обрыв ответа - проверьте стоп-токены. Kimi K2.5 требует явного указания , и .

Оптимизация для H200: что еще нужно знать

На кластере 8x H200 Kimi K2.5 показывает странное поведение, если не настроить коммуникацию между GPU. Добавьте эти параметры:

# Для vLLM на H200
export NCCL_IB_HCA=mlx5_0,mlx5_1,mlx5_2,mlx5_3
export NCCL_IB_GID_INDEX=3
export NCCL_IB_TC=136
export NCCL_IB_QPS_PER_CONNECTION=8
export NCCL_SOCKET_IFNAME=eth0

# Запуск с оптимизациями для H200
python -m vllm.entrypoints.openai.api_server \
    --model moe-community/Kimi-K2.5-8x7B \
    --tensor-parallel-size 8 \
    --gpu-memory-utilization 0.92 \  # Не 0.95! H200 нужен запас
    --max-parallel-loading-workers 4 \
    --disable-custom-all-reduce \
    --enforce-eager \
    --pipeline-parallel-size 1  # Для MoE моделей всегда 1

Почему --gpu-memory-utilization 0.92, а не 0.95? H200 с Kimi K2.5 иногда страдает от фрагментации памяти при высокой утилизации. 0.92 дает буфер для маневров.

Проверка решения: тестовые запросы

После настройки проверьте систему этими запросами:

  • Базовый запрос: "Привет, как дела?" - должен вернуть нормальный ответ без тегов
  • Запрос с reasoning: "Реши задачу: у Васи 5 яблок, у Пети на 3 больше. Сколько всего?" - может содержать <thinking>, но в очищенном выводе его не должно быть
  • Tool call запрос: "Какая погода в Лондоне?" - может вызвать tool call, который должен быть извлечен парсером
  • Длинный контекст: Запрос на 20к токенов - проверка стабильности генерации

Если все работает - вы победили капризную архитектуру Kimi K2.5. Если нет - проверьте версию vLLM/SGLang. На 28.01.2026 нужны vLLM >= 0.4.3 и SGLang >= 0.3.0 с поддержкой MoE-моделей.

А что если проблема не в настройках?

Бывает, что проблема глубже. Если после всех настроек Kimi K2.5 все равно выдает (no content):

  1. Проверьте загрузку весов: иногда часть экспертов MoE не загружается
  2. Убедитесь, что используете актуальную версию модели (на 28.01.2026 это Kimi-K2.5-8x7B, а не более ранние версии)
  3. Проверьте совместимость CUDA версий: H200 требует CUDA 12.4+
  4. Посмотрите логи vLLM на предмет ошибок инициализации экспертов

Если модель загружена частично (не все эксперты), она может генерировать мусор или молчать. В логах ищите Loaded expert - должно быть 56 экспертов для 8x7B конфигурации.

💡
Kimi K2.5 на H200 иногда требует перезапуска vLLM после нескольких часов работы. Память GPU фрагментируется из-за динамической активации экспертов MoE. Профилактический перезапуск раз в 12 часов решает 90% странных проблем.

Похожие проблемы бывают и с другими сложными моделями. Если вы сталкивались с прерыванием генерации в Claude Code или бесконечным reasoning в GLM 4.7, принцип решения тот же: понять внутреннюю структуру модели и настроить стоп-токены под нее.

Архитектура MoE, которую использует Kimi K2.5, вообще создает много уникальных проблем. Если хотите глубже разобраться в экономии памяти на таких моделях, посмотрите как MoE меняет подход к VRAM.

И последнее: не верьте, что однажды настроив Kimi K2.5, вы решили проблему навсегда. Модели обновляются, vLLM и SGLang выпускают новые версии. То, что работает сегодня, может сломаться завтра после обновления. Держите под рукой логи, мониторьте вывод и будьте готовы к тому, что через месяц придется настраивать все заново. Такая цена работы с cutting-edge моделями на 28.01.2026.