Почему ИИ в Kotlin — это не просто генерация кода?
В 2024-2025 годах каждый второй Kotlin-разработчик бросался оборачивать вызовы ChatGPT в suspend fun и радовался. К 2026 году хайф спал, а на поверхность всплыли горы технического долга. Я сам видел проекты, где AI-агенты вшиты прямо в контроллеры Ktor, а логика перемешана с JSON-парсингом. Это путь в ад.
Проблема не в том, что ИИ плохо генерирует код. Проблема в том, что мы забываем базовые принципы: SOLID, разделение ответственности, тестирование. Когда вы делегируете часть работы нейросети, вы не перестаёте быть архитектором. Наоборот — вы становитесь дирижером, который должен объяснить агентам, как играть ноты, не разрушив оркестр.
В этой статье я разберу практические архитектурные шаблоны для коллаборации с ИИ в Kotlin: как не выстрелить себе в ногу, используя LangChain4j, Ktor и автономных агентов. Если тема технического долга от AI-генерации вам близка — советую сначала глянуть на статью про зелёный CI и пустую архитектуру — там разобрано, как невинные AI-правки превращаются в чёрную дыру багов.
Три главные ошибки при работе с ИИ в Kotlin-проектах
Прежде чем показывать правильные паттерны, давайте пройдёмся по граблям. Я встречал их снова и снова.
Ошибка 1. ИИ как замена архитектору
Разработчик просит: «Сгенерируй микросервис на Ktor с PostgreSQL». Получает тонну кода. Коммитит. Через месяц выясняется, что нет обработки ошибок, транзакции размазаны, а зависимости захардкожены. Это классический архитектурный долг ИИ — нейросеть не видит контекста вашего проекта.
Ошибка 2. Игнорирование SOLID при интеграции AI-агентов
Самый частый антипаттерн — AiService, который делает всё: от вызова LLM до парсинга ответа и записи в БД. Это God Object. Когда придётся заменить OpenAI на локальную модель, вы проклянёте тот день, когда скопировали код из промпта.
Ошибка 3. Отсутствие контрактов и тестирования
AI-генерация — стохастический процесс. Вы не можете гарантировать, что модель вернёт валидный JSON. Если не писать тесты на парсинг и не фолбечить альтернативными промптами — прод упадёт в самый неожиданный момент. Подробнее про риск-менеджмент в AI-кодинге — в профессиональных практиках.
Теперь, когда мы знаем врага в лицо, построим правильную архитектуру.
Правильный фундамент: Agent-as-a-Service и SOLID
Ключевая идея: AI-агент — это внешний сервис с чётким контрактом. Вы не должны знать, какая модель внутри. Главное — интерфейс.
interface CodeAnalyzerAgent {
suspend fun analyze(code: String, context: AnalysisContext): AnalysisResult
}
data class AnalysisResult(val issues: List<Issue>, val suggestions: List<Suggestion>)
data class AnalysisContext(val language: String, val severityLevel: Severity)Теперь реализация с LangChain4j (актуальная версия на май 2026 — 1.0.0-M4, но API стабильно):
class LangChain4jCodeAnalyzer(
private val llm: ChatLanguageModel
) : CodeAnalyzerAgent {
override suspend fun analyze(code: String, context: AnalysisContext): AnalysisResult {
val prompt = """
Analyze the following Kotlin code. Return a JSON with two arrays:
- "issues": objects with "line", "message", "severity"
- "suggestions": objects with "title" and "description"
Code:
$code
""".trimIndent()
val response = llm.generate(prompt)
return parseResponse(response)
}
private fun parseResponse(raw: String): AnalysisResult {
// используем kotlinx.serialization надёжный парсинг
return Json.decodeFromString(raw)
}
}Важно: Не делайте парсинг ответа внутри агента доверенным. Всегда валидируйте поля. Если LLM вернула битый JSON — пусть метод бросает исключение, которое вы обработаете выше.
Этот подход закрывает Single Responsibility и Dependency Inversion. Захотели заменить OpenAI на Llama 3.5 — написали другую реализацию интерфейса. Тестировать агента изолированно? Легко: мокаете интерфейс.
LangChain4j + Ktor: строим AI-эндпоинты без боли
Теперь, когда агент живёт отдельно, нужно дать ему эндпоинт. Вот как это выглядит на Ktor 3.x (к 2026 году Ktor 3.1 стабилен):
fun Application.aiModule(agent: CodeAnalyzerAgent) {
routing {
post("/analyze-code") {
val request = call.receive<AnalysisRequest>()
val result = agent.analyze(request.code, request.context)
call.respond(result)
}
}
}Всё чисто. Но есть нюанс: LLM-запросы могут длиться 5-10 секунд. Ktor использует корутины, так что поток не блокируется. Однако не забывайте про timeout:
val deferred = async { agent.analyze(code, context) }
val result = withTimeout(30_000L) { deferred.await() }Типичная ошибка — не обрабатывать сценарий, когда LLM вернула мусор. Добавьте fallback: если парсинг упал — верните 503 с сообщением. Иначе клиент получит краш.
ACDD и атомарные коммиты: почему это спасёт ваш проект
Когда AI генерирует код, многие коммитят всё одной кучей. Потом git bisect бесполезен. Тут на помощь приходит ACDD и атомарное мышление.
Правило: каждый коммит — это одно логическое изменение. Если AI-агент сгенерировал новый эндпоинт и изменил модель данных — делайте два коммита. Разделяйте генерацию интерфейса, реализации и тестов. Так вы всегда сможете откатить часть изменений.
Я использую простой промпт для агента: «Сгенерируй код только для нового репозитория, не трогай сервисы». А потом повторный вызов: «Сгенерируй реализацию для интерфейса». Да, это дольше, зато потом head не болит.
Автономные AI-агенты на Kotlin Multiplatform
В 2026 году Kotlin Multiplatform уже мейнстрим. Если вы пишете AI-агента, который должен работать на Android, iOS и бэкенде — KMP ваш выбор. В практическом руководстве по KMP AI-агентам детально разобрана архитектура с expect/actual и общими интерфейсами.
Здесь важно: не тяните LLM на мобильное устройство. Делайте агента как клиент удалённого AI-сервиса. На устройстве — лёгкий слой кэширования и валидации ответов.
Метрики и мониторинг: как не ослепнуть
AI-генерация — чёрный ящик. Вы должны логировать каждый запрос к LLM: промпт, ответ, время, количество токенов. Иначе понять, почему качество упало, невозможно.
На уровне архитектуры добавьте декоратор, который оборачивает вызов агента:
class LoggingAgentDecorator(
private val inner: CodeAnalyzerAgent,
private val logger: Logger
) : CodeAnalyzerAgent {
override suspend fun analyze(code: String, context: AnalysisContext): AnalysisResult {
val start = System.currentTimeMillis()
val result = inner.analyze(code, context)
val duration = System.currentTimeMillis() - start
logger.info("Agent call: duration={}ms, issues={}", duration, result.issues.size)
return result
}
}Ссылка на статью про граждан-разработчиков — там показано, как ИИ меняет роли в команде, а мониторинг становится критическим для контроля качества.
Вместо заключения: никогда не доверяйте ИИ то, что нельзя переписать вручную
Через пару лет AI будет генерировать 90% кода. Это неизбежно. Но архитектурные решения — границы модулей, выбор паттернов, обработка ошибок — останутся за человеком. Если вы сейчас не научитесь выстраивать чёткие контракты и отделять AI-слой от бизнес-логики, вас заменят те, кто умеет.
Коллаборация с ИИ — это не рабство и не магия. Это инженерия. Добавьте SOLID, оберните агентов в сервисы, тестируйте каждый шаг. И тогда Kotlin в связке с AI станет вашим супероружием, а не источником бессонницы.