第 17 期:灌注知识 — Qdrant 向量数据库部署与文档索引管道
本期目标
搭建一个完整的文档索引管道,将 PDF/Markdown 文件灌入 Qdrant 向量数据库。
graph LR
subgraph "输入源"
PDF[📄 PDF 文件]
MD[📝 Markdown]
CSV[📊 CSV]
end
subgraph "n8n 索引工作流"
Load[📥 文件加载] --> Split[✂️ 分块]
Split --> Embed[🧮 Embedding]
Embed --> Insert[💾 写入 Qdrant]
end
PDF & MD & CSV --> Load
style Insert fill:#22c55e,stroke:#16a34a,color:#fff1. Docker 部署 Qdrant
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# 在 n8n 的 docker-compose.yml 中添加 Qdrant 服务
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
services:
# ... 已有的 n8n 和 postgres 配置 ...
# ── Qdrant 向量数据库 ──────────────────────────
qdrant:
image: qdrant/qdrant:latest # 官方镜像
container_name: n8n_qdrant
restart: unless-stopped
ports:
- "6333:6333" # REST API 端口
- "6334:6334" # gRPC 端口 (高性能)
volumes:
- qdrant_data:/qdrant/storage # 持久化向量数据
environment:
- QDRANT__SERVICE__GRPC_PORT=6334
networks:
- n8n_network # 与 n8n 同一网络
volumes:
qdrant_data: # Qdrant 数据卷
# ... 其他已有卷 ...
# 启动 Qdrant
docker compose up -d qdrant
# 验证 Qdrant 运行状态
curl http://localhost:6333/healthz
# 预期: {"title":"qdrant - vectorass engine","version":"1.x.x"}
# 查看集合列表 (初始为空)
curl http://localhost:6333/collections
# 预期: {"result":{"collections":[]},"status":"ok"}
2. 构建索引工作流
完整工作流拓扑
graph TB
Trigger[⚡ Manual Trigger
或 Schedule Trigger] --> ReadFiles[📂 Read Binary Files
从文件夹批量读取]
ReadFiles --> Extract[📄 Extract from File
PDF → 纯文本]
Extract --> Metadata[⚙️ Set 节点
添加元数据]
Metadata --> Splitter[✂️ Recursive Character
Text Splitter]
Splitter --> VectorStore[💾 Qdrant Vector Store
模式: Insert]
subgraph "嵌入式子节点"
VectorStore --> EmbModel[🧮 OpenAI Embeddings
text-embedding-3-small]
end
style VectorStore fill:#22c55e,stroke:#16a34a,color:#fff
style EmbModel fill:#8b5cf6,stroke:#7c3aed,color:#fff节点逐一配置
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 节点 1: Read Binary Files — 批量读取文件
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// File Selector: /data/knowledge-base/*.pdf
// 输出: 每个文件生成一个 Item (包含 binary 数据)
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 节点 2: Extract from File — 提取文本
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Operation: "Extract From File"
// 输入: binary 数据 (PDF)
// 输出: { "json": { "text": "提取出的纯文本内容..." } }
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 节点 3: Set 节点 — 添加元数据标签
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 添加字段:
// source: {{ $json.fileName }} // 文档来源文件名
// category: "product-docs" // 文档分类
// indexedAt: {{ $now.toISO() }} // 索引时间
// 为什么加元数据?
// → 检索时可以按 category 过滤: "只搜索产品文档,不搜索 HR 政策"
// → 管理时可以按 source 追踪: "哪些文件已索引过"
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 节点 4: Text Splitter — 递归分块
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Chunk Size: 800
// Chunk Overlap: 200
// Separator: ["\n\n", "\n", " "] // 先按双换行,再按单换行,最后按空格
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 节点 5: Qdrant Vector Store — 写入向量
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Mode: Insert Documents
// Collection Name: "knowledge-base" // 集合名 (类似数据库表名)
// Vector Dimension: 1536 // 与 embedding 模型维度一致!
//
// ⚠️ 集合不存在时 n8n 会自动创建
// ⚠️ 确保 Vector Dimension 与 Embedding 模型匹配:
// text-embedding-3-small → 1536
// text-embedding-3-large → 3072
3. 索引管道的数据流转
sequenceDiagram
participant File as 📄 退款政策.pdf
participant Extract as 📄 Extract
participant Split as ✂️ Splitter
participant Embed as 🧮 Embedding
participant QD as 💾 Qdrant
File->>Extract: 二进制 PDF 数据
Extract->>Extract: PDF → 纯文本 (3000 字)
Extract->>Split: "第一章: 退款条件...第二章: 退款流程..."
Split->>Split: 分成 5 个块 (每块 ~600 字)
loop 对每个块
Split->>Embed: 块文本
Embed->>Embed: 调用 OpenAI API
Embed-->>QD: 向量 [0.023, -0.147, ...] + 元数据
end
Note over QD: 存储了 5 条向量记录
每条包含: 向量 + 原文 + 元数据4. 验证索引结果
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# 验证 Qdrant 中的数据
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# 查看集合信息 (向量数量、维度等)
curl http://localhost:6333/collections/knowledge-base
# {
# "result": {
# "status": "green",
# "vectors_count": 42, ← 已索引 42 条向量
# "points_count": 42,
# "config": {
# "params": {
# "vectors": { "size": 1536 } ← 向量维度
# }
# }
# }
# }
# 浏览 Qdrant Dashboard (可视化管理)
# 打开浏览器: http://localhost:6333/dashboard
常见问题排查
| 问题 | 原因 | 解决 |
|---|---|---|
| 写入时报 "dimension mismatch" | Embedding 维度 ≠ Collection 配置 | 删除集合后重建,确保维度一致 |
| PDF 提取出的文本是乱码 | PDF 是扫描版(图片) | 需要先 OCR,或使用 Document AI |
| 索引速度很慢 | 文件太大,分块太多 | 增大 Chunk Size 或限制文件数量 |
| 元数据未存储 | Set 节点位置错误 | 确保元数据在 Text Splitter 之前添加 |
下一步
在 Ep 18 中,我们将构建检索侧工作流——将 Qdrant 作为 Agent Tool 连接,让 AI Agent 能够实时搜索知识库并生成基于事实的回答。