AI语义搜索系统架构怎么设计
传统关键词搜不准,想做语义/AI搜索,却不清楚混合检索、向量索引、重排和高并发下的性能该怎么权衡。
有人这样说「我们站内搜索老是搜不准,用户抱怨多,想上语义搜索但不知道怎么设计架构。」
能替你选的都选好了。不想动,直接点下面「交给 AI 办」,点一下就开始做;想改,就点一下要的那个。
模板:已按内容配好换一套
标题、表头的配色和字体
已替你选好想改就点一下
只有你知道的不填就先空着,成品里留【待填】
还想怎么改可以不选
高级选项一般不用动 · 这件事通常 1 分钟左右做好
示例成品全文按上面这组示例选项真跑出来的
直接给你一套能落地的架构。按你“日活过万、Java/Spring、通义/智谱、要流式回复、要知识库RAG”的情况,默认推荐:
Elasticsearch 8.x 做 BM25 + 向量混合检索 + Redis 做 embedding 缓存 + 通义/智谱做 embedding/LLM + Spring MVC SseEmitter 做流式。
不要一上来上 Milvus,你的量级大概率用不到。
1. 整体架构
用户搜索/提问
│
▼
┌─────────────────────────────┐
│ Spring Boot API │
│ /api/search │
│ /api/chat/stream │
└─────────────┬───────────────┘
│
▼
┌─────────────────────────────┐
│ SearchOrchestrator │
│ - 查询预处理 │
│ - 混合检索编排 │
│ - RRF 合并 │
│ - 降级处理 │
│ - 重排 │
└──────┬──────────────┬───────┘
│ │
▼ ▼
┌─────────────┐ ┌──────────────────────┐
│ Redis │ │ Elasticsearch 8.x │
│ embedding │ │ - BM25 全文 │
│ 缓存 │ │ - dense_vector 向量 │
│ 查询/文档 │ │ - RRF 混合排序 │
└──────┬──────┘ └──────────────────────┘
│
▼
┌─────────────────────────────┐
│ EmbeddingClient │
│ 通义/智谱 embedding │
└─────────────────────────────┘
RAG 流式:
SearchOrchestrator 召回 top_k
│
▼
┌─────────────────────────────┐
│ PromptBuilder │
│ 拼资料 + 问题 │
└─────────────┬───────────────┘
│
▼
┌─────────────────────────────┐
│ LLMClient │
│ 通义/智谱 chat/completions │
│ stream=true │
└─────────────┬───────────────┘
│
▼
SSE 流式返回搜索和聊天分开:
/api/search?q=xxx:返回结果列表,不流式。/api/chat/stream:走 RAG,返回 SSE 流式回答。
2. 向量库选型:别拍脑袋,按这个标准选
选型看四个东西:
- 数据量多大
- 峰值 QPS 多少
- 过滤条件复杂不复杂
- 团队运维能力
你的情况:日活 1 万,站内搜索,假设文档量几十万到几百万级,峰值搜索 QPS 通常不到 100。这个量级,
| 方案 | 适用场景 | 结论 |
|---|---|---|
| Elasticsearch 8.x | 既要 BM25 全文,又要向量检索,数据量千万级以下,需要过滤、排序、高可用 | ✅ 默认选它 |
| pgvector | 数据量百万级以内,团队 PostgreSQL 熟,但全文检索和混合排序要自己做 | 可选 |
| Milvus | 向量规模上亿,或者纯向量召回场景 | ❌ 你现在不用上 |
默认直接用 Elasticsearch 8.13+。理由:
- 一个引擎同时做 BM25 + 向量召回,能直接用 RRF 合并。
- 你 Java 后端集成成熟。
- 少一套运维,少一个故障点。
3. 索引设计
ES 索引 Mapping 直接给你:
{
"mappings": {
"properties": {
"doc_id": { "type": "keyword" },
"chunk_id": { "type": "keyword" },
"title": {
"type": "text",
"analyzer": "ik_max_word",
"fields": { "raw": { "type": "keyword" } }
},
"content": { "type": "text", "analyzer": "ik_max_word" },
"url": { "type": "keyword" },
"category": { "type": "keyword" },
"status": { "type": "keyword" },
"published_at": { "type": "date" },
"content_hash": { "type": "keyword" },
"embedding": {
"type": "dense_vector",
"dims": 1024,
"index": true,
"similarity": "cosine"
}
}
}
}注意:
dims严格按你用的 embedding 模型维度配,比如通义text-embedding-v3或智谱embedding-2,不要猜,先调一次 API 看返回长度。- 中文全文用
ik_max_word分词,如果没装 IK 插件,用默认分词效果会差。
4. 入库流程:文档怎么进向量库
入库不要同步做 embedding,否则发布文章会卡。
流程:
文章/知识库文档变更
│
▼
清洗 HTML/Markdown/非法字符
│
▼
按标题、段落切 chunk
每个 chunk 300~800 字
重叠 10%~20%
│
▼
计算 content_hash
│
▼
先写 ES 基础字段,embedding 暂时留空
│
▼
发 MQ/Spring Event 异步处理
│
▼
消费者批量调 embedding API
│
▼
写回 ES embedding 字段切分规则:
- 优先按标题、段落切。
- 每 chunk 不超过 500 字左右。
- 相邻 chunk 保留 50~100 字重叠,避免语义断裂。
- 如果站内文章本身很短,一篇文章就是一个 chunk,不硬切。
5. Embedding 缓存:必须做
这