Files
portal/app/tests/Feature/Bot/ToursRelevanceTest.php
T
Дмитрий 6a3a9e5d3a fix(bot): хвосты — экскурсии по теме и только из кабинета, приветствие, доступы
1. Экскурсии «Показать на портале» включены обратно. Прежнее правило давало ссылку,
   если совпало ХОТЯ БЫ ОДНО слово, — бот вёл на уведомления в ответ на «первое
   сообщение подряд». Теперь два условия: реплика должна быть ВОПРОСОМ и слово из
   него должно попасть в заголовок статьи или её синонимы. Плюс экскурсия предлагается
   ТОЛЬКО клиенту из кабинета: гостю с лендинга ссылка внутрь кабинета бесполезна.
   Ссылки в окошке стали кликабельными (без innerHTML — текст ответа от модели).

2. На «hello» бот отвечал «в моей инструкции нет ответа» и просил телефон: заготовка
   знала только русские приветствия. Приветствие — не вопрос, эскалировать его глупо.

3. «Хочу, чтобы менеджер не видел биллинг» бот принимал за просьбу позвать НАШЕГО
   менеджера и уводил к специалисту, хотя ответ есть в статье, — и через реплику сам
   же отвечал, отчего выглядел противоречащим себе. Правило про доступ дополнено.
   Статья «Команда и доступ» дописана: сколько человек можно пустить, почему нельзя
   скрыть раздел, баланс общий на всех.

Тесты 161/161.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 10:14:52 +03:00

65 lines
3.7 KiB
PHP

<?php
declare(strict_types=1);
use App\Models\KnowledgeChunk;
use App\Services\Bot\KnowledgeSearch;
use Illuminate\Foundation\Testing\RefreshDatabase;
uses(RefreshDatabase::class);
/**
* Экскурсия «Показать на портале» цеплялась, если у вопроса и статьи совпало ХОТЯ БЫ ОДНО
* слово, — поэтому на болтовню («первое сообщение подряд») бот предлагал экскурсию по
* уведомлениям, а на вопрос про поддержку уводил в другой раздел (живая проверка 13.07.2026,
* до этого два десятка таких ссылок в прогоне 12.07).
*
* Правило: экскурсия только тогда, когда реплика — ВОПРОС (есть «?» или вопросительное
* слово) И слово из него попало в заголовок статьи или её синонимы. «Показать на портале»
* имеет смысл, только если человек о чём-то спрашивает.
*/
beforeEach(function () {
KnowledgeChunk::create([
'source_path' => 'help/create.md', 'title' => 'Как создать проект', 'tour' => 'create-project',
'topics' => 'создать проект, новый проект, запустить сбор', 'chunk_index' => 0,
'content' => 'Проект создаётся в разделе «Проекты», кнопка «Создать».',
]);
KnowledgeChunk::create([
'source_path' => 'help/notify.md', 'title' => 'Уведомления портала', 'tour' => 'notifications',
'topics' => 'уведомления, сообщение, письмо, колокольчик', 'chunk_index' => 0,
'content' => 'Уведомления приходят в колокольчик и на почту.',
]);
});
it('на болтовню без сути экскурсию не предлагает', function () {
$search = app(KnowledgeSearch::class);
$notify = KnowledgeChunk::where('tour', 'notifications')->firstOrFail();
foreach (['привет', 'ага', 'слушаю', 'спасибо'] as $chatter) {
expect($search->isOnTopic($notify, $chatter))->toBeFalse();
}
});
it('реплика не-вопрос экскурсию не получает, даже если слово совпало', function () {
$search = app(KnowledgeSearch::class);
$notify = KnowledgeChunk::where('tour', 'notifications')->firstOrFail();
// «сообщение» есть в ключевых словах статьи про уведомления — но человек ни о чём
// не спрашивает. Именно так бот и водил на экскурсию по уведомлениям (13.07.2026).
expect($search->isOnTopic($notify, 'первое сообщение подряд'))->toBeFalse();
});
it('вопрос по теме статьи экскурсию получает', function () {
$search = app(KnowledgeSearch::class);
$create = KnowledgeChunk::where('tour', 'create-project')->firstOrFail();
expect($search->isOnTopic($create, 'как создать проект?'))->toBeTrue();
});
it('совпадение по двум ключевым словам тоже считается', function () {
$search = app(KnowledgeSearch::class);
$notify = KnowledgeChunk::where('tour', 'notifications')->firstOrFail();
expect($search->isOnTopic($notify, 'куда приходит письмо и колокольчик?'))->toBeTrue();
});