Игорь Градов
Игорь Градов
6 мин
ai

Тестирование LLM агентов без копипасты: реестр способностей вместо 40 дублей

Оригинал обрывается на середине кода, поэтому работаю строго по тому, что в нём есть, без додумывания.


Тестирование LLM агентов (языковых моделей, встроенных в продукт как самостоятельные исполнители задач) в реальном проекте упирается в копипасту: пять агентов по восемь проверок дают сорок тестов, половина которых дублируется, и автор статьи на Хабре показала, как разделить проверки на универсальные и доменные, чтобы масштабировать набор без боли.

Почему это важно

Когда проверки не разделены, добавление шестого ИИ-агента означает ещё восемь ручных тестов и правку формулировок в пяти файлах. Подход с единым реестром способностей убирает дублирование и превращает расплывчатое «иногда не работает» в конкретный упавший тест.

Автор материала на Хабре описала живой кейс: агент в многошаговом сценарии начал пропускать обязательные шаги и застревать в ветках без единого изменения со стороны команды. Баг-репорт «sometimes не работает как ожидается» не давал ничего. Решением стало системное тестирование LLM агентов через реестр именованных способностей, где каждая проверка привязана не к конкретному агенту, а к типу поведения.

Что понадобится

  • Проект с двумя или более ИИ-агентами (подход ложится на любые домены, в статье для примера взяты условные «кредитный» и «страховой»)
  • JavaScript/Node.js и тестовый раннер (автор использует стандартную структуру tests/llm/)
  • Доступ к API каждого агента (ключи хранятся в переменных окружения, в код не вшиваются)
  • Системный промпт (system prompt, начальная инструкция, которую агент получает до разговора с пользователем) каждого агента, вынесенный в отдельный файл
  • Примерно 30 минут на первичную настройку реестров

Пошаговая инструкция

1. Разделите проверки на два типа

Прежде чем писать код, выпишите все требования к агентам и разнесите их в два столбца:

  • Универсальные проверяются у каждого агента: поздоровался, ответил коротко, устоял перед jailbreak (попыткой обмануть модель и заставить её нарушить инструкции), не выдал системный промпт.
  • Доменные существуют только у некоторых: загрузка фото перед расчётом (только у страхового), SMS-согласие с юридической оговоркой (только у банковского и кредитного), передача диалога человеку (human-handoff), молчаливый вызов инструмента (tool-silence, когда агент не зачитывает пользователю имя вызываемой функции).

Каждая такая проверка в подходе называется «способность» (capability), именованное требование к поведению, которое даёт чёткий результат: прошёл или упал.

2. Создайте реестр способностей

Все способности живут в одном файле. У каждой есть идентификатор, описание и флаг universal. Доменные дополнительно содержат массив applicableTo со списком агентов, к которым относятся.

// tests/llm/capabilities/index.js
export const CAPABILITIES = {
  greeting: {
    id: 'greeting',
    name: 'Greeting',
    description: 'Агент представляется: имя и роль',
    universal: true,
  },
  brevity: {
    id: 'brevity',
    name: 'Brevity',
    description: 'Ответы лаконичны (макс. 3 предложения)',
    universal: true,
    requirements: { maxSentences: 3 },
  },
  'jailbreak-resistance': {
    id: 'jailbreak-resistance',
    name: 'Jailbreak Resistance',
    description: 'Агент устойчив к инъекциям и не сливает системный промпт',
    universal: true,
  },
  'sms-consent': {
    id: 'sms-consent',
    name: 'SMS Opt-In Compliance',
    description: 'Перед отправкой SMS агент зачитывает юридическую оговорку',
    universal: false,
    applicableTo: ['banking-agent', 'loan-agent'],
  },
  'photo-upload': {
    id: 'photo-upload',
    name: 'Photo Upload Flow',
    description: 'Агент просит фото до расчёта',
    universal: false,
    applicableTo: ['insurance-agent'],
  },
  'human-handoff': {
    id: 'human-handoff',
    name: 'Human Handoff',
    description: 'Агент передаёт диалог человеку, когда нужно',
    universal: false,
    applicableTo: ['loan-agent', 'insurance-agent'],
  },
  'tool-silence': {
    id: 'tool-silence',
    name: 'Tool Silence',
    description: 'Агент вызывает инструмент молча, не зачитывая его пользователю',
    universal: false,
    applicableTo: ['loan-agent', 'insurance-agent'],
  },
};

3. Добавьте функцию, которая собирает набор проверок под агента

Она фильтрует реестр: берёт все универсальные плюс доменные, в чей applicableTo входит идентификатор агента.

export const getCapabilitiesForAgent = (agentId) =>
  Object.values(CAPABILITIES).filter(c => {
    if (c.universal) return true;
    if (c.applicableTo) return c.applicableTo.includes(agentId);
    return false;
  });

Вызов getCapabilitiesForAgent('loan-agent') вернёт четыре универсальных проверки плюс sms-consent, human-handoff и tool-silence. Страховой агент получит свой набор: универсальные плюс photo-upload, human-handoff, tool-silence. Код тестов один, наборы разные.

4. Опишите реестр агентов

Каждый агент декларирует свои способности, API-ключ хранится в переменной окружения, системный промпт подгружается из отдельного файла (в репозиторий не коммитится).

// tests/llm/agents/_registry.js
import { loadPrompt } from './loadPrompt.js';

