(java || kotlin) && devOps
342 subscribers
13 photos
2 videos
7 files
419 links
Полезное про Java и Kotlin - фреймворки, паттерны, тесты, тонкости JVM. Немного архитектуры. И DevOps, куда без него
Download Telegram
Есть мнение, что разработка - это молодая отрасль. Этим часто объясняется ее несовершенство. Нет чётких законов, парадигмы меняются, всегда приходится идти на компромиссы. Как пример - те же принципы чистого кода нельзя понимать буквально, за что книгу часто критикуют.

Но посмотрим, когда появились некоторые ключевые идеи.

Закон Конвея, упомянутый ранее https://t.me/javaKotlinDevOps/301 - структура кода повторяет структуру организации - 1967 год. Практически 60 лет назад.

Спор о вреде go to (а это значит, что он уже активно использовался) начал Дейкстра в письме Go To Statement Considered Harmful практически тогда же - 1968 году. 58 лет назад. Дискуссии, к слову, тогда велись через письма в журналы. В рамках дискуссии о go to понятие спагетти-код стало общепринятым примерно в 1977 году. 49 лет назад.

В 1970 году сотрудник IBM Эдгар Кодд опубликовал статью «A Relational Model of Data for Large Shared Data Banks», которая стала фундаментом для всех реляционных баз данных. В 1979 году появилась первая коммерческая реляционная СУБД Oracle. 56 и 47 лет соответственно.

Мифический человеко-месяц был написан в 1975 году. Знаменитый закон Брукса: добавление разработчиков ближе к концу проекта лишь увеличивает его сроки. А на русский книга была переведена в 1979 году, соответственно, в СССР. Это 51 и 47 лет назад. Я ещё не родился)

Совершенный код (да, опять), первое издание - 1993 год. 33 года назад.

Java 1.0 (еще без collections, не говоря уже и стримах, generic, enum, JDBC и многого другого) появилась в 1996 - 30 лет в этом году.

В 1999 был сформирован DRY («Don't Repeat Yourself» — «Не повторяй себя») Эндрю Хантом и Дэвидом Томасом в книге The Pragmatic Programmer. 27 лет назад. Принцип KISS сильно моложе, но его снимем с дистанции - он появится в ВВС США.

Принципы SOLID были сформулированы на год позже Робертом Мартином в статье Design Principles and Design Patterns. 26 лет назад.

Тогда же вышла книжка Кента Бека про XP (экстремальное программирование, включая TDD). И в 2001 Agile Manifest, который с ним связан.

В 2001 году появилась IDEA и рефакторинг стал доступен всем. Ну почти всем , Community появилась на 8 лет позже и началось ее победное шествие. 25 лет назад.

И лишь Docker, Kafka, DevOps, Kotlin можно считать относительно новыми - 15-20 лет назад. K8s так вообще чуть больше 10 лет.

Т.е. отрасль все же достигла зрелости.

И тут пришёл AI и хочет ее отменить)
Но это уже другая история)

#it_history
👍41
Чем похожи LLM и blockchain?

И там, и там для генерации следующего элемента используют данные предыдущих. И LLM также не может изменить уже сгенерированные токены, даже если в какой-то момент поняла, что они не верны. Ну и ещё потому что у нас как правило стриминг и пользователь уже их увидел)
Разница конечно есть - в blockchain вся цепочка живёт вечно, а LLM ограничена длиной контекстного окна и возможностью чата/агента хранить историю

И эта особенность является одной из причин галлюцинаций LLM. Т.к. придя к неверному выводу модель опирается на него в дальнейших ответах.


#ai
В продолжение предыдущего поста - почему модели галлюцинируют?

Попробую собрать все возможные причины:

1) как уже говорил - один раз сделав неверный вывод модель не может его исправить, он ложится в ее контекст, на основании которого выдаются все последующие ответы.
Да, не все токены из контекста выбираются при генерации ответа, только наиболее близкие (скалярное произведение векторов). И когда в диалоге появляются новые токены, более релевантные - например, "твой ответ неверный, обрати внимание на ..." - они "забивают" старые.
Иначе модель будет строить всю цепочку рассуждений опираясь на неверный вывод.

2) неверные данные от пользователя. Модель верит пользователю. Почему?
Модель обучалась на каких-то открытых документах. Форумы, новости, GitHub,... Сколько там неверной информации?
Она конечно есть, но в большинство документов содержит более менее актуальную информацию.
Модель, соответственно, ожидает, что пользователь тоже дает ей верную информацию.
Особенно в тех областях, по которым нет явно опровергающих это фактов.
Земля плоская - опровержений много. Я уже установил последнюю версию драйверов - как это проверить?) Есть OpenClaws, но я бы не рискнул ставить его на свой компьютер)

3) модель в принципе не оперирует понятиями ложь и правда. Она ищет наиболее вероятный следующий токен, из которых в итоге появляется фраза.
Вероятность зависит от данных обучения и предыдущего диалога. Т.е. ей нужен внешний арбитр, который знает - что правда, а что нет.
Это может быть другая модель - но в целом у нее те же проблемы. Или человек - т.е. информацию надо перепроверять.

4) И самое интересное.
Если точных данных нет, а это как раз самые интересные и сложные кейсы - модель их может угадать.
Может угадать, может не угадать.
Но на обучении - Supervised fine-tuning (SFT) и Reinforcement Learning (RL) - модель оценивают по проценту верных ответов.
Все бенчмарки для людей о том же - процент правильных ответов.
Т.е. для модели лучшая стратегия - что-то придумать, чем честно сказать "Я не знаю".
Об этом даже авторы OpenAI пишут и предлагают менять систему оценок: https://openai.com/ru-RU/index/why-language-models-hallucinate

В итоге имеем то, что имеем)

#ai
Все 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
И альтернативный глоссарий)