5942 字
约 19 分钟
3
语义搜索入门:什么时候使用,以及如何实现一套语义检索系统

语义搜索入门:什么时候使用,以及如何实现一套语义检索系统

一、什么是语义搜索?

在传统搜索系统中,我们搜索一段内容,通常依赖的是:

关键词匹配

例如文档中存在:

员工离职后,需要及时关闭内部系统账号和访问权限。

如果用户搜索:

员工 离职 账号

传统关键词搜索很容易找到这篇文档,因为:

员工
离职
账号

这些关键词真实存在于文档中。

但是现实中的用户并不一定知道文档原文是怎么写的。

例如用户可能搜索:

员工辞职之后公司账号怎么办?

此时用户使用的是:

辞职
公司账号
怎么办

而文档中使用的是:

离职
系统账号
关闭访问权限

从字面来看,两边并不完全相同。

但是从人的理解来看:

辞职
≈
离职

公司账号怎么办
≈
关闭内部系统账号和访问权限

它们表达的其实是同一个意思。

这就是语义搜索想解决的问题。

所谓:

Semantic Search

也就是:

不仅仅判断“有没有相同的词”,而是判断“用户的问题和文档表达的意思是否相似”。

可以简单记成:

关键词搜索
=
找相同的字

语义搜索
=
找相近的意思

二、为什么会出现语义搜索?

传统关键词检索其实非常强大。

像 Elasticsearch、OpenSearch 这一类搜索引擎,通过:

倒排索引
+
BM25

已经能够很好地解决大量搜索问题。

例如搜索:

Spring Boot

文档中出现:

Spring Boot 项目开发规范

自然可以匹配。

但是问题在于:

用户表达一个意思,并不一定使用文档中的原始词语。

例如知识库里面写:

用户无法正常登录系统时,可以通过账号中心执行密码重置。

用户却问:

忘记密码了怎么办?

关键词搜索可能重点寻找:

忘记
密码
怎么办

但是原文里面可能根本没有:

忘记密码

只有:

无法登录
密码重置

语义搜索则希望能够理解:

忘记密码怎么办
≈
密码重置

因此语义搜索特别适合处理:

用户不知道标准术语
用户不知道文档原文
用户使用自然语言提问
同一个意思存在很多不同表达方式

这也是为什么现在 AI 知识库、RAG 系统越来越依赖语义搜索。


三、什么时候应该使用语义搜索?

语义搜索并不是所有搜索场景都适合。

判断要不要使用语义搜索,可以先问一个问题:

用户搜索时,是在寻找“某个准确的词”,还是在表达“某个意思”?

如果用户主要是在表达意思,那么语义搜索通常非常适合。

比如企业知识库。

文档:

员工工作满一年后,可以享受五天带薪年假。

用户可能问:

一年可以休几天年假?

也可能问:

公司的带薪假有多少天?

还可能问:

工作一年以后能休多少天?

这三个问题的字面完全不同。

但实际上都在问:

年假天数

这种场景就非常适合语义搜索。

再比如客服知识库。

知识库写:

订单付款完成后,如果商家尚未发货,可以在订单详情页申请退款。

用户问:

东西还没寄出来,我能退钱吗?

关键词几乎没有完全对应。

但是语义非常接近。

所以:

知识库问答
客服问答
企业文档搜索
技术文档搜索
合同搜索
论文搜索
FAQ搜索
RAG

通常都非常适合语义搜索。


四、什么时候不要只使用语义搜索?

这是实际做项目时非常重要的一点。

不要产生一个错误认知:

语义搜索比关键词搜索高级

所以:

语义搜索 > 关键词搜索

实际上不是。

例如用户搜索:

ERR_10086

这时候他可能就是想找到:

ERR_10086

而不是寻找:

和 ERR_10086 意思相似的内容

再例如:

订单号:

202609100001

用户搜索:

202609100001

最重要的是:

精确匹配

再例如搜索:

NullPointerException

或者:

Spring Boot 3.5.2

或者:

GB/T 22239-2019

这些:

错误码
订单号
产品型号
版本号
标准编号
类名
方法名
专有名词

关键词搜索往往更可靠。

所以可以这样理解:

搜索内容 更适合
订单号 关键词搜索
ERR_10086 关键词搜索
NullPointerException 关键词搜索
产品型号 关键词搜索
“忘记密码怎么办” 语义搜索
“员工辞职后账号怎么办” 语义搜索
“公司一年可以休多少天假” 语义搜索
知识库自然语言问答 语义搜索

