Русский
Руководитель читает день команды у агентов сотрудников
Представьте команду, где у каждого сотрудника свой ИИ-агент (у нас это hermes), и каждый агент держит память в своей базе trip2g. Руководитель хочет утром знать, что команда собирается делать, а вечером — как прошёл день. Без созвона-летучки, без формы, которую надо заполнять, и без отчётов, которые кто-то должен присылать.
В trip2g уже есть три вещи, из которых это собирается: подграфы, федерация и шаблоны. Связывает их небольшой скрипт. Ниже — по шагам, как это устроено. Работающий пример, три базы в Docker и без агентов, лежит в trip2g/federation_team_sync.

Что понадобится из trip2g
- Подграф — именованная группа заметок, единица доступа в trip2g. Заметка попадает в подграф через frontmatter:
subgraph: daily_sync. Подробно — Подграфы. Frontmatter-патч ставит это поле сразу на всю папку, чтобы не помнить о нём в каждой заметке. - Федерация даёт одной базе читать часть другой. База сотрудника заводит входящий секрет, даёт ему доступ к одному подграфу и передаёт руководителю ключ передачи. База руководителя устанавливает ключ как исходящий секрет и добавляет KB-заметку: маленькую заметку, которая указывает на базу сотрудника и даёт ей короткий id,
kb_id. После этого база руководителя может читать этот подграф инструментами MCPfederated_*по этому id — и больше ничего из базы сотрудника. Подробно — MCP Federation. - Свой шаблон, layout, — собственная страница для заметки на языке Jet. Он лежит в
_layouts/<имя>.html, а заметка выбирает его строкойlayout: <имя>. Шаблон умеет находить другие заметки и раскладывать их так, как нужно странице. Подробно — Шаблоны и Справочник функций Jet.
Как всё связано
flowchart LR
subgraph E1["База Маши"]
N1["daily-sync/2026-10-19.md"] --> S1["подграф daily_sync"]
end
subgraph E2["База Пети"]
N2["daily-sync/2026-10-19.md"] --> S2["подграф daily_sync"]
end
subgraph M["База руководителя"]
P["скрипт, раз в час"] --> C["team/masha/…<br/>team/petya/…"]
C --> L["шаблон team"]
L --> D["dashboards/team"]
end
P -. "federated_note_html<br/>kb_id masha-daily" .-> S1
P -. "federated_note_html<br/>kb_id petya-daily" .-> S2
База каждого сотрудника ведёт по заметке на день и открывает руководителю только эти заметки. Скрипт в базе руководителя раз в час читает их и сохраняет копию, а один шаблон рисует из копий страницу.
Шаг 1. Заметка на каждый день в базе сотрудника
В базе каждого сотрудника по файлу на день, daily-sync/ГГГГ-ММ-ДД.md:
---
date: 2026-10-19
person: Маша
---
# 2026-10-19 — Маша
## План
- Подготовить КП для «Вектора».
- Обновить прайс в CRM.
## Итог
- КП отправлено, прайс перенесён на завтра.
## Нужна помощь
- Решение руководителя по скидке 10% для «Альфа-Логистик».
trip2g всё равно, кто пишет эту заметку. У нас это делает агент: задача по расписанию утром и вечером задаёт сотруднику вопрос, а агент пересказывает каждый ответ несколькими строками.
Шаг 2. Открыть руководителю только эти заметки
Frontmatter-патч кладёт всю папку в один подграф:
---
type: frontmatter-patch
include:
- "daily-sync/**"
---
```jsonnet
{ subgraph: "daily_sync" }
```
Затем база сотрудника заводит входящий секрет с именем по человеку, masha-daily, даёт ему доступ только к daily_sync и передаёт руководителю ключ передачи — по каналу, которому доверили бы пароль. База руководителя устанавливает ключ и добавляет KB-заметку на базу Маши с mcp_federation_kb_id: masha-daily — тем же именем, что у ключа.
Теперь руководитель видит дни Маши — и больше ничего из её базы: ни других заметок, ни памяти её агента. Отзыв этого секрета всё закрывает.
Имена важны. По суффиксу -daily база руководителя отличает ключ со статусами коллеги от всех остальных своих ключей, а одинаковые имена у ключа и KB-заметки позволяют скрипту идти от ключа сразу к вызову.
Шаг 3. Скопировать дни в базу руководителя
В базе руководителя раз в час работает скрипт. Он делает три вещи.
Находит команду. Берёт список секретов федерации своей базы и оставляет исходящие, не отозванные, с именем на -daily. Команда — это ровно набор подключённых ключей статуса, так что никакой список вручную вести не нужно:
{ admin { federationSecrets { kid kbUrl revokedAt } } }
Исходящий секрет — тот, у которого есть kbUrl, адрес базы, которую он читает. Это админский вызов: про такие вызовы trip2g обещает меньше, чем про API, так что после обновления его стоит проверить. Подробно — Вызовы API и админские вызовы.
Читает каждый день. Для каждого ключа вызывает инструмент MCP federated_note_html с этим id и заранее известным путём:
для каждого ключа -daily:
federated_note_html(kb_id, "daily-sync/<сегодня>.md")
Путь задан соглашением, поэтому ничего не нужно искать.
Сохраняет копию. federated_note_html отдаёт HTML, поэтому скрипт превращает три раздела обратно в markdown-заголовки и списки, выбрасывает пустые и записывает результат в team/<человек>/<дата>.md. Сверху он добавляет frontmatter, который нужен странице: person для имени в строке, date и help: true, если в «Нужна помощь» есть что-то кроме «нет».
Почему копия, а не чтение вживую? Прошедший день — факт с датой, его потом никто не правит, поэтому его копия не устаревает; копия сегодняшнего дня перезаписывается каждый час и отстаёт от вечернего ответа не больше чем на час. Если база сотрудника недоступна, его дни всё равно остаются на странице. А сама страница читает только локальные заметки.
Шаг 4. Нарисовать страницу шаблоном
Страница — одна заметка, dashboards/team.md, с layout: team, а шаблон лежит в _layouts/team.html. Два списка, которые нужны шаблону, скрипт пишет во frontmatter заметки:
---
title: Команда — последние дни
layout: team
people: [masha, petya]
dates: ["2026-10-19", "2026-10-18", "2026-10-17"]
---
Шаблон обходит «люди × даты» и находит каждую клетку по пути:
{{ range i, alias := people }}
{{ range j, day := dates }}
{{ if cell := nvs.ByPath("team/" + alias + "/" + day + ".md"); cell }}
{{ parts := cell.PartialRenderer() }}
<td class="{{ cell.M().GetBool("help", false) ? "t-help" : "t-done" }}">
{{ if plan := parts.Section("План"); plan }}{{ plan.ContentHTML }}{{ end }}
{{ if result := parts.Section("Итог"); result }}{{ result.ContentHTML }}{{ end }}
</td>
{{ else }}
<td class="t-empty">нет статуса</td>
{{ end }}
{{ end }}
{{ end }}
PartialRenderer().Section("План") отдаёт этот markdown-раздел копии готовым HTML, так что список печатается ровно как написан. Полная версия шаблона ещё поднимает над сеткой сегодняшние просьбы о помощи. Вся страница рисуется на сервере: ни JavaScript, ни запросов из браузера. Даты и обратный перевод HTML в markdown делает скрипт, шаблон только подставляет найденное.
А можно вообще без копий
Страница на своём шаблоне может читать базы сотрудников и вживую, прямо из браузера посетителя. Она обращается к /_system/mcp своей базы с сессионной cookie посетителя, а там есть и федеративные инструменты. База подписывает каждый вызов своими секретами федерации, и посетителю доступны только те пиры, которые ему разрешено читать. Подробно — MCP из браузера.
Для этой страницы мы выбрали копии. Три дня тридцати сотрудников — это девяносто запросов на каждое открытие страницы, с таймаутом в две секунды на пира, а сотрудник, чья база недоступна, пропал бы со страницы. Чтение вживую подходит странице, которая показывает одну вещь, меняющуюся каждую минуту.
Тот же приём: онбординг
Замените дни на шаги — и получится страница о том, как идёт онбординг каждого новичка. Шаги — знакомство, первая настоящая задача и разговор через неделю. Агент новичка расспрашивает сотрудника, делает задачу и пишет по заметке на каждый пройденный шаг, своими словами. Второй скрипт по журналам самого агента добавляет факты, которых агент не пишет: дату первого разговора и сколько разговоров было. База руководителя копирует и то и другое, а второй шаблон рисует «люди × шаги».
Смысл такой страницы — читать, что каждый агент говорит о своём сотруднике, и видеть, где слова расходятся с фактами.
На что обратить внимание
- Вопрос должен попасть в историю чата. Если утренний вопрос присылает задача по расписанию, а не агент в разговоре, вопрос должен лечь в ту же сессию чата. Иначе агент получит ответ без вопроса перед ним.
- Заметка на шаг. Если агенту поручить обновлять одну рабочую заметку, он обычно дописывает датированные записи внизу, а не переписывает нужный раздел. Если заметки нет, пока шаг не пройден, дописывать некуда.
- Имена по соглашению. Суффикс
-dailyставит человека на страницу, а id KB-заметки должен совпадать с именем ключа. Имя не по соглашению молча оставит человека за бортом. - Имя сотрудника — от самого сотрудника. Пока не поправят, агент берёт имя из профиля в чате.
- Приватность заложена в устройство. Сотрудник знает, что руководитель читает его день. В заметке — пересказ словами агента, никогда не переписка.