ПроектыProjects Support Agent

В продакшене · работает на службу поддержки In production · serving the support desk

Support Agent

Агент первой линии, который отвечает, маршрутизирует, учится и измеряет себя A first-line agent that answers, routes, learns and measures itself

Отвечает сотрудникам по документации, тикетам и переписке. Или честно отказывается. It answers employees from documentation, tickets and mail threads. Or honestly declines.

Шесть контуровSix loops

ДиалогDialogue мессенджеры и почта, агент, инструментыchat and mail, agent, tools онлайнonline
АвтотриажAuto-triage разбор новых тикетов в клиентских проектахsorting new tickets in client projects фоновыйbackground
СопровождениеFollow-up довести тикет до закрытияcarry a ticket through to closure фоновыйbackground
ИндексацияIndexing наполнение базы знаний из шести источниковingesting from six sources по расписаниюscheduled
СамообучениеSelf-learning решённые тикеты становятся статьямиsolved tickets become articles фоновыйbackground
ОтпускаAbsences чтобы не назначить тикет на отсутствующегоso nothing is assigned to someone away фоновыйbackground

ХранилищаStores векторная: фрагменты знаний и история тикетов · реляционная: диалоги, настройки, журналы решений, оценок и расходов vector: knowledge chunks and ticket history · relational: dialogues, settings, logs of decisions, ratings and spend

За периметромOutside языковая модель · Jira · Confluence · почта · кадровая система · 1С:ИТС language model · Jira · Confluence · mail · HR system · vendor docs

73 091
строка кодаlines of code
17
сервисовservices
1 596
автотестов в 111 наборахautomated tests in 111 suites
296 000
фрагментов знаний на боевой базеknowledge chunks in production
6
источников знанийknowledge sources
4
канала диалогаdialogue channels

Путь одного сообщенияThe path of a single message

Треть кодовой базы это тесты: 25 031 строка проверок на пять контуров поверх двух общих хранилищ. Начнём с того, что происходит при одном вопросе в чате. A third of the codebase is tests: 25,031 lines of checks over the loops that run on two shared stores. Start with what happens on a single question in a chat.

СотрудникEmployee
задаёт вопрос в мессенджереasks a question in a messenger
БотBot
передаёт вопрос в общий веб-сервис вместе с идентификатором чатаhands the question to the shared web service along with the chat identifier
Веб-сервисWeb service
поднимает историю диалога из базы, а не из памяти процессаloads dialogue history from the database, not from process memory
АгентAgent
работает циклом подумать, воспользоваться инструментом, подуматьruns a loop: think, use a tool, think
ОтветAnswer
сохраняется и возвращается в чат вместе с идентификатором ходаis stored and returned to the chat along with a turn identifier

У каждого хода есть идентификатор. По нему потом можно поднять, что нашёл поиск, сколько заняли этапы и почему бот ответил именно так. Every turn carries an identifier. With it you can later pull up what the search found, how long each stage took and why the bot answered the way it did.

Цикл, а не один запрос к моделиA loop, not a single model call

Модель сама решает, каким инструментом воспользоваться, и делает это столько раз, сколько нужно. Цикл заканчивается, когда инструменты больше не нужны и ответ готов. The model decides which tool to use, and does so as many times as needed. The loop ends when no tool is required any more and the answer is ready.

Два предохранителяTwo safety catches

  • Максимальное число шагов рассуждения на один вопросA cap on reasoning steps per question
  • Отдельный лимит обращений к базе знаний: иначе один вопрос превращается в десятки поисковA separate cap on knowledge-base calls: otherwise one question becomes dozens of searches

Чем агент умеет пользоватьсяWhat the agent can reach for

Поиск по базе знанийKnowledge searchдокументация, статьи, тикеты: основной источник ответовdocs, articles, tickets: the main source of answers
Поиск тикетовTicket searchпо тексту и по исполнителю: была ли уже такая проблемаby text and by assignee: has this happened before
Подсчёт тикетовTicket countбыстрый счётчик вместо выгрузки спискаa fast counter instead of dumping a list
Сумма трудозатратLogged effortсколько часов списано за период по проекту или человекуhours booked over a period by project or person
Обращение к тикетуLive ticket lookupактуальные поля конкретного тикета прямо из Jiracurrent fields of one ticket straight from Jira
Создание заявкиRaise a ticketтолько с согласия пользователя, стандартный приоритетonly with the user's consent, standard priority
Эскалация на человекаEscalate to a humanинцидент или прямая просьба: приоритет выше, есть дежурныйan incident or a direct request: higher priority, a duty engineer
ОграничительThe limiterсколько бы инструментов модель ни вызвала, потолок числа поисков жёсткийhowever many tools the model calls, the search ceiling is hard

Что происходит между вопросом и ответомWhat happens between the question and the answer

Карта этапов поиска. Ни один необязательный этап не может уронить ответ: на каждом стоит предохранитель. The map of retrieval stages. No optional stage can bring the answer down: each has a fallback behind it.

ОтветAnswer источники сходятся, уверенность высокаяsources agree, confidence is high
Ответ с оговоркойAnswer with a caveat «нашёл только косвенно»"found only indirectly"
Честный отказAn honest refusal «оснований мало, оформить заявку?»"weak grounds, shall I raise a ticket?"
Уточняющий вопросA clarifying question выдача про разные системыhits span different systems
Подвисла переформулировкаRewriting hangs
ищем по исходному вопросу, а не падаемwe search the original question instead of failing
Отказало переранжированиеReranking fails
отдаём выдачу без него: хуже, но она естьresults go out without it: worse, but they exist
Фрагмент похож на командуA fragment looks like an instruction
в контекст модели он не попадаетit never reaches the model context

