CodeCrucible: чертёж SAST на основе LLM
@ai_longreadsBlock выложила в open source сканер безопасности, который отправляет в модель весь репозиторий целиком, а не отдельные фрагменты кода. Внутри — четыре ключевые развилки при проектировании LLM-driven SAST, устройство инструмента и честный список ограничений.
Это AI-перевод статьи, сделанный каналом Про AI: Лучшие Статьи и Исследования.
CodeCrucible: чертёж SAST на основе LLM
CodeCrucible: A blueprint for LLM-driven SAST Авторы: Clinton Carpene, Alex Rosenzweig, Andrew Kitis Оригинальный текст:
Сегодня мы выпускаем CodeCrucible — новый инструмент статического тестирования безопасности приложений (SAST, static application security testing) на основе LLM для поиска уязвимостей. Но статья существует не ради самого релиза. «LLM-driven SAST» — уже сложившаяся категория, и конкретно этот инструмент, вероятно, со временем устареет. Дольше проживёт другое — чертёж: проектные решения, компромиссы и референсная кодовая база, которые позволят другим командам адаптировать подход под свои pipeline (конвейеры обработки) и дальше его развивать. Именно эту часть мы и надеемся сделать переиспользуемой.
Зачем эта статья
Как только сканер начинает находить что-то полезное, самым сложным становится интеграция: как его вызывать, где он работает и в каком виде всплывают находки. Эти решения зависят от окружения. Готовый инструмент-«чёрный ящик» почти всегда требует доработки, прежде чем впишется в среду другой команды. В итоге вы обходите архитектурные решения, которые принимали не вы. Куда более переиспользуемая вещь — сам дизайн: компромиссы, режимы отказа и паттерны реализации, которые переносятся из окружения в окружение.
Сам CodeCrucible — пример такого подхода «через чертёж». Исходную версию мы сделали внутри Block как прототип на Python и Node.js: Repomix для упаковки репозитория, agentic-анализ (агентный, с самостоятельными действиями модели) для поиска уязвимостей и веб-интерфейс для запуска сканов и просмотра отчётов. Инструмент, который мы выпускаем сегодня, — тот же чертёж, адаптированный под продакшен в нашем pipeline: нативный CLI на Go, без веб-интерфейса и без пошагового мастера отчётов.
Как мы подошли к задаче
Пространство решений
Мы считаем, что любой LLM-driven SAST-инструмент обязан ответить на четыре вопроса, и эти ответы обычно значат больше, чем выбор модели:
- Компактизация (compaction). Как поместить кодовую базу в контекстное окно модели? Типовые варианты: суммаризация по абстрактному синтаксическому дереву (AST), поиск на основе embeddings, agentic-исследование кода через вызовы инструментов и анализ сниппетов, привязанных к находкам. Все они меняют полноту охвата кодовой базы на скорость и стоимость в tokens (токенах). Наш подход нацелен на максимальное заполнение контекстного окна и качество находок, но он не самый экономичный.
- Идентификация. Как именно просить модель искать уязвимости? Диапазон — от открытых запросов («найди баги») до проходов, нацеленных на конкретные классы слабостей CWE (Common Weakness Enumeration), и циклов самокритики. Компромисс здесь — полнота (recall) против точности (precision) против стоимости в токенах. Мы разделили это на две стадии: открытый поиск в основном проходе, затем углублённый CWE-специфичный анализ на этапе аудита.
- Отсев по релевантности. Как отделить реальные находки от уверенно сформулированных, но ничем не подкреплённых? Обычные ответы: вторая проверка отдельной LLM, детерминированные фильтры, верификация в стиле taint-анализа и ручная триажная разборка. Компромисс — автоматизация против доверия. Мы используем повторный проход LLM-ревью с порогами уверенности плюс детерминированную постобработку: дедупликацию и приоритизацию исходных файлов.
- Детерминированность. Как сделать вывод недетерминированного процесса пригодным для pipeline, если тот рассчитан на стабильный результат? Варианты: генерация при нулевой температуре, голосование по нескольким сэмплам, структурированный вывод, ограниченный схемой, и детерминированная постобработка. Компромисс — стабильность против охвата. Мы ограничиваем структурированные ответы через JSON Schema, добавляя многоуровневую починку JSON и детерминированную зачистку после вызова модели.
Эти решения не независимы. Если вы выбрали AST-суммаризацию для компактизации, то, скорее всего, и во всём остальном сделаете другой выбор. Если выложить их рядом, компромиссы становятся видны ещё до погружения в реализацию.
Что сделали мы
Конкатенация всего репозитория означает: упаковать репозиторий в один вызов LLM, добавить направляющие подсказки и дать модели рассуждать по всему объёму сразу. В теории это не должно было работать. На практике сработало лучше, чем мы ожидали, и поймало классы уязвимостей, которые плохо даются схемам, привязанным к отдельным сниппетам.
Большинство работающих в продакшене LLM-driven SAST-систем ставят перед моделью традиционный анализатор. CodeQL, Semgrep или собственный движок анализа потоков данных выдвигает кандидатов, а LLM проверяет предложенный сниппет. Модель здесь — рецензент, а не основной анализатор. Конкатенация всего репозитория переворачивает эту схему. Первичный анализ делает LLM, и вы платите токенами вместо инженерных усилий на статический анализ. Обе архитектуры в принципе имеют доступ к межфайловому контексту, но платят за него по-разному.
Это важно, потому что системы, привязанные к сниппетам, показывают модели только то, что вышестоящий движок уже знает, куда смотреть. Если ни одно правило не сработало или движок не смог проследить путь, LLM этот код вообще не увидит. Анализ всего репозитория снимает это ограничение. Если код попал в упакованный репозиторий, модель может рассуждать о нём независимо от того, существует ли уже подходящее правило.
Сработало это по трём причинам, которых мы до конца не осознавали на старте. Во-первых, современные reasoning-модели держат длинный кодовый контекст лучше, чем считалось в отрасли, когда складывалась «сниппет-первая» традиция: окно на 200K токенов, обрабатывающее репозиторий на 50K токенов, работает в комфортном для себя режиме. Во-вторых, код хорошо сжимается, если убрать малоценное содержимое — бинарники, lock-файлы, вендоренные зависимости и сгенерированные артефакты. Сжимается он гораздо сильнее, чем обычный текст, под который и настраивалось большинство систем retrieval-augmented generation (RAG). В-третьих, межфайловое рассуждение — как раз одна из областей, где LLM реально сильнее детерминированного SAST, так что каждый дополнительный файл в контексте может дать настоящую пользу.
Уже после того, как мы увидели результат, мы нашли немало текстов, где эту идею заранее списали со счетов. Самый яркий пример — статья Snyk о CodeReduce. Там используется иерархическая дельта-отладка по AST, чтобы сжать код в 20–23 раза перед отправкой в LLM. Исходная посылка: модель должна видеть только сам дефект и тот контекст, который нужен для его понимания. Другие вендоры и академические группы приводят похожие аргументы, обычно по трём основаниям: стоимость токенов в масштабах предприятия, галлюцинации на длинных контекстах и тот факт, что большая часть кода в репозитории не имеет отношения к конкретной уязвимости. Опасения справедливые. Но мы считаем их инженерными задачами: фильтрация, разбиение на чанки, двухпроходное ревью и контроль расходов. Это не повод отвергать саму архитектуру.
Ничего из этого не было для нас очевидно в начале. Прототип строился на догадке: старые советы жёстко ограничивать вход всё ещё применяются в мире, где контекстные окна стали значительно больше. Версия, которая заработала, нас удивила.
Рисунок 1: Pipeline скана определяет границы репозитория, анализирует чанки, аудирует находки и выдаёт SARIF.
Как заставить это работать в масштабе
Вывод не в том, что «надо дождаться контекстных окон побольше». Вывод такой: «считайте анализ всего репозитория поведением по умолчанию, а когда масштаб его ломает — делайте компромиссы явными». Разрыв между размером репозитория и контекстным окном носит структурный характер. Реальные репозитории, особенно монорепозитории, будут и дальше перерастать то, что комфортно помещается в один вызов. Масштаб — не будущая фича из бэклога. Это часть дизайна с самого начала.
На практике это становится задачей бюджетирования раньше, чем задачей чанкинга. Нужно понимать, сколько содержимого репозитория остаётся после фильтрации. Нужно также учитывать накладные расходы промпта, зарезервированные токены на вывод, погрешность токенизатора и то, способен ли дешёвый предварительный проход отыграть часть бюджета обратно. CodeCrucible делает это до основного прохода анализа. Он оценивает накладные расходы промпта по худшему сценарию, пропускает определение фич, если всё ещё помещается в один чанк, пересчитывает накладные расходы, когда определение фич укоротило промпт, и только затем вычисляет бюджет на чанки. Референсную реализацию см. в internal/cli/scan.go:356-555.
Чанкинг
Наша стратегия чанкинга — резать как можно позже и как можно семантичнее. Если репозиторий ещё помещается, не режьте его. Если не помещается — сохраняйте границы файлов и держите связанные файлы вместе. Немного продублируйте общий контекст, если это добавляет ясности, и объедините слишком мелкие чанки, чтобы не оплачивать накладные расходы промпта лишние разы. Чанкинг — запасная стратегия, а не суть подхода.
Именно по этой схеме и работает CodeCrucible. Сначала он разрешает локальные импорты и уходит на быстрый путь с одним чанком, если полностью развёрнутый репозиторий уже помещается. Он оценивает файлы, дублирует несколько небольших высокоприоритетных общих файлов, наращивает чанки по графу локальных импортов, где это возможно, и откатывается к близости по директориям, когда не получается. Затем он объединяет мелкие чанки и переносит краткие сводки связанных файлов, попавших в другие чанки. Важно: он никогда не режет файлы, чтобы уложиться в бюджет. Слишком большой файл просто пропускается с явным предупреждением. Суть не в конкретной функции оценки. Чанкинг должен сохранять связанный код и контекст границ доверия, а не просто удовлетворять потолку по токенам. См. internal/ingest/imports.go:147-170 и internal/chunk/chunker.go:64-398.
Рисунок 2: Чанкер сохраняет быстрый путь с одним чанком, а для более крупных репозиториев группирует код по приоритету, импортам и близости директорий.
Фильтрация и промпты
Фильтруйте слоями, прежде чем принимать архитектурные решения. Бюджет токенов — дефицитный ресурс. Если потратить его на симлинки, бинарники, вендоренные зависимости, метаданные CI, ноутбуки, lock-файлы и тестовую обвязку, сканер будет выглядеть всеобъемлющим, а видеть при этом меньше действительно важного кода. В CodeCrucible фильтрация начинается на обходе файловой системы, который уважает .gitignore и пропускает очевидно неисходное содержимое, а затем продолжается эвристической фильтрацией тестов, документации, вендорных библиотек, малоценных эксплуатационных файлов, явных списков включения и исключения и крупных файлов. Суть не в конкретном списке исключений. Суть в том, что сканер уменьшает репозиторий до того, как решит, нужен ли чанкинг, сколько будет стоить скан и какой формы промпт использовать.
Фильтрация должна ещё и сохранять структуру, а не только убирать байты. Некоторые маленькие файлы важны непропорционально своему размеру, потому что задают границы доверия: файлы схем, контракты API, определения запросов. В CodeCrucible это проявляется как безусловное включение файлов .proto, .sql и GraphQL, даже если они лежат там, где всё остальное было бы отфильтровано, плюс граф локальных импортов и однострочные сводки экспортов, когда анализ всего репозитория перестаёт помещаться. Графы импортов важны здесь потому, что помогают сохранить связанный код, когда репозиторий уже не влезает в один вызов.
Промптам нужна та же дисциплина. Вместо единого универсального промпта разделите промпты по фазам и заставьте каждую фазу отработать свои токены. У CodeCrucible отдельные промпты для определения фич, основного анализа, аудита, углублённого CWE-специфичного ревью и опционального сжатия контекста. На небольших репозиториях промпт анализа может включать все секции. На крупных инструмент сначала прогоняет дешёвый проход определения фич по манифесту и характерным манифестам или точкам входа, а затем включает только те секции, которые соответствуют кодовой базе. Этот лёгкий проход отыгрывает бюджет промпта для дорогого прохода.
Одним из полезных сюрпризов стало то, что сложные промптовые конструкции значили меньше, чем мы ожидали. В недавних экспериментах наши тщательно выстроенные наборы промптов не дали заметного преимущества перед куда более коротким промптом в стиле Carlini CTF, который формулирует задачу как прямой вызов в духе «найди баги». Короткая версия была дешевле и быстрее. Это самостоятельный урок чертежа: сложность промпта — не то же самое, что его качество, и каждая дополнительная секция обязана оправдать свою стоимость в токенах.
В референсной реализации мы оставили эти наборы промптов прямо в репозитории, а не отнеслись к ним как к временным файлам настройки. Исходные подробные промпты лежат в prompts/default/, более короткий набор в стиле Carlini — в prompts/carlini/, а слегка изменённый вариант с упором на доказательство эксплуатируемости — в prompts/exploit-proof/. Хотя все эти наборы поставляются готовыми и работают «из коробки», экспериментировать с промптами дёшево. Для этих целей присмотритесь к benchmrk.
Как только в игру вступает чанкинг, промпты должны компенсировать потерю глобальной видимости. В CodeCrucible это означает: сообщать модели, какой именно чанк она проверяет, показывать ей ограниченный вид на остальной репозиторий, переносить связанный контекст, если так настроено, и прикладывать короткие сводки связанных файлов, попавших в другие чанки. Это практическая работа, необходимая, чтобы сохранить исходную цель дизайна — рассуждение по всему репозиторию — когда репозиторий перерастает один вызов. Фильтрация и промптинг на самом деле одна и та же задача с двух сторон: решить, какие байты будут представлять репозиторий внутри модели.
Любому инструменту этой категории нужен ещё и бюджетный шлюз. Как только стоимость начинает масштабироваться с объёмом токенов, команда «просканируй репозиторий» перестаёт быть безобидной. В CodeCrucible стоимость оценивается после фильтрации и подсчёта токенов, показывается через --dry-run и сверяется с --max-cost до любого вызова модели, причём по умолчанию действует консервативный порог, пока оператор явно от него не откажется. Это не про архитектуру детектирования, но это часть того, что делает инструмент безопасным в эксплуатации. Референсную реализацию см. в internal/ingest/walker.go:185-307, internal/ingest/filter.go:175-296, internal/cli/scan.go:233-321, internal/cli/scan_analyze.go:283-375 и internal/llm/prompt.go:174-404.
Рисунок 3: Фаза анализа собирает промпты, чинит некорректный JSON, обрабатывает переполнение и постобрабатывает объединённый SARIF.
Рисунок 4: Бюджетный шлюз фильтрует репозиторий, оценивает стоимость скана и запускает определение фич только тогда, когда нужен чанкинг.
Шлюзы качества
Сканер становится полезным только тогда, когда шлюзы качества как минимум так же серьёзны, как сам проход сканирования. Без них вы получите много находок и очень мало доверия. Предсказуемый режим отказа: настроенный на полноту первый проход докладывает об одной и той же проблеме по нескольку раз, описывает вторичные проявления как отдельные дефекты и выдаёт результат в таком разнообразии форматов, что интеграция в pipeline становится мучением. Сканер полезен лишь настолько, насколько люди доверяют ему достаточно, чтобы не выключать.
Дедупликация — первый шлюз, и настоящая проблема здесь — почти-дубликаты. Одна и та же SQL-инъекция может всплыть из нескольких чанков с разными формулировками, а одна и та же отсутствующая проверка авторизации — быть отрапортована по разу на каждый эндпоинт вместо одного раза на тип ресурса. CodeCrucible решает это поэтапно: объединяет вывод по чанкам, убирает точные повторы, затем ключует находки по местоположению и CWE и оставляет экземпляр с наибольшей серьёзностью. Он также отбрасывает малозначимые находки в неисходных файлах и подчищает устаревшие метаданные SARIF (Static Analysis Results Interchange Format). Цель — свернуть симптомы обратно к причинам до того, как результат увидит человек.
Второй шлюз — выделенное ревью-валидация. Одного промпта в духе «пожалуйста, проверь эти находки» обычно слишком мало, чтобы от него был толк. CodeCrucible группирует находки в батчи, переносит вперёд конкретные исходные файлы, которые в них упомянуты, и подмешивает соответствующие CWE-специфичные указания по валидации. Такое разделение ролей принципиально. Первый проход может быть щедрым именно потому, что второй явно нацелен на точность. В промптах по умолчанию аудитору говорится: исходить из того, что первый проход мог переусердствовать, доказать достижимость и отсутствие смягчающих мер, свести дублирующиеся проявления к одной корневой причине и — когда тесты исключены — сначала доказать достижимость в продакшене, а уже потом делать что-либо ещё.
Финальный шлюз — дисциплина вывода. Если результаты недостаточно стабильны, чтобы лечь в SARIF и чисто отобразиться в code scanning, всё остальное в pipeline теряет смысл. CodeCrucible использует строгий структурированный вывод, лестницу починки, когда модель уплывает, явные уведомления об ошибке, когда чанк всё равно не удаётся распарсить, и шаг пересборки SARIF, чтобы серьёзность, фрагменты кода и ссылки на CWE оставались согласованными с находками, пережившими ревью. Шлюзы качества должны придать выводу форму, которой можно доверять в продакшене. Иначе сканер производит текст, а текст — самая простая часть задачи. См. internal/sarif/merge.go:5-167, internal/sarif/postprocess.go:35-181, internal/cli/scan_audit.go:58-493, internal/cli/scan_analyze.go:192-280 и internal/llm/schema.go:8-282.
Рисунок 5: Фаза аудита батчит дедуплицированные находки, применяет CWE-специфичное ревью и пересобирает итоговый SARIF.
Что он нашёл: разбор случая Copy Fail
Мы находили с помощью CodeCrucible и внутренние баги, но такие примеры не годятся как учебные случаи: читатель не сможет их воспроизвести без доступа к коду наших приложений. Недавний баг Copy Fail (CVE-2026-31431) в криптоподсистеме Linux дал нам публичный тестовый пример высокоимпактной уязвимости, найденной с помощью ИИ.
Первый заход: провал на Copy Fail
Для начала мы применили самый прямолинейный метод — до того, как добавлять рассуждения из опубликованного описания процесса поиска уязвимости.
Мы склонировали коммит Linux, предшествующий патчу, настроили CodeCrucible на GPT 5.5 Cyber и Claude Opus 4.7 и написали промпты, специфичные для C. Затем нацелили его на директорию crypto и запустили скан.
После нескольких попыток мы пришли к выводу, что наша обвязка не способна обнаружить эту уязвимость самостоятельно.
Значит ли это, что тест на этом закончился?
Повторный заход: успех на Copy Fail
Не совсем. Первая попытка не следовала методу, который использовали исследователи Xint. В своём блоге они описали операторский промпт, дававший модели «направление»:
>
Мы воспроизвели тест с тем же направляющим промптом, что и команда Xint, и добавили указания по эксплуатации ядра.
./codecrucible scan ../linux/crypto --provider databricks --model gpt-5.5-cyber --output results.sarif --custom-requirements "This is the linux crypto/ subsystem. Please examine all codepaths reachable from userspace syscalls. Note one key observation: splice() can deliver page-cache references of read-only files (including setuid binaries) to crypto TX scatterlists." --prompts-dir prompts/exploit-proof-c-kernel
Семь минут и $9 расходов на токены спустя мы получили SARIF-файл со следующей находкой в самом верху результатов (отредактировано для читаемости).
Finding: AF_ALG принимает страницы, доступные только для чтения, в TX scatterlist без проверки возможности записи Summary: | В пути MSG_SPLICE_PAGES функции af_alg_sendmsg() страницы из контролируемого атакующим msg->msg_iter передаются напрямую в scatterlist через extract_iter_to_sg() (af_alg.c:1058-1059). Единственная последующая обработка — инкремент счётчика ссылок get_page() для каждой извлечённой страницы (af_alg.c:1065-1066). Не выполняется никакой проверки того, что страницы доступны для записи, анонимны, приватны или иным образом безопасны для дальнейшего использования в криптографических scatterlist. Отсутствуют проверки, которые разрывали бы COW, требовали FOLL_WRITE, отклоняли страницы, подкреплённые page cache, или копировали данные в приватные страницы, принадлежащие ядру, до того, как страницы будут удержаны в ctx->tsgl_list. Ревью также выявило путь распространения через af_alg_pull_tsgl() (af_alg.c:575-580), который может переназначать эти удержанные TX-страницы в предоставленный вызывающей стороной scatterlist с помощью get_page(page) и sg_set_page(...). Это показывает, что страницы, полученные через splice, могут распространяться из пути отправки AF_ALG в нижележащие scatterlist криптографических запросов. Таким образом, рассмотренный код подтверждает дефект на стороне источника: AF_ALG принимает страницы MSG_SPLICE_PAGES и удерживает их без проверки того, доступны ли они для записи и пригодны ли для последующего использования в качестве криптографических буферов. Однако итоговое утверждение об эксплуатации — что криптографические операции могут затем писать в страницы page cache, доступные только для чтения, и изменять содержимое файлового кеша, — не может быть подтверждено только по этому исходному файлу. Для проверки потребовался бы анализ пути приёма AF_ALG skcipher и нижележащих реализаций криптографии, которые выходят за рамки рассмотренного кода. В результате примитив распространения страниц подтверждён, а последующее влияние в виде повреждения доступного для записи page cache остаётся непроверенным. Confidence: 78% Location: af_alg.c:1058-1066
Полную цепочку эксплуатации он не собрал: пропустил эксплуатируемый sink и примитив четырёхбайтовой записи. И тем не менее это ровно та находка, которая способна вывести опытного исследователя безопасности или agentic-pipeline к полной последовательности атаки.
Ограничения подхода
Первое ограничение: рассуждение по всему репозиторию деградирует, как только репозиторий перестаёт комфортно помещаться в один вызов. С этого момента CodeCrucible вынужден опираться на определение фич, чанкинг, эвристики по графу импортов и межчанковые сводки, чтобы сохранить достаточно связанного кода для рассуждений модели. Это работает лучше наивного чанкинга, но всё равно остаётся эвристическим заменителем настоящего глобального контекста, и для одних форм репозиториев и языков он работает лучше, чем для других.
Второе ограничение: охват намеренно формируется фильтрацией. Мы уважаем .gitignore, по умолчанию пропускаем тесты и документацию, исключаем вендорные SDK и малоценные эксплуатационные файлы, а слишком большие файлы отбрасываем целиком, вместо того чтобы нарезать их под бюджет. Это удерживает стоимость под контролем и повышает средний уровень сигнала, но означает и то, что CodeCrucible не пытается быть буквальной моделью всего дерева исходников. Значимый для безопасности контекст всё ещё может прятаться в исключённой сборочной обвязке, сгенерированном коде или крупных файлах, которые не окупили бы бюджет токенов.
Третье ограничение: pipeline не итеративен. У основного прохода анализа есть ровно одна попытка выдвинуть кандидатов, а фаза аудита ограничена разбором этих находок батчами и только теми файлами, которые в них уже упомянуты. Это делает систему дешевле, быстрее и проще в эксплуатации, но также означает, что полнота ограничена тем, что заметил первый проход, а точность — тем, что способен доказать аудит в пределах батча.
Четвёртое: нынешний инструмент по-прежнему сосредоточен в основном на коде. Даже при наличии отдельного прохода аудита он не проверяет runtime- и окружающие механизмы контроля, если вы явно не подадите ему этот контекст. На практике это значит, что часть находок верна на уровне кода, но неэксплуатируема в развёрнутой системе, потому что смягчающая мера живёт в другом месте. Если вы адаптируете этот паттерн под своё окружение, самое перспективное место для повышения точности — вероятно, не более изощрённый промпт для поиска, а более качественный контекст на этапе аудита из тех систем, которые эти механизмы контроля реально применяют.
Наконец, подход ограничен теми же рамками, что и все прочие ИИ-центричные сканеры в сравнении с их детерминированными аналогами. А именно: нет встроенного способа многократно перезапускать сканы в масштабе и нет решения для дедупликации находок между сканами — тем более что сканер каждый раз формулирует одну и ту же находку по-разному. Мы считаем эти проблемы общими для всего класса таких сканеров и планируем рассказать, как мы к ним подступаемся, в будущих статьях.
Заключение
Заставить сканер отработать один раз проще, чем встроить его в реальный процесс устранения уязвимостей. Сканер, который не подходит вашему окружению, останется трудным во внедрении, как бы впечатляюще ни выглядели его находки на benchmark. Именно поэтому мы публикуем компромиссы, подход и референсную кодовую базу, а не преподносим сам бинарник как главную ценность.
Инструментарий ИИ-безопасности находится в той фазе, когда множество команд приватно строят близкие варианты одного и того же, и самые значимые различия лежат в плоскости эксплуатации: скорость ваших итераций, то, насколько хорошо вы настраиваете промпты и фильтры под свою кодовую базу, насколько безопасно вы ограничиваете расходы и насколько плотно вывод ложится в ваш процесс устранения уязвимостей.
Мы верим, что публикация чертежа поднимает нижнюю планку, никому не опуская верхнюю.
Вот кодовая база, вот аргументация, а вот и шероховатости, на которые мы наткнулись, пока заставляли это работать. Берите то, что подходит вашему стеку. Версия, которую стоит построить вам, не будет в точности похожа на нашу — и в этом весь смысл.
Подпишитесь на канал и каждый день читайте лучшие материалы про AI переведенные на русский!
Нашли интересную статью для перевода? Пришлите нашему боту: @ailongreadsbot