Проекты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
Хранилища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
Путь одного сообщения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
Что происходит между вопросом и ответом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.
- Подвисла переформулировка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.
Раскрытие местоименийPronoun resolution
«а как её настроить?» превращается в «как настроить БИТ.Финанс»"and how do I set it up?" becomes "how do I set up the finance module"
Несколько формулировокMultiple phrasings
один вопрос ищется в нескольких редакциях, выдачи сливаютсяone question is searched in several variants and the results are fused
Гипотетический ответ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.
Почему одного вектора малоWhy vectors alone are not enough
Для векторного поиска «ОРУ-123», «1С:8.3.20» и «БИТ.Финанс» это почти одинаковый шум. Поэтому параллельно работает словарный поиск с учётом падежей. To a vector index, product codes, version numbers and internal system names all look like the same noise. So a lexical search with morphology runs alongside it.
Баллы двух методов живут в разных шкалах: складывать их без калибровки нельзя, поэтому вклад документа определяется его местом в каждой выдаче, а не баллом. The two methods score on different scales, so adding them up without calibration is meaningless. A document contributes by its rank in each result set, not by its score.
Словарный поиск написали сами. Готовая библиотека тянула полгигабайта зависимостей ради алгоритма на полсотни строк. Важность слова при этом считается по фактическому составу базы, а не по догадке: за это отвечает сама поисковая система. The lexical search is hand-written. The off-the-shelf library pulled half a gigabyte of dependencies for an algorithm of about fifty lines. Term importance is still computed from the real corpus rather than guessed: the search engine itself handles that.
Время поиска на боевой базе, секундRetrieval time in production, seconds
18-кратное ускорение при неизменившихся метриках качества, замер на базе в 296 000 фрагментов. Попутно выключился старый приём, добиравший совпадения по заголовку: минус девять запросов на каждый поиск. Переход на новый формат занял минуты вместо часов простоя и не потребовал переиндексации. An 18-fold speed-up with quality metrics unchanged, measured on a 296,000-chunk corpus. It also retired an older trick that topped up matches by title: nine fewer queries per search. The move to the new format took minutes instead of hours of downtime and needed no reindexing.
Когда бот не уверен, он это говоритWhen the bot is unsure, it says so
Два механизма честности вместо правдоподобной выдумки: отказ и уточняющий вопрос. Two honesty mechanisms instead of a plausible invention: refusal, and a clarifying question.
Пять признаков, по которым бот оценивает свой ответ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.
Опрос сервера ботом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
Постоянное соединениеA persistent connection
- В каналах отвечает только по упоминаниюIn channels it answers only when mentioned
- Оценка реакцией на сообщениеRating by reacting to the message
- Снятие реакции убирает оценкуRemoving the reaction removes the rating
Разбор очереди событий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
Отдельный ящик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.
База знаний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
Ключ может быть опечаткой, поэтому шаг проверки отдельный. Если ключа нет, смотрим, было ли привязано предыдущее письмо этой же цепочки. 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
Пороги настраиваются: чем выше планка, тем меньше ошибочных комментариев в чужих тикетах. 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.
Как это выглядит на одном тикете: How it looks on a single ticket:
Кандидат проходит три фильтраA candidate passes three filters
Единственный допустимый вариант поля получает максимальную уверенность по определению: выбирать не из чего. Если назначить выбранного не удалось, берётся следующий по рейтингу. 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
Первый режим ничего не меняет и пишет рекомендацию комментарием: безопасный для обкатки. Третий требует доверия к модели и настроенных справочников. 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.
Закрытые по молчанию помечаются отдельно: в отчётах их не спутать с решёнными. 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.
и цикл повторяется на следующем вопросе 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.
Детектор намеренно осторожен. Обычная документация сплошь и рядом содержит слова «инструкция», «система», «игнорировать»: ложное срабатывание дороже пропущенной экзотики, потому что фрагмент просто исчезнет из ответа и никто не поймёт почему. Обфускация снимается до сравнения: невидимые символы, смесь кириллицы и латиницы, переносы строк внутри фразы. Согласие на действие даёт только человек, текст из документа согласием не считается. 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
Итого 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
Как проверяется, что стало лучше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
Красный результат блокирует слияние. Отдельным прогоном идут тесты на живых базах в одноразовых контейнерах: 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
Эмбеддинги сравниваются иначе. У другой модели другая размерность вектора, поэтому сравнивать на боевой коллекции физически нельзя. Прогон берёт часть документов, раскладывает их во временные коллекции по одной на кандидата и считает качество поиска там. Боевую коллекцию он только читает, а временные снимаются в любом случае, в том числе если прогон упал. 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.
Поведение и каналыBehaviour and channels
- Поведение и лимитыBehaviour and limits
- Помощник инженераEngineer's assistant
- МессенджерыMessengers
Что индексируетсяWhat gets indexed
- Индексатор и файлыIndexer and files
- Confluence, Jira, почтаConfluence, Jira, mail
- Почта как канал, черновики статейMail as a channel, article drafts
Чем и как ищемHow the search runs
- Языковая модель, векторизацияLanguage model, vectorisation
- Векторная базаVector store
- Поиск и ранжированиеSearch and ranking
Разбор входящегоHandling the inbox
- Автотриаж, дубли, прогнозAuto-triage, duplicates, forecast
- Сопровождение тикетов, отпускаTicket follow-up, absences
- Аналитика и алертыAnalytics and alerts
Измерение и экспериментыMeasurement and experiments
- Обратная связьFeedback
- Оценка качества и канарейкаQuality runs and canary
- Инструкции модели и сравнение редакцийModel instructions and revision comparison
Периметр и эксплуатация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.