Files
portal/app/tests/Feature/Bot/ChatToursTest.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

55 lines
2.9 KiB
PHP

<?php
declare(strict_types=1);
use App\Models\KnowledgeChunk;
use App\Services\Bot\BotAnswerService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Http;
uses(RefreshDatabase::class);
/**
* Экскурсии «Показать на портале» 13.07.2026 были ВЫКЛЮЧЕНЫ: ссылки уходили не по теме
* вопроса (совпадало одно случайное слово — бот приписывал экскурсию по уведомлениям
* к реплике «первое сообщение подряд»). Правило переписано, экскурсии включены обратно.
*
* Теперь их держат два условия, и оба обязательны:
* 1. Вопрос по теме статьи (KnowledgeSearch::isOnTopic — тесты в ToursRelevanceTest).
* 2. Клиент пишет ИЗ КАБИНЕТА. Гостю с лендинга ссылка внутрь кабинета бесполезна:
* он там ещё не зарегистрирован, и показывать ему нечего.
*
* Этот сторож не даёт незаметно вернуть ссылки гостям.
*/
beforeEach(function () {
config()->set('services.bot.tours_enabled', true);
config()->set('app.url', 'https://lk.liderra.ru');
config()->set('services.yandexgpt', [
'api_key' => 'k', 'folder_id' => 'f', 'model' => 'yandexgpt-lite/latest',
'endpoint' => 'https://llm.api.cloud.yandex.net/foundationModels/v1/completion',
'timeout_seconds' => 8,
]);
Http::fake([
'llm.api.cloud.yandex.net/*' => Http::response([
'result' => ['alternatives' => [['message' => ['role' => 'assistant', 'text' => 'Проект создаётся в разделе «Проекты».']]]],
]),
]);
KnowledgeChunk::create([
'source_path' => 'help/create.md', 'title' => 'Как создать проект', 'tour' => 'create-project',
'topics' => 'создать проект, новый проект', 'chunk_index' => 0,
'content' => 'Проект создаётся в разделе «Проекты», кнопка «Создать».',
]);
});
it('клиенту из кабинета экскурсия по теме предлагается', function () {
$answer = app(BotAnswerService::class)->answer('как создать проект?', [], fromPortal: true);
expect($answer->text)->toContain('https://lk.liderra.ru/?tour=create-project');
});
it('гостю с лендинга экскурсию не предлагаем — ему нечего показывать в кабинете', function () {
$answer = app(BotAnswerService::class)->answer('как создать проект?', [], fromPortal: false);
expect($answer->text)->not->toContain('?tour=');
});