而真正成熟的系统,通常不是二选一。

而是:

关键词搜索
+
语义搜索

这叫:

Hybrid Search

也就是:

混合检索

这一点后面再详细讲。


五、语义搜索到底是怎么实现的?

语义搜索最核心的技术只有两个概念:

Embedding
+
Vector Search

也就是:

文本向量化
+
向量相似度搜索

整个过程可以先记成:

文本
 ↓
Embedding Model
 ↓
Vector
 ↓
Vector Database
 ↓
相似度搜索

这里最重要的是:

Embedding

六、什么是 Embedding?

Embedding 可以简单理解为:

把一段文字转换成一组能够表示其语义特征的数字。

例如:

员工离职后需要关闭账号

经过 Embedding 模型:

员工离职后需要关闭账号

          ↓

     Embedding Model

          ↓

[0.12, -0.53, 0.88, 0.21, ...]

这串数字:

[0.12, -0.53, 0.88, 0.21, ...]

就叫:

Vector

中文叫:

向量

真实情况下可能不是四个数字,而是几百、上千甚至更多维。

例如:

[0.121,
 -0.532,
  0.882,
  0.217,
  ...
]

重点并不是记住这些数字。

重点在于:

意思越接近的文本,在向量空间中的位置通常越接近。

例如:

员工离职以后需要关闭账号

可能转换成:

Vector A

而:

员工辞职以后公司账号怎么办?

转换成:

Vector B

因为两个句子的语义接近:

Vector A
≈
Vector B

于是系统就可以通过比较两个向量的距离,判断:

这两句话表达的意思非常相似

七、向量相似度是什么意思?

假设知识库里有三段内容:

A:
员工离职后需要关闭账号。

B:
员工每年可以享受五天带薪年假。

C:
公司服务器每天凌晨进行数据库备份。

分别生成:

A → Vector A

B → Vector B

C → Vector C

用户搜索:

辞职后系统权限怎么办?

同样进行:

用户问题
   ↓
Embedding
   ↓
Query Vector

然后计算:

Query Vector
和
Vector A

有多相似?

再计算:

Query Vector
和
Vector B

有多相似?

以及:

Query Vector
和
Vector C

有多相似?

最终可能得到:

A   0.94

B   0.55

C   0.21

那么系统自然认为:

A

最符合用户的问题。

这就是:

Vector Similarity Search

也就是:

向量相似度检索

八、常见的相似度计算方式

向量检索中经常会看到:

Cosine Similarity

Dot Product

Euclidean Distance

刚入门的时候不用急着研究复杂数学。

其中非常常见的是:

Cosine Similarity

也就是:

余弦相似度

它本质上是在比较:

两个向量的方向有多接近

可以简单理解:

意思越接近
   ↓
Vector方向越接近
   ↓
相似度越高

例如:

员工辞职后账号怎么办?

员工离职后关闭系统访问权限。

相似度:

0.93

而:

员工辞职后账号怎么办?

服务器每天凌晨两点备份数据库。

相似度可能:

0.17

于是就能把真正相关的文档排到前面。


九、为什么需要向量数据库?

假设你的知识库里面只有:

10条数据

实际上根本不需要专门的向量数据库。

完全可以:

Query Vector
↓
依次和10个Vector比较
↓
排序

但是如果变成:

100万 Chunk

如果每次用户搜索都:

Query Vector

和

100万个 Vector

全部计算一次

性能就会出现问题。

于是需要:

Vector Index

也就是:

向量索引

通过专门的数据结构快速找到:

距离 Query Vector 最近的一批向量

于是出现了:

Vector Database

例如:

Milvus

Qdrant

Weaviate

Pinecone

但需要特别注意:

做语义搜索,不一定必须拥有一个独立的向量数据库。

比如:

Elasticsearch

本身就支持向量搜索。

PostgreSQL 可以通过:

pgvector

支持。

所以:

语义搜索
≠
必须使用 Milvus

更加准确应该是:

语义搜索
=
Embedding
+
Vector
+
Vector Index/Search

至于 Vector 放在哪里,是架构选择。


十、完整的语义搜索系统分成两个阶段

真正做语义搜索,一定要理解:

知识入库

和:

用户检索

是两个不同阶段。


十一、第一阶段:知识入库

假设用户上传:

员工管理制度.pdf

第一步一般不是直接 Embedding。

而是:

PDF
 ↓
Apache Tika
 ↓
提取文本

例如:

第一章 入职管理……

第二章 请假管理……

员工每年可以享受五天带薪年假……

第三章 离职管理……

员工离职后应及时关闭所有内部系统访问权限……

然后进入:

Chunk

也就是:

文本切片

十二、为什么必须进行 Chunk?

假设一本 PDF:

300页

你如果直接:

整本PDF
   ↓
Embedding
   ↓
一个Vector

那么这个 Vector 就要同时表达:

入职
请假
薪资
离职
报销
考勤
绩效
信息安全

大量不同主题。

这样语义就会变得非常模糊。

所以一般需要:

Document
 ↓
Chunk

例如:

Chunk 1

员工入职流程……
Chunk 2

员工每年享受五天带薪年假……
Chunk 3

员工离职以后需要关闭系统账号……

每个 Chunk 单独:

Chunk
 ↓
Embedding
 ↓
Vector

这样:

一个 Vector

对应一个比较明确的语义单元。


十三、Chunk 应该多大?

这是 RAG 和语义搜索里面非常重要的问题。

假设 Chunk 太小:

员工离职。

上下文可能不够。

你不知道:

离职以后到底怎么处理?

如果 Chunk 太大:

整整20页员工制度

又会包含太多不同语义。

所以 Chunk 本质上是在寻找:

上下文完整性

和:

语义纯度

之间的平衡。

实际项目中经常会根据:

Token
字符数
段落
标题
Markdown结构

进行切分。

例如:

标题:

3.2 离职管理

正文:

员工提出离职以后,应由部门负责人确认,
同时通知管理员关闭OA、邮箱、VPN等系统权限。

最好整个作为:

一个 Chunk

而不要机械切成:

员工提出离职以后,应由部门

和:

负责人确认,同时通知管理员……

否则语义会被破坏。

所以好的 Chunking 往往应该:

优先保持自然语义边界

而不仅仅是:

每500个字符一刀切

十四、知识入库完整流程

到这里整个入库链路就很清晰了:

                   PDF / Word

                       ↓

                  Apache Tika

                       ↓

                     Text

                       ↓

                   Cleaning

                       ↓

                    Chunking

                       ↓

             ┌─────────┴─────────┐
             ↓                   ↓

          Chunk Text           Metadata

             ↓

          Embedding

             ↓

            Vector

             ↓

       Vector Search Engine

其中一条数据最终可能保存成:

{
  "id": "chunk_10001",
  "documentId": "doc_1001",
  "title": "员工管理制度",
  "chapter": "离职管理",
  "content": "员工离职以后,应及时关闭内部系统访问权限。",
  "page": 18,
  "vector": [
    0.12,
    -0.53,
    0.88,
    0.21
  ]
}

注意这里并不是只保存 Vector。

还应该保留:

原始文本
+
Vector
+
Metadata

因为最后给用户看的仍然是:

原始文本

而不是:

[0.12, -0.53, 0.88 ...]

十五、第二阶段:用户进行语义搜索

假设用户输入:

辞职以后公司的系统账号怎么办?

第一步:

Query
 ↓
Embedding
 ↓
Query Vector

注意:

查询时通常应该使用和知识入库时兼容的 Embedding 模型。

如果知识库使用:

Embedding Model A

生成 Vector。

查询突然换:

Embedding Model B

两套向量空间未必兼容。

这样搜索结果就可能出现严重问题。

因此:

Document Embedding

和:

Query Embedding

通常需要保持模型一致。


十六、什么是 TopK?

用户问题生成 Query Vector 后,需要去向量库搜索。

例如:

Search TopK = 5

意思就是:

找和当前问题最相似的前 5 个 Chunk。

可能得到:

Top 1
员工离职后应关闭内部系统账号。
similarity = 0.94


Top 2
员工离职需要归还电脑及门禁卡。
similarity = 0.87


Top 3
管理员负责企业系统账号权限维护。
similarity = 0.81


Top 4
新员工入职需要创建系统账号。
similarity = 0.72


Top 5
员工账号权限每季度需要审计。
similarity = 0.68

这就是:

TopK Retrieval

因此你以后看到:

TopK = 5

TopK = 10

TopK = 20

不要觉得神秘。

就是:

先召回前几个最相关结果

十七、Metadata Filter 为什么非常重要?

