552 lines
13 KiB
Markdown
552 lines
13 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`
|
||
- `AgentKey`
|
||
- `InvocationLog`
|
||
- `UsageRollup`
|
||
|
||
### 2.6. YAML как формат обмена конфигурацией
|
||
|
||
Помимо канонической JSON-модели система поддерживает импорт и экспорт конфигураций в `YAML`.
|
||
|
||
### 2.7. Streaming operation обязана быть bounded
|
||
|
||
Если операция использует upstream streaming, она должна работать в одном из execution modes:
|
||
|
||
- `window`
|
||
- `session`
|
||
- `async_job`
|
||
|
||
Бесконечный passthrough stream не является допустимой моделью `Operation`.
|
||
|
||
### 2.8. Execution model важнее transport-specific особенностей
|
||
|
||
Каждая операция в системе определяется двумя измерениями:
|
||
|
||
- `protocol`
|
||
- `execution_mode`
|
||
|
||
Это позволяет описывать:
|
||
|
||
- REST unary;
|
||
- REST SSE window;
|
||
- gRPC server-stream session;
|
||
- WebSocket event feed;
|
||
- SOAP request-response;
|
||
|
||
в рамках одной общей модели runtime и MCP publishing.
|
||
|
||
### 2.9. Уровень защиты задается операцией
|
||
|
||
Обязательная политика машинного доступа задается на уровне `Operation`, а не на уровне `Agent`.
|
||
|
||
Допустимые значения:
|
||
|
||
- `standard`
|
||
- `elevated`
|
||
- `strict`
|
||
|
||
Принцип:
|
||
|
||
- если операция требует более строгий режим доступа, агент не может его ослабить;
|
||
- один агент может публиковать операции с разной критичностью данных;
|
||
- `mcp-server` сравнивает тип представленного credential с требуемым `security_level`.
|
||
|
||
## 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`
|
||
- `security_level`
|
||
- `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. `AgentKey`
|
||
|
||
Отдельная сущность для машинного доступа к инструментам одного конкретного AI-агента.
|
||
|
||
Поля:
|
||
|
||
- `id`
|
||
- `workspace_id`
|
||
- `agent_id`
|
||
- `name`
|
||
- `prefix`
|
||
- `scopes`
|
||
- `status`
|
||
- `created_at`
|
||
- `last_used_at`
|
||
- `expires_at`
|
||
|
||
Секрет:
|
||
|
||
- полное значение ключа показывается только один раз при создании;
|
||
- в persistent storage сохраняется только `secret_hash`;
|
||
- ключ не должен использоваться как основной рабочий токен вызова в целевой защищенной модели;
|
||
- ключ используется для выпуска короткоживущего токена доступа.
|
||
|
||
### 3.10. `IssuedAgentToken`
|
||
|
||
Учет выданных машинных токенов для вызова MCP-инструментов.
|
||
|
||
Поля:
|
||
|
||
- `id`
|
||
- `workspace_id`
|
||
- `agent_id`
|
||
- `agent_key_id`
|
||
- `platform_client_id`
|
||
- `token_kind`
|
||
- `status`
|
||
- `scopes`
|
||
- `max_uses`
|
||
- `used_count`
|
||
- `cnf_jkt`
|
||
- `expires_at`
|
||
- `created_at`
|
||
- `used_at`
|
||
|
||
Принцип:
|
||
|
||
- токен живет ограниченное время;
|
||
- токен может быть одноразовым;
|
||
- токен может быть привязан к ключевой паре клиента;
|
||
- токен выражает более узкие права, чем длинноживущий ключ агента.
|
||
|
||
### 3.11. `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`
|
||
|
||
### 4.4. `WebSocketTarget`
|
||
|
||
- `kind`
|
||
- `url`
|
||
- `subprotocols`
|
||
- `subscribe_message_template`
|
||
- `unsubscribe_message_template`
|
||
- `static_headers`
|
||
|
||
### 4.5. `SoapTarget`
|
||
|
||
- `kind`
|
||
- `wsdl_ref`
|
||
- `service_name`
|
||
- `port_name`
|
||
- `operation_name`
|
||
- `endpoint_override`
|
||
- `soap_version`
|
||
- `soap_action`
|
||
- `binding_style`
|
||
- `headers`
|
||
- `fault_contract`
|
||
- `metadata`
|
||
|
||
## 5. `ProtocolOptions`
|
||
|
||
`ProtocolOptions` хранит protocol-specific runtime tuning, который не должен смешиваться с общей `ExecutionConfig`.
|
||
|
||
### 5.1. `GrpcProtocolOptions`
|
||
|
||
- `use_tls`
|
||
|
||
### 5.2. `WebsocketProtocolOptions`
|
||
|
||
- `heartbeat_interval_ms`
|
||
- `reconnect_max_attempts`
|
||
- `reconnect_backoff_ms`
|
||
|
||
### 5.3. `SoapProtocolOptions`
|
||
|
||
- `use_tls`
|
||
- `validate_certificate`
|
||
- `ws_security_profile`
|
||
|
||
## 6. XML normalization model
|
||
|
||
Для SOAP/XSD-пайплайна `crank-schema` теперь фиксирует XML-origin metadata отдельными code-level types:
|
||
|
||
- `XmlNodeKind`
|
||
- `XmlQualifiedName`
|
||
- `XmlSchemaBinding`
|
||
|
||
Они описывают:
|
||
|
||
- является ли узел element / attribute / text;
|
||
- локальное имя и namespace;
|
||
- repeated / nillable semantics.
|
||
|
||
## 7. `Schema`
|
||
|
||
`Schema` - нормализованное описание входа или выхода.
|
||
|
||
Поддерживаются:
|
||
|
||
- скалярные поля;
|
||
- вложенные объекты;
|
||
- массивы;
|
||
- enum;
|
||
- nullable-поля;
|
||
- `oneof` для protobuf.
|
||
|
||
## 8. Принцип совместимости
|
||
|
||
Если UI требует сущность, которой нет в текущем backend, эта сущность должна быть сначала явно добавлена в эту модель данных, а уже потом в код и БД.
|
||
|
||
Для upstream credentials это означает:
|
||
|
||
- placeholder-строки вида `${secrets.API_KEY}` не считаются реальной моделью данных;
|
||
- рабочая продуктовая модель строится только через `Secret` + `AuthProfile`.
|
||
|
||
Для `Operations` и `Wizard` дополнительный уровень контрактной детализации закреплен в:
|
||
|
||
- `docs/operations-workspace-contracts.md`
|