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 и зарегистрированный обработчик; второе скрипт делает сам.

Три опубликованных подхода близки к этому. Они касаются разных частей системы, и мы не утверждаем, что они не умеют того, что описано здесь.

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. Ограничения

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
Listenerextella-listener 1.2.5 (настольное приложение Extella 1.3.0)
Исходникиhandler.py, handler_c.py, run_tiny.py, verify_all.py
Сырые ответыruns-2026-10-01.json: восемь запусков с отметками времени, временем полного круга и ответами устройства
Замер из раздела 5measure3.py, generic_server.py, measure3-result-2026-10-02.json: скрипт, универсальный исполнитель и сырые результаты

verify_all.py повторяет восемь запусков по порядку: регистрирует обработчик tally, запускает программу, запускает программу с отказом, заменяет программу и запускает снова, запускает Go, запускает Rust, регистрирует обработчик C, запускает C.

Среду исполнения спроектировал Тимур Рыспеков, CTO и сооснователь Extella.