返回文档
指南 05

功能指南

功能指南,包含内部实现、数据流和 file:line 引用。针对每项功能:如何工作、如何使用、如何扩展。

6 分钟阅读

功能指南,包含内部实现、数据流和 file:line 引用。针对每项功能:如何工作、如何使用、如何扩展。

混合 RAG(Vector + PostgreSQL tsvector、RRF)

混合搜索分布在两个进程中:collector 只拥有向量组件(LanceDB/Qdrant);服务器拥有 FTS(PostgreSQL tsvector)以及融合两者的 RRF。

流程(packages/server/src/services/hybridSearchService.ts:104):

  1. agent 调用 skill rag_search(agent/builtinSkills.ts:27),参数为 query、workspaceId、archiveId。
  2. hybridSearch - Guard D-01:将 EMBEDDING_MODEL 与 workspace 中 Document 的 embeddingModel 进行比较;如果不匹配 → 跳过 vector leg(FTS-only,无错误)。
  3. Promise.all([vectorSearchViaCollector(), ftsSearch()]) - vector 通过 collector 的 POST /api/ingest/query;FTS 通过对 document_chunks.searchVector 执行 prisma.$queryRaw(GIN 索引、ts_rank)。
  4. RRF fusion(k=60):每个 chunk score += 1/(60+rank+1);sources Set 区分 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 以带时间戳的 ingest 元数据上的预过滤器形式到达 collector(pgvector JSONB 谓词 + Qdrant must-clause payload;LanceDB 和 Chroma 忽略新键并记录 warn 日志),FTS leg 获得针对 documents 表的参数化 SQL 谓词。retrieval 之后针对权威 documents 表的服务器端 backstop 保证每个 provider 上过滤器的正确性。从现在起 ingest 的文档始终可过滤;legacy 向量(pre-stamping)在过滤器激活期间被排除,并通过 admin re-embed 恢复可过滤性 - 无 schema change、不重新 embed 现有文档、无过滤器行为不变。

Grounded citations(v0.22.0):去重之后,grounding 过滤器按 relevance score 将每个文档的引用限制为 top-2,并删除文本与最终答案没有显著词汇重叠的 chunk 引用(保守 gate - wiki/archive 页面、web/memory 结果、无 chunk text 的引用以及非常简短的回答始终保留)。"No supporting evidence" 免责声明在过滤后的集合上判断(streaming 和非 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)。按 providerType:modelName 缓存的 singleton。

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 + factory getEmbeddingProvider + 在 KNOWN_MODEL_DIMS 中添加维度。RRF 调优 → RRF_K(hybridSearchService.ts:15)。更换 embedding 模型 → 通过 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-vs-回答分支。

循环:

  1. Setup:加载 WorkspaceAgentConfig、解析 enabledSkills、解析模板(resolveSystemPrompt)、前置 user.customInstructions。
  2. 解析 provider:resolveProviderConfig(providerId, model) → fallback buildFallbackConfig。
  3. constraint 模板:localLLMOnly(阻止非 ollama provider)、hybridSearchForced、citationRequired。
  4. 如果存在 ragContext(widget)→ 注入 system prompt,从 activeSkills 中移除 rag_search。
  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 迭代 → 丢弃 buffer;final-answer → 将 token replay 到真实的 onToken(progressive DLP flush)。
    • !toolCall → 尝试 resolveImplicitToolCall(XML-ish recovery tag)→ final answer,break。
    • Skill 未找到 → circuit breaker BOT-03:连续 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(基于 SystemConfig 的 KV,键为 ws_memory:<ws>:<key>)、document_temp_process(向 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。30s polling refreshModels → Provider.isAvailable。前端 3-tier fallback 在 useChat.ts 中带 toast Undo。服务器 fallback buildFallbackConfig(orchestrator.ts:879)。SSE done 包含 modelUsed、providerUsed。

扩展:新 skill → 在 builtinSkills.ts 中 registerSkill({...})。per-chat 模型 → PATCH /workspaces/:ws/chats/:chatId/model。Plan mode → 启用 WorkspaceAgentConfig.planMode。Budget 调优 → env AGENT_*(见 03 - Configuration)。

聊天流式(SSE)

POST /api/workspaces/:id/chat/stream → SSE。事件:token、status、citations({ sources })、done({ chatId, messageId, modelUsed, providerUsed, mcpSources })、error、plan、wiki_edit。前端:useChat.ts(@microsoft/fetch-event-source),通过 AbortController 中止。Slash /model、palette Cmd+K、对比 Cmd+Shift+M。有效模型的持久化通过 loadChat PATCH(stale default skipped,见 frontend/CLAUDE.md)。

文档、摄取、OCR

摄取(collector/routes/ingest.ts:219):multer(100 MB,MIME allowlist)→ 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 → mammoth fallback)、PPTX(officeparser)、XLSX(node-xlsx)、TXT/MD/CSV(UTF-8)、YouTube(youtube-transcript-plus,无 transcript 时 422)。ocrMode(auto/vision/skip)在 IngestUploadBodySchema 中。

