示例成品 · 平台演示,按左边这组点选真跑出来的
直接给你一套能落地的架构。按你“日活过万、Java/Spring、通义/智谱、要流式回复、要知识库RAG”的情况,默认推荐:
> **Elasticsearch 8.x 做 BM25 + 向量混合检索 + Redis 做 embedding 缓存 + 通义/智谱做 embedding/LLM + Spring MVC SseEmitter 做流式。**
不要一上来上 Milvus,你的量级大概率用不到。
---
## 1. 整体架构
```text
用户搜索/提问
│
▼
┌─────────────────────────────┐
│ 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. 向量库选型:别拍脑袋,按这个标准选
选型看四个东西:
1. 数据量多大
2. 峰值 QPS 多少
3. 过滤条件复杂不复杂
4. 团队运维能力
你的情况:日活 1 万,站内搜索,假设文档量几十万到几百万级,峰值搜索 QPS 通常不到 100。这个量级,
| 方案 | 适用场景 | 结论 |
|---|---|---|
| Elasticsearch 8.x | 既要 BM25 全文,又要向量检索,数据量千万级以下,需要过滤、排序、高可用 | ✅ 默认选它 |
| pgvector | 数据量百万级以内,团队 PostgreSQL 熟,但全文检索和混合排序要自己做 | 可选 |
| Milvus | 向量规模上亿,或者纯向量召回场景 | ❌ 你现在不用上 |
**默认直接用 Elasticsearch 8.13+**。理由:
- 一个引擎同时做 BM25 + 向量召回,能直接用 RRF 合并。
- 你 Java 后端集成成熟。
- 少一套运维,少一个故障点。
---
## 3. 索引设计
ES 索引 Mapping 直接给你:
```json
{
"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,否则发布文章会卡。
流程:
```text
文章/知识库文档变更
│
▼
清洗 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 缓存:必须做
这
点左边「开工 · 直接出成品」,出一份你自己的版本(文字免费)