Skip to content

Возможности Wiki ​

Wiki упорядочивает материалы базы знаний в связанные друг с другом Markdown-страницы. После приёма документов модель извлекает такие сущности, как персоны, продукты и концепции, и генерирует страницы со ссылками на источники, что упрощает просмотр по темам.

Пользователи могут напрямую редактировать сгенерированные страницы, просматривать историю версий и откатывать их. Агент также может читать и поддерживать страницы через инструменты Wiki; область доступа по-прежнему определяется правами базы знаний.

Включение и просмотр Wiki ​

  1. Откройте базу знаний → включите Wiki в разделе «Стратегия индексации»;
  2. Загрузите документы (уже имеющиеся документы тоже будут включены, повторно загружать не нужно);
  3. Дождитесь генерации. Генерация Wiki выполняется асинхронно и при большом числе документов занимает время; в «хлебных крошках» базы знаний появляется подсказка «Индексируется»;
  4. По завершении откройте вкладку Wiki базы знаний для просмотра; на вкладке Граф видны связи между страницами.

Генерация Wiki увеличивает количество вызовов модели; объём зависит от размера документов и плотности извлечения. В конфигурации Wiki базы знаний можно выбрать focused, standard или exhaustive; подробности см. в разделе Гранулярность извлечения.

Просмотр графа Wiki ​

Граф организован группами по связанным страницам и показывает поясняющие темы, объединяющие содержимое группы. Узлы и заголовки не перекрываются, что упрощает поиск интересующего элемента в общей сети связей.

  • По клику на тему группы можно сфокусироваться на этих страницах, переключаться между другими группами с помощью меню групп и выходить из фокуса кнопкой «Вернуться к полному графу» или Esc.
  • Страницы без связей можно скрывать; для поиска нужной страницы доступны поиск и фильтр по типам страниц.
  • По клику на узел открывается страница, а по линиям — связанные страницы; страницы также соединены с сводными страницами документов-источников, что упрощает отслеживание материалов.

Эта страница показывает связи между страницами Wiki и отличается от графа знаний, который извлекает сущности и связи в настройках базы знаний.

Редактирование и откат страниц ​

После редактирования текста на странице Wiki можно просмотреть историю версий и сравнить изменения. История различает источники: автоматическая генерация, агент, ручное редактирование и откат. Откат создаёт новую версию на основе выбранного исторического содержимого, а номер версии продолжает увеличиваться.

Система в первую очередь удаляет старые автоматически сгенерированные версии: после достижения 50 исторических версий удаляются усекаемые снимки, а при достижении 200 удаляются все источники по жёсткому пределу. Содержимое, которое нужно хранить долго, следует архивировать отдельно. Версии, записанные до обновления, или уже удалённые версии не имеют снимков: при выборе такой версии панель истории показывает предупреждение вместо различий; при отсутствии базовой линии для сравнения сразу отображается полный текст выбранной версии. Сравнение и откат остальных версий не затрагиваются.

Просмотр изменений и состояния генерации ​

Записи операций создания, обновления, удаления и пакетной генерации страниц отображаются в разделе «База знаний → Настройки → Активность». Во время обработки документов можно проверить состояние индексации; после перезапуска сервиса система восстанавливает сохранённые ожидающие задачи Wiki. При удалении базы знаний её страницы Wiki, каталоги, проблемы и история версий также очищаются. Совместимость интерфейсов и механизмы восстановления см. в справочном разделе.

Справочник по версиям и работе ​

Ручное редактирование и история версий ​

Страницы Wiki поддерживают ручное редактирование и откат версий: пользователи могут исправлять сгенерированное содержимое и просматривать источник, автора и время каждой версии. Записи версий введены миграцией 000075.

Источник версии и автор ​

В wiki_pages есть два поля происхождения; last_edit_source помечает тип автора текущей версии:

edit_sourceЗначение
pipelineзаписано конвейером генерации Wiki (в унаследованных строках пустая строка трактуется как pipeline)
agentзаписано Agent через инструменты wiki_write_page / wiki_replace_text и т. п.
userизменено вручную в редакторе
revertверсия, созданная откатом

Сопутствующее поле last_editor_id фиксирует автора (пусто при записи фоновым конвейером). Интерфейс на основе этого показывает источник редактирования каждой версии.