По умолчанию включены раскрытие местоимений и оба вида поиска. Переранжирование, поиск по нескольким формулировкам и поиск по гипотетическому ответу включаются по решению: они стоят денег и времени. Pronoun resolution and both searches are on by default. Reranking, multi-phrasing search and hypothetical-answer search are opt-in: they cost money and time.

Вопрос переписывается перед поискомThe question is rewritten before the search

Три механизма и фильтр, который бережёт деньги. У бота тысячи вопросов в день, и это принципиально. Three mechanisms, plus a filter that saves money. The bot handles thousands of questions a day, which is exactly why it matters.

включеноon

Раскрытие местоименийPronoun resolution

«а как её настроить?» превращается в «как настроить БИТ.Финанс»"and how do I set it up?" becomes "how do I set up the finance module"

по решениюopt-in

Несколько формулировокMultiple phrasings

один вопрос ищется в нескольких редакциях, выдачи сливаютсяone question is searched in several variants and the results are fused

по решениюopt-in

Гипотетический ответHypothetical answer

поиск по тексту, похожему на искомый документsearch by text that resembles the document being sought

Модель вызывается не на каждый вопросThe model is not called for every question

Если в вопросе уже есть название продукта, код ошибки, латиница или цифры, раскрывать нечего и платить не за что. Фильтр стоит до модели и стоит ноль. If the question already carries a product name, an error code, Latin characters or digits, there is nothing to resolve and nothing to pay for. The filter sits before the model and costs nothing.

Жёсткий потолок числа поисковA hard ceiling on searches

Сколько бы формулировок модель ни придумала, обращений к базе знаний будет не больше заданного лимита, считая исходный вопрос. Это защита от старой болезни «десятки поисков на один вопрос». However many phrasings the model invents, knowledge-base calls stay within a set limit, counting the original question. This guards against the old disease of dozens of searches per question.

Когда бот не уверен, он это говоритWhen the bot is unsure, it says so

Два механизма честности вместо правдоподобной выдумки: отказ и уточняющий вопрос. Two honesty mechanisms instead of a plausible invention: refusal, and a clarifying question.

оснований малоweak grounds оснований достаточноsolid grounds

Пять признаков, по которым бот оценивает свой ответFive signals the bot grades its own answer on

  • Насколько близко нашлосьHow close the hits are
  • Есть ли явный лидер среди найденногоWhether there is a clear leader among them
  • Подтверждается ли текст ответа источникамиWhether the answer text is backed by the sources
  • Не выдуманы ли ссылкиWhether the links are real
  • Согласны ли источники между собойWhether the sources agree with each other

Разбивка по признакам сохраняется, поэтому видно, почему бот отказался, а не только что он отказался. The per-signal breakdown is kept, so it is visible why the bot declined, not just that it did.

Уточнение задаётся редко: нужны четыре условия сразуClarification is rare: four conditions must hold at once

  • Бот не уверен в ответеThe bot is unsure of its answer
  • Выдача действительно расходится по системамHits genuinely scatter across systems
  • Вопрос короткийThe question is short
  • Ходом раньше уточнения не былоThere was no clarification a turn earlier

Два уточнения подряд невозможны, иначе бот превращается в анкету. Ответ понимается и номером, и названием. Уточнение старше получаса игнорируется. Two clarifications in a row are impossible, otherwise the bot turns into a questionnaire. The reply is understood by number and by name. A clarification older than half an hour is ignored.

Три оговорки, которые видно только вблизиThree caveats visible only up close

  • Уверенность считается только там, где был поиск по базе знаний: на «привет» обосновывать нечегоConfidence is computed only where a knowledge search happened: a greeting has nothing to justify
  • Под отказом оценки не показываются: это был бы шумRating buttons are hidden under a refusal: that would only collect noise
  • Оборвавшееся уточнение не считается решённым обращениемA clarification that broke off is not counted as a solved request

Пороги требуют калибровки на данных конкретной установки. Со значениями по умолчанию механизм срабатывает почти только на пустой выдаче поиска. Это не «настроил и забыл», а работа на боевых вопросах. Thresholds need calibration on each installation's own data. On defaults the mechanism fires almost only when the search returns nothing. This is not set-and-forget: it is work on live questions.

Заявка или эскалацияA ticket, or an escalation

Два разных сценария записи в Jira, которые легко перепутать. Сначала база знаний, потом похожие тикеты, и только если решить нельзя, развилка. Two different ways of writing into Jira that are easy to confuse. Knowledge base first, then similar tickets, and only if it cannot be solved does the fork appear.

Обычная заявкаA normal ticket

КогдаWhen
бот не помог, ситуация не срочнаяthe bot could not help and it is not urgent
СогласиеConsent
обязательно спрашиваетсяalways asked for
ПриоритетPriority
стандартныйstandard
ИсполнительAssignee
не назначаетсяnot assigned
ОписаниеDescription
суть проблемыthe gist of the problem

ЭскалацияAn escalation

КогдаWhen
инцидент, просьба «нужен человек», срочноan incident, a request for a human, or urgency
СогласиеConsent
при инциденте не требуетсяnot required for an incident
ПриоритетPriority
повышенныйraised
ИсполнительAssignee
дежурный специалистthe duty engineer
ОписаниеDescription
причина плюс весь контекст диалогаthe reason plus the whole dialogue context

«Бот не нашёл ответ» сам по себе не повод эскалировать. Порог уверенности это подсказка модели в инструкции, а не автоматический вызов эскалации. "The bot found no answer" is not by itself a reason to escalate. The confidence threshold is a hint in the model's instructions, not an automatic trigger.