假设你的知识库有:

1000家公司

用户 A 属于:

Company 100

用户搜索:

年假多少天?

如果纯粹做:

Vector Search

很可能搜到:

Company 200

的员工制度。

语义完全正确。

但是:

业务上完全错误。

所以语义搜索不能只关注:

Vector Similarity

还必须结合:

Metadata Filter

例如:

companyId = 100

于是查询实际上应该变成:

WHERE companyId = 100

AND

Vector Similarity Search

再比如知识库权限:

departmentId

userId

knowledgeBaseId

documentType

language

createdAt

都可能需要参与过滤。

所以真正的语义检索应该理解成:

业务过滤
+
向量相似度检索

而不是单纯:

找最近的 Vector

十八、为什么语义搜索通常要配合关键词搜索?

来看一个问题:

Spring Boot 3.5 Redis ERR_10086 怎么解决?

这里有两种信息。

第一种:

Spring Boot 3.5

Redis

ERR_10086

这些是:

精确关键词

尤其:

ERR_10086

非常适合 BM25。

而:

怎么解决

属于:

用户意图

语义搜索更擅长。

所以最合理的是:

                  Query

                    ↓

          ┌─────────┴─────────┐
          ↓                   ↓

     Keyword Search       Vector Search

          ↓                   ↓

         BM25            Semantic Score

          │                   │
          └─────────┬─────────┘

                    ↓

               Merge / Fusion

                    ↓

                 Ranking

                    ↓

                  TopK

这个就叫:

Hybrid Search

也就是:

混合检索

现在做比较成熟的知识库系统,一般都应该认真考虑 Hybrid Search。


十九、Hybrid Search 为什么通常比纯 Vector 更稳定?

假设用户搜索:

Redis ERR_10086

语义搜索可能找到:

Redis连接失败问题排查

意思确实非常相关。

但是还有一个文档:

ERR_10086 Redis Connection Timeout

显然第二个更应该优先。

关键词搜索能很好捕获:

ERR_10086

语义搜索又能够捕获:

connection timeout
≈
连接失败

两个组合起来,就会更加稳定。

所以可以简单理解:

BM25
负责:

“词准不准”


Vector Search
负责:

“意思准不准”


Hybrid Search
负责:

“词和意思都尽量准”

二十、语义搜索之后为什么还会有 Rerank?

这是再往生产系统走一步非常重要的知识。

假设 Vector Search:

TopK = 20

召回:

20个 Chunk

向量检索本身非常擅长:

快速从大量内容里面召回相关结果

但第一名和第二名到底谁更准确,不一定永远排序完美。

因此可以增加:

Reranker

也叫:

重排序模型

流程:

100万 Chunk

    ↓

Vector Search

    ↓

Top 20

    ↓

Reranker

    ↓

重新判断 Query 与每个 Chunk 的相关性

    ↓

Top 5

所以:

Vector Search

更像:

快速海选

而:

Reranker

更像:

精细复审

最后送给 LLM 的可能只有:

Top 5

二十一、完整的生产级语义搜索架构

现在可以把所有东西串起来:

                    【知识入库】

PDF / Word / PPT / HTML
          ↓
      Apache Tika
          ↓
         Text
          ↓
       Cleaning
          ↓
       Chunking
          ↓
        Chunk
          ↓
      Embedding
          ↓
        Vector
          ↓
   Vector Search Engine



                    【用户查询】

      用户自然语言 Query
               ↓
           Embedding
               ↓
          Query Vector
               ↓
       ┌───────┴────────┐
       ↓                ↓
   Keyword Search   Vector Search
       ↓                ↓
      BM25          Semantic TopK
       └───────┬────────┘
               ↓
            Fusion
               ↓
         Metadata Filter
               ↓
            Rerank
               ↓
             TopK
               ↓
         返回搜索结果

如果这是 RAG 系统:

TopK
 ↓
LLM Prompt
 ↓
Large Language Model
 ↓
回答用户问题

于是完整链路变成:

Document
 ↓
Tika
 ↓
Chunk
 ↓
Embedding
 ↓
Vector Store
 ↓
Semantic Search
 ↓
Hybrid Search
 ↓
Rerank
 ↓
LLM
 ↓
Answer

这基本就是一个现代知识库系统的核心主干。


二十二、一个最小语义搜索 Demo 应该怎么做?

假设我们只有三个知识:

Document 1

