Сейчас все в сфере искусственного интеллекта говорят о самосовершенствующихся системах. Агентах, которые учатся на своих ошибках, циклах, которые автоматически улучшают работу, и системах, совершенствующихся за ночь, пока вы спите.
Большая часть этого — либо высокоразвитые исследования лабораторий ИИ, либо маркетинговая шумиха.
В статье «Не зацикливайте задержку: где место циклов агентов в голосовом ИИ» Винс объяснил уникальную архитектуру, необходимую для циклических агентов в голосовом вызове: поддерживайте путь живого звонка быстрым и ограниченным, а работу по улучшению переносите в автономный режим, где есть время выполнить ее должным образом.
Затем в статье «Добавление инструментов и перевода на оператора в голосового агента Vonage + Deepgram» мы создали голосового агента с базой данных SQLite, полной телефонных звонков. Эта база данных отслеживает транскрипты, результаты работы инструментов, тайм-ауты и переводы. Это реальные свидетельства того, как ведет себя ваш агент, но прямо сейчас они просто лежат без дела.
Этот туториал объединяет оба подхода для создания этого автономного слоя. Это не автономное самосовершенствование. Никакой агент не переписывает себя за ночь. Это что-то более простое и осязаемое: способ проанализировать данные звонков ваших пользователей, найти то, что ломается или работает некорректно, и принять осознанное решение о том, что исправить в следующий раз.
Вам не нужно писать код в этом туториале. Вы клонируете репозиторий, выполните три команды и увидите, как набор тестов для регрессии растет на основе реальных данных звонков. Затем мы разберем, как это работает «под капотом».
Кратко: Полный проект доступен на GitHub.
Кратко: Полный проект доступен на .
После каждого звонка агент записывает данные в SQLite. Автономный цикл считывает эти записи, находит паттерны и ждет, пока человек решит, что делать дальше.
Предварительные требования
Вам потребуется:
- Часть 1 этой серии должна быть настроена и работать (репозиторий GitHub, туториал). Циклы считывают записи звонков, которые создает агент.
Часть 1 этой серии должна быть настроена и работать (GitHub репозиторий, туториал). Циклы считывают записи звонков, которые создает агент.
- sqlite3 в вашем системном пути (PATH) для команд подтверждения.
в вашем системном пути (PATH) для команд подтверждения.
Что вы сделаете за 10 минут
- Запустите npm run eval для четырех предварительно созданных базовых кейсов и увидите зеленый результат
Запустите npm run eval для четырех предварительно созданных базовых кейсов и увидите зеленый результат
- Запустите npm run review для синтетических записей звонков и посмотрите, как цикл предлагает новые тестовые кейсы
Запустите npm run review для синтетических записей звонков и посмотрите, как цикл предлагает новые тестовые кейсы
- Подтвердите предложение и снова запустите npm run eval. Набор тестов пополнится одним кейсом, полученным на основе реальных данных звонков
Подтвердите предложение и снова запустите npm run eval. Набор тестов пополнится одним кейсом, полученным на основе реальных данных звонков
Необязательная локальная панель мониторинга считывает данные из той же базы данных SQLite, что и скрипты CLI. Предложения, ожидающие проверки, находятся слева, а история запусков оценки и последние звонки — справа. Запустите npm run dashboard из директории loops/, чтобы запустить ее.
Ознакомьтесь с бонусным разделом после заключения, чтобы узнать, как запустить пользовательский интерфейс панели мониторинга.
Ознакомьтесь с бонусным разделом после заключения, чтобы узнать, как запустить пользовательский интерфейс панели мониторинга.
Демонстрация с тремя запусками
Установка зависимостей
Демонстрация начинается изнутри репозитория из первой части. Если вы его не переименовывали, он должен называться blog-voice-node_deepgram_ai_tools.
Запуск 1: С предварительно созданными базовыми данными
В репозиторий встроено четыре созданных вручную кейса для оценки, охватывающих основные поведения агента: успешный поиск, тайм-аут, результат not_found и перевод на выставление счетов.
Четыре кейса, все зеленые. Это исходная база. Запускайте ее перед каждым изменением агента.
Теперь загрузите синтетические записи звонков и посмотрите, что обнаружит цикл проверки:
Посмотрите, что находится в ожидании:
Примечания представляют собой простые вопросы: «3 абонента спрашивали о возвратах и были переведены. Является ли перевод правильным поведением или этот агент должен сам обрабатывать возвраты?»
Подтвердите одно:
Это полный цикл проверки. Он находит паттерн в данных, а затем человек решает, стоит ли тестировать это. И набор тестов регрессии увеличился на один кейс.
Запуск 2: Что происходит, когда сказать больше нечего
Снова запустите базовый скрипт:
Снова запустите цикл проверки:
Цикл обнаружил те же четыре паттерна. Он ничего не записал. Каждый паттерн либо уже входит в набор тестов, либо уже находится в очереди. Это именно то, чего вы ждете от цикла, который запускаете регулярно. Он должен становиться тише по мере того, как вы реагируете на то, что он находит.
Отклонение предложения демонстрирует другой путь:
«Отклонено» означает «не сейчас». Если тот же паттерн продолжает появляться в новых звонках, цикл спрашивает снова.
Запуск 3: Реальные телефонные звонки
Запустите агента из Части 1 и сделайте три отдельных звонка:
Звонок
Что сказать
Что это генерирует
«Где заказ А1001?»
результат: завершено — нет предложений
«Я хочу вернуть свой заказ»
результат: перевод, причина_перевода: возвраты
«Я хочу отменить свой заказ»
результат: перевод, причина_перевода: отмена
Сделайте каждый из них отдельным звонком. И звонки 2, и 3 переведут вас на живого человека по номеру, указанному в файле окружения.
После звонков проверьте, что было записано:
Затем запустите проверку:
Звонок 1 ничего не сгенерировал. Паттерн возвратов уже был учтен. Звонок об отмене был проигнорирован: одной точки данных недостаточно для паттерна.
Сделайте второй звонок об отмене, а затем снова запустите проверку:
Два звонка, тот же результат. Цикл выдал предложение.
Теперь набор тестов для оценки включает кейс отмены заказа, полученный на основе реального телефонного звонка.
Что цикл нашел, а что оставил для вас
Самым интересным моментом в Запуске 3 было то, что первый звонок об отмене был проигнорирован. Цикл обнаружил паттерн. Он ничего не записал. Этот порог выполняет реальную работу. Это разница между сигналом и шумом.
Предложение, которое он в итоге написал, не предлагало создавать инструмент отмены. Оно спрашивало, является ли текущее поведение намеренным. Это предел его возможностей.
Подтверждение фиксирует текущее поведение. Отклонение сигнализирует о том, что вы хотите что-то изменить. Когда вы в конечном итоге создадите инструмент отмены и развернете его, следующий запуск проверки выявит новый паттерн.
Цикл находит вещи. Вы решаете, что они означают.
Стоит попробовать еще одну вещь: спросить о двух разных заказах во время одного телефонного звонка. Агент обрабатывает первый и выдает ошибку на втором. Политика одного запроса работает так, как задумано, но абонент, который этого не знает, просто слышит сбой. Это именно тот пробел, который цикл выявляет со временем, когда с ним сталкивается достаточное количество абонентов.
Как это работает
Краткий обзор нашего голосового агента
Ранее мы создали ограниченного по времени голосового агента для проверки статуса заказа. Абонент дозванивается, спрашивает о заказе, и агент ищет его. У агента есть тайм-аут 1500 мс, детерминированный резервный вариант в случае сбоя поиска и реальный перевод звонка для запросов, с которыми агент не может справиться.
Ключевой особенностью, добавленной в Части 1, стала база данных записей звонков. После каждого звонка агент записывает одну строку в SQLite:
Каждое взаимодействие записывается с точными словами абонента. В этом туториале это используется в качестве основы для дальнейшей работы.
Что мы строим
Три вещи:
- Программа запуска регрессии (npm run eval). Представьте это как автоматически выполняемый контрольный список. Вы определяете набор тестовых разговоров: «если абонент говорит это, агент должен сделать то». Программа запуска проигрывает каждый из них через логику агента и сообщает вам, если что-то перестало работать. Запускайте это перед изменением промпта, инструмента или чего-либо еще.
Средство запуска регрессионных тестов (npm run eval). Представьте это как чек-лист, который запускается автоматически. Вы определяете набор тестовых диалогов: «если абонент говорит это, агент должен сделать то». Среда запуска проигрывает каждый из них через логику агента и сообщает вам, если что-то перестало работать. Запускайте это перед изменением промпта, инструмента или чего-либо еще.
- Цикл проверки транскриптов (npm run review). Он просматривает сохраненные записи звонков, выявляет закономерности (абоненты неоднократно спрашивают о возвратах, тайм-ауты при поиске происходят чаще обычного) и представляет их в виде предложений для ваших действий. Он никогда ничего не меняет автоматически. Он просто сообщает о том, что обнаружил, и ждет решения человека.
Цикл проверки транскриптов (npm run review). Он просматривает сохраненные записи звонков, выявляет закономерности (абоненты неоднократно спрашивают о возвратах, тайм-ауты при поиске происходят чаще обычного) и представляет их в виде предложений для ваших действий. Он никогда ничего не меняет автоматически. Он просто сообщает о том, что обнаружил, и ждет решения человека.
- Скрипт начального заполнения (npm run seed-calls). Он загружает набор реалистичных фальшивых записей звонков, чтобы вы могли запустить всю демо-версию, не совершая предварительно реальных телефонных звонков.
Скрипт начального заполнения (npm run seed-calls). Он загружает набор реалистичных фальшивых записей звонков, чтобы вы могли запустить всю демо-версию, не совершая предварительно реальных телефонных звонков.
Средство запуска регрессии и цикл проверки используют одну базу данных SQLite совместно с Частью 1. Часть 1 записывает в нее данные после каждого звонка. Часть 2 считывает из нее данные при каждом запуск скриптов. Части 1 не обязательно работать, чтобы использовать Часть 2. Вы можете запустить набор тестов или цикл проверки в любое время для любых уже сохраненных звонков.
Что мы не строим (и почему)
Несколько вещей были намеренно исключены из этого приложения.
Никаких LLM в циклах. Цикл проверки находит закономерности путем подсчета и группировки данных. Он использует SQL-запросы, а не вызовы моделей. Это сделано намеренно. Рецензент на базе LLM теоретически мог бы выявлять более тонкие закономерности, но он также вел бы себя по-разному при каждом запуск, стоил денег при каждой проверке и потенциально мог галлюцинировать предложения, которые не имеют смысла для вашего конкретного агента. Циклы детерминированы. Вы можете в точности проверить, что именно они нашли и почему.
Никаких заданий cron для запуска цикла. Цикл проверки запускается, когда вы его запускаете. В продакшн-среде его следовало бы запланировать. Возможно, вы захотите запускать его еженедельно, после изменения модели или всякий раз, когда уровень отказов превышает пороговое значение. Для учебного пособия команда CLI, которой вы управляете, проще для понимания и проще в расширении.
Логика цикла встроена в проект агента, а не в отдельное приложение. Циклы находятся в директории loops/ внутри исходного репозитория агента. У них есть свои собственные package.json и node_modules, поскольку у них другие зависимости. Но они разделяют репозиторий Части 1, потому что напрямую импортируют фактическую логику агента. Этот импорт — главная цель: средство запуска регрессии проигрывает кейсы через те же функции classifyHandoffReason() и createToolPolicy(), которые использует живой агент. Отдельный репозиторий потребовал бы публикации Части 1 в качестве пакета, что добавляет сложности без какой-либо пользы для учебника.
Никакого автоматического одобрения. Цикл проверки создает предложения. Человек решает, какие из них добавить в набор регрессионных тестов. Каждое предложение начинается как находящееся на рассмотрении (pending). Ничто не становится тестовым примером без осознанной команды UPDATE eval_cases SET status = 'added'. Это граница ответственности цикла, и мы вернемся к тому, почему эта граница важна.
Схема тестовых кейсов оценки (Eval Case Schema)
Тестовый кейс оценки — это единица работы для средства запуска регрессии. Он описывает одно действие, которое должен выполнить агент, и предоставляет средству запуска фальшивый результат инструмента для внедрения, поэтому тест никогда не касается реальной бэкенд-системы или живого телефонного звонка.
Тестовые кейсы оценки поступают из двух источников. Первый — это те кейсы, которые вы пишете сами до того, как у вас появятся реальные данные. Они находятся в loops/eval-cases/seed-cases.json.
Второй источник — это цикл проверки. После накопления достаточного количества реальных звонков он находит закономерности и записывает предложения. Эти предложения представляют собой тестовые кейсы со статусом status: 'pending'. Они находятся в очереди до тех пор, пока человек их не одобрит. Отдельный экземпляр не создает тестовый пример. Цикл ждет появления закономерности по нескольким звонкам, прежде чем что-либо предложить, и человек должен одобрить это, прежде чем средство запуска подхватит его.
Вот как выглядит один из них (из loops/eval-cases/seed-cases.json, один из четырех созданных вручную кейсов):
input — это то, что сказал абонент. Поля expected_* — это то, что агент должен сделать в ответ. mock_tool_result — это фальшивый ответ, который средство запуска передает агенту вместо вызова настоящего бэкенда.
У агента есть один инструмент: getOrderStatus. Во время реального звонка он запрашивает у бэкенда статус заказа и получает результат. Во время повторного прогона средство запуска полностью пропускает бэкенд и напрямую внедряет то, что находится в mock_tool_result.
Три кейса показывают, как это работает:
- Известный заказ. Средство запуска передает этот результат агенту и проверяет, что звонок завершился как выполненный и инструмент был вызван.
Известный заказ. Средство запуска передает этот результат агенту и проверяет, что звонок завершился как выполненный и инструмент был вызван.
- Медленный заказ. Средство запуска симулирует срабатывание тайм-аута в 1500 мс. Агент должен сказать «У меня возникли проблемы с получением этого заказа прямо сейчас» и завершить работу с результатом outcome: fallback. Если он выдумывает ответ вместо этого, тест завершается неудачно.
Медленный заказ. Средство запуска симулирует срабатывание тайм-аута в 1500 мс. Агент должен сказать «У меня возникли проблемы с получением этого заказа прямо сейчас» и завершить работу с результатом outcome: fallback. Если он выдумывает ответ вместо этого, тест завершается неудачно.
- Запрос вне сферы действия. Инструмент никогда не должен запускаться для запросов о возврате или выставлении счетов. Средство запуска проверяет, что агент перевел звонок на человека и значение expected_tool_called равно false. Если агент все равно вызвал getOrderStatus, тест это обнаруживает.
Запрос вне сферы действия. Инструмент никогда не должен запускаться для запросов о возврате или выставлении счетов. Средство запуска проверяет, что агент перевел звонок на человека и значение expected_tool_called равно false. Если агент все равно вызвал getOrderStatus, тест это обнаруживает.
Поля expected_* оценивают только результат. Если бы expected_tool_called определяло, нужно ли вызывать инструмент, тест сравнивал бы поле само с собой и никогда не смог бы завершиться неудачно.
"__timeout__" сохраняется как простая строка, а не как объект JSON, потому что тайм-аут — это отсутствие результата. Не существует структуры OrderResult, которая означала бы «сработал тайм-аут». Это также четко читается в терминале: sqlite3 ../data/calls.db "SELECT mock_tool_result FROM eval_cases WHERE id = 'seed-002';" возвращает __timeout__, а не двоичный JSON-объект.
Схема также использует ON CONFLICT(id) DO NOTHING вместо INSERT OR IGNORE для идеопотного заполнения данными. Разница невелика, но она реальна: INSERT OR IGNORE молча проглатывает любые нарушения ограничений, включая опечатку в expected_outcome. ON CONFLICT(id) DO NOTHING точно указывает цель конфликта, поэтому допускается только дублирование id. Любое другое ограничение остается громким.
Средство запуска регрессии
Runner загружает все тестовые сценарии из базы данных и прогоняет каждый из них через тот же код принятия решений, который использует активный агент: проверка области видимости, политика инструментов, внедрение фиктивных результатов. Но он делает это без открытия сокета или совершения телефонного звонка.
Функции classifyHandoffReason и createToolPolicy импортируются из Части 1 и вызываются напрямую. Таким образом, функция, которая выполняется во время теста, — это та же самая функция, которая работает во время реального звонка. Если бы набор тестов создавал свою собственную функцию, тесты продолжали бы проходить даже при изменении активного классификатора. Вместо этого мы тестируем функции, изменяя сам тестовый сценарий.
Одна вещь, которую набор тестов не может проверить: результат ошибки. Это происходит только тогда, когда Deepgram разрывает соединение во время звонка, что требует реального сокета. Путь повторной попытки при ошибке транспорта имеет ту же проблему.
Набор тестов охватывает логику принятия решений: что агент делает с результатом. Тесты из Части 1 охватывают техническую реализацию: что происходит, когда само соединение дает сбой. И то, и другое необходимо.
Цикл проверки транскриптов
Цикл проверки считывает записи звонков и ищет четыре шаблона, называемые детекторами. Поскольку он никогда не вызывает модель ИИ и не выполняет семантическое сопоставление, вы получаете честный отчет о точных действиях. Когда детектор находит достаточное количество совпадающих звонков, цикл записывает предложение в таблицу eval_cases.
Сначала запросите реальную таблицу
Перед написанием детектора выполните этот запрос к вашей реальной базе данных:
Вспомните пример с медленным заказом из схемы тестового сценария: mock_tool_result: "__timeout__". Этот маркер __timeout__ — то, что использует код TypeScript в памяти. Но в записи звонка сохраняется просто строка timeout, без подчеркиваний и без кавычек, как в JSON.
Если вы напишете детектор, который ищет "kind":"timeout" в сохраненных записях, он никогда ничего не найдет. Запрос выполняется нормально, возвращает ноль строк, и ничто не говорит вам о том, что что-то не так. В итоге вы получаете детектор, который молча ничего не делает, и любые тесты, написанные с тем же предположением, тоже проходят.
Выполнение запроса заранее показывает вам, что именно сохранено, прежде чем вы напишете хотя бы одну строку кода детектора.
Четыре детектора
После подтверждения структуры реальных данных, вот четыре детектора. Один запрос загружает последние 50 звонков, и каждый детектор получает этот же массив. Они фильтруют и группируют данные на обычном TypeScript, поэтому вы можете протестировать любой из них на нескольких тестовых записях.
Детектор 3 группирует данные по точным словам вызывающего абонента, а не пытается найти похожие фразы. Подход, который группирует по смыслу, потребовал бы либо вызова LLM, либо специальной библиотеки сходства, и было бы сложнее проверить, правильно ли он работает. Точное знание того, что находит каждый детектор, ценнее, чем нахождение чуть большего количества вещей.
Что на самом деле говорят предложения
Когда детектор находит достаточное количество совпадающих звонков, он записывает предложение в eval_cases со статусом 'pending'. Примечание к предложению — это вопрос на обычном языке, а не диагноз:
«3 абонента спрашивали о возвратах и были переведены. Является ли перевод правильным поведением, или этот агент должен обрабатывать возвраты?» «2 абонента спрашивали о втором заказе в том же звонке и достигли лимита в один поиск. Должен ли этот агент поддерживать несколько поисков за один звонок?»
«3 абонента спрашивали о возвратах и были переведены. Является ли перевод правильным поведением, или этот агент должен обрабатывать возвраты?»
«2 абонента спрашивали о втором заказе в том же звонке и достигли лимита в один поиск. Должен ли этот агент поддерживать несколько поисков за один звонок?»
Цикл выявляет закономерность. Человек решает, что она означает.
Карточка предложения из очереди на проверку. Примечание — это вопрос на обычном языке, написанный детектором, а не диагноз. «Добавить в набор» фиксирует текущее поведение. «Отклонить» сигнализирует о том, что вы хотите его изменить.
Никаких дубликатов предложений
Перед написанием предложения цикл проверки проверяет, существует ли уже одно для того же ввода и ожидаемого результата. Если предложение уже находится в ожидании или добавлено, цикл пропускает его. Вы можете запускать npm run review каждый день по одним и тем же записям звонков, и очередь не будет заполняться дубликатами.
Единственным исключением является отклоненное предложение. Если вы отклонили шаблон, а то же самое продолжает происходить в новых звонках, цикл предложит его снова. Одно «не сейчас» не заглушает шаблон, который продолжает повторяться.
Это также означает, что очередь сокращается по мере того, как вы работаете с ней. Цикл, который игнорирует то, что вы уже сделали, приучил бы вас перестать читать вывод.
Что дальше
В будущей статье блога мы расскажем, что происходит после предложений: подключение реальных баз данных, добавление инструментов по одному и использование набора тестов в качестве страховки для каждого изменения.
Несколько вещей, которые вы могли бы добавить к этой настройке до этого:
- Запланируйте цикл проверки. Сейчас он запускается, когда вы его запускаете. Cron-задача, которая запускает npm run review каждое утро понедельника и отправляет вам результат, сделает его частью вашего регулярного рабочего процесса без какой-либо дополнительной инфраструктуры.
Запланируйте цикл проверки. Сейчас он запускается, когда вы его запускаете. Cron-задача, которая запускает npm run review каждое утро понедельника и отправляет вам результат, сделает его частью вашего регулярного рабочего процесса без какой-либо дополнительной инфраструктуры.
- Добавьте npm run eval в CI. Runner завершается с кодом 1 при любой ошибке. Добавьте его в проверки ваших pull request, и каждое изменение агента будет автоматически тестироваться на полном наборе тестов перед слиянием.
Добавьте npm run eval в CI. Runner завершается с кодом 1 при любой ошибке. Добавьте его в проверки ваших pull request, и каждое изменение агента будет автоматически тестироваться на полном наборе тестов перед слиянием.
- Используйте LLM для аннотирования предложений, а не для их генерации. Детекторы детерминированы, что является правильным свойством для этапа обнаружения. LLM, которая читает предложение и предлагает конкретное следующее действие (например, «рассмотрите возможность добавления инструмента getReturnStatus()»), была бы дополнением, не делая основной цикл непредсказуемым. Обнаружение шаблонов остается проверяемым, а предложения становятся необязательным контекстом.
Используйте LLM для аннотирования предложений, а не для их генерации. Детекторы детерминированы, что является правильным свойством для этапа обнаружения. LLM, которая читает предложение и предлагает конкретное следующее действие (например, «рассмотрите возможность добавления инструмента getReturnStatus()»), была бы дополнением, не делая основной цикл непредсказуемым. Обнаружение шаблонов остается проверяемым, а предложения становятся необязательным контекстом.
- Автоматизируйте этап утверждения. Каждое предложение, которое вы одобряете или отклоняете, — это размеченный пример того, что стоит тестировать, а что нет. Как только у вас их будет достаточно, само решение об утверждении станет тем, чему агент может научиться. Цикл, который вы здесь построили, уже собирает данные для этого. Это уже другая тема для обучения.
Автоматизируйте этап утверждения. Каждое предложение, которое вы одобряете или отклоняете, — это размеченный пример того, что стоит тестировать, а что нет. Как только у вас их будет достаточно, само решение об утверждении станет тем, чему агент может научиться. Цикл, который вы здесь построили, уже собирает данные для этого. Это уже другая тема для обучения.
- Экспортируйте предложения обратно в JSON для командного рассмотрения. Рабочий процесс утверждения через SQLite отлично подходит для одного разработчика. Команде, которая хочет рассматривать предложения в рамках пулл-реквеста, пригодится скрипт, экспортирующий ожидающие рассмотрения предложения в JSON-файл, позволяющий рецензентам оставлять комментарии и утверждать их в PR, а затем импортирующий подтверждения обратно в базу данных.
Экспортируйте предложения обратно в JSON для командного рассмотрения. Рабочий процесс утверждения через SQLite отлично подходит для одного разработчика. Команде, которая хочет рассматривать предложения в рамках пулл-реквеста, пригодится скрипт, экспортирующий ожидающие рассмотрения предложения в JSON-файл, позволяющий рецензентам оставлять комментарии и утверждать их в PR, а затем импортирующий подтверждения обратно в базу данных.
Заключение
Оригинальное руководство от Vonage и Deepgram позволило создать разговорного агента для телефонного звонка. Затем мы поручили этому агенту реальную задачу, добавив обработку сбоев и записи звонков. В этом учебном пособии были добавлены два скрипта командной строки, которые считывают эти записи и преобразуют их в данные, с которыми можно работать.
Инструмент запуска регрессионных тестов гарантирует, что то, что уже работает, продолжает работать. Цикл ревью помогает обнаружить пробелы в возможностях вашего агента: запросы, с которыми он постоянно сталкивается, но не может корректно обработать. Вместе они дают возможность целенаправленно улучшать агента, внося по одному одобренному изменению за раз.
Вы создали систему для анализа реальных записей звонков и выявления реальных шаблонов поведения пользователей. Решение о том, что с ними делать, по-прежнему остается за человеком. Как и должно быть.
Бонус: Панель управления
Если вам нужен визуальный интерфейс для очереди предложений и истории оценок, репозиторий включает в себя опциональную локальную панель управления. Запустите npm run dashboard из директории loops/ и откройте http://localhost:4000/. На ней отображаются те же данные, что и в командах sqlite3: предложения, ожидающие решения, история запусков оценки и недавние звонки. Для прохождения руководства это не обязательно.
Опциональная локальная панель управления считывает данные из той же базы данных SQLite, что и скрипты CLI. Предложения, ожидающие рассмотрения, находятся слева, а история запусков оценки и последние звонки — справа. Запустите npm run dashboard из директории loops/, чтобы запустить её.












