К документации
Руководство 05

Руководство по функциям

Руководство по функциям с внутренним устройством, потоком данных и ссылками file:line. Для каждой функции: как работает, как использовать, как расширять.

7 мин чтения

Руководство по функциям с внутренним устройством, потоком данных и ссылками file:line. Для каждой функции: как работает, как использовать, как расширять.

Гибридный RAG (Vector + PostgreSQL tsvector, RRF)

Гибридный поиск распределён между двумя процессами: collector владеет только векторной составляющей (LanceDB/Qdrant); сервер владеет FTS (PostgreSQL tsvector) и RRF, который их объединяет.

Поток (packages/server/src/services/hybridSearchService.ts:104):

  1. Агент вызывает skill rag_search (agent/builtinSkills.ts:27) с query, workspaceId, archiveId.
  2. hybridSearch - Guard D-01: сравнивает EMBEDDING_MODEL с embeddingModel у Document рабочего пространства; при несовпадении → пропускает vector leg (FTS-only, без ошибки).
  3. Promise.all([vectorSearchViaCollector(), ftsSearch()]) - vector через POST /api/ingest/query collector; FTS через prisma.$queryRaw по document_chunks.searchVector (GIN-индекс, ts_rank).
  4. RRF fusion (k=60): для каждого chunk score += 1/(60+rank+1); Set sources различает vector/fts/both. Детерминированный tiebreaker: score DESC → documentId ASC → chunkIndex ASC.
  5. rag_search форматирует chunk'и как [Source: name (match: semantic+keyword|vector|fts, score: ...)] и возвращает SourceCitation[].
  6. Fallback archive D-07: если 0 результатов и присутствует archiveId, перезапускает на archive:${archiveId} и переназначает тег source: "archive".
  7. Цитаты передаются в SSE citations (routes/chat.ts:508).

Metadata filtering (v0.22): rag_search принимает фильтры documentTypes (pdf/md/txt/csv/docx/xlsx) и dateFrom/dateTo (ISO; ограничение только по дате включает весь день). Фильтры проходят через оба leg гибридного поиска: vector leg достигает collector как pre-фильтры по метаданным ingest с timestamp (предикаты JSONB pgvector + must-clause payload Qdrant; LanceDB и Chroma игнорируют новые ключи с залогированным warn), FTS leg получает параметризованные SQL-предикаты по таблице documents. Серверный backstop после retrieval против авторитетной таблицы documents гарантирует корректность фильтра у каждого provider. Документы, ингестированные с этого момента, всегда фильтруемы; legacy-векторы (pre-stamping) исключаются, пока активен фильтр, и возвращают фильтруемость через admin re-embed - без schema change, без re-embed существующих документов, поведение без фильтров не меняется.

Grounded-цитаты (v0.22.0): после дедупликации фильтр grounding ограничивает цитаты на документ топ-2 по relevance score и удаляет цитаты chunk'ов, текст которых не имеет значимого лексического пересечения с финальным ответом (консервативный gate - страницы wiki/archive, результаты web/memory, цитаты без chunk text и очень короткие ответы всегда сохраняются). Дисклеймер "No supporting evidence" оценивается на отфильтрованном наборе (streaming и non-streaming пути).

Chunking (collector/services/chunker.ts:16): RecursiveCharacterTextSplitter, chunkSize=1000, overlap=200, разделители ["\n\n","\n",". "," ",""]. Metadata: documentId, paragraph, charStart.

Embedding (collector/services/embeddings.ts): LocalEmbeddingProvider (Xenova all-MiniLM-L6-v2, 384-dim, air-gap) по умолчанию; OpenAIEmbeddingProvider (text-embedding-3-small, 1536-dim). Singleton с кэшем по providerType:modelName.

Vector store (collector/services/vectorStore.ts): LanceDBProvider (по умолчанию, путь ${STORAGE_PATH}/vectors/lancedb, таблица на workspace ws_<name>_<shortId>); QdrantProvider (enterprise-заглушка).

FTS pass-through: collector возвращает chunks[].chunkText серверу, который пишет DocumentChunk с searchVector = to_tsvector('simple', text). Collector никогда не открывает Prisma.

Расширение: новый embedding provider → реализуй EmbeddingProvider + фабрику getEmbeddingProvider + размерности в KNOWN_MODEL_DIMS. Тюнинг RRF → RRF_K в hybridSearchService.ts:15. Смена модели embedding → reindex через Settings → Maintenance (POST /api/system/reindex-documents).

Agent Orchestrator (ReAct)

packages/server/src/agent/orchestrator.ts - входы runAgent (non-stream, :78) и runAgentStreaming (:416). Паттерн ReAct single-call: один `streamLLM` на итерацию - это decision call - streamResult.toolCall определяет ветку tool-или-ответ.