Снимки и откат ​

  • Перед каждым перезаписыванием страницы старая версия целиком сохраняется в снимок в wiki_page_revisions (заголовок, текст, аннотация, тип страницы, состояние, псевдонимы, а также автор и время версии); текущая версия существует только в wiki_pages. Уникальный индекс по (page_id, version) вместе с ON CONFLICT DO NOTHING делает путь записи «сначала снимок, потом обновление» идемпотентным при повторных попытках;
  • GET /revisions/*slug возвращает список исторических версий (в обратном порядке, без текста, с текущим номером версии); с ?version=N возвращается полный текст этой версии для diff;
  • POST /revert принимает {slug, version} для отката. Откат создаёт новую версию на основе содержимого целевой версии, номер версии продолжает увеличиваться, а edit_source помечается как revert, поэтому сам откат тоже можно откатить. Откат к текущей версии возвращает 400 (обычно это значит, что фронтенд использует устаревший список истории).

Политика хранения истории ​

История версий очищается по двум уровням пределов (internal/types/wiki_page.go):

  • Мягкий предел 50 версий: удаляются только «усекаемые» снимки, то есть записанные pipeline и унаследованные пустые источники;
  • Жёсткий предел 200 версий: усекаются все независимо от автора, что гарантирует ограниченность хранилища даже для страниц, поддерживаемых исключительно вручную.

До достижения жёсткого предела система в первую очередь очищает автоматически сгенерированную историю, сохраняя записи ручного редактирования.

История операций (поток активности базы знаний) ​

Раньше Wiki вела отдельный журнал операций (таблица wiki_log_entries + эндпоинт GET /wiki/log + вкладка журнала в WikiBrowser). Эта лента дублировала поток активности базы знаний, поэтому была полностью удалена миграцией 000077_remove_wiki_log: таблица была удалена через DROP, унаследованные страницы с page_type = 'log' также удалены, а log больше не является допустимым типом страницы.

Теперь поток активности базы знаний — единственный источник истории операций:

  • по завершении пакета ingest wiki_ingest_batch.go суммирует число действий каждого типа в пакете и вызывает service.RecordWikiContentActivity(), записывая активность wiki_content_changed;
  • при ручном создании/обновлении/удалении страницы в WikiBrowser internal/handler/wiki_page.go так же проецирует manual_create / manual_update / manual_delete в поток активности;
  • записи активности попадают в систему журналов аудита (kb_activity.go → AuditLogService), их можно просмотреть в разделе «База знаний → Настройки → Активность», а политика хранения совпадает с другими журналами аудита (см. Наблюдаемость и аудит);
  • запись выполняется best-effort: сбой записи активности не приводит к сбою самого редактирования wiki.

Замечание по обновлению: если внешние интеграции по-прежнему вызывают GET /api/v1/knowledgebase/:kb_id/wiki/log, нужно перейти на интерфейс потока активности базы знаний GET /api/v1/knowledge-bases/:id/activity.

Восстановление после сбоев ​

internal/container/recover_pending_wiki_tasks.go при запуске сервиса закрывает пробелы, оставленные режимом Lite (внутрипроцессный SyncTaskExecutor) или прерыванием постановки в очередь Redis:

  1. Сканирует в сохранённой таблице task_pending_ops ожидающие комбинации с scope = knowledge_base и task_type ∈ {wiki:ingest, wiki:finalize};
  2. Удаляет остаточные строки для удалённых KB или тенантов (fail-closed);
  3. Для каждой активной KB повторно ставит в очередь задачи-триггеры: wiki:ingest без TaskID (допускается параллельность нескольких пакетов), wiki:finalize с дедупликацией по "wiki-finalize-" + KB_ID (для одной KB остаётся только один finalize). Повторная постановка безвредна — ingest разбирает непересекающиеся строки, а finalize объединяются внутри lane.

Прочая защита во время работы:

  • Тенант удалён: задачи ingest / finalize перед вызовом модели проверяют, жив ли тенант; если тенант удалён, очередь этой базы знаний отбрасывается и запросы к модели больше не создаются;
  • Wiki не может работать: если при срабатывании выясняется, что в базе знаний выключен Wiki, отсутствует модель синтеза или модель удалена, неразобранные операции ingest очищаются, а соответствующие документы освобождаются — документы не застревают в состоянии «Индексируется»;
  • Потеря задачи-триггера: базы знаний, у которых есть только сохранённые операции, но нет задач-триггеров, повторно запускаются фоновым обходом, не чаще одного раза на пороговый период обхода для каждой базы знаний;
  • Повтор при временных ошибках: при вызове модели в случае 408/429/5xx, таймаута, сброса соединения или 403 с признаками ограничения частоты (например «превышена частота вызовов», rate limit) повтор выполняется с экспоненциальной задержкой; 403, связанные с авторизацией, не повторяются;
  • Приоритет по числу сбоев: при разборе ожидающих документов в первую очередь обрабатываются документы с меньшим числом сбоев, чтобы постоянно сбоящие документы не блокировали новые.

Справочник по страницам и интерфейсам ​

Модель страниц и иерархия ​

Типы страниц (PageType) ​

internal/types/wiki_page.go определяет 6 типов страниц:

ТипОписание
summaryстраница аннотации одного исходного документа (slug вида summary/<knowledge-uuid>)
entityстраница сущности (персоны, организации, продукты, технологии и т. п.)
conceptстраница концепции/темы
indexстраница индекса уровня wiki (метаданные)
synthesisстраница комплексного анализа, создаётся только Agent через инструмент wiki_write_page
comparisonстраница сравнения, создаётся только Agent через инструмент wiki_write_page

Состояния страницы (WikiPageStatus): draft / published (по умолчанию) / archived.

Дерево каталогов (Folder Hierarchy) ​

Миграция 000061_wiki_page_hierarchy.up.sql вводит отдельную таблицу wiki_folders (модель списка смежности):

  • WikiFolder организует дерево через ParentID (пустая строка означает корень) + материализованный Path (цепочка имён, соединённая /); пустые папки могут существовать самостоятельно, пользователь может сначала построить каркас;
  • WikiPage.FolderID — единственный источник истины о принадлежности страницы (FK → wiki_folders.id, пустая строка означает корень wiki);
  • CategoryPath / WikiPath / Depth / SortOrder на странице — это кешированная проекция, производная от цепочки folder;
  • генерируемый моделью путь категории имеет глубину не более 3 уровней (константа WikiCategoryMaxDepth = 3), а CleanWikiCategoryPath() нормализует полноширинные разделители (/, | → /) и удаляет метки типов, такие как «сущность», «концепция»;
  • когда страница помещается в созданную пользователем папку, путь папки используется как путь категории без изменений; папки с именами «концепция», «Concepts» и т. п. не отбрасываются как метки типа.

Ключевые поля ​

  • Slug: уникальный идентификатор страницы внутри KB (см. следующий раздел);
  • SourceRefs: ссылки на источники в формате "<knowledge_id>|<doc_title>"; ChunkRefs: ссылки на доказательства на уровне блоков;
  • InLinks / OutLinks: обратные/прямые ссылки wiki-link, поддерживают структуру графа; GET /graph позволяет запрашивать глобальное представление или ego-вид;
  • Aliases: псевдонимы (используются для поиска и указания старых имён после объединения при дедупликации); Version: номер версии.

Публикация и доступ ​

Все маршруты Wiki находятся под /api/v1/knowledgebase/:kb_id/wiki (internal/router/routes_knowledge.go), публичного режима доступа без входа нет, чтение и запись подчиняются RBAC и контролю доступа KB:

Интерфейсы чтения (Viewer + KBAccessRead) ​

МетодПутьОписание
GET/pagesСписок страниц
GET/pages/*slugПолучить одну страницу по slug
GET/foldersДерево каталогов
GET/indexСтраница индекса
GET/graphГраф ссылок (глобальный обзор / ego-режим)
GET/statsСтатистика
GET/search?q=...Поиск
GET/lint / /issuesРезультаты проверки качества / список проблем
GET/revisions/*slugСписок истории версий; с ?version=N — полный текст этой версии

KBAccessRead охватывает: владельца KB, общий доступ в организации и доступ, полученный через общий Agent.

Интерфейсы записи (OwnedWikiKBOrAdmin + KBAccessWrite) ​

МетодПутьОписание
POST / PUT / DELETE/pages, /pages/*slugСоздание / обновление / удаление страницы
POST / PUT / DELETE/folders, /folders/:folder_idУправление каталогами
PUT/move-pageПереместить страницу в каталог
POST/rebuild-linksПерестроить граф ссылок
POST/auto-fixЗапустить автоматическое исправление
PUT/issues/:issue_id/statusОбновить состояние проблемы
POST/revertОткат к указанной версии (body: {slug, version})

Права на запись определяются принадлежностью KB: участнику достаточно владеть этой KB, чтобы управлять её wiki, иначе 403. Для API Key используется сопоставление по возможностям ingest / retrieve.

Просмотр на фронтенде обеспечивает WikiBrowser.vue; во время разбора документов wikiStatusRefresh.ts опрашивает parse_status (pending / processing / finalizing) и продолжает опрос, если разбор завершён, а аннотации ещё генерируются. Состояние раскрытия дерева каталогов поддерживается отдельно в wikiDirectoryState.ts: после создания папки или обновления данных expandWikiDirectoryPath() помечает все уровни каталогов на текущем пути как «раскрытые пользователем», чтобы при обновлении всё дерево не схлопывалось обратно в состояние по умолчанию.

Связь с Agent ​

Агент может читать, изменять страницы и обрабатывать проблемы через следующие 9 инструментов Wiki (для повторного чтения исходных документов используется общий read_document); определения инструментов находятся в internal/agent/tools/definitions.go:

ИнструментНазначениеКлючевые параметры
wiki_read_pageПакетно читать полный текст страниц по slugslugs: string[]
wiki_searchПоиск страниц (заголовок / slug / псевдоним / аннотация / содержимое; query интерпретируется как регистронезависимое POSIX-регулярное выражение; текст, не являющийся корректным регулярным выражением, например C++, сопоставляется буквально; regex=false принудительно включает буквальное сопоставление)query, regex?, knowledge_base_ids?, limit? (старые параметры queries, knowledge_base_id по-прежнему принимаются)
wiki_write_pageСоздание/перезапись всей страницы (synthesis, comparison можно создать только так)slug, title, summary, content, page_type, aliases?, source_refs?
wiki_replace_textТочная замена текста внутри страницыslug, old_text, new_text
wiki_rename_pageПереименовать slug с автоматическим обновлением обратных ссылокslug, new_slug
wiki_delete_pageУдалить страницу и очистить битые ссылкиslug
read_documentПовторно прочитать исходный документ: заголовок метаданных + блоки, можно постранично или с query для поиска внутри документа (заменяет прежний wiki_read_source_doc; доступен и для KB только с Wiki, так как блоки всегда сохраняются в БД)id (dN / cN), offset?, limit?, query?, regex?, context?
wiki_flag_issueПометить проблему страницыslug, issue_type ∈ {mixed_entities, contradictory_facts, out_of_date, other}, description
wiki_read_issueПросмотреть детали проблемыID проблемы
wiki_update_issueОбновить состояние проблемыID проблемы, status ∈ {pending, ignored, resolved}

Рекомендуемый порядок чтения — wiki_search → wiki_read_page → read_document: сначала найти страницу, затем прочитать её целиком, а при необходимости точной ссылки вернуться к блокам cN исходного источника. Вывод инструментов имеет XML-подобную структуру (<wiki_page><metadata>...<summary>...<content>...), фронтенд разбирает её с помощью parseWikiToolReferences() из frontend/src/utils/wikiToolReferences.ts и отрисовывает карточки ссылок в диалоге.

Сопутствующие механизмы:

  • Wiki Scope: в рамках сессии Agent поддерживается белый список wiki-KB, поддерживается сужение области до конкретных документов/тегов через @mention, а при выполнении инструментов автоматически фильтруются source_refs (internal/agent/tools/wiki_tools.go);
  • Права на запись: области поиска достаточно прав на чтение, тогда как инструменты изменения (wiki_write_page, wiki_replace_text, wiki_rename_page, wiki_delete_page, wiki_flag_issue, wiki_update_issue) действуют только на редактируемые вызывающим базы знаний, то есть базы знаний текущего пространства или базы знаний, к которым получены права editor и выше через общий доступ организации. Сам вызывающий тоже должен иметь права на запись: роль в пространстве Contributor и выше; ограниченному API key требуется возможность ingest. Работающие от имени Viewer IM, веб-встраивание и эндпоинты MCP поэтому только для чтения. Если в области нет редактируемой базы знаний wiki, эти инструменты не регистрируются. Общий агент всегда только для чтения;
  • Wiki Fixer: встроенный Agent (types.BuiltinWikiFixerID), отвечает за автоматическое исправление проблем wiki (битые ссылки, путаница сущностей и т. п.). При кросс-тенантном доступе к общей KB требуется роль в тенанте ≥ Editor и выполняется автоматическое повышение до контекста тенанта-источника (internal/handler/session/wiki_fixer_scope.go). После повышения используются встроенные конфигурации по умолчанию, только для этой одной KB, не включаются MCP, навыки, песочница и веб-поиск, модель откатывается к собственной модели этой KB, а пользовательские настройки fixer от вызывающего не попадают в пространство-источник;
  • Замкнутый цикл проблем: таблица wiki_page_issues + интерфейс lint + auto-fix; и люди, и Agent могут сообщать о проблемах и обрабатывать их.

Процесс генерации ​

Генерация Wiki запускается приёмом документов (knowledge ingest) и выполняется асинхронно через очередь задач Redis. Типы задач определены в internal/types/task.go:

go
TypeWikiIngest   = "wiki:ingest"
TypeWikiFinalize = "wiki:finalize"

Весь конвейер состоит из четырёх этапов (структура Map-Reduce):

ЭтапЗадачаЧто делаетПромпт LLM (internal/agent/prompts_wiki.go)
Pass 0: извлечение кандидатовwiki:ingestИзвлекает из документов каркас slug-кандидатов (JSON из entities + concepts)WikiCandidateSlugPrompt
Pass 1..N: цитирование по блокамwiki:ingestДля каждого chunk помечает ссылками slug-кандидатов, выводит { citations: {"slug": ["c001", ...]}, new_slugs: [...] }; для длинных префиксов используется prefix cachingWikiChunkCitationPrompt
Reduce: объединение страницwiki:ingestИнкрементально обновляет или объединяет страницы по slug, выводит SUMMARY: ... + текст Markdown; строгая привязка к источникам, запрет галлюцинаций, дедупликация, запрет самоссылокWikiPageModifySystemPrompt + WikiPageModifyUserPrompt
Finalize: завершениеwiki:finalizeПерестраивает страницы индекса, чистит битые ссылки, добавляет перекрёстные ссылки, подрезает каталоги — чистый SQL/алгоритмы на графе, без вызова LLM—

Вспомогательные промпты:

  • WikiTaxonomyPlanPrompt: единообразно планирует пути каталогов для всех сущностей/концепций одного пакета (не более 2 уровней, с приоритетом повторного использования существующих папок), обеспечивая связность дерева каталогов;
  • WikiDeduplicationPrompt: определяет, указывает ли новый извлечённый элемент на тот же объект, что и существующая страница; ключевой принцип — "related ≠ same" (связанное не значит одинаковое), возвращает { merges: { "entity/new": "entity/existing" } }. Имена и псевдонимы сравниваются в обе стороны (имя с псевдонимом, псевдоним с псевдонимом), но общего псевдонима или сокращения самого по себе недостаточно, чтобы считать объекты одним и тем же.

Прочие правила дедупликации и записи:

  • Повторное использование имени между типами: при повторном разборе модель может сначала отнести один и тот же объект к concept/X, а затем к entity/X. При отсутствии страницы того же типа страница другого типа с совпадающим нормализованным заголовком обновляется напрямую, а не создаётся страница-двойник;
  • Ссылки на изображения с описанием: на этапе Reduce при чтении процитированных блоков подпись изображения / текст OCR встраиваются прямо в содержимое блока, чтобы модель могла оценить, относится ли изображение к странице;
  • Усечение при перезаписи всей страницы: если перезапись всей страницы обрывается из-за предела длины вывода, выполняется не более 3 дополнительных раундов с бесшовной склейкой; если текст так и не дописан, запись отменяется и фиксируется предупреждение, а неполный текст не записывается на страницу;
  • Отдельный иероглиф не связывается автоматически: страница с заголовком из одного иероглифа может существовать, но ссылка на неё автоматически не добавляется в текст других страниц, чтобы избежать ошибочных ссылок в словах вроде «ветер-песок» или «ядро».

Гранулярность извлечения ​

WikiExtractionGranularity в WikiConfig (хранится в столбце JSONB knowledge_bases.wiki_config) управляет плотностью извлечения:

ГранулярностьПоведение
focusedТолько 3-7 основных тем
standard (по умолчанию)Темы + сущности/концепции, которые содержательно обсуждаются (абзац/несколько упоминаний/более 2-3 предложений)
exhaustiveИсчерпывающе все именованные объекты и общепризнанные концепции

Параллелизм и пакетная обработка ​

Параметры, связанные с WikiConfig (internal/types/wiki_page.go):

ПараметрПо умолчаниюОписание
IngestBatchSize5Число ожидающих документов, разбираемых за один пакет
IngestMapParallel10Число параллельных задач errgroup на этапе Map (извлечение+цитирование по каждому документу)
IngestReduceParallel10Число параллельных задач на этапе Reduce (запись страницы по каждому slug)
IngestMaxInflight4Максимум параллельных пакетов для одной KB (обеспечивает справедливость между KB)

На этапе планирования каталогов при вычислении эмбеддингов папок и страниц запросы, как и другие вызовы эмбеддингов, разбиваются по BATCH_EMBED_SIZE, поэтому служба модели не отклоняет их из-за слишком большого одиночного ввода.

Диаграмма процесса генерации ​

Механизм Slug ​

  • Формат: <type>/<name>, например entity/acme-corp, concept/rag, summary/<knowledge-uuid>; в нижнем регистре, разделяется дефисами, нелатинские имена транслитерируются/пиньинизируются;
  • Уникальность: уникальный индекс БД (000037_wiki_and_indexing.up.sql):
sql
 CREATE UNIQUE INDEX idx_kb_slug ON wiki_pages (knowledge_base_id, slug) WHERE deleted_at IS NULL

то есть slug уникален в пределах одной базы знаний и может повторяться между KB;

  • Стабильность: при повторном извлечении после обновления документа промпт заставляет модель повторно использовать старый slug —

If an entity or concept from the previous extraction still exists in the current document, reuse its exact slug from the previous list. Do NOT generate a new slug for the same thing.

Новый slug создаётся только для впервые появившихся объектов, а исчезнувшие элементы больше не выводятся;

  • Slug Handle (прокси-дескриптор): в вызовах LLM при ingest настоящие slug с высокой энтропией (особенно summary/... с UUID) заменяются короткими дескрипторами (ref-1, ref-2), а после того как модель выводит [[ref-1|title]], бэкенд восстанавливает настоящий slug, чтобы модель не перепутала UUID (internal/application/service/wiki_slug_handles.go);
  • Роль ссылок: ссылки wiki в ответах Agent имеют вид [[slug|title]], а InLinks/OutLinks поддерживают граф страниц по slug; переименование slug (инструмент wiki_rename_page) автоматически обновляет все обратные ссылки.

Справочник по реализации ​

Все пути ниже указаны относительно корня репозитория:

СлойФайл
Структуры данныхinternal/types/wiki_page.go
HTTP Handlerinternal/handler/wiki_page.go
Конвейер генерацииinternal/application/service/wiki_ingest.go, wiki_ingest_batch.go, wiki_ingest_cite.go, wiki_ingest_dedup.go, wiki_ingest_taxonomy.go
Сервис страницinternal/application/service/wiki_page.go, wiki_linkify.go, wiki_lint.go, wiki_slug_handles.go
Промпты LLMinternal/agent/prompts_wiki.go
Инструменты Agentinternal/agent/tools/wiki_*.go (регистрируются в internal/agent/tools/definitions.go)
Восстановление после сбоевinternal/container/recover_pending_wiki_tasks.go
МаршрутыRegisterWikiPageRoutes из internal/router/routes_knowledge.go (тесты поведения: internal/router/router_wiki_test.go)
Миграции БДmigrations/versioned/000037_wiki_and_indexing.up.sql, 000061_wiki_page_hierarchy.up.sql, 000077_remove_wiki_log.up.sql
Фронтендfrontend/src/views/knowledge/wiki/WikiBrowser.vue, frontend/src/api/wiki/, frontend/src/utils/wikiToolReferences.ts

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