Перейти к содержимому

Песочница и LLM_AGENT

Навыки, запускающие скрипты (SCRIPT, HYBRID, SUBAGENT, LLM_AGENT), выполняются в выбранном вами рантайме. Выберите песочницу, чтобы сдержать недоверенный код.

Задайте runtime в AgentSkillStepConfig (с runtime_config для опций бэкенда):

runtimeИзоляция
localЗапуск в локальном подпроцессе (наименьшая изоляция).
dockerЗапуск внутри Docker-контейнера.
e2bЗапуск в облачной песочнице e2b.
firejailЗапуск под firejail (Linux).
AgentSkillStepConfig(
skill="github://anthropics/skills/skills/pdf@main",
task="Extract text from {pdf_path}.",
execution_mode=AgentSkillExecutionMode.LLM_AGENT,
runtime="docker",
extra_pip=["pdfplumber"], # ставится перед запуском скриптов
trust_policy="sha_pinned", # "any" | "sha_pinned"
)

Исходящий трафик рантайма навыка управляется NetworkPolicy — одним из трёх строковых значений, по умолчанию "none" (запрет по умолчанию):

ПолитикаЭффект
noneНикакого исходящего трафика (по умолчанию). Контейнеризованные бэкенды отключают весь egress (--network none); local/firejail могут лишь задокументировать это.
allowlistТолько трафик к хостам из runtime_config["network_allowlist"] (объединённым с объявлениями WebFetch(domain:…) из манифеста навыка).
hostПолный доступ к сети хоста — аварийная лазейка. Бэкенды логируют UserWarning при её выборе.

Задайте политику через runtime_config:

AgentSkillStepConfig(
skill="github://anthropics/skills/skills/pdf@main",
task="Fetch and summarise {url}.",
execution_mode=AgentSkillExecutionMode.LLM_AGENT,
runtime="docker",
runtime_config={
"network": "allowlist",
"network_allowlist": ["api.example.com"],
},
)

Если вы создаёте кастомный рантайм (ниже), можно переиспользовать те же помощники разрешения:

from mmar_carl import (
resolve_network_policy, parse_network_allowlist_from_allowed_tools,
)
# Normalise runtime_config + the manifest's allowed-tools into (policy, hosts).
policy, allowlist = resolve_network_policy(
runtime_config,
manifest_allowed_tools=manifest.get_allowed_tools(),
)
# Or pull just the WebFetch(domain:…) hosts out of an allowed-tools declaration.
hosts = parse_network_allowlist_from_allowed_tools("Bash(git:*) WebFetch(domain:api.x.com)")
# → ["api.x.com"]

resolve_network_policy бросает SkillRuntimeError при неизвестной строке политики и объединяет явный network_allowlist с любыми хостами WebFetch(domain:…), объявленными в манифесте навыка (так манифест становится источником истины о том, какой egress нужен навыку). parse_network_allowlist_from_allowed_tools игнорирует голый WebFetch без домена — неограниченная политика является осознанным выбором автора цепочки, а не манифеста.

Режим LLM_AGENT соответствует модели прогрессивного раскрытия AgentSkills: LLM крутит цикл вызовов инструментов (run_script / read_file / write_file / list_resources), пока не сформирует финальный ответ — всё в изолированном workspace.

  • Входные файлы из input_mapping размещаются в /workspace/in/.
  • Файлы, записанные LLM в /workspace/out/, собираются в result_data["output_files"].
  • output_capture ("stdout" / "files" / "both") выбирает, что вернуть.
  • output_files_glob фильтрует, какие выходные файлы сохранить.
  • llm_max_iterations ограничивает число раундов вызовов инструментов.

trust_policy="sha_pinned" (вместе с skill_sha256) проверяет дайджест SKILL.md навыка перед запуском — см. резолверы. filter_security_terms (по умолчанию True) вырезает разделы про password/encrypt/decrypt из промпта LLM.

runtime диспетчеризуется через реестр, так что вы можете подключить собственную песочницу, не форкая CARL. Встроенные имена (local, docker, e2b, firejail) саморегистрируются при импорте; дополнительные регистрируйте сами:

from mmar_carl import (
register_skill_runtime, get_skill_runtime, list_skill_runtimes,
)
class MySkillRuntime:
name = "mysandbox"
async def prepare(self, skill, workspace, config): ... # → SkillRuntimeHandle
async def run(self, handle, cmd, *, env=None, stdin=None, timeout=None, cwd=None): ...
async def read_file(self, handle, path): ...
async def write_file(self, handle, path, data): ...
async def cleanup(self, handle): ...
register_skill_runtime(MySkillRuntime.name, MySkillRuntime)
list_skill_runtimes() # ['docker', 'e2b', 'firejail', 'local', 'mysandbox']
runtime = get_skill_runtime("mysandbox") # instantiates the registered class

Рантайм должен удовлетворять Protocol SkillRuntime: пять асинхронных методов — prepare (создаёт workspace, возвращает SkillRuntimeHandle), run (выполняет одну команду, возвращает RuntimeRunResult), read_file, write_file и cleanup (идемпотентная очистка). Хендл протягивается через каждый вызов, чтобы бэкенд мог оставаться stateful (например, удерживать долгоживущий контейнер), и несёт workspace_root / workspace_in / workspace_out плюс произвольный словарь backend.

register_skill_runtime(name, cls) регистрирует класс (повторная регистрация имени перезаписывает его). get_skill_runtime(name) инстанцирует его и бросает SkillRuntimeError при неизвестном имени (с подсказкой «установите подходящий extra»); list_skill_runtimes() возвращает отсортированные зарегистрированные имена. Всё это, плюс NetworkPolicy, SkillRuntime, SkillRuntimeHandle, RuntimeRunResult и SkillRuntimeError, импортируется из mmar_carl.