Документ отвечает на вопрос «что реально можно поручить агенту, у которого есть этот сервер», и на не менее важный «чего поручать не стоит». Каждый кейс описан как процесс: кто его начинает, какие инструменты идут в дело, где процесс может свернуть не туда и что при этом увидит человек.
Перечень инструментов — в README, сценарии-заготовки для слэш-команд — в docs/prompts.md, измерение фактического использования — в docs/суточный-отчёт-по-mcp.md. Здесь — уровень выше: зачем всё это бизнесу.
Pyrus — система, где работа уже описана: формы, этапы, согласования, справочники, права. Агент не добавляет к ней новых сущностей. Он снимает три ограничения, которые до сих пор упирались в человеческое время.
| Ограничение | Как жили без агента | Что даёт агент |
|---|---|---|
| Реестр не читается глазами | Выгрузка в Excel, ручная сводка, устаревает к обеду | Сведение по запросу, на живых данных, за минуты |
| Однотипное действие ×100 | Либо руками, либо заказать интеграцию | Разовая операция без разработки |
| Знание сидит в задачах | Ответ ищут по переписке те, кто помнит | Ответ собирается из закрытых задач и ложится в базу знаний |
И одно ограничение агент не снимает: он не заменяет
ни интеграцию, ни регламент. Всё, что должно происходить каждый день
само и без свидетелей — это работа для бота Pyrus
(create_bot) или для внешнего сервиса, а не для агента в
чате.
flowchart LR
H["Человек<br/>роль в компании"] -->|"запрос словами"| A["Агент<br/>Claude / другой LLM"]
A <-->|"MCP, 88 инструментов"| S["Pyrus MCP<br/>сервер-шлагбаум"]
S <-->|"REST API"| P["Pyrus<br/>контур компании"]
S -->|"строка на каждый вызов"| L["Лог вызовов<br/>кто, что, чем кончилось"]
L -->|"суточный отчёт"| H
subgraph guard["Что сервер берёт на себя"]
G1["Токен владельца:<br/>кто именно пришёл"]
G2["Реквизиты Pyrus<br/>заголовками, на каждый запрос"]
G3["Потолки ответа:<br/>отказ вместо потопа"]
end
S -.- guard
Важная деталь этой схемы: реквизиты Pyrus приходят на каждый запрос отдельно. Один сервер обслуживает несколько контуров и несколько владельцев, и агент работает ровно с теми правами, которые ему выдали в Pyrus. Агент не может больше, чем человек, от чьего имени он ходит.
Та же шкала, что в prompts.md, — чтобы не выдавать
гипотезу за практику.
| Уровень | Что означает |
|---|---|
| 1 | Пройдено на живых данных, ловушки закреплены тестами |
| 2 | Логика ясна, инструменты есть, повторяемость ещё не подтверждена |
| 3 | Соблазнительно, но цена ошибки высока — только с человеком в контуре |
Кому. Руководителю направления, аналитику, финансисту — всем, кому нужен ответ вида «сколько», «за какой срок», «в каком этапе застревает».
Как звучит запрос. «Посчитай по форме заявок, сколько пришло за полугодие, сколько закрыто и где они висят дольше всего».
Инструменты. get_form(summary=true) →
plan_registry_walk →
get_registry(format="csv", field_ids=…) по каждому
окну.
flowchart TD
Q["Человек: «посчитай за полгода»"] --> F["get_form summary=true<br/>какие поля вообще есть"]
F --> N["Выбрать 3-5 полей<br/>вместо всей ширины формы"]
N --> W["plan_registry_walk<br/>разбить период на окна"]
W --> C{"Окно влезает<br/>под потолок?"}
C -->|"нет"| W2["Дробить дальше<br/>пополам по датам"]
W2 --> C
C -->|"да"| R["get_registry по окну<br/>format=csv"]
R --> M{"Окна кончились?"}
M -->|"нет"| R
M -->|"да"| S["Сводка человеку:<br/>цифры и где застревает"]
Ловушки, которые сервер держит за агента.
include_archived=true.Что человек получает. Не выгрузку, а ответ. Файл при этом никуда не едет: агент считает по частям и отдаёт сводку.
Кому. Администратору процесса, руководителю проекта — тому, кто отвечает за порядок в форме.
Как звучит запрос. «Добавь юриста наблюдателем во все договоры, которые висят на этапе согласования дольше двух недель».
Инструменты. get_registry (найти) →
get_task/get_tasks (убедиться) →
add_subscribers / assign_task /
update_task_fields / comment_task.
Это единственный кейс, который уже наблюдался в
проде: 28 августа за одно утро агент обошёл реестр 85 запросами
и сделал 82 записи add_subscribers за 13 минут. Отсюда и
оговорки ниже — они не гипотеза, а замер.
flowchart TD
Q["Человек: «добавь наблюдателя<br/>во все такие-то задачи»"] --> R["get_registry<br/>узкая выборка, только нужные поля"]
R --> L{"Выборка<br/>влезла?"}
L -->|"response_too_large"| N["Сузить: поля, период,<br/>этапы, item_count"]
N --> R
L -->|"да"| SHOW["Показать человеку:<br/>сколько задач и какие"]
SHOW --> OK{"Человек<br/>подтвердил?"}
OK -->|"нет"| STOP["Остановиться"]
OK -->|"да"| W["Запись по каждой задаче"]
W --> E{"Отказ на<br/>отдельной задаче?"}
E -->|"да"| K["Запомнить и продолжить<br/>остальные"]
E -->|"нет"| K
K --> M{"Список<br/>кончился?"}
M -->|"нет"| W
M -->|"да"| REP["Отчёт: сделано N,<br/>не вышло M и почему"]
Три правила, без которых кейс становится опасным.
delete_task —
нет.Чего кейс стоит. Каждый add_subscribers
возвращает задачу целиком — 18 КБ на вызов, 1,4 МБ за то утро, ~400
тысяч токенов контекста на операцию записи. Пока это так, массовые
операции лучше делать в отдельной сессии, а не в середине важного
разговора.
Кому. Руководителю, у которого несколько процессов и нет времени открывать каждый.
Как звучит запрос. «Что горит сегодня?» — или то же
самое по расписанию, через /loop либо внешний
планировщик.
Инструменты. get_overdue_tasks() и
get_tasks_due_soon(days=3) — два вызова, а не обход
по каждой форме. Оба берут сроки из календаря, а он не привязан
к форме: form_id там фильтр, а не требование. Дальше
сведение и, если нужно, comment_task на эскалацию.
flowchart TD
T["Утро, запуск по расписанию"] --> O["get_overdue_tasks<br/>что уже просрочено"]
T --> D["get_tasks_due_soon days=3<br/>у чего срок на подходе"]
O --> AGG["Свести: что горит,<br/>что вот-вот, у кого"]
D --> AGG
AGG --> C{"Есть что<br/>докладывать?"}
C -->|"нет"| Q["Одна строка:<br/>«всё в срок»"]
C -->|"да"| REP["Сводка по владельцам<br/>и срокам"]
REP --> ESC{"Нужна<br/>эскалация?"}
ESC -->|"да"| CT["comment_task:<br/>напоминание в самой задаче"]
ESC -->|"нет"| REP2["Только доклад человеку"]
Почему не по каждой форме. Так этот сценарий и был описан раньше — и это ровно тот перебор, за который приходится платить: на портфеле из двадцати форм получается сорок вызовов вместо двух. Календарь отдаёт сроки по всем формам сразу; сужать до одной формы стоит, только когда вопрос и правда про неё.
Замер, объясняющий переделку. До 09.09.2026 оба
инструмента ходили в реестр, а сроков в реестре нет вовсе: задача оттуда
несёт create_date, current_step,
fields, id, last_modified_date,
last_note_id. Поэтому get_tasks_due_soon
фильтровал окно изменения задачи и на живой форме возвращал
ноль, а get_overdue_tasks отдавал одни и те же задачи на
любое значение фильтра. Сценарий работал — и показывал не то.
Почему это работает лучше уведомлений Pyrus. Уведомление приходит по одной задаче и в момент события. Обзор отвечает на другой вопрос — «где сегодня хуже всего» — и сравнивает процессы между собой, чего ни одно уведомление не делает.
Оговорка. «Всё в срок» — полноценный ответ. Агент, который каждое утро выдаёт три абзаца ни о чём, перестаёт читаться на четвёртый день.
Кому. Владельцу процесса, когда сроки поехали, а причина не названа.
Инструменты.
get_registry(steps=[…], include_archived=true) →
get_task(last_comments=…) по образцам → сводка по
этапам.
flowchart LR
Q["«Почему заявки стали<br/>идти вдвое дольше?»"] --> R["get_registry по этапам:<br/>сколько где стоит"]
R --> H["Найти этап<br/>с аномальным скоплением"]
H --> S["get_task на 5-10 образцах<br/>с последними комментариями"]
S --> W["Прочитать, что общего:<br/>кого ждут, чего не хватает"]
W --> A["Ответ: этап, причина,<br/>кто может расшить"]
Где агент силён. Он читает комментарии десятка задач за минуту и находит общий знаменатель — «во всех случаях ждут скан от контрагента». Человек это тоже увидит, но потратит час.
Где он слаб. Этап (current_step) — не
то же самое, что поле формы со стадией. Задача на 20-м шаге может быть
открыта, а закрытые — стоять на 2-м, 3-м и 5-м. Считать «закрытость» по
шагу нельзя, это проверено.
Кому. Администратору Pyrus, службе безопасности, владельцу формы с чувствительными данными.
Инструменты. get_form_permissions →
get_contacts / get_members /
get_roles → change_form_permissions.
flowchart TD
Q["«Кто видит форму с зарплатами?»"] --> P["get_form_permissions<br/>id и уровни доступа"]
P --> N["get_contacts / get_members<br/>превратить id в людей"]
N --> R["Список: кто, какой уровень,<br/>из какого отдела"]
R --> F{"Есть<br/>лишние?"}
F -->|"нет"| OK["Доклад: доступ в порядке"]
F -->|"да"| SHOW["Показать человеку<br/>кандидатов на отзыв"]
SHOW --> APP{"Человек<br/>подтвердил?"}
APP -->|"нет"| OK2["Ничего не менять"]
APP -->|"да"| CH["change_form_permissions<br/>уровень none"]
CH --> V["get_form_permissions ещё раз:<br/>проверить, что применилось"]
Что закреплено в сервере. Доступ к форме Pyrus
ключует только числовым id — почты и имена отвергаются
на входе, а не молча игнорируются. Уровень
restricted_manager Pyrus документирует, но через этот
эндпоинт не применяет: проверено вживую — отвечает 200 и не меняет
ничего. Сервер его отклоняет, чтобы отзыв доступа не выглядел успешным,
не будучи им.
Обязательный шаг — перечитать права после изменения. Это тот случай, когда «успешный ответ» и «изменение произошло» — разные вещи.
Кому. Тому, кто держит в Pyrus номенклатуру, прайс, список контрагентов, приходящий из 1С или другой системы.
Инструменты. get_catalog(summary=true)
→ сравнение → update_catalog_items (диффом) или
sync_catalog(apply=false) как сухой прогон.
flowchart TD
SRC["Выгрузка из внешней системы"] --> S["get_catalog summary=true<br/>размер и колонки, без строк"]
S --> CMP["Сверить: что добавилось,<br/>изменилось, исчезло"]
CMP --> D{"Изменений<br/>много?"}
D -->|"единицы-десятки"| U["update_catalog_items<br/>upsert + delete"]
D -->|"полная замена"| DRY["sync_catalog apply=false<br/>сухой прогон"]
DRY --> CHECK["Показать человеку:<br/>что будет удалено"]
CHECK --> APP{"Подтверждено?"}
APP -->|"да"| SYNC["sync_catalog apply=true"]
APP -->|"нет"| STOP["Отменить"]
U --> LOG["Отчёт: N добавлено,<br/>M удалено"]
SYNC --> LOG
Главная опасность кейса — в одном предложении документации
Pyrus: sync_catalog — это полная
замена, всё, чего нет в переданном списке, удаляется. Диффом
(update_catalog_items) безопаснее всегда, когда изменений
не большинство.
Дешёвый первый шаг. summary=true на
незнакомом справочнике: замерено — 329 строк ужались с 44 КБ до 722 байт
(61 раз), 128 строк с 20 КБ до 787 байт. Сначала узнать размер, потом
решать, тянуть ли строки вообще.
Кому. Поддержке, HR, любому отделу, который отвечает на одно и то же.
Инструменты. get_registry (закрытые за
период) → get_task (как решали) →
get_knowledge_base_structure →
create_knowledge_base_entity →
update_knowledge_base_permissions.
flowchart TD
Q["«Что мы объясняем чаще всего?»"] --> R["get_registry<br/>закрытые за квартал"]
R --> G["Сгруппировать по теме:<br/>что повторяется"]
G --> T["get_task на образцах:<br/>как именно решали"]
T --> KB["get_knowledge_base_structure<br/>куда это положить"]
KB --> EX{"Статья<br/>уже есть?"}
EX -->|"да"| UPD["update_knowledge_base_entity<br/>дополнить"]
EX -->|"нет"| NEW["create_knowledge_base_entity<br/>черновик статьи"]
NEW --> PERM["update_knowledge_base_permissions<br/>кому видно"]
UPD --> REV["Человек вычитывает<br/>и публикует"]
PERM --> REV
Почему именно агент. Материал для статьи уже написан — в комментариях десятков закрытых задач. Не хватало только того, кто согласится это прочитать и свести. Черновик всегда идёт человеку на вычитку: база знаний — это лицо компании внутрь себя, и опечатка агента живёт в ней годами.
Кому. Отделу продаж и поддержке, где обращение начинается со звонка.
Инструменты. register_call →
get_upload_target → загрузка файла прямо в Pyrus →
attach_call_record.
sequenceDiagram
participant ATS as Телефония
participant AG as Агент
participant MCP as Pyrus MCP
participant P as Pyrus
ATS->>AG: звонок завершён, есть запись
AG->>MCP: register_call — account_id, номера, internal_number
MCP->>P: регистрация
P-->>MCP: task_id + is_new_task + ответственный
MCP-->>AG: «создана задача» или «комментарий к существующей»
AG->>MCP: get_upload_target
MCP-->>AG: URL и токен загрузки
AG->>P: POST аудио напрямую, мимо MCP
P-->>AG: guid файла
AG->>MCP: attach_call_record — task_id, record_file=guid
MCP->>P: запись и метаданные к звонку
AG-->>ATS: готово: задача N
Две детали, которые экономят день отладки.
internal_number — это то, как звонок находит хозяина:
Pyrus ищет сотрудника по рабочему телефону. Без него задача приезжает
без ответственного.get_upload_target выдаёт адрес, и файл грузится в Pyrus
напрямую. Цель принадлежит тому контуру, в котором получена, — файл,
загруженный не туда, к задаче не прицепится.Честная оговорка. Телефонные инструменты требуют включённой интеграции в Pyrus (Расширения). Этот сервер не смог выполнить их вживую — они собраны по документации и на живом контуре не проверялись. Уровень 2 здесь означает именно это.
Кому. HR и администратору Pyrus.
Инструменты приёма. create_member →
update_role(added_members) →
change_form_permissions →
get_knowledge_base_structure (что читать) →
create_task (чек-лист адаптации).
Инструменты ухода. get_registry (что на
нём висит) → assign_task (передать) →
update_role(removed_members) →
change_form_permissions(none) →
block_member.
flowchart TD
subgraph priem["Приём — риск низкий"]
A1["create_member"] --> A2["update_role<br/>добавить в роли"]
A2 --> A3["change_form_permissions<br/>доступ к формам"]
A3 --> A4["create_task<br/>чек-лист адаптации"]
end
subgraph uhod["Уход — риск высокий"]
B1["get_registry:<br/>что на нём висит"] --> B2["Показать человеку<br/>список задач"]
B2 --> B3{"Кому<br/>передаём?"}
B3 --> B4["assign_task<br/>по каждой задаче"]
B4 --> B5["update_role removed_members<br/>+ доступы в none"]
B5 --> B6["block_member<br/>последним шагом"]
end
uhod -.->|"порядок нарушать нельзя"| WARN["Заблокировать раньше передачи<br/>= задачи повисают на никого"]
Почему уровень 3. block_member
не имеет обратной операции — в API Pyrus нет
разблокировки. Заблокировать не того человека — это поездка к
администратору, а не отмена в чате. Порядок шагов тоже не косметика:
сначала передать задачи, потом снимать доступы, блокировать — последним.
Роль, удалённая без task_receiver_id, оставляет свои задачи
без получателя.
Что агент делает хорошо даже здесь. Готовит список: что висит, кому логично передать, какие доступы есть. Решение и нажатие — за человеком.
Не всякий инструмент, который есть, годится для «сделай сам».
| Действие | Почему не без человека |
|---|---|
delete_task |
Безвозвратно. Для «завершить» есть close_task |
sync_catalog(apply=true) |
Полная замена: всё, чего нет в списке, удаляется |
block_member |
Разблокировки в API нет |
delete_role |
Без task_receiver_id задачи роли остаются без
адресата |
delete_bot,
delete_knowledge_base_entity |
Удаляют вместе с содержимым |
Массовое close_task |
Закрытие меняет отчётность; выборка должна быть проверена глазами |
Правило простое: необратимое действие требует, чтобы человек увидел выборку до её применения. Это не про недоверие к модели, а про то, что отката в Pyrus нет.
Они не про технику, а про то, как формулировать задачу агенту.
| Потолок | Значение | Что делать |
|---|---|---|
| Ответ инструмента | 200 КБ | Сузить поля, период, item_count; CSV вместо JSON |
| Скачивание файла | 16 МБ | Работать по ссылке (get_file_download_url) |
| Реестр Pyrus | 20 000 задач, молча | Обход окнами через plan_registry_walk |
| Справочник | отдаётся целиком | summary=true первым шагом |
| Реестр по умолчанию | только открытые задачи | include_archived=true при любом подсчёте |
| Сроки задач | в реестре их нет вовсе | Брать из календаря — на нём стоят get_overdue_tasks и
get_tasks_due_soon |
Отказ по размеру — не поломка, а часть протокола: сервер называет размер, говорит, сколько это примерно токенов, и подсказывает, как сузить. Агент, который читает подсказку, проходит дальше сам.
Повторять отказ бесполезно, и сервер об этом скажет. Три одинаковых отказа подряд — и к сообщению добавляется, что повтор не поможет. Правило для агента проще формулировки: если два запроса подряд упали одинаково, следующим действием должен быть не третий запрос, а сужение или вопрос человеку. Замер, из которого это правило выросло: 32 одинаковых отказа за 25 минут без единой смены подхода.
Сервер пишет на каждый вызов три вещи: владельца с инструментом, исходом, размером и длительностью; отпечаток аргументов — что именно спрашивали (идентификаторы, размер страницы, факт курсора, но не значения фильтров и не тексты); а при отказе — то, что ответил сам Pyrus. Отсюда видно не «сколько раз дёрнули API», а то, что важно бизнесу.
flowchart LR
L["Лог вызовов"] --> R["scripts/mcp_daily_report.py"]
R --> Q1["Кто ходит:<br/>кейс живёт или один энтузиаст"]
R --> Q2["Во что упираются:<br/>чего не хватает в инструментах"]
R --> Q5["Что повторяется:<br/>обход или перебор одного и того же"]
R --> Q3["Что дорого:<br/>вызовы на десятки КБ"]
R --> Q4["Что сломано:<br/>internal_error, no_credentials"]
Ориентиры нормы, порядок разбора и что докладывать человеку — в docs/суточный-отчёт-по-mcp.md.
Живой пример из первого же периода наблюдений (27–29 августа 2026): подключены три клиента, но 169 вызовов из 172 сделал один владелец и в одном сценарии — кейс 2. Второй клиент сделал три вызова, все три неудачные; третий не вызвал ничего, только держал соединение. Это и есть честная картина внедрения: не «88 инструментов доступны», а «один кейс дошёл до практики». Остальные девять описаны здесь для того, чтобы было видно, куда идти дальше.