сук, я не хотел вообще этот пост писать. Наоборот, у меня контент по плану было кое-что позитивное. Но у меня щас просто разбомбило сракатан в пух и прах!😡
В чем же дело, спросите вы. Да, блять в том, как в python описываются типы в mypy. Постоянно какая-то хрень вылазиет из жопы, когда работаешь с ним.
Вот у меня есть generic класс
я хочу в него добавить метод, который будет возвращать новый инстанс этого класса по типу
Давайте поможем Даше найти, как расставить типы в объявлении метода!
ну что вы напишите в этом случае? ну наверное что-то типа
Обратите внимание, как же сука говяно описываются колаблы. Ну это ладно. Этот код впринципи некорректный!
А знаете какой код корректный? Вот такой
Задача: найти два отличия.
Ответ:левая кавычка и правая кавычка.
Причем, сука задаешь этот вопрос дипсику и perplexity, так он тебе старый способ объявления дженериков в ответ ставит, который еще более уебский с пежней типа
Я не знаю, че там с самой системой типов в питоне. Наверное там умные мужики все делали, и там все корректно и правильно. Но с точки зрения синтаксиса эти дядьки похоже класть хотели на принцип наименьшего удивления😤
Извинити, просто реал бомбануло
В чем же дело, спросите вы. Да, блять в том, как в python описываются типы в mypy. Постоянно какая-то хрень вылазиет из жопы, когда работаешь с ним.
Вот у меня есть generic класс
class Result[T]:
pass
я хочу в него добавить метод, который будет возвращать новый инстанс этого класса по типу
def transform(self, transformer):
data = transformer(self.__data)
return Result(data)
Давайте поможем Даше найти, как расставить типы в объявлении метода!
ну что вы напишите в этом случае? ну наверное что-то типа
def transform[V](self, transformer: Callable[[str], V]) -> Result[V]:
pass
Обратите внимание, как же сука говяно описываются колаблы. Ну это ладно. Этот код впринципи некорректный!
А знаете какой код корректный? Вот такой
def transform[V](self, transformer: Callable[[str], V]) -> "Result[V]":
pass
Задача: найти два отличия.
Ответ:
Причем, сука задаешь этот вопрос дипсику и perplexity, так он тебе старый способ объявления дженериков в ответ ставит, который еще более уебский с пежней типа
T = TypeVar('T')
class Result(Generic[T]):
pass
Я не знаю, че там с самой системой типов в питоне. Наверное там умные мужики все делали, и там все корректно и правильно. Но с точки зрения синтаксиса эти дядьки похоже класть хотели на принцип наименьшего удивления😤
Извинити, просто реал бомбануло
👍4😁4
Тут на меня навалилось всякое, и я что-то не нашел времени отметить знаменательное событие. Ведь на прошлой неделе самому умирающему языку программирования исполнилось 30 лет🥳 с днем рождения php!
Какой бы топ ЯП вы не взяли, php стабильно где-то в жепе! Наверное трудно найти какой-то другой язык, который хейтили бы больше, но который также активно использовался бы в продакшне. А между тем множество из хейтеров вкатывались в АЙТИ именно через php. Но ощущение такое, что php развивался, а хейтеры нет. Ведь за последнюю декаду в языке появилось столько всего, что удобство разработки и качество кода перешло на новый уровень. Перечислю список новых фич за последние 10 лет, которые, на мой взгляд, оказали наибольшее влияние на процесс разработки:
* строгая типизация в рантайме
* enums
* JIT компиляция
* атрибуты
* стрелочные функции
* first-class callable
* readonly свойства и классы, проперти хуки
* распаковка массивов, деструктурирующее присваивание (прям как в этом вашем js)
* файберы
И это я перечислил только то, что мне пришло на ум, и то, чем я пользуюсь. Там еще до хера всего. И вы можете сказать "но постой, это есть во всех других современных языках". Так в этом и соль, что php - это нормис. Это хороший современный язык! И он отлично справляется с работой, для которой он и создавался, а именно веб приложения. Его "умирающая" модель исполнения идеально подходит для веба, где большая часть взаимодействия построена по принципу request-response.
Кстати про умирающую модель. И здесь у пхп большие подвижки. На момент написания этого поста у пхп есть уже 2 продакшн реди решения (roadrunner и frankenphp), где используется воркерная модель, когда php процессы живут "вечно". А еще есть асинхронные библиотеки openswoole и reactphp. Так что даже в этом плане php такой же нормис.
Но вот что делает php очень крутым, так это тулинг и фреймворки. Что касается тулинга то тут у нас и статические анализаторы, и линтеры, и тулзы для рефакторинга. Казалось бы, в других языках это тоже все есть, но в пхп это все унифицированно (нету тысячи вариантов сделать одну и ту же задачу), работает быстро и очень хорошо поддерживается. Поверьте, разница с тем же питоном и жс есть, и она не в пользу последних.
Короче говоря за 10 лет php проделал огромный путь. У меня ощущение, что php 5 и php 8 это два разных языка - уж на столько их возможности отличаются. Будем надеяться, что следующая декада пройдет не менее активно. Нам тут уже обещают дженерики и async-await, но в целом, я был бы рад если бы в язык встроили дебаггер 🤡
Короче говоря, не умер и ладно!
Какой бы топ ЯП вы не взяли, php стабильно где-то в жепе! Наверное трудно найти какой-то другой язык, который хейтили бы больше, но который также активно использовался бы в продакшне. А между тем множество из хейтеров вкатывались в АЙТИ именно через php. Но ощущение такое, что php развивался, а хейтеры нет. Ведь за последнюю декаду в языке появилось столько всего, что удобство разработки и качество кода перешло на новый уровень. Перечислю список новых фич за последние 10 лет, которые, на мой взгляд, оказали наибольшее влияние на процесс разработки:
* строгая типизация в рантайме
* enums
* JIT компиляция
* атрибуты
* стрелочные функции
* first-class callable
* readonly свойства и классы, проперти хуки
* распаковка массивов, деструктурирующее присваивание (прям как в этом вашем js)
* файберы
И это я перечислил только то, что мне пришло на ум, и то, чем я пользуюсь. Там еще до хера всего. И вы можете сказать "но постой, это есть во всех других современных языках". Так в этом и соль, что php - это нормис. Это хороший современный язык! И он отлично справляется с работой, для которой он и создавался, а именно веб приложения. Его "умирающая" модель исполнения идеально подходит для веба, где большая часть взаимодействия построена по принципу request-response.
Кстати про умирающую модель. И здесь у пхп большие подвижки. На момент написания этого поста у пхп есть уже 2 продакшн реди решения (roadrunner и frankenphp), где используется воркерная модель, когда php процессы живут "вечно". А еще есть асинхронные библиотеки openswoole и reactphp. Так что даже в этом плане php такой же нормис.
Но вот что делает php очень крутым, так это тулинг и фреймворки. Что касается тулинга то тут у нас и статические анализаторы, и линтеры, и тулзы для рефакторинга. Казалось бы, в других языках это тоже все есть, но в пхп это все унифицированно (нету тысячи вариантов сделать одну и ту же задачу), работает быстро и очень хорошо поддерживается. Поверьте, разница с тем же питоном и жс есть, и она не в пользу последних.
Короче говоря за 10 лет php проделал огромный путь. У меня ощущение, что php 5 и php 8 это два разных языка - уж на столько их возможности отличаются. Будем надеяться, что следующая декада пройдет не менее активно. Нам тут уже обещают дженерики и async-await, но в целом, я был бы рад если бы в язык встроили дебаггер 🤡
Короче говоря, не умер и ладно!
🔥5❤3
немношк бомбанул очком 😤 https://www.youtube.com/shorts/wA2ToBLrl6o
вам задача: найти базированного челика в шортсе!
вам задача: найти базированного челика в шортсе!
YouTube
Стрёмно ли Писать Код на PHP в 2025 году? #ОлегКозырев #айти #программирование
С вами ОлегКозырев, здесь про рост в IT без выгорания и пафоса.👉 Лучшее закрытое сообщество для гоферов по самой доступной цене: https://t.me/olezhek28_cour...
😁4
Как не зайдешь в эти ваши интернеты, везде одни истории успеха😔Один навайбкодил стартап, другой выкатил обнову для своей библиотеки, третий на подкаст пришел и рассказал, какой он крутой. И никто сука не рассказывает о факапах и тупняках. Поэтому об этом буду рассказывать я😊
На работе делаем супер продвинутого ИИ помощника (на питоне, разумеется). Как истинный хороший программист, я решил внедрить статусное программирование (спасибо Кириллу Мокевнину с его докладами). Это означает, что сессия пользователя может находиться только в одном из доступных состояний. Например, есть состояние awaiting, когда помощник готов принимать запрос, или processing, когда происходит обработка запроса пользователя. Переходы между состояниями запрограммированы через finite state machine (fsm).
Однако в какой-то момент потребовалось научиться "ждать" определенного перехода между состояниями. Например, приходит запрос на создание новой сессии, но предыдущая сессия находится в состоянии summarizing, когда llm подгатавливает саммари по ней. И надо дождаться, когда состояние станет finished, прежде, чем продолжать операцию.
И вот трое нас, я, чатгпт и дипсик, родили совершенно потрясающее решение, основанное на Lockах и Eventах. Я даже тесты написал!🥳
И вдруг сижу и чувствую, что по штанам что-то течет. Ведь ничо тот факт, что у меня кубернетис и может быть хоть 10 реплик приложения с 10ю локами, которым по барабану друг на друга. Пришлось полностью выбросить это решение. Но хотя бы тесты остались.
И тут бы мне остановиться придумывать херню, ведь можно сделать классический поллинг - просто периодически долбить базу с запросом "ну че там, статус поменялся?". Но нет, случился очередной ступор мозговины. Ведь мне нужно КРАСИВОЕ решение.
И тут начинается полезная инфа для вас. Если вы работаете с postgres, то подобную задачу можно решить используя механизм LISTEN / NOTIFY. То есть в одной сессии подпиcываетесь на определенный ивент с помощью команды LISTEN event, а в другой сессии можно уведомить подписоту с помощью NOTIFY event. Еще payload можно передать. Ну круто же!
Но есть одна сложность. Допустим вы прочитали значение из бд, оно вам не понравилось, и вы хотите подождать другого. Если вы просто оформляете подписку после чтения, значение уже может измениться в этот момент. Это классическая гонка данных. Поэтому и listen, и notify важно делать в транзакциях с локами. В частности в транзакции, где у вас notify, нужно сделать select ... for update, а там, где listen должно быть select ... for share.
В итоге, такое решение работает. Но оно явно сложнее, чем если бы был простой поллинг. Да, у поллинга будет задержка. Но зато оно просто выглядит, меньше подвержено ошибкам и быстрее в реализации.
Вывод: не выпендривайтесь, делайте просто! 👆
Из бонусов, я кайфанул от того, что написал тесты, а затем полностью переписал реализацию, а тесты почти не трогал! Почти оргазм! 🥹
На работе делаем супер продвинутого ИИ помощника (на питоне, разумеется). Как истинный хороший программист, я решил внедрить статусное программирование (спасибо Кириллу Мокевнину с его докладами). Это означает, что сессия пользователя может находиться только в одном из доступных состояний. Например, есть состояние awaiting, когда помощник готов принимать запрос, или processing, когда происходит обработка запроса пользователя. Переходы между состояниями запрограммированы через finite state machine (fsm).
Однако в какой-то момент потребовалось научиться "ждать" определенного перехода между состояниями. Например, приходит запрос на создание новой сессии, но предыдущая сессия находится в состоянии summarizing, когда llm подгатавливает саммари по ней. И надо дождаться, когда состояние станет finished, прежде, чем продолжать операцию.
И вот трое нас, я, чатгпт и дипсик, родили совершенно потрясающее решение, основанное на Lockах и Eventах. Я даже тесты написал!🥳
И вдруг сижу и чувствую, что по штанам что-то течет. Ведь ничо тот факт, что у меня кубернетис и может быть хоть 10 реплик приложения с 10ю локами, которым по барабану друг на друга. Пришлось полностью выбросить это решение. Но хотя бы тесты остались.
И тут бы мне остановиться придумывать херню, ведь можно сделать классический поллинг - просто периодически долбить базу с запросом "ну че там, статус поменялся?". Но нет, случился очередной ступор мозговины. Ведь мне нужно КРАСИВОЕ решение.
И тут начинается полезная инфа для вас. Если вы работаете с postgres, то подобную задачу можно решить используя механизм LISTEN / NOTIFY. То есть в одной сессии подпиcываетесь на определенный ивент с помощью команды LISTEN event, а в другой сессии можно уведомить подписоту с помощью NOTIFY event. Еще payload можно передать. Ну круто же!
Но есть одна сложность. Допустим вы прочитали значение из бд, оно вам не понравилось, и вы хотите подождать другого. Если вы просто оформляете подписку после чтения, значение уже может измениться в этот момент. Это классическая гонка данных. Поэтому и listen, и notify важно делать в транзакциях с локами. В частности в транзакции, где у вас notify, нужно сделать select ... for update, а там, где listen должно быть select ... for share.
В итоге, такое решение работает. Но оно явно сложнее, чем если бы был простой поллинг. Да, у поллинга будет задержка. Но зато оно просто выглядит, меньше подвержено ошибкам и быстрее в реализации.
Вывод: не выпендривайтесь, делайте просто! 👆
Из бонусов, я кайфанул от того, что написал тесты, а затем полностью переписал реализацию, а тесты почти не трогал! Почти оргазм! 🥹
💅7
Штош, шутки шутками, но настало время более серьезно поговорить про вайбкодинг, а точнее, про влияние вайбкодинга на разработчика. Так получилось, что сам я не выйбкодю. И далее по тексту вы увидите мои оправдашки, почему так.
В качестве поддержки своего мнения, я бы хотел обратиться к офигенной статье Why I stopped using AI code editors. На момент написания поста оригинал статьи почему-то недоступен, но есть копия в вебархиве. Я советую прочитать, очень отрезвляюще. Особенно прикольна аналогия с теслой, когда долгое использование автопилота привело к деградации навыка вождения у автора.
Почему же я не вайбкодю? Я отвечаю себе на этот вопрос так: прекращение работы над простыми вещами делает сложные вещи еще сложнее. В статье об этом говорится в части про Fingerspitzengefühl.
Приведу свою налогию. Какое-то время назад я занимался единоборствами. Одним из ключевых упражнений в тренировках было бить по мешку один и тот же удар определенное количество раз. Ну например, надо 100 раз ударить прямым ударом руки по мешку. Затем поменять руку и стойку и также 100 раз ударить. Потом тоже самое с другими видами ударов руками и ногами. Скучно кабзда! Но мне кажется, мало кто сомневается в полезности таких упражнений. Тренер всегда объяснял нам, зачем это делать. Нужно довести наши удары до автоматизма в "тепличных" условиях. В спаринге не будет таких условий. В спаринге часто оказываешься в неудобной позиции, тебя бьют, ты устаешь. Но ключ к победе в том, чтоб "прошел" один единственный удар, который ты наносишь, находясь в супер неудобных условиях. Вот для этого ты и долбишь часами один и тот же удар. И даже самые опытные бойцы продолжают бить эти самые удары и связки ударов. Потому что это важно.
Теперь представьте, что у вас появляется ИИ в тренировке. И он добавляет "множитель" к вашим ударам. Например, множитель 5: теперь вместо 100 ударов, надо сделать всего 20. Формально ты выполнишь тренировку, нанеся всего 20 ударов. Сделает ли это твою жизнь проще? Конечно сделает! Но в спарринге, человек, который честно набил 100 ударов будет иметь преимущество. У него и удары техничнее, и руки "набиты" лучше.
Любая аналогия ложна, в том числе и эта. Все таки тренировка не работа разработчиком, а спаринг не построение архитектуры ПО. Но я все равно считаю, что каким бы ремеслом ты не занимался (а программирование - это ремесло), важно продолжать делать "простые" вещи, если хочешь оставаться в этом ремесле и не терять квалификацию. При этом эти самые простые вещи можно делать с помощью современного инструментария: крутой IDE, фреймворка или даже ИИ. Но главное, делать.
Проблема вайбкодинга в том, что он отобирает возможность делать простые вещи. Это особенно черевато для джунов и тех, кто только хочет вкатиться в IT. Обманчивая иллюзия скорости и эффективности, которая по итогу приводит к отсуствию навыка у джунов и потере навыка у более опытных разработчиков. Кроме того, согласно недавнему исследованию Microsoft, само по себе использование ИИ оказывает негативное влияние на способность человека мыслить критически и к чрезмерной зависимости от этого самого ИИ.
Я непротив использования ИИ в целом. Я считаю, что ИИ пришел всерьез и надого, и сам активно использую его в работе. Оно очень помогает погрузиться в какую-то тему максимально быстро, получить ответ на вопрос, накинуть множество вариантов решения для выбора, объяснить что-то. Глупо спорить, что с ИИ продуктивность разработчиков сильно выросла по сравнению с традицонным гуглом + стэковефлоу.
Но на мой взгляд, вайбкодинг - это когда ИИ переступает уже черту крутого инструмента, забирая локус контроля у разработчика. Сам я боюсь потери навыка. Поэтому пока без него.
В завершении отмечу, что я тут не говорю о влиянии на бизнес, о корректности работы самого ИИ, о влиянии ИИ на код и индустрию. Я думаю, никто не сможет сказать, как это будет через год, два или пять лет. Но в одном я уверен: пока ИИ оперирует обычными ЯП, пока валидация решения остается на уровне обычного кода, никуда программистские навыки не денутся, кто бы там что не говорил.
В качестве поддержки своего мнения, я бы хотел обратиться к офигенной статье Why I stopped using AI code editors. На момент написания поста оригинал статьи почему-то недоступен, но есть копия в вебархиве. Я советую прочитать, очень отрезвляюще. Особенно прикольна аналогия с теслой, когда долгое использование автопилота привело к деградации навыка вождения у автора.
Почему же я не вайбкодю? Я отвечаю себе на этот вопрос так: прекращение работы над простыми вещами делает сложные вещи еще сложнее. В статье об этом говорится в части про Fingerspitzengefühl.
Приведу свою налогию. Какое-то время назад я занимался единоборствами. Одним из ключевых упражнений в тренировках было бить по мешку один и тот же удар определенное количество раз. Ну например, надо 100 раз ударить прямым ударом руки по мешку. Затем поменять руку и стойку и также 100 раз ударить. Потом тоже самое с другими видами ударов руками и ногами. Скучно кабзда! Но мне кажется, мало кто сомневается в полезности таких упражнений. Тренер всегда объяснял нам, зачем это делать. Нужно довести наши удары до автоматизма в "тепличных" условиях. В спаринге не будет таких условий. В спаринге часто оказываешься в неудобной позиции, тебя бьют, ты устаешь. Но ключ к победе в том, чтоб "прошел" один единственный удар, который ты наносишь, находясь в супер неудобных условиях. Вот для этого ты и долбишь часами один и тот же удар. И даже самые опытные бойцы продолжают бить эти самые удары и связки ударов. Потому что это важно.
Теперь представьте, что у вас появляется ИИ в тренировке. И он добавляет "множитель" к вашим ударам. Например, множитель 5: теперь вместо 100 ударов, надо сделать всего 20. Формально ты выполнишь тренировку, нанеся всего 20 ударов. Сделает ли это твою жизнь проще? Конечно сделает! Но в спарринге, человек, который честно набил 100 ударов будет иметь преимущество. У него и удары техничнее, и руки "набиты" лучше.
Любая аналогия ложна, в том числе и эта. Все таки тренировка не работа разработчиком, а спаринг не построение архитектуры ПО. Но я все равно считаю, что каким бы ремеслом ты не занимался (а программирование - это ремесло), важно продолжать делать "простые" вещи, если хочешь оставаться в этом ремесле и не терять квалификацию. При этом эти самые простые вещи можно делать с помощью современного инструментария: крутой IDE, фреймворка или даже ИИ. Но главное, делать.
Проблема вайбкодинга в том, что он отобирает возможность делать простые вещи. Это особенно черевато для джунов и тех, кто только хочет вкатиться в IT. Обманчивая иллюзия скорости и эффективности, которая по итогу приводит к отсуствию навыка у джунов и потере навыка у более опытных разработчиков. Кроме того, согласно недавнему исследованию Microsoft, само по себе использование ИИ оказывает негативное влияние на способность человека мыслить критически и к чрезмерной зависимости от этого самого ИИ.
Я непротив использования ИИ в целом. Я считаю, что ИИ пришел всерьез и надого, и сам активно использую его в работе. Оно очень помогает погрузиться в какую-то тему максимально быстро, получить ответ на вопрос, накинуть множество вариантов решения для выбора, объяснить что-то. Глупо спорить, что с ИИ продуктивность разработчиков сильно выросла по сравнению с традицонным гуглом + стэковефлоу.
Но на мой взгляд, вайбкодинг - это когда ИИ переступает уже черту крутого инструмента, забирая локус контроля у разработчика. Сам я боюсь потери навыка. Поэтому пока без него.
В завершении отмечу, что я тут не говорю о влиянии на бизнес, о корректности работы самого ИИ, о влиянии ИИ на код и индустрию. Я думаю, никто не сможет сказать, как это будет через год, два или пять лет. Но в одном я уверен: пока ИИ оперирует обычными ЯП, пока валидация решения остается на уровне обычного кода, никуда программистские навыки не денутся, кто бы там что не говорил.
web.archive.org
Why I stopped using AI code editors ·
Luciano Nooijen
Luciano Nooijen
In the past I used AI code editors for all of my programming, but I stopped using it and recommend others to consider this as well
❤5🔥4👍1
Корочи, на работе продолжаем пилить жесткую тему связанную с ИИ🤖Для этого у нас в команде есть специально обученный (нет) человек, чтоб заниматься непотребствами с llm и всячески их тюнить. Ибо тема эта новая, и, кажется, человечество создало нечто такое, в чем само поитогу разобраться не может. Требуются бесконечные эксперименты с промптами и всякими агентами.
Разумеется, то тут, то там, начали вылезать фреймворки для разработки "expressive, customizable agent workflows". Суть их в том, чтоб ускорить накидывание говна в llm, в надежде получить че-то хорошее на выходе. По сути - ускорить обратную связь. Одним из таких фреймворков является набирающий популярность сейчас langgraph. Решили попробовать часть функционала переписать с помощью него. Потому что на бумаге все звучит очень круто🤩
Основная идея, на которой построен langgraph - представление логики обработки запроса пользователя в виде графа. В этом графе каждый узел - это какая-то функция, которая может сходить в llm или вызвать локальный инструмент (tool) или запросить помощь от пользователя. Можно определить условия перехода в тот или иной узел. А еще langgraph позволяет работать с состоянием. Каждая нода ведет себя каксучька редьюсер в react redux - принимает на вход старое состояние и возвращает новое. Ну не красота ли!
Но по факту опыт получился смешанный. Потому что нахуевертили!
Если вы начнете погружаться в langgraph, то обнаружите, что там довольно много документации. Ситуация осложняется еще и тем, что разработчики предоставили миллиард способов сделать одну и ту же херню. Да там даже 2 разных независимых API существует зачем-то! Короче, готовьтесь пару дней тратить только на чтение буков🤓
Ну это ладно. Самая большая проблема для нас оказалась в персистентности. Ведь логично предположить, что агент, который забывает предыдущий ответ пользователя - это как дед с деменцией - вроде парень неплохой, только ссытся и глухой. Поэтому разработчики добавили "память" в langgraph. Но сделали это через жепу.
Вам предлагается модель чекпоинтеров, когда переход через каждый узел сохраняется в хранилище. Поэтому всегда можно восстановить состояние и продолжить.
Проблема в том, что я могу НЕ хотеть сохранять промежуточный мусор пока запрос не выполнился полностью. Потому что если на середине пути у меня вывалилась ошибка, то я хочу просто начать сначала без предыдущих потуг. И langgraph не особо дает это сделать. Более того, разработчики, видимо поняв, что идея с чекпоинтерами не слишком гибкая, добавили еще один вид памяти long term. И это какая то невнятная дичь. Вдовесок ко всему, модель хранения langgraph по сути скрыта. Например, захотите вы проанализировать транскрипт пользователя - ну удачи вам.
В итоге, мы решили использовать langgraph только как способ организовать код в виде графа. Это реально облегчило прототипирование, поскольку позволило отвязать весь наш остальной код от логики работы агента. Мы сами храним и состояние, и транскрипт, и общую память о пользователе, а в langgraph бегаем просто как в black box. На вход ему подаем наши загруженные данные + запрос пользователя. После обработки запроса графом, мы используем результат для обновления состояний, метрик, транскрипта и т.п.
Я считаю, что в этом и состоялась основная польза от использования langgraph. Именно отделение кода логики агента от остального кода - основная ценность. Поскольку этими двумя частями заведуют разные люди в команде.
А сам по себе langgraph на 5.5 из 10. Много что есть сразу, но не гибко, сложно, потно!😰Будем надеяться, что это просто первые попытки человечества создать фреймворк под написание агентов, и дальше будет лучше.
Разумеется, то тут, то там, начали вылезать фреймворки для разработки "expressive, customizable agent workflows". Суть их в том, чтоб ускорить накидывание говна в llm, в надежде получить че-то хорошее на выходе. По сути - ускорить обратную связь. Одним из таких фреймворков является набирающий популярность сейчас langgraph. Решили попробовать часть функционала переписать с помощью него. Потому что на бумаге все звучит очень круто🤩
Основная идея, на которой построен langgraph - представление логики обработки запроса пользователя в виде графа. В этом графе каждый узел - это какая-то функция, которая может сходить в llm или вызвать локальный инструмент (tool) или запросить помощь от пользователя. Можно определить условия перехода в тот или иной узел. А еще langgraph позволяет работать с состоянием. Каждая нода ведет себя как
Но по факту опыт получился смешанный. Потому что нахуевертили!
Если вы начнете погружаться в langgraph, то обнаружите, что там довольно много документации. Ситуация осложняется еще и тем, что разработчики предоставили миллиард способов сделать одну и ту же херню. Да там даже 2 разных независимых API существует зачем-то! Короче, готовьтесь пару дней тратить только на чтение буков🤓
Ну это ладно. Самая большая проблема для нас оказалась в персистентности. Ведь логично предположить, что агент, который забывает предыдущий ответ пользователя - это как дед с деменцией - вроде парень неплохой, только ссытся и глухой. Поэтому разработчики добавили "память" в langgraph. Но сделали это через жепу.
Вам предлагается модель чекпоинтеров, когда переход через каждый узел сохраняется в хранилище. Поэтому всегда можно восстановить состояние и продолжить.
Проблема в том, что я могу НЕ хотеть сохранять промежуточный мусор пока запрос не выполнился полностью. Потому что если на середине пути у меня вывалилась ошибка, то я хочу просто начать сначала без предыдущих потуг. И langgraph не особо дает это сделать. Более того, разработчики, видимо поняв, что идея с чекпоинтерами не слишком гибкая, добавили еще один вид памяти long term. И это какая то невнятная дичь. Вдовесок ко всему, модель хранения langgraph по сути скрыта. Например, захотите вы проанализировать транскрипт пользователя - ну удачи вам.
В итоге, мы решили использовать langgraph только как способ организовать код в виде графа. Это реально облегчило прототипирование, поскольку позволило отвязать весь наш остальной код от логики работы агента. Мы сами храним и состояние, и транскрипт, и общую память о пользователе, а в langgraph бегаем просто как в black box. На вход ему подаем наши загруженные данные + запрос пользователя. После обработки запроса графом, мы используем результат для обновления состояний, метрик, транскрипта и т.п.
Я считаю, что в этом и состоялась основная польза от использования langgraph. Именно отделение кода логики агента от остального кода - основная ценность. Поскольку этими двумя частями заведуют разные люди в команде.
А сам по себе langgraph на 5.5 из 10. Много что есть сразу, но не гибко, сложно, потно!😰Будем надеяться, что это просто первые попытки человечества создать фреймворк под написание агентов, и дальше будет лучше.
langchain-ai.github.io
Overview
Build reliable, stateful AI systems, without giving up control
❤5👍2
В прошлый раз рассказывал про langgraph. а сегодня расскажу о том, почему у меня сгорела дыра из-за него🌚
Дело в том, что langgraph пытается казаться на столько удобным решением, что они даже запилили собственную IDE, и назвали ее langgraph studio (далее lgs). Очень блять оригинально.
Как же выглядит IDE для разработки агентов? Нужно ли там писать код? Можно ли там вайбкодить с помощью агента, которого сам же пишешь? 🤔 Все эти вопросы сразу остро встают на повестке дня.
Но все гораздо прозаичнее (см. картинку). Эта присрачка позволяет запускать каждый узел в графе (функцию) отдельно, мейнтейнить стейт, менять входные параметры. Короче говоря, позволяет всячески эксперементировать с узлами без необходимости работать с полным циклом запрос-ответ. Удобно? Не то слово! Идея просто бомбическая.
Разумеется, наш специально обученный человек сразу захотел lgs использовать. И тут его трудно осуждать.
Сначала все было хорошо. Сплошные востороженные возгласы. Но внезапно мне прилетает тревожное сообщение "В lgs не работает подключение к бд"😱
Начал разбираться в проблеме. Как вы можете помнить, коннекшн к бд я храню в contextvars. Подробнее об этом я писал тут. Когда lgs запускает отдельно ноду, то contextvars пустые. Поэтому и подключения нет. Надо как-то подключиться к бд и положить коннекшн в переменную перед запуском каждой ноды. Изи!
И вот тут меня настигло абсолютное разочарование. Потому что никакого способа это сделать разработчики не предоставили. Ни хуков, ни api для плагинов, вообще ни-ху-я. Просто нет возможности выполнить код в этой IDE помимо кода узла😔
ТО ЕСТЬ, разрабы lgs дали способ работать с каждым узлом изолированно от всего остального кода, но не подумали, что может потребоваться ЧТО-ТО от этого остального кода. Гении ебать!
В общем ключе задача формулируется так: "У библиотеки или тулзы отсутствует нужный api для взаимодействия, а вам позарез надо вмешаться в ее модель выполнения". Звучит конечно так себе. Поэтому добавим "...для разработки или тестирования"😏
Это был пост-затравка. В следующем посте я расскажу, как решал эту проблему в python. Но подобный метод может применяться практически в любых интерпретируемых ЯП. В комментах пишите, как бы вы решали подобную проблему👇
Дело в том, что langgraph пытается казаться на столько удобным решением, что они даже запилили собственную IDE, и назвали ее langgraph studio (далее lgs). Очень блять оригинально.
Как же выглядит IDE для разработки агентов? Нужно ли там писать код? Можно ли там вайбкодить с помощью агента, которого сам же пишешь? 🤔 Все эти вопросы сразу остро встают на повестке дня.
Но все гораздо прозаичнее (см. картинку). Эта присрачка позволяет запускать каждый узел в графе (функцию) отдельно, мейнтейнить стейт, менять входные параметры. Короче говоря, позволяет всячески эксперементировать с узлами без необходимости работать с полным циклом запрос-ответ. Удобно? Не то слово! Идея просто бомбическая.
Разумеется, наш специально обученный человек сразу захотел lgs использовать. И тут его трудно осуждать.
Сначала все было хорошо. Сплошные востороженные возгласы. Но внезапно мне прилетает тревожное сообщение "В lgs не работает подключение к бд"😱
Начал разбираться в проблеме. Как вы можете помнить, коннекшн к бд я храню в contextvars. Подробнее об этом я писал тут. Когда lgs запускает отдельно ноду, то contextvars пустые. Поэтому и подключения нет. Надо как-то подключиться к бд и положить коннекшн в переменную перед запуском каждой ноды. Изи!
И вот тут меня настигло абсолютное разочарование. Потому что никакого способа это сделать разработчики не предоставили. Ни хуков, ни api для плагинов, вообще ни-ху-я. Просто нет возможности выполнить код в этой IDE помимо кода узла😔
ТО ЕСТЬ, разрабы lgs дали способ работать с каждым узлом изолированно от всего остального кода, но не подумали, что может потребоваться ЧТО-ТО от этого остального кода. Гении ебать!
В общем ключе задача формулируется так: "У библиотеки или тулзы отсутствует нужный api для взаимодействия, а вам позарез надо вмешаться в ее модель выполнения". Звучит конечно так себе. Поэтому добавим "...для разработки или тестирования"😏
Это был пост-затравка. В следующем посте я расскажу, как решал эту проблему в python. Но подобный метод может применяться практически в любых интерпретируемых ЯП. В комментах пишите, как бы вы решали подобную проблему👇
❤5
Итак, я обозначил проблему. Вкратце: у нас библиотека или тулза, которая настолько говяная, что к ней не подступиться через публичный api, но недостаточно говяная, чтоб от нее отказываться. А нам позарез надо что-то поменять в ее поведении.
В комментах никто не написал, как бы это делал (эх 😔), поэтому дам ответ сам: monkey patch - это возможность менять код в рантайме.
Как же это выглядит в моем случае. У меня тулза - это вонючий langgraph studio (lgs), и мне надо, чтоб во всем коде, при запуске через lgs было доступно подключение к бд.
Напоминаю, что все питонисты обмазываются декораторами. Я решил не отставать. Написал декоратор db_aware такой, что
Пусть точка входа в наш граф находится в файлике my_agent/graph.py, его мы подаем на вход lgs. В файлике my_agent/nodes.py находится код всех нод графа. Напоминаю, что ноды - это просто функции. Тогда можно написать новую точку входа только для lgs
Выглядит потно, согласен. Но на самом деле, главное здесь - это подход. Ведь он применим ко всем популярным интерпертируемым языкам программирования: python, php, ruby, js.
Например, в php можно тупо перегрузить stream для чтения файлов, и менять код прямо во время чтения интерпретатором исходников. Именно так работает пакет dg/bypass-finals, позволяющий мокать final классы.
Самое главное, помните, что monkey patch следует использовать только при разработке! В продакшне использовать подобное - это надо быть очень смелым, либо очень тупым. Лучше не надо.
В комментах никто не написал, как бы это делал (эх 😔), поэтому дам ответ сам: monkey patch - это возможность менять код в рантайме.
Как же это выглядит в моем случае. У меня тулза - это вонючий langgraph studio (lgs), и мне надо, чтоб во всем коде, при запуске через lgs было доступно подключение к бд.
Напоминаю, что все питонисты обмазываются декораторами. Я решил не отставать. Написал декоратор db_aware такой, что
@db_aware
async def some_func():
# db стопроц будет тут
db = get_db()
# далее в коде юзаем db в хвост и гриву
Пусть точка входа в наш граф находится в файлике my_agent/graph.py, его мы подаем на вход lgs. В файлике my_agent/nodes.py находится код всех нод графа. Напоминаю, что ноды - это просто функции. Тогда можно написать новую точку входа только для lgs
# Исходный пакет с нодами
NODES_MODULE: Final[str] = "my_agent.nodes"
class DbAwareNodes(ModuleType):
def __init__(self, module: str):
super().__init__(module)
# Сохраняем исходные функции
self._original_nodes = importlib.import_module(NODES_MODULE)
# __getattr__ вызывается когда интерпретатор импортирует имя из модуля
def __getattr__(self, name):
# Находим исходную функцию
node = getattr(self._original_nodes, name)
# Декоратор работает только с корутинами
if iscoroutinefunction(node):
# Декорируем исходную функцию
return db_aware(node)
return node
# Здесь подменяем дефолтный модуль на наш пропатченный
sys.modules[NODES_MODULE] = DbAwareNodes(NODES_MODULE)
# Экспортируем наш граф, будто это точка входа.
from pain_agent.graph import graph
__all__ = ["graph"]
Выглядит потно, согласен. Но на самом деле, главное здесь - это подход. Ведь он применим ко всем популярным интерпертируемым языкам программирования: python, php, ruby, js.
Например, в php можно тупо перегрузить stream для чтения файлов, и менять код прямо во время чтения интерпретатором исходников. Именно так работает пакет dg/bypass-finals, позволяющий мокать final классы.
Самое главное, помните, что monkey patch следует использовать только при разработке! В продакшне использовать подобное - это надо быть очень смелым, либо очень тупым. Лучше не надо.
❤3👍3🙈2
Вышло очередное обновление индекса языков программирование TIOBE. Меня прикалывает, как создатели TIOBE продолжают срать себе в штаны, делая вид, что все в порядке👌
"У нас Ada обогнал PHP и Kotlin, а Visual Basic будет попизже Golang. Perl попал в 10ку, а Ruby, даже в 20ке не видно. Typescript мы поместили на 35е место. Вас что-то смущает?.. Нам поебать!" - так прокомментировал свежие данные рейтинга глава TIOBE Вротстислав Хуепутолов.
Ну а если серьезно, когда видишь в одном рейтинге matlab и php, то сразу же вопрос возникает, а для кого это составляется вообще? в чем польза?
The index can be used to check whether your programming skills are still up to date or to make a strategic decision about what programming language should be adopted when starting to build a new software system.
Я считаю, что индекс можно использовать для проверки того, на сколько челики в TIOBE ебнулись в своих метриках, который нахер никому кроме них самих не нужны. Тупее было бы только составить рейтинг по количеству букв в названии языка. Хотя нет, даже это имело бы больше смысла😡
Единственное, что могло бы оправдать этот рейтинг из жопы, это если бы TIOBE просто бабок занесли. Но мы об этом никогда не узнаем.
"У нас Ada обогнал PHP и Kotlin, а Visual Basic будет попизже Golang. Perl попал в 10ку, а Ruby, даже в 20ке не видно. Typescript мы поместили на 35е место. Вас что-то смущает?.. Нам поебать!" - так прокомментировал свежие данные рейтинга глава TIOBE Вротстислав Хуепутолов.
Ну а если серьезно, когда видишь в одном рейтинге matlab и php, то сразу же вопрос возникает, а для кого это составляется вообще? в чем польза?
The index can be used to check whether your programming skills are still up to date or to make a strategic decision about what programming language should be adopted when starting to build a new software system.
Я считаю, что индекс можно использовать для проверки того, на сколько челики в TIOBE ебнулись в своих метриках, который нахер никому кроме них самих не нужны. Тупее было бы только составить рейтинг по количеству букв в названии языка. Хотя нет, даже это имело бы больше смысла😡
Единственное, что могло бы оправдать этот рейтинг из жопы, это если бы TIOBE просто бабок занесли. Но мы об этом никогда не узнаем.
Че-то в последнее время тяжеловато идет написание постов. Много работы и уходящее лето, когда надо успеть чуток почиллить, - делают свое дело!
Контент план есть, тем много. Темы появляются быстрее, чем успевается о них писать. Но я тот еще писака, поэтому на написание одного поста у меня уходит от 2х часов 😔 Сами понимаете, как с такими таймингами тяжело вписаться в текущую жизнь.
Тем не менее, будем надеяться, что с приходом осени все стабилизируется. На улице будет нечего делать, и придется заниматься эскопизмом в канале 😊
Контент план есть, тем много. Темы появляются быстрее, чем успевается о них писать. Но я тот еще писака, поэтому на написание одного поста у меня уходит от 2х часов 😔 Сами понимаете, как с такими таймингами тяжело вписаться в текущую жизнь.
Тем не менее, будем надеяться, что с приходом осени все стабилизируется. На улице будет нечего делать, и придется заниматься эскопизмом в канале 😊
❤2🤯2
Но чтобы перерывы в постах о программировании не были такими уж длинными, я воспользуюсь читерной рубрикой "подписчики набрасывают на вентилятор".
Мне тут скинули старую, но презабавнейшую статью What is {} + {} in JavaScript?. В статье автор задается вопросом, а что будет при выполнении следующих операций в js
Спойлер:будут приколы.
Для []+{} ответ '[object Object]'. []+[] - это ''. И наконец, два моих "любимых": {} + [] дает 0, а {} + {} вернет NaN.
Для "любимых" объяснение такое, что первый {} js интерпретирует как пустой блок кода 🤡, а дальше уже приводит второй аргумент к числу в соответствии с действием унарного оператора +. Ну не красота ли! Об остальных можно прочесть в статье.
Может показаться, что всем результатам есть логичное объяснение. И в статье это объяснение дается. Но по факту, это просто торчащие уши плохого дизайна js, с которым приходится мириться и по сей день, используя костыли в виде ts. Запоминать такое поведение категорически противопоказанно для сохранения рассудка! 👆
А если на собеседованиях вас спрашивают про подобные особенности, то можете смело проваливать это интервью, потому что тех. лид долбаеб, и делать в такой компании нечего. Ну либо можете зазубрить кучу таких кейсов и задоминировать! Но будьте осторожны, ведь тогда можно самому ебнуться наглухо и стать вонючим душнилой🤓 Смотрите сами короче
Мне тут скинули старую, но презабавнейшую статью What is {} + {} in JavaScript?. В статье автор задается вопросом, а что будет при выполнении следующих операций в js
[]+{}
[]+[]
{}+[]
{}+{}
Спойлер:
Для []+{} ответ '[object Object]'. []+[] - это ''. И наконец, два моих "любимых": {} + [] дает 0, а {} + {} вернет NaN.
Для "любимых" объяснение такое, что первый {} js интерпретирует как пустой блок кода 🤡, а дальше уже приводит второй аргумент к числу в соответствии с действием унарного оператора +. Ну не красота ли! Об остальных можно прочесть в статье.
Может показаться, что всем результатам есть логичное объяснение. И в статье это объяснение дается. Но по факту, это просто торчащие уши плохого дизайна js, с которым приходится мириться и по сей день, используя костыли в виде ts. Запоминать такое поведение категорически противопоказанно для сохранения рассудка! 👆
А если на собеседованиях вас спрашивают про подобные особенности, то можете смело проваливать это интервью, потому что тех. лид долбаеб, и делать в такой компании нечего. Ну либо можете зазубрить кучу таких кейсов и задоминировать! Но будьте осторожны, ведь тогда можно самому ебнуться наглухо и стать вонючим душнилой🤓 Смотрите сами короче
2Ality
What is {} + {} in JavaScript?
Recently, the lightning talk “Wat” by Gary Bernhardt pointed out an interesting JavaScript quirk: You get unexpected results when you add objects and/or arrays. This post explains those results.
😁5💯2❤1