Guia de funcionalidades
Guia das features com internals, fluxo de dados e referências file:line. Para cada feature: como funciona, como usar, como estender.
Guia das features com internals, fluxo de dados e referências file:line. Para cada feature: como funciona, como usar, como estender.
RAG híbrido (Vector + PostgreSQL tsvector, RRF)
A busca híbrida está repartida em dois processos: o collector possui apenas a componente vetorial (LanceDB/Qdrant); o servidor possui a FTS (PostgreSQL tsvector) e o RRF que funde ambas.
Fluxo (packages/server/src/services/hybridSearchService.ts:104):
- O agente invoca a skill
rag_search(agent/builtinSkills.ts:27) comquery,workspaceId,archiveId. hybridSearch- Guard D-01: comparaEMBEDDING_MODELcom osembeddingModeldosDocumentdo workspace; em caso de mismatch → pula o leg vector (FTS-only, sem erro).Promise.all([vectorSearchViaCollector(), ftsSearch()])- vector viaPOST /api/ingest/querydo collector; FTS viaprisma.$queryRawemdocument_chunks.searchVector(índice GIN,ts_rank).- RRF fusion (k=60): para cada chunk
score += 1/(60+rank+1); o Setsourcesdistinguevector/fts/both. Tiebreaker determinístico: score DESC → documentId ASC → chunkIndex ASC. rag_searchformata os chunks com[Source: name (match: semantic+keyword|vector|fts, score: ...)]e retornaSourceCitation[].- Fallback archive D-07: se 0 resultados e
archiveIdpresente, relança emarchive:${archiveId}e retagueiasource: "archive". - Citações propagadas ao SSE
citations(routes/chat.ts:508).
Metadata filtering (v0.22): rag_search aceita filtros documentTypes (pdf/md/txt/csv/docx/xlsx) e dateFrom/dateTo (ISO; o limite apenas-data inclui o dia inteiro). Os filtros atravessam os dois legs da busca híbrida: o leg vector chega ao collector como pré-filtros nos metadados de ingest com timestamp (predicados JSONB pgvector + must-clause payload Qdrant; LanceDB e Chroma ignoram as novas chaves com warn logado), o leg FTS ganha predicados SQL parametrizados na tabela documents. Um backstop server-side pós-retrieval contra a tabela documents autoritativa garante a correção do filtro em cada provider. Os documentos ingeridos a partir de agora são sempre filtráveis; os vetores legacy (pre-stamping) ficam excluídos enquanto um filtro está ativo e recuperam filtratibilidade via re-embed admin - nenhum schema change, nenhum re-embed dos documentos existentes, comportamento sem filtros inalterado.
Citações grounded (v0.22.0): após a deduplicação, um filtro de grounding limita as citações por documento às top-2 por relevance score e elimina as citações de chunks cujo texto não compartilha sobreposição lexical significativa com a resposta final (gate conservador - páginas wiki/archive, resultados web/memory, citações sem chunk text e respostas muito curtas são sempre mantidas). O disclaimer "No supporting evidence" é julgado sobre o conjunto filtrado (caminhos streaming e não-streaming).
Chunking (collector/services/chunker.ts:16): RecursiveCharacterTextSplitter, chunkSize=1000, overlap=200, separadores ["\n\n","\n",". "," ",""]. Metadata: documentId, paragraph, charStart.
Embedding (collector/services/embeddings.ts): LocalEmbeddingProvider (Xenova all-MiniLM-L6-v2, 384-dim, air-gap) por padrão; OpenAIEmbeddingProvider (text-embedding-3-small, 1536-dim). Singleton cacheado por providerType:modelName.
Vector store (collector/services/vectorStore.ts): LanceDBProvider (padrão, caminho ${STORAGE_PATH}/vectors/lancedb, tabela por-workspace ws_<name>_<shortId>); QdrantProvider (placeholder enterprise).
FTS pass-through: o collector retorna chunks[].chunkText ao servidor, que grava DocumentChunk com searchVector = to_tsvector('simple', text). O collector nunca abre Prisma.
Estender: novo provider de embedding → implemente EmbeddingProvider + factory getEmbeddingProvider + dimensões em KNOWN_MODEL_DIMS. Tuning RRF → RRF_K em hybridSearchService.ts:15. Troca de modelo de embedding → reindex via Settings → Maintenance (POST /api/system/reindex-documents).
Agent Orchestrator (ReAct)
packages/server/src/agent/orchestrator.ts - entrada runAgent (non-stream, :78) e runAgentStreaming (:416). Padrão ReAct single-call: um único `streamLLM` por iteração é a decision call - streamResult.toolCall determina a ramificação tool-vs-resposta.
Loop:
- Setup: carrega
WorkspaceAgentConfig, parseiaenabledSkills, resolve o template (resolveSystemPrompt), prependeuser.customInstructions. - Resolve o provider:
resolveProviderConfig(providerId, model)→ fallbackbuildFallbackConfig. - Template de constraints:
localLLMOnly(bloqueia providers não-ollama),hybridSearchForced,citationRequired. - Se
ragContext(widget) → injeta no system prompt, removerag_searchdeactiveSkills. - Plan mode (
agentConfig.planMode): fase planning síncrona → SSEplanevent → injeção no prompt de execução. - ReAct
while(true):- Watchdog wallclock + token budget.
streamLLMcom buffered replay D-02: iteração tool-call → descarta o buffer; final-answer → replay de tokens aoonTokenreal (progressive DLP flush).!toolCall→ tentaresolveImplicitToolCall(recovery XML-ish tag) → final answer, break.- Skill não encontrada → circuit breaker BOT-03: trip a 3 consecutivos / 5 no total →
abortReason: "unknown_tool_breaker". - Loop detection, per-skill timeout,
truncateToolOutput, context cap.
finally: libera o slot de concorrência + persisteWorkspaceTokenUsageem todos os caminhos de saída.
Skills builtin (agent/builtinSkills.ts): rag_search, workspace_memory (KV em SystemConfig com chave ws_memory:<ws>:<key>), document_temp_process (upload ad-hoc ao collector), wiki_query (FTS archive_pages + fallback vetorial + BFS wikilinks, máx 10 páginas 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 mantém sysMsg + pinned user msg + last N.
Seleção de modelo + fallback: prioridade per-chat → workspace default → global default → ENV (providerService.resolveProviderConfig). deriveCapabilities etiqueta local-only/fastest/smartest/reasoning. Polling 30s refreshModels → Provider.isAvailable. Frontend fallback 3-tier em useChat.ts com toast Undo. Servidor fallback buildFallbackConfig (orchestrator.ts:879). SSE done inclui modelUsed, providerUsed.
Estender: nova skill → registerSkill({...}) em builtinSkills.ts. Modelo per-chat → PATCH /workspaces/:ws/chats/:chatId/model. Plan mode → habilita WorkspaceAgentConfig.planMode. Tuning de budget → env AGENT_* (ver 03 - Configuration).
Chat streaming (SSE)
POST /api/workspaces/:id/chat/stream → SSE. Eventos: token, status, citations ({ sources }), done ({ chatId, messageId, modelUsed, providerUsed, mcpSources }), error, plan, wiki_edit. Frontend: useChat.ts (@microsoft/fetch-event-source), abort via AbortController. Slash /model, paleta Cmd+K, comparação Cmd+Shift+M. Persistência do modelo efetivo via loadChat PATCH (stale default skipped, ver frontend/CLAUDE.md).
Documentos, ingestão, OCR
Ingestão (collector/routes/ingest.ts:219): multer (100 MB, allowlist MIME) → parseFile → chunkText → embed → vectorStore.addDocuments → notifyServerStatus (PUT /api/documents/:id/status com X-Collector-Secret). A resposta inclui chunks[].chunkText para a FTS do lado servidor.
Parser (collector/services/parser.ts): PDF (pdf-parse, <100 chars → OCR skip D-04, vision é server-side), DOCX (officeparser → fallback mammoth), PPTX (officeparser), XLSX (node-xlsx), TXT/MD/CSV (UTF-8), YouTube (youtube-transcript-plus, 422 se não há transcript). ocrMode (auto/vision/skip) em IngestUploadBodySchema.
OCR vision (server-side) (services/ragOcrService.ts:59): pdfjs-dist carrega o PDF (cap 50 páginas) → render PNG → modelo vision Ollama → strip grounding tags → concatenação. Job service ocrJobService.ts com workflow approve/reject, qualityScore, credibilityScore. Routes routes/ocr.ts. Frontend SettingsOcr (catálogo de modelos, job list, preview, custom instructions).
DLP filter (services/dlpFilter.ts): scanContent(text) → {hasMatch, matches, redactedText}. Desde a v0.22 os padrões são 10 built-in database-backed (tabela dlp_patterns, até 50 padrões custom com live test preview): além dos 6 originais (email, credit_card, ssn, api_key sk-..., aws_key AKIA..., private_key PEM) foram adicionados 4 padrões EU/IT - it_vat_iva (Partita IVA label-anchored), it_codice_fiscale (16 caracteres, uppercase-only), iban (prefixo de país + comprimento ≥ 15, ReDoS-safe), eu_phone (telefone IT/EU, seedado DISABLED para review admin). Redaction [REDACTED]. Gate DLP_ENABLED (default `"true"` em CONFIG_DEFAULTS, permissions.ts:173); setting DLP_BYPASS_ROLES (array JSON de roles, default []) isenta os roles selecionados de todo scan/redaction com audit event dlp.bypassed. Toggle admin "Show/Hide DLP texts" para revelar/re-ocultar os textos redigidos (somente admin, session-scoped). Cada audit event dlp.* marca a origem (source: "chat" | "widget") derivada do header X-Widget-Id, com filtros no painel DLP Match History. Fallback ao set built-in se o DB estiver inacessível (cache TTL 5 min); o streaming flush token-por-token mantém os padrões built-in. Integrado em routes/chat.ts (entrada + saída); SSE done → dlp_warning: true; o widget mostra PIIWarningPrompt.
Arquivos, Wiki, Synthesis
Archive: knowledge base multipágina workspace-scoped, páginas markdown com path slug e wikilink [[Title]]. 14 serviços archive (archivePageService, archiveGraphService, archiveIndexService, archiveBacklinkService, ...). Routes archive*.ts (20+ arquivos): 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 (tabela wiki_pages, workspaceId = archive:<id>). Skill wiki_query (FTS + BFS wikilinks) e wiki_write (preview/dry-run + wiki_edit SSE).
Synthesis (services/synthesisService.ts): pipeline 5-pass: entity_extraction → summary_generation → candidate_search (BM25 tsvector) → llm_decision (CREATE/UPDATE/SKIP/FLAG_CONTRADICTION) → write_overview. PHI gate: se ArchiveConfig.localLLMOnly=true e provider não-ollama → abort pre-egress. Workflow admin: trigger → preview → approve/reject (routes/synthesis.ts).
Backup
Scheduler Bree (services/backupSchedulerService.ts): @mintplex-labs/bree, closeWorkerAfterMs:0/timeout:0 (long-running). bootstrapJobs no startup (cleanStaleLocks → carrega os BackupJob enabled). Mutex DB-level via 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).
Encryption (services/encryptionService.ts): AES-256-GCM. Key: ENCRYPTION_KEY (base64 32 byte) ou scryptSync(JWT_SECRET, salt). Decrypt chain rotation-safe: [current, ...LEGACY_PREVIOUS_ENCRYPTION_KEYS, scryptLegacy?].
Destinos (services/backup/providers/): LocalProvider, S3Provider, S3CompatibleProvider, DropboxProvider, GoogleDriveProvider, FtpProvider, SftpProvider, EmailProvider (interface IBackupDestinationProvider). License backup_enabled + max_backup_destinations (Community 1, Enterprise ∞).
Restore (backupService.ts:617): restoreDatabaseWithPsql (psql --single-transaction --set ON_ERROR_STOP=on, atomicidade all-or-nothing). Routes restore.ts + POST /api/backups/trigger (manual).
Retention (backupRetentionService.ts): per-job retentionDays (default 30); cleanup BackupLog success além do cutoff + hard delete. Erros individuais não bloqueiam.
Push (VAPID Web Push)
routes/push.ts: initVapid lê as env VAPID_* (auto-geradas em dev). GET /vapid-key (público), POST/DELETE /subscribe (license push_notifications), POST /test (admin). sendPushNotification(title, body) export para event-trigger; remove subscription 410 Gone. Frontend: service worker API.
Webhook
services/webhookService.ts: dispatchWebhookEvent(event, payload) fire-and-forget paralelo. deliverWebhook: body {event, timestamp, data}, header X-Webhook-Signature: sha256=HMAC-SHA256(secret, body). Retry 3 (1s/2s/4s); 10 falhas consecutivas → auto-disable. Eventos: document.processed, chat.completed, user.created, workspace.updated, mcp.installed/uninstalled, ... Disparados por eventLogService.logEvent (async post-DB). Routes webhooks.ts (license webhooks).
Cross-link
- Widget embed: 06 - Widget Integration
- MCP: 07 - MCP Marketplace
- Endpoints: 04 - API Reference
- Env/knobs de budget: 03 - Configuration