posted on 2026-04-19

В эти выходные решал проблемку с отвалившейся named-readtables на UltraLisp.
Named-readtables библиотека довольно много где используется, и то что она стала недоступна - большая проблема.
Дебажить пришлось долго, и вот что оказалось.
Звёзды так сошлись, что:
• Ultralisp выкидывает из диста проект при ошибках проверки очередного коммита (это стоит починить • Gábor Melis намутил в своих либах циклическую зависимость, когда mgl-pax зависит от свежей версии dref и наоборот и попытался это решить с помощью либы autoload. • Процесс, проверяющий проекты в Ultralisp сам по себе зависел от старой версии mgl-pax, которая была притянута как транзитивная зависимость • Новый mgl-pax конфиликтовал со старым и не мог просто подгрузиться в образ где уже была старая версия. • Из-за этого не мог провериться dref которому нужна новая версия mgl-pax. • То есть, эти три либы mgl-pax, dref и autoload надо было обновлять все разом, а Ultralisp так не умеет.
В итоге, чтобы обновить эту пачку либ пришлось пересобрать сам Ultralisp так, чтобы там была вкомпилирована свежая версия mgl-pax. Теперь всё заработало.
Вывод - надо уменьшать количество зависимостей в бинаре, который чекает загрузку других библиотек. В идеале - до нуля!
posted on 2026-04-18
Последнее время я вернулся к работе над своим проектом кодового ассистента. Идея в том, чтобы сделать такого ассистента, которого можно расширять и интерактивно отлаживать в процессе его работы. Конечно, я его делаю на CommonLisp, но многие вещи делаю с помощью другого кодового ассистента - OpenCode. Мой проект, кстати, называется Codabrus. Если интересно, подписывайтесь на обновление этого проекта на GitHub, ставьте ему звездочки, шерьте с друзьями.
Так вот, для разработки я сейчас использую OpenCode, и у него есть один инструмент, позволяющий сделать eval внутри работающего Lisp процесса. А в качестве модели я использую подписку на GLM 5.1. В целом, GLM неплохо справляется с разработкой на CommonLisp, лишь чуть хуже, чем Claude Sonnet, я бы сказал.
Единственное, с чем у него частенько возникают проблемы, это со скобками. Тут он часто косячит и потом не может правильно выставить скобки. Ну, есть и другие проблемки, которые там, тут, здесь всплывают. Но, что удивительно, есть один трюк, который почти все эти проблемы может полечить, если его планомерно использовать.
Этот трюк я подсмотрел на стриме у одного из коммон-лиспера - Николая Матюшева. Заключается трюк в том, что модель нужно научить учиться. Да, многие модели у себя в памяти как бы и так хранят какие-то знания о вашем проекте, окружении и прочем, но я предпочитаю сохранять знания явно.
Как это делается? Вы просто в промпте внутри файлика AGENT.md просите модель выписывать те важные уроки, которые она получила в ходе работы над каждой сессией. И модель выписывает эти уроки в файлик lessons-learned.md. Так, например, когда у меня случилась проблема с тем, что модель никак не могла расставить скобочки в коде, я попросил ее разобраться, в чем была проблема и придумать решение на будущее так, чтобы этой проблемы больше не возникало.
Модель проанализировала всю историю текущей сессии и выписала несколько важных пунктов. Таких, например, как она поняла, что если функция слишком большая и имеет большой уровень вложности, то модели сложнее правильно расставить скобки. И значит функцию надо делать короче и поменьше. Кроме того, она взяла и написала для себя кусочек кода, который позволяет ей валидировать открывающиеся и закрывающие скобки. Все это моделька сохранила в файлик lessons-learned.md. И после этого я не замечал, чтобы она зацикливалась, пытаясь правильно поставить скобки в коде.
Так что, приём очень полезный. Единственное, чего я опасаюсь, это того, что этот файлик lessons-learned будет разрастаться. Кроме того, не очень понятно, что делать с шерингом этих знаний, потому что многие лайфхаки, которые модель для себя выписывает, были бы полезны и в других проектах со схожим стэком. А значит, шерить знания как-то надо. Таскать из проектов проект файлики lessons-learned.md не очень хорошо, потому что тогда будет сложно эти знания обновлять.
И вот я думаю, что в своем кодовом ассистенте Codabrus я, наверное, придумаю какую-то структурную память, которая позволит шерить такие знания между проектами, над которыми работает кодовый ассистент. А может быть, даже и шерить их куда-то наружу в виде лайфхаков для других AI-ассистентов.
posted on 2026-04-02

Некоторое время назад один из коллег опубликовал свою поделку, которая показывает, сколько осталось квоты на использование нейросетей. Она была написана на Python с отдельной библиотекой для встраивания в тулбар на OS X. Я подумал, что это хороший проектик, чтобы попробовать переписать его на Common Lisp с помощью нейросетей.
► Как нейросеть писала код
У меня были исходники коллеги на Python. Я дал LLM простой запрос — переписать программу на Common Lisp, используя существующие библиотеки, найденные через ql:system-apropos. В качестве ассистента использовал Claude Code + модель Opus 4.6.
Нейронка написала код. В процессе она даже нашла библиотеку для работы с Objective-C, потому что для отображения пунктов меню и иконки в macOS нужно использовать фреймворк на Objective-C. Нейронка оценила библиотеку, решила, что она недостаточно хороша, отказалась от неё и написала свою обёртку над Objective-C через CFFI.
► Отладка и запуск
Конечно, с первого раза программа не заработала. Я сделал несколько итераций. К сожалению, такие графические программы неудобно запускать через мой MCP-сервер для разработки на Common Lisp, потому что при неправильном создании биндингов программа крэшится и роняет вместе с собой весь MCP-сервер. Пришлось запускать программу вручную и скармливать тексты ошибок ассистенту.
После нескольких итераций ассистент допилил программу до состояния, когда она начала запускаться и работать достаточно стабильно. Теперь у меня есть полный аналог той программы, которую написал коллега, но на Common Lisp. Её очень удобно расширять, добавляя новый функционал прямо через REPL.
► Что дальше
Но интереснее другое. Раньше у меня была похожая программка для встраивания в тулбар macOS под названием Barista. Она была написана на LispWorks с его фреймворком CAPI. Там было ограничение: при распространении программы через компиляцию в LispWorks отключаются возможности компилятора, то есть нельзя подгружать динамические плагины.
Теперь я могу переписать Barista так, чтобы использовался SBCL, и там не будет таких ограничений. Программа станет полностью расширяемой с помощью Lisp-скриптов.
posted on 2026-03-14
Смотрите чего я навайбкодил: https://github.com/40ants/microgpt
Это порт на Common Lisp скрипта microgpt, который недавно опубликовал Andrej Karpathy.
Эта штука включает в себя код трансформера и инференс. То есть она может обучиться на каких-то входных текстах, а потом генерировать похожие тексты. Всё как у больших LLM, только буквально в одном Python-скрипте. Ну и, конечно, эта штука больше создана для обучения, а не для того, чтобы показывать хорошую производительность.
В этом примере она учится на корпусе русских имен и может генерить новые, похожие по написанию:
% ./microgpt.py
num docs: 484
vocab size: 57
num params: 5152
step 1000 / 1000 | loss 2.3474
--- inference (new, hallucinated names) ---
sample 1: Небромир
sample 2: Миловета
sample 3: Милана
sample 4: Свеладр
sample 5: Милана
sample 6: Ратевоба
sample 7: Миловисла
sample 8: Крана
sample 9: Бородосл
./microgpt.py 54.06s user 0.82s system 99% cpu 55.011 total
Я подумал, что это хороший пример, чтобы попробовать, как LLM справится с переписыванием этого кода на Common Lisp.
Промпт для переписывания был очень простой. Буквально я сказал LLM: "Вот тебе код на Python, сделай мне то же самое, но на Common Lisp, для загрузки датасета используй либу Dexador". При этом я использовал в качестве агента Claude Code и нейросеть Claude Sonnet 4.6.
Что меня удивило - то что нейросеть сама создала ASDF систему, а так же решила декомпозировать код на модули, а не склеила всё в один большой скрипт.
Первоначальная версия которая получилась, работала аналогично питоновской, но в 5 раз быстрее:
% time roswell/microgpt.ros
num docs: 484
vocab size: 57
num params: 5152
step 1000 / 1000 | loss 1.9185
--- inference (new, hallucinated names) ---
sample 1: Велослав
sample 2: Бореслав
sample 3: Любра
sample 4: Влавослав
sample 5: Добран
sample 6: Любегост
sample 7: Светисл
sample 8: Вирослав
sample 9: Зослав
roswell/microgpt.ros 9.41s user 0.61s system 99% cpu 10.038 total
Дальше я просил LLM проанализировать что можно сделать чтобы повысить производительность и в итоге было сделано следующее:
*CLOS классы педеланы на структуры: * ```
roswell/microgpt.ros 6.00s user 0.47s system 99% cpu 6.489 total
То есть, после этого программа стала **быстрее python** оригинала **почти в 10 раз**.
А вот после объявления ftype и inline для некоторых функций, производительность улучшилась незначительно:
roswell/microgpt.ros 5.70s user 0.51s system 99% cpu 6.232 total
```
У меня не было цели упарываться в оптимизацию, но думаю можно выжать ещё больше скорости если захотеть. Основной темой эксперимеынта было - проверить, как LLM справится с подобным проектом. Ведь иногда так бывает, что для Common Lisp какой-то библиотеки нет, но она есть для другого языка. Переписывать вручную - занятие грустное, но если можно сделать это автоматически с помощью LLM и сэкономить себе много часов работы, то почему нет?
posted on 2026-03-04

По мотивам предыдущего поста про готовность OpenAI убивать людей за пределами США мне стало интересно в каких областях можно применять современный ИИ в войне. Вот с десяток идей который без проблем сгенерила наша отечественная Алиса.
*1. Анализ разведданных в реальном времени * LLM обрабатывают тонны информации: соцсети, новости, перехваченные сообщения, данные с дронов. Вытаскивают главное, строят хронологии и готовят сводки — вместо недель за часы.
*2. Динамические сценарии для учений * Больше никаких статичных планов. LLM генерируют тактику условного противника на лету: учитывают местность, погоду, ресурсы — тренировки становятся максимально реалистичными.
*3. Мгновенный перевод и анализ * Перехватили радиопереговоры? LLM переведёт, выделит ключевые данные (координаты, имена) и даже определит настроение говорящего — всё за секунды.
*4. Помощник командира * В бою каждая секунда на счету. LLM анализирует обстановку и предлагает варианты действий с прогнозом исхода, а ещё рассчитывает логистику: сколько нужно боеприпасов, топлива, медикаментов.
*5. Дезинформация и её обнаружение * LLM умеют и распространять фейки (для влияния на противника), и ловить их: ищут признаки пропаганды в потоках информации, выявляют скоординированные кампании.
*7. Виртуальные инструкторы * LLM заменяют наставников: отвечают на вопросы по технике, уставам и тактике, проводят тесты, тренируют переговоры и допросы в симуляциях.
*8. Прогноз поломок техники * Датчики, журналы техобслуживания, отчёты механиков — LLM анализируют всё это и предсказывают, когда что‑то сломается. Можно починить заранее, а не в разгар операции.
*9. Управление дронами и роботами * Группы дронов или наземных роботов действуют слаженно: LLM координирует разведку, поиск целей, доставку грузов и адаптируется к изменениям без постоянного контроля оператора.
*10. Кибербезопасность * LLM сканируют сети на аномалии, выявляют попытки взлома, генерируют сложные шифры и тренируют специалистов через имитацию атак.
Довольно много всего, но почти везде у LLM лишь вспомогательная роль – координация или обработка информации.
В любом случае, надеюсь, вояки не читают мой блог, а то ещё возьмут что-нибудь из этого на вооружение :(
posted on 2026-03-04

Кто-нибудь в курсе, что это за шумиха и почему даже некоторые известные личности отказываются от подписок OpenAI?
Например, автор книг про Lisp и AI Mark Watson написал отдельный пост о том, что удаляет свой профиль OpenAI. Более того, он удаляет все упоминания об использовании их API из своих книг и будет публиковать обновленные версии, в которых не будет ничего про OpenAI.
Что вообще происходит?
posted on 2026-02-25

На днях ввечеру буквально пару часов времени потерял на пустом месте, пытаясь отдебажить кейс, в котором у меня определение функции не появлялось в пакете после перекомпиляции приложения.
При этом при загрузке модуля с помощью asdf:load-system компилятор выдает только одно предупреждение о том, что функция не определена, но она где-то используется.
В конце концов нашел проблему – иллюстрация на картинке выше. И эта проблема была принесена LLM.
В чем же было дело? Дело в том, что, внося очередные изменения, LLM у одного определения функции убрала закрывающую скобку, а у другого определения функции добавила лишнюю закрывающую скобку. В итоге весь код, который был между первым и вторым определением функции, оказался внутри тела первой функции.
Если коротко проиллюстрировать это, можно сделать это таким кодом:
(defun foo ()
(loop repeat 3
do (format t "Iterating"))
(defun blah ()
(format t "Blah called"))
(defun bar ()
(blah)))
На первый взгляд тут все ок. Проблема еще состоит в том, что настоящий LISPR скобки не считает. Мы судим о структуре кода по отступам, и здесь на первый взгляд все хорошо.
Проблема в глаза не бросается, потому что LLM не заботится о том, чтобы сохранять отступы так, как это делает нормальный LISP редактор.
Если отформатировать этот код в соответствии с правилами форматирования LISP, проблема станет очевидна. Вот во что он превратится:
(defun foo ()
(loop repeat 3
do (format t "Iterating"))
(defun blah ()
(format t "Blah called"))
(defun bar ()
(blah)))
Внезапно сразу оказывается, что определения функций blah и bar попали внутрь определения функции foo. И компилятору Common Lisp это нормально. Никаких ошибок он не выдаёт. Такие дела :(
posted on 2026-01-28

На своем ноутбуке с Mac OS я помимо Homebrew использую Nix для того, чтобы ставить пакеты. Почему я когда-то заинтересовался Nix? Так это потому, что в нем интересная концепция поколений, когда ты можешь безопасно попробовать какую-нибудь программу, а потом откатить все изменения в системе, которые были сделаны при установке этой программы.
Более того, Nix даже задуман так, что в нем есть специальный способ установить программу, не меняя системного окружения вообще. То есть ты запускаешь специальный Shell, в котором какая-нибудь программа доступна, а при выходе из этого Shell она исчезает. Ну и вообще концепция Nix очень похожа на блокчейн. То есть там каждый пакет зависит от других пакетов.
И прикольно то, что одновременно в системе могут быть библиотеки и программы, использующие разные версии библиотек. Например, какой-нибудь Postgres, который использует Lipsy одной версии, не знаю, ffmpeg, которому нужно Lipsy другой версии. И они спокойно могут сосуществовать, потому что Nix собирает их окружение с помощью симлинков.
Так вот, недавно я нашел прекрасную серию статей про то, как устроен Nix, его язык описания пакетов и все такое прочее. Дело в том, что раньше я пользовался Nix на самом базовом уровне - просто устанавливал пакеты и пользовался ими. А тут мне попалась эта серия статей, и я решил разобраться подробнее, как же там внутри все устроено, и может быть, мне тоже нужно описывать свои пакеты, модули Nix, так, чтобы пользователи Nix могли устанавливать Common Lisp библиотеки прямо из https://ultralisp.org. А это, согласитесь, было бы прикольно - сделать так, чтобы UltraLisp имел свой Nix Channel и библиотеки для можно было бы ставить без использования QuickLisp.
Ну так вот, начал я читать эту серию статей, и там дошел сейчас до момента, где описывается язык, на котором декларируется описание пакетов. Это полноценный язык программирования, но, господи, за что? Там такой синтаксис наркоманский! Он, с одной стороны, иногда выглядит прикольно, но, с другой стороны, иногда думаешь, блин, что, что курили создатели этого языка!?
Вот, например, на скриншоте к этому посту показано содержимое одного файла, и в нем код который определяет функцию. И это очень не похоже на другие языки программирования . Более того, ниже под содержимым файла показан REPL, где происходит вообще странное - мы как бы делаем импорт, и одновременно вызываем функцию в той же строке! То есть импорт возвращает нам объект функции, в которой сразу применяются аргументы, и получается какой-то результат. Ну согласитесь, это какое-то наркоманство!
Не знаю, буду пока дальше изучать, может быть, в этом есть какой-то более глубинный смысл, но пока что выглядит странновато. Если у вас есть опыт использования NIX, и вы познали все его тайны, или наоборот хотели бы копнуть глубже - пишите в комментариях, буду рад пообщаться.
posted on 2026-01-01
Итак, рассказываю, как проходил полуфинал MTS TrueTech в 2025 году. Полуфинал заключался в том, что нам нужно было написать софт, который бы управлял реальным роботом так, чтобы робот мог пройти лабиринт из одного угла в противоположный. При этом структуру лабиринта, по которому будут запускать робота, мы не знали.
► Про херовую организацию
Организаторы запускали роботов в закрытой комнате, откуда не было трансляции. Но была комната для подготовки роботов, и мы могли видеть, что происходит в этой комнате через камеру на потолке, и камеры роботов. Проблема, однако, заключалась в том, что роботов, к которым нам дали доступ, было много. Поэтому в лабиринт их все не запускали разом, а в каждый день подготовки выбирался какой-нибудь один робот, который мог ездить по лабиринту. Другие роботы стояли на подставочках, и всё, что можно было с ними сделать, это подключиться по SSH и разобраться с тем, как работают реальные датчики.
Доступ к тому роботу, который катался по лабиринту, выдавался по записи. Записаться можно было всего на один час в день. При этом организация процесса была настолько хреновая, насколько это только возможно придумать. Например, ты мог потерять 30-40 минут просто потому, что какие-то проблемы с паролем или какие-то проблемы с сетью. VPN не подключался к сети, откуда можно было попасть на робота и так далее. Кроме того, слоты иногда задерживались. Отлаживать софт можно было и ночью, но ночью все роботы стояли на подставочках а так много не натестируешь.
Также хреново было организовано и тестирование робота в реальном лабиринте. Каждой команде дали по три попытки запустить робота по лабиринту. Причем мы не видели, что происходит. Максимум, на что можно было рассчитывать, это голосовые комментарии от организаторов, где они говорили: "Ваш робот застрял в углу, крутит колесами".
Естественно, слоты для записи на реальный прогон робота по лабиринту тоже выдавались по записи. Подключение к твоему слоту происходило с задержками, причем порой огромными, вплоть до четырех часов. И это было отвратительно.
► Жесть с железом
Еще одна из проблем, которая отчасти тоже организационная, заключается в том, что сами роботы были готовы не полностью. В первые дни, когда нам дали доступ, на некоторых роботах не хватало некоторых датчиков.
Кроме того, в процессе выяснилось, что роботам не хватает питания. Например, на них были установлены лидары и камеры глубины. И если включали камеру глубины, то отваливался лидар. Если начинали пользоваться лидаром, отваливалась камера глубины. И эту проблему, кстати, так до конца не решили. Поэтому некоторые команды пользовались чем-то одним, либо лидаром, либо камерой глубины.
► Итог
Прикольно было попробовать Common Lisp для управления роботом. Это реально круто - иметь REPL через который можно управлять колесами, смотреть состояние робота и тд.
Самое досадное то, что физика движения реального робота оказалась сильно отличается от его движений в виртуальном лабиринте, который нам давали в заданиях до полуфинала. И я понял, как исправить это, только за один день до конца приёма сдачи полуфинального лабиринта. Так что получалось, что я отлаживал движение робота в симуляторе не с той физикой, которая близка к реальному миру.
Из-за этого мой робот в реальности двигался совсем иначе. Если бы не этот факт, если бы я узнал об этом хотя бы за неделю до окончания полуфинала, то шансы на то, чтобы попасть в финал, были бы гораздо выше.
Что порадовало меня в этом состязании, так это то, что всё-таки удалось погонять свой софт на реальном роботе, который катается в реальном физическом мире. Это было прикольно! Теперь хочу себе такого же, чтобы можно было потестировать его у себя дома.
► Другие части
posted on 2025-12-09
В двух предыдущих постах я описывал, как решал первую задачу из отборочного этапа хакатона МТС True Tech Champ.
Всего в отборочном этапе было три задачи. Сегодня расскажу о второй. В ней нужно было провести робота по лабиринту. Причем робот должен был дойти от угла лабиринта до его центра. Раз так, значит банальное правило левой или правой руки могло и не помочь, потому что с ним робот мог ходить по внешней стене до бесконечности.
► Визуализация лабиринта
Первое, что я сделал, это создал себе программу для визуализации данных, как их видит робот. Напомню, что в этом состязании у робота нет знания о структуре лабиринта. Всё, что он знает - это то, что видит перед собой с помощью лидара. Лидар это лазер, который вращается и сообщает роботу дистанцию до препятствий, находящихся под разным углом. Так вот, с помощью библиотеки RayLib я сделал визуализацию, которая отображает известную роботу часть лабиринта. И по ходу движения робот этот лабиринт дорисовывает, ощупывая стены лучами лидара.
Забегая немножечко вперед, скажу, что сделать такую карту было достаточно просто, но только за счет того, что положение потенциальных стен было известно - лабиринт имел регулярную структуру с одинаковой шириной коридоров. Как это выглядит в построении лабиринта, вы можете видеть на видео. Кроме того, я разбил все поле, в котором работает робот, на клетки и написал алгоритм таким образом, чтобы робот понимал, в какой клетке он сейчас находится.
► Коррекция положения
За счет того, что робот видит стены с помощью лидара и понимает, что стена должна быть закреплена на определенной сетке, он может корректировать свое положение. Ведь мы в этом состязании получаем координаты робота неточные. Они рассчитаны на основе вращения колес. Эти координаты постепенно накапливают ошибку. Поэтому я сделал так, чтобы робот, когда движется по горизонтали или по вертикали, доезжая до стены, корректировал свое положение, зная, где точно эта стена должна располагаться на сетке лабиринта и зная расстояние до нее по лидару.
Поэтому на визуализации видно два силуэта робота. Чем дальше к концу, тем больше они сдвигаются относительно друг друга. Это происходит потому, что белый силуэт – это положение робота, каким бы оно было только по координатам, вычисленным по вращению колес, а розовое – это положение робота скорректированное.
► Прокладка пути
Чтобы дойти до центра лабиринта, как я уже сказал, нельзя просто полагаться на правила левой или правой руки. Нужен более хитрый алгоритм. Я выбрал алгоритм заполнения лабиринта - flood fill.
В чем он заключается? Для каждой точки лабиринта мы рассчитываем вес, который обозначает количество шагов, минимальное количество шагов, за которое можно дойти до цели (в нашем случае до центра). И пока стены этому не препятствуют, лабиринт закрашен равномерным градиентом. B вес клетки зависит от веса соседних клеток. Получается, что вес любой клетки равен минимальному весу любой соседней клетки плюс один. И таким образом закрашен весь лабиринт. У клеток в центре вес, соответственно, ноль, потому что это цель.
По мере того, как робот обнаруживает стены в лабиринте, он пересчитывает веса клеток, прилежащих к новой стене. Веса таким образом могут только увеличиваться. Вес клеточек я визуализировал цветом. Чем клетка темнее, тем больше у нее вес, и значит, тем больше путь нужно пройти роботу, чтобы дойти до центра в этом направлении. На видео хорошо видно, как прокрашиватеся темным длинный коридор, как только робот обнаруживает тупик.
Алгоритм следования по маршруту получился очень простой - робот выбирает то направление, в котором лежит клетка с наименьшим весом. Что получилось, вы видите на видео. Робот достаточно быстро проехал лабиринт. Я думаю, можно было бы даже поработать еще над скоростью и прохождением поворотов без торможения, но времени, к сожалению, уже не оставалось.
В следующем посте расскажу про то, как проходил полуфинал. Там все было уже гораздо сложнее.