Перейти к содержанию

Поиск

Один поисковый индекс хранит заявки и статьи базы знаний. Справочный центр, форма запроса, рабочая область и API обращаются к нему, и каждый показывает спрашивающему только то, что он может читать. Он работает на библиотеке YetiSearch (SQLite FTS5 с ранжированием BM25), встроенной в Helpdesk Pro, поэтому устанавливать нечего и не нужно запускать сервис.

Где находится индекс

Индекс — один файл SQLite, user/data/helpdesk-pro/search/helpdesk.db, с маркером helpdesk.meta.json рядом. Он следует за database.sqlite.path: это папка search/ рядом с папкой базы данных, и она получает запрещающий всё .htaccess. Это производные данные: удалите их в любое время, и следующий поиск построит их снова.

Файл содержит два индекса:

Индекс Что в нём Кто ищет
hd_public Один документ на заявку (тема, первое сообщение, публичные ответы, названия меток) и один на статью базы знаний (заголовок, текст, категории и теги) Клиенты, гости (только статьи) и сотрудники
hd_internal Один документ на заявку с внутренними заметками: текст заметки и ничего больше Только сотрудники

Текст заметок записывается только в hd_internal, и клиентский код поиска не может назвать этот индекс, поэтому никакой клиентский поиск не доходит до заметки.

Кто что находит

Каждый поиск сначала сужается по правам, затем каждая находка снова проверяется по базе данных.

  • Гости находят публичные статьи.
  • Вошедшие клиенты находят статьи, которые могут читать, заявки, которые они отправили или в копии которых были, и общие заявки проектов, в которых состоят. Они видят сводку и статус заявки словами клиента, но никогда её заметки, внутреннюю активность или людей.
  • Сотрудники находят заявки в проектах, где работают (администраторы — все), включая внутренние заметки, и каждую статью. Заявка, найденная по заметке, помечается в списке рабочей области Matched in an internal note.

Как индекс остаётся актуальным

  • Заявки переиндексируются после каждого изменения: новая заявка, ответ или заметка, правка, метка, статус, участник, объединение или удаление. Записи делаются один раз в конце запроса, после отправки ответа. Запрос, изменивший больше 100 заявок (массовое редактирование), передаёт их заданиям search.index.
  • Статьи переиндексируются при сохранении, переводе или перемещении страницы в админке и удаляются при удалении, снятии с публикации или смене типа страницы. Правки вне Grav (git sync, FTP, развёртывание) подхватывает ежечасное задание kb.sync и первый поиск в справочном центре после очистки кэша.
  • Самовосстановление. Первый поиск в запросе проверяет, что файл и маркер существуют и что ни одна заявка не была записана в обход индекса (импорт или восстановленная база). Если что-то не так, он перестраивает индекс на месте до двух секунд. Больший индекс завершает задание search.rebuild, а пока поиск переходит на обычное совпадение по сводкам заявок и заголовкам статей. Нечитаемый файл удаляется и перестраивается так же.

Перестройка индекса

Используйте любой способ:

  • Operations → Search → Rebuild index в рабочей области (Rebuild articles only перестраивает только часть базы знаний).
  • bin/plugin helpdesk-pro reindex на сервере. --scope=tickets или --scope=kb перестраивает одну часть; --status печатает состояние индекса, не перестраивая его.
  • POST /helpdesk-pro/search/rebuild через API.

Operations → Search также показывает, построен ли индекс, сколько документов он хранит, когда его последний раз перестраивали, его размер и путь, сколько синонимов загружено и состояние семантического поиска. Для экрана нужно helpdesk-pro.settings.

Синонимы

Люди и статьи не всегда используют одни и те же слова. Поиск любого слова или фразы из группы также находит остальные:

log in, login, sign in, signin, log on
log out, logout, sign out
password, passcode, pass word, pwd
reset, forgot, recover, change password
account, profile, user
email, e-mail, mail, inbox
invoice, bill, receipt
refund, money back, chargeback
cancel, unsubscribe, stop, close account
subscription, plan, membership, renewal
error, bug, broken, not working, doesn't work, crash, issue
slow, lag, loading, performance
upload, attach, attachment, file
download, install, setup, set up
delete, remove, erase
two-factor, 2fa, mfa, authenticator, verification code
payment, card, charge, billing
shipping, delivery, tracking
license, licence, key, activation

Чтобы изменить список:

  1. Скопируйте user/plugins/helpdesk-pro/data/synonyms.json в user/data/helpdesk-pro/search/synonyms.json.
  2. Отредактируйте его groups. Каждая группа — список слов и фраз (["log in", "login", "sign in"]), и фраза совпадает как фраза.

Helpdesk Pro читает вашу копию вместо копии плагина. Установите search.synonyms в false, чтобы отключить синонимы.

Когда поиск не находит ничего, он пробует ещё раз с исправлением опечаток, если search.fuzzy не равен false.

Похожие заявки

Панель Similar tickets на экране заявки перечисляет заявки в проектах, где вы работаете, тема и текст которых похожи на эту. Сама заявка и заявки, объединённые с ней, исключаются, а слабые совпадения (меньше 35% от оценки лучшего) отбрасываются, поэтому панель появляется, только когда что-то действительно близко. Клиенты никогда не видят похожие заявки; форма запроса вместо этого предлагает статьи и их собственные обращения.

Поиск по ключевым словам находит слова. Семантический поиск находит ещё и смысл: «my card was charged twice» может найти статью «Duplicate payments». Ему нужен API встраивания, говорящий на формате OpenAI (OpenAI, OpenRouter, Ollama, Voyage, Mistral и другие), и по умолчанию он выключен.