Цикл:

  1. Setup: загружает WorkspaceAgentConfig, парсит enabledSkills, разрешает шаблон (resolveSystemPrompt), добавляет в начало user.customInstructions.
  2. Разрешает provider: resolveProviderConfig(providerId, model) → fallback buildFallbackConfig.
  3. Шаблон ограничений: localLLMOnly (блокирует не-ollama provider'ы), hybridSearchForced, citationRequired.
  4. Если ragContext (widget) → внедряет в system prompt, удаляет rag_search из activeSkills.
  5. Plan mode (agentConfig.planMode): синхронная фаза planning → SSE plan event → внедрение в prompt выполнения.
  6. ReAct while(true):
    • Watchdog wallclock + token budget.
    • streamLLM с buffered replay D-02: итерация tool-call → буфер отбрасывается; final-answer → replay токенов в реальный onToken (progressive DLP flush).
    • !toolCall → пробует resolveImplicitToolCall (recovery XML-ish tag) → final answer, break.
    • Skill не найден → circuit breaker BOT-03: trip при 3 подряд / 5 всего → abortReason: "unknown_tool_breaker".
    • Loop detection, per-skill timeout, truncateToolOutput, context cap.
  7. finally: освобождает слот конкурентности + сохраняет WorkspaceTokenUsage на всех путях выхода.

Builtin skills (agent/builtinSkills.ts): rag_search, workspace_memory (KV на SystemConfig с ключом ws_memory:<ws>:<key>), document_temp_process (ad-hoc загрузка в collector), wiki_query (FTS archive_pages + векторный fallback + BFS wikilinks, максимум 10 страниц depth 3), wiki_write (preview + SSE wiki_edit, RBAC archive:write).

Budget tracker (services/agentBudgetService.ts:122): watchdog wallclock/token/context/loop-detection/per-skill-timeout/per-user-concurrency (CHAT_MAX_CONCURRENT_PER_USER). AbortReason: none|wallclock|token_budget|context_overflow|loop_detected|aborted|done|unknown_tool_breaker. truncateContextToByteBudget сохраняет sysMsg + pinned user msg + last N.

Выбор модели + fallback: приоритет per-chat → workspace default → global default → ENV (providerService.resolveProviderConfig). deriveCapabilities помечает local-only/fastest/smartest/reasoning. Polling 30s refreshModels → Provider.isAvailable. Frontend fallback 3-tier в useChat.ts с toast Undo. Серверный fallback buildFallbackConfig (orchestrator.ts:879). SSE done включает modelUsed, providerUsed.

Расширение: новый skill → registerSkill({...}) в builtinSkills.ts. Модель per-chat → PATCH /workspaces/:ws/chats/:chatId/model. Plan mode → включи WorkspaceAgentConfig.planMode. Тюнинг бюджета → env AGENT_* (см. 03 - Configuration).

Chat streaming (SSE)

POST /api/workspaces/:id/chat/stream → SSE. События: token, status, citations ({ sources }), done ({ chatId, messageId, modelUsed, providerUsed, mcpSources }), error, plan, wiki_edit. Frontend: useChat.ts (@microsoft/fetch-event-source), abort через AbortController. Slash /model, палитра Cmd+K, сравнение Cmd+Shift+M. Сохранение фактической модели через loadChat PATCH (stale default skipped, см. frontend/CLAUDE.md).

Документы, ингестия, OCR

Ингестия (collector/routes/ingest.ts:219): multer (100 MB, allowlist MIME) → parseFile → chunkText → embed → vectorStore.addDocuments → notifyServerStatus (PUT /api/documents/:id/status с X-Collector-Secret). Ответ включает chunks[].chunkText для серверного FTS.

Parser (collector/services/parser.ts): PDF (pdf-parse, <100 символов → OCR skip D-04, vision на стороне сервера), DOCX (officeparser → fallback mammoth), PPTX (officeparser), XLSX (node-xlsx), TXT/MD/CSV (UTF-8), YouTube (youtube-transcript-plus, 422 если нет transcript). ocrMode (auto/vision/skip) в IngestUploadBodySchema.

Vision OCR (серверный) (services/ragOcrService.ts:59): pdfjs-dist загружает PDF (cap 50 страниц) → рендер PNG → vision-модель Ollama → strip grounding tags → конкатенация. Job service ocrJobService.ts с workflow approve/reject, qualityScore, credibilityScore. Routes routes/ocr.ts. Frontend SettingsOcr (каталог моделей, job list, preview, custom instructions).

DLP filter (services/dlpFilter.ts): scanContent(text) → {hasMatch, matches, redactedText}. С v0.22 паттерны - 10 встроенных database-backed (таблица dlp_patterns, до 50 кастомных паттернов с live test preview): помимо 6 оригинальных (email, credit_card, ssn, api_key sk-..., aws_key AKIA..., private_key PEM) добавлены 4 паттерна EU/IT - it_vat_iva (Partita IVA label-anchored), it_codice_fiscale (16 символов, uppercase-only), iban (префикс страны + длина ≥ 15, ReDoS-safe), eu_phone (телефон IT/EU, seeds DISABLED для admin review). Redaction [REDACTED]. Gate DLP_ENABLED (по умолчанию `"true"` в CONFIG_DEFAULTS, permissions.ts:173); setting DLP_BYPASS_ROLES (JSON-массив ролей, по умолчанию []) освобождает выбранные роли от каждого scan/redaction с audit event dlp.bypassed. Admin-переключатель "Show/Hide DLP texts" для раскрытия/повторного скрытия отредактированных текстов (только admin, session-scoped). Каждый audit event dlp.* помечает источник (source: "chat" | "widget"), выведенный из header X-Widget-Id, с фильтрами в панели DLP Match History. Fallback на встроенный набор, если DB недоступна (cache TTL 5 min); token-by-token streaming flush сохраняет встроенные паттерны. Интегрирован в routes/chat.ts (вход + выход); SSE done → dlp_warning: true; widget показывает PIIWarningPrompt.

Архивы, Wiki, Synthesis

Archive: workspace-scoped многопользовательская knowledge base, markdown-страницы с path slug и wikilink [[Title]]. 14 archive-сервисов (archivePageService, archiveGraphService, archiveIndexService, archiveBacklinkService, ...). Routes archive*.ts (20+ файлов): CRUD, pages, config, search (hybrid RRF), graph, export, index, import (copy-from-doc), schema templates.

Wiki embedding (services/wikiEmbeddingService.ts:8): indexWikiPage → chunk 800/100 → collector POST /api/ingest/wiki-pages (таблица wiki_pages, workspaceId = archive:<id>). Skill wiki_query (FTS + BFS wikilinks) и wiki_write (preview/dry-run + wiki_edit SSE).

Synthesis (services/synthesisService.ts): 5-pass pipeline: entity_extraction → summary_generation → candidate_search (BM25 tsvector) → llm_decision (CREATE/UPDATE/SKIP/FLAG_CONTRADICTION) → write_overview. PHI gate: если ArchiveConfig.localLLMOnly=true и provider не-ollama → abort pre-egress. Admin workflow: trigger → preview → approve/reject (routes/synthesis.ts).

Backup

Bree scheduler (services/backupSchedulerService.ts): @mintplex-labs/bree, closeWorkerAfterMs:0/timeout:0 (long-running). bootstrapJobs при старте (cleanStaleLocks → загружает BackupJob enabled). DB-level mutex через BackupLog.status="running"; cleanStaleLocks → running >2h = failed. Cron helper buildCronExpression(frequency, time, ...).

Pipeline (services/backupService.ts:282): dumpDatabase (pg_dump spawn no-shell) → collectFiles (storage dirs, path-traversal guard) → createArchive (archiver zlib level 9) → computeChecksum (SHA-256 streaming) → uploadWithRetry (3 retry backoff) → sendFailureNotification (SMTP). Safety backup pre-restore (runSafetyBackup).

Шифрование (services/encryptionService.ts): AES-256-GCM. Key: ENCRYPTION_KEY (base64 32 байта) или scryptSync(JWT_SECRET, salt). Decrypt chain rotation-safe: [current, ...LEGACY_PREVIOUS_ENCRYPTION_KEYS, scryptLegacy?].

Назначения (services/backup/providers/): LocalProvider, S3Provider, S3CompatibleProvider, DropboxProvider, GoogleDriveProvider, FtpProvider, SftpProvider, EmailProvider (интерфейс IBackupDestinationProvider). License backup_enabled + max_backup_destinations (Community 1, Enterprise ∞).

Restore (backupService.ts:617): restoreDatabaseWithPsql (psql --single-transaction --set ON_ERROR_STOP=on, атомарность all-or-nothing). Routes restore.ts + POST /api/backups/trigger (вручную).

Retention (backupRetentionService.ts): per-job retentionDays (по умолчанию 30); cleanup BackupLog success за cutoff + hard delete. Отдельные ошибки не блокируют.

Push (VAPID Web Push)

routes/push.ts: initVapid читает env VAPID_* (автогенерация в dev). GET /vapid-key (публичный), POST/DELETE /subscribe (license push_notifications), POST /test (admin). sendPushNotification(title, body) экспорт для event-trigger; удаляет subscription 410 Gone. Frontend: service worker API.

Webhook

services/webhookService.ts: dispatchWebhookEvent(event, payload) fire-and-forget параллельно. deliverWebhook: body {event, timestamp, data}, header X-Webhook-Signature: sha256=HMAC-SHA256(secret, body). Retry 3 (1s/2s/4s); 10 последовательных сбоев → auto-disable. События: document.processed, chat.completed, user.created, workspace.updated, mcp.installed/uninstalled, ... Триггерятся из eventLogService.logEvent (async post-DB). Routes webhooks.ts (license webhooks).