员工每年可以享受五天带薪年假。


Document 2

员工离职以后需要关闭内部系统账号。


Document 3

公司数据库每天凌晨两点进行备份。

第一步:

Embedding(document1)
→ vector1

Embedding(document2)
→ vector2

Embedding(document3)
→ vector3

保存:

document1 + vector1

document2 + vector2

document3 + vector3

用户:

辞职之后系统权限怎么办?

执行:

Embedding(query)
→ queryVector

然后:

similarity(queryVector, vector1)

similarity(queryVector, vector2)

similarity(queryVector, vector3)

假设:

vector1 = 0.41

vector2 = 0.94

vector3 = 0.12

最终返回:

员工离职以后需要关闭内部系统账号。

恭喜。

这实际上已经完成了一套最简单的:

Semantic Search

系统。

Vector Database 只是把:

存储
索引
查询
排序

这几个步骤工业化了。


二十三、语义搜索最容易踩的几个坑

第一个坑:

整个 PDF 生成一个 Vector

通常不好。

应该:

Document
↓
Chunk
↓
Vector

第二个坑:

Chunk 切得太碎

导致上下文缺失。

第三个坑:

Chunk 切得太大

导致一个 Vector 包含太多主题。

第四个坑:

只相信 similarity score

实际上还必须考虑:

权限
租户
知识库
文档类型
时间
标签

等 Metadata。

第五个坑:

只做 Vector Search

对于:

编号
错误码
版本号
人名
产品名
专有名词

效果可能不如:

BM25

因此正式项目最好考虑:

Hybrid Search

第六个坑:

Embedding 模型换了

但是历史 Vector 没有重新生成。

这时候:

新Query Vector

和:

旧Document Vector

可能处于不同向量空间。

结果就会异常。

因此更换 Embedding 模型时通常要考虑:

重新 Embedding

二十四、做项目时推荐怎样渐进实现?

第一版不要一上来就设计:

Tika
+
Kafka
+
Milvus
+
Elasticsearch
+
Reranker
+
LLM
+
十几个微服务

这样反而很容易把自己绕进去。

最开始可以:

Document
 ↓
Chunk
 ↓
Embedding
 ↓
Vector Store
 ↓
TopK Search

先把:

纯语义搜索

跑通。

然后增加:

Metadata Filter

再增加:

Keyword Search

形成:

Hybrid Search

再增加:

Reranker

最后如果需要 AI 问答:

Retrieval
 ↓
LLM

这样逐步演化会比较清晰。


二十五、最终总结

理解语义搜索,其实只需要抓住一条主线:

文本
 ↓
Embedding
 ↓
Vector
 ↓
Vector Similarity Search
 ↓
找到意思最接近的文本

传统关键词检索关注:

“有没有这个词?”

而语义检索关注:

“是不是这个意思?”

例如:

文档:

员工离职后应关闭所有内部系统访问权限。


用户:

辞职以后公司的账号怎么办?

两句话字面不同。

但是:

Embedding
        ↓
Vector
        ↓
Similarity
        ↓
高度相似

于是语义搜索能够把它找出来。

如果进一步放到真正的知识库系统里:

                 用户上传文件
                       ↓
                  Apache Tika
                       ↓
                      Text
                       ↓
                     Chunk
                       ↓
                   Embedding
                       ↓
                     Vector
                       ↓
               Vector Database
                       ↓
                Semantic Search
                       ↓
                Hybrid Search
                       ↓
                    Rerank
                       ↓
                     TopK
                       ↓
                      LLM
                       ↓
                     Answer

你可以把整个体系最终压缩成几个概念:

Tika
负责:
文件 → 文本

Chunk
负责:
长文本 → 小语义单元

Embedding
负责:
文本 → Vector

Vector Search
负责:
找意思相似的 Chunk

BM25
负责:
找关键词匹配的 Chunk

Hybrid Search
负责:
关键词 + 语义一起搜索

Rerank
负责:
对召回结果进行更精细排序

LLM
负责:
根据检索结果组织最终答案

所以语义搜索真正适合的场景,不是“我知道我要搜索哪个准确字符串”,而是:

我知道自己想找什么意思,但我不知道文档里究竟用了什么词。

这就是语义搜索最核心的价值。

语义搜索入门:什么时候使用,以及如何实现一套语义检索系统
http://clxhxhhr.top/posts/563/
作者
clxstart
发布于
2026-09-11
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。