Включите его в настройках плагина (вкладка Search) или в user/config/plugins/helpdesk-pro.yaml:

search:
  semantic:
    enabled: true
    api_key: 'env:OPENAI_API_KEY'

Пишите ключ как env:NAME, чтобы прочитать его из переменной окружения NAME. Ключ, введённый в настройках, хранится как есть в user/config. Ключ никогда не возвращается API и не показывается в рабочей области.

Как это работает:

  • Документы встраиваются заданием search.embed пакетами по search.semantic.batch_limit после индексации. Это задание выполняется только в фоновом процессе, никогда в конце запроса посетителя, поэтому семантическому поиску нужен планировщик в cron (Задания и cron).
  • Поиск встраивает запрос и ранжирует по словам и смыслу вместе, а search.semantic.weight решает долю. Запросы короче search.semantic.min_query_chars остаются только по ключевым словам.
  • Встраивается только hd_public. Внутренние заметки отправляются в API, только когда включён search.semantic.notes; иначе текст заметок никогда не покидает ваш сервер.
  • Когда API недоступен, поиск продолжает по ключевым словам. Operations → Search показывает, сколько документов встроено и сколько ждёт.

Заявки по умолчанию имеют 256 измерений. При 512 измерениях поиск стоит около 110 мс на 10 000 фрагментов; 256 уменьшает это вдвое. Каждая заявка считается в результатах один раз, на сколько бы фрагментов ни делился её текст.

Общие настройки с YetiSearch Pro

Семантические ключи повторяют настройки semantic: YetiSearch Pro имя в имя. Любой пустой ключ берёт значение YetiSearch Pro, когда этот плагин включён со своим семантическим поиском, и встроенное значение по умолчанию в остальных случаях, поэтому сайт, где работают оба, настраивает одну модель в одном месте.

Ключи соединения (base_url, api_key, model, dimensions и два префикса) идут вместе: если задать любой из них здесь, ни один не берётся из YetiSearch Pro. enabled, notes, embed_after_indexing и batch_limit никогда не берутся из YetiSearch Pro.

Настройки

Ключ По умолчанию Что делает
search.fuzzy true Повторять поиск, не нашедший ничего, с исправлением опечаток.
search.synonyms true Расширять поиск списком синонимов.
search.semantic.enabled false Ранжировать по смыслу, а также по словам.
search.semantic.provider openai_compatible Единственный провайдер: любой OpenAI-совместимый API встраивания.
search.semantic.base_url пусто (https://api.openai.com/v1) Базовый URL API, например http://localhost:11434/v1 для Ollama.
search.semantic.api_key пусто Ключ или env:NAME. Не нужен для локального сервера.
search.semantic.model пусто (text-embedding-3-small) Модель встраивания.
search.semantic.dimensions пусто (256) Длина вектора для моделей, которые её принимают; 0 — собственная длина модели.
search.semantic.query_prefix пусто Текст перед запросами (search_query: для nomic-embed-text).
search.semantic.document_prefix пусто Текст перед документами (search_document:).
search.semantic.weight пусто (0.5) Доля ранжирования из смысла: 0 — только слова, 1 — только смысл.
search.semantic.min_similarity пусто (0.25) Документы менее похожие на это никогда не появляются только по смыслу.
search.semantic.min_query_chars пусто (3) Более короткие запросы только по ключевым словам.
search.semantic.query_timeout пусто (5) Секунды ожидания встраивания запроса перед переходом на ключевые слова.
search.semantic.timeout пусто (20) Секунды ожидания фонового процесса для пакета встраиваний документов.
search.semantic.embed_after_indexing true Ставить search.embed в очередь при каждом изменении документов.
search.semantic.batch_limit 200 Документов на задание search.embed.
search.semantic.notes false Также встраивать внутренние заметки (отправляет текст заметок в API).
portal.suggestions true Предлагать статьи, пока люди печатают в поиске и форме запроса.
portal.kb_session_days 30 Сколько cookie hd_kb объединяет поиски посетителя.
privacy.kb_events_days 90 Поиски, просмотры и ответы справочного центра удаляются через столько дней.

Пустой семантический ключ разрешается, как описано в разделе Общие настройки с YetiSearch Pro; значение в скобках — встроенное значение по умолчанию.

API и MCP

Маршрут Право Что делает
GET /helpdesk-pro/search desk q, scope (all, tickets или kb), include_notes (по умолчанию включено), project, limit (до 100). Отвечает {tickets, articles, semantic, fallback}; строки заявок несут score и matched_in_note. fallback: true означает, что ответило обычное совпадение, пока индекс перестраивался.
GET /helpdesk-pro/tickets/{id}/similar desk Похожие заявки (limit до 20) с относительным score.
POST /helpdesk-pro/search/rebuild settings scope: all, tickets или kb. Ставит search.rebuild в очередь и отвечает 202 с {job_id, scope, queued}; queued: false, когда перестройка уже ждёт.

Инструменты MCP: search, find_similar_tickets и rebuild_search.

Задания

Задание Что делает
search.index Перезаписывает документы некоторых заявок: путь переполнения для массовых изменений.
search.rebuild Перестраивает индекс. По одному за раз.
search.embed Один проход встраивания по индексу, назначается снова, пока документы ждут. Только фоновый процесс.
kb.sync Ежечасно: переиндексирует базу знаний, когда статья изменилась вне Grav.