fix(bot): гостю больше нельзя приписывать его ступень, баланс и заявки

Проверка на боевом 14.07.2026 (сразу после выката фикса цен): гость — человек не вошёл,
никаких заявок у него нет — показал скриншот с 50 ₽, и бот ответил: «Вы сейчас на второй
ступени (50 ₽), потому что в этом месяце уже получили заявки». Выдумка про человека.

Причина: сторож сверяет личные цифры только у ВОШЕДШЕГО (карточка фактов). У гостя
карточки нет — и модель фантазировала свободно.

Теперь AnswerGuard знает, посчитаны ли личные цифры собеседника. Если нет (гость) —
режет фразы, утверждающие его нынешнее состояние: «вы сейчас на… ступени», «у вас
на балансе N», «вы уже получили заявки». Общие объяснения не трогаются: «вы платите
только за полученные заявки», «когда наберёте объём — перейдёте на следующую ступень».

Тесты бота: 242/242 (+2 новых).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Дмитрий
2026-07-14 15:49:53 +03:00
parent 7b7718d319
commit a2bd2dbc8b
3 changed files with 87 additions and 2 deletions
@@ -203,6 +203,47 @@ it('уверенным считается совпадение хотя бы н
expect($search->confidentTopicMatch($sbor, 'сколько стоит сбор источников?'))->toBeTrue();
});
it('гостю бот не приписывает его ступень и баланс — у гостя их нет', function () {
twoPricingGrids();
KnowledgeChunk::create([
'source_path' => 'help/tarify.md', 'title' => 'Тарифы — сколько стоит заявка', 'tour' => null,
'topics' => 'тариф, цена, ступень, сколько стоит заявка', 'chunk_index' => 0,
'content' => 'Всего 7 ступеней. Сейчас сетка такая: {{тарифы}}.',
]);
// Проверка на боевом 14.07.2026: гостю (он не вошёл, заявок у него нет) бот заявил
// «Вы сейчас на второй ступени (50 ₽), потому что уже получили заявки». Выдумка.
llmAnswers('Вы сейчас на второй ступени (50 ₽), потому что в этом месяце уже получили заявки. На второй ступени цена заявки — 50 ₽, дальше она опускается.');
askBot('c-guest', 'Сколько стоит заявка на второй ступени?');
$answer = (string) BotDialog::where('chat_id', 'c-guest')->where('direction', 'out')->value('message');
// Личное состояние — вырезано.
expect($answer)->not->toContain('Вы сейчас на второй ступени')
->and($answer)->not->toContain('уже получили заявки');
// Безличное объяснение про сетку — осталось, человек не остался без ответа.
expect($answer)->toContain('50 ₽');
});
it('вошедшему клиенту его личные цифры называть можно — они из карточки фактов', function () {
twoPricingGrids();
KnowledgeChunk::create([
'source_path' => 'help/balans.md', 'title' => 'Баланс', 'tour' => null,
'topics' => 'баланс, сколько денег', 'chunk_index' => 0,
'content' => 'Баланс виден в разделе «Биллинг».',
]);
$guard = app(App\Services\Bot\AnswerGuard::class);
$facts = "ФАКТЫ КЛИЕНТА:\nбаланс: 11 025 ₽\nступень: 2";
$clean = $guard->clean('У вас сейчас на балансе 11 025 ₽.', $facts, personalFactsKnown: true);
expect($clean)->toContain('11 025 ₽');
});
it('ответ не начинается с середины фразы, когда снята отговорка «в инструкции этого нет»', function () {
KnowledgeChunk::create([
'source_path' => 'help/o-servise.md', 'title' => 'О сервисе', 'tour' => null,