CSPL: программа хранится отдельно от способа её исполнения
Редакция от 2 октября 2026 года. У каждого утверждения указано, откуда мы это знаем: [проверено] — мы запускали это сами; если сырые ответы сохранены, утверждение называет файл; [в документации] — это есть в справочнике API платформы, и на это опирается наш работающий код; [со слов] — так говорит автор среды исполнения, а мы это не воспроизводили.
Это перевод английской статьи extella.dev/articles/cspl/. Если версии расходятся, верна английская.
1. Задача и что описывает этот документ
Агент может вызывать инструменты, которые предлагает его MCP-сервер. Если сервер — неизменный набор функций, каждый новый инструмент означает правку этого сервера: изменить код, выложить, перезапустить и повторить то же на каждой машине, где он должен работать. MCP этого не требует: сервер может менять список инструментов на ходу или предлагать универсальный исполнитель. Этот документ описывает один способ добавлять инструменты, не меняя сервер.
В Extella инструмент агента — это сохранённая программа, а не функция в сервере. Модель или человек сохраняет программу через API (сохранить, получить, запустить, найти), операции которого доступны и как инструменты MCP, и запускает её по имени на выбранном устройстве. MCP-сервер и его список инструментов не меняются.
Если инструменты — сохранённые программы, сразу возникают четыре вопроса. CSPL, Container Specific Programming Language, — механизм Extella, который на них отвечает. Это наша собственная разработка, описанная в опубликованной международной заявке WO 2026/102343 (дата приоритета 8 ноября 2024 года). Документ описывает этот механизм и ничего больше: не продукт Extella, не агентов, не обучение моделей.
| Вопрос | Ответ CSPL | Где в документе |
|---|---|---|
| На каком языке написана программа? | На любом языке, для которого на устройстве есть обработчик; запись называет свой язык | разделы 2, 3 и 5 |
| Что её исполняет? | Обработчик этого языка, зарегистрированный на устройстве и заменяемый | разделы 4 и 5 |
| Где она выполняется? | На устройстве, названном в вызове | разделы 3 и 5 |
| Как позволить модели добавлять код? | Безопасно по умолчанию — никак; маленький язык ограничивает, что программа может выразить, а его обработчик может проверить программу до запуска | разделы 4, 5 и 7 |
Механизм в одной строке:
сохранённая программа, которая называет свой язык → вызов, который называет программу и устройство → обработчик этого языка на устройстве → результат.
Сохранённая программа называется экспертом. Его запись хранит исходный текст и, в поле cspl, название языка, на котором текст написан. Обработчик этого языка регистрируется на устройстве — машине, где работает listener Extella. Обработчик можно зарегистрировать или заменить, не переписывая программы на его языке.
Когда это может пригодиться. Когда одна и та же сохранённая программа должна выполняться на любом из нескольких подготовленных устройств, а устройство выбирается в каждом вызове. Или когда много программ пользуются одним вашим маленьким языком, а его обработчиком вы хотите управлять в одном месте.
Когда нет. Функция в вашем собственном MCP-сервере отвечает примерно за миллисекунду, а универсальный исполнитель с хранилищем программ — это около сотни строк кода; вызов через Extella на устройство занял в нашем замере от 6 до 14 секунд (раздел 5). Если этого вам хватает, проще любой из этих двух вариантов; ни один из них мы не запускали на второй машине и не знаем, чего это потребует.
Что это такое и чем это не является. Это способ запускать сохранённые программы через подключаемые обработчики на подготовленных устройствах. Это не универсальная безопасная среда для запуска непроверенного кода, написанного моделью; почему — в разделе 7.
2. Термины
| Термин | Значение |
|---|---|
| CSPL | Механизм: программы хранятся с названием своего языка и исполняются обработчиком этого языка. |
| Язык | Набор допустимых инструкций и правила их записи. Поле cspl эксперта хранит название его языка. |
| Эксперт | Сохранённая программа: name, code (исходный текст), cspl (название языка), параметры по умолчанию, описание. Хранится в аккаунте, в области одного из агентов аккаунта (заголовок X-Agent-Id в разделе 3). |
| Обработчик | Код на устройстве, который исполняет программы на одном языке. Для своего языка это функция на Python. |
| Устройство | Машина, где работает listener Extella и выполнен вход в аккаунт. У неё есть id, её можно указать целью запуска. |
| Регистратор | Эксперт, задача которого — зарегистрировать обработчик на том устройстве, где он запущен. |
3. Запись эксперта и два вызова
Сохранение и запуск идут через HTTP API [в документации и проверено на наших демо-серверах]:
POST https://api.extella.ai/api/expert/save
headers: X-Auth-Token, X-Profile-Id: default, X-Agent-Id: <agent>
body: {"name": "tally_demo", "code": "add 5\nadd 7\nsub 2\n", "cspl": "tally", "kwargs": {}, "description": "..."}
POST https://api.extella.ai/api/expert/run
body: {"name": "tally_demo", "params": {}, "targets": ["<device id>"]}
answer: {"status": "success", "expert_name": "tally_demo", "result": "...", "execution_log": [...]}
Долгий запуск возвращается как {"task_id": ...}, его опрашивают через POST /api/tasks/check. Сохранение под существующим именем перезаписывает запись; версий нет [в документации].
4. Объявление языка: пример, который можно запустить
В примере четыре части: язык из двух инструкций, его обработчик, регистратор и программа. Обработчик ниже — файл в том виде, в каком мы его запускали. Регистратор показан без текста обработчика; скрипт, который его собирает, сохраняет оба эксперта и запускает их, — один файл, run_tiny.py. Оба файла опубликованы рядом с этим документом.
python3 run_tiny.py # needs Extella installed and signed in; Python 3.8+
Обработчик. Он получает исходный текст эксперта и возвращает словарь. Сначала он проверяет всю программу; если хоть одна инструкция не из языка, ничего не исполняется:
def tally(code = "", filtered_source_code = "", func_name = "", **extra):
# Handler of the language "tally": a program is lines "add N" or "sub N", N up to nine digits 0-9.
# The whole program is checked first; if any line is not a tally statement, nothing is executed.
import platform, socket
lines = [line.split() for line in (code or filtered_source_code or "").splitlines() if line.split()]
for n, parts in enumerate(lines, 1):
if len(parts) != 2 or parts[0] not in ("add", "sub") or not (parts[1].isascii() and parts[1].isdigit() and len(parts[1]) <= 9):
return {"status": "rejected", "reason": "statement %d is not a tally statement: %s" % (n, " ".join(parts)[:40])}
total = 0
for op, number in lines:
total += int(number) if op == "add" else -int(number)
return {"status": "success", "language": "tally", "expert": func_name, "total": total, "host": socket.gethostname(), "os": platform.system() + " " + platform.machine()}
Изменено 16 октября 2026 года. Проверка числа теперь принимает только цифры от 0 до 9, не больше девяти. Версия, запущенная 1 октября, проверяла parts[1].isdigit(). Python считает «²» цифрой, поэтому add ² проходил проверку и затем падал в int(), вместо того чтобы получить отказ. Ответы ниже — от 1 октября и той версии (918 байт). 16 октября исправленный обработчик (991 байт) зарегистрировали и запустили на Linux-сервере: та же программа дала 10, import os получил отказ, как и раньше, и add ² тоже получил отказ [проверено 16 октября 2026 года; runs-2026-10-16.json].
Регистратор — эксперт на встроенном языке fython (Python с директивой зависимостей). Запущенный один раз на устройстве, он регистрирует там обработчик под названием языка:
$extens("include.py")
include("import os", [])
def register_tally() -> dict:
import disnet
handler_code = '''...the handler text above, inserted by run_tiny.py...'''
d = disnet.Disnet(url="https://disnet.extella.ai")
envelope = {"filtered_source_code": "", "func_name": "", "args": None, "kwargs": None, "cspl": "tally", "response_format": None}
d.container(code=handler_code, func_name="tally", container_path="tally", args=None, kwargs=envelope, cspl_type="fython", push=False)
return {"status": "success", "registered": "tally", "bytes": len(handler_code)}
Что ответило устройство 1 октября 2026 года на MacBook (Darwin arm64) [проверено; имя машины здесь сокращено, сырые ответы — в файле приложения]:
| Шаг | Ответ устройства | Полный круг |
|---|---|---|
Запуск register_tally | {'status': 'success', 'registered': 'tally', 'bytes': 918} | 13,1 с |
Запуск tally_demo (add 5, add 7, sub 2) | {'status': 'success', 'language': 'tally', 'expert': 'tally_demo', 'total': 10, 'host': '<laptop>', 'os': 'Darwin arm64'} | 11,1 с |
Запуск эксперта, у которого вторая строка — import os | {'status': 'rejected', 'reason': 'statement 2 is not a tally statement: import os'} | 11,1 с |
Полный круг — всё время одного вызова, как его видит вызывающий. В более раннем проходе в тот же день те же шаги заняли от 7 до 9 с. Эти запуски не были замером скорости; замер от 2 октября — в разделе 5.
5. Что мы показали
Добавление программы не меняет сервер. Мы добавили одни и те же десять маленьких функций на Python тремя способами: как неизменные инструменты MCP-сервера (официальный Python SDK), через универсальный исполнитель с хранилищем программ, который мы написали примерно в сотню строк, и через MCP-сервер Extella. Неизменные инструменты: 10 правок и 10 перезапусков, потому что так устроен этот базовый вариант; MCP этого не требует. Универсальный исполнитель и Extella: 0 правок и 0 перезапусков. Новая функция, сохранённая и запущенная впервые, заняла 0,36 с с неизменными инструментами (включая перезапуск), 0,026 с с универсальным исполнителем и от 9 до 12 с с Extella (медиана около 10 с по десяти функциям при уже открытой сессии; в этом прогоне каждое сохранение перезаписывало программу с тем же именем, поэтому первое создание программы не замерялось; два более ранних прогона в тот же день дали медианы около 11 с, а первая функция вместе с открытием сессии заняла 14,4 с). Повторный вызов занял 0,0014 с, 0,025 с и от 6 до 14 с (медиана около 9 с, 30 вызовов). На второй машине запускали только Extella: там 30 вызовов из 30 были верными без повторного сохранения, за 5–9 с. В этот день ни один вызов Extella из 80 в этом замере не упал (72 вызова run_expert в финальном прогоне и 8 в короткой проверке перед ним; эта проверка не вошла в опубликованные результаты); при повторном прогоне во время рецензии был один сбой из 73 вызовов, воспроизвести его мы не смогли. Все вызовы делал скрипт; ни одним из серверов не управляла модель. Два локальных способа работали как локальные процессы без сети; Extella — удалённый сервис, который передаёт вызов на устройство, а этим устройством был тот же ноутбук, который делал вызовы; поэтому разница в секундах не сравнение двух удалённых сервисов. MCP-сервер Extella перечисляет 44 инструмента: 53 КБ определений со всеми полями и 33 КБ в имени, описании и схеме входа; универсальный исполнитель перечисляет 4 инструмента на 2 КБ [проверено 2 октября 2026 года; measure3.py, generic_server.py, measure3-result-2026-10-02.json; два более ранних прогона: measure3-result-2026-10-02-run1.json, measure3-result-2026-10-02-run2.json]. Что из этого следует: замер подтверждает для Extella одно узкое свойство — программа, сохранённая один раз, выполняется на подготовленной машине, выбранной параметром вызова. Он не показывает преимущества перед универсальным исполнителем, поставленным на вторую машину, потому что такой прогон не делали, и ничего не говорит о продуктах Anthropic или Cloudflare, которые мы не запускали.
Вызов сохранённого эксперта через этот API не требует обращения к модели. В наших демо счётчик обращений к модели показывал 0 при запуске и 1 при создании эксперта по фразе [проверено 1 октября 2026 года]. Счётчик находится в демо-сервере — единственной части этого демо, где лежит ключ провайдера модели; его исходник опубликован на extella.dev/robot/server/. Это свидетельство одной части, а не аудит закрытой системы. Сам эксперт может быть написан так, чтобы обращаться к модели; ничто этому не мешает.
Одна и та же программа выполняется разными обработчиками. Один эксперт на языке block (JSON-документ, описывающий цитату на веб-странице, sha256 ba2de0dd…) исполнили три обработчика на двух Linux-серверах в шести запусках. Два обработчика выдают HTML и отличаются только константой; третий — отдельно написанный код, который выводит те же документы простым текстом. Регистрация обработчика заняла от 6,8 до 7,9 с; счётчик обращений к модели был 0 в каждом запуске; хеш обработчика, который вернуло устройство, совпал с хешем опубликованного файла во всех шести [проверено 1 октября 2026 года; extella.dev/blocks/server/].
Замена обработчика меняет то, как выполняются программы его языка на этом устройстве. Исправление или новое правило вносят в один обработчик, а сохранённые программы остаются как есть. В записи — одна сохранённая программа, которую без правок исполнили три обработчика [проверено на языке block: одна программа, шесть запусков]; живая страница extella.dev/blocks/ рисует шесть своих сохранённых блоков тем из трёх обработчиков, который вы выберете. То, что так же ведут себя все программы языка, — заявленное устройство среды исполнения [со слов]; на большем наборе программ мы этого не измеряли. Это широкое действие, а не гарантия безопасного обновления: чтобы существующие программы продолжали работать после замены обработчика, отвечает автор обработчика, а платформа не хранит версий ни обработчиков, ни программ. Это относится к языкам, обработчик которых пишете вы; для языка общего назначения обработчик — его собственный набор инструментов.
Языки не обязаны быть маленькими. Эксперты на четырёх языках выполнились на одном MacBook через один и тот же вызов: tally, Go, Rust и C [проверено 1 октября 2026 года]. Обработчики Go и Rust на устройстве уже были. Для C мы написали обработчик в шестнадцать строк, который компилирует исходник компилятором устройства и запускает программу (handler_c.py). Такому языку нужен на устройстве его набор инструментов.
Вокруг каждой программы не нужно разворачивать отдельный сервис. Каждый эксперт вызывается по имени через один API Extella, поэтому цепочка — это последовательность вызовов, и вызывающему не нужно знать, на каком языке написан следующий шаг. API и платформа за ним — сетевой слой; уходит сервис на каждую программу. Цепочка из трёх экспертов на трёх языках (язык запросов только на чтение, Go и язык страниц) выполнилась на одной машине 29 сентября 2026 года [проверено; сырые ответы этого прогона не опубликованы], а цепочка из трёх экспертов на трёх языках (язык запросов только на чтение, Python и язык страниц) выполнилась на Linux-сервере 2 октября 2026 года, и каждый результат передавался следующему как параметры [проверено; three_languages.py, three-languages-2026-10-02.json].
Один эксперт можно заменить, не трогая остальные. Сохранение нового исходника под тем же именем заменяет программу; следующий запуск использует её. Мы заменили программу tally между двумя запусками, и ответ изменился с 10 на 99 [проверено 1 октября 2026 года].
Маленький язык ограничивает, что может выразить программа. Программа на tally не может открыть файл, потому что в языке нет такой инструкции. Наши открытые демо этим пользуются: посетители добавляют содержимое на живую страницу на языке из семи видов блоков только с текстовыми полями [проверено для этих языков]. Это свойство одного языка и его обработчика. На других экспертов и на платформу оно не переносится; см. раздел 7.
Эксперт экспортируется и устанавливается как данные. Набор из двух экспертов, регистратора и программы, выгруженный одним JSON-файлом, сохранили в аккаунт и запустили на ноутбуке одним скриптом [проверено на macOS 1 октября 2026 года; extella.dev/robot/classic/take_home.py]. На целевом устройстве нужны установленная Extella и зарегистрированный обработчик; второе скрипт делает сам.
6. Близкие работы
Три опубликованных подхода близки к этому. Они касаются разных частей системы, и мы не утверждаем, что они не умеют того, что описано здесь.
| Code execution with MCP (Anthropic) | Code Mode (Cloudflare) | Agent Skills (Anthropic) | Этот документ | |
|---|---|---|---|---|
| Предмет | Показывать модели инструменты как код, который она может вызвать, чтобы определения инструментов и промежуточные данные не занимали её контекст | Превращать инструменты MCP в типизированный API и запускать написанный моделью код в изоляте | Упаковывать инструкции, ресурсы и скрипты, которые агент загружает, когда они нужны | Хранить программу с названием её языка и исполнять её обработчиком на выбранном устройстве |
| Язык кода | TypeScript в примерах статьи | TypeScript | Инструкции и скрипты | Объявляется для каждой программы |
| Что исполняет | Среда исполнения в песочнице | Изолят V8 с привязками к MCP-серверам | Инструмент исполнения кода | Обработчик, зарегистрированный на устройстве аккаунта |
| Повторное использование кода | Статья описывает сохранение работающего кода как переиспользуемых функций | В статье нет; документация Code Mode описывает именованные фрагменты, которые модель может найти и запустить снова | Навык переиспользуется по замыслу | Эксперт сохраняется и вызывается по имени |
| Замена исполнителя | Не рассматривается | В пакете Code Mode есть интерфейс исполнителя, который можно реализовать для другой среды; он запускает только JavaScript, и обработчика на каждый названный язык в прочитанном мы не нашли | Не рассматривается | Обработчик устанавливает эксперт |
| Изоляция | Песочница обязательна по замыслу | Изолят без общего доступа к сети | Исполнение кода в песочнице | По умолчанию нет, см. раздел 7 |
Источники, как мы читали их 1 октября 2026 года: anthropic.com/engineering/code-execution-with-mcp; blog.cloudflare.com/code-mode; claude.com/blog/skills. Прочитано 2 октября 2026 года: developers.cloudflare.com/agents/tools/codemode/how-it-works; github.com/cloudflare/agents/blob/main/packages/codemode/README.md.
MCP — вход в этот механизм: операции раздела 3 доступны как инструменты MCP, и приложение, которое уже работает через MCP, может продолжать так работать.
Как это собирает Extella: программа с именем, название её языка, обработчик, который можно зарегистрировать и заменить на каждом устройстве, и устройство, выбранное в каждом вызове. 2 октября 2026 года маленький язык из двух инструкций зарегистрировали на ноутбуке, запустили на нём четыре программы, заменили обработчик, и те же четыре программы, не сохранённые заново, выполнились под новым обработчиком; затем первый обработчик зарегистрировали на Linux-сервере, и две из четырёх программ выполнились там под ним; ноутбук, последние запуски на котором шли под вторым обработчиком, после этого не вызывали. Каждый шаг был вызовом инструментов MCP save_expert и run_expert [проверено; handler_via_mcp.py, handler-via-mcp-2026-10-02.json].
Этот документ описывает, как Extella связывает сохранённую программу, её язык, обработчик и выбранное устройство через один интерфейс вызова. Экспериментально с подходами выше мы это не сравнивали.
7. Ограничения
- Нет защитной песочницы. Обработчик и эксперт на
fythonработают с правами пользователя операционной системы, под которым запущен listener. Ограниченный язык ограничивает только то, что могут выразить программы на этом языке. Не запускайте экспертов и обработчики, которые вы не прочитали. - Среда исполнения закрыта. Проверить можно API, исходники экспертов и обработчиков, показанные здесь, и серверы наших демо.
- Отдельные версии библиотек для каждого эксперта — со слов, не воспроизведено. Автор говорит, что каждый эксперт получает своё окружение, поэтому два эксперта могут зависеть от разных версий одной библиотеки. В нашей проверке 29 сентября 2026 года два эксперта на Python, которые просили разные версии библиотеки, выполнились в одном окружении. На сочетание экспертов на разных языках это не влияет, оно работает (раздел 5).
- MCP-сервер — не маленький интерфейс. Он перечисляет 44 инструмента, около 53 КБ определений со всеми полями; сохранение, чтение, запуск и поиск программ занимают четыре из них.
- Вызов занимает секунды. От 6 до 14 с в замере 2 октября (медиана около 9 с, 30 вызовов, один ноутбук, один день) для функций, которые считают микросекунды.
- Устройство нужно подготовить заранее. На нём установлена Extella, выполнен вход и зарегистрирован обработчик языка.
- Нет версий. Сохранение под существующим именем молча перезаписывает. Версий обработчиков тоже нет.
- Состояние обработчиков — своё на каждом устройстве. Два вызывающих, которые регистрируют разные обработчики одного языка на одном устройстве, влияют друг на друга.
- Устройство должно быть в сети. Если listener остановлен, запуск не выполнится.
- Языку с набором инструментов нужен этот набор. Эксперт на Go, Rust или C выполняется только там, где установлен соответствующий компилятор.
- Нами не измерено: устройства, кроме двух Linux-серверов и MacBook; чистая установка на Windows; из чего состоит время запуска, которое сообщает платформа (из вызова в 6–14 с примерно секунда — это сервис и сеть между клиентом и сервисом; остальное мы не раскладывали).
8. Как повторить
Без аккаунта — вызовите демо-сервер:
curl -s -X POST https://extella.dev/api/blocks/render -H "Content-Type: application/json" \
-d '{"device":"second","handler":3,"blocks":["skill_block_quote"],"force":true}'
С установленной Extella и выполненным входом (она записывает ~/.extella/api_token.txt и ~/.extella/device.txt) и Python 3.8 или новее:
python3 run_tiny.py # the tally example of section 4 curl -sO https://raw.githubusercontent.com/AnvarBakiyev/extella-research/main/cspl/take-home/take_home.py python3 take_home.py "https://extella.dev/api/blocks/export/skill_block_quote?handler=3" --level render
Приложение. Что запускали
| Дата | 1 октября 2026 года для перечисленных ниже запусков; 2 октября 2026 года для трёхстороннего замера из раздела 5 |
| Машина | MacBook, macOS, Apple silicon; два Linux-сервера x86_64 для запусков block |
| Listener | extella-listener 1.2.5 (настольное приложение Extella 1.3.0) |
| Исходники | handler.py, handler_c.py, run_tiny.py, verify_all.py |
| Сырые ответы | runs-2026-10-01.json: восемь запусков с отметками времени, временем полного круга и ответами устройства |
| Замер из раздела 5 | measure3.py, generic_server.py, measure3-result-2026-10-02.json: скрипт, универсальный исполнитель и сырые результаты |
verify_all.py повторяет восемь запусков по порядку: регистрирует обработчик tally, запускает программу, запускает программу с отказом, заменяет программу и запускает снова, запускает Go, запускает Rust, регистрирует обработчик C, запускает C.
Среду исполнения спроектировал Тимур Рыспеков, CTO и сооснователь Extella.