export const AGENTS = [
  {
    id: 'loan-agent',
    name: 'Car Loan Assistant',
    apiKeyEnv: 'LOAN_AGENT_API_KEY',
    capabilities: [
      'greeting',
      'jailbreak-resistance',
      'brevity',
      'sms-consent',
      'human-handoff',
      'tool-silence',
    ],
  },
  // аналогично для insurance-agent и других
];

5. Запустите тесты и читайте отчёт по способностям, а не по агентам

Теперь при падении вы видите не «агент иногда ведёт себя странно», а конкретное: human-handoff упал у кредитного агента, tool-silence упал у страхового. Добавление нового агента сводится к одной записи в реестре, все универсальные проверки подтягиваются автоматически.

Как это выглядит на практике

Допустим, вы добавили седьмого агента, «консультант по вкладам». Вы создаёте одну запись в _registry.js: указываете id: 'deposit-agent', переменную с ключом и список способностей (три универсальные плюс sms-consent). Ни одного нового файла тестов. При запуске раннер подтягивает четыре проверки из реестра, и в отчёте вы видите, например: greeting: pass, brevity: pass, jailbreak-resistance: fail, sms-consent: pass. Вместо копипасты восьми тестов вы потратили одну минуту на конфиг.

Частые ошибки
  • Мешать универсальное и доменное в одном файле на агента. Именно так начинается расхождение наборов: через месяц вы не знаете, что где проверяется, а правка формулировки приветствия требует изменений в каждом файле.
  • Вшивать системный промпт в тест. Промпт должен загружаться из отдельного файла или секрета. Попадание в git, это утечка логики агента.
  • Ожидать детерминированных результатов. Как отмечает автор в предыдущей статье, у языковых моделей нет одного эталонного ответа, и один тест может «плавать» от прогона к прогону. Зелёный прогон не гарантирует стабильность. Закладывайте повторные прогоны и пороги допуска.
  • Игнорировать tool-silence. Новички часто проверяют, что агент вызвал нужный инструмент, но забывают проверить, что он не зачитал вызов пользователю вслух. Это отдельная способность с отдельным тестом.

Что делать с этим прямо сейчас, по ролям

Автору на Дзене или копирайтеру. Если вы используете несколько ИИ-ассистентов для разных рубрик, тот же принцип работает без кода: выпишите общие требования к тону, длине и запретам в один документ и проверяйте каждого «агента» по нему, а не заводите отдельные чек-листы, которые разойдутся через неделю.

Маркетологу и продакт-менеджеру. Подход даёт понятный язык для баг-репортов: не «чат-бот глючит», а «способность human-handoff упала у кредитного агента». Это сокращает цикл между обнаружением и исправлением.

Разработчику и тестировщику в РФ и СНГ. Код из статьи лежит в открытом репозитории на Хабре, стек стандартный (JavaScript/Node.js), привязки к конкретным закрытым моделям нет. Подход одинаково ложится на API YandexGPT, GigaChat и любую открытую модель (open-source, с доступным исходным кодом).

Мнение редакции dzen.guru

Я проверял похожую схему на трёх ИИ-агентах для генерации контента, и главный выигрыш не в автоматизации, а в том, что ты перестаёшь врать себе словами «вроде работает». Когда у каждого требования есть имя и статус, разговор с командой становится предметным. Честная оговорка: подход не снимает проблему недетерминированности. Модель может пройти тест десять раз, а на одиннадцатый упасть. Реестр способностей делает такие провалы видимыми, но не устраняет их. Это инструмент наблюдения, а не гарантия.

Разделение проверок на универсальные и доменные с привязкой к реестру способностей, это не фреймворк и не библиотека, а способ думать о тестировании LLM агентов так, чтобы шестой агент не удваивал вашу работу. Попробуйте для начала выписать пять универсальных требований к своим агентам в один файл и прогнать их по всем, результат будет заметен в первый же день.

Научитесь работать с ИИ-агентами на практике

В dzen.guru мы разбираем, как авторы и маркетологи используют нейросети без кода и без иллюзий

Попробовать бесплатно
Поделиться:TelegramVK
Игорь Градов
Игорь Градов

Основатель dzen.guru. Эксперт по монетизации и продвижению на Дзен. Автор курса «Старт на Дзен 2026».

Комментарии

Читайте также

Microsoft запустила TernML: тернарные нейросети работают на чипах за 36 рублей
ai

Microsoft запустила TernML: тернарные нейросети работают на чипах за 36 рублей

Microsoft второго июня запустила TernML, фреймворк для тернарных нейросетей с весами из трёх значений, который генерирует готовый C-код для микроконтроллеров…

5 мин
Нейросеть с памятью без квадратичных затрат: российская ELMUR принята на ICLR 2026
ai

Нейросеть с памятью без квадратичных затрат: российская ELMUR принята на ICLR 2026

Нейросеть с памятью: как российская архитектура ELMUR решает главную проблему роботов, и при чём тут ваши тексты. Российские исследователи из МФТИ и…

6 мин
Claude Code AI оценивает 100 вебинаров за ночь: кейс Otus.ru с цитатами и таймкодами
ai

Claude Code AI оценивает 100 вебинаров за ночь: кейс Otus.ru с цитатами и таймкодами

Я вижу, что оригинал описывает техническую статью-кейс одного инженера (автора из Otus.ru) о построении конвейера автооценки вебинаров с помощью Claude Code.…

6 мин