(java || kotlin) && devOps
342 subscribers
13 photos
2 videos
7 files
419 links
Полезное про Java и Kotlin - фреймворки, паттерны, тесты, тонкости JVM. Немного архитектуры. И DevOps, куда без него
Download Telegram
Все AI dev агенты делятся на 2 категории по тому, как хранят свои правила.

Один файл:
Claude Code — CLAUDE.md
Gemini CLI — GEMINI.md
OpenAI Codex CLI — AGENTS.md
JetBrains Junie — .junie/guidelines.md

Папка с несколькими файлами:
Cursor — .cursor/rules/*.mdc
Windsurf — .windsurf/rules/*.md
Cline — .clinerules/*.md
Roocode — .roo/rules/*.md
GitHub Copilot — .github/instructions/*.instructions.md
JetBrains AI Assistant — .aiassistant/rules/*.md
Continue — .continue/rules/*.md
GigaCode - .gitverse/pr_rules/*.md (для версии на GitVerse)

Cline тут выделяется тем, что вообще говоря позволяет оба варианта - файл .clinerules или одноименный каталог.

Особенность Claude - кастомные команды, хранятся в кастомные команды в ~/.claude/commands/<name>.md. В принципе можно использовать и как аналог rule при явном указании в чате. Но без автозагрузки.

И еще сильнее выделяется OpenCode.
Он вначале пытается быть совместимым с OpenAI (AGENTS.md), если не нашел - с Claude Code (CLAUDE.md). А кроме того позволяет через customInstructions в opencode.json подключать произвольные файлы, включая загрузку по http, что является уникальной фичей.

Какие плюсы\минусы у этих подходов?

Один файл - простота.
Особенно учитывая тот факт, что практически ни один агент не дает менять имя файла\папки - т.е. имеем дело с convention over configuration во всей красе.
Пару слов про смену имени: единственное исключение - Gemini CLI, позволяет задать contextFileName в settings.json.
Еще в теории с этим подходом больше шансов получить непротиворечивые правила, но с ростом размера файла это преимущество нивелируется.

Каталог - а мне этот вариант кажется более удобным - имеет следующие плюсы:
1) если в репо лежит разнородный код, который пишут разные люди: фронт, бэк, devops, автотесты то удобнее, если у каждого будет свой файл с правилами. Да, AI способствует T-shape, но это пока что процесс, а не свершившийся факт.
2) несколько файлов точно легче читать и править человеку. А со слабыми моделями - возможно и LLM.
3) в теории при наличии нескольких файлов LLM исходя из текущей задачи сама могла бы выбрать только нужные, тем самым уменьшив расход токенов и контекстного окна и ускорив работу.

Обходной вариант для single rule агентов - можно указать в промте или rules: "прочитай инструкции из каталога xxx", - и т.об решить проблему. Но это менее надежно.

И главный вопрос - как избежать vendor lock и не нарушать DRY?
Делаем базовую папку с правилами, дале в Windows - создаем ссылку с помощью junction, в Linux - symlink.
И лучше добавить в .gitignore для надежности, чтобы случайно не задублировалось.
Для single rule агентов можно навайбкодить утилиту по склеиванию всех файлов в один, или использовать готовую cursor2claude.

Но вообще говоря эта область прям напрашивается на стандартизацию - либо через convention, либо через использование отдельного компонента.

#ai #ai_agents
Agile мир победил, микросервисы оказались сильней)

Вот данные из моей любимой книжки по разработке.

Процентное соотношение проектов по размеру команды:

Размер команды  | Доля
----------------|------
1–3 человека | 25%
4–10 человек | 30%
11–25 человек | 20%
26–50 человек | 15%
>50 человек | 10%

Источники: Beck and Perkins (1983), Highsmith (2002), Boehm and Turner (2003)


Медиану можно оценить ~9, среднее ~19, но среднее мало информативно из-за сильной правой асимметрии (маленьких команд сильно больше, чем больших).

Я бы из него сделал вывод, что микросервисы были всегда, просто раньше назывались по-другому — например, SOA (Service Oriented Architecture).
Но конечно ситуация меняется (хотел написать «не могла не измениться», но вспомнил свой же пост про двойное отрицание))).

А вот более современные данные, не такие подробные.

В исследовании Rodriguez et al. (JSS, 2012) на базе 951 проекта — 75% имели команду менее 10 человек. Медиана = 5, среднее ≈ 7.9.


Если сравнить 2003 и 2012 годы — медиана уменьшилась почти в 2 раза: 9 → 5.

Ещё есть данные опроса пользователей JetBrains (2024) https://www.jetbrains.com/lp/devecosystem-2024/

Размер команды  | Доля
----------------|------
1 человек | 8%
2–7 человек | 49%
8–12 человек | 22%
13–20 человек | 10%
21–40 человек | 6%
>40 человек | 5%


Медиана ≈ 6 человек, среднее ≈ 11. Примерно то же, что в 2012, только как ни странно числа стали чуть больше). Зато видно, что больших команд стало в 2+ раза меньше.

По моему опыту (кровавый enterprise) медианный размер команды на данный момент выше, ближе к 9. Но то, что 80% команд укладываются в 12 человек — сходится.
В 2003 году было ~60%.
Т.е. мы пока одной ногой шагнули в новую эру) enterprise требует жертв. Но тренд понятен.

О причинах — часть уже назвал: Agile и микросервисы. Ещё — улучшившийся инструментарий: IDE и фреймворки/библиотеки под любую потребность. А сейчас ещё и AI.

Так что думаю, что к 6 людям в команде мы все скоро придём. Будет ли меньше? В теории да: ВП, аналитик, 1–2 разработчика, тестировщик.

P.S. Т.к. в таблицах процент команд, а не процент людей - в командах более 12 человек все еще работает более 50% разработчиков. Было - более 80%. Интересно кто это?

P.P.S. Станет ли медианным значением 1 — ВП, который с помощью AI пилит и тестирует сервисы сам? ...Где-то да, уже сейчас. Но массово — может помешать Skynet. Или поспособствовать — сидишь себе в матрице, вайб-кодишь с AI микросервисы.

#book_review #agile #ai #microservices
👍1
Иногда кажется, что если попросить какую-то LLM модель проверить информацию, указав при этом, что получил ее от другой модели - она это делает с какой-то особой тщательностью)))
Скорее всего кажется - как проекция взаимоотношений создателей на их модели. Я про OpenAI, Claude, Grok и Gemini.

Но в любом случае это отличный быстрый способ проверки информации из LLM.

И тут хорош Perplexity, т.к. там все перечисленные модели есть.

#ai
Как AI интегрируется в существующие программные продукты?

Пример.
Есть такое понятие как API шлюз - прокси, позволяющий согласовать API из разных источников, навесить туда аутентификацию и авторизацию, rate limiting, мониторинг и некоторые другие функции.
Один из самых известных open source шлюзов - Kong Gateway.

Где тут AI?
А вот https://developer.konghq.com/plugins/?category=ai

Что можно навесить на шлюз:

1) AI Proxy - API разных LLM моделей отличается, поэтому актуальной является конвертация API. Если быть точным: на вход OpenAI API, на выходе - другие модели.
Вот список https://developer.konghq.com/plugins/?category=ai
Почему на входе OpenAI формат думаю понятно, так работает большинство AI Proxy.

2) AI Prompt Decorator - добавляет текст в начало и конец промта. Область применения видится для случаев, когда есть некий AI чат-клиент и недостаточно функционала для AI агента (сервера).
Тогда данный компонент может сформировать системный промт или обогатить промт историей.
Еще важный момент - текст в начале и в конце промта (который мы добавляем таким образом) имеет больший вес для модели.

3) AI Request Transformer\AI Response Transformer - обогащение\преобразование промта с использованием ИИшки.
Например, добавить страну к каждому городу в промте.
Тут конечно важен вопрос скорости, прокси не должен тормозить запрос

4) AI Prompt Guard - ограничение доступных запросов по regexp. Не 100% гарантия, но зато быстро.

5) AI Prompt Template - создаем набор типовых запросов к модели, от клиента просим только заполнить {{параметры}}

Есть еще опции, требующие лицензии, среди них я бы отметил:

1) AI LLM as Judge - валидацию ответа LLM другой моделью из коробки

2) AI MCP Proxy - преобразование протокола MCP в HTTP или объединение нескольких MCP серверов в один + возможность навесить на MCP запрос все фичи API шлюза - аутентификацию, rate limiting, мониторинг. Видится полезным для быстрого подключения существующих сервисов компании как тулов для AI агента.

3) AI Prompt Compressor - сжатие пользовательского промта. Цель - снижение стоимости. Интересно, но обязательно надо тестировать качество получаемых ответов.

4) AI Semantic Cache - кэшируем в векторной БД ответы LLM опять же для снижения стоимости

Выводы.
- все вышеперечисленные плагины можно рассматривать как AI паттерны, которые можно реализовать или на отдельном шлюзе, или внутри сервиса
- AI-шка внедряется в традиционные стеки

#ai
Магические 80% покрытия кода тестами.

Часто возникает вопрос - почему 80?
Краткий ответ - почему бы и нет)

А если серьезно: оценка может быть числовой (процент покрытия) или бинарной (достаточно или нет).
Бинарная плоха тем, что не документирована и зависит от мнения эксперта. Это может привести к проблемам на код-ревью.
А тут умные люди из SonarQube придумали метрику покрытия https://docs.sonarsource.com/sonarqube-server/user-guide/code-metrics/metrics-definition#coverage
и дали её рекомендуемое значение. И это не 100%, т.е. запас есть.
Считаю, что от этой рекомендации стоит отталкиваться.
К тому же JaCoCo, обычно используемый для расчета покрытия, позволяет гибко настраивать сам процесс.

Следующий вопрос, который может возникнуть: "Почему ради достижения 80% я должен писать тесты на элементарный код?" Я про getter, setter и все, все, все https://t.me/javaKotlinDevOps/34
Ответ: "Нет, не нужны эти тесты".
Лишний код можно исключить из покрытия:
1) generated код исключается по умолчанию начиная с JaCoCo 0.8.2
2) generated код Lombok - через lombok.addLombokGeneratedAnnotation = true (жаль, что только для Lombok это можно сделать, но есть еще альтернатива - см. следующий абзац.)
3) тривиальный код - явно исключая классы или пакеты по маске через настройки JaCoCo. Если какой-то код не получается исключить таким образом - это повод задуматься о рефакторинге.

Еще вопрос: "Зачем писать модульные (unit) тесты ради покрытия на интеграционный код: сервисные классы или классы контроллеров, которые представляют линейную цепочку вызовов методов?"
Ответ: "Не обязательно писать unit тесты".
Для простых микросервисов является нормальной перевернутая пирамида тестирования - когда интеграционных тестов разработки больше, чем модульных.
Простой код можно покрыть интеграционными тестами, включив их в общее покрытие. JaCoCo позволяет это настроить. Единственная проблема может быть, если тесты находятся в одном модуле, а код для покрытия - в другом, но и она решается.

И тут может возникнуть самый главный вопрос - а это не читерство? Чем это лучше отсутствия цели по покрытию?
Ответ: лучше тем, что мы четко задаем - вот это код не нужно покрывать тестами, а остальной - нужно.
И смотря на паттерны исключения - на их состав и количество - можно их быстро оценить и при необходимости скорректировать.

Ну и если ничего не помогло - тогда уже стоит задуматься о новом Quality Gate в SonarQube с меньшим процентом покрытия.
Никакой магии нет - https://t.me/javaKotlinDevOps/403.
Но числовой показатель (80% по умолчанию), задокументированные исключения и цель покрыть максимум кода, в котором возможны ошибки, быть должны.

P.S. Еще AI можно попросить тесты написать, у него это неплохо получается)

#unittests #integration_tests #java
Что такое Agile для разработчика?

Возьмем SCRUM, как наиболее типичный его вариант.

IMHO конечно.

Это не церемонии - Daily, планирование, ретро, демо.
Не наличие Scrum мастера в команде.
Это красивый burn-down chart и velocity.
Не poker planing и оценка в story point.
Не работа с досками.
И даже не работающий инкремент каждую неделю.

База - это две вещи:

1) навык декомпозиции задач.
Который приводит к небольшим инкрементам.
А конечные цели:
- минимум зависимостей от других
- маленькие PR = быстрое ревью и вливание
- инкрементная интеграция кода = возможность вливания в целевую ветку и отправку на тестирование без ожидания других доработок.

2) точность оценки задач.
Не важно в чем - в story point, человеко-днях или в конкретных датах.
Оценка, кстати, зависит от декомпозиции, т.к. мелкие задачи можно оценить точнее, а потом оценки просуммировать.
Конечная цель - совпадение прогнозируемых сроков с реальными.
В данном случае речь про сроки этапа разработки, за соблюдение сроков интеграции со смежниками отвечает PO, DL и команда в целом.

Не всегда эти цели достижимы на 100%, но ставить их нужно. Если их себе ставить, то навык рано или поздно появится.

И традиционные ответы на не заданные вопросы)
Вопрос: "А разве это не навыки сеньоров?"
Ответ: когда-то возможно да, но с учетом последних тенденций (не могу не вставить AI в пост) кажется всем надо повышать уровень "сеньорности".

#agile
👍3
Почему победил компактный стиль форматирования кода?

Я про сталь Oracle\Sun и очень похожий на него стиль Google:

public class OrderService {

private static final int MAX_RETRIES = 3;

public String processOrder(Order order, boolean isPriority) {
if (order == null) {
...

vs

public class OrderService
{
private static final int MAX_RETRIES = 3;

public String processOrder(Order order, boolean isPriority)
{
if (order == null)
{
...


Базовый ответ - потому что он компактнее)

Но я бы накинул еще один аргумент. Рассуждение:

- стиль форматирования нужен человеку, и не нужен машине
- человеку важно понимать, где начало и конец логического блока кода или управляющей конструкции
- "железобетонный" способ это понять - четкие маркеры, хороший пример: begin-end в Delphi, If-End If в VB
- хороший, но не идеальный, т.к. слишком много символов - 8 вместо минимально возможных 2
- скобки на той же строке, что и управляющая конструкция, у Google\Sun - это по сути компактная эмуляция begin-end. Эмуляция потому что они не обязательны. Скобки как бы становятся частью управляющей конструкции - if, for, while, switch, try.
- а вот альтернативный вариант - это что-то странное, т.к. скобки по отступам находятся на уровне управляющей конструкции, но зачем тогда перенос на новую строку?

Тогда логичнее вот так:

public class OrderService
{
private static final int MAX_RETRIES = 3;

public String processOrder(Order order, boolean isPriority)
{
if (order == null)
{
...

Но это еще страннее смотрится.

Ну и напоследок каких целей должно достичь хорошее форматирование кода:
1) разделение логических блоков кода: пробелы, отступы, скобки
2) единообразие
3) улучшение читаемости
4) облегчение правок - возможность быстро понять, какой блок кода нужно перенести или удалить целиком
5) компактность. Причем как раз компактность с увеличением размера монитора становится менее
важна

P.S. Спонсор выпуска - Совершенный код, Стив Макконнелл)

#java #formating
👍1
Почему LLM может писать хороший код, но плохо генерит JSON?
Может и плохой код писать, к слову, зависит от модели и задачи)

Я уже писал о том, что генерация JSON - не конёк LLM https://t.me/javaKotlinDevOps/484
Сейчас попробую расписать почему.
Возьмем ситуацию до появления паттерна structured output. LLM отвечает текстом, если нам нужно вызвать тулу и передать туда определенный набор параметров - можно описать этот набор JSON схемой.
JSON схема как валидатор объекта параметров.
Получался примерно такой запрос к модели на формирование вызова тулы:
{
"model": "gpt-4o-mini",
"messages": [
{
"role": "user",
"content": "Создай заказ: 2 товара по цене 10"
}
],
"tools": [
{
"type": "function",
"function": {
"name": "create_order",
"description": "Создание заказа",
"parameters": {
"type": "object",
"properties": {
"items": {
"type": "array",
"items": {
"type": "object",
"properties": {
"price": { "type": "number" },
"quantity": { "type": "integer" }
},
"required": ["price", "quantity"]
}
}
},
"required": ["items"]
}
}
}
]
}


Здесь две проблемы.
1) модель конечно знает что такое JSON, но она генерирует токены, а не текст по формату.
2) у агента\чата есть задача уменьшить контекст, поэтому даже если ему дать полную схему с minimum, minimum, pattern, minItems - он ее может порезать (и режет) для оптимизации.
Поэтому модель могла вернуть и некорректный по структуре JSON, и проигнорировать ряд атрибутов схемы. Первое реже, второе - всегда.

Когда появился structured json - модель же гарантирует корректность возвращаемого JSON (JSON = возвращаемый объект) проблема казалось бы должна уйти в прошлое?
В модель летит такой запрос:

{
"model": "gpt-4.1",
"messages": [
{
"role": "user",
"content": "Создай заказ: 2 товара по цене 10"
}
],
"response_format": {
"type": "json_schema",
"json_schema": {
"name": "order_response",
"schema": {
"type": "object",
"properties": {
"items": {
"type": "array",
"items": {
"type": "object",
"properties": {
"price": { "type": "number" },
"quantity": { "type": "integer" }
},
"required": ["price", "quantity"],
"additionalProperties": false
}
},
"total": {
"type": "number",
"description": "Общая стоимость"
}
},
"required": ["items", "total"],
"additionalProperties": false
},
"strict": true
}
}
}


Тут даже "additionalProperties": false есть. И required само собой. И это все даже работает)
Но в целом гарантии точному соответствию схеме нет.

Как в большинстве моделей реализуется structured output?
Схема превращается в набор т.наз. грамматик.
Например
  "type": "array",
"items": { "type": "integer" }

превратится в что-то такое:
  [ INT (, INT)* ]

А далее по этим грамматикам строится state machine.
И при генерации очередного токена во внимание принимается не только его вероятность (как при обычном выводе), но и идет проверка на разрешенное состояние по этой state machine.
И если не разрешено - выбираем следующий.
Процесс называется constrained decoding, вот пример библиотеки, его реализующей: https://xgrammar.mlc.ai/

И я бы из ее описания обратил внимание на фразу:

aiming at bring flexible zero-overhead structure generation everywhere
zero-overhead. Т.е. при генерации state machine под схему из схемы снова выкидывается все лишнее, для скорости. А скорость ответа для LLM критична.
Т.е. constrained decoding работает не так строго, как обычный валидатор JSON Schema в Java или Python.
И minimum, maximum, pattern, format снова не работают.
Зато быстро. Но все равно чуть медленнее, чем без structured output, т.к. и дополнительная валидация появляется, и перебор вариантов.

В теории можно было бы взять open-source движок инференса и прикрутить туда нормальный JSON валидатор. Но подозреваю LLM станет отвечать очень медленно.

Как с этим бороться?
Все уже придумано, паттерн Retry.

MAX_RETRIES = 3

def generate_order(user_input: str):
prompt = f"Сформируй заказ в JSON: {user_input}"

last_error = None

for attempt in range(MAX_RETRIES):
raw = call_llm(prompt)

try:
order = Order.model_validate_json(raw)
return order

except ValidationError as e:
last_error = str(e)

# repair prompt
prompt = f"""
Ты вернул невалидный JSON.

Ошибка:
{last_error}

Твой предыдущий ответ:
{raw}

Исправь JSON строго по схеме.
Верни только JSON, без комментариев.
"""

raise Exception(f"Не удалось получить валидный ответ: {last_error}")


Да, и structured output - это все равно шаг вперед. Модель не генерирует как бы json, есть валидатор, проверяющий ну скажем 80-90% схемы.

#ai #llm
Кто же изобрел ноутбуки (Jupiter notebook)?

Ранее уже писал, что это питонисты, а точнее ML инженеры питонисты.

Выяснилось, что это не совсем так.
Дональд Кнут Грамотное программирование, 2001. Тот самый, что написал 3 тома Искусства программирования.
Собственно концепция грамотного программирования - это писать код для людей, а не для машин. Т.е. в формате рассказа, где главный - текст.

Т.е не
// Используем метод сортировки пузырьком, суть которого...
// Декларация метода:
void boubleSort(int[] array) {
...


а скорее
Используем метод сортировки пузырьком, суть которого...
Декларация метода:

@sort_method =
void (int[] array) {
...
@

А код - это переменная, которую можно переиспользовать.

При нужны соответствующие правила форматирования и движок для рендера всего это в удобочитаемый вид.

Кнут предлагал его всем разработчикам, взяли только дата сатанисты) Но с развитием AI может и пошире распространится)


Плюс понятен - читаемость. Минус - сложнее (дольше) писать. Мы же вообще уходим от документации к самодокументирующемуся кода. Но для сложного кода кажется смысл в нем есть.

#python #notebooks
Еще о форматировании.

Может показаться, что форматирование уже не критично, ведь весь код скоро будет писать AI, а мы все переквалифицируемся в архитекторы)
Насколько скоро - отдельный холиварный вопрос. Но не буду отвлекаться.

1) Факт номер раз: модель код (текст) превращает в токены. Токен с пробелом, табом, скобкой или переводом строки и без него - это разные токены.
Факт номер два: форматирование должно подчеркивать логическую структуру кода https://t.me/javaKotlinDevOps/559.
Следовательно, единообразное форматирование улучшает понимание кода моделью.

2) модель генерирует код по аналогии. Код проекта имеет больший приоритет, чем код, на котором ее обучали. Следовательно, какое форматирование на входе, такое и на выходе.

Далее повторяем 1) и 2) для каждой новой фичи и в итоге получаем, что хорошее форматирование кода может повысить шансы на выживание сервиса после его рефакторинга AI-шкой.

Да и при ревью человеком AI кода хорошее форматирование сильно поможет. А код-ревью нужно, сейчас так точно. Если смотреть в будущее - как опция, для сложных участков кода.

Итого - как раз таки для LLM, в отличие от компилятора, форматирование кода важно.

И видится плюс: если код пишет AI - форматированием и читаемость кода должны становится лучше при условии хорошей базы.
Модели легче сгенерировать пару лишних токенов, чем человеку поставить нужное число пробелов или скобок.

#ai #formating
Что C++ унаследовал от Java?

Внимательный читатель должен сказать - стой, автор, ты ничего не перепутал? Может Java от C++?

Не перепутал)
Да, Java был создан как безопасная и легко портируемая альтернатива C++.
Но кое-что он вернул прародителю.

Как ни странно - это стандарт документирования кода. C++ изначально шел без стандарта документирования. Используйте что хотите. А JavaDoc с Java был изначально. По факту стандартной тулой для документации С++ стал Doxygen. А Doxigen взял за основу JavaDoc.
Такие дела)


#java #c++
Чем ещё может нам помочь AI?

Есть такая проблема - устаревание любой документации. Причина простая - код в большом числе случаев невозможно забыть исправить. Программа сломается, тесты сломаются. В худшем случае - ПРОМ сломается, но не будем о грустном)
А вот ошибка в документации никак себя не проявит, пока кто-то не начнёт эту документацию читать. И даже тогда ошибка может не бросаться в глаза, наоборот, ввести в заблуждение.

Как "заставить" разработчика всегда поддерживать актуальность документации?
Да никак.
Поэтому и стал популярным самодокументирующий код.

А вот AI как раз таки заставить можно. Одним промтом. Ещё один агент для мультиагентной системы. И опять же - в итоге тот же AI будет лучше понимать получившийся код.

#ai
Иногда лучше молчать, чем говорить - продолжение)

Я про auto complete в dev агентах в IDE, который не должен всегда выдавать хоть что-то. А должен выдавать что-то адекватное)

Чтд ... https://habr.com/ru/companies/tbank/articles/1012196/

Суть. Ребята из Т-банка сделали двойную фильтрацию подсказок с помощью LLM+ML с целью выбросить нерелевантные. И в итоге смогли сдвинуться с мертвой точки и улучшить процент принятых подсказок. Этот показатель у них вышел на плато и не двигался несколько лет.

Интересно, что им бизнес разрешил фильтровать не более 5% подсказок чтобы не ухудшить пользовательский опыт. Но даже в этом случае процент принятия увеличился на 5+%. И доверие к фиче, что более важно!

В статье достаточно много деталей реализации - кому интересно, почитайте.

P.S. Интересно, делают ли так другие агенты?)

P.P.S. часть первая https://t.me/javaKotlinDevOps/539

#ai #ai_agents
🔥1
Снова нападки на Scala)

Я уже немного писал про Scala когда говорил о выборе новых языков программирования https://t.me/javaKotlinDevOps/251
Но осталось чувство недосказанности. Сложный - да, о ведь функционально крутой. Может стоит набрать команду "спецназа" от разработки, пусть на Scala пишут ядро системы?

Я бы не стал. Сложность это полбеды. Сложность не там, где ей стоит быть - вот проблема.

Большиство предметных областей дают нам бизнесовую сложность. Если бы это было не так - вместо разработки стоило бы купить готовое решение. Или взять готовую библиотеку.
Сложность должна быть в бизнес-логике. А не в конструкциях языка.

Второй момент - основная задача разработчика (чего-то сложнее hello world) - борьба со сложностью. SOLID, DRY. переиспользование кода, микросервисы, уровни приложения, говорящие имена, читаемость кода, DDD, итеративная разработка... - все это помогает уменьшить сложность. А тут язык провоцирует ее увеличение.

И ещё пример. Google. Компания может позволить себе нанять наверное любого программиста. Популяризовать любой язык.

Но что она делает?

Видит проблему на backend.
C++ быстр, но не безопасен и сложен.
Java безопасная, попроще, но не идеал.
И придумывает Go: быстрый и простой. Ещё заточенный на многопоточность, но простую - goroutines.

Мобильная разработка - есть завязка на JDK. Почему так - тема отдельная. Базовый язык для JDK - Java, про Java см. выше. Тут появляется более простой Kotlin - и Google делает его стандартом Android разработки.

Итого - в обоих случаях Google сделал разработку проще. production подход. Хотя где-то у них используется Scala.

#java #kotlin #scala
👍1
AI, везде AI)

Кажется назрел пост-глоссарий терминов. Сам иногда путаюсь)

ML модель - узкоспециализированная небольшая модель, как правило требующая обучения под конкретную задачу: классификация текстов, картинок или видео, определение тональности текста, преобразования Image\Audio в Text и обратно.
Существуют достаточно давно.

LLM - модель общего назначения, базово умеет продолжать текстовые фразы.
С конвертерами и тулами - принимать и генерировать любой контент, недостающие знания запрашивая из внешнего мира.
За универсальность приходится платить - LLM медленнее и требует более мощного железа.

промт - часть запроса к LLM. Почему часть? Как правило исходный запрос пользователя не попадает напрямую в LLM.
Исходный запрос - это пользовательский промт, а еще бывают системный и assistant.
Системный - это промт, "захардкоженный" внутри сервиса, взаимодействующего с LLM, и имеющий более высокий приоритет. Например, чата или агента.
assistant промт - прошлые ответы модели, добавленные в диалог.

tool - метод агента, имеющий четкое API и выполняющий некое действие\возвращающий некие данных.
Должен быть корректно описан, чтобы модель поняла зачем он нужен и вызывала его в случаях, когда сама не может выполнить какой-то запрос.
Как правило tool-ы встроенные, но иногда есть возможность создать пользовательские tool.

MCP - протокол клиент-сервер, позволяющий вынести tool-ы из агента.
Позволяет легко переиспользовать tool в разных агентах и разделить разработку логики агента и бизнесовых инструментов.

skill - навык агента. По сути более сложный tool, скорее сценарий, оркестрирующий вызовы нескольких тулов. Включает промт, документацию, схемы валидации, скрипты.

rule - правила работы, которые должны применяться при любом запросе к LLM.

RAG - статическая база знаний по узкой предметной области, содержащая данные, которых нет в LLM.

Контекст - набор данных, на основании которого LLM генерирует текст. В контекст имеет смысл включать:
а) промты
б) историю диалога и вызовов инструментов
в) полезные данные из RAG
г) список доступных tools, skills и MCP серверов
д) rules
Контекст привязан к клиентской сессии.

Контекстное окно - ограничение на размер контекста в сессии. Контекст хранится в памяти, память дорогая, приходится ограничивать. При достижении предела нужно принять два решения:
а) падать\ не падать (последнее - плавающее окно)
б) удалять \ сжимать старые данные.

Агент - сервис, с помощью AI способный самостоятельно понять и решить поставленную задачу.
В простейшем виде это бесконечный цикл вида:
а) ожидание входных данных
б) формирование данных, которые могут попасть в запрос к LLM
в) фильтрация этих данных
г) вызов LLM
д) получение дополнительных данных с помощью tool
е) валидация ответа
д) возврат ответа
В более сложных случаях - ациклический граф (DAG, не путать с RAG) или стейт-машина. Или даже циклический граф, для обработки ошибочного ответа.

Команды - по сути shortcut вида /execute для быстрого выполнения промта при общении с агентом в чате.

ACP - протокол для общения IDE и агента. Альтернатива MCP для вызова функций IDE из агента.
При использовании MCP агент просто вызывает функции IDE как тулы.
При использовании ACP - взаимодействие двухстороннее. Где это может понадобиться? Разве что для реализации единого окна чата в IDE для разных агентов.

Субагент - агент внутри агента. Ключевые характеристики - свой системный промт, свой набор тулов и свое контекстное окно. Простейший способ реализации мультиагентной системы.
Можно считать, что субагент - это развитый skill, имеющий свой контекст.

Обучение модели - в случае LLM по сути альтернатива RAG. Вместо того, чтобы хранить данные рядом с агентом - добавляем их в модель.
В целом LLM не требуют обучения, но иногда оно полезно.
Обучение дорогое - в случае LLM для него требуется множество GPU. Есть разные виды обучения, но это отдельная тема)

Инференс (inference) - обработка пользовательских запросов моделью. Инференс менее ресурсозатратный процесс, но для больших LLM также требует GPU.
ollama - самый популярный движок для локального инференса.
👍3🔥1
embeddings - преобразование текста в вектора (набор чисел). LLM работает с векторами, RAG хранит данные в виде векторов, при поиске по RAG запрос также превращается в векторную форму. Для формирования embeddings есть специальные ML модели, меньшие и более быстрые.

Семантический поиск = векторный поиск. Суть в том, что ищет не похожие по написанию слова, а близкие слова в пространстве векторов.


#ai
👍1
И альтернативный глоссарий)
Не знаю кто автор, но это гениально:

mcp-сервер – это ящик с лобзиками и напильниками

skills – это инструкция как из деревяшки сделать разделочную доску

rules – это «палец себе не отпили»

agents – это пятиклашки

prompt – это «сегодня на уроке все делаем подарок маме на 8 марта»

vibecoder – это трудовик, который дал задачу, а сам ушёл бухать с физруком
🔥1
Еще две мысли о комментариях в коде.

Я как наверное и большинство разработчик - ярый сторонник самодокументирующегося кода.
При этом JavaDoc (KDoc) иногда нужны.
Когда - уже говорил тут https://t.me/javaKotlinDevOps/183

Сейчас хочется добавить еще один позитивный кейс и один признать негативным.

Документация полезна, когда описывает то, чего нет в коде - это понятно.
Но также она полезна, если в нескольких строчках описывает то, что происходит в сложном методе или классе, включающем, скажем, 100-300 строк кода.
В первом случае время прочтения < 1 минуты, во втором прочитать и главное разобраться потребует в разы или на порядок больше времени.
А мы большей частью код читаем, а не пишем. И с учетом потока информации в наше время - код забывается быстро.

И антипаттерн. Ранее я писал, что документирование неочевидных вещей в коде может быть полезно, но это также повод задуматься о рефакторинге.
Сейчас прихожу к мнению, что единственный нормальный комментарий для особого случая - это
// TODO исправить ...

И такой TODO - это code smell в терминологии SonarQube. Не все TODO = code smell, это могут быть и планы на будущее, но в данном случае это точно техдолг.

#documentation
Что такое промт-инжиниринг?

Прочитал книжку "Промт-инжиниринг для LLM. Искусство построения приложений на основе больших языковых моделей"

Ясно, что в этой области все быстро устаревает, но в целом рекомендовать могу. Кажется книге не хватает структурированности, это скорее набор мыслей идей, инсайтов как сейчас модно говорить)

Но сейчас хотел бы обратить внимание на другое.
До сих пор я считал, что промт инжиниринг - это написание текстовых пользовательских промтов, no code.
После прочтения этой книги я бы сказал, что это только первый уровень.

Второй уровень - системный промт, который нужно писать с учетом того, что он должен задавать четкую роль для агента и направлять\ограничивать пользовательские промты. Т.е. встает вопрос совместимости.

А третий уровень - это собственно формирование диалога с LLM.
Напомню, запрос к LLM может выглядеть так:

Системный промт - вступление
Пользовательский промт
История диалога
rules
Список тулов, skills, MCP
Результаты вызова тулов, skills, MCP
Релевантные данные из RAG
Любые другие данные из контекста (например, открытые в IDE файлы для dev агента)
Системный промт - окончание.

Это все нужно собрать, приоритезировать, отфильтровать и встроить в шаблон запроса к LLM. А вначале этот шаблон придумать. А потом верифицировать ответ, если надо сделать цикл обратной связи для обработки некорректных ответов. А далее возможно декомпозировать на отдельные задачи для субагентов. Написать тесты для всего этого дела. Придумать метрики для отслеживания релевантности, точности и полноты ответов в ПРОМе. Наверняка еще задачи отладки и логирования станут актуальными, чтобы понять по каким веткам нашего графа идут запросы.

В общем, работы много.
И это уже не no code.
И быть такие промт-инженером уже не обидно для разработчика)

P.S. Авторы книги - бывшие разработчики GitHub Copilot.

#ai #dev
👍1