ИИ-агент на 100 строк Lisp

Разработчик Джейми Бич вспоминает, как около 2000 года изучал Lisp на курсе по ИИ в Университете Гвельфа (Канада), тогда, по словам его преподавателя, это был «язык символического ИИ»: экспертные системы, доказательство теорем, программы, манипулирующие символами и правилами. Ни нейронных сетей, ни трансформеров, ни CUDA, ни PyTorch тогда попросту не существовало. Позже статистические методы, а затем глубокое обучение вытеснили символический ИИ, и Lisp, по наблюдению автора, оказался забыт вместе с ним. Спустя 25 с лишним лет, уже больше месяца строя собственную платформу ИИ-агентов Agent Foundry, автор задался вопросом: не пригодится ли Lisp именно для цикла работы агента (agent loop)? С помощью Claude он собрал рабочего агента на SBCL (реализация Common Lisp), около 100 строк кода, всего две библиотеки (dexador для HTTP-запросов и shasht для JSON), обращения к модели идут через OpenRouter.
Ядро агента, рекурсивная функция agent-loop длиной 8 строк: список сообщений отправляется модели; если модель запрашивает вызов инструмента, инструмент выполняется, результат добавляется к истории, и функция вызывает сама себя с обновлённым списком сообщений; если модель просто отвечает текстом, рекурсия останавливается и возвращается полная история. Никакого отдельного цикла с состоянием или фреймворка, состояние агента, это просто аргумент, который «прокручивается» через рекурсию с каждым новым вызовом.
Единственный инструмент, который получил агент, eval. Lisp гомоиконичен: программа на нём записана в виде списков, той же структуры данных, которой Lisp оперирует, то есть код и данные неразличимы. Вместо того чтобы городить каталог отдельных инструментов, автор просто дал модели сам язык: функция lisp-eval принимает от модели строку с Lisp-кодом, выполняет её через eval и возвращает результат (либо текст ошибки). Проверяя это, автор попросил агента не «вспомнить», а вычислить 30-е число Фибоначчи, агент сам написал рекурсивную функцию fibonacci, вызвал её и получил верный ответ: 832040. Автор прямо предупреждает: инструмент eval означает, что модель выполняет на машине произвольный код, поэтому это лишь игрушка для изолированной песочницы, сам он запускал агента исключительно в локальном Docker-контейнере.
Память агента реализована всего в ~20 строк, без схемы и миграций: список сообщений (по сути уже JSON, набор хэш-таблиц) целиком сериализуется в файл функцией remember и читается обратно функцией recall при следующем запуске, так что формат хранения совпадает с рабочим форматом самого агента. Из недостатков, история памяти растёт неограниченно и рано или поздно упрётся в лимит контекстного окна модели; как возможное решение автор называет отдельный шаг «сжатия», вызов модели, суммирующей собственную прошлую историю, но в описанном эксперименте этого не реализует.
Самый показательный эпизод, веб-поиск. Автор просто вставил в переписку с агентом временный API-ключ Brave Search, ничего специально не программируя. Агент сам, через свой единственный инструмент eval, определил функцию brave-search: написал HTTP-запрос к API Brave Search, разбор JSON-ответа, и начал отвечать на вопросы, используя живые результаты поиска. Автор подчёркивает: он сам никогда не создавал инструмент веб-поиска, агент создал его самостоятельно, использовав единственный уже имевшийся у него инструмент для написания второго.
Это переворачивает типичный подход к агентным платформам, где каталог инструментов фиксируется заранее людьми-разработчиками, так устроены и Agent Foundry, и Claude Code, и, по сути, сам протокол MCP, формализующий возможности агента как заранее согласованные контракты. В описанном эксперименте каталог инструментов открыт: модель сама решает, что ей нужно, и создаёт это на лету. Автор отмечает любопытную деталь: функции, которые определяет агент, существуют только в работающем Lisp-образе и исчезают при завершении процесса, но их исходный код остаётся в текстовом файле памяти, поэтому в новой сессии агент, перечитывая собственную историю, заново «воскрешает» функцию через eval; его навыки хранятся как воспоминания о том, что он когда-то их написал.
Вывод автора: символический ИИ (экспертные системы на Lisp) не «ошибся», а «опередил время», сама идея программ, манипулирующих программами (код как данные), оказалась удачным описанием того, чем на самом деле является ИИ-агент. Просто символическое рассуждение теперь выполняет языковая модель, а не жёстко закодированные правила. Рассуждение делают модели, но цикл вокруг них, то, что инженеры проектируют сами, как раз то, в чём Lisp всегда был силён. Полный исходный код и Dockerfile выложены на GitHub: github.com/jamiebeach/lisp-agent.
Ключевые факты
- Разработчик Джейми Бич с помощью Claude построил ИИ-агента на ~100 строк Common Lisp (SBCL + библиотеки dexador и shasht), обращающегося к модели через OpenRouter.
- Весь цикл работы агента (agent loop) уместился в 8 строк рекурсивной функции agent-loop: если модель просит инструмент, он выполняется, и функция рекурсивно вызывает себя с обновлённой историей; если модель просто отвечает, рекурсия останавливается.
- Единственный инструмент агента, eval: благодаря гомоиконности Lisp модель сама пишет и исполняет себе нужный код вместо использования заранее подготовленных инструментов; агент самостоятельно написал функцию Фибоначчи и вычислил её 30-е число, 832040.
- Получив от автора временный API-ключ Brave Search, агент через тот же eval сам написал функцию веб-поиска (HTTP-запрос и разбор JSON-ответа), инструмент, который автор никогда не создавал.
- Память агента занимает ~20 строк: список сообщений просто сериализуется в файл и читается обратно без схемы и миграций, но растёт неограниченно и рано или поздно упрётся в лимит контекстного окна модели.
Почему это важно
Эксперимент показывает, что цикл работы ИИ-агента (agent loop) можно свести к рекурсивной функции в несколько строк, а благодаря гомоиконности Lisp (программа записана в виде тех же структур данных, которыми оперирует) единственным нужным агенту инструментом может быть eval, выполнение кода, который агент пишет сам себе на лету. Это ставит под сомнение индустриальный стандарт, при котором каталог инструментов агента (в Agent Foundry, Claude Code, протоколе MCP) фиксируется заранее людьми-разработчиками: автор демонстрирует агента, который сам решает, какие возможности ему нужны, и создаёт их прямо в разговоре.
Кому это важно
Разработчикам платформ для ИИ-агентов и инженерам, проектирующим системы вызова инструментов (в том числе через протокол MCP); исследователям альтернативных архитектур agent loop; всем, кто помнит Lisp как «язык символического ИИ» и интересуется, как его идеи (код как данные, гомоиконность) неожиданно пригодились в эпоху агентов на основе языковых моделей.
Как это применить
Полный исходный код и Dockerfile выложены автором на GitHub (github.com/jamiebeach/lisp-agent). Стек, SBCL (реализация Common Lisp) и всего две библиотеки: dexador для HTTP-запросов и shasht для работы с JSON; обращение к модели идёт через OpenRouter. Сам цикл агента, 8 строк рекурсивной функции, единственный инструмент, функция, которая принимает от модели строку с Lisp-кодом и исполняет её через eval, память, сериализация списка сообщений в файл и чтение обратно (около 20 строк, без схемы и миграций). Автор прямо предупреждает: eval-инструмент означает, что модель выполняет произвольный код на машине, поэтому годится это только для изолированной песочницы, сам он запускал агента исключительно в локальном Docker-контейнере.
Можно ли доверять
Это личный блог-пост разработчика Джейми Бича о собственном любительском эксперименте, а не рецензируемое исследование или продакшен-система: выводы опираются на единичные демонстрации (агент вычислил число Фибоначчи, сам написал функцию для Brave Search API). Материал набрал 142 балла и 18 комментариев на Hacker News; исходный код полностью открыт на GitHub, что позволяет проверить заявленное самостоятельно.
Риски и подводные камни
Главный риск называет сам автор: инструмент eval означает, что модель может выполнить на машине произвольный, непредсказуемый код, подход годится только для тестовой песочницы, не для продакшена без дополнительной изоляции. Отдельная проблема, память агента: история диалога сохраняется целиком и ничем не ограничена, поэтому рано или поздно упирается в лимит контекстного окна модели; автор лишь предлагает гипотетическое решение (отдельный вызов модели для сжатия своей же истории), но не реализует его в описанном эксперименте.
«Код, это данные, данные, это код, а возможности агента, это просто разговор.»
— Джейми Бич (Jamie Beach), автор блога thebeach.dev, в посте «An agent in 100 lines of Lisp»