Во время инцидента инженер одновременно работает с несколькими источниками: пользовательской документацией, SRE-ранбуками, фрагментами кода, логами и описаниями релизов. Информация есть в репозитории, но разбросана по файлам — инструкция по queue lag лежит в одном месте, описание регистрации в другом, детали поведения функции только в docstring'е. Разработчик под ником llmortem собрал одноимённый FastAPI-сервис, который решает именно эту задачу: ищет релевантные фрагменты по локальным источникам и передаёт их как контекст в локальную LLM.
RAG (Retrieval-Augmented Generation) — подход, при котором языковая модель получает не только вопрос пользователя, но и заранее найденные фрагменты документов. Это позволяет модели отвечать точнее и не «придумывать» детали, которых она не знает. Без такой прослойки LLM не имеет доступа к внутренней документации проекта и отвечает слишком обобщённо.
| Endpoint | Назначение |
|---|---|
| GET /health | Проверка состояния сервиса |
| POST /reindex | Пересборка индекса после изменений в документах или коде |
| POST /search | Проверка retrieval отдельно от LLM |
| POST /ask/docs | Ответы на вопросы по документации и SRE-сценариям |
| POST /postmortem | Генерация черновика постмортема |
| POST /draft-doc | Генерация черновика документации |
| POST /v1/chat/completions | OpenAI-compatible endpoint для подключения OpenWebUI |
Архитектура llmortem линейна: запрос от пользователя приходит через OpenWebUI, попадает на /v1/chat/completions в FastAPI-приложение, там определяется тип запроса, запускается BM25-поиск по нужным источникам, собирается prompt и отправляется в Ollama. OpenWebUI воспринимает llmortem как обычный OpenAI-compatible backend — достаточно указать Base URL и любой API-ключ. Это избавляет от необходимости писать собственный интерфейс.
Вместо векторной базы используется BM25: для технических терминов вроде queue lag или 5xx точный лексический поиск работает достаточно хорошо.
Вместо векторной базы данных автор выбрал BM25 — классический алгоритм лексического поиска. Для технической документации это оправданно: запросы вроде «queue lag», «5xx», «database connection» или «reset_password» содержат точные термины, которые встречаются в ранбуках дословно. BM25 не требует embedding-модели, не нужно пересчитывать векторы при обновлении документов, а сама библиотека подключается без дополнительной инфраструктуры. Ограничение очевидно: если пользователь формулирует вопрос иначе, чем написано в документации, поиск может не найти нужный фрагмент. Автор называет гибридный поиск BM25 + embeddings логичным следующим шагом.
Отдельный блок — поиск по коду. Сервис использует Python AST для извлечения docstring'ов модулей, классов и функций, а также сигнатур. Документация нередко отстаёт от реализации, тогда как docstring'и остаются актуальными. Если добавить в репозиторий файл с описанием процесса сброса пароля в docstring'е модуля, после переиндексации через /reindex запрос «How to change the password?» найдёт именно этот фрагмент.
Сервис различает три типа запросов: документационный, SRE/incident и общий. Для каждого типа приоритизируются разные источники: запрос про queue lag направляется в ранбуки, а не в пользовательскую документацию. Индекс строится при старте или по вызову /reindex, поэтому на каждый запрос выполняется только определение типа, BM25-поиск, сборка prompt'а и вызов Ollama. По словам автора, узким местом в прототипе была именно локальная модель, а не RAG-прослойка — и даже слабая модель отвечает заметно лучше, когда получает релевантный фрагмент ранбука в контексте.
llmortem позиционируется как RAG-ядро, а не полноценная платформа управления инцидентами: нет on-call-ротаций, эскалаций, автоматического таймлайна, интеграций с Grafana, Slack или GitLab. Зато есть endpoint /postmortem для генерации черновика постмортема и /draft-doc для документации. Код опубликован на GitHub.