Vision OCR(服务器端)(services/ragOcrService.ts:59):pdfjs-dist 加载 PDF(上限 50 页)→ 渲染 PNG → Ollama vision 模型 → strip grounding tags → 拼接。Job service ocrJobService.ts 带 approve/reject 工作流、qualityScore、credibilityScore。Routes routes/ocr.ts。前端 SettingsOcr(模型目录、job list、preview、custom instructions)。

DLP filter(services/dlpFilter.ts):scanContent(text) → {hasMatch, matches, redactedText}。从 v0.22 起 pattern 为 10 个内置 database-backed(表 dlp_patterns,最多 50 个带 live test preview 的自定义 pattern):在 6 个原始 pattern(email、credit_card、ssn、api_key sk-...、aws_key AKIA...、private_key PEM)之外新增了 4 个 EU/IT pattern - it_vat_iva(Partita IVA label-anchored)、it_codice_fiscale(16 字符,uppercase-only)、iban(国家前缀 + 长度 ≥ 15,ReDoS-safe)、eu_phone(IT/EU 电话,seed 为 DISABLED 待 admin review)。Redaction [REDACTED]。Gate DLP_ENABLED(CONFIG_DEFAULTS 中默认 `"true"`,permissions.ts:173);setting DLP_BYPASS_ROLES(角色 JSON 数组,默认 [])使选定角色豁免所有 scan/redaction,并记录 audit event dlp.bypassed。Admin toggle "Show/Hide DLP texts" 用于显示/重新隐藏 redact 的文本(仅 admin、session-scoped)。每个 audit event dlp.* 打上来源标签(source: "chat" | "widget",从 X-Widget-Id header 推导),并在 DLP Match History 面板中提供过滤器。DB 不可达时回退到内置集合(cache TTL 5 min);token-by-token 的 streaming flush 保留内置 pattern。集成在 routes/chat.ts(输入 + 输出);SSE done → dlp_warning: true;widget 显示 PIIWarningPrompt。

归档、Wiki、Synthesis

Archive:workspace-scoped 的多页 knowledge base,markdown 页面带 path slug 和 wikilink [[Title]]。14 个 archive service(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 → pre-egress abort。Admin 工作流: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)。通过 BackupLog.status="running" 实现 DB-level mutex;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)。Restore 前的 safety backup(runSafetyBackup)。

Encryption(services/encryptionService.ts):AES-256-GCM。Key:ENCRYPTION_KEY(base64 32 字节)或 scryptSync(JWT_SECRET, salt)。Decrypt chain 轮换安全:[current, ...LEGACY_PREVIOUS_ENCRYPTION_KEYS, scryptLegacy?]。

Destinations(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);清理超过 cutoff 的 BackupLog success + hard delete。单个错误不阻塞。

Push(VAPID Web Push)

routes/push.ts:initVapid 读取 VAPID_* env(dev 中自动生成)。GET /vapid-key(公开)、POST/DELETE /subscribe(license push_notifications)、POST /test(admin)。sendPushNotification(title, body) 导出供 event-trigger 使用;移除 410 Gone 的 subscription。前端: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)。

交叉链接