[refactor][agent-roles-catalog] Перевод каталога ролей на YAML (instructions блок-скаляром, построчные диффы) #229
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Проблема
В каталоге ролей (
agent-roles-catalog/) полеinstructions— это один большой системный промпт, который в JSON хранится как одна длинная строка с экранированными\n. Из‑за этого:\n, экранирование кавычек).Цель — перевести файлы каталога на YAML и хранить
instructionsкак литеральный блок‑скаляр (|-), чтобы:Архитектурный контекст (важно)
Каталог — это данные, которые читает отдельный сервер в рантайме. Сервер (
AiAgentRolesCatalogProvider,apps/server/src/core/ai-chat/roles/catalog/) скачивает RAW‑файлы по базовому URL (AI_AGENT_ROLES_CATALOG_URL, задаётся per‑branch в CI: develop‑образ → develop‑URL, main‑образ → main‑URL), делаетJSON.parseи прогоняет через рукописные type‑guard'ы (осознанно без zod / без зависимостей, т.к. вход недоверенный).Сейчас guard жёстко требует
typeof v.instructions === 'string'— массив он бы отверг. Поэтому смена формата на YAML — это не чистая правка данных, а скоординированное изменение: данные + парсер сервера + валидаторcheck.mjs+ тесты + доки, выезжающие одной веткой.Предлагаемое решение
1. Данные → YAML (5 файлов)
index.json→index.yamlbundles/editorial/ru.json→ru.yaml,bundles/editorial/en.json→en.yamlbundles/research/ru.json→ru.yaml,bundles/research/en.json→en.yaml.jsonудалить.instructionsхранить литеральным блок‑скаляром|-(chomp, без хвостового\n), чтобы резолвнутый промпт был байт‑в‑байт прежним. Остальные поля (slug,emoji,name,description,launchMessage,version,schemaVersion,language) — обычные скаляры.Конвертацию делать программно через библиотеку
yaml(round‑trip‑safe), а не руками:lineWidth: 0отключает фолдинг (чтобы однострочныеdescriptionне превратились в folded‑скаляр). Многострочные строкиyamlсам пишет литеральным блоком.2. Сервер
AiAgentRolesCatalogProvideryamlвapps/server/package.json;JSON.parseна безопасныйparseизyaml(дефолтная схема: только JSON‑совместимые типы и стандартные теги, без кастомных!!‑тегов / выполнения кода);index.json→index.yaml,bundles/${bundleId}/${language}.json→.yaml;isCatalogRoleи др.) НЕ меняются — после парсингаinstructionsостаётсяstring;^[a-z0-9-]+$),redirect: 'error', таймаут 10 c, потоковый лимитMAX_BYTES = 1_000_000.3. Валидатор
agent-roles-catalog/scripts/check.mjsyamlвместоJSON.parse;.json→.yaml, обновить тексты ошибок;yamlвagent-roles-catalog/package.json(devDependency). Нюанс: каталог не входит в pnpm‑workspace (packages: ['apps/*','packages/*']), но приimport 'yaml'изscripts/check.mjsNode поднимается по дереву до корневогоnode_modules/yaml(он уже в сторе и станет прямой зависимостью сервера). Проверить, что в CIcheck.mjsнаходит модуль.4. Тесты
ai-agent-roles-catalog.provider.spec.tsJSON.stringify(...)→ YAML (черезYAML.stringifyили YAML‑литералы);instructionsкак многострочный блок‑скаляр парсится и склейка строк корректна (равна ожидаемому промпту).5. Доки и прочее
agent-roles-catalog/README.md: формат файлов (<lang>.yaml,index.yaml), примеры jsonc → yaml, «server fetches … .yaml», секция валидации;apps/server/src/core/ai-chat/roles/catalog/catalog-types.ts: комментарии про.json→.yaml;.env.example(~стр. 136): «appends /index.json and /bundles//.json» →.yaml;AI_AGENT_ROLES_CATALOG_URLуказывает на директорию, расширение добавляет сервер; Dockerfile каталог не копирует.Версионирование и деплой‑связность
versionролей вindex.yamlНЕ бампать — резолвнутый контент идентичен, меняется только сериализация.schemaVersion: на обсуждение — оставить1(набор полей не изменился) либо бампнуть до2как сигнал смены сериализации..json(рекомендуется, если есть долгоживущие старые образы);Безопасность
YAML‑парсинг недоверенного ввода: использовать
parseс дефолтной схемой (без кастомных тегов / без!!js). Действующий лимит 1 МБ ограничивает DoS от расширения алиасов (billion laughs). Проверить/настроить лимиты парсера. Нужен security‑ревью этого пункта (есть меткаsecurity, если решим повесить).Затрагиваемые файлы (чеклист)
agent-roles-catalog/index.yaml(изindex.json)agent-roles-catalog/bundles/editorial/{ru,en}.yamlagent-roles-catalog/bundles/research/{ru,en}.yaml*.jsonapps/server/.../catalog/ai-agent-roles-catalog.provider.tsapps/server/.../catalog/ai-agent-roles-catalog.provider.spec.tsapps/server/.../catalog/catalog-types.ts(комментарии)apps/server/package.json(+yaml)agent-roles-catalog/scripts/check.mjsagent-roles-catalog/package.json(+yamldevDep)agent-roles-catalog/README.md.env.exampleCHANGELOG.md(запись о ротации формата)Критерии приёмки (DoD)
.jsonудалены.node agent-roles-catalog/scripts/check.mjs→OK.YAML.parse(new)deepEqualJSON.parse(old).instructionsкаждой роли байт‑в‑байт совпадает с прежним.pnpm --filter server testзелёный; провайдер парсит YAML, отвергает невалидный YAML и неверную схему какBadGateway, SSRF‑гард не тронут..env.exampleобновлены.Открытые вопросы
schemaVersionоставить1или бампнуть до2?.yaml(в репо естьpnpm-workspace.yaml) vs.yml(crowdin.yml,docker-compose.yml) — предлагаю.yaml.Спроектировано на основе разбора кода
apps/server/src/core/ai-chat/roles/catalog/иagent-roles-catalog/. Реализация требует ревью (формат данных + парсинг недоверенного ввода).Ghost referenced this issue2026-06-28 23:45:43 +03:00