Руководство по точкам расширения
Парсеры, стратегии разбиения, движки поиска, поставщики моделей, поисковики, источники данных, IM-адаптеры, инструменты агента и бэкенды хранения подключаются через интерфейсы. При добавлении нового следует реализовать соответствующий интерфейс, подключить его в точке регистрации и проверить существующую цепочку вызовов. Ниже по типам расширений перечислены интерфейсы, имеющиеся реализации и шаги подключения.
Обзор точек расширения
Большая часть точек расширения на стороне Go сосредоточена в регистрационном центре internal/container/container.go (контейнер внедрения зависимостей):
- Регистрация движков поиска:
initRetrieveEngineRegistry() - Регистрация веб-поисковиков:
registerWebSearchProviders() - Регистрация IM-адаптеров:
registerIMAdapters() - Регистрация источников данных:
registerDataSourceProviders() - Регистрация инструментов агента:
registerTools()/ маршрутизация инструментов - Регистрация бэкендов хранения:
initStorageRegistry()
В Python-части docreader/ парсеры объединены в реестр (docreader/parser/registry.py), подключение — там же. Адаптеры и движки, специфичные для доменов, помечены в списке ниже по конкретным файлам.
Добавление нового парсера документов (docreader, Python)
Определение интерфейса
Базовый класс — docreader/parser/base_parser.py. После упрощения рефакторинга BaseParser отвечает только за преобразование документа в markdown-текст + ссылки на исходные изображения (разбиение, сохранение изображений, OCR, генерация подписей VLM выполняются на стороне Go):
class BaseParser(ABC):
name: str
engine: str
ext: list[str]
def parse_into_text(self, content: bytes) -> Document:
...Возвращаемый Document (docreader/models/document.py, pydantic-модель) содержит ключевые поля content: str (markdown) и images: Dict[str, str] (путь → base64).
Механизм регистрации
docreader/parser/registry.py содержит ParserEngineRegistry с двухуровневым отображением «имя движка → {расширение файла → класс парсера}». Если запрошенный движок не поддерживает тип файла, автоматически используется fallback на движок builtin. Реестр по умолчанию строится в _build_default_registry().
При инициализации модуля выполняется:
# docreader/parser/__init__.py
REGISTRY = ParserEngineRegistry(_build_default_registry())Существующие реализации
| Движок | Парсер | Файл |
|---|---|---|
builtin | Docx2Parser, DocParser, PDFParser, MarkdownParser, ExcelParser, EPUBParser, HTMLParser, MHTMLParser, ImageParser (jpg/png/jpeg/jpg2/jpeg2/webp/bmp/tif/tiff), TextParser, CSVParser, JSONParser, XMLParser, RTFParser, NumPyParser | docreader/parser/builtin_parser.py |
markitdown | MarkitdownParser (Microsoft MarkItDown, многоформатный) | docreader/parser/markitdown_parser.py |
opendataloader | OpenDataLoaderParser (анализ верстки PDF, требует Java 11+, проверка доступности check_available) | docreader/parser/opendataloader_parser.py |
Шаги добавления
- Создать
docreader/parser/my_parser.py, унаследоватьBaseParser, реализоватьparse_into_text(content: bytes) -> Document. - Точка регистрации:
docreader/parser/registry.py— добавить в_build_default_registry():
"myengine": {
".pdf": MyParser,
".docx": MyParser,
...
}- При необходимости добавить зависимость в
docreader/pyproject.toml(или системный пакет), описать работу вdocs/06-development/01-dev-guide.md. - Проверить:
make lint-docreader/ запустить локально парсинг тестового файла через HTTP API docreader.
Добавление стратегии разбиения на фрагменты (chunking)
Интерфейс
Стратегии разбиения определены в internal/application/chunking/. Базовый интерфейс: ChunkStrategy:
type ChunkStrategy interface {
Name() string
Split(ctx context.Context, text string, cfg *types.ChunkingConfig) ([]*types.Chunk, error)
}Конфигурация разбиения: types.ChunkingConfig (internal/types/knowledge.go), включая ChunkingStrategy, ChunkSize, ChunkOverlap, Separator, RagMaxContextSize, прочие параметры, специфичные для стратегии.
Регистрация
Фабрика/реестр: internal/application/chunking/factory.go (или registry.go).
func GetStrategy(name string) ChunkStrategy {
switch strings.ToLower(name) {
case "recursive":
return NewRecursiveChunker()
case "semantic":
return NewSemanticChunker()
case "layout":
return NewLayoutChunker()
...
default:
return NewRecursiveChunker()
}
}Существующие реализации
| Стратегия | Файл |
|---|---|
recursive (рекурсивный по разделителям) | internal/application/chunking/recursive.go |
semantic (семантическое разбиение, с эмбеддингом/кластерами) | internal/application/chunking/semantic.go |
layout (с учётом разметки/таблиц) | internal/application/chunking/layout.go |
none / прочие по конфигурации | см. фабрику |
Шаги добавления
- Создать
internal/application/chunking/mystrategy.go, реализоватьChunkStrategy. - Добавить кейс в фабрику
GetStrategy. - По необходимости расширить
types.ChunkingConfigи валидацию вinternal/validation/.... - Обновить документацию по разбиению на блоки.
- Прогнать тесты:
go test ./internal/application/chunking/....
Добавление движка поиска (retrieval engine)
Движки поиска подключаются для гибридного/векторного/полнотекстового поиска в БЗ.
Интерфейс
В internal/application/retrieval/ (или internal/retrieval/) общий интерфейс, обычно:
type Engine interface {
Name() string
Init(ctx context.Context, cfg *config.Config, deps ...any) error
Search(ctx context.Context, req *types.SearchRequest) (*types.SearchResult, error)
// при наличии: Upsert/Delete, Health
}Также есть Registry для выбора движка по названию (config.KnowledgeBase.IndexingStrategy.RetrieveEngine или глобально).
Регистрация
Центр регистрации: internal/container/container.go → initRetrieveEngineRegistry().
registry := retrieval.NewRegistry()
registry.Register("weaviate", weaviate.New())
registry.Register("qdrant", qdrant.New())
registry.Register("milvus", milvus.New())
registry.Register("paradedb", paradedb.New())
registry.Register("pgvector", pgvector.New())
// ...
container.SetRetrieveEngineRegistry(registry)Существующие реализации
| Движок | Файл/пакет |
|---|---|
weaviate | internal/retrieval/weaviate/ |
qdrant | internal/retrieval/qdrant/ |
milvus | internal/retrieval/milvus/ |
paradedb | internal/retrieval/paradedb/ |
pgvector (Postgres + pgvector) | internal/retrieval/pgvector/ |
opensearch (при наличии) | см. контейнер/реестр |
Шаги добавления
- Реализовать пакет
internal/retrieval/<engine>/с реализацией интерфейсаEngine. - Зарегистрировать в
initRetrieveEngineRegistry()контейнера. - Добавить конфигурацию в
internal/config/(knowledge.retrieval.<engine>.*), маппинг в YAML/ENV. - Обновить поисковые движки.
- Интеграционные тесты:
go test ./internal/retrieval/<engine>/...(при наличии тестового стенда).
Добавление поставщика моделей (provider)
Модели (chat/embedding/rerank/vision/asr/tts и др.) подключаются через провайдерский интерфейс.
Интерфейс
В internal/models/provider/ общий интерфейс Provider (ключевые методы зависят от типа модели):
type Provider interface {
ID() string
Name() string
Capabilities() []Capability
CreateChatCompletion(ctx context.Context, req *ChatRequest) (*ChatResponse, error)
CreateChatCompletionStream(ctx context.Context, req *ChatRequest, w StreamWriter) error
// для embedding/rerank — отдельные интерфейсы или методы
}Также используются Registry и фабрика по provider_id.
Регистрация
- Статическая регистрация провайдеров:
internal/models/provider/registry.go(илиinit()каждого пакета) - Либо через контейнер:
internal/container/container.goпри инициализации модели-системы - Администрирование через UI/API: провайдеры также могут быть добавлены через
config/models.json(наложение развёртывания) — см. Управление моделями
Типичный шаблон регистрации:
func init() {
registry.Register("openai", NewOpenAIProvider)
registry.Register("anthropic", NewAnthropicProvider)
registry.Register("ollama", NewOllamaProvider)
// ...
}Существующие реализации
См. пакеты в internal/models/provider/: openai, anthropic, azure_openai, ollama, deepseek, qwen, moonshot, siliconflow, zhipu, minimax, mistral, google, volcengine, baidu, cohere, huggingface, vllm, lmdeploy, xinference, xai, groq, fireworks, together, openrouter и др.
Шаги добавления
- Создать
internal/models/provider/myprovider/, реализоватьProvider(и при необходимостиEmbeddingProvider,RerankProvider). - Зарегистрировать в реестре провайдеров (
registry.Register("myprovider", ...)). - Добавить дефолты в
internal/config/models_defaults.goили черезconfig/models.jsonдля конкретного стенда. - Обновить Управление моделями (список встроенных провайдеров, compat-переопределения при необходимости).
- Тесты:
go test ./internal/models/provider/myprovider/....
Добавление поисковика (web search provider)
Веб-поиск используется агентом/функциями при необходимости поиска в интернете.
Интерфейс
В internal/websearch/ (или internal/application/websearch/):
type Provider interface {
Name() string
Search(ctx context.Context, query string, opts *SearchOptions) ([]*SearchResult, error)
IsAvailable(ctx context.Context) bool
}SearchOptions включает Count, Country, Language, таймауты и др.
Регистрация
Центр: internal/container/container.go → registerWebSearchProviders().
ws := websearch.NewManager()
ws.Register(google.New(cfg.WebSearch.Google))
ws.Register(bing.New(cfg.WebSearch.Bing))
ws.Register(duckduckgo.New(cfg.WebSearch.DuckDuckGo))
ws.Register(serpapi.New(cfg.WebSearch.SerpAPI))
ws.Register(tavily.New(cfg.WebSearch.Tavily))
ws.Register(brave.New(cfg.WebSearch.Brave))
ws.Register(searxng.New(cfg.WebSearch.SearXNG))
container.SetWebSearchManager(ws)Существующие реализации
| Провайдер | Пакет |
|---|---|
google (Programmable Search/Custom Search) | internal/websearch/google/ |
bing | internal/websearch/bing/ |
duckduckgo | internal/websearch/duckduckgo/ |
serpapi | internal/websearch/serpapi/ |
tavily | internal/websearch/tavily/ |
brave | internal/websearch/brave/ |
searxng | internal/websearch/searxng/ |
Шаги добавления
- Реализовать
internal/websearch/myprovider/с интерфейсомProvider. - Зарегистрировать в
registerWebSearchProviders(). - Добавить секцию конфигурации
WebSearch.MyProviderвinternal/config/. - Обновить документацию Веб-поиск.
- Тесты с моками:
go test ./internal/websearch/myprovider/....
Добавление источника данных (data source)
Источники данных (Confluence, Notion, Feishu/Lark, Git, S3, Seafile и др.) используются для синхронизации/импорта в БЗ.
Интерфейс
В internal/datasource/ базовый интерфейс Provider:
type Provider interface {
ID() string
Name() string
ValidateConfig(ctx context.Context, cfg map[string]any) error
ListResources(ctx context.Context, cfg map[string]any, opts *ListOptions) ([]*Resource, error)
FetchContent(ctx context.Context, cfg map[string]any, res *Resource) (*Content, error)
// опц.: Watch/Sync, Auth flow
}Регистрация
Центр: internal/container/container.go → registerDataSourceProviders().
ds := datasource.NewRegistry()
ds.Register(confluence.New())
ds.Register(notion.New())
ds.Register(feishu.New())
ds.Register(seafile.New())
ds.Register(github.New())
ds.Register(s3.New())
// ...
container.SetDataSourceRegistry(ds)Существующие реализации
| Провайдер | Файл |
|---|---|
confluence | internal/datasource/confluence/ |
notion | internal/datasource/notion/ |
feishu / lark | internal/datasource/feishu/ |
seafile | internal/datasource/seafile/ |
github | internal/datasource/github/ |
s3 / minio | internal/datasource/s3/ |
webhook / прочие | см. реестр |
Шаги добавления
- Создать
internal/datasource/myds/с реализациейProvider. - Зарегистрировать в
registerDataSourceProviders(). - Добавить UI-форму/валидацию конфигурации (frontend
src/pages/datasource/при необходимости). - Обновить Импорт источников данных и Seafile при релевантности.
- Тесты:
go test ./internal/datasource/myds/....
Добавление IM-адаптера (IM integration)
Поддерживаются интеграции с корпоративными мессенджерами для канальной/ботовой работы.
Интерфейс
В internal/im/ интерфейс Adapter:
type Adapter interface {
ID() string
Name() string
Init(ctx context.Context, cfg *Config) error
Start(ctx context.Context) error
Stop(ctx context.Context) error
SendMessage(ctx context.Context, to string, msg *Message) error
OnMessage(handler func(*InboundMessage)) // подписка
}Регистрация
Центр: internal/container/container.go → registerIMAdapters().
imReg := im.NewRegistry()
imReg.Register(wechatwork.New())
imReg.Register(dingtalk.New())
imReg.Register(lark.New())
imReg.Register(feishu.New())
imReg.Register(slack.New())
imReg.Register(telegram.New())
imReg.Register(discord.New())
container.SetIMRegistry(imReg)Существующие реализации
См. internal/im/<adapter>/: wechatwork, dingtalk, lark, feishu, slack, telegram, discord, custom и др.
Шаги добавления
- Реализовать
internal/im/myim/adapter.go. - Зарегистрировать в
registerIMAdapters(). - Добавить конфигурацию в
internal/config/im.goи хранение в БД (модель/миграция при необходимости). - Обновить Интеграция IM.
- Интеграционные тесты с заглушкой:
go test ./internal/im/myim/....
Добавление инструмента агента (agent tool)
Инструменты агента расширяют возможности агента (поиск, выполнение кода, чтение файлов, вызов MCP и др.).
Интерфейс
В internal/agent/tools/ используется интерфейс инструмента (типовой для движка агента):
type Tool interface {
Name() string
Description() string
Parameters() map[string]any // JSON Schema
Execute(ctx context.Context, args map[string]any) (*ToolResult, error)
}Результат: ToolResult с Content, Error, Metadata.
Регистрация
Центр: internal/container/container.go → registerTools() (или internal/agent/tools/registry.go).
tools := []tools.Tool{
newSearchTool(deps),
newReadTool(deps),
newWriteTool(deps),
newThinkTool(),
newWebSearchTool(deps.WebSearch),
newMCPTool(deps.MCP),
// ...
}
agent.SetTools(tools)Также инструменты MCP динамически подгружаются из внешних MCP-сервисов (MCP-интеграция).
Существующие инструменты
См. internal/agent/tools/: search, read, write, think, web_search, shell, python, file, browser, mcp, knowledge, graph, wiki, skill, sandbox, memory и др.
Шаги добавления
- Создать
internal/agent/tools/mytool.goс реализациейTool. - Зарегистрировать в
registerTools()контейнера (с проверкой доступности/прав). - При необходимости добавить в системный промпт/описание агента (
internal/agent/prompts.go) — см. Сборка промптов диалога. - Обновить документацию по агенту/инструментам (Агент-движок).
- Тесты:
go test ./internal/agent/tools/....
Добавление бэкенда хранения (storage backend)
Файловое хранение (аватары, вложения, превью, экспорт) поддерживает несколько бэкендов.
Интерфейс
В internal/storage/ интерфейс Backend:
type Backend interface {
Name() string
Put(ctx context.Context, key string, r io.Reader, size int64, meta map[string]string) (string, error)
Get(ctx context.Context, key string) (io.ReadCloser, error)
Delete(ctx context.Context, key string) error
URL(ctx context.Context, key string, opts *URLOptions) (string, error) // публичная/подписанная ссылка
Exists(ctx context.Context, key string) (bool, error)
}Регистрация
Центр: internal/container/container.go → initStorageRegistry().
reg := storage.NewRegistry()
reg.Register("local", local.New(cfg.Storage.Local))
reg.Register("s3", s3.New(cfg.Storage.S3))
reg.Register("oss", oss.New(cfg.Storage.OSS))
reg.Register("cos", cos.New(cfg.Storage.COS))
reg.Register("minio", minio.New(cfg.Storage.MinIO))
reg.Register("r2", r2.New(cfg.Storage.R2))
container.SetStorageRegistry(reg)Активный бэкенд выбирается через config.Storage.Type.
Существующие реализации
| Бэкенд | Пакет |
|---|---|
local (файловая система) | internal/storage/local/ |
s3 (AWS S3-совместимый) | internal/storage/s3/ |
minio | internal/storage/minio/ |
r2 (Cloudflare R2) | internal/storage/r2/ |
oss (Alibaba Cloud OSS) | internal/storage/oss/ |
cos (COS) | internal/storage/cos/ |
Шаги добавления
- Реализовать
internal/storage/mybackend/с интерфейсомBackend. - Зарегистрировать в
initStorageRegistry(). - Добавить конфигурацию
Storage.MyBackendвinternal/config/storage.goи валидацию. - Обновить Бэкенды хранения.
- Тесты:
go test ./internal/storage/mybackend/....
Прочие практические замечания
- Зависимости через контейнер. По возможности получать зависимости из
internal/container.Container, не создавать глобальные синглтоны вне контейнера. - Конфигурация. Все новые параметры добавлять в
internal/config/с валидацией и описанием ENV. - Миграции БД. Если расширение требует новых таблиц/полей — добавить миграцию в
migrations/(формат, принятый в проекте). - Логирование/трейсинг. Использовать
internal/logи существующий контекстный логгер. - Безопасность. Для внешних вызовов учитывать таймауты, повторы с бэк-оффом, rate-limit, валидацию входных данных.
- Документация. Обновить релевантный раздел в
website-docs/(features/development), добавить скриншоты при изменении UI. - Проверка связей. После изменений запустить
npm run check:linksиnpm run check:mermaidвwebsite-docs/, а такжеgo build ./...и релевантныеgo test.
