428 lines
9.6 KiB
Markdown
428 lines
9.6 KiB
Markdown
# Модель данных
|
||
|
||
## 1. Назначение документа
|
||
|
||
Этот документ фиксирует целевую формальную модель данных платформы. Его цель - определить такие структуры, которые можно без существенных изменений перенести в:
|
||
|
||
- Rust domain types;
|
||
- HTTP DTO;
|
||
- структуру таблиц БД;
|
||
- runtime-представление операций и агентов;
|
||
- UI-формы и конфигурационные экраны.
|
||
|
||
## 2. Общие принципы модели
|
||
|
||
### 2.1. Одна операция - один интеграционный контракт
|
||
|
||
Каждая `Operation` соответствует одному интеграционному контракту:
|
||
|
||
- GraphQL -> один конкретный `query` или `mutation`;
|
||
- gRPC -> один unary method или один server-streaming method в bounded execution mode;
|
||
- REST -> один endpoint-сценарий.
|
||
|
||
Однако MCP tool публикуется не напрямую из operation, а через `AgentOperationBinding` внутри конкретного `Agent`.
|
||
|
||
### 2.2. Внутренний транспортный формат - JSON
|
||
|
||
Независимо от внешнего протокола внутри системы данные представлены в JSON-ориентированном виде.
|
||
|
||
### 2.3. Mapping всегда явный
|
||
|
||
Даже если система умеет строить черновой mapping по примерам данных, итоговая конфигурация mapping сохраняется явно.
|
||
|
||
### 2.4. JSONPath как единый язык адресации
|
||
|
||
Для input и output mapping используется `JSONPath`.
|
||
|
||
### 2.5. Workspace - обязательная граница данных
|
||
|
||
Все продуктовые сущности принадлежат одному `Workspace`.
|
||
|
||
Минимальный набор workspace-scoped сущностей:
|
||
|
||
- `Secret`
|
||
- `SecretVersion`
|
||
- `Operation`
|
||
- `OperationVersion`
|
||
- `AuthProfile`
|
||
- `Agent`
|
||
- `PlatformApiKey`
|
||
- `InvocationLog`
|
||
- `UsageRollup`
|
||
|
||
### 2.6. YAML как формат обмена конфигурацией
|
||
|
||
Помимо канонической JSON-модели система поддерживает импорт и экспорт конфигураций в `YAML`.
|
||
|
||
### 2.7. Streaming operation обязана быть bounded
|
||
|
||
Если операция использует upstream streaming, она должна работать в одном из execution modes:
|
||
|
||
- `window`
|
||
- `session`
|
||
- `async_job`
|
||
|
||
Бесконечный passthrough stream не является допустимой моделью `Operation`.
|
||
|
||
## 3. Корневые сущности
|
||
|
||
### 3.1. `Workspace`
|
||
|
||
Поля:
|
||
|
||
- `id`
|
||
- `slug`
|
||
- `display_name`
|
||
- `status`
|
||
- `settings`
|
||
- `created_at`
|
||
- `updated_at`
|
||
|
||
Назначение:
|
||
|
||
- логическая изоляция команд;
|
||
- scoping для операций, агентов, ключей и логов;
|
||
- основа для multi-tenant MCP endpoints.
|
||
|
||
### 3.2. `Operation`
|
||
|
||
Поля:
|
||
|
||
- `id`
|
||
- `workspace_id`
|
||
- `name`
|
||
- `display_name`
|
||
- `category`
|
||
- `protocol`
|
||
- `status`
|
||
- `version`
|
||
- `target`
|
||
- `input_schema`
|
||
- `output_schema`
|
||
- `input_mapping`
|
||
- `output_mapping`
|
||
- `execution_config`
|
||
- `tool_description`
|
||
- `samples`
|
||
- `generated_draft`
|
||
- `config_export`
|
||
- `streaming_config`
|
||
- `created_at`
|
||
- `updated_at`
|
||
- `published_at`
|
||
|
||
### 3.3. `Agent`
|
||
|
||
`Agent` - пользовательская MCP-поверхность, которая собирает ограниченный набор published operations.
|
||
|
||
Поля:
|
||
|
||
- `id`
|
||
- `workspace_id`
|
||
- `slug`
|
||
- `display_name`
|
||
- `description`
|
||
- `status`
|
||
- `current_draft_version`
|
||
- `latest_published_version`
|
||
- `created_at`
|
||
- `updated_at`
|
||
- `published_at`
|
||
|
||
### 3.4. `AgentVersion`
|
||
|
||
Снимок конфигурации агента.
|
||
|
||
Поля:
|
||
|
||
- `agent_id`
|
||
- `version`
|
||
- `status`
|
||
- `instructions`
|
||
- `tool_selection_policy`
|
||
- `bindings`
|
||
- `created_at`
|
||
|
||
### 3.5. `AgentOperationBinding`
|
||
|
||
Связь published operation с agent version.
|
||
|
||
Поля:
|
||
|
||
- `operation_id`
|
||
- `operation_version`
|
||
- `tool_name`
|
||
- `tool_title`
|
||
- `tool_description_override`
|
||
- `enabled`
|
||
|
||
### 3.6. `Secret`
|
||
|
||
Секрет для доступа к внешней системе.
|
||
|
||
Поля:
|
||
|
||
- `id`
|
||
- `workspace_id`
|
||
- `name`
|
||
- `kind`
|
||
- `status`
|
||
- `current_version`
|
||
- `created_at`
|
||
- `updated_at`
|
||
|
||
Значение:
|
||
|
||
- plaintext не возвращается в list/get endpoints;
|
||
- текущее значение хранится в зашифрованном виде через `SecretVersion`;
|
||
- rotate создает новую версию секрета без потери ссылочной целостности.
|
||
|
||
### 3.7. `SecretVersion`
|
||
|
||
Зашифрованное значение секрета.
|
||
|
||
Поля:
|
||
|
||
- `secret_id`
|
||
- `version`
|
||
- `ciphertext`
|
||
- `key_version`
|
||
- `created_at`
|
||
|
||
### 3.8. `AuthProfile`
|
||
|
||
Используется только для доступа к внешним системам.
|
||
|
||
Поля:
|
||
|
||
- `id`
|
||
- `workspace_id`
|
||
- `name`
|
||
- `kind`
|
||
- `config`
|
||
|
||
Принцип:
|
||
|
||
- `AuthProfile` не хранит plaintext;
|
||
- config ссылается на `secret_id` или пару `secret_id`, если auth-схема составная;
|
||
- runtime применяет profile к запросу только в момент вызова upstream.
|
||
|
||
### 3.9. `PlatformApiKey`
|
||
|
||
Отдельная сущность для доступа к самой платформе.
|
||
|
||
Поля:
|
||
|
||
- `id`
|
||
- `workspace_id`
|
||
- `name`
|
||
- `prefix`
|
||
- `scopes`
|
||
- `status`
|
||
- `created_at`
|
||
- `last_used_at`
|
||
|
||
Секрет:
|
||
|
||
- полный secret показывается только один раз при создании;
|
||
- в persistent storage сохраняется только `secret_hash`.
|
||
|
||
### 3.10. `User`
|
||
|
||
Поля:
|
||
|
||
- `id`
|
||
- `email`
|
||
- `display_name`
|
||
- `password_hash`
|
||
- `status`
|
||
- `created_at`
|
||
|
||
Пароль:
|
||
|
||
- хранится только как `Argon2id` hash;
|
||
- plaintext пароль не сохраняется;
|
||
- верификация использует `password_pepper` из env.
|
||
|
||
### 3.11. `UserSession`
|
||
|
||
Поля:
|
||
|
||
- `id`
|
||
- `user_id`
|
||
- `secret_hash`
|
||
- `status`
|
||
- `expires_at`
|
||
- `last_seen_at`
|
||
- `created_at`
|
||
|
||
Секрет:
|
||
|
||
- браузеру выдается только opaque session token;
|
||
- в persistent storage сохраняется только `secret_hash`;
|
||
- подпись и верификация используют `session_secret` из env.
|
||
|
||
### 3.12. `Membership`
|
||
|
||
Поля:
|
||
|
||
- `workspace_id`
|
||
- `user_id`
|
||
- `role`
|
||
- `created_at`
|
||
|
||
### 3.13. `InvitationToken`
|
||
|
||
Поля:
|
||
|
||
- `id`
|
||
- `workspace_id`
|
||
- `email`
|
||
- `role`
|
||
- `status`
|
||
- `expires_at`
|
||
- `created_at`
|
||
|
||
Токен:
|
||
|
||
- полный invite token показывается только один раз при создании;
|
||
- в persistent storage сохраняется только `token_hash`.
|
||
|
||
### 3.14. `InvocationLog`
|
||
|
||
Продуктовая запись о вызове tool.
|
||
|
||
Поля:
|
||
|
||
- `id`
|
||
- `workspace_id`
|
||
- `agent_id`
|
||
- `operation_id`
|
||
- `request_id`
|
||
- `level`
|
||
- `status`
|
||
- `duration_ms`
|
||
- `error_kind`
|
||
- `request_preview`
|
||
- `response_preview`
|
||
- `created_at`
|
||
|
||
### 3.15. `UsageRollup`
|
||
|
||
Агрегированная статистика по периоду.
|
||
|
||
Поля:
|
||
|
||
- `workspace_id`
|
||
- `agent_id`
|
||
- `operation_id`
|
||
- `period_kind`
|
||
- `period_start`
|
||
- `calls_total`
|
||
- `calls_ok`
|
||
- `calls_error`
|
||
- `p50_ms`
|
||
- `p95_ms`
|
||
- `p99_ms`
|
||
|
||
### 3.16. `StreamSession`
|
||
|
||
Поля:
|
||
|
||
- `id`
|
||
- `workspace_id`
|
||
- `agent_id`
|
||
- `operation_id`
|
||
- `mode`
|
||
- `status`
|
||
- `cursor`
|
||
- `state`
|
||
- `expires_at`
|
||
- `last_poll_at`
|
||
- `created_at`
|
||
- `closed_at`
|
||
|
||
Назначение:
|
||
|
||
- хранение bounded session state для streaming tools;
|
||
- поддержка `start/poll/stop`;
|
||
- cleanup orphaned и expired sessions.
|
||
|
||
### 3.17. `AsyncJobHandle`
|
||
|
||
Поля:
|
||
|
||
- `id`
|
||
- `workspace_id`
|
||
- `agent_id`
|
||
- `operation_id`
|
||
- `status`
|
||
- `progress`
|
||
- `result`
|
||
- `error`
|
||
- `expires_at`
|
||
- `created_at`
|
||
- `updated_at`
|
||
- `finished_at`
|
||
|
||
Назначение:
|
||
|
||
- поддержка `start/status/result/cancel` для long-running upstream actions.
|
||
|
||
## 4. `Target`
|
||
|
||
`Target` описывает конкретный внешний вызов. Это discriminated union по протоколу.
|
||
|
||
### 4.1. `RestTarget`
|
||
|
||
- `kind`
|
||
- `base_url`
|
||
- `method`
|
||
- `path_template`
|
||
- `static_headers`
|
||
|
||
### 4.2. `GraphqlTarget`
|
||
|
||
- `kind`
|
||
- `endpoint`
|
||
- `operation_type`
|
||
- `operation_name`
|
||
- `query_template`
|
||
- `response_path`
|
||
|
||
### 4.3. `GrpcTarget`
|
||
|
||
- `kind`
|
||
- `server_addr`
|
||
- `package`
|
||
- `service`
|
||
- `method`
|
||
- `descriptor_ref`
|
||
- `descriptor_set_b64`
|
||
|
||
## 5. `Schema`
|
||
|
||
`Schema` - нормализованное описание входа или выхода.
|
||
|
||
Поддерживаются:
|
||
|
||
- скалярные поля;
|
||
- вложенные объекты;
|
||
- массивы;
|
||
- enum;
|
||
- nullable-поля;
|
||
- `oneof` для protobuf.
|
||
|
||
## 6. Принцип совместимости
|
||
|
||
Если UI требует сущность, которой нет в текущем backend, эта сущность должна быть сначала явно добавлена в эту модель данных, а уже потом в код и БД.
|
||
|
||
Для upstream credentials это означает:
|
||
|
||
- placeholder-строки вида `${secrets.API_KEY}` не считаются реальной моделью данных;
|
||
- рабочая продуктовая модель строится только через `Secret` + `AuthProfile`.
|
||
|
||
Для `Operations` и `Wizard` дополнительный уровень контрактной детализации закреплен в:
|
||
|
||
- `docs/operations-workspace-contracts.md`
|