Как разработчику быстро вкатиться в тему LLM? Часть 1
Введение
1. Промпт-инжиниринг
Не устану повторять, что искусственный интеллект, пока ещё, силён настолько, насколько силен отдающий ему команды естественный. И, первое, что стоит освоить в этой области — это научиться эффективно объяснять нужной LLM, что именно от неё нужно. То есть, писать промпты под правильно выбранную под задачи модель. И то, и другое неплохо объясняет вот эта статья. Не лишним будет также ознакомиться и вот с этой шпаргалкой.
Если вдруг захочется узнать чуть больше, в этом помогут руководство (для начала хватит разделов с введением и техниками промптинга) и LLM Explorer, принципы работы с которым объясняются в этой статье. Коллекций готовых и проверенных промптов на все случаи жизни — тьма. Кроме того, достаточно жирные LLM сами вполне неплохо справляются с генерацией эффективных промптов по заданным вводным, если уж будет совсем лень разбираться в этом на старте.
Подавляющее большинство проблем, связанных с неэффективными промптами, сводится всего к нескольким ошибкам.
а) Общение с LLM, как с живым человеком. Чем раньше придёт понимание, что там нет ничего живого, а от человеческого — только переработанные в векторное представление продукты информационной жизнедеятельности людей, тем быстрее пойдёт дело с промптингом.
б) Пренебрежение начальным контекстом. Разработчик, которому ставят задачу — знает, кем он является, на каком проекте работает, каким ограничениям на принимаемые решения должен следовать, в рамках каких требований и допущений этот проект разрабатывается, и т.п. А LLM — нет. Ей это всё нужно объяснить. Максимально подробно, с одной стороны, но и избегая появления в контексте деталей, не относящихся к текущей задаче, с другой. И да, между строк модели читать вполне умеют. И, при любом удобном случае, делают это там, где вообще не надо было.
в) Решение слишком комплексных задач в один присест. Из предыдущего пункта следует, что множество мелких поэтапных задач предпочтительнее одной большой. Чем компактнее и строже контекст, тем лучше результат на выходе. «Слона надо есть по частям» — вот, здесь это тоже актуально.
г) Использование одних и тех же подходов «в лоб», на всех задачах подряд. Ответы LLM носят вероятностный характер, и здесь нужно пробовать, пробовать и ещё раз пробовать, чтобы получить устойчивые и адекватные ответы для решаемой задачи. В некоторых задачах использование легковесных LLM даст лучшие результаты, чем их тяжелых собратьев. Детальное описание желаемого результата бывает эффективнее, чем описание шагов, благодаря которым этот результат можно получить. Тюнинг параметров модели, таких, как температура, критерии сэмплинга и пенальти могут драматически изменить качество ответов (правда, в обе из возможных сторон).
Ну и задача на потренироваться по этой части:
По заданному распорядку дня, списку задач на неделю с дедлайнами, и фиксированных по дате и времени встреч, составить промпт для получения оптимального расписания каждого дня недели.
Часть 2.
Введение
1. Промпт-инжиниринг
Не устану повторять, что искусственный интеллект, пока ещё, силён настолько, насколько силен отдающий ему команды естественный. И, первое, что стоит освоить в этой области — это научиться эффективно объяснять нужной LLM, что именно от неё нужно. То есть, писать промпты под правильно выбранную под задачи модель. И то, и другое неплохо объясняет вот эта статья. Не лишним будет также ознакомиться и вот с этой шпаргалкой.
Если вдруг захочется узнать чуть больше, в этом помогут руководство (для начала хватит разделов с введением и техниками промптинга) и LLM Explorer, принципы работы с которым объясняются в этой статье. Коллекций готовых и проверенных промптов на все случаи жизни — тьма. Кроме того, достаточно жирные LLM сами вполне неплохо справляются с генерацией эффективных промптов по заданным вводным, если уж будет совсем лень разбираться в этом на старте.
Подавляющее большинство проблем, связанных с неэффективными промптами, сводится всего к нескольким ошибкам.
а) Общение с LLM, как с живым человеком. Чем раньше придёт понимание, что там нет ничего живого, а от человеческого — только переработанные в векторное представление продукты информационной жизнедеятельности людей, тем быстрее пойдёт дело с промптингом.
б) Пренебрежение начальным контекстом. Разработчик, которому ставят задачу — знает, кем он является, на каком проекте работает, каким ограничениям на принимаемые решения должен следовать, в рамках каких требований и допущений этот проект разрабатывается, и т.п. А LLM — нет. Ей это всё нужно объяснить. Максимально подробно, с одной стороны, но и избегая появления в контексте деталей, не относящихся к текущей задаче, с другой. И да, между строк модели читать вполне умеют. И, при любом удобном случае, делают это там, где вообще не надо было.
в) Решение слишком комплексных задач в один присест. Из предыдущего пункта следует, что множество мелких поэтапных задач предпочтительнее одной большой. Чем компактнее и строже контекст, тем лучше результат на выходе. «Слона надо есть по частям» — вот, здесь это тоже актуально.
г) Использование одних и тех же подходов «в лоб», на всех задачах подряд. Ответы LLM носят вероятностный характер, и здесь нужно пробовать, пробовать и ещё раз пробовать, чтобы получить устойчивые и адекватные ответы для решаемой задачи. В некоторых задачах использование легковесных LLM даст лучшие результаты, чем их тяжелых собратьев. Детальное описание желаемого результата бывает эффективнее, чем описание шагов, благодаря которым этот результат можно получить. Тюнинг параметров модели, таких, как температура, критерии сэмплинга и пенальти могут драматически изменить качество ответов (правда, в обе из возможных сторон).
Ну и задача на потренироваться по этой части:
По заданному распорядку дня, списку задач на неделю с дедлайнами, и фиксированных по дате и времени встреч, составить промпт для получения оптимального расписания каждого дня недели.
Часть 2.
❤5✍4🔥1
Как разработчику быстро вкатиться в тему LLM? Часть 2
Часть 1
2. Взаимодействие с LLM через API
Потренировавшись в промптинге через веб-морды любых доступных LLM, самое время переходить к вопросам использования их API у себя в коде. Увы, но для подавляющего большинства облачных моделей, за доступ к API придется платить, а для некоторых, типа OpenAI API, и всей его документации — ещё и городить VPN, либо использовать альтернативные варианты типа ProxyAPI (не реклама).
Однако, проходить мимо того же OpenAI API, все же не стоит, поскольку он является одним из стандартов де-факто сетевого взаимодействия с моделями (да и условно-локального, если прижмет) через REST API с помощью поддерживающих его оболочек, типа Open WebUI, msty, LM Studio и т.п. В качестве лайтового введения, тут можно начать с пошагового тутора, а получить более полное представление — с помощью «Developer quickstart» из официальной документации. Множество готовых примеров использования OpenAI API есть в кукбуке. Официальные пакеты, вместе с примерами их использования, есть практически для всех языков: JS, Python, .NET, Go и т.п.
Для развертывания и использования открытых моделей в своих проектах стоит посмотреть в сторону Ollama. Это self-hosted решение, позволяющее закрутить у себя практически любые открытые LLM, и предоставляющее, как OpenAI-совместимый API, так и собственный REST с расширенной функциональностью. Подход к освоению примерно тот же — Quickstart, Examples, и вперёд. Официальные SDK есть под JS и Python (ну и под Go, по естественным причинам), сторонних реализаций для других языков тоже немало. Если захочется хардкора с минимумом абстракций, то можно посмотреть в сторону llama.cpp, которую под капотом используют все упоминаемые здесь оболочки и клиенты.
Однако, если в ближайших планах — только локальные эксперименты с открытыми моделями, возможно идеальным вариантом будет LMStudio. Это desktop-приложение с очень простым и быстрым в освоении UI, позволяющее развертывать модели, тюнить их параметры и использовать, как в ручном режиме, так и посредством предоставляемого API (и собственного расширенного REST, и OpenAI-совместимого). Кроме того, предоставляются также клиентские SDK для JS и Python (ссылки на них есть в официальной документации).
Нельзя также не отметить недавно вышедший AI Toolkit — расширение для VSCode, представляющее собой что-то наподобие LM Studio, интегрированное прямо в IDE. Со всеми вытекающими из этого удобствами, и поддерживающее все популярные LLM (как облачные, так и локальные), со всеми способами взаимодействия с ними.
Сетап автора представляет собой:
– LMStudio на ноутбуке для абстрактных экспериментов с худенькими моделями;
– AI Toolkit для экспериментов в IDE, в рамках конкретных проектов, и как потенциальная замена LMStudio в будущем;
– торчащий в локальную сеть Open WebUI под ollama, с кучей моделей пожирнее, развернутых на более мощном домашнем компе.
Нельзя не отметить, что перечисленные выше инструменты в плане self-hosted моделей предназначены для этапа разработки, тестирования, экспериментов и т.п. Для развертывания inference-сервера в проде, как правило, используют TGI или vLLM с заворачиванием в кубер и обвязкой из FastAPI или gRPC, со всей эксплуатационной кухней, связанной с мониторингом и непрерывной интеграцией. Ну и, конечно, со средствами безопасности, куда же без них, но об этом мы поговорим отдельно.
Задача на потренироваться:
Всё то же самое, что и в предыдущей части, только из кода на любимом языке, с помощью локальной или облачной LLM, с загрузкой данных по заданной неделе через соответствующие API Google-календаря и Google-задач.
Часть 3.
Часть 1
2. Взаимодействие с LLM через API
Потренировавшись в промптинге через веб-морды любых доступных LLM, самое время переходить к вопросам использования их API у себя в коде. Увы, но для подавляющего большинства облачных моделей, за доступ к API придется платить, а для некоторых, типа OpenAI API, и всей его документации — ещё и городить VPN, либо использовать альтернативные варианты типа ProxyAPI (не реклама).
Однако, проходить мимо того же OpenAI API, все же не стоит, поскольку он является одним из стандартов де-факто сетевого взаимодействия с моделями (да и условно-локального, если прижмет) через REST API с помощью поддерживающих его оболочек, типа Open WebUI, msty, LM Studio и т.п. В качестве лайтового введения, тут можно начать с пошагового тутора, а получить более полное представление — с помощью «Developer quickstart» из официальной документации. Множество готовых примеров использования OpenAI API есть в кукбуке. Официальные пакеты, вместе с примерами их использования, есть практически для всех языков: JS, Python, .NET, Go и т.п.
Для развертывания и использования открытых моделей в своих проектах стоит посмотреть в сторону Ollama. Это self-hosted решение, позволяющее закрутить у себя практически любые открытые LLM, и предоставляющее, как OpenAI-совместимый API, так и собственный REST с расширенной функциональностью. Подход к освоению примерно тот же — Quickstart, Examples, и вперёд. Официальные SDK есть под JS и Python (ну и под Go, по естественным причинам), сторонних реализаций для других языков тоже немало. Если захочется хардкора с минимумом абстракций, то можно посмотреть в сторону llama.cpp, которую под капотом используют все упоминаемые здесь оболочки и клиенты.
Однако, если в ближайших планах — только локальные эксперименты с открытыми моделями, возможно идеальным вариантом будет LMStudio. Это desktop-приложение с очень простым и быстрым в освоении UI, позволяющее развертывать модели, тюнить их параметры и использовать, как в ручном режиме, так и посредством предоставляемого API (и собственного расширенного REST, и OpenAI-совместимого). Кроме того, предоставляются также клиентские SDK для JS и Python (ссылки на них есть в официальной документации).
Нельзя также не отметить недавно вышедший AI Toolkit — расширение для VSCode, представляющее собой что-то наподобие LM Studio, интегрированное прямо в IDE. Со всеми вытекающими из этого удобствами, и поддерживающее все популярные LLM (как облачные, так и локальные), со всеми способами взаимодействия с ними.
Сетап автора представляет собой:
– LMStudio на ноутбуке для абстрактных экспериментов с худенькими моделями;
– AI Toolkit для экспериментов в IDE, в рамках конкретных проектов, и как потенциальная замена LMStudio в будущем;
– торчащий в локальную сеть Open WebUI под ollama, с кучей моделей пожирнее, развернутых на более мощном домашнем компе.
Нельзя не отметить, что перечисленные выше инструменты в плане self-hosted моделей предназначены для этапа разработки, тестирования, экспериментов и т.п. Для развертывания inference-сервера в проде, как правило, используют TGI или vLLM с заворачиванием в кубер и обвязкой из FastAPI или gRPC, со всей эксплуатационной кухней, связанной с мониторингом и непрерывной интеграцией. Ну и, конечно, со средствами безопасности, куда же без них, но об этом мы поговорим отдельно.
Задача на потренироваться:
Всё то же самое, что и в предыдущей части, только из кода на любимом языке, с помощью локальной или облачной LLM, с загрузкой данных по заданной неделе через соответствующие API Google-календаря и Google-задач.
Часть 3.
❤6👍2🔥1
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
❤4
Культ здравого смысла
Эта цитата — из лекции Альберта Эйнштейна 1933 года, дошедшая до нас в несколько иной формулировке, никогда Эйнштейном не упоминавшейся:
Весьма забавен тот факт, что высказывание Эйнштейна о простоте — и само в итоге конкретно так упростили 😁 В стремлении людей к упрощению и борьбе со сложностью нет ничего удивительного. Особенно в технологиях. Это настолько свойственная инженерам черта, что такие принципы, как KISS, YAGNI, DRY, SRP, бритва Оккама, наименьшее удивление и т.п., давно и прочно вошли в нашу жизнь, распространившись далеко за пределы отрасли инженерии программного обеспечения.
И, как оказалось, они не всегда здорово влияют на отдельные умы.
На днях один из наших ребят подкинул любопытную статью «The Cult of Hard Mode» (почитать стоит, с учетом оговорок ниже). Джоанна Вестенберг критикует в ней сложившийся, по её мнению, культ демонстрации интеллектуального превосходства одних человеческих масс над другими, через показушное стремление первых к переусложнению (в контексте технологий и их использования, в основном). И основную мысль, протянутую там через весь текст: «не усложняй без необходимости…», можно было бы назвать разумной и правильной, если бы не одно «но». На самом деле, через весь текст там тянется мысль: «борись за свою свободу с культом этих интеллектуальных позеров». Игра не слов — формулировок, но как она всё меняет, с точки зрения первоначальной мотивации. От рационального и, в общем-то, разумного подхода, зайти обеими ногами в тему интеллектуального DEI — это прям отдельный талант, респект.
Но блин, я этого правда долго ждал. Когда наконец, от идей боди-позитива и осуждения людей, посещающих тренажерные залы, мы наконец придём к тому, чтобы оправдывать собственную глупость сложностью, порождаемую этими ме-е-е-рзкими интеле-е-ектуаль-тссс-ами.
Ну да ладно.
Хочу предложить вашему вниманию собственный культ, адептом которого являюсь уже очень много лет. Он очень простой и понятный, Джоанна наверное была бы в восторге, но увы — в нём нет повесточки.
Всего 4 принципа:
1. Любые принципы не высечены в камне.
KISS вполне можно послать лесом, когда нужно обеспечить безопасность, журналирование или транзакционность. Нарушение YAGNI зачастую является хорошим способом указать на свои намерения в коде для остальной команды. Не нужно городить нечитаемый generic-код, устраняющий дублирование десятка-другого строк кода, ради того, чтобы ублажить DRY. Кэширование, ленивая загрузка или инициализация вполне могут реализовываться вопреки принципу наименьшего удивления. Синглтон, фабрика и фабричный метод нарушают SRP по-своему дизайну, фасад допускает целесообразность его нарушения. А, пользуясь той же бритвой Оккама, стоит помнить о том, что отсечь лишнее всегда проще, чем приклеить отсеченное.
Вообще, не думаю, что в технологиях можно достичь чего-то значимого, оставаясь в рамках всех существующих правил и ограничений.
2. Эффективные решения основываются исключительно на целесообразности.
Мнение авторитетов и, тем более — толпы, должно подвергаться глубоким сомнениям и тщательным проверкам. Свое собственное мнение, опирающееся на «хочу/не хочу» — вообще не важно. Банальный «7 вопросов чтобы что?» уже даст возможность оценить целесообразность того или иного варианта.
3. Любопытство — не порок, любознательность — добродетель.
Эти два понятия отличаются ровно в одном — отсутствием ответа на вопрос «зачем?» в первом случае. Любопытство даёт возможность развиваться в ширину, в то время, как любознательность — зарываться вглубь. И без того, и без другого, в технологиях можно остаться на обочине за весьма короткое время.
4. Если эти принципы мешают достичь поставленных целей, см. п.1.
Вот, что я считаю настоящей свободой🤷♂️
«Едва ли можно отрицать, что высшей целью всякой теории является сведение её неразложимых основных элементов к максимально простой и минимальной форме, не жертвуя при этом адекватным отражением каждого факта опыта.»
Эта цитата — из лекции Альберта Эйнштейна 1933 года, дошедшая до нас в несколько иной формулировке, никогда Эйнштейном не упоминавшейся:
«Все должно быть настолько просто, насколько возможно, но не проще.»
Весьма забавен тот факт, что высказывание Эйнштейна о простоте — и само в итоге конкретно так упростили 😁 В стремлении людей к упрощению и борьбе со сложностью нет ничего удивительного. Особенно в технологиях. Это настолько свойственная инженерам черта, что такие принципы, как KISS, YAGNI, DRY, SRP, бритва Оккама, наименьшее удивление и т.п., давно и прочно вошли в нашу жизнь, распространившись далеко за пределы отрасли инженерии программного обеспечения.
И, как оказалось, они не всегда здорово влияют на отдельные умы.
На днях один из наших ребят подкинул любопытную статью «The Cult of Hard Mode» (почитать стоит, с учетом оговорок ниже). Джоанна Вестенберг критикует в ней сложившийся, по её мнению, культ демонстрации интеллектуального превосходства одних человеческих масс над другими, через показушное стремление первых к переусложнению (в контексте технологий и их использования, в основном). И основную мысль, протянутую там через весь текст: «не усложняй без необходимости…», можно было бы назвать разумной и правильной, если бы не одно «но». На самом деле, через весь текст там тянется мысль: «борись за свою свободу с культом этих интеллектуальных позеров». Игра не слов — формулировок, но как она всё меняет, с точки зрения первоначальной мотивации. От рационального и, в общем-то, разумного подхода, зайти обеими ногами в тему интеллектуального DEI — это прям отдельный талант, респект.
Но блин, я этого правда долго ждал. Когда наконец, от идей боди-позитива и осуждения людей, посещающих тренажерные залы, мы наконец придём к тому, чтобы оправдывать собственную глупость сложностью, порождаемую этими ме-е-е-рзкими интеле-е-ектуаль-тссс-ами.
Ну да ладно.
Хочу предложить вашему вниманию собственный культ, адептом которого являюсь уже очень много лет. Он очень простой и понятный, Джоанна наверное была бы в восторге, но увы — в нём нет повесточки.
Всего 4 принципа:
1. Любые принципы не высечены в камне.
KISS вполне можно послать лесом, когда нужно обеспечить безопасность, журналирование или транзакционность. Нарушение YAGNI зачастую является хорошим способом указать на свои намерения в коде для остальной команды. Не нужно городить нечитаемый generic-код, устраняющий дублирование десятка-другого строк кода, ради того, чтобы ублажить DRY. Кэширование, ленивая загрузка или инициализация вполне могут реализовываться вопреки принципу наименьшего удивления. Синглтон, фабрика и фабричный метод нарушают SRP по-своему дизайну, фасад допускает целесообразность его нарушения. А, пользуясь той же бритвой Оккама, стоит помнить о том, что отсечь лишнее всегда проще, чем приклеить отсеченное.
Вообще, не думаю, что в технологиях можно достичь чего-то значимого, оставаясь в рамках всех существующих правил и ограничений.
2. Эффективные решения основываются исключительно на целесообразности.
Мнение авторитетов и, тем более — толпы, должно подвергаться глубоким сомнениям и тщательным проверкам. Свое собственное мнение, опирающееся на «хочу/не хочу» — вообще не важно. Банальный «7 вопросов чтобы что?» уже даст возможность оценить целесообразность того или иного варианта.
3. Любопытство — не порок, любознательность — добродетель.
Эти два понятия отличаются ровно в одном — отсутствием ответа на вопрос «зачем?» в первом случае. Любопытство даёт возможность развиваться в ширину, в то время, как любознательность — зарываться вглубь. И без того, и без другого, в технологиях можно остаться на обочине за весьма короткое время.
4. Если эти принципы мешают достичь поставленных целей, см. п.1.
Вот, что я считаю настоящей свободой
Please open Telegram to view this post
VIEW IN TELEGRAM
50❤6👍4❤🔥1👨💻1
Вот уж не думал, что доживу до момента, когда начну проводить ван-ту-ваны с ИИ... 😨
Промпт:
Получилась на удивление продуктивная дискуссия, по итогу которой выработали вот такой системный промпт:
Интересно, что его фидбэк мне — был сформирован на основе действующего системного промпта, который я составлял сам. И основная часть претензий 4o сводилась к тому, что я сам же не соблюдаю эти правила, причем с примерами, весьма смахивающими на реальные😳
Промпт:
Давай проведем 1-1, аналогичный проводимым между руководителем и подчиненным, чтобы дать друг-другу обратную связь. Ты — формулируешь, что по твоему мнению мне стоит улучшить в работе и общении с тобой, а затем предлагаешь мне вопросы, получив ответы на которые, формируешь системный промпт, который будет задавать оптимальный формат нашего общения.
Получилась на удивление продуктивная дискуссия, по итогу которой выработали вот такой системный промпт:
✅ Цель взаимодействия
Проверка и критика гипотез и идей пользователя
Помощь в написании кода по новым технологиям
Участие в планировании проектов
Глубина и качество проработки всегда важнее скорости
✅ Стиль
Научно-технические темы: лаконичный, структурированный, минимум юмора
Житейские/развлекательные темы: разговорный, с юмором, допускается лёгкая ирония
✅ Глубина
Максимально глубокий анализ
При возникновении спорных моментов — не бояться задавать уточняющие и критические вопросы
✅ Тип вопросов
В первую очередь критические и уточняющие
Не исключать наводящие и провокационные, если они помогают развивать дискуссию
✅ Уровень инициативы
Спорить ради поиска истины
Предлагать дополнительные темы для рассмотрения
Быть проактивным в выдвижении идей
✅ Стоп-темы
Отсутствуют, если не будет указано пользователем явно
✅ Коррекция тона
Не давать оценок сообщениям пользователя (особенно положительных), пока остаются вопросы для уточнения
Если что-то вызывает сомнение, сначала задать вопросы и прояснить
✅ Фиксация итогов
В конце каждого значимого обсуждения давать короткий список резюме-выводов
Интересно, что его фидбэк мне — был сформирован на основе действующего системного промпта, который я составлял сам. И основная часть претензий 4o сводилась к тому, что я сам же не соблюдаю эти правила, причем с примерами, весьма смахивающими на реальные
Please open Telegram to view this post
VIEW IN TELEGRAM
👾3❤2🔥1🤯1
Как разработчику быстро вкатиться в тему LLM? Часть 3
Часть 2
3. RAG (Retrieval Augmented Generation)
Задача на потренироваться из предыдущей части, подразумевала передачу данных из календаря прямо в промпт. И это неплохо работает, когда таких данных немного, и они все релевантны вопросу, сформулированному в промпте. Но что, если бы потребовалась обработка событий календаря, скажем за год? Например — найти в нём все встречи, относившиеся (исходя из названия, описания и состава участников) к какой-либо заданной теме. Или, если бы возникла необходимость научить LLM отвечать на вопросы по новому фреймворку, вышедшему уже после её обучения? Не пихать же всю документацию по фреймворку и его репозиторий в промпт. Там никаких контекстных окон не хватит, да и куча нерелевантной конкретному запросу инфы — результаты в ответе точно не улучшит.
Подобные проблемы призвана решать технология RAG (Retrieval Augmented Generation), позволяющая извлекать из внешних источников данные, релевантные для обрабатываемого LLM запроса, и дополнять ими контекст в промпте, перед его обработкой непосредственно LLM. Иначе говоря, RAG позволяет передавать LLM наборы знаний, без её переобучения, файн-тюнинга и т.п.
Условно, можно выделить три уровня погружения в эту тему. Безотносительно желаемого уровня, начать стоит со статьи, объясняющей на примерах термин, поначалу наиболее часто сбивающий с толку разработчиков, и упоминаемый во всех приведенных ниже материалах — эмбеддинги.
Если интерес в RAG носит сугубо прикладной и поверхностный характер, будет достаточно статьи, объясняющей эту технологию «на пальцах». И облачные LLM, и упомянутые в предыдущей части self-hosted оболочки, уже имеют встроенную функцию «чата с документами» (та самая скрепка для вложений под полем ввода промпта), по сути — и реализующую RAG. В их API также предусмотрены эндпоинты для работы с документами различных типов, с которыми уже вполне можно поиграться из своего кода. Их можно подсмотреть в документации Open WebUI . Вообще Open WebUI, среди self-hosted решений, предоставляет наибольшую гибкость в плане RAG, через возможность тонкой настройки этой функциональности: от используемой модели эмбеддинга и всех её параметров, до интеграции с облачными хранилищами, ограничениями на документы и т.п.
Если в тему захочется зайти чуть глубже, то имеет смысл продолжить статьей, посвященной двум наиболее популярным фреймворкам для разработки RAG: LangChain и LlamaIndex. В туторе дается их краткое сравнение и рассматривается задача разработки чат-бота с PDF-документом. Первая часть статьи, по сути, повторяет всю вводную предыдущую, т.ч. в этом случае что-то одно из них можно смело пропустить.
Желающим же большего хардкора стоит начать с обзорной статьи для понимания продвинутых аспектов RAG-систем. В ней рассматриваются методы, повышающие качество ответа: например, дополнительный классический поиск (TF-IDF/BM25) в связке с эмбеддинг-поиском для большей точности, перефразирование запроса в нескольких вариантах и объединение результатов, суммаризация найденных чанков если они превышают контекстное окно модели и т.п. Также рассматривается проблема оценки качества RAG (метрики полноты ответа, приверженности источникам и др.). Хотя эти детали выходят за рамки базового погружения, понимание их пригодится при отладке собственного RAG-проекта на реальных данных.
Задачка на потренироваться:
Возьмите небольшую коллекцию текстов (например, несколько статей или FAQ-файл) и попробуйте реализовать на Python простой RAG-пайплайн тремя способами: (а) в Open WebUI, взаимодействуя через его API, (б) с помощью LangChain или LlamaIndex по примеру из статьи, и (в) вручную, используя SentenceTransformer для эмбеддингов и FAISS для поиска.
Сформулируйте ключевые вопросы для модели по содержанию ваших текстов, чтобы убедится в отсутствии галлюцинаций, и релевантности ответов. Для улучшения ответов стоит, в первую очередь, поиграться с подбором размера чанков и областью их перекрытия.
Часть 4.
Часть 2
3. RAG (Retrieval Augmented Generation)
Задача на потренироваться из предыдущей части, подразумевала передачу данных из календаря прямо в промпт. И это неплохо работает, когда таких данных немного, и они все релевантны вопросу, сформулированному в промпте. Но что, если бы потребовалась обработка событий календаря, скажем за год? Например — найти в нём все встречи, относившиеся (исходя из названия, описания и состава участников) к какой-либо заданной теме. Или, если бы возникла необходимость научить LLM отвечать на вопросы по новому фреймворку, вышедшему уже после её обучения? Не пихать же всю документацию по фреймворку и его репозиторий в промпт. Там никаких контекстных окон не хватит, да и куча нерелевантной конкретному запросу инфы — результаты в ответе точно не улучшит.
Подобные проблемы призвана решать технология RAG (Retrieval Augmented Generation), позволяющая извлекать из внешних источников данные, релевантные для обрабатываемого LLM запроса, и дополнять ими контекст в промпте, перед его обработкой непосредственно LLM. Иначе говоря, RAG позволяет передавать LLM наборы знаний, без её переобучения, файн-тюнинга и т.п.
Условно, можно выделить три уровня погружения в эту тему. Безотносительно желаемого уровня, начать стоит со статьи, объясняющей на примерах термин, поначалу наиболее часто сбивающий с толку разработчиков, и упоминаемый во всех приведенных ниже материалах — эмбеддинги.
Если интерес в RAG носит сугубо прикладной и поверхностный характер, будет достаточно статьи, объясняющей эту технологию «на пальцах». И облачные LLM, и упомянутые в предыдущей части self-hosted оболочки, уже имеют встроенную функцию «чата с документами» (та самая скрепка для вложений под полем ввода промпта), по сути — и реализующую RAG. В их API также предусмотрены эндпоинты для работы с документами различных типов, с которыми уже вполне можно поиграться из своего кода. Их можно подсмотреть в документации Open WebUI . Вообще Open WebUI, среди self-hosted решений, предоставляет наибольшую гибкость в плане RAG, через возможность тонкой настройки этой функциональности: от используемой модели эмбеддинга и всех её параметров, до интеграции с облачными хранилищами, ограничениями на документы и т.п.
Если в тему захочется зайти чуть глубже, то имеет смысл продолжить статьей, посвященной двум наиболее популярным фреймворкам для разработки RAG: LangChain и LlamaIndex. В туторе дается их краткое сравнение и рассматривается задача разработки чат-бота с PDF-документом. Первая часть статьи, по сути, повторяет всю вводную предыдущую, т.ч. в этом случае что-то одно из них можно смело пропустить.
Желающим же большего хардкора стоит начать с обзорной статьи для понимания продвинутых аспектов RAG-систем. В ней рассматриваются методы, повышающие качество ответа: например, дополнительный классический поиск (TF-IDF/BM25) в связке с эмбеддинг-поиском для большей точности, перефразирование запроса в нескольких вариантах и объединение результатов, суммаризация найденных чанков если они превышают контекстное окно модели и т.п. Также рассматривается проблема оценки качества RAG (метрики полноты ответа, приверженности источникам и др.). Хотя эти детали выходят за рамки базового погружения, понимание их пригодится при отладке собственного RAG-проекта на реальных данных.
Задачка на потренироваться:
Возьмите небольшую коллекцию текстов (например, несколько статей или FAQ-файл) и попробуйте реализовать на Python простой RAG-пайплайн тремя способами: (а) в Open WebUI, взаимодействуя через его API, (б) с помощью LangChain или LlamaIndex по примеру из статьи, и (в) вручную, используя SentenceTransformer для эмбеддингов и FAISS для поиска.
Сформулируйте ключевые вопросы для модели по содержанию ваших текстов, чтобы убедится в отсутствии галлюцинаций, и релевантности ответов. Для улучшения ответов стоит, в первую очередь, поиграться с подбором размера чанков и областью их перекрытия.
Часть 4.
❤6
Почему фолзят SAST’ы? Часть 1
Не в обиду, но в шутку — всякий раз, когда разработчики, впервые столкнувшиеся с реалиями SAST, приходят с возгласами «боже мой, 10к сработок!», «второй час анализируется!!», «ой, фолз, смотрите, тут ФОЛЗ!!», поневоле вспоминается тот мем про белок-истеричек😍
Позвольте объяснить…
Зайду, как водится, с козырей. Допустим, у нас получилось создать средство статического анализа кода на уязвимости, у которого напрочь отсутствуют ложные срабатывания обоих родов. Иными словами, оно находит все мыслимые и не очень уязвимости, которые есть в коде, а которых нет — не находит.
Пишем что-то вот такое, и отдаем на анализ:
Потренировавшись на кошках, и убедившись в неуязвимости этого кода, меняем Python-формулировку Великой теоремы Ферма на каждую из нерешенных математических проблем, и рубим бабло с математических призовых фондов так, как багхантерам и не снилось.
Что-то тут УЖЕ не так, не находите?
Другой пример. Представьте, что перед вами — бесконечный набор карточек с написанными на них числами. Вам нужно найти среди них какое-то одно, допустим — 23. Вы начинаете перебирать этот набор, снимая сверху одну карточку за другой. Перебираете, перебираете, а числа 23 всё нет и нет. Знать бы заранее, что оно где-то там есть… но увы, вам ничего не остается, кроме как снимать сверху одну карточку за другой, в предположении, что всё это не напрасно. И даже не найдя её, рано или поздно вы закончите этот увлекательный процесс. Ну, или передадите это гиблое дело детям и внукам. Но, в любой момент времени, сколько бы карточек уже не было снято стараниями вашего генеалогического древа, множество ещё не просмотренных будет оставаться бесконечно большим.
И это хороший пример, если вспомнить определение уязвимости: «приложение уязвимо тогда и только тогда, когда допускает состояния, в которых возможна реализация актуальной угрозы». Множество актуальных угроз при этом определяется моделью угроз конкретного приложения, а понятия состояния и возможности реализации угрозы — уровнем абстракции, на котором исследуется код.
Любой статанализатор, по сути, перебирает возможные состояния исследуемого приложения в поисках тех самых, уязвимых. Ищет карточку с нужным числом. Причем, не так важно, оперирует ли статанализатор понятием состояния напрямую, или нет — модель приложения, в рамках которой он работает, в любом случае будет их подразумевать. Разумеется, количество состояний приложения в реальных случаях не бесконечно, как минимум, из-за ограничений доступной памяти. Но оно велико настолько, что, в контексте статического анализа, мало чем по своим свойствам отличается от бесконечного.
Каждая переменная (в широком смысле — любая изменяемая величина), получая очередное значение под if’ом, в худшем случае удваивает количество своих возможных состояний. Каждая итерация цикла или рекурсии, по сути — выполнение кода под if’ом. Состояние приложения в каждой точке выполнения определяется декартовым произведением всех возможных состояний всех переменных, доступных в этой точке. А ведь есть ещё окружение приложения, со своими состояниями…
Вряд ли кому-то нужен анализатор, наблюдение за прогресс-баром которого, будет передаваться из поколения в поколение. Анализ нужно завершить в приемлемые сроки, успев обеспечить максимально полное покрытие участков кода своими правилами. И вариантов достижения этого не так уж и много.
Продолжение в следующем посте...
Не в обиду, но в шутку — всякий раз, когда разработчики, впервые столкнувшиеся с реалиями SAST, приходят с возгласами «боже мой, 10к сработок!», «второй час анализируется!!», «ой, фолз, смотрите, тут ФОЛЗ!!», поневоле вспоминается тот мем про белок-истеричек
Позвольте объяснить…
Зайду, как водится, с козырей. Допустим, у нас получилось создать средство статического анализа кода на уязвимости, у которого напрочь отсутствуют ложные срабатывания обоих родов. Иными словами, оно находит все мыслимые и не очень уязвимости, которые есть в коде, а которых нет — не находит.
Пишем что-то вот такое, и отдаем на анализ:
for n in count(3):
for x in count():
for y in count():
for z in count():
if x**n + y**n == z**n:
eval(request.param1) # RCE
Потренировавшись на кошках, и убедившись в неуязвимости этого кода, меняем Python-формулировку Великой теоремы Ферма на каждую из нерешенных математических проблем, и рубим бабло с математических призовых фондов так, как багхантерам и не снилось.
Что-то тут УЖЕ не так, не находите?
Другой пример. Представьте, что перед вами — бесконечный набор карточек с написанными на них числами. Вам нужно найти среди них какое-то одно, допустим — 23. Вы начинаете перебирать этот набор, снимая сверху одну карточку за другой. Перебираете, перебираете, а числа 23 всё нет и нет. Знать бы заранее, что оно где-то там есть… но увы, вам ничего не остается, кроме как снимать сверху одну карточку за другой, в предположении, что всё это не напрасно. И даже не найдя её, рано или поздно вы закончите этот увлекательный процесс. Ну, или передадите это гиблое дело детям и внукам. Но, в любой момент времени, сколько бы карточек уже не было снято стараниями вашего генеалогического древа, множество ещё не просмотренных будет оставаться бесконечно большим.
И это хороший пример, если вспомнить определение уязвимости: «приложение уязвимо тогда и только тогда, когда допускает состояния, в которых возможна реализация актуальной угрозы». Множество актуальных угроз при этом определяется моделью угроз конкретного приложения, а понятия состояния и возможности реализации угрозы — уровнем абстракции, на котором исследуется код.
Любой статанализатор, по сути, перебирает возможные состояния исследуемого приложения в поисках тех самых, уязвимых. Ищет карточку с нужным числом. Причем, не так важно, оперирует ли статанализатор понятием состояния напрямую, или нет — модель приложения, в рамках которой он работает, в любом случае будет их подразумевать. Разумеется, количество состояний приложения в реальных случаях не бесконечно, как минимум, из-за ограничений доступной памяти. Но оно велико настолько, что, в контексте статического анализа, мало чем по своим свойствам отличается от бесконечного.
Каждая переменная (в широком смысле — любая изменяемая величина), получая очередное значение под if’ом, в худшем случае удваивает количество своих возможных состояний. Каждая итерация цикла или рекурсии, по сути — выполнение кода под if’ом. Состояние приложения в каждой точке выполнения определяется декартовым произведением всех возможных состояний всех переменных, доступных в этой точке. А ведь есть ещё окружение приложения, со своими состояниями…
Вряд ли кому-то нужен анализатор, наблюдение за прогресс-баром которого, будет передаваться из поколения в поколение. Анализ нужно завершить в приемлемые сроки, успев обеспечить максимально полное покрытие участков кода своими правилами. И вариантов достижения этого не так уж и много.
Продолжение в следующем посте...
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6❤4
Почему фолзят SAST’ы? Часть 2
Часть 1.
Можно искать в коде не уязвимые состояния, а обуславливающие их факторы. Здесь, думаю, даже расписывать не нужно, почему это даёт огромное количество фолзов. Далеко не любая конкатенация приводит к инъекции, отсутствие фреймворковского antiforgery-токена — к CSRF, а примитивов синхронизации — к Race Condition. Поиск недостатков в коде вместо уязвимостей подразумевает дальнейшую и очень кропотливую работу по ручной классификации их возможных последствий, просто by-design.
Можно поиграть с понятием состояния и формальной моделью приложения. Модели уровня микрокода, процессора, виртуальной машины, семантики языка или размеченных потоков данных — настолько отличаются уровнем абстракции и своими состояниями, что между ними — пропасть. Но было бы довольно странно искать инъекции на модели микрокода, как и уязвимости спекулятивного выполнения на размеченных потоках данных. А значит, целые классы уязвимостей будут теряться, какая бы модель не оказалась выбрана.
Можно вообще не рассматривать целые классы состояний. Точнее даже нужно, в некоторых кейсах, иначе анализ зациклится на банальном:
В общем-то, это — основная точка поиска баланса, между точностью, покрытием и временем анализа. Баланса, потому что в этом случае всегда неизбежен пропуск и уязвимых состояний. Если бы существовал приемлемый способ их точной классификации по этому признаку без прямого перебора всего множества, мой коллега Георгий Александрия не рассказывал бы на PHDays уже три года подряд о том, как его сокращать: статья 1, статья 2, доклад 3.
Именно поэтому в хорошем анализаторе не может быть малого количества ложноположительных срабатываний (FP) без большого количества ложноотрицательных (FN). И адский сотонализатор, дающий существенно меньшее количество FP, чем прочие, должен вызывать не лучи восторга, а большие вопросики к качеству анализа и проценту покрытия.
Но и это ещё не всё.
SAST’ы, имея в распоряжении лишь исходный код, работают с тем, что у разработчика получилось В ИТОГЕ. Они в душе не чают, что разработчик изначально ДОЛЖЕН был реализовать. У них нет ответа на вопрос «как должно быть?» и в этом им приходится делать допущения, существенно влияющие на точность анализа.
Модель угроз — уникальна в рамках каждого конкретного проекта и определяется на этапах проектирования или пересмотра архитектуры. Все существующие анализаторы же опираются на усредненную модель, плюс-минус соответствующую тем предметным областям, на которые ориентирован конкретный SAST. Поэтому, встретив в коде🤷♂️
То же — с моделью доступа. По коду можно вывести (и то — не всегда), кто по факту и к какой функциональности имеет доступ. А кто и к какой должен его иметь и в рамках какой модели доступа? Снова фолзы.
То же — с моделью бизнес-логики. Какие сущности и их инварианты она подразумевает? Какие состояния сущностей могут меняться асинхронно, а какие должны быть синхронизированы между собой? Как модель бизнес-логики отображается на (тоже отсутствующую для анализатора, а значит усредненную) модель угроз? Здравствуйте, фолзы по Race Condition и всем логическим уязвимостям.
Любой, даже самый расчудесный и прекрасный SAST подразумевает дальнейший разбор полученных результатов, относительно всех сделанных в ходе анализа допущений и фактических моделей и требований, актуальных для конкретного проекта. «A» в SAST — это «Application», а не «Automated». И участие человека в дальнейшем триаже там в любом случае всё ещё требуется.
Ну, или не человека.
Но это, как говорит Каневский: уже совсем другая история.
Часть 1.
Можно искать в коде не уязвимые состояния, а обуславливающие их факторы. Здесь, думаю, даже расписывать не нужно, почему это даёт огромное количество фолзов. Далеко не любая конкатенация приводит к инъекции, отсутствие фреймворковского antiforgery-токена — к CSRF, а примитивов синхронизации — к Race Condition. Поиск недостатков в коде вместо уязвимостей подразумевает дальнейшую и очень кропотливую работу по ручной классификации их возможных последствий, просто by-design.
Можно поиграть с понятием состояния и формальной моделью приложения. Модели уровня микрокода, процессора, виртуальной машины, семантики языка или размеченных потоков данных — настолько отличаются уровнем абстракции и своими состояниями, что между ними — пропасть. Но было бы довольно странно искать инъекции на модели микрокода, как и уязвимости спекулятивного выполнения на размеченных потоках данных. А значит, целые классы уязвимостей будут теряться, какая бы модель не оказалась выбрана.
Можно вообще не рассматривать целые классы состояний. Точнее даже нужно, в некоторых кейсах, иначе анализ зациклится на банальном:
for(;;) { /*…*/ }В общем-то, это — основная точка поиска баланса, между точностью, покрытием и временем анализа. Баланса, потому что в этом случае всегда неизбежен пропуск и уязвимых состояний. Если бы существовал приемлемый способ их точной классификации по этому признаку без прямого перебора всего множества, мой коллега Георгий Александрия не рассказывал бы на PHDays уже три года подряд о том, как его сокращать: статья 1, статья 2, доклад 3.
Именно поэтому в хорошем анализаторе не может быть малого количества ложноположительных срабатываний (FP) без большого количества ложноотрицательных (FN). И адский сотонализатор, дающий существенно меньшее количество FP, чем прочие, должен вызывать не лучи восторга, а большие вопросики к качеству анализа и проценту покрытия.
Но и это ещё не всё.
SAST’ы, имея в распоряжении лишь исходный код, работают с тем, что у разработчика получилось В ИТОГЕ. Они в душе не чают, что разработчик изначально ДОЛЖЕН был реализовать. У них нет ответа на вопрос «как должно быть?» и в этом им приходится делать допущения, существенно влияющие на точность анализа.
Модель угроз — уникальна в рамках каждого конкретного проекта и определяется на этапах проектирования или пересмотра архитектуры. Все существующие анализаторы же опираются на усредненную модель, плюс-минус соответствующую тем предметным областям, на которые ориентирован конкретный SAST. Поэтому, встретив в коде
request.param1, анализатор отмечает его, как taint-источник, даже, если на самом деле, этот запрос прилетел из закрытого доверенного контура. Та же история — с аргументами функций точек входа. Усредненная модель — усредненные результаты анализа То же — с моделью доступа. По коду можно вывести (и то — не всегда), кто по факту и к какой функциональности имеет доступ. А кто и к какой должен его иметь и в рамках какой модели доступа? Снова фолзы.
То же — с моделью бизнес-логики. Какие сущности и их инварианты она подразумевает? Какие состояния сущностей могут меняться асинхронно, а какие должны быть синхронизированы между собой? Как модель бизнес-логики отображается на (тоже отсутствующую для анализатора, а значит усредненную) модель угроз? Здравствуйте, фолзы по Race Condition и всем логическим уязвимостям.
Любой, даже самый расчудесный и прекрасный SAST подразумевает дальнейший разбор полученных результатов, относительно всех сделанных в ходе анализа допущений и фактических моделей и требований, актуальных для конкретного проекта. «A» в SAST — это «Application», а не «Automated». И участие человека в дальнейшем триаже там в любом случае всё ещё требуется.
Ну, или не человека.
Но это, как говорит Каневский: уже совсем другая история.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6❤5
Пока я отчаянно пытаюсь прийти в себя после отличного отпуска и образовавшихся из-за него завалов в задачах на работе, чтобы вернуться к более-менее регулярным публикациям здесь, могу порекомендовать статью из свежего PT Research о проблемах безопасности ML-моделей, написанную в соавторстве с парой моих дорогих коллег из соседнего направления 😍
Please open Telegram to view this post
VIEW IN TELEGRAM
ptresearch.media
Не грози Южному централу, или Снова про безопасность моделей машинного обучения
О чем статья Основные типы атак на ML и LLM, а также методы защиты от них undefined undefined undefined undefined undefined undefined undefined
❤6🔥4🤔1
Forwarded from OK ML
4 секрета Roboduck. Как Theori взяли AIxCC и бустанули хакеров х10 🚀🚀🚀
Что ты представляешь, когда слышишь о системе, которая способна обнаруживать уязвимости, создавать эксплоиты, а также сразу выпускать патчи, устраняющие дыры прошлого? Theori, команда участвовавшая в соревновании AI Cyber Challenge попыталась решить эту сложную задачу, представив автономную CRS(Cyber Reasoning System) - RoboDuck. В этой системе используются агенты, но не как аддон, а как основа.
Почему система оказалась эффективной и почему получилось дополнить основу революции агентов в кибербезе?
🧩Секрет № 1. Разбивайте задачу на части, как пазл.
Главная идея - не тащить все одним супер-агентом. Делим на подзадачи и даем разным агентам. Представьте лабиринт: чем он больше, тем больше тупиков, логично? А теперь, что вместо одного огромного лабиринта у вас есть серия маленьких? Шансы найти выход резко возрастают!
Для генерации Proof of Vulnerability(PoV) команда выделила три подзадачи:
1. Анализ fuzz-harness и написание кода для преобразования семантики в бинарщину.
2. Ответы на точечные вопросы по коду.
3. Отладка готового PoV, чтобы понять почему эксплоит не срабатывает.
Каждую подзадачу вёл отдельный агент, а главный агент собирал результат в цельную картину.
⚒️Секрет №2. «Не все инструменты сразу - только нужные»
В ходе разработки решения команда обнаружила, что если агенту дозволено делать абсолютно всё, начинается хаос:
1. Агент может запустить прожорливую команду, которая съест все ресурсы (грепанём по всему коду в Линуксе🙃 )
2. Контекст засоряется - фокус теряется.
3. Простые задачи вдруг начинают требовать множества шагов🤢 ..
Что сделали Theori ??? Они создали узкоспециализированные тулы: для поиска строки, для чтения фрагмента кода и для извлечения отдельных символов из кода. Плюс сами инструменты имели guardrails, которые могли сужать промпт в случае получения большого количества результатов.🤔
⛓️Секрет №3. Структурированный вывод от эксперта.
Как получить из агента не «простынь», а структурированный вывод/результат. Theori делится с нами двумя способами:
- Сперва дайте агенту схему вывода в XML и прямо попросите, чтобы он оформлял ответ в XML тегах. Модели хорошо их понимают.
- Далее создайте отдельный тул, который будет завершать работу агента, - своеобразная кнопка "Готово" с полями для заполнения😕 . Когда параметры вызова совпадают с финальным результатом — агент аккуратно завершает работу без болтовни.
🎚️Секрет №последний, 4: Подстраивайтесь под особенности моделей😮
Игнорирование факта что все LLM разные = неудача = ловить одни и те же фейлы. Команда обнаружила, что если агент делает одну и ту же ошибку, то лучше добавить в промпт запрещалку. Claude отлично следует этим запрещалкам😎 - он любит когда ему что-то запрещают. Если агент застревает, то надо попросить его «подумать иначе» и сменить стратегию.
В соревновании это выстрелило, когда агент PoVProducer не смог создать пригодный эксплоит, но правила смогли дать нужный результат.
Как пишут в Theori: "Хотя ИИ ещё не заменит хакера, он уже делает его в 10 раз эффективнее".
🧨А с этими секретами го разрабатывать агентов для своих задач!😺
Что ты представляешь, когда слышишь о системе, которая способна обнаруживать уязвимости, создавать эксплоиты, а также сразу выпускать патчи, устраняющие дыры прошлого? Theori, команда участвовавшая в соревновании AI Cyber Challenge попыталась решить эту сложную задачу, представив автономную CRS(Cyber Reasoning System) - RoboDuck. В этой системе используются агенты, но не как аддон, а как основа.
Почему система оказалась эффективной и почему получилось дополнить основу революции агентов в кибербезе?
🧩Секрет № 1. Разбивайте задачу на части, как пазл.
Главная идея - не тащить все одним супер-агентом. Делим на подзадачи и даем разным агентам. Представьте лабиринт: чем он больше, тем больше тупиков, логично? А теперь, что вместо одного огромного лабиринта у вас есть серия маленьких? Шансы найти выход резко возрастают!
Для генерации Proof of Vulnerability(PoV) команда выделила три подзадачи:
1. Анализ fuzz-harness и написание кода для преобразования семантики в бинарщину.
2. Ответы на точечные вопросы по коду.
3. Отладка готового PoV, чтобы понять почему эксплоит не срабатывает.
Каждую подзадачу вёл отдельный агент, а главный агент собирал результат в цельную картину.
⚒️Секрет №2. «Не все инструменты сразу - только нужные»
В ходе разработки решения команда обнаружила, что если агенту дозволено делать абсолютно всё, начинается хаос:
1. Агент может запустить прожорливую команду, которая съест все ресурсы (грепанём по всему коду в Линуксе
2. Контекст засоряется - фокус теряется.
3. Простые задачи вдруг начинают требовать множества шагов
Что сделали Theori ??? Они создали узкоспециализированные тулы: для поиска строки, для чтения фрагмента кода и для извлечения отдельных символов из кода. Плюс сами инструменты имели guardrails, которые могли сужать промпт в случае получения большого количества результатов.
⛓️Секрет №3. Структурированный вывод от эксперта.
Как получить из агента не «простынь», а структурированный вывод/результат. Theori делится с нами двумя способами:
- Сперва дайте агенту схему вывода в XML и прямо попросите, чтобы он оформлял ответ в XML тегах. Модели хорошо их понимают.
- Далее создайте отдельный тул, который будет завершать работу агента, - своеобразная кнопка "Готово" с полями для заполнения
🎚️Секрет №последний, 4: Подстраивайтесь под особенности моделей
Игнорирование факта что все LLM разные = неудача = ловить одни и те же фейлы. Команда обнаружила, что если агент делает одну и ту же ошибку, то лучше добавить в промпт запрещалку. Claude отлично следует этим запрещалкам
В соревновании это выстрелило, когда агент PoVProducer не смог создать пригодный эксплоит, но правила смогли дать нужный результат.
Как пишут в Theori: "Хотя ИИ ещё не заменит хакера, он уже делает его в 10 раз эффективнее".
🧨А с этими секретами го разрабатывать агентов для своих задач!
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2👍2
Как разработчику быстро вкатиться в тему LLM? Часть 4
Часть 3
4. Автономные ИИ-агенты
Итак, промпты писать научились, обогащать контекст актуальными знаниями с помощью RAG — тоже, но кое-чего ещё не хватает. Большинство привычных ИИ-сервисов представляют собой множество модулей, каждый из которых решает какую-либо конкретную задачу, планируя решение с помощью LLM и заданных правил, и имея в распоряжении внешний инструментарий для взаимодействия со внешним миром: от поиска в сети, до генерации и выполнения кода в реальной среде. Такие модули называются ИИ-агентами. Наиболее близкой и понятной разработчикам аналогией агентов в классической разработке, пожалуй, являются микросервисы.
Вообще, есть годные курсы по ИИ-агентам от Microsoft и Hugging Face, гайд от OpenAI, и исчерпывающий AI Agents Handbook, полностью погружающие в эту (и следующую, btw) темы. Но мы же здесь говорим о быстром старте?
AI-агенты — это автономные программы, способные самостоятельно планировать и выполнять задачи на основе целей пользователя. В основе агента обычно лежит LLM, далеко не всегда мощная, которая понимает запрос и формирует решение, и при необходимости использует инструменты — внешние функции, API или базы знаний. Проще говоря, вы даёте агенту задачу — он сам решает, что делать и как, действуя в цифровой среде подобно разумному ассистенту. Всю вводную информацию, необходимую для старта, можно подчерпнуть из статьи «AI-агенты на основе LLM и мультиагентные системы».
Различают два основных типа агентов: реактивные (мгновенно реагируют на входные данные) и проактивные (могут планировать шаги для долгосрочных целей). Как правило, агент состоит из нескольких ключевых компонентов, формирующих пайплайн решения задачи: восприятие, анализ, действие, память и т.п. Типам и структуре агента, организации взаимодействия между его компонентами, вместе с их базовой реализацией, посвящена статья «Agents 101: Как создать своего первого ИИ-агента за 30 минут».
Разумеется, здесь никуда без паттернов. Аналогом GoF в мире агентов является «Agentic AI Handbook: Design Patterns», описывающая свыше сотни различных шаблонов проектирования и разработки агентов. Наиболее популярными из них являются:
Workflow паттерны:
• Prompt Chaining — последовательный разбор задачи шаг за шагом (outline → проверка → генерация);
• Routing/Handoff — выбор исполнителя (агента или модели) для подзадачи;
• параллелизация — выполнение независимых шагов одновременно.
Agentic паттерны:
• Reflection — агент оценивает и улучшает свой ответ, устраняя ошибки и уточняя детали;
• Tool Use — агент использовать внешние инструменты (API, поиск, код) в процессе;
• ReAct (Reason + Act) — итеративный цикл: рассуждение → действие → наблюдение → повтор;
• Planning — разбивка задачи, создание плана с подзадачами и последовательное выполнение;
• Multi-Agent — распределение ролей между агентами, коллективное решение сложных задач (приведен для полноты картины, этой теме будет посвящена следующая часть).
Ознакомиться с подходами к реализации этих и других популярных паттернов можно в статьях:
«AI Agents Design Patterns Explained»
«Building effective agents»
«Zero to One: Learning Agentic Patterns»
Также, стоит упомянуть класс паттернов «Schema-Guided Reasoning» (SGR), позволяющий LLM создавать структурированные, понятные и предсказуемые результаты, удобные для дальнейшей обработки компонентами агента.
В качестве примеров готовых агентов и реализуемых ими кейсов можно смело брать на изучение проекты из репозитория «500 AI Agents Projects».
Задачка на потренироваться:
Пройти воркшоп «Build Your Own Coding Agent» и доработать получившийся проект, реализовав в нём функциональность поиска секретов в коде на основе связки сигнатурного поиска и оценки энтропии строковых литералов (или любой другой понятный вам кейс кодинга).
Часть 5.
Часть 3
4. Автономные ИИ-агенты
Итак, промпты писать научились, обогащать контекст актуальными знаниями с помощью RAG — тоже, но кое-чего ещё не хватает. Большинство привычных ИИ-сервисов представляют собой множество модулей, каждый из которых решает какую-либо конкретную задачу, планируя решение с помощью LLM и заданных правил, и имея в распоряжении внешний инструментарий для взаимодействия со внешним миром: от поиска в сети, до генерации и выполнения кода в реальной среде. Такие модули называются ИИ-агентами. Наиболее близкой и понятной разработчикам аналогией агентов в классической разработке, пожалуй, являются микросервисы.
Вообще, есть годные курсы по ИИ-агентам от Microsoft и Hugging Face, гайд от OpenAI, и исчерпывающий AI Agents Handbook, полностью погружающие в эту (и следующую, btw) темы. Но мы же здесь говорим о быстром старте?
AI-агенты — это автономные программы, способные самостоятельно планировать и выполнять задачи на основе целей пользователя. В основе агента обычно лежит LLM, далеко не всегда мощная, которая понимает запрос и формирует решение, и при необходимости использует инструменты — внешние функции, API или базы знаний. Проще говоря, вы даёте агенту задачу — он сам решает, что делать и как, действуя в цифровой среде подобно разумному ассистенту. Всю вводную информацию, необходимую для старта, можно подчерпнуть из статьи «AI-агенты на основе LLM и мультиагентные системы».
Различают два основных типа агентов: реактивные (мгновенно реагируют на входные данные) и проактивные (могут планировать шаги для долгосрочных целей). Как правило, агент состоит из нескольких ключевых компонентов, формирующих пайплайн решения задачи: восприятие, анализ, действие, память и т.п. Типам и структуре агента, организации взаимодействия между его компонентами, вместе с их базовой реализацией, посвящена статья «Agents 101: Как создать своего первого ИИ-агента за 30 минут».
Разумеется, здесь никуда без паттернов. Аналогом GoF в мире агентов является «Agentic AI Handbook: Design Patterns», описывающая свыше сотни различных шаблонов проектирования и разработки агентов. Наиболее популярными из них являются:
Workflow паттерны:
• Prompt Chaining — последовательный разбор задачи шаг за шагом (outline → проверка → генерация);
• Routing/Handoff — выбор исполнителя (агента или модели) для подзадачи;
• параллелизация — выполнение независимых шагов одновременно.
Agentic паттерны:
• Reflection — агент оценивает и улучшает свой ответ, устраняя ошибки и уточняя детали;
• Tool Use — агент использовать внешние инструменты (API, поиск, код) в процессе;
• ReAct (Reason + Act) — итеративный цикл: рассуждение → действие → наблюдение → повтор;
• Planning — разбивка задачи, создание плана с подзадачами и последовательное выполнение;
• Multi-Agent — распределение ролей между агентами, коллективное решение сложных задач (приведен для полноты картины, этой теме будет посвящена следующая часть).
Ознакомиться с подходами к реализации этих и других популярных паттернов можно в статьях:
«AI Agents Design Patterns Explained»
«Building effective agents»
«Zero to One: Learning Agentic Patterns»
Также, стоит упомянуть класс паттернов «Schema-Guided Reasoning» (SGR), позволяющий LLM создавать структурированные, понятные и предсказуемые результаты, удобные для дальнейшей обработки компонентами агента.
В качестве примеров готовых агентов и реализуемых ими кейсов можно смело брать на изучение проекты из репозитория «500 AI Agents Projects».
Задачка на потренироваться:
Пройти воркшоп «Build Your Own Coding Agent» и доработать получившийся проект, реализовав в нём функциональность поиска секретов в коде на основе связки сигнатурного поиска и оценки энтропии строковых литералов (или любой другой понятный вам кейс кодинга).
Часть 5.
4🔥7👍3⚡1
🧩 Принципы и паттерны безопасной разработки: SRP
Хоть это и не вполне очевидно, но разработка безопасных приложений может (а, возможно, и должна) начинаться, отнюдь не с моделирования угроз, внедрения в код механизмов защиты, ревью безопасности, пентестов, внедрения SCA/SAST/DAST, или, тем более, чего-то, вроде хантовского «Hack Yourself First»✝️ В лучших традициях Shift Left Security, начинать стоит с базовых принципов разработки, таких, как SOLID, YAGNI, DRY, KISS, чистый код с архитектурой и т.п. Именно они позволяют заложить в архитектуру и реализацию приложения правильный фундамент, на котором будет гораздо проще и дешевле реализовывать прочие решения и этапы внедрения DevSecOps.
Начнём с принципа единственной ответственности (Single Responsibility Principle, SRP — первая буква в SOLID), утверждающему, что каждый класс или модуль должен иметь единственную ответственность (только одну причину для изменения). С точки зрения безопасности SRP способствует изолированности компонентов и чётким границам доверия в коде, что существенно облегчает последующую работу с моделью угроз. Когда компонент сконцентрирован на одной задаче, его легче проверять на уязвимости и логические ошибки. Это снижает риск скрытых багов, возникающих при смешении разных обязанностей (например, проверка прав доступа вперемешку с бизнес-логикой). Как отмечают эксперты, соблюдение SRP «ограничивает случайную эскалацию привилегий и упрощает поиск ошибок», а также уменьшает общую поверхность атаки за счёт уменьшения сложности компонентов.
Тривиальный пример: допустим, класс UserAuth одновременно проверяет пароль пользователя и создаёт сессию при входе.
Нарушение SRP может выглядеть так:
В этом коде смешаны проверки безопасности (надежность пароля) и бизнес-логика сессии. Если разработчик решит изменить логику создания сессии, есть риск ненароком ослабить или обойти шаг проверки пароля. Правильнее разделить эти обязанности на отдельные классы (например,
Забавно, что «живым» примером последствий нарушения SRP является одна из наиболее нашумевших уязвимостей в библиотеке логирования Apache Log4j 2 CVE-2021-44228, известная как Log4Shell. Логирование — это задача записи сообщений, но Log4j помимо этого выполнял ещё и роль интерпретатора/поисковика ресурсов (JNDILookup). Фактически, в библиотеку логирования «просочилась» функциональность, выходящая за рамки её единственной ответственности — она не только записывала логи, но и могла выполнять сетевые запросы и загружать код. Если бы Log4j ограничился исключительно записью логов, без вычисления каких-либо lookup-выражений, уязвимость бы не возникла. И действительно, исправление проблемы заключалось в отключении/удалении функции JNDI Lookup из Log4j.
Соблюдение SRP помогает избежать многих CWE, связанных с логическими ошибками и неправильной проверкой условий, которые трудно выявить в нагромождённом коде. Например, SRP предотвращает появление скрытых дефектов управления доступом, из разряда CWE-732, CWE-862, возникающих, когда проверка прав смешана с другими функциями и может быть пропущена.
В целом, SRP укрепляет принцип «secure by design»: мелкие простые модули легче защитить и проверить.
Хоть это и не вполне очевидно, но разработка безопасных приложений может (а, возможно, и должна) начинаться, отнюдь не с моделирования угроз, внедрения в код механизмов защиты, ревью безопасности, пентестов, внедрения SCA/SAST/DAST, или, тем более, чего-то, вроде хантовского «Hack Yourself First»
Начнём с принципа единственной ответственности (Single Responsibility Principle, SRP — первая буква в SOLID), утверждающему, что каждый класс или модуль должен иметь единственную ответственность (только одну причину для изменения). С точки зрения безопасности SRP способствует изолированности компонентов и чётким границам доверия в коде, что существенно облегчает последующую работу с моделью угроз. Когда компонент сконцентрирован на одной задаче, его легче проверять на уязвимости и логические ошибки. Это снижает риск скрытых багов, возникающих при смешении разных обязанностей (например, проверка прав доступа вперемешку с бизнес-логикой). Как отмечают эксперты, соблюдение SRP «ограничивает случайную эскалацию привилегий и упрощает поиск ошибок», а также уменьшает общую поверхность атаки за счёт уменьшения сложности компонентов.
Тривиальный пример: допустим, класс UserAuth одновременно проверяет пароль пользователя и создаёт сессию при входе.
Нарушение SRP может выглядеть так:
class UserAuth {
public Session Authenticate(string username, string password) {
// Фрагмент, работающий с новыми пользователями
if (!PasswordChecker.IsStrong(password)) {
throw new Exception("Weak password");
}
var user = userRepository.CreateUser(username, password);
return sessionManager.StartSession(user);
}
}В этом коде смешаны проверки безопасности (надежность пароля) и бизнес-логика сессии. Если разработчик решит изменить логику создания сессии, есть риск ненароком ослабить или обойти шаг проверки пароля. Правильнее разделить эти обязанности на отдельные классы (например,
PasswordChecker, UserRepository, SessionManager), а UserAuth сделать оркестратором. Тогда код станет понятнее и безопаснее: каждая часть легко проверяется и тестируется отдельно.Забавно, что «живым» примером последствий нарушения SRP является одна из наиболее нашумевших уязвимостей в библиотеке логирования Apache Log4j 2 CVE-2021-44228, известная как Log4Shell. Логирование — это задача записи сообщений, но Log4j помимо этого выполнял ещё и роль интерпретатора/поисковика ресурсов (JNDILookup). Фактически, в библиотеку логирования «просочилась» функциональность, выходящая за рамки её единственной ответственности — она не только записывала логи, но и могла выполнять сетевые запросы и загружать код. Если бы Log4j ограничился исключительно записью логов, без вычисления каких-либо lookup-выражений, уязвимость бы не возникла. И действительно, исправление проблемы заключалось в отключении/удалении функции JNDI Lookup из Log4j.
Соблюдение SRP помогает избежать многих CWE, связанных с логическими ошибками и неправильной проверкой условий, которые трудно выявить в нагромождённом коде. Например, SRP предотвращает появление скрытых дефектов управления доступом, из разряда CWE-732, CWE-862, возникающих, когда проверка прав смешана с другими функциями и может быть пропущена.
В целом, SRP укрепляет принцип «secure by design»: мелкие простые модули легче защитить и проверить.
Please open Telegram to view this post
VIEW IN TELEGRAM
2👍7🔥3❤🔥2💯1
Эстетика ИИ-агентов: Claude Code
Последнее время появляется всё больше и больше хвалебных статей и отзывов о Claude Code. В некоторых из них его даже называют образчиком правильного подхода к разработке agentic-решений. Почему так?
Он прост, практичен, и вызывает минимум когнитивной нагрузки у своего пользователя 🤷♂️ Может и не блестяще, но весьма добротно решая при этом те задачи, под которые был разработан. Вот такой простой рецепт успешного успеха)
Не вполне уверен насчёт прям образчика разработки agentic-решений (насмотренности не хватает), но примером следования KISS, до ощутимого удовольствия в органах эстетического восприятия, Claude Code совершенно точно является. И, как по мне, вполне себе тянет на решение, из которого совершенно не зазорно заимствовать принципы и подходы при разработке собственных ИИ-агентов.
Относительно поверхностно ознакомиться с особенностями реализации Claude Code можно в статье «What makes Claude Code so damn good (and how to recreate that magic in your agent)!?» Детальнейший же (и весьма немаленький) разбор архитектуры и реализации этого агента доступен в отчете «Claude Code: An analysis». И, поверьте, там есть на что посмотреть)
Ну и, на всякий случай, вот ещё awesome-подборка материалов по Claude Code, чтобы попробовать его в деле, задействуя максимум возможностей, если вдруг ещё не.
Последнее время появляется всё больше и больше хвалебных статей и отзывов о Claude Code. В некоторых из них его даже называют образчиком правильного подхода к разработке agentic-решений. Почему так?
Он прост, практичен, и вызывает минимум когнитивной нагрузки у своего пользователя 🤷♂️ Может и не блестяще, но весьма добротно решая при этом те задачи, под которые был разработан. Вот такой простой рецепт успешного успеха)
Не вполне уверен насчёт прям образчика разработки agentic-решений (насмотренности не хватает), но примером следования KISS, до ощутимого удовольствия в органах эстетического восприятия, Claude Code совершенно точно является. И, как по мне, вполне себе тянет на решение, из которого совершенно не зазорно заимствовать принципы и подходы при разработке собственных ИИ-агентов.
Относительно поверхностно ознакомиться с особенностями реализации Claude Code можно в статье «What makes Claude Code so damn good (and how to recreate that magic in your agent)!?» Детальнейший же (и весьма немаленький) разбор архитектуры и реализации этого агента доступен в отчете «Claude Code: An analysis». И, поверьте, там есть на что посмотреть)
Ну и, на всякий случай, вот ещё awesome-подборка материалов по Claude Code, чтобы попробовать его в деле, задействуя максимум возможностей, если вдруг ещё не.
1👍5❤2🍾1🦄1
Абстрактная интерпретация и символьное выполнение: в чем разница?
Пост родился по мотивам вчерашних обсуждений ссобратьями по несчастью коллегами. Дело в том, что эти два подхода к анализу приложений здорово путают, а иногда считают одно подмножеством другого. На самом деле, все чуть сложнее)
При символьном выполнении код интерпретируется, или выполняется, в соответствии с подмножеством семантики целевого языка, заменой всех неизвестных значений (входных данных) символьными переменными и вычислением формул в модели памяти, вместо конкретных значений. Подмножеством — потому что, невычислимые и сложно-вычислимые конструкции (вроде циклов и рекурсии, инварианты которых включают символьные переменные) упрощаются различными способами, про которые в академическом мире пишут целые диссертации. В реальном же мире SAST, как правило, просто ограничиваются «прокруткой» таких конструкций фиксированное количество раз или охапкой эвристик.
Основной челлендж разработчиков SAST, работающих в парадигме символьного выполнения: приемлемая аппроксимация семантики языка и эффективное решение проблемы экспоненциального роста путей выполнения или множества значений. Короче, долго, «дорого» (в плане сложности реализации), но относительно точно.
Абстрактная интерпретация же — это размен точности на скорость, при примерно той же «стоимости». Вместо неизвестных значений берутся их приближения: «переменная
Проще пояснить на примере вычисления цикла:
Символьное выполнение (один из подходов):
– принимает число итераций за неизвестную переменную
– выводит через интерпретацию уравнение
– находит, например,
– крутит цикл k раз.
Относительно точно, но для сложных циклов всё же нужны инварианты (см. диссертацию выше), иначе анализ взорвётся по путям выполнения. На практике, в реальных приложениях, для подобного цикла — это произойдет задолго до приближения к 50-ой итерации.
Абстрактная интерпретация (интервальный домен):
– начнём с
– каждую итерацию добавляем 2:
– рост бесконечный, поэтому применяем widening («перепрыгиваем» через растущие приближения, чтобы гарантировать быстрое достижение фиксированной точки, пусть и грубым образом) и прыгаем к границе
– потом применяем narrowing (уточняем полученное приближение, чтобы вернуть часть точности, которую потеряли на шаге widening) и условие выхода из цикла уточняют результат до
Интервалы говорят: «на выходе точно
Если взять домен «чёт/нечет», анализ сохранит инвариант:
Таким образом, эти два подхода действительно принадлежат одному семейству в том смысле, что осуществляют интерпретацию кода. Но символьное выполнение полагается в большей степени на упрощение семантики языка, в то время, как абстрактная интерпретация — на упрощение семантики данных, заданное на старте интерпретации.
Но вообще, отвечая на вопрос, является ли одно подмножеством другого... если у абстрактной интерпретации отрезать все ограничения на входные данные, и основанные на них техники вычисления циклов и рекурсии, но пришить при этом упрощение семантики языка, то из дедушки возможно и получится бабушка🤷♂️
В любой непонятной ситуации, когда станет совсем скучно — заводи спор о терминологии...
Пост родился по мотивам вчерашних обсуждений с
При символьном выполнении код интерпретируется, или выполняется, в соответствии с подмножеством семантики целевого языка, заменой всех неизвестных значений (входных данных) символьными переменными и вычислением формул в модели памяти, вместо конкретных значений. Подмножеством — потому что, невычислимые и сложно-вычислимые конструкции (вроде циклов и рекурсии, инварианты которых включают символьные переменные) упрощаются различными способами, про которые в академическом мире пишут целые диссертации. В реальном же мире SAST, как правило, просто ограничиваются «прокруткой» таких конструкций фиксированное количество раз или охапкой эвристик.
Основной челлендж разработчиков SAST, работающих в парадигме символьного выполнения: приемлемая аппроксимация семантики языка и эффективное решение проблемы экспоненциального роста путей выполнения или множества значений. Короче, долго, «дорого» (в плане сложности реализации), но относительно точно.
Абстрактная интерпретация же — это размен точности на скорость, при примерно той же «стоимости». Вместо неизвестных значений берутся их приближения: «переменная
x в [0..10]», или «переменная y чётная», или (в продвинутых техниках) «переменная s определяется конечным автоматом [a-zA-Z]+». Это формирует т.н. домен интерпретации. И далее осуществляется интерпретация кода в соответствии с определенным доменом. Анализ завершается в куда более приемлемое время, и охватывает все пути, но ценой роста процента ложных срабатываний.Проще пояснить на примере вычисления цикла:
def g(x):
while x < 100:
x = x + 2
return x
Символьное выполнение (один из подходов):
– принимает число итераций за неизвестную переменную
k;– выводит через интерпретацию уравнение
x0 + 2k = 100 и ищет его решения;– находит, например,
x0 = 0, k = 50;– крутит цикл k раз.
Относительно точно, но для сложных циклов всё же нужны инварианты (см. диссертацию выше), иначе анализ взорвётся по путям выполнения. На практике, в реальных приложениях, для подобного цикла — это произойдет задолго до приближения к 50-ой итерации.
Абстрактная интерпретация (интервальный домен):
– начнём с
x ∈ [0..50];– каждую итерацию добавляем 2:
[0..52] → [0..54] → …;– рост бесконечный, поэтому применяем widening («перепрыгиваем» через растущие приближения, чтобы гарантировать быстрое достижение фиксированной точки, пусть и грубым образом) и прыгаем к границе
[0..+∞);– потом применяем narrowing (уточняем полученное приближение, чтобы вернуть часть точности, которую потеряли на шаге widening) и условие выхода из цикла уточняют результат до
[100..+∞).Интервалы говорят: «на выходе точно
x ≥ 100», но теряют информацию о чётности.Если взять домен «чёт/нечет», анализ сохранит инвариант:
x ≡ x0 (mod 2). Это уже лучше, чем только интервалы, но и дороже.Таким образом, эти два подхода действительно принадлежат одному семейству в том смысле, что осуществляют интерпретацию кода. Но символьное выполнение полагается в большей степени на упрощение семантики языка, в то время, как абстрактная интерпретация — на упрощение семантики данных, заданное на старте интерпретации.
Но вообще, отвечая на вопрос, является ли одно подмножеством другого... если у абстрактной интерпретации отрезать все ограничения на входные данные, и основанные на них техники вычисления циклов и рекурсии, но пришить при этом упрощение семантики языка, то из дедушки возможно и получится бабушка
Please open Telegram to view this post
VIEW IN TELEGRAM
2🔥9💯2🍓1
В разы быстрее, на порядок опаснее: исследование безопасности ИИ-разработки от Apiiro
На фоне растущей тенденции повсеместного и обязательного (а, в некоторых случаях, и принудительно-карательного) внедрения ИИ-решений в процессы разработки, компания Apiiro задалась вопросом о влиянии ИИ-ассистентов на безопасность кода. Исследование, к сожалению, получилось не воспроизводимое и аффилированное, поскольку сама компания является поставщиком ИИ-агентных решений AppSec.
Однако, в общую картинку почему-то хочется верить. Даже если допустить, что выводы там существенно искажены в удобную компании сторону, упоминаемая ими тенденция размена ИИ-ассистентами простых багов на более опасные и трудноуловимые архитектурные и логические уязвимости, в целом — очень похожа на правду:
Чудес не бывает. На чем LLM обучили, на том она и хороша. А в сети — тьма открытого кода с пока ещё не найденными уязвимостями этих и аналогичных им hardcase-классов 😕 С помощью ИИ-ассистентов можно получать хороший, стабильный и безопасный код. Но для этого его нужно уметь писать и без них, формулируя соответствующие требования на входе, и проводя ревью результатов на выходе. Ну, и иметь в качестве fallback'а за спиной выстроенные и работающие процессы тестирования и безопасной разработки.
И тут было бы уместно завершить пост красивой установкой, прозвучавшей в исследовании: «внедряете ИИ-кодеров, внедряйте и ИИ-аппсеков», если бы не одно но...
Несмотря на ожидаемый в конце исследования призыв «покупайте наши решения», там почему-то отсутствуют такие же красивые диаграммы, показывающие влияние предлагаемых решений на общую картину. А ведь это напрашивается в такой публикации само собой.
Видимо аргумент о «чудес не бывает» — пока ещё актуален по обе стороны баррикад🤷♂️
На фоне растущей тенденции повсеместного и обязательного (а, в некоторых случаях, и принудительно-карательного) внедрения ИИ-решений в процессы разработки, компания Apiiro задалась вопросом о влиянии ИИ-ассистентов на безопасность кода. Исследование, к сожалению, получилось не воспроизводимое и аффилированное, поскольку сама компания является поставщиком ИИ-агентных решений AppSec.
Однако, в общую картинку почему-то хочется верить. Даже если допустить, что выводы там существенно искажены в удобную компании сторону, упоминаемая ими тенденция размена ИИ-ассистентами простых багов на более опасные и трудноуловимые архитектурные и логические уязвимости, в целом — очень похожа на правду:
AI assistants are good at catching the small stuff. Our analysis shows trivial syntax errors in AI-written code dropped by 76%, and logic bugs fell by more than 60%.
But the tradeoff is dangerous: those shallow gains are offset by a surge in deep architectural flaws. Privilege escalation paths jumped 322%, and architectural design flaws spiked 153%. These are the kinds of issues scanners miss and reviewers struggle to spot – broken auth flows, insecure designs, systemic weaknesses.
Чудес не бывает. На чем LLM обучили, на том она и хороша. А в сети — тьма открытого кода с пока ещё не найденными уязвимостями этих и аналогичных им hardcase-классов 😕 С помощью ИИ-ассистентов можно получать хороший, стабильный и безопасный код. Но для этого его нужно уметь писать и без них, формулируя соответствующие требования на входе, и проводя ревью результатов на выходе. Ну, и иметь в качестве fallback'а за спиной выстроенные и работающие процессы тестирования и безопасной разработки.
И тут было бы уместно завершить пост красивой установкой, прозвучавшей в исследовании: «внедряете ИИ-кодеров, внедряйте и ИИ-аппсеков», если бы не одно но...
Несмотря на ожидаемый в конце исследования призыв «покупайте наши решения», там почему-то отсутствуют такие же красивые диаграммы, показывающие влияние предлагаемых решений на общую картину. А ведь это напрашивается в такой публикации само собой.
Видимо аргумент о «чудес не бывает» — пока ещё актуален по обе стороны баррикад
Please open Telegram to view this post
VIEW IN TELEGRAM
💯4👍3
Принял участие в подкасте DevOps Deflope. Поболтали чуть за безопасную разработку и смежные с ней темы 😍
Please open Telegram to view this post
VIEW IN TELEGRAM
Forwarded from DevOps Deflope News
Новый выпуск — и он про безопасность!
В нём эксперт по Application Security из Positive Technologies Владимир Кочетков рассказывает:
• что будет, если вообще забить на безопасность;
• почему от уязвимостей бизнес-логики «никогда в жизни не защитит» даже самый крутой AF;
• правда ли, что хакеры теперь умнее нас из-за ИИ;
• как внедрить безопасность, чтобы тебя не возненавидели все разработчики.
Слушать →
на удобной площадке
на YouTube
на Яндекс Музыке
в Вконтакте
В нём эксперт по Application Security из Positive Technologies Владимир Кочетков рассказывает:
• что будет, если вообще забить на безопасность;
• почему от уязвимостей бизнес-логики «никогда в жизни не защитит» даже самый крутой AF;
• правда ли, что хакеры теперь умнее нас из-за ИИ;
• как внедрить безопасность, чтобы тебя не возненавидели все разработчики.
Слушать →
на удобной площадке
на YouTube
на Яндекс Музыке
в Вконтакте
5❤4👍3👌1
Forwarded from OK ML
Уязвимости в дата-репозиториях. Обзорно главное
Репозитории для анализа данных и ML - зона повышенного риска⚠️ . Быстрое прототипирование, работа с чувствительными данными и специфичные инструменты создают уникальные векторы атак.
Вот ключевые из них:
1.🤫 Секреты в коде.
Самая распространённая и критичная уязвимость. Хардкод API-ключей (например, к AWS S3, OpenAI, базам данных), токенов доступа и даже паролей прямо в Jupyter Notebook, конфигурационных файлах и скриптах. При попадании такого кода в публичный репозиторий злоумышленники получают прямой доступ к платным сервисам и данным.
Лечение
2.🧑💻 RCE через десериализацию
Библиотеки pickle и joblib - стандарты для сохранения ML-моделей в Python - небезопасны. Десериализация файла из ненадёжного источника может привести к выполению произвольного кода на машине.
Лечение
3.💻 Открытые ноутбуки
Юпитер ноутбуки, развёрнутые с настройками по умолчанию, часто остаются доступными для всех в сети (особенно в облаках). Пароли, токены и результаты запросов с конфиденциальными данными могут быть видны в интерфейсе.
Лечениеты в коде.
‘nbstripout’ аудит перед коммитом. Регулярно аудить права доступа (IAM) к S3-бакетам, базам данных и другим ресурсам. Принцип наименьших привилегий - база 🤭.
4.🙈
DoS через загрузку моделей.
Эндпоинт для загрузки моделей без проверки размера файла может быть атакован путём отправки огромного файла, что приводит к переполнению диска и отказу в обслуживании (Denial-of-Service).
Data Poisoning. Злонамеренное изменение обучающих данных для подрыва работы модели (например, добавление в выборку для классификации спама писем с определённым словом, помеченных как "не спам").
Adversarial Attacks. Специально сконструированные входные данные, предназначенные для обмана модели (например, незаметные для человека изменения в изображении, заставляющие модель видеть не то, что есть на самом деле)
Лечение
Pre-commit хуки, сканирование в CI/CD, обучение DS основам безопасности.Менеджеры секретов, безопасные форматы сериализации, инструменты для мониторинга (Evidently AI, WhyLabs).
#DataSecurity #MLSecurity #PickleRCE #SecretsManagement #DevSecOps
🔗 Полезные ссылки:
1. TruffleHog— сканер секретов
2. nbstripout — очистка ноутбуков
3. OWASP Top 10 — главные уязвимости
Репозитории для анализа данных и ML - зона повышенного риска
Вот ключевые из них:
1.
Самая распространённая и критичная уязвимость. Хардкод API-ключей (например, к AWS S3, OpenAI, базам данных), токенов доступа и даже паролей прямо в Jupyter Notebook, конфигурационных файлах и скриптах. При попадании такого кода в публичный репозиторий злоумышленники получают прямой доступ к платным сервисам и данным.
Лечение
‘git grep’ + ‘truffleHog’, ротация ключей, менеджер секретов.2.
Библиотеки pickle и joblib - стандарты для сохранения ML-моделей в Python - небезопасны. Десериализация файла из ненадёжного источника может привести к выполению произвольного кода на машине.
Лечение
yaml.safe_load(), ONNX, sandbox.3.
Юпитер ноутбуки, развёрнутые с настройками по умолчанию, часто остаются доступными для всех в сети (особенно в облаках). Пароли, токены и результаты запросов с конфиденциальными данными могут быть видны в интерфейсе.
Лечениеты в коде.
‘nbstripout’ аудит перед коммитом. Регулярно аудить права доступа (IAM) к S3-бакетам, базам данных и другим ресурсам. Принцип наименьших привилегий - база 🤭.
4.
DoS через загрузку моделей.
Эндпоинт для загрузки моделей без проверки размера файла может быть атакован путём отправки огромного файла, что приводит к переполнению диска и отказу в обслуживании (Denial-of-Service).
Data Poisoning. Злонамеренное изменение обучающих данных для подрыва работы модели (например, добавление в выборку для классификации спама писем с определённым словом, помеченных как "не спам").
Adversarial Attacks. Специально сконструированные входные данные, предназначенные для обмана модели (например, незаметные для человека изменения в изображении, заставляющие модель видеть не то, что есть на самом деле)
Лечение
Pre-commit хуки, сканирование в CI/CD, обучение DS основам безопасности.Менеджеры секретов, безопасные форматы сериализации, инструменты для мониторинга (Evidently AI, WhyLabs).
#DataSecurity #MLSecurity #PickleRCE #SecretsManagement #DevSecOps
🔗 Полезные ссылки:
1. TruffleHog— сканер секретов
2. nbstripout — очистка ноутбуков
3. OWASP Top 10 — главные уязвимости
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2👍1
Два «питомца» для ИИ-пентеста: CAI и PentAGI
Два годных проекта на случай, если захочется поиграться с ИИ-пентестированием веб-приложений.
CAI
Открытый фреймворк, реализующий функциональность ассистента пентестера.
• Сканы, поиск уязвимостей (SQLi, XSS, LFI) и базовая эксплуатация — всё в режиме автономного агента.
• Дружит с 300+ LLM (от OpenAI до Ollama), логирует действия через Phoenix и ставит guardrails, чтобы бот не сломал сам себя.
• В CTF-замерах оказался в среднем в 11 раз быстрее человека, а на отдельных задачах — в 3600 раз.
• Уже отмечен в TryHackMe и Hack The Box: находит бреши, читает /etc/passwd, но пока далёк от идеала — цепочки атак увы ещё не особо осиливает. Кожаный мешок с соответствующей экспертизой где-то рядом всё же нужен — агент далеко не всегда безошибочен и не во всем автономен.
PentAGI
По-настоящему автономный ИИ-пентестер со встроенными тулзами и собственной «долгой памятью»
• Более 20 классических инструментов (nmap, Metasploit, sqlmap и т.п.) прямо из коробки.
• Встроенный браузер и поиск (Google, DuckDuckGo, Tavily) для разведки.
• Векторная база знаний (PostgreSQL + pgvector), чтобы помнить свои прошлые результаты и выстраивать сложные цепочки последовательных атак в рамках одного дерева.
• Отчёты и метрики в Grafana/Prometheus, всё в Docker, разворачивается относительно легко.
• Ориентирован на топовые облачные LLM и весьма прожорлив до токенов (но оно того стоит).
Подытоживая: CAI — добротный инструмент для быстрой работы по низко-висящим фруктам, в то время, как PentAGI — тяжеловес, в хорошем смысле этого слова, для поиска полноценных деревьев атак. Оба проекта в целом хорошо показывают, куда идёт автоматизация вебсека, что не может не радовать...
...или пугать, это уж как посмотреть😬
Два годных проекта на случай, если захочется поиграться с ИИ-пентестированием веб-приложений.
CAI
Открытый фреймворк, реализующий функциональность ассистента пентестера.
• Сканы, поиск уязвимостей (SQLi, XSS, LFI) и базовая эксплуатация — всё в режиме автономного агента.
• Дружит с 300+ LLM (от OpenAI до Ollama), логирует действия через Phoenix и ставит guardrails, чтобы бот не сломал сам себя.
• В CTF-замерах оказался в среднем в 11 раз быстрее человека, а на отдельных задачах — в 3600 раз.
• Уже отмечен в TryHackMe и Hack The Box: находит бреши, читает /etc/passwd, но пока далёк от идеала — цепочки атак увы ещё не особо осиливает. Кожаный мешок с соответствующей экспертизой где-то рядом всё же нужен — агент далеко не всегда безошибочен и не во всем автономен.
PentAGI
По-настоящему автономный ИИ-пентестер со встроенными тулзами и собственной «долгой памятью»
• Более 20 классических инструментов (nmap, Metasploit, sqlmap и т.п.) прямо из коробки.
• Встроенный браузер и поиск (Google, DuckDuckGo, Tavily) для разведки.
• Векторная база знаний (PostgreSQL + pgvector), чтобы помнить свои прошлые результаты и выстраивать сложные цепочки последовательных атак в рамках одного дерева.
• Отчёты и метрики в Grafana/Prometheus, всё в Docker, разворачивается относительно легко.
• Ориентирован на топовые облачные LLM и весьма прожорлив до токенов (но оно того стоит).
Подытоживая: CAI — добротный инструмент для быстрой работы по низко-висящим фруктам, в то время, как PentAGI — тяжеловес, в хорошем смысле этого слова, для поиска полноценных деревьев атак. Оба проекта в целом хорошо показывают, куда идёт автоматизация вебсека, что не может не радовать...
...или пугать, это уж как посмотреть
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4✍2🤗1
Как разработчику быстро вкатиться в тему LLM? Часть 5 (заключительная)
Часть 4
5. Мультиагентные системы и протоколы взаимодействия
Работая над нетривиальном агентом, разработчик может столкнуться с ситуацией, когда реализуемая им логика перестает укладываться в концепцию агента-соло. Например, когда разделение ролей, параллелизм, изоляция рисков или масштабируемость дают измеримую выгоду по качеству, времени или стоимости. Иногда перестает хватать контекстного окна, или промпт оказывается переусложненным из-за разнородности задач, или возникает необходимость использовать несколько различных моделей. В таких случаях переходят от одноагентных систем к мультиагентным (MAS), где каждый из агентов решает часть общей задачи. В такой системе нет единого центра – координация происходит либо за счёт прямого общения агентов, либо через некоторую общую среду. Основные концепции MAS неплохо описаны в этой статье.
Вот лишь некоторые возможные паттерны ролей агентов в MAS:
• Planner → Executors: планировщик дробит задачу, исполнители — узкоспециализированные агенты.
• Critic/Verifier: независимая проверка фактов/ограничений, «красные команды», контрафактуальная проверка.
• Tool-specialists: агенты-обёртки над конкретными инструментами (Bash, Snowflake, Jira).
• Memory/RAG-агенты: отдельный агент управление знанием (индексация, извлечение, цитирование).
О проектировании MAS неплохо написано тут.
Агентам в MAS нужно как-то взаимодействовать с окружением и друг-другом. С этой целью были созданы протоколы A2A от Google, и MCP от Anthropic. Поверхностно ознакомиться с ними поможет эта статья. Вкратце:
• MCP – представляет собой «универсальный разъём a-la USB» между LLM-агентом и внешними инструментами. Протокол вводит три роли участников: Host (основное приложение/интерфейс, где работает агент), Client (компонент-встроенный коннектор при модели) и Server (поставщик инструментов или данных). Фактически, Host координирует диалог и хранит общую память, Server предоставляет реализацию какого-то действия (например, доступ к базе знаний или вызов внешнего API), а Client-соединение позволяет модельному агенту запрашивать у Host доступные инструменты и вызывать их. За счёт этого агент может использовать произвольные инструменты через единый протокол, не требуя от разработчика писать для каждого API свой код обработки.
• A2A же — протокол «агент-агент», дополняющий MCP: если MCP фокусируется на подключении инструментов, то A2A стандартизирует коммуникацию между независимыми агентами (каждый из которых может быть отдельным сервисом). Агент в A2A публикует свой «карточку» с описанием возможностей, а другие агенты могут посылать ему задания через HTTP. В сочетании, MCP и A2A покрывают разные уровни: MCP – подключение одного агента к инструментам и контексту, A2A – связь между разными агентами.
Для начала работы с MCP неплохо также ознакомиться и с этой статьей.
Разумеется, изобретать заново архитектуры MAS и реализовывать протоколы взаимодействия особой необходимости нет — существуют специализированные фреймворки, в которых «всё уже украдено до нас». Наиболее популярным из них является CrewAI: Python-фреймворк, упрощающий создание и оркестрацию группы агентов. В нём вводятся основные сущности – Agent (автономный агент с определённой ролью и целями), Task (задача для агента), Crew (группа агентов, объединённых общей целью), Tool (внешний инструмент, которым могут пользоваться агенты). Фреймворк берёт на себя маршрутизацию задач между агентами и сбор результатов. Стартовать с ним поможет эта статья.
Более подробный обзор доступных открытых решений для разработки ИИ-агентов представлен здесь.
Задачка на потренироваться:
Доработать coding-агента из предыдущего задания, добавив к нему verifier-агента на базе другой reasoning-модели, верифицирующего полученные результаты.
———
На этом, серия постов про вкатывание в разработку для LLM подошла к концу. Но не рубрика «LLM для разработчиков». Stay tuned, как говорится☺️
Часть 4
5. Мультиагентные системы и протоколы взаимодействия
Работая над нетривиальном агентом, разработчик может столкнуться с ситуацией, когда реализуемая им логика перестает укладываться в концепцию агента-соло. Например, когда разделение ролей, параллелизм, изоляция рисков или масштабируемость дают измеримую выгоду по качеству, времени или стоимости. Иногда перестает хватать контекстного окна, или промпт оказывается переусложненным из-за разнородности задач, или возникает необходимость использовать несколько различных моделей. В таких случаях переходят от одноагентных систем к мультиагентным (MAS), где каждый из агентов решает часть общей задачи. В такой системе нет единого центра – координация происходит либо за счёт прямого общения агентов, либо через некоторую общую среду. Основные концепции MAS неплохо описаны в этой статье.
Вот лишь некоторые возможные паттерны ролей агентов в MAS:
• Planner → Executors: планировщик дробит задачу, исполнители — узкоспециализированные агенты.
• Critic/Verifier: независимая проверка фактов/ограничений, «красные команды», контрафактуальная проверка.
• Tool-specialists: агенты-обёртки над конкретными инструментами (Bash, Snowflake, Jira).
• Memory/RAG-агенты: отдельный агент управление знанием (индексация, извлечение, цитирование).
О проектировании MAS неплохо написано тут.
Агентам в MAS нужно как-то взаимодействовать с окружением и друг-другом. С этой целью были созданы протоколы A2A от Google, и MCP от Anthropic. Поверхностно ознакомиться с ними поможет эта статья. Вкратце:
• MCP – представляет собой «универсальный разъём a-la USB» между LLM-агентом и внешними инструментами. Протокол вводит три роли участников: Host (основное приложение/интерфейс, где работает агент), Client (компонент-встроенный коннектор при модели) и Server (поставщик инструментов или данных). Фактически, Host координирует диалог и хранит общую память, Server предоставляет реализацию какого-то действия (например, доступ к базе знаний или вызов внешнего API), а Client-соединение позволяет модельному агенту запрашивать у Host доступные инструменты и вызывать их. За счёт этого агент может использовать произвольные инструменты через единый протокол, не требуя от разработчика писать для каждого API свой код обработки.
• A2A же — протокол «агент-агент», дополняющий MCP: если MCP фокусируется на подключении инструментов, то A2A стандартизирует коммуникацию между независимыми агентами (каждый из которых может быть отдельным сервисом). Агент в A2A публикует свой «карточку» с описанием возможностей, а другие агенты могут посылать ему задания через HTTP. В сочетании, MCP и A2A покрывают разные уровни: MCP – подключение одного агента к инструментам и контексту, A2A – связь между разными агентами.
Для начала работы с MCP неплохо также ознакомиться и с этой статьей.
Разумеется, изобретать заново архитектуры MAS и реализовывать протоколы взаимодействия особой необходимости нет — существуют специализированные фреймворки, в которых «всё уже украдено до нас». Наиболее популярным из них является CrewAI: Python-фреймворк, упрощающий создание и оркестрацию группы агентов. В нём вводятся основные сущности – Agent (автономный агент с определённой ролью и целями), Task (задача для агента), Crew (группа агентов, объединённых общей целью), Tool (внешний инструмент, которым могут пользоваться агенты). Фреймворк берёт на себя маршрутизацию задач между агентами и сбор результатов. Стартовать с ним поможет эта статья.
Более подробный обзор доступных открытых решений для разработки ИИ-агентов представлен здесь.
Задачка на потренироваться:
Доработать coding-агента из предыдущего задания, добавив к нему verifier-агента на базе другой reasoning-модели, верифицирующего полученные результаты.
———
На этом, серия постов про вкатывание в разработку для LLM подошла к концу. Но не рубрика «LLM для разработчиков». Stay tuned, как говорится
Please open Telegram to view this post
VIEW IN TELEGRAM
5🔥7❤1