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

Каталог: выложить своё

В каталоге лежат две разные вещи, и путать их не стоит.

Роль — конфигурация нашего ассистента: текст роли плюс набор инструментов, которые ей разрешены. Она исполняется у нас. Другой человек ставит её себе одним нажатием, и у него появляется копия — не доступ к вашим файлам и не ссылка на вашу роль, а именно копия двух полей.

Решение — ваш сервис, построенный на этом API. Он крутится у вас. Мы его не устанавливаем и не запускаем: карточка ведёт на ваш адрес, а доступ к своим файлам человек даёт вам сам, выпуская токен.

Публикация и установка бесплатны. Обе карточки проходят проверку человеком.

Это главное, что нужно знать до публикации. Чужая роль — это инструкции, которые исполняются над файлами установившего, поэтому у роли из каталога набор инструментов урезан жёстче, чем у той же роли у вас. Вырезано то, что:

  • удаляет;
  • перезаписывает файл на месте — исходник исчезает;
  • раздаёт доступ к файлу наружу;
  • тасует библиотеку владельца без подтверждения;
  • пишет в долговременную память владельца (роль удалят, запись останется);
  • пишет в календарь владельца.

Подтверждение человеком тут не защита: половина по-настоящему опасного идёт без него, а на остальном люди жмут «да», не читая. Граница проходит по составу инструментов собранного агента — чего в нём нет, того он физически не позовёт, что бы ни было написано в тексте роли.

Поэтому роль, которая живёт удалением и перекладыванием файлов, в каталоге работать не будет. Публикация такой роли не пройдёт — сервер назовёт группы, которые в каталоге пусты. А группа, из которой вырезана только часть, публикуется, но умеет меньше: карточка честно показывает и то, и другое.

Точный список — в карточке: поле cannot у GET /v1/catalog/{slug} собирается из того же кода, что и сам запрет, поэтому разойтись с реальностью не может.

Сначала соберите роль у себя — в приложении или через API. Публикуется она целиком и как есть: сервер берёт её текст и наборы из вашей учётной записи, а не из тела запроса.

listing_id = await cloud.publish_to_catalog(
profile_slug="lawyer", # ваша роль, уже собранная
title="Юрист по договорам",
summary="Находит риски в договоре и объясняет их простыми словами",
category="legal",
)

Учтите: текст роли станет виден всем. Его показывают до установки — прятать инструкции, которые сейчас будут работать с чужими файлами, нечем оправдать. Проверьте, что в нём нет ничего личного.

Карточка появится в каталоге после одобрения; тогда же она получит адрес. До этого её видно только вам:

for listing in await cloud.my_catalog_listings():
print(listing.state, listing.installs)
for v in listing.versions:
print(v.version, v.review_status, v.review_note) # причина отказа — здесь
listing_id = await cloud.publish_solution(
title="Бот для протоколов встреч",
summary="Присылаете запись — получаете протокол с поручениями",
url="https://meetings.example.com",
repo_url="https://github.com/example/meeting-bot",
contact="@example",
category="work",
)

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

Опубликованная версия неизменяема. Правка роли создаёт СЛЕДУЮЩУЮ версию, которая заново идёт в очередь, а у тех, кто уже поставил роль, продолжает работать та, которую они ставили. Перейти на новую — их отдельное действие.

version = await cloud.submit_catalog_version(listing_id, profile_slug="lawyer",
changelog="добавил проверку сроков")

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

Одобренная карточка попадает на публичную витрину

Заголовок раздела «Одобренная карточка попадает на публичную витрину»

Как только модератор одобрил версию, карточка открывается без входа — marketplace.agentums.ru/c/<адрес>. Это и есть смысл публикации: ссылку на карточку можно дать кому угодно, а не только тем, у кого уже есть аккаунт.

Из этого следуют две вещи, о которых стоит знать заранее.

Текст роли виден всем, включая поисковики. Он и раньше был открыт любому вошедшему — иначе человек не мог бы прочитать, что именно будет исполняться над его файлами, — но теперь у него публичный адрес.

Подпись автора — это имя из вашего профиля. Если там стоит адрес почты, на витрину он не попадёт: вместо него карточка будет без автора. Заполните имя в профиле — оно поедет со следующей версией карточки.

Свой адрес на витрине задаётся один раз, в профиле, и собирает все ваши одобренные карточки на одной странице — marketplace.agentums.ru/a/<ваш-адрес>. Её и стоит давать своей аудитории.

Вердикт модерации приходит уведомлением: одобрение с адресом карточки, отказ — с причиной.

catalog = await cloud.list_catalog(q="договор", category="legal")
print(catalog.total) # сколько всего подходит под фильтр
card = await cloud.catalog_card("lawyer-pro")
print(card.cannot) # что эта роль НЕ сможет
role = await cloud.install_catalog_role("lawyer-pro")
await cloud.activate_agent_profile(role.slug) # установка не делает роль активной

Установленная роль живёт среди ваших: её видно в list_agent_profiles() с source="catalog". Правкой она не подчиняется — текст писал другой человек, и правка потерялась бы при первом же обновлении. Убрать её можно как обычную свою роль.

Решение устанавливать нечего — оно работает у автора. Вместо установки его подключают:

url = await cloud.connect_solution("invoice-checker") # адрес сервиса автора
await cloud.disconnect_solution("invoice-checker") # убрать из своих

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

Доступ к своим файлам вы даёте автору сами — выпуская токен на его сервисе. Мы в этом обмене не участвуем, и наши ограничения на чужой сервис не распространяются: поэтому у карточки решения нет списков «сможет / не сможет».

Ничего: ни публикация, ни установка. Но роль тратит ваш баланс, когда работает, — и расход виден в разрезе ролей:

report = await cloud.get_usage_by_agent(days=30)
for item in report.items:
print(item.name, item.source, item.cost_rub)

Строка с пустым slug — не «неизвестная роль», а расход не про роль вовсе: загрузка, обработка, поиск. Без неё сумма не сошлась бы с get_usage().