第 17 期:灌注知识 — Qdrant 向量数据库部署与文档索引管道

⏱ 预计阅读 11 分钟 更新于 2026/4/9

本期目标

搭建一个完整的文档索引管道,将 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:#fff

1. 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 能够实时搜索知识库并生成基于事实的回答。