Финальное ревью проекта и итоги курса¶
Шаг 1¶
За время курса AI Support Desk вырос из одного запроса к языковой модели в полноценный учебный AI-бэкенд. В проекте появились чат с историей, структурированные ответы, инструменты, обработка файлов, HTTP API, тесты, журналирование, метрики и контейнерный запуск. Каждая возможность добавлялась постепенно, поэтому во время работы мы обычно видели только несколько связанных файлов.
В последнем разделе не будем снова запускать все сценарии. Мы уже проверяли их при изучении соответствующих тем, а в предыдущем разделе убедились, что FastAPI, Prometheus и Grafana вместе работают в Docker Compose. Повторение тех же действий почти ничего не добавит к пониманию проекта.
Вместо этого давайте проведём финальное архитектурное ревью. Сначала еще раз разберём и уже зафиксируем, для чего нужен каждый крупный каталог и как данные проходят между частями приложения.
Что находится в корне проекта?¶
Начнём не с отдельных классов, а с верхнего уровня. В корне находятся исходный код, проверки, учебные данные и файлы, описывающие запуск приложения:
ai-support-desk/
├── app/ основной код приложения
├── tests/ автоматические тесты Python-кода и HTTP API
├── evals/ сценарии оценки поведения настоящей модели
├── monitoring/ конфигурация Prometheus
├── sample_files/ учебные документы, таблицы и изображения
├── main.py запуск консольного чата
├── Dockerfile сборка образа AI-бэкенда
├── docker-compose.yml совместный запуск API, Prometheus и Grafana
├── requirements.txt зависимости Python
├── .env локальные настройки и секреты
├── .dockerignore файлы, не попадающие в Docker-образ
└── .gitignore файлы, не попадающие в репозиторий
Каталог app содержит код, необходимый самому приложению. Именно он копируется в Docker-образ и импортируется точками входа. Здесь находятся сервисы, модели данных, интеграция с ProxyAPI, FastAPI и другие действующие части системы.
Каталог tests предназначен для автоматических тестов. В нём мы используем заранее подготовленные ответы вместо настоящей модели и проверяем предсказуемые правила: HTTP-статусы, работу сессий, формат SSE, временные файлы, лимиты и расчёт стоимости. Такие проверки должны давать одинаковый результат при каждом запуске.
Каталог evals решает другую задачу. Его сценарии обращаются к настоящей модели и помогают оценивать качество её поведения: соблюдает ли она правила, запоминает ли контекст и возвращает ли необходимые сведения. Результат evals может немного меняться, поэтому спорные ответы дополнительно читает человек.
В sample_files находятся только учебные входные данные для ручных примеров: PDF, DOCX, Markdown, таблицы и изображения. Эти файлы не являются частью бизнес-логики. В реальном приложении документы поступают от клиента через HTTP API, а не выбираются из этого каталога.
Каталог monitoring хранит конфигурацию Prometheus. Сам Prometheus не является Python-библиотекой внутри app: это отдельный сервис, который периодически обращается к /metrics. Поэтому его конфигурация лежит рядом с docker-compose.yml, а не среди модулей приложения.
У проекта остались две точки входа. Корневой main.py запускает консольный чат, а app/api/main.py создаёт FastAPI-приложение. Они предоставляют разные интерфейсы, но используют одну функцию build_application() и одни и те же сервисы. Благодаря этому бизнес-логика не копируется между консолью и HTTP API.
Как устроен пакет app?¶
Теперь раскроем основной пакет подробнее:
app/
├── api/ HTTP-слой FastAPI
├── files/ безопасная работа с файлами разных форматов
├── llm/ единая граница обращения к модели
├── models/ прикладные модели данных
├── observability/ структурированные логи и наблюдение за LLM-вызовами
├── prompts/ версии инструкций для модели
├── services/ пользовательские сценарии приложения
├── tools/ функции, доступные модели через Tool Calling
├── application.py создание и связывание зависимостей
├── chat_history.py хранение истории и подготовка контекста
├── config.py чтение и проверка настроек
└── console.py взаимодействие с пользователем в терминале
app/api отвечает только за HTTP-взаимодействие. В routes находятся эндпоинты чата, анализа обращений, файлов, состояния приложения и метрик. Каталог schemas содержит Pydantic-модели именно для HTTP-запросов и ответов. Файлы dependencies.py, errors.py, middleware.py, lifespan.py и sse.py помогают маршрутам получать сервисы, одинаково оформлять ошибки, присваивать Request ID, управлять временем жизни приложения и передавать потоковые события.
Разделение routes и schemas не является обязательным правилом FastAPI, но удобно для выросшего проекта. Эндпоинт описывает действие, например отправку сообщения, а схема — форму данных, которые клиент передаёт или получает. Благодаря этому один файл не превращается в смесь маршрутов, моделей и вспомогательного кода.
app/services содержит прикладные сценарии. Например, ChatService управляет диалогом, SupportRequestAnalyzer превращает обращение в типизированные данные, а DocumentAIService координирует анализ файлов. Сервис отвечает на вопрос «что должно произойти в этом сценарии», но не знает деталей Swagger UI и не создаёт соединение с ProxyAPI самостоятельно.
app/llm изолирует работу с моделью. client.py содержит вызовы SDK, errors.py — понятные приложению ошибки, limits.py — ограничение одновременно выполняющихся генераций, а cost.py — приблизительный расчёт стоимости. Если способ подключения к провайдеру изменится, основная часть изменений останется внутри этого пакета.
app/prompts хранит инструкции отдельно от сервисов. Для каждого сценария создан собственный каталог с файлами версий и реестром. Поэтому новая формулировка промпта не перезаписывает прежнюю, а evals могут сравнивать поведение разных версий.
app/models содержит прикладные Pydantic-модели, которые описывают смысл данных внутри приложения: результат анализа обращения или документа. Они отличаются от моделей в app/api/schemas. Прикладная модель нужна сервисам независимо от способа запуска, а HTTP-схема определяет внешний контракт конкретного эндпоинта.
app/tools объединяет функции, которые модель может выбрать через Tool Calling. В отдельных файлах находятся узкие операции получения статуса заказа, чтения правил возврата и создания заявки. registry.py описывает доступные инструменты, execution.py проверяет аргументы и безопасно вызывает обработчик, а default_registry.py собирает стандартный набор.
app/files отвечает за путь от недоверенной загрузки до данных, пригодных для анализа. Общие файлы проверяют размер, имя и формат, documents извлекает текст из документов, tables читает и вычисляет показатели таблиц, а images подготавливает изображения для мультимодального запроса. Эти операции находятся отдельно от HTTP-эндпоинта, поэтому их можно использовать и тестировать без FastAPI.
app/observability содержит общую логику наблюдаемости. context.py переносит Request ID по цепочке вызовов, logging.py настраивает JSON-формат, а llm.py создаёт технические события обращений к модели. Этот пакет не относится только к API или только к LLMClient: его данные связывают несколько частей приложения.
Структура становится понятнее, если посмотреть не только на названия каталогов, но и на направление вызовов:
HTTP-слой принимает данные и передаёт их сервису. Сервис управляет сценарием и при необходимости обращается к LLMClient, обработчикам файлов или инструментам. Ответ проходит обратно тем же путём и преобразуется во внешний формат. Файл application.py заранее создаёт нужные объекты и связывает их между собой, поэтому сервисам не приходится самостоятельно искать настройки и конструировать зависимости.
Обратите внимание на важную границу, модули services, llm, files и tools не должны зависеть от конкретного эндпоинта FastAPI. Благодаря этому тот же ChatService работает и в консоли, и через HTTP API, а тест может вызвать его без запуска веб-сервера.
При этом не нужно стремиться вынести каждый класс в отдельный каталог. Хорошая структура создаётся вокруг разных обязанностей, а не ради максимального количества файлов. В текущем проекте уже отделены интерфейсы, прикладные сценарии, интеграция с моделью, обработка файлов и инструменты.
Подготавливаем конфигурацию для другого разработчика¶
После того, как мы еще раз зафиксировали какую роль играет каждая часть AI Support Desk и почему файлы находятся именно в этих каталогах, давайте проведём точечную правку проекта и подготовим проект так, чтобы другой разработчик мог разобраться в его назначении и настроить своё окружение.
Проект работает на нашем компьютере, потому что в локальном файле .env уже находятся необходимые настройки. Однако настоящий .env содержит API-ключ и поэтому не должен передаваться вместе с исходным кодом. Если другой разработчик получит проект без пояснений, ему придётся изучать Settings и самостоятельно угадывать список переменных.
Для решения этой проблемы создают .env.example — безопасный образец конфигурации без настоящих секретов. В корне проекта добавьте файл:
LLM_API_KEY=replace_with_your_key
BASE_URL=https://api.proxyapi.ru/openai/v1
MODEL=gpt-4.1
TEMPERATURE=0.2
MAX_OUTPUT_TOKENS=1000
CHAT_INPUT_TOKEN_BUDGET=6000
REQUEST_TIMEOUT_SECONDS=30
STREAM_TIMEOUT_SECONDS=60
LLM_MAX_CONCURRENT_REQUESTS=4
LLM_QUEUE_TIMEOUT_SECONDS=5
LLM_INPUT_PRICE_PER_MILLION=
LLM_OUTPUT_PRICE_PER_MILLION=
LLM_COST_CURRENCY=RUB
MAX_RETRIES=2
TOOL_MAX_STEPS=3
APP_ENV=development
FILE_MAX_SIZE_BYTES=10485760
.env.example не используется приложением автоматически. Новый разработчик копирует его под именем .env, вставляет собственный ключ и при необходимости меняет параметры. Значение replace_with_your_key специально показывает, что это место нужно заполнить. Пустые тарифы допустимы: без них приложение продолжит учитывать токены, но не сможет рассчитать приблизительную стоимость.
Образец должен оставаться синхронизированным с классом Settings. Когда в конфигурации появляется новая обязательная переменная, её нужно добавить и в .env.example. Иначе приложение будет работать у автора, но новый разработчик столкнётся с непонятной ошибкой при первом запуске.
Рядом создайте README.md. Это первая страница, которую обычно открывает человек после получения проекта. README не должен пересказывать весь курс или объяснять каждую строку кода. Его задача — быстро ответить на основные вопросы:
- какую проблему решает AI Support Desk
- какие возможности уже реализованы
- какие версии Python и Docker нужны
- как подготовить
.envпо образцу - как запустить консоль и FastAPI локально
- как поднять всю систему через Docker Compose
- как выполнить
pytestи evaluation-набор - где открыть Swagger UI, Prometheus и Grafana
- какие ограничения есть у учебной версии
Команды в README лучше указывать вместе с рабочим каталогом и коротким пояснением результата. Тогда инструкция помогает не только автору, который и так помнит устройство проекта, но и человеку, впервые открывшему репозиторий. Для данного проекта я написал следующее README.md, можете посмотреть его и использовать в своем проекте.
Наконец, откройте .gitignore и убедитесь, что там есть .env, каталоги виртуального окружения, кэш Python и файлы IDE. Для macOS и новых отчётов evals дополнительно можно добавить:
В evals/results могут появляться новые JSON-отчёты при каждом запуске, поэтому обычно их не добавляют в репозиторий все подряд. Если команде нужен эталонный отчёт для сравнения, его лучше выбрать осознанно и сохранить отдельно под понятным именем.
Напомню про различие между .gitignore и .dockerignore. Первый определяет, какие файлы не попадут в Git. Второй ограничивает контекст Docker-сборки. Например тесты могут храниться в репозитории и быть полезны разработчикам, но при этом не копироваться в рабочий образ приложения.
В заключительном шаге мы зафиксируем, что именно создано, где проходит граница учебной версии и какие направления логично изучать дальше.
Шаг 2¶
Поздравляю! Вы дошли до заключительной лекции большого курса. Здесь не будет нового кода и ещё одного повторного запуска проекта. Вместо этого мы посмотрим на итоговую систему с позиции AI-разработчика: какие задачи она решает, какие инженерные гарантии появились вокруг модели и какие ограничения мы осознанно оставили за границами учебной версии.
За шесть модулей мы прошли путь от первого запроса к языковой модели до AI-бэкенда с несколькими пользовательскими сценариями, HTTP API, тестами, наблюдаемостью и воспроизводимым запуском. Многие возможности, которые сейчас выглядят естественной частью проекта, появлялись постепенно. Сначала мы разбирались с каждой задачей отдельно, а затем связывали решения в общую систему.
Теперь AI Support Desk умеет вести чат с историей, возвращать структурированные данные, безопасно вызывать инструменты, анализировать документы, таблицы и изображения. Клиент обращается к этим возможностям через FastAPI, а приложение контролирует ресурсы, обрабатывает ошибки, ведёт технические журналы и собирает метрики.
Но главный результат курса находится не в количестве файлов, классов или эндпоинтов. Мы научились помещать вероятностную модель внутрь обычного Python-приложения с понятными границами:
Модель не получает произвольный доступ к коду, файловой системе или данным. Пользовательский ввод остаётся недоверенным, аргументы инструментов проверяются Pydantic-моделями, а изменяющая операция требует отдельного подтверждения. Ошибки провайдера не выходят наружу как случайные исключения SDK, а преобразуются в контролируемые ошибки приложения и HTTP-ответы.
Что мы создали за курс?¶
В первом модуле проект начинался с одного обращения к LLM. Мы разобрались, из каких сообщений состоит запрос, как параметры влияют на генерацию, где хранить секреты и почему работу с провайдером полезно закрыть собственным LLMClient.
Затем появился чат с историей. Мы научились отделять сессии пользователей, ограничивать контекст по размеру, сохранять резюме старых сообщений и передавать ответ частями. Промпты получили версии, а evaluation-набор позволил сравнивать поведение модели после изменений.
В третьем модуле свободный текст превратился в данные с постоянной формой. Мы использовали структурированные ответы и Pydantic, разделили синтаксическую и смысловую проверку, добавили ограниченный исправляющий запрос. Затем познакомились с Tool Calling: создали реестр инструментов, общий исполнитель, аудит и подтверждение операций, изменяющих данные.
Четвёртый модуль расширил приложение работой с файлами. Мы проверяли недоверенные загрузки, извлекали структуру документов, читали таблицы обычным Python-кодом, готовили изображения для мультимодальной модели и собирали контекст с однозначными ссылками на источники.
В пятом модуле эти возможности стали AI-бэкендом на FastAPI. Появились HTTP-контракты, зависимости, единый формат ошибок, Request ID, жизненный цикл чат-сессий, SSE и безопасная временная загрузка файлов. Автоматические тесты начали проверять Python-код и HTTP API без расходования баланса ProxyAPI.
В шестом модуле мы занялись эксплуатационными свойствами проекта: ограничили одновременно выполняющиеся генерации, добавили оценку стоимости, структурированные логи и метрики. Prometheus научился хранить изменения показателей во времени, Grafana — показывать их на дашборде, а Docker Compose объединил AI-бэкенд и наблюдение в одну воспроизводимую среду.
В результате в проекте появились:
- единая интеграция с LLM-провайдером и контролируемые ошибки
- версионируемые промпты, история, суммаризация и потоковая генерация
- структурированные ответы и несколько уровней проверки
- инструменты с ограниченными полномочиями и подтверждением изменений
- анализ документов, таблиц и изображений
- HTTP API, сессии, загрузка файлов и SSE
- автоматические тесты и evals для разных видов качества
- ресурсные ограничения, логи, метрики и оценка стоимости
- Dockerfile и единая среда Docker Compose
Теперь у вас есть законченный учебный проект и понимание того, из каких инженерных частей складывается реальное AI-приложение.
Какие ограничения остаются?¶
AI Support Desk стал законченным учебным AI-бэкендом, но для полноценной эксплуатации его потребуется дополнительно развить.
Сейчас сессии и заявки хранятся в памяти и исчезают после перезапуска. В приложении нет регистрации и проверки прав доступа, а ограничение LLM-запросов действует только внутри одного процесса. Prometheus, Grafana и Docker Compose настроены для локального запуска, а не для развёртывания системы под реальной нагрузкой.
Это нормальная граница учебного проекта. Мы сосредоточились на работе с LLM и построении контролируемого AI-бэкенда, а полноценная инфраструктура, постоянное хранение данных и управление пользователями являются отдельными большими темами.
Если вы хотите продолжить работу над проектом самостоятельно, можете выбрать одну из следующих задач:
- перенести чат-сессии и заявки в базу данных
- добавить регистрацию, аутентификацию и связь сессии с пользователем
- реализовать рейтлимит для отдельных пользователей
- добавить централизованное хранение журналов
- настроить уведомления в Grafana при росте ошибок или стоимости
- подключить поиск по собственной базе знаний
- создать новый безопасный инструмент для чата
- подготовить автоматическое развёртывание приложения
Не обязательно реализовывать всё сразу. Выберите одну интересную задачу и попробуйте самостоятельно встроить её в существующую архитектуру. Так AI Support Desk станет основой для вашего следующего проекта, а полученные знания постепенно превратятся в уверенный практический навык.
Готовый проект на GitHub¶
Финальную версию AI Support Desk я загрузил в репозитории проекта на GitHub. В нём можно посмотреть итоговую структуру каталогов, сопоставить файлы со своими результатами и вернуться к отдельным частям проекта после завершения курса.
Куда двигаться дальше?¶
Дальнейшее направление зависит от того, какую часть проекта вы хотите усилить. Если вам интересно создавать полноценные веб-сервисы, глубже изучите FastAPI. В этом курсе мы использовали только возможности, необходимые для AI-бэкенда: маршруты, Pydantic-схемы, зависимости, обработку ошибок, загрузку файлов, lifespan и потоковые ответы. Сам фреймворк предоставляет значительно больше возможностей.
Продолжить изучение FastAPI можно в моих отдельных курсах:
- FastAPI для начинающих — победитель в номинации «Лучший платный курс»
- Продвинутый FastAPI для продолжающих
- Полный курс по FastAPI: от новичка до профессионала — пакет из двух курсов
- FastAPI для профессионалов: онлайн-школа на чистой архитектуре
Чтобы перенести чат-сессии, заявки и другие данные из памяти процесса в постоянное хранилище, потребуется работа с базой данных. Этой теме посвящён курс SQLAlchemy: практическая работа с базой данных в Python.
В качестве небольшого бонуса я хочу предложить вам скидку 50% на любой из моих курсов. Для получения скидки оставьте отзыв об этом курсе, а затем напишите мне на электронную почту perminoff-ilya@yandex.ru или в Telegram — @Perminoff_ilya.
А если вы хотите развиваться именно в создании более сложных AI-систем, следующим шагом станут поиск по базе знаний, RAG, агенты, MCP и эксплуатация таких решений. Сейчас я также готовлю курс «AI Engineer на Python: RAG, агенты, MCP и production-системы». Он продолжит темы этого курса и будет сосредоточен на системах, в которых модель работает с внешними знаниями, несколькими шагами и более развитой инфраструктурой.
Поздравляю с окончанием курса и желаю успехов в следующих AI-проектах!
Шаг 3¶
Я буду очень рад, если вы расскажите, всё ли у вас получилось, какие темы оказались наиболее полезными, а какие задания или объяснения вызвали затруднения.
Мне очень важна ваша обратная связь, ведь именно она поможет улучшить курс, сделать материал понятнее и добавить новые полезные темы.
Напишите ответ в свободной форме. Например вы можете рассказать:
- что вам понравилось
- что показалось сложным или непонятным
- каких тем или практических заданий вам не хватило
- что, по вашему мнению, стоит изменить или дополнить