DataHub 语义搜索架构深度解析双索引设计、Embedding 数据流与 k-NN 实战配置【免费下载链接】datahubThe Context Platform for your Data and AI Stack项目地址: https://gitcode.com/GitHub_Trending/da/datahub语义搜索Semantic Search是 DataHub 在传统关键词检索之上新增的向量相似度检索能力它将文档与查询文本转换为高维向量通过 OpenSearch k-NN 索引实现按语义而非字面的召回。本文以仓库内架构文档 docs/dev-guides/semantic-search/ARCHITECTURE.md 为主线结合 SemanticContent.pdl、application.yaml 及 ingestion 端 chunking 源码系统讲解 DataHub 语义搜索的设计哲学、双索引过渡架构、Embedding 生成与查询链路、分块策略与 k-NN 参数调优帮助你理解该功能如何工作并能在自己的部署中正确开启与配置。设计哲学为什么需要语义搜索传统关键词搜索keyword search存在三类固有局限词汇不匹配Vocabulary Mismatch用户使用的词与文档中的词不一致例如文档写的是data governance用户却搜metadata compliance同义词盲区Synonym Blindnessaccess request无法匹配permission request尽管两者语义相同缺乏上下文理解Context Ignorance关键词匹配停留在字符串层面不理解含义。语义搜索通过向量嵌入vector embeddings——一种能捕捉文本语义相似度的数值表示——来理解文本的含义从而缓解以上问题。DataHub 语义搜索的设计遵循四条核心原则见架构文档 Core Principles 一节非侵入Non-invasive语义搜索是增量叠加能力不会取代或破坏现有关键词搜索可配置Configurable由组织自行决定启用哪些实体类型、使用哪个嵌入模型可扩展Extensible新增嵌入模型无需改动整体架构通过配置即可挂接异步处理Async ProcessingEmbedding 生成异步进行不阻塞元数据摄取ingestion主流程。索引架构双索引Dual-Index过渡策略每个实体类型维护两个索引对每个开启语义搜索的实体类型DataHub 同时维护两个 OpenSearch 索引下文以 document 实体为例┌─────────────────────────────────┐ ┌─────────────────────────────────┐ │ documentindex_v2 │ │ documentindex_v2_semantic │ ├─────────────────────────────────┤ ├─────────────────────────────────┤ │ Standard OpenSearch index │ │ OpenSearch index with k-NN │ │ │ │ │ │ Fields: │ │ Fields: │ │ - urn │ │ - urn │ │ - title (text) │ │ - title (text) │ │ - text (text) │ │ - text (text) │ │ - browsePaths │ │ - browsePaths │ │ - tags │ │ - tags │ │ - ... │ │ - ... │ │ │ │ │ │ │ │ embeddings (nested object): │ │ │ │ - cohere_embed_v3: │ │ │ │ - model_version │ │ │ │ - generated_at │ │ │ │ - chunks[] (nested): │ │ │ │ - position │ │ │ │ - text │ │ │ │ - vector (knn_vector) │ └─────────────────────────────────┘ └─────────────────────────────────┘documentindex_v2标准 OpenSearch 索引承载现有关键词检索documentindex_v2_semantic启用 k-NN 的语义索引除常规字段外还以nested object形式保存embeddings其中每个模型名下再嵌套chunks[]包含position、text、vector等。为什么采用双索引过渡架构的工程取舍架构文档明确指出双索引是过渡性架构transitional architecture长期演进分三个阶段阶段一当前过渡期两个索引并行运行阶段二将全部搜索流量迁移到语义索引阶段三彻底下线v2索引。过渡方案带来四个直接收益零停机迁移语义能力构建期间用户可继续使用关键词搜索渐进验证可在全量上线前充分验证语义搜索的检索质量回滚安全出现问题时可随时回退到关键词搜索增量生成 Embedding可离线回填backfill向量而不阻塞线上操作。未来终态迁移完成后_semantic索引将成为主且唯一搜索索引同一份索引同时支持关键词搜索通过 OpenSearch 标准文本匹配语义搜索通过 k-NN 向量相似度。统一索引既简化了运维也降低了存储开销。从源码看该双索引体系由 metadata-io 模块下的索引构建器落地实现语义索引的 mapping 与 settings 分别由 V2SemanticSearchMappingsBuilder.java 和 V2SemanticSearchSettingsBuilder.java 负责索引实际创建由 ESIndexBuilder.java 统一调度。Embeddings 存储 Schema语义索引中的向量数据采用嵌套结构存放示例文档{ urn: urn:li:document:example-doc, title: Data Access Guide, text: How to request access to datasets..., embeddings: { cohere_embed_v3: { model_version: bedrock/cohere.embed-english-v3, generated_at: 2024-01-15T10:30:00Z, chunking_strategy: sentence_boundary_400t, total_chunks: 3, total_tokens: 850, chunks: [ { position: 0, text: How to request access to datasets..., character_offset: 0, character_length: 450, token_count: 95, vector: [0.023, -0.041, 0.087, ...] }, { position: 1, text: For sensitive data, additional approval..., character_offset: 450, character_length: 380, token_count: 82, vector: [0.019, -0.055, 0.091, ...] } ] } } }其中vector维度取决于所选模型文档示例为 Cohere Embed v3 的 1024 维。多模型支持embeddings结构天然支持同时存储多个嵌入模型的结果{ embeddings: { cohere_embed_v3: { ... }, openai_text_embedding_3: { ... }, custom_model: { ... } } }这带来三个能力不同模型的A/B 测试模型间的渐进迁移可新旧模型并存、逐步切换模型专属优化不同模型可按需调整索引参数。该设计与 SemanticContent.pdl 中的定义完全一致——embeddings字段类型为map[string, EmbeddingModelData]key 即模型标识如cohere_embed_v3、openai_ada_002。值得一提的是PDL 中还预留了skipReason如EMPTY_TEXT、BELOW_MIN_TEXT_LENGTH、NO_INDEXABLE_CONTENT与skippedAt两个字段用于区分实体本就不可嵌入与索引滞后或失败方便消费方排查问题。数据流从摄取到查询摄取链路Ingestion Flow整体链路为源系统 → 摄取连接器ingestion connector→ GMS → OpenSearch。┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ Source │ 1. Extract documents │ System │ └──────┬──────┘ │ ▼ ┌─────────────┐ 2. Generate embeddings for document content │ Ingestion │ (using connectors embedding provider) │ Connector │ └──────┬──────┘ │ ▼ ┌─────────────┐ 3. Send document embeddings to GMS │ GMS │ └──────┬──────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ OpenSearch │ │ ┌─────────────────────┐ ┌─────────────────────────────────┐ │ │ │ entityindex_v2 │ │ entityindex_v2_semantic │ │ │ │ (keyword search) │ │ (keyword vector search) │ │ │ │ - urn / title / ... │ │ - urn / title / text / │ │ │ └─────────────────────┘ │ embeddings.model.chunks[]. │ │ │ │ vector │ │ │ └─────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────┘关键一步在摄取连接器它在摄取时即完成文档 Embedding 生成并通过MCPMetadata Change Proposal将文档内容与向量一并提交给 GMS。这样做的好处是一致性每个被摄取文档从一开始就携带 Embedding简单性无需单独维护回填backfill任务新鲜度Embedding 始终与文档内容同步更新审计追踪向量变化记录在 Metadata Change LogMCL中隐私支持敏感数据源可在本地生成 Embedding仅共享向量而不外传原文。MCP 驱动的 Embedding 流转┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐ │ Source │───▶│ Ingestion │───▶│ DataHub GMS │ │ System │ │ Connector │ │ │ └──────────────┘ └──────┬───────┘ └──────────┬───────────┘ │ │ ▼ ▼ ┌──────────────────┐ ┌──────────────────┐ │ Generate document│ │ Process MCP and │ │ embeddings │ │ write to semantic │ │ (in connector) │ │ search index │ └────────┬─────────┘ └──────────────────┘ │ ▲ │ MCP with │ └─────SemanticContent─┘ aspectSemanticContent Aspect第一等公民的元数据向量并非存于旁路系统而是以 DataHub 标准的aspect形式落库。SemanticContentaspect 在 SemanticContent.pdl 中定义Aspect { name: semanticContent }其 MCP 载荷示例{ entityType: document, entityUrn: urn:li:document:my-doc, aspectName: semanticContent, aspect: { embeddings: { cohere_embed_v3: { modelVersion: bedrock/cohere.embed-english-v3, generatedAt: 1702234567890, totalChunks: 2, chunks: [ { position: 0, vector: [...], text: ... }, { position: 1, vector: [...], text: ... } ] } } } }在 ingestion 端该 aspect 的实际产出位于 chunking_source.py 的process_elements_inline()方法中——它对非结构化文档元素进行分块、生成 Embedding并以SemanticContentaspect 的 WorkUnit 形式输出该方法同样服务于 Notion 等外部文档源的 notion_source.py 与 confluence_source.py。隐私敏感场景每个 chunk 的text字段是可选的。这支持以下场景源数据包含敏感信息PII、商业秘密客户只希望把向量存进 DataHub而不存储源文本Embedding 在数据源本地生成。注意Embedding 是单向的——无法从向量反推出原始文本。查询链路GMS 侧生成查询向量与文档 Embedding 相反查询 EmbeddingQuery Embedding由 GMS 在搜索时生成使用 GMS 配置的嵌入提供者如 AWS Bedrock┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ GraphQL │───▶│ GMS │───▶│ Embedding │───▶│ OpenSearch │ │ Client │ │ │ │ Provider │ │ k-NN Query │ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │ ▼ Query embedding generated here (for search only)关键点GMS 的 Embedding Provider只负责查询向量文档向量一律由摄取连接器负责——这是文档嵌入与查询嵌入在职责上的硬性分工。完整的查询流程如下┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ GraphQL │───▶│ GMS │───▶│ Embedding │───▶│ OpenSearch │ │ Client │ │ │ │ Provider │ │ k-NN Query │ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │ semanticSearchAcrossEntities( │ query: how to access data │ ) │ ▼ ┌─────────────────────────────┐ │ Nested k-NN Query: │ │ { │ │ nested: { │ │ path: embeddings │ │ .cohere_embed_v3 │ │ .chunks, │ │ query: { │ │ knn: { │ │ ...chunks.vector:│ │ { vector: [...], │ │ k: 10 } │ │ } │ │ } │ │ } │ │ } │ └─────────────────────────────┘查询通过semanticSearchAcrossEntitiesGraphQL 入口发起如查询 how to access dataGMS 生成查询向量后对语义索引发起nested k-NN 查询nested路径定位到embeddings.model.chunksknn在该层级的vector字段上执行近似最近邻检索示例k: 10表示返回 Top-10 命中。服务端实现位于 SemanticSearchService.java并针对 OpenSearch 与 Elasticsearch 8 分别提供了查询适配见 OpenSearchSearchClientShim.java 与 Es8SearchClientShim.java。分块策略Chunking Strategy为什么需要分块嵌入模型有 token 上限例如 Cohereembed-english-v3.0为 512 tokens长文档必须拆分为块Token 限制模型无法处理无限长度的文本精确性更小的块允许更精确的匹配相关性一篇文档可能只有某个段落与查询高度相关。分块算法def chunk_text(text, max_tokens400): Chunk text at sentence boundaries, respecting token limits. 1. Split text into sentences 2. Accumulate sentences until approaching limit 3. Save chunk, start new accumulation 4. Handle oversized sentences by character splitting 参数说明max_tokens目标块大小默认 400chars_per_token字符/token 估算比例默认约 4 字符 ≈ 1 token。在真实 ingestion 实现中分块参数被抽象为 chunking_config.py 中的ChunkingConfig配置项默认值说明strategyby_title分块策略可选basic或by_title按标题/章节切分max_characters500每个块的最大字符数overlap0块与块之间的字符重叠量combine_text_under_n_chars100小于该尺寸的碎片块将合并到相邻块同时EmbeddingConfig 还提供批量与限流参数batch_size默认 25单次 Embedding API 调用的文档数、request_timeout默认 60s、rate_limit默认开启、documents_per_minute默认 300 篇/分钟。这些参数让生产环境可以控制外部 Embedding API 的调用压力。另外需要注意model_embedding_key会被校验为仅含字母、数字与下划线因为 Elasticsearch 字段名不允许.、:等标点如 Bedrock Titan 的模型 IDamazon.titan-embed-text-v2:0就不能直接用作字段名需改用cohere_embed_v3这类下划线形式。块元数据Chunk Metadata每个块保存用于调试与分析的元数据{ position: 0, // Order in document text: ..., // Chunk content character_offset: 0, // Start position in original character_length: 450, // Length in characters token_count: 95, // Estimated tokens vector: [...] // Embedding vector }k-NN 搜索配置OpenSearch k-NN 设置语义索引使用 OpenSearch 的 k-NN 插件引擎为FAISS映射示例{ settings: { index.knn: true }, mappings: { properties: { embeddings: { type: nested, properties: { cohere_embed_v3: { type: nested, properties: { chunks: { type: nested, properties: { vector: { type: knn_vector, dimension: 1024, method: { name: hnsw, engine: faiss, space_type: cosinesimil, parameters: { ef_construction: 128, m: 16 } } } } } } } } } } } }HNSW 参数说明参数值说明ef_construction128建图精度越大越准建索引越慢m16每个节点的连接数越大越准内存占用越高space_typecosinesimil相似度度量余弦相似度在 application.yaml 中如何落地上述 k-NN 参数在 GMS 配置 application.yamlelasticsearch.entityIndex.semanticSearch段中均可通过环境变量覆盖且索引维度、引擎、度量空间逐模型配置elasticsearch: entityIndex: semanticSearch: enabled: ${ELASTICSEARCH_SEMANTIC_SEARCH_ENABLED:false} enabledEntities: ${ELASTICSEARCH_SEMANTIC_SEARCH_ENTITIES:document} models: text_embedding_3_large: vectorDimension: ${ELASTICSEARCH_SEMANTIC_VECTOR_DIMENSION:3072} knnEngine: ${ELASTICSEARCH_SEMANTIC_KNN_ENGINE:faiss} spaceType: ${ELASTICSEARCH_SEMANTIC_SPACE_TYPE:cosinesimil} efConstruction: ${ELASTICSEARCH_SEMANTIC_EF_CONSTRUCTION:128} m: ${ELASTICSEARCH_SEMANTIC_M:16}配置文件还预置了多个常用模型的索引参数模板包括nomic_embed_textOllama 默认768 维、gemini_embedding_001Vertex AI默认 3072 维、snowflake_arctic_embed_s384 维、snowflake_arctic_embed_l1024 维、bge_base_en_v1_5768 维等均使用faisscosinesimilef_construction: 128m: 16。此外 EntityIndexConfiguration.java 与 SearchServiceConfiguration.java 分别承载了语义搜索配置与整体搜索配置的 Java 绑定。嵌入提供者Embedding Provider配置GMS 查询侧支持六种 Embedding Provider见 EmbeddingProviderConfiguration.java在semanticSearch.embeddingProvider段配置Provider 类型说明模型示例默认关键环境变量aws-bedrockAWS Bedrock Runtime APIcohere.embed-english-v31024 维BEDROCK_EMBEDDING_AWS_REGION默认us-west-2、BEDROCK_EMBEDDING_MODELopenaiOpenAI Embeddings APItext-embedding-3-large3072 维OPENAI_API_KEY、OPENAI_EMBEDDING_MODEL、OPENAI_EMBEDDING_ENDPOINTcohereCohere Embed APIembed-english-v3.01024 维COHERE_API_KEY、COHERE_EMBEDDING_MODEL、COHERE_EMBEDDING_ENDPOINTlocal本地 OpenAI 兼容服务Ollama、LM Studio、llama.cpp 等nomic-embed-text768 维LOCAL_EMBEDDING_ENDPOINT默认http://localhost:11434/v1/embeddings、LOCAL_EMBEDDING_MODELvertex_aiGoogle Vertex AI Embeddings APIgemini-embedding-001VERTEX_AI_PROJECT_ID、VERTEX_AI_LOCATION默认us-east1、VERTEX_AI_EMBEDDING_MODELonnxJVM 进程内 ONNX Runtime 推理无需外部服务snowflake_arctic_embed_s/l、bge_base_en_v1_5ONNX_EMBEDDING_MODEL_NAME、ONNX_EMBEDDING_MODEL_DIR、ONNX_EMBEDDING_POOLING默认cls、ONNX_EMBEDDING_QUERY_INSTRUCTION基础配置项application.yaml L827-834semanticSearch: embeddingProvider: type: ${EMBEDDING_PROVIDER_TYPE:openai} maxCharacterLength: ${EMBEDDING_PROVIDER_MAX_CHAR_LENGTH:2048}其中maxCharacterLength默认 2048对应 Cohere Embed v3 对请求体的 2048 字符硬限制独立于 token 上下文窗口。各提供者的要点与约束源码注释与工厂实现 EmbeddingProviderFactory.java 中均有明确校验openai必须提供 API keyOPENAI_API_KEY或配置项否则启动即抛异常endpoint 可指向 Azure OpenAI 部署地址。aws-bedrock依赖共享的defaultAwsCredentialsProviderBeanbedrock.awsRegion必填且支持与 Pod 的AWS_REGION不同的跨区域访问。cohere必须提供COHERE_API_KEY。local兼容任何 OpenAI 兼容端点Docker Compose 下quickstart-ai profile使用http://ollama:11434/v1/embeddings。vertex_ai必须配置projectId与location使用 Application Default Credentials启动时即验证凭据fail-fast。onnxmodelName必须匹配semanticSearch.models中的某个 keymodelDir需包含model.onnx或model_quantized.onnx与tokenizer.json启动时会对模型实际输出维度与配置的vectorDimension做一致性校验不匹配会直接拒绝启动避免建出维度错误的索引pooling必须与文档侧一致默认cls适用于 Arctic-embed 与 BGE 系列否则查询与文档向量不在同一子空间kNN 召回会失效。重要当语义搜索未启用时工厂会返回 NoOpEmbeddingProvider一个使用即抛异常的占位实现确保系统无需嵌入配置也能正常启动。在摄取侧连接器默认会通过 AppConfig API 从服务器自动拉取嵌入配置见 chunking_config.py 的get_semantic_search_config()确保文档侧与查询侧使用同一模型若本地显式配置则会与服务器配置逐项比对provider、model、model_embedding_key、region 等不一致时报错并给出修复建议另有allow_local_embedding_config: true作为破窗开关不推荐。安全考虑数据隐私Embedding 存储向量与文档同库存放沿用相同的访问控制外部 API 调用Embedding Provider 会收到文档文本查询侧收到查询文本需确保符合合规要求凭据管理API key 与 AWS 凭据必须妥善保管可通过环境变量注入。访问控制语义搜索遵循 DataHub 现有的访问控制体系用户只能看到自己有权限查看的结果返回结果前会强制校验实体级权限。性能考虑以下数据为架构文档给出的工程评估量级实际数值取决于索引规模、硬件与模型应以实测为准。索引性能双写影响双索引并行写入带来约 10%-20% 的写入延迟增加Embedding 生成异步执行不阻塞摄取主流程批量处理Embedding 以批量方式生成以提升效率摄取端默认batch_size: 25。查询性能k-NN 开销每次查询约 50-200ms取决于索引规模查询向量生成约 100-300ms端到端总延迟典型 200-500ms。扩容建议索引规模建议 10 万篇文档单节点即可10 万 - 100 万篇考虑部署专用 k-NN 节点 100 万篇推荐分片sharding与副本replicas未来增强方向架构文档展望了两个演进方向混合搜索Hybrid Search融合关键词与语义两路打分提升整体相关性模型微调Model Fine-tuning面向特定领域微调 Embedding 模型进一步提升准确度。配合前文所述迁移完成后统一索引的终态这两项增强将共同构成 DataHub 搜索体验的下一步演进。小结DataHub 语义搜索是一套设计克制的增量能力通过双索引过渡架构保证零停机迁移与回滚安全通过SemanticContent aspect MCP让向量成为一等公民元数据天然获得审计追踪与权限控制通过文档侧由连接器生成、查询侧由 GMS 生成的职责分工兼顾数据新鲜度与隐私边界分块策略与 k-NN 参数ef_construction、m、space_type则提供了精确性与成本之间的调节旋钮。理解这条链路后你可以在 application.yaml 中通过semanticSearch配置段与ELASTICSEARCH_SEMANTIC_*、EMBEDDING_PROVIDER_*等环境变量为自己的部署启用并调优语义搜索。【免费下载链接】datahubThe Context Platform for your Data and AI Stack项目地址: https://gitcode.com/GitHub_Trending/da/datahub创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考