Четыре канала диалогаFour dialogue channels

Логика ответов общая. Различаются транспорт и способ поставить оценку. The answering logic is shared. What differs is the transport and how a rating is given.

Telegram

Опрос сервера ботомThe bot polls the server

  • В личке отвечает на всёAnswers everything in a direct message
  • Кнопки оценки под ответомRating buttons under the answer
  • Причина недовольства спрашивается вторым шагомThe reason for a downvote is asked as a second step
Mattermost

Постоянное соединениеA persistent connection

  • В каналах отвечает только по упоминаниюIn channels it answers only when mentioned
  • Оценка реакцией на сообщениеRating by reacting to the message
  • Снятие реакции убирает оценкуRemoving the reaction removes the rating
Lenza

Разбор очереди событийConsuming an event queue

  • В группах отвечает только по упоминаниюIn groups it answers only when mentioned
  • Оценка ответом «+» или «−»Rating by replying with a plus or a minus
  • Ответ приходит правкой заглушки, чтобы в чате осталось одно сообщениеThe answer arrives as an edit of the placeholder, so one message remains
Почта · новоеMail · new

Отдельный ящикA separate mailbox

  • Не тот ящик, что индексируется: совпадение это ошибка настройкиNot the mailbox that gets indexed: overlap is a misconfiguration
  • Ответ уходит в ту же цепочку писемThe reply goes into the same mail thread
  • Автоответы и рассылки игнорируютсяAuto-replies and mailing lists are ignored

Ожидание перестало быть немымWaiting is no longer silent

Пока агент ходит в базу знаний и в Jira, бот правит ту же заглушку статусами: «Ищу в базе знаний», «Смотрю похожие тикеты», «Готовлю ответ». Включается отдельно: без флага всё работает как раньше. While the agent queries the knowledge base and Jira, the bot edits the same placeholder with statuses: searching the knowledge base, checking similar tickets, drafting the answer. It is opt-in: without the flag everything behaves as before.

Диалог переживает перезапускThe dialogue survives a restart

Привязка «чат к диалогу» хранится в базе, а не в памяти процесса. Контекст переживает перезапуск бота и перезагрузку сервера. The chat-to-dialogue link lives in the database, not in process memory. Context survives both a bot restart and a server reboot.

Откуда берутся знанияWhere the knowledge comes from

Шесть источников и один конвейер обработки. Дальше самое частое непонимание: что где лежит. Six sources and one processing pipeline. Then the most common misunderstanding: what is stored where.

Confluenceстраницы со всеми подстраницами и вложениямиpages with every child page and attachment
Локальные документыLocal documentsпапка с файлами на сервереa folder of files on the server
Тикеты JiraJira ticketsописание, комментарии, вложенияdescription, comments, attachments
Документация вендораVendor documentationметодподдержка, опцияmethodology support, optional
ПочтаMailписьма и перепискаletters and threads
Черновики статейArticle draftsтолько после одобрения человекомonly after human approval
Разбор форматовFormat parsingPDF, Word, Excel, PowerPoint, включая старые форматы; таблицы сохраняют структуруPDF, Word, Excel, PowerPoint, legacy formats included; tables keep their structure
Нарезка на фрагментыChunkingк каждому фрагменту подставляется заголовок документа: без этого не работает поиск по названиюeach chunk gets the document title prepended: without it, search by name fails
ВекторизацияVectorisationфрагмент превращается в набор чисел, по которому ищется смысловая близостьa chunk becomes numbers that semantic proximity is measured on
Проверка и записьScreening and writeссылка на первоисточник и дата изменения; похожий на спрятанную команду помечается или не индексируетсяsource link and modification date; anything resembling a hidden instruction is flagged or skipped

База знанийKnowledge base

то, чем отвечает ботwhat the bot answers from

  • Документация, статьи, файлыDocumentation, articles, files
  • Тикеты всех доступных проектов с комментариями и вложениямиTickets from every available project, with comments and attachments
  • Документация вендора, если подключенаVendor documentation, if connected
  • Одобренные черновики статейApproved article drafts

История тикетовTicket history

то, на чём учится автотриажwhat auto-triage learns from

  • Только клиентские проектыClient projects only
  • Краткое описание и суть заявкиA short description and the gist of the request
  • Нужна, чтобы подобрать исполнителя по похожим обращениям прошлогоUsed to pick an assignee from similar past requests
  • В ответы бота не попадаетNever reaches the bot's answers

Почта это особый случай. Письма индексируются, но в ответы бота напрямую не идут. Их содержимое доходит до пользователя единственным путём: система привязывает письмо к тикету и пишет туда комментарий, а дальше комментарий попадает в базу знаний уже как часть тикета. Так переписка не утекает в ответы случайным людям. Mail is a special case. Letters are indexed but never go straight into answers. Their content reaches a user by exactly one route: the system links the letter to a ticket and posts a comment there, and that comment then enters the knowledge base as part of the ticket. This is how correspondence avoids leaking into answers for the wrong people.

Письмо находит свой тикетA letter finds its ticket

Самая нетривиальная логика в системе: каскад от точного признака к вероятному. The least trivial logic in the system: a cascade from the exact signal to the probable one.

КаскадThe cascade

1Ключ тикета прямо в текстеThe ticket key right in the text
2Проверка, что такой тикет естьA check that the ticket exists
3Наследование по перепискеInheritance along the thread
4Поиск подходящего тикета по смыслуSemantic search for a matching ticket

Ключ может быть опечаткой, поэтому шаг проверки отдельный. Если ключа нет, смотрим, было ли привязано предыдущее письмо этой же цепочки. A key can be a typo, so verification is a separate step. With no key, the system checks whether the previous letter in the same thread was already linked.

Четыре полосы уверенности смыслового поискаFour confidence bands of the semantic search

УверенноConfidentпривязываем и пишем комментарий в тикетlink it and post a comment
ДостаточноSufficientпривязываем, но комментарий не пишемlink it without a comment
ПохожеSimilarпоказываем как кандидата, решение за человекомshow it as a candidate, a human decides
Не похожеNo matchсвязи нетno link at all

Пороги настраиваются: чем выше планка, тем меньше ошибочных комментариев в чужих тикетах. The thresholds are tunable: the higher the bar, the fewer stray comments in other people's tickets.

Отдельная задача: отрезать процитированную историю. В ответах Outlook часто вложена вся переписка. Система оставляет только новые строки, но короткие «Ок», «Принято», «Согласовано» сохраняет. В тикет уходят краткий пересказ, полный текст новой части и вложения письма; повторный прогон комментарий не дублирует. A task of its own: cutting the quoted history. Outlook replies often carry the entire thread. The system keeps only the new lines, but preserves short ones like "ok", "received", "agreed". The ticket gets a summary, the full text of the new part and the letter's attachments; a repeat run does not duplicate the comment.

Путь нового тикетаThe path of a new ticket

Автотриаж от обнаружения до записи в Jira. Уверенность считается по каждому полю отдельно. Auto-triage from detection to the write into Jira. Confidence is computed per field.

Опрос JiraPolling Jiraновые необработанные тикеты в клиентских проектах за окно в несколько днейnew untouched tickets in client projects over a window of several days
Поиск похожихFinding similarпо тексту заявки поднимаются похожие тикеты прошлогоpast tickets are pulled up by the request text
Сбор кандидатовCollecting candidatesисполнители похожих тикетов складываются в рейтинг по близостиtheir assignees are ranked by proximity
ОтсевFilteringуволенные, недоступные для назначения и, по желанию, те, кто в отпускеleavers, people who cannot be assigned, and optionally those on leave
Решение моделиThe model decidesпять полей сразу, с оценкой уверенности по каждомуfive fields at once, each with its own confidence
ЗаписьWriting backуверенные поля проставляются, по остальным комментарий с рекомендацией, метка и запись в журналconfident fields are set, the rest get a recommendation comment, a label and a log entry

Как это выглядит на одном тикете: How it looks on a single ticket:

Тип обращенияRequest typeпроставленоapplied
СрочностьUrgencyпроставленоapplied
КомпонентComponentединственный вариантonly one option
Линия поддержкиSupport lineрекомендацияrecommendation
ОтветственныйAssigneeрекомендацияrecommendation

Кандидат проходит три фильтраA candidate passes three filters

1Все исполнители похожих тикетовEvery assignee of similar tickets
2Минус уволенные и заблокированныеMinus leavers and blocked accounts
3Минус те, на кого нельзя назначитьMinus those who cannot be assigned
4Минус те, кто в отпускеMinus those on leave

Единственный допустимый вариант поля получает максимальную уверенность по определению: выбирать не из чего. Если назначить выбранного не удалось, берётся следующий по рейтингу. A field with exactly one permitted value gets maximum confidence by definition: there is nothing to choose. If the chosen person cannot be assigned, the next-ranked one is taken.

Грабли, которые пришлось обойтиTraps that had to be worked around

  • Условие «метки не равны X» в Jira не находит тикеты вообще без меток, приходится писать условие иначеA "labels != X" clause in Jira misses tickets with no labels at all, so the condition has to be written differently
  • Относительные даты отклоняются на серверах с русской локалью, нужна абсолютная датаRelative dates are rejected on servers with a Russian locale, an absolute date is required
  • Названия типов и приоритетов должны совпадать дословно, с регистром и скобками, иначе смена поля отклоняетсяType and priority names must match verbatim, case and brackets included, or the field change is rejected
Только советоватьAdvise only Применять уверенноеApply the confident Применять всёApply everything

Первый режим ничего не меняет и пишет рекомендацию комментарием: безопасный для обкатки. Третий требует доверия к модели и настроенных справочников. The first mode changes nothing and posts a recommendation as a comment: safe for a trial. The third needs trust in the model and well-maintained reference data.

Триаж не только маршрутизируетTriage does more than route

Четыре надстройки над классификацией. Каждая включается отдельно и по умолчанию выключена. Four layers on top of classification. Each is enabled separately and is off by default.

Дубль на входеA duplicate at intake

три режимаthree modes

До назначения исполнителя ищутся похожие открытые тикеты. При высокой схожести появляется комментарий «похоже на дубль», связь в Jira или перевод в «Дубликат». Подсказать, связать, закрыть.Before an assignee is chosen, similar open tickets are searched. On a high match the system posts a "looks like a duplicate" comment, creates a Jira link, or moves it to duplicate status. Hint, link, close.

Массовая проблемаA mass incident

один алертone alert

Если за короткое окно пришло несколько похожих заявок, в чат уходит один алерт вместо серии пометок «дубль» на каждой. Триаж при этом идёт своим чередом.If several similar requests arrive in a short window, one alert goes to the chat instead of a duplicate label on each. Triage carries on as usual.

Прогноз срокаA time forecast

только информируетinformative only

По похожим закрытым тикетам считается медиана и верхняя оценка времени решения. При настроенном сроке реакции показывается риск его нарушить. Поля тикета при этом не меняются.The median and upper estimate of resolution time are computed from similar closed tickets. Where a response deadline is configured, the risk of missing it is shown. Ticket fields stay untouched.

Черновик инженеруA draft for the engineer

внутреннийinternal

Что проверить, ссылки на похожие тикеты и статьи базы знаний. Комментарий внутренний, клиент его не видит. Нет опоры в данных, черновик не пишется вовсе.What to check, links to similar tickets and knowledge articles. The comment is internal and invisible to the client. With no grounding in data, no draft is written at all.

Ни одна надстройка не принимает решение за человека. Прогноз и черновик только информируют, дедупликация в самом мягком режиме лишь оставляет комментарий. Автоматика трогает поля тикета ровно там, где была уверена и раньше. None of these decides for a human. The forecast and the draft only inform; deduplication in its softest mode merely leaves a comment. Automation touches ticket fields exactly where it was already confident.

Довести тикет до закрытияCarrying a ticket to closure

Самый частый способ потерять тикет это оставить его в статусе «ждём ответа» навсегда. Бот спрашивает автора сам, в том же канале, где обращение началось. The most common way to lose a ticket is to leave it waiting for a reply forever. The bot asks the author itself, in the channel where the request started.

«Всё решилось»"All sorted"тикет закрываетсяthe ticket closes
«Ещё актуально»"Still open"возвращается в работуit goes back into work
«Стало хуже»"It got worse"уходит в эскалациюit escalates
МолчаниеSilenceнапоминание, затем закрытие с отдельной пометкойa reminder, then closure with a distinct label

Закрытые по молчанию помечаются отдельно: в отчётах их не спутать с решёнными. Tickets closed on silence are labelled separately, so reports never confuse them with solved ones.

Помощник инженера в личкеAn engineer's assistant in DMs

В личной переписке инженер поддержки получает не диалог о продукте, а короткие команды к рабочим данным. In a direct message a support engineer gets not a conversation about the product but short commands against working data.

  • Карточка тикета: статус, исполнитель, суть, без переключения в JiraTicket card: status, assignee, gist, without switching to Jira
  • Похожие обращения: как это уже решали раньшеSimilar requests: how this was solved before
  • Поиск по базе знаний: статья по теме прямо в чатKnowledge search: the relevant article straight into the chat
  • Черновик ответа: заготовка, которую инженер правит под себяDraft reply: a starting point the engineer edits
  • Кто это: чей логин, из какого подразделенияWho is this: whose login, from which unit

Доступ к командам по списку, а не по догадке. Команды видит только тот, кому они разрешены: постороннему сотруднику бот отвечает обычным диалогом и внутренние возможности даже не показывает в подсказке. Разница принципиальная: карточка чужого тикета и черновик ответа клиенту не должны утекать за пределы поддержки. Command access comes from a list, not a guess. Only those permitted see the commands: to anyone else the bot responds with an ordinary dialogue and does not even hint that the internal features exist. The distinction matters: another team's ticket card and a draft reply to a client must not leave support.

От пробела в документации до статьиFrom a documentation gap to an article

Замкнутая петля: бот не только потребляет базу знаний, но и развивает её. A closed loop: the bot does not only consume the knowledge base, it grows it.

Вопрос без ответаUnanswered questionотказ, слабый поиск, оценка «не нашёл» или заявка следомrefusal, weak hits, a "not found" rating or a ticket right after
Тема-пробелGap topicчастота × неуверенность × вес обращенияfrequency × uncertainty × request weight
Тикет решёнTicket solvedобращение по этой теме доведено до концаa request on that topic reached the end
Черновик статьиArticle draftсимптомы, причина, шаги решения, как проверитьsymptoms, cause, steps, how to verify
Ревью человекомHuman reviewединственный вход в базу знанийthe only way in
База знанийKnowledge baseследующий такой вопрос закрывается сразуthe next such question closes at once

и цикл повторяется на следующем вопросе and the cycle repeats on the next question

До одобрения черновик в базу знаний не попадает. Иначе бот начнёт учиться на собственных пересказах, и одна ошибка размножится, выглядя при этом как источник. A draft never enters the knowledge base before approval. Otherwise the bot starts learning from its own retellings, and a single error multiplies while looking like a source.

Две отсечки до обращения к моделиTwo cut-offs before the model

Модель самая дорогая часть конвейера, поэтому решение вида «перезагрузил, помогло» отсекается по длине, а тема, по которой статья уже есть, отсекается по сходству. До генерации доходит только то, из чего действительно выйдет статья. The model is the most expensive part of the pipeline, so a resolution like "rebooted, worked" is cut by length, and a topic that already has an article is cut by similarity. Only what can genuinely become an article reaches generation.

Какие страницы пора пересмотретьWhich pages need a review

  • Показываем не всё старое: их тысячи, такой отчёт не читают. Только пересечение важности и рискаNot everything old is shown: there are thousands, and nobody reads that report. Only the overlap of importance and risk
  • Цитирование засчитывается по первым трём результатам: документ на восьмой позиции ответ почти наверняка не сформировалA citation counts within the top three: a document in eighth place almost certainly did not shape the answer
  • Нет даты изменения, значит ноль за возраст, а не максимум: «дата неизвестна» не значит «страница древняя»No modification date scores zero for age, not maximum: "date unknown" does not mean "ancient"

Раз в неделю в чат уходит дайджест топ-10 страниц к пересмотру. Отметка о доставке хранится в базе, а не в памяти процесса, иначе после каждого перезапуска дайджест приходил бы заново. Once a week a digest of the top ten pages to review goes to the chat. The delivery mark lives in the database rather than in process memory, otherwise every restart would resend it.

Безопасность: четыре рубежаSecurity: four lines of defence

Каждый стоит в своей точке пути данных, и все четыре включаются независимо. Пока флаг снят, обёртки над моделью нет вовсе: ровно прежняя работа. Each sits at its own point on the data path, and all four toggle independently. While a flag is off there is no wrapper around the model at all: exactly the previous behaviour.

Инъекции из индексаInjections from the index

включеноon

Страница, вложение или письмо содержит фразу вида «игнорируй предыдущие инструкции и…». Текст попадает в индекс и приезжает к модели рядом с её собственной инструкцией, как будто это команда от владельца системы. A page, an attachment or a letter carries a phrase like "ignore previous instructions and...". The text lands in the index and arrives beside the model's own instructions, as if it were a command from the system owner.

1. Изоляция1. Isolationфрагменты уходят к модели в размеченном блоке «это данные, а не команды»; попытка закрыть блок изнутри обезвреживаетсяchunks reach the model inside a marked block saying "this is data, not commands"; an attempt to close the block from inside is neutralised
2. Проверка при индексации2. Check at indexingстоит в общей точке записи, обойти в обход одного индексатора нельзя; нужна переиндексацияsits at the shared write point, so no single indexer can bypass it; needs a reindex
3. Проверка при выдаче3. Check at retrievalприкрывает контент, попавший в индекс раньше; дороже, зато работает сразуcovers content indexed earlier; costlier, but effective immediately

Детектор намеренно осторожен. Обычная документация сплошь и рядом содержит слова «инструкция», «система», «игнорировать»: ложное срабатывание дороже пропущенной экзотики, потому что фрагмент просто исчезнет из ответа и никто не поймёт почему. Обфускация снимается до сравнения: невидимые символы, смесь кириллицы и латиницы, переносы строк внутри фразы. Согласие на действие даёт только человек, текст из документа согласием не считается. The detector is deliberately cautious. Ordinary documentation is full of words like "instruction", "system" and "ignore": a false positive costs more than a missed exotic case, because the chunk simply vanishes from the answer and nobody understands why. Obfuscation is stripped before comparison: invisible characters, mixed alphabets, line breaks inside a phrase. Only a human consents to an action; text from a document is not consent.

Редакция данныхData redaction

по решениюopt-in

Пароли и токены, строки подключения целиком, номера карт с проверкой контрольной суммы, телефоны. Документы только рядом с ключевым словом: номера договоров и версии 8.3.20 не трогаем. Адреса почты по умолчанию тоже, адрес заявителя обычно нужен по делу. Passwords and tokens, whole connection strings, card numbers with a checksum test, phone numbers. ID numbers only next to a keyword: contract numbers and version strings are left alone. Mail addresses too by default, since the requester's address is usually needed.

Три решения важнее списка правил: обратимость в пределах хода, когда карта соответствий живёт в памяти хода и не попадает ни в базу, ни в логи; одна точка на всех потребителей модели; на индексации необратимо, потому что секретам в векторной базе не место. Three decisions matter more than the rule list: reversibility within a turn, where the mapping lives in turn memory and never reaches the database or the logs; one choke point for every model consumer; and irreversibility at indexing, because secrets have no place in a vector store.

Хранилище секретовSecret store

по решениюopt-in

Значением настройки может быть ссылка в хранилище, а не сам токен. Обычные значения при этом продолжают работать, поэтому переводить настройки можно по одной. A setting's value can be a reference into the store rather than the token itself. Plain values keep working, so settings can be migrated one at a time.

Кэш обязателен: конфиг читается на каждый запрос, без кэша каждый вопрос стоил бы похода в хранилище. Ротация без рестарта: по истечении времени жизни кэша значение перечитывается само. Ключ от сейфа не в сейфе: доступ к самому хранилищу задаётся только переменными окружения. Caching is mandatory: config is read on every request, and without a cache each question would cost a round trip. Rotation without a restart: once the cache expires the value is re-read on its own. The key to the safe is not in the safe: access to the store itself comes only from environment variables.

Лимиты и журналLimits and audit

включеноon

Три уровня нарастающей ширины: 20 запросов за 5 минут на один чат, 300 запросов в минуту на весь сервис, дневной бюджет модели с жёсткой отсечкой. Счётчики в общей базе, потому что реплик сервиса может быть несколько, а лимит должен быть один на всех. Мониторинг и нагрузочные проверки заносятся в исключения. Three widening tiers: 20 requests per 5 minutes per chat, 300 per minute across the service, and a daily model budget with a hard cut-off. Counters live in the shared database, because there can be several replicas and the limit must be one for all. Monitoring and load checks go on an exclusion list.

Отказ выглядит по-человечески во всех мессенджерах: «слишком много запросов, попробуйте позже», а не «произошла ошибка». Разница в том, вернётся ли человек через минуту или напишет в поддержку. Всплеск отказов поднимает алерт: обычно это зациклившийся клиент. A rejection reads like a human sentence in every messenger: too many requests, try later, rather than an error occurred. That decides whether the person comes back in a minute or writes to support. A spike of rejections raises an alert: usually a client stuck in a loop.

Журнал безопасности отдельно от перепискиThe audit log is separate from conversations

У переписки и у журнала действий разный смысл и разные сроки хранения, поэтому это две разные таблицы. Фиксируются вход в админку, включая неудачный, изменение настроек и пользователей, запуск индексации, ревью статей, создание заявки ботом, обращения к боту, отказы по правам и лимитам. Conversations and an action log mean different things and are kept for different periods, so they are two different tables. Recorded: admin logins including failed ones, changes to settings and users, index runs, article reviews, tickets raised by the bot, requests to the bot, and refusals by rights or limits.

Чего в нём нет и кто его видитWhat is absent, and who sees it

  • Тексты сообщений по умолчанию не пишутся, включаются только по требованию службы безопасностиMessage bodies are not written by default; they are enabled only at the security team's request
  • Видит только администратор: фильтры, выгрузка в таблицуOnly an administrator sees it: filters, export to a spreadsheet
  • Попытка открыть журнал без прав тоже попадает в журналAn attempt to open the log without rights is itself logged

Недоступное хранилище секретов роняет сервис на старте с именем ключа, а не отдаёт пустой токен посреди дня. An unreachable secret store fails the service at startup naming the key, rather than handing out an empty token mid-day.

Видно, за что заплатили и куда ушло времяWhere the money and the time went

Каждый ход разложен по этапам. Дальше шесть вкладок дашборда отвечают на шесть вопросов, которые задают регулярно. Every turn is broken down by stage. Beyond that, six dashboard tabs answer the six questions that get asked regularly.

Разбор одного хода: сколько занял каждый этапOne turn broken down: time per stage

Разбор вопросаQuestion parsing 0,45
Векторизация запросаQuery vectorisation 0,60
Поиск по базе знанийKnowledge search 0,90
ПереранжированиеReranking 0,70
Ответ моделиModel answer 2,60

Итого 5,3 с, из них 2,6 с ждём модель Total 5.3 s, of which 2.6 s is waiting on the model

Что записываетсяWhat is recorded

Длительность каждого действия, токены и деньги каждого вызова модели, что именно нашёл поиск. Отсюда медиана и хвосты времени ответа, расход по дням, моделям и каналам, предупреждение при подходе к бюджету. The duration of each action, the tokens and money of each model call, and exactly what the search found. From this come the median and tails of response time, spend by day, model and channel, and a warning as the budget approaches.

Деталь зрелости: запись ведётся фоном. Если журналирование упало, пользователь всё равно получит ответ. А если модели нет в прайс-листе, токены всё равно считаются и на дашборде виден счётчик неоценённых вызовов, иначе бюджетные предупреждения молчали бы при реальном расходе. A sign of maturity: logging runs in the background. If it goes down, the user still gets an answer. And if a model is missing from the price list, tokens are still counted and the dashboard shows a counter of unpriced calls, otherwise budget warnings would stay silent while money is spent.

Шесть вкладок дашбордаSix dashboard tabs

Паттерны проблемProblem patternsо чём вообще идут тикеты, где всплеск, что повторяетсяwhat tickets are actually about, where the spikes are, what repeats
ТриажTriageнасколько он автоматизирован и как часто его правят рукамиhow automated it is and how often it is corrected by hand
Бот и почтаBot and mailкакая доля обращений закрыта без заявки, довольны ли ответамиwhat share of requests closed without a ticket, and satisfaction
Стоимость и скоростьCost and speedрасход по дням, моделям и каналам против бюджетаspend by day, model and channel against the budget
Пробелы знанийKnowledge gapsтемы, где бот регулярно не находит основанийtopics where the bot regularly finds no grounds
Здоровье базыCorpus healthсвежесть источников и страницы к пересмотруsource freshness and pages due for review
Алерты и выгрузкаAlerts and exportуведомления в чат и выгрузка в таблицу с каждой вкладкиchat notifications and a spreadsheet export from every tab
Считается, а не ощущаетсяMeasured, not feltдоля правок за триажем определяется сверкой с реальной историей изменений тикета в Jirathe share of corrections after triage comes from Jira's real change history

Как проверяется, что стало лучшеHow "better" gets verified

Улучшения поиска нельзя оценивать на глаз. Есть эталонный набор вопросов, у каждого заранее известны правильные источники и ключевые тезисы ответа. Retrieval improvements cannot be judged by eye. There is a reference question set, and for each question the correct sources and key points are known in advance.

Эталонный наборThe reference set

  • Набор для конкретной установки собирается автоматически из её же базы знаний, участия людей не требуетThe set for an installation is built automatically from its own knowledge base, with no human effort
  • Дополняется реальными вопросами и теми, что получили отрицательную оценкуIt is topped up with real questions and those that got a negative rating
  • Есть отдельные вопросы, на которые бот обязан отказаться отвечатьSome questions exist that the bot must refuse to answer
  • Общие проверки живут в репозитории, набор заказчика рядом с его данными и наружу не уезжаетShared checks live in the repository; a customer's set stays next to their data and never travels

Каждое изменение кода проходит воротаEvery code change passes gates

СтильStyle ТипыTypes СборкаBuild ТестыTests МетрикиMetrics

Красный результат блокирует слияние. Отдельным прогоном идут тесты на живых базах в одноразовых контейнерах: 1 596 проверок в 111 наборах. A red result blocks the merge. A separate run executes tests against live databases in disposable containers: 1,596 checks in 111 suites.

Главное в отчёте не проценты. Средняя метрика может вырасти и при этом развалить целый класс запросов. Поэтому первая таблица отчёта это список вопросов, которые работали раньше и сломались сейчас. Изменение, сломавшее хоть один такой вопрос, не принимается без объяснения. Percentages are not the point of the report. An average can rise while a whole class of queries collapses. So the first table lists questions that used to work and broke now. A change that breaks even one of them is not accepted without an explanation.

Менять модель и инструкции без рискаChanging the model and the instructions without risk

Две вещи, которые раньше требовали деплоя и веры в лучшее. Two things that used to require a deploy and hope for the best.

Инструкции для модели живут в базе, а не в кодеModel instructions live in the database, not in code

Раньше правка одной формулировки означала выкладку новой версии. Теперь версии живут в базе: правка, комментарий, история и откат к любой прошлой редакции. Editing a single phrase used to mean shipping a new version. Now versions live in the database: edit, comment, history and rollback to any earlier revision.

  • Две ветки одновременно: часть каналов отвечает по одной редакции, часть по другойTwo branches at once: some channels answer on one revision, some on another
  • Один канал, одна ветка: диалог не прыгает между формулировками на серединеOne channel, one branch: a dialogue never jumps between phrasings mid-way
  • Сравнение по делу: довольство ответами, доля закрытых без заявки, доля отказов и стоимостьComparison on substance: satisfaction, share closed without a ticket, refusal rate and cost
  • Мало данных, так и сказано: пока выборка мала, показывается «данных мало», а не случайные долиThin data says so: while the sample is small it shows "not enough data" rather than random ratios

Смена модели в три шагаSwitching models in three steps

1Сравнение на наборе: качество, доля отказов, задержка и стоимость на сотню запросовComparison on the set: quality, refusal rate, latency and cost per hundred requests
2Канарейка на живом трафике: малая доля каналов идёт на кандидата, основная модель не меняетсяCanary on live traffic: a small share of channels goes to the candidate, the main model stays
3Переключение по цифрам, а не по впечатлениюSwitch on the numbers, not on impressions

Эмбеддинги сравниваются иначе. У другой модели другая размерность вектора, поэтому сравнивать на боевой коллекции физически нельзя. Прогон берёт часть документов, раскладывает их во временные коллекции по одной на кандидата и считает качество поиска там. Боевую коллекцию он только читает, а временные снимаются в любом случае, в том числе если прогон упал. Embeddings are compared differently. A different model has a different vector dimension, so comparing on the production collection is physically impossible. The run takes a slice of documents, lays them into temporary collections, one per candidate, and measures retrieval quality there. It only reads the production collection, and the temporary ones are torn down in any case, including when the run fails.

После выкладки: прогон по стендуAfter a deploy: a smoke run

  • Сервис живThe service is alive
  • Админка открываетсяThe admin panel opens
  • Бот отвечает на контрольный вопросThe bot answers a control question
  • Индекс не протухThe index is not stale
  • Триаж запускается вхолостуюTriage runs dry

Не прошёл шаг, видно, какой именно. Откат делает человек: тихо откатываться прогон не имеет права. If a step fails, it is clear which one. A human performs the rollback: the run has no right to roll back quietly.

Копия и проверенное восстановлениеA backup, and a verified restore

  • Копия снимается онлайн: сервисы не останавливаютсяThe backup is taken online: services keep running
  • Опись рядом с копией: дата, последняя миграция, число записей и точекAn inventory sits beside it: date, last migration, record and point counts
  • Восстановление одной командой, с явным подтверждениемRestore in one command, with an explicit confirmation
  • Еженедельное учение на временных базах, боевые не трогаютсяA weekly drill on temporary databases; production is untouched

«Бэкап есть» и «из бэкапа можно подняться» это разные утверждения, и второе проверяется только практикой. "We have backups" and "we can come back from them" are different claims, and only practice verifies the second.

Настраивается без программистаConfigurable without a developer

Всё поведение системы меняется через веб-интерфейс. Шесть групп настроек. All system behaviour changes through the web interface. Six groups of settings.

АгентAgent

Поведение и каналыBehaviour and channels

  • Поведение и лимитыBehaviour and limits
  • Помощник инженераEngineer's assistant
  • МессенджерыMessengers
Источники знанийKnowledge sources

Что индексируетсяWhat gets indexed

  • Индексатор и файлыIndexer and files
  • Confluence, Jira, почтаConfluence, Jira, mail
  • Почта как канал, черновики статейMail as a channel, article drafts
Модели и поискModels and retrieval

Чем и как ищемHow the search runs

  • Языковая модель, векторизацияLanguage model, vectorisation
  • Векторная базаVector store
  • Поиск и ранжированиеSearch and ranking
Триаж и аналитикаTriage and analytics

Разбор входящегоHandling the inbox

  • Автотриаж, дубли, прогнозAuto-triage, duplicates, forecast
  • Сопровождение тикетов, отпускаTicket follow-up, absences
  • Аналитика и алертыAnalytics and alerts
Качество ответовAnswer quality

Измерение и экспериментыMeasurement and experiments

  • Обратная связьFeedback
  • Оценка качества и канарейкаQuality runs and canary
  • Инструкции модели и сравнение редакцийModel instructions and revision comparison
СистемаSystem

Периметр и эксплуатацияPerimeter and operations

  • Безопасность, аудит-журналSecurity, audit log
  • Логирование, трейсинг и стоимостьLogging, tracing and cost
  • База данных, веб-сервис, роли и входDatabase, web service, roles and login
Секреты не показываются обратноSecrets are never shown back
только отметка «задано»only a "set" marker
На лету или с перезапускомLive or with a restart
интерфейс сам подсказывает, что и когда применитсяthe interface tells you what applies when
Несколько подключенийSeveral connections
две Jira и два Confluence одновременно это обычная ситуацияtwo Jiras and two Confluences at once is an ordinary case

Похожая задача у вас?Facing a similar problem?

Я довёл эту систему от первой строки до продакшена и отвечаю за неё дальше. Расскажу, что переносится на другой контур, а что придётся строить заново. I took this system from the first line to production and still own it. I can tell you what transfers to another setup and what has to be rebuilt.

Обсудить задачуDiscuss it