fix(jivo-bot): мягкий ИЛИ-поиск при пустом строгом + стоп-тема «у меня … баланс» с разрывом слов

Два живых бага демо 12.07.2026 (TDD, 34/34 зелёные):
- KnowledgeSearch: websearch_to_tsquery требует ВСЕ слова вопроса; «зачем он нужен»
  ронял поиск в пусто → добавлен мягкий запасной проход через ИЛИ (plainto | )
- BotAnswerService STOP_PATTERN: «какой у меня сейчас баланс» проскакивал стоп-тему
  (слова между «у меня» и «баланс») → допускаем до 3 слов разрыва, сч[её]т
- Протокол: журнал 12.07 (аккаунт Jivo, канал, цвет, ключ zofyNxmnRP, стенд-проверка)
- Скриншоты живой проверки виджета в findings

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Дмитрий
2026-07-12 09:30:53 +03:00
parent d9bfb4f360
commit ba3108e94c
12 changed files with 64 additions and 9 deletions
+21 -1
View File
@@ -27,8 +27,28 @@ it('стоп-тема (мой баланс) → эскалация без пох
$answer = app(BotAnswerService::class)->answer('какой у меня баланс?');
// Текст именно стоп-эскалации («передаю ему диалог»), а не «в инструкции нет ответа»:
// до фикса 12.07.2026 вопрос проскакивал стоп-гейт и эскалировал лишь пустым поиском.
expect($answer->escalate)->toBeTrue()
->and($answer->text)->toContain('специалисту');
->and($answer->text)->toContain('передаю ему диалог');
Http::assertNothingSent();
});
it('стоп-тема со словами между «у меня» и «баланс» → эскалация ДО поиска и LLM', function () {
// Живой баг 12.07.2026: «какой у меня сейчас баланс?» проскакивал мимо стоп-темы,
// поиск находил статью про пополнение и бот отвечал общими словами вместо передачи
// человеку. Статья про баланс в базе ЕСТЬ — эскалация обязана сработать раньше поиска.
KnowledgeChunk::create([
'source_path' => 'help/b.md', 'title' => 'Как пополнить баланс', 'tour' => 'top-up-balance',
'topics' => 'пополнить, баланс', 'chunk_index' => 0,
'content' => 'Пополнить баланс: раздел Биллинг, кнопка Пополнить.',
]);
Http::fake();
$answer = app(BotAnswerService::class)->answer('какой у меня сейчас баланс?');
expect($answer->escalate)->toBeTrue()
->and($answer->text)->toContain('передаю ему диалог');
Http::assertNothingSent();
});