Skip to content

База данных и миграции ​

Система использует версионированные миграции для поддержки структуры базы данных, при этом для PostgreSQL и SQLite используются отдельные каталоги миграций. Миграции могут выполняться автоматически при запуске приложения или вручную с помощью скриптов; при добавлении новых полей или таблиц необходимо поддерживать обе ветки.

Поддерживаемые базы данных ​

Основное приложение подключается к базе данных через GORM, драйвер определяется переменной окружения DB_DRIVER. Оператор switch в функции initDatabase() в файле internal/container/container.go принимает только два значения:

DB_DRIVERОписание
postgresСтандартный режим. Поддерживает как нативный PostgreSQL (+pgvector), так и ParadeDB (ветка PostgreSQL со встроенными pg_search/BM25, официальный образ compose по умолчанию paradedb/paradedb:v0.25.11-pg17). DSN GORM формируется из DB_HOST/DB_PORT/DB_USER/DB_PASSWORD/DB_NAME, принудительно устанавливаются sslmode=disable и TimeZone=UTC
sqliteLite-режим. Путь берется из DB_PATH (по умолчанию ./data/weknora.db), в DSN добавляются параметры _journal_mode=WAL&_busy_timeout=5000&_foreign_keys=on, также загружается расширение sqlite-vec (sqlite_vec.Auto()) для векторного поиска
Другие значенияВыдает ошибку unsupported database driver

MySQL не является основным вариантом базы данных: драйвер go-sql-driver/mysql в go.mod используется для регистрации драйвера индексации Doris (протокол MySQL, database/sql, см. комментарии в импортах container.go). Файл migrations/mysql/00-init-db.sql является одноразовым скриптом инициализации для 10 ключевых таблиц (tenants/models/knowledge_bases/knowledges/sessions/messages/message_suggestion_sets/message_suggestion_events/chunks/chunk_revisions), на который не ссылается никакой код Go или скрипты. Он не включен в процесс запуска приложения и может рассматриваться как устаревший или предназначенный для внешней инициализации.

Механизм индексации (хранение векторных индексов/ключевых слов) отделен от основной базы данных и управляется через RETRIEVE_DRIVER (postgres / elasticsearch / qdrant / milvus / sqlite и др., подробности см. в «Руководстве по точкам расширения»). Если RETRIEVE_DRIVER не содержит postgres, в DSN миграции добавляется options=-c app.skip_embedding=true, что позволяет пропустить миграции для таблицы embeddings с помощью этого условия GUC.

Структура каталогов миграций ​

text
migrations/
├── versioned/     # Версионированные миграции для PostgreSQL/ParadeDB: версии с 000000 по 000118 (всего 119 версий, 238 файлов .up/.down.sql)
├── sqlite/        # Миграции для SQLite: 000000_init (полная схема) + версии 000001-000036
├── paradedb/      # Дополнительные скрипты для ParadeDB: 00-init-db.sql, 01-migrate-to-paradedb.sql (переход существующей базы)
└── mysql/         # 00-init-db.sql, устаревший скрипт инициализации MySQL (не используется в коде)
  • Каталог versioned/ для PostgreSQL содержит миграции от 000000_init до 000118_mcp_oauth_timestamp_tz;
  • В sqlite/ версия 000000_init используется как полная инициализация (учтены диалектные различия: JSONB→TEXT, SERIAL→AUTOINCREMENT и т.д.), за ней следуют инкрементальные версии (на текущий момент до 000036_messages_attachment_lookup), которые также последовательно выполняются через golang-migrate;
  • Расширения uuid-ossp, vector, pg_trgm, pg_search и другие создаются при выполнении миграций versioned/ (000000, 000002); BM25-индекс использует китайский токенизатор Lindera и создается для поля embeddings.content;
  • Скрипт paradedb/00-init-db.sql содержит как создание расширений, так и операторы создания таблиц для старых версий. Он не вызывается через compose или код. Не используйте его в качестве скрипта initdb для базы данных: создание старых таблиц приведет к ошибке при выполнении 000000_init.

Обзор истории миграций versioned/ (по темам) ​

Диапазон версийТемаКлючевые таблицы/поля
000000Инициализация ядраtenants, models, knowledge_bases, knowledges, chunks, sessions, messages
000001Аутентификация + Agent + MCPusers, auth_tokens, custom_agents, mcp_services, knowledge_tags
000002-000011Векторы/Поискembeddings (HNSW + BM25, под управлением app.skip_embedding), chunks.flags, seq_id, индекс ParadeDB BM25
000012-000018Сотрудничество между тенантамиorganizations, organization_members, kb_shares, agent_shares, organization_join_requests
000019-000028Сообщения/IMРасширение messages (images, rendered_content, agent_duration_ms), im_channels, im_channel_sessions
000029-000036Абстракция источников данных и векторных хранилищdata_sources, sync_logs, web_search_providers, vector_stores, конфигурация asr_config/vector_store_id в базе знаний
000037-000041Wiki и очереди задачwiki_pages, wiki_folders, wiki_page_issues, wiki_log_entries (удалено в 000077), task_pending_ops, task_dead_letters
000042-000054RBAC / Аудит / Приглашения / Настройки системыmcp_tool_approvals, tenant_members, audit_logs, organization_tenant_members (переименовано из organization_members), user_resource_favorites, tenant_invitations, user_kb_pins, system_settings и users.is_system_admin (000053), поля ссылок приглашений tenant_invitations.token/accepted_count (000054)
000055-000060Конвейер обработки и каналы эмбеддингаknowledge_processing_spans, knowledges.pending_subtasks_count (000056, статус finalizing), embed_channels, индекс HNSW 1024 измерений
000061-000067Иерархия Wiki / OAuth / Мультитеги документов / Идентификаторы принципалов / Предложения вопросовИерархия wiki_pages, mcp_oauth_clients, mcp_oauth_tokens, knowledge_tag_relations, поля идентификаторов (000064: tenants.api_principal_config, mcp_oauth_tokens.principal_type/principal_id), tenant_api_keys (000065, удаление tenants.api_key), message_suggestion_sets, message_suggestion_events
000068-000074Хранилища/Ресурсы/Временные документыstorage_backends, resources, resource_bindings, resource_access_grants, temporary_documents, API key на уровне платформы, продление срока действия OAuth
000075-000076История версий Wiki и индексацияwiki_page_revisions, wiki_pages.last_edit_source/last_editor_id, префиксный индекс knowledges.metadata->>'external_id'
000077Удаление логов операций WikiУдаление wiki_log_entries и исторических страниц page_type = 'log'; изменения Wiki записываются в ленту активности базы знаний
000078Редактирование блоков и пользовательские метаданныеДобавление в chunks полей source_content/content_revision/index_status/last_editor_id/context_header, новая таблица chunk_revisions, поле custom_metadata в knowledges
000079Дерево папок базы знанийДобавление folder_path в knowledges с обратной миграцией путей (ранее путь был в file_name), индекс (tenant_id, knowledge_base_id, folder_path)

Миграции после 000080 ​

ВерсияИзменение
000080knowledge_bases.auto_tag_config
000081messages.artifacts, сохранение сгенерированных файлов (с 000103 перенесено в message_artifacts)
000082tenant_sandbox_configs, управление несколькими бэкендами и сроками смены конфигурации
000083sessions.sandbox_config_id
000084Шесть таблиц личной памяти, tenants.memory_config, messages.used_memories
000085messages.usage
000086tenant_skills, tenant_skill_snapshots, учет установки и снимков
000087install_session_id / install_message_id навыков, логи установки
000088planned_name снимков, запись имени плана перед созданием
000089envs навыков, tenant_user_env_vars
000090tenant_skill_catalog; tenant_skills.catalog_id, обратная миграция существующих установок
000091mcp_tool_approvals.enabled, по умолчанию true
000092mcp_services.usage_instructions; таблица mcp_metadata, снимок инструментов сервисов MCP
000093browser_devices, browser_pairings, browser_task_interruptions, авторизация и сопряжение устройств для браузера
000094memory_subjects.extraction_state, memory_items.replaces_id, таблица memory_extraction_sessions (прогресс извлечения по сессиям); исправление ошибок ранней замены выводов в старых версиях
000095memory_item_embeddings.embedding (halfvec, добавляется только при наличии расширения vector) и поисковые индексы
000096Миграция данных: канал DingTalk изменен с режима webhook на websocket (Stream). Необходимо включить режим Stream в консоли разработчика DingTalk; down — пустая операция
000097Форк сессий: sessions.parent_session_id / forked_from_message_id / fork_bootstrap, messages.sandbox_checkpoint
000098Таблица fork_snapshot_leases, запись снимков песочницы перед форком сессии для последующей очистки
000099Обновление pg_search с версий 0.22.2–0.22.5 до 0.22.6; пропускается, если образ не предоставляет 0.22.6, установлена другая версия или используется app.skip_embedding=true
000100Удаление трех индексов в chunks, замедляющих запись: idx_chunks_chunk_type, idx_chunks_content_hash, idx_chunks_tenant_kg
000101knowledges.profile (портрет документа), knowledge_bases.profile_config / generated_profile (AI-описание базы знаний)
000102Таблица mcp_endpoints, эндпоинты MCP, опубликованные рабочим пространством
000103Таблица message_artifacts, обратная миграция из messages.artifacts; далее только новая таблица. Строки с данными в старой колонке устанавливаются в NULL (колонка сохранена для совместимости при обновлении); нераспознанные file_size/created_at сбрасываются до 0 и времени создания сообщения, миграция не прерывается. Down-миграция возвращает данные в старую колонку
000104tenant_skills.served, запись версии, предоставляемой в образе при установке или сбое
000105messages.context_checkpoint, сжатая сводка контекста Agent
000106Индекс для сообщений (session_id, created_at DESC, id DESC) с именем idx_messages_session_created_id, создается через CREATE INDEX CONCURRENTLY без блокировки записи; прерывание создания оставляет INVALID-индекс, перед перезапуском необходимо DROP INDEX
000107message_artifacts.deleted_at, сохранение удаленных файлов как удаленных строк (заглушки)
000108sessions.sandbox_config_tenant_id (BIGINT, по умолчанию 0), принадлежность конфигурации песочницы; обратная миграция для сессий, использующих общие Agent
000109sessions.host_workspace_dir (VARCHAR(1024)), каталог проекта песочницы для Lite-версии, не меняется после создания
000110im_channels.locale (VARCHAR(16), по умолчанию пустая строка), фиксированный язык ответа канала IM; пустая строка использует значение по умолчанию деплоя
000111model_catalog_configs, административные переопределения каталога и последние версии истории
000112Индексы embeddings по knowledge_id и chunk_id; обычное создание индекса, для больших таблиц перед обновлением можно предварительно создать одноименный индекс CONCURRENTLY, чтобы снизить блокировки
000113Таблица-проекция изображений chunk_images и триггеры синхронизации; данные галереи заполняются из существующего image_info
000114chunks.source_locators, информация для перехода к первоисточнику по цитате
000115Обновление подходящих установленных расширений pg_search / vector до 0.25.11 / 0.8.4; для этого необходимо заранее переключить сопутствующий образ базы данных
000116Запись пустых массивов allowed_tools исторических агентов интеллектуального рассуждения в виде явного списка инструментов по умолчанию
000117GIN-индекс эффективных пользовательских сообщений messages.attachments для запросов сохранения ссылок на вложения
000118Временные метки токенов и клиентов MCP OAuth переводятся в timestamptz; старые литеральные значения преобразуются как UTC

Версии SQLite развиваются независимо и не могут напрямую сопоставляться с номерами PostgreSQL:

Версия SQLiteИзменение
000001–000002Удаление логов Wiki, пути папок
000003–000004Автоматическая тегизация, долгосрочная память
000005Вложения сообщений и поля приглашений
000006–000008Задачи/dead-letters, администрирование системы, spans обработки/подсчет подзадач
000009Флаги памяти Embed; поле не используется в интерфейсе текущего канала
000010–000011Мультитеги, модель principal
000012–000013usage сообщений, enabled для инструментов MCP (соответствует 000085, 000091)
000014–000016Авторизация браузера, консистентность памяти, векторный индекс памяти (соответствует 000093–000095; в SQLite нет векторной колонки, сортировка в приложении)
000017Канал DingTalk переведен на websocket (соответствует 000096)
000018–000019Форк сессий, аренды снимков форка (соответствует 000097–000098)
000020–000022Удаление индексов chunks, портрет знаний, эндпоинты MCP (соответствует 000100–000102)
000023Таблица message_artifacts, миграция и очистка старых колонок (соответствует 000103)
000024–000026messages.context_checkpoint, индекс времени сессий сообщений, deleted_at message_artifacts (соответствует 000105–000107)
000027sessions.sandbox_config_tenant_id (соответствует 000108; в Lite нет общих пространств, обратная миграция не выполняется)
000028Добавление tenant_skills, tenant_skill_snapshots, tenant_skill_catalog, tenant_user_env_vars (соответствует 000086–000090 и 000104). Ранее Lite не имел этих таблиц, что вызывало ошибку no such table
000029–000030sessions.host_workspace_dir, im_channels.locale (соответствует 000109–000110)
000031model_catalog_configs (соответствует 000111)
000032–000033chunk_images и source_locators (соответствует 000113–000114)
000034Достраивание MCP usage_instructions и mcp_metadata (соответствует 000092)
000035Явные инструменты по умолчанию для агентов (соответствует 000116)
000036Индекс для запросов по эффективным сообщениям с вложениями (соответствует 000117; в SQLite обычный индекс)

Версии PostgreSQL 000099, 000112, 000115, 000118 не имеют соответствующих версий в SQLite.

Базовая схема и последующие инкрементальные миграции совместно определяют итоговое состояние новых и существующих баз данных; нельзя судить о наличии таблицы в Lite только по имени файла миграции.

Итоговая структура таблиц ​

Ниже представлена итоговая структура после наложения всех up-миграций (ALTER-команды для ранних таблиц объединены). Большинство бизнес-таблиц имеют поля created_at / updated_at, многие включают deleted_at (мягкое удаление GORM), они здесь не перечисляются.

Тенанты и пользователи ​

ТаблицаНазначениеКлючевые поля
tenantsТенант (рабочее пространство), корень мульти-тенантовid (SERIAL, от 10000), name, retriever_engines (JSONB), status, storage_quota/storage_used, agent_config/context_config/conversation_config/web_search_config/credentials/api_principal_config/memory_config (JSONB), default_storage_backend_id. Колонка api_key удалена в 000065
usersЗарегистрированный пользовательid (UUID), username (уник.), email (уник.), password_hash, tenant_id (FK→tenants), is_active, can_access_all_tenants (кросс-тенанты), is_system_admin (системный админ, 000053), preferences (JSON)
auth_tokensТокены входаid, user_id (FK→users), token, token_type (access/refresh), expires_at (TIMESTAMPTZ, с 000072), is_revoked
tenant_membersRBAC-членство в тенантеuser_id+tenant_id (уник. с учетом мягкого удаления), role (owner/admin/contributor/viewer), status, invited_by, joined_at
tenant_invitationsПриглашенияtenant_id, invitee_user_id, role, status (pending/accepted/rejected), expires_at; уникальность pending; token (ссылка)/accepted_count (000054)
tenant_api_keysAPI Key тенанта/платформыtenant_id (NULL для платформы), scope_type (tenant/platform), key_hash (уник.), full_access, knowledge_base_ids, capabilities, expires_at/revoked_at
user_kb_pinsЗакрепление базы знаний пользователемPK (tenant_id,user_id,kb_id) + pinned_at
user_resource_favoritesИзбранное пользователяPK (user_id,tenant_id,resource_type,resource_id)
system_settingsНастройки системы (000053)key, value/value_type, category, is_secret, requires_restart, last_modified_by
audit_logsАудит-логи (000044)tenant_id, actor_user_id/actor_role, action, target_type/target_id/target_user_id, request_path/request_method, outcome (success/denied), scope_type/scope_id, details (JSONB)

Добавленная в v0.8.3 таблица model_catalog_configs хранит переопределения каталога на уровне деплоя (одна строка с id=1), версии и историю; chunk_images — запросимая проекция chunks.image_info, триггеры поддерживают ее в соответствии с записью, удалением и изменением состояния блоков. Сохранение вложений определяется ссылками на них из действительных сообщений сессии; expires_at очищает только те просроченные вложения, на которые больше никто не ссылается.

Модели и базы знаний ​

ТаблицаНазначениеКлючевые поля
modelsКонфигурация модели AI (LLM/эмбеддинг/реранк и т.д.)id, tenant_id (FK→tenants), name/display_name, type (KnowledgeQA / Embedding / Rerank / VLLM / ASR), source, parameters (JSONB), is_default, is_builtin, managed_by, status
knowledge_basesБаза знанийid (UUID), tenant_id, name, type (document/faq/wiki), chunking_config/image_processing_config/vlm_config/faq_config/asr_config/wiki_config/indexing_strategy/auto_tag_config/profile_config/generated_profile (JSONB), embedding_model_id/summary_model_id (FK→models), vector_store_id (FK→vector_stores), storage_backend_id (FK→storage_backends), creator_id (FK→users), is_temporary, activity_scope
knowledgesЗнание (документы/веб-страницы/FAQ)id, tenant_id, knowledge_base_id (FK), type, title, source (VARCHAR(2048)), parse_status (pending/processing/finalizing/completed/failed/cancelled/deleting), pending_subtasks_count (000056), enable_status, file_name/file_type/file_size/file_path/file_hash, metadata, custom_metadata (JSONB, с 000078), folder_path (дерево папок, 000079), summary_status, profile (JSONB, портрет документа: gist/темы/тип/типовые вопросы, 000101), channel, processed_at/error_message. Без колонки tag_id — теги с 000063 связаны через knowledge_tag_relations
chunksБлок (единица поиска)id, tenant_id, knowledge_base_id, knowledge_id (FK), content, source_content, content_revision, index_status, last_editor_id, context_header, chunk_index, start_at/end_at, pre_chunk_id/next_chunk_id, parent_chunk_id, chunk_type, image_info/video_info, relation_chunks/indirect_relation_chunks (JSONB), source_locators (JSONB, 000114), is_enabled, flags, status, content_hash, seq_id, tag_id
chunk_revisionsВерсии истории блоков (000078)id, tenant_id, knowledge_base_id, knowledge_id, chunk_id+revision (уник. индекс), content, is_enabled, editor_id, edit_source, edited_at
embeddingsИндекс векторов + BM25 (для Postgres/ParadeDB, под app.skip_embedding)id, source_id+source_type (уник.), chunk_id/knowledge_id/knowledge_base_id, content (BM25), dimension, embedding (halfvec), is_enabled, tag_id
knowledge_tagsТеги знаний (классификация FAQ и т.д.)id, tenant_id, knowledge_base_id, name, seq_id
knowledge_tag_relationsСвязь документа с тегами (000063)PK (knowledge_id,tag_id) + created_at; индексы с двух сторон
vector_storesКонфигурация подключения внешней векторной БД (000032)id, tenant_id, name (уник. в тенанте), engine_type, connection_config/index_config (JSONB)

Сессии и сообщения ​

ТаблицаНазначениеКлючевые поля
sessionsСессия (контекст диалога и снимки параметров поиска)id, tenant_id, title, knowledge_base_id, agent_id (FK→custom_agents), user_id, max_rounds, enable_rewrite, fallback_strategy/fallback_response, keyword_threshold/vector_threshold, embedding_top_k/rerank_top_k/rerank_threshold, rerank_model_id/summary_model_id, agent_config/context_config (JSONB), sandbox_config_id/sandbox_config_tenant_id (конфигурация песочницы и принадлежащее ей пространство; 0 — собственное пространство сессии, 000108), parent_session_id/forked_from_message_id/fork_bootstrap (форк сессии, 000097; без внешних ключей), host_workspace_dir (каталог проекта настольной версии Lite, 000109)
messagesСообщениеid, request_id, session_id (FK), role, content/rendered_content, knowledge_references (JSONB, ссылки), agent_steps (JSONB, ход рассуждений Agent), mentioned_items/images (JSONB), is_completed/is_fallback, channel (канал web/IM), agent_id+agent_tenant_id, model_id, knowledge_id, agent_duration_ms, execution_context, attachments, used_memories/usage (JSONB), sandbox_checkpoint (снимок песочницы в точке форка, 000097), context_checkpoint (сжатая сводка контекста Agent, 000105). Колонка artifacts с 000103 больше не читается и не записывается и очищена (сохранена только для отката и rolling-upgrade); сгенерированные файлы хранятся в message_artifacts
message_artifactsФайлы, сгенерированные навыками, по одной строке на файл (000103)session_id, message_id, position (порядковый номер внутри сообщения, соответствует index в API скачивания; уникален в паре с message_id), url (ссылка на хранилище или resource://, клиенту не возвращается), file_name/file_type/file_size, content_hash, source_path (один и тот же путь в одной сессии считается несколькими версиями одного файла), mod_time (текст RFC 3339, точность до наносекунд для сравнения сборщиком), created_at, deleted_at (000107: после удаления файла пользователем строка-заглушка сохраняется, чтобы position оставался стабильным и файл не собирался повторно). При мягком удалении сообщения его файлы тоже скрываются
message_suggestion_setsНабор предложений (000067)tenant_id, session_id, assistant_message_id, placement (starter/follow_up), config_hash+locale (кеш-ключ, уникален), status, questions (JSONB), статистика токенов/задержки, lease_until
message_suggestion_eventsСобытия показов/кликов предложенийsuggestion_set_id (FK, CASCADE), question_id, event_type, actor_id
temporary_documentsВременные документы сессии (000070)tenant_id, session_id, resource_ref, file_name/file_type/file_size, status (uploaded/processing/ready/failed), content, chunks (JSONB), expires_at

Агенты и MCP ​

ТаблицаНазначениеКлючевые поля
custom_agentsПользовательский агентPK (id,tenant_id), name, is_builtin, created_by (FK→users), runnable_by_viewer, config (JSONB)
mcp_servicesКонфигурация сервиса MCPid, tenant_id, name, enabled, transport_type (stdio/sse/…), url/headers/auth_config/advanced_config/stdio_config/env_vars (JSONB), is_builtin, usage_instructions (000092)
mcp_metadataСнимок каталога инструментов MCP (000092)PK (tenant_id,service_id,principal), config_fingerprint, tools (JSONB), instructions, server_name/server_version/server_description, synced_at
mcp_endpointsЭндпоинты MCP (000102)tenant_id, name/description, enabled, token_hash (SHA-256), knowledge_base_ids (JSONB), tools (JSONB), default_agent_id, rate_limit_per_minute, last_used_at
mcp_tool_approvalsПолитика одобрения инструментов MCP (000042)(tenant_id,service_id,tool_name) уник., require_approval, enabled (default true)
mcp_oauth_clientsКлиент OAuth MCP (000062)(tenant_id,service_id) уник., client_id/client_secret/redirect_uri
mcp_oauth_tokensТокен OAuth MCP(tenant_id,principal_type,principal_id,service_id) уник., access_token/refresh_token, expires_at, refresh_lease_id/refresh_lease_until (000074)

Подключение браузера ​

ТаблицаНазначение и ключевые поля
browser_devicesАвторизованные расширения браузера (000093), лейбл, token_hash, expires_at/renew_after, last_seen_at, revoked_at, владелец аренды
browser_pairingsХеш токена сопряжения с истечением
browser_task_interruptionsПрерванные задачи для восстановления пользователем (scope_key + session)

Песочница и навыки ​

ТаблицаНазначение и ключевые поля
tenant_sandbox_configsid, tenant_id, name, sandbox_type, config (JSONB), cordoned_at
tenant_skill_catalogКаталог навыков тенанта; name/version/description/instructions, bundle_ref/bundle_sha256
tenant_skillsУстановка; enabled/status/error, installed_snapshot_id, installing_since, install_session_id/install_message_id, envs, served (000104)
tenant_skill_snapshotssandbox_config_id, skill_id, snapshot_id/parent_snapshot_id, generation, trigger/state, planned_name, superseded_at
tenant_user_env_varstenant_id, principal_type/principal_id, sandbox_config_id, skill_id, name, value (шифр)
fork_snapshot_leasessnapshot_id (PK), tenant_id, sandbox_config_id, created_at

Долгосрочная память ​

ТаблицаНазначение и ключевые поля
memory_subjects(tenant_id,subject_id) уник.; enabled, block_text, item_count, extraction_state (000094)
memory_extraction_sessionsPK (tenant_id,subject_id,session_id); прогресс извлечения
memory_itemskind/content/topic/normalized_key, importance/origin/status, сессия/сообщение, valid_from/invalid_at/expires_at, superseded_by, replaces_id (000094)
memory_tombstonesУдаленные/отклоненные темы и хеши содержимого
memory_topic_statstopic/aliases, hits, last_seen_at/promoted_at
memory_doc_affinityknowledge_id/knowledge_base_id/title, hits/last_used_at
memory_item_embeddingsitem_id, model_id, dims, vector

Организации (кросс-тенанты) ​

ТаблицаНазначениеКлючевые поля
organizationsОрганизация (000012)id, name, owner_id (FK), owner_tenant_id, invite_code (уник.), require_approval, searchable, member_limit
organization_tenant_membersЧлены организации (000045)(organization_id,tenant_id) уник., role, representative_user_id
organization_join_requestsЗаявки на вступлениеorganization_id, user_id, status (pending), requested_role, request_type (join/upgrade)
kb_sharesШаринг базы знаний(knowledge_base_id,organization_id) уник., source_tenant_id, permission
agent_sharesШаринг АгентаFK (agent_id,source_tenant_id), organization_id, permission
tenant_disabled_shared_agentsОтключенные шар-агентыPK (tenant_id,agent_id,source_tenant_id)

Wiki ​

ТаблицаНазначениеКлючевые поля
wiki_pagesWiki (000037)id, tenant_id, knowledge_base_id, slug, title, page_type, status, content/summary, иерархия (000061: parent_slug, folder_id и др.), source_refs/chunk_refs/in_links/out_links (JSONB), version
wiki_foldersПапки Wikiknowledge_base_id, parent_id, name, path (материализованный путь), depth, sort_order
wiki_page_issuesПроблемы Wikiknowledge_base_id, slug, issue_type, description, suspected_knowledge_ids, status, reported_by
wiki_page_revisionsИстория Wiki (000075)page_id+version (уник. индекс), снапшот данных, edit_source, editor_id, edited_at

Источники / Каналы / Поиск ​

ТаблицаНазначениеКлючевые поля
data_sourcesВнешние источники (Feishu/Notion/Confluence и др., 000029)id, tenant_id, knowledge_base_id, type, config (JSONB), sync_schedule, sync_mode, conflict_strategy, sync_deletions, last_sync_at/last_sync_cursor/last_sync_result
sync_logsЛоги синхронизацииdata_source_id (FK), status, started_at/finished_at, статистика, error_message
im_channelsКонфигурация IMtenant_id, platform, agent_id, knowledge_base_id, mode, output_mode, session_mode, bot_identity, credentials, locale (000110)
im_channel_sessionsМаппинг IM ↔ сессияim_channel_id, session_id, agent_id, идентификатор пользователя/сессии платформы
embed_channelsВстраиваемые веб-виджеты (000060)tenant_id, agent_id, настройки токена/домена
web_search_providersНастройки поиска (000030)id, tenant_id, name, provider, parameters (JSONB), is_default

Хранилище / Ресурсы / Задачи / Наблюдаемость ​

ТаблицаНазначениеКлючевые поля
storage_backendsКонфигурация объектного хранилища (000068)id, tenant_id, name, provider, config (JSONB), source, legacy_alias
resourcesРеестр ресурсов (000069)id, handle, tenant_id, storage_backend_id, provider, physical_path, location_hash, mime_type/original_name/size/content_hash, lifecycle (persistent/temporary)+expires_at, state
resource_bindingsПривязка ресурсов (сообщения/знания/сессии)(resource_id,owner_type,owner_id,relation) уник.
resource_access_grantsТокены доступаtoken_hash (уник.), resource_id, access_scope, expires_at/revoked_at
task_pending_opsОчередь задач (000041)tenant_id, task_type, scope/scope_id, op, dedup_key, payload (JSONB), fail_count, enqueued_at/claimed_at
task_dead_lettersАрхив задач (dead letter)task_type, scope/scope_id/related_id, payload, last_error, fail_count, failed_at
knowledge_processing_spansТрассировка обработки документов (000055)(knowledge_id,attempt,span_id) уник., parent_span_id, name, kind, status, input/output/metadata (JSONB), error_code/error_message, duration_ms
schema_migrationsСтатус golang-migrateversion, dirty

ER-диаграмма (основные таблицы) ​

Механизм миграций (golang-migrate) ​

Используется инструмент golang-migrate/migrate v4 (go.mod: github.com/golang-migrate/migrate/v4 v4.19.1), состояние хранится в таблице schema_migrations (version + dirty). Существуют два пути выполнения:

Автоматическая миграция при запуске (по умолчанию) ​

Функция initDatabase() в internal/container/container.go:

  • Если AUTO_MIGRATE != "false" (включено по умолчанию), вызывается database.RunMigrationsWithOptions(migrateDSN, opts);
  • Если AUTO_RECOVER_DIRTY != "false" (включено по умолчанию), устанавливается MigrationOptions.AutoRecoverDirty = true, для автоматической попытки восстановления при dirty-состоянии;
  • Ошибки миграции записываются только в лог Warn и не прерывают запуск (поскольку миграции могут управляться извне), поэтому при диагностике обязательно проверяйте логи запуска;
  • DSN миграции для postgres дополняется options=-c app.skip_embedding=<true|false> (зависит от того, содержит ли RETRIEVE_DRIVER строку postgres), что определяет, будут ли выполняться миграции для таблицы embeddings.

Логика выбора пути в internal/database/migration.go:

go
// internal/database/migration.go
migrationsPath := "file://migrations/versioned"
if strings.HasPrefix(dsn, "sqlite3://") {
    migrationsPath = "file://migrations/sqlite"
}

То есть postgres/ParadeDB используют migrations/versioned/, а SQLite — migrations/sqlite/.

Выполнение вручную: scripts/migrate.sh ​

scripts/migrate.sh является оберткой CLI migrate (цели Makefile migrate-* вызывают его):

  • Автоматически загружает .env из корня;
  • DSN берется приоритетно из DB_URL (при этом sslmode=require/prefer принудительно заменяется на disable), иначе собирается из DB_HOST/DB_PORT/DB_USER/DB_PASSWORD/DB_NAME (по умолчанию localhost:5432/postgres/aihelpme), пароль кодируется с помощью urllib.parse.quote (Python), чтобы поддержать спецсимволы;
  • Каталог миграций по умолчанию MIGRATIONS_DIR=migrations/versioned;
  • Если migrate не установлен, выводится подсказка: go install -tags 'postgres' github.com/golang-migrate/migrate/v4/cmd/migrate@latest.
bash
make migrate-up                    # Выполнить все ожидающие миграции
make migrate-down                  # Откат
make migrate-version               # Просмотр версии и флага dirty
make migrate-create name=add_xxx   # Создание следующего файла миграции add_xxx.up.sql / .down.sql
make migrate-force version=74      # Принудительная установка версии (восстановление из dirty)
make migrate-goto version=60       # Миграция/Откат до указанной версии

Как добавить миграцию ​

  1. Создание файлов: make migrate-create name=add_my_feature, создаются файлы следующего номера версии в migrations/versioned/ (перед созданием проверьте каталог, чтобы использовать следующий номер);
  2. Написание up SQL: учитывайте диалект PostgreSQL (JSONB, индексы, TIMESTAMP WITH TIME ZONE); при изменении таблицы embeddings используйте условную проверку app.skip_embedding (аналогично существующим миграциям: SELECT current_setting('app.skip_embedding', true)), чтобы обеспечить прохождение миграций для развертываний без postgres как движка индексации;
  3. Написание down SQL: миграция должна быть обратимой (drop column/table/index), иначе цепь откатов прервется;
  4. Синхронизация SQLite: добавьте инкрементальную миграцию с номером версии в migrations/sqlite/, дублируя изменения (с учетом диалекта: JSONB→TEXT, SERIAL→INTEGER AUTOINCREMENT, TIMESTAMPTZ→DATETIME и т.д.). Существующие базы Lite не перезапускают 000000_init, изменение только базовой схемы приведет к пропуску колонок/таблиц. Также обновите список таблиц/колонок и ожидаемую версию в internal/database/migration_sqlite_versioned_schema_test.go;
  5. Синхронизация моделей GORM: добавьте поля в соответствующие структуры в internal/types/ (GORM используется только для маппинга ORM, AutoMigrate для создания таблиц в production не используется, структура управляется SQL-миграциями);
  6. Проверка: make migrate-up → make migrate-down → make migrate-up для подтверждения обратимости; для SQLite запустите один раз с DB_DRIVER=sqlite для проверки скрипта инициализации Lite.

Устранение неполадок при миграциях ​

Порядок диагностики для production, проверки расширений и границы восстановления см. в разделе Устранение неполадок миграций БД. Команда force только изменяет маркер версии и не отменяет выполненный SQL; сначала сделайте бэкап и сверьтесь с фактической схемой БД, нельзя предполагать, что неудачная миграция была полностью отменена.

dirty state (наиболее часто) ​

Если миграция завершилась неудачей или процесс был убит, schema_migrations.dirty = true, последующие миграции будут отклонены.

bash
# 1. Проверка состояния
make migrate-version            # вывод вида "74 (dirty)"
# Или прямой запрос к таблице
# SELECT version, dirty FROM schema_migrations;

# 2. Остановка записи, бэкап, проверка схемы и места падения SQL

# 3. Выполнять только если схема соответствует версии 73 и миграция может быть повторена безопасно
# 73 — только пример, нельзя просто вычитать 1 из версии
make migrate-force version=73
make migrate-up

По умолчанию в приложении включен AUTO_RECOVER_DIRTY (container.go), при запуске система пытается восстановиться сама; если отключено (false), в логах будет совет использовать force.

Миграция "успешна", но таблица не создана ​

Проверьте логи запуска: неудачи автоматической миграции записываются как Warn (Database migration failed ... Continuing with application startup) и не останавливают процесс. Кроме того, объекты embeddings управляются через app.skip_embedding — если RETRIEVE_DRIVER не содержит postgres, отсутствие индексов embeddings является ожидаемым поведением.

Ошибки подключения из-за спецсимволов в пароле ​

Для CLI migrate требуется DSN в виде URL, пароли с @ # ! и другими спецсимволами должны быть URL-кодированы. scripts/migrate.sh и container.go уже обрабатывают это (используют Python quote и Go url.QueryEscape соответственно); если собираете DB_URL вручную, закодируйте его самостоятельно.

Различия ParadeDB / нативного Postgres ​

BM25-индексы (USING bm25, китайская токенизация Lindera) доступны только в ParadeDB; для нативного Postgres убедитесь, что соответствующие ветки миграции работают, либо используйте Elasticsearch. Инструкции по переходу существующих нативных БД Postgres на ParadeDB см. в migrations/paradedb/01-migrate-to-paradedb.sql.

Официальный образ — paradedb/paradedb:v0.25.11-pg17 (с v0.8.2 до этого использовался v0.22.6-pg17). Миграция 000115 при наличии на сервере соответствующих пакетов расширений обновляет установленные pg_search 0.22.0–0.25.10 до 0.25.11, а pgvector 0.8.0–0.8.3 — до 0.8.4; историческая миграция 000099 действует только пока используется образ v0.22.6-pg17. Другие ветки версий автоматически не обновляются и не понижаются. Каталог расширений обязан совпадать с версией образа, иначе вызов некоторых функций расширений обрушивает базу данных; ручное обновление описано в разделе Обновление существующих баз ParadeDB; на новом образе не выполняйте команду ALTER EXTENSION pg_search UPDATE TO '0.22.6'.

Конфликты файлов версий ​

Конфликт происходит, когда в разных ветках добавляется один и тот же номер версии: golang-migrate сортирует их по цифрам, и если в каталоге появляются дубликаты версий, он не загружается, и все развертывания падают с ошибкой миграции. При слиянии необходимо изменить номер своей миграции на следующий свободный (переименовать файлы up/down). internal/database/migration_versions_test.go загружает оба каталога versioned/ и sqlite/, конфликты выявляются в CI (000105 был перенумерован с 000104 именно по этой причине).

Версия v0.8.3 · MIT License