第 20 期:知识库运维 — 增量更新、过期清理与检索质量监控

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

RAG 不是一次性工程

很多团队在建好 RAG 后就"放任不管"。但知识库与代码一样,需要持续维护

graph TB
    subgraph "RAG 运维闭环"
        Index["📥 索引 (Ep 17)"] --> Retrieve["🔍 检索 (Ep 18)"]
        Retrieve --> Optimize["⚡ 优化 (Ep 19)"]
        Optimize --> Monitor["📊 监控质量"]
        Monitor --> Update["🔄 增量更新"]
        Update --> Clean["🗑️ 过期清理"]
        Clean --> Index
    end
    
    style Monitor fill:#f59e0b,stroke:#d97706,color:#fff
    style Update fill:#22c55e,stroke:#16a34a,color:#fff

1. 增量更新管道

graph TB
    Schedule[⏰ Schedule Trigger
每天凌晨 3:00] --> Scan[📂 扫描文档目录] Scan --> Query[查询 Data Table
'已索引文件列表'] Query --> Compare{新增/修改文件?} Compare -->|"有新文件"| NewPipe["📄 提取 → ✂️ 分块 → 🧮 嵌入
→ 💾 写入 Qdrant"] Compare -->|"文件已修改"| UpdatePipe["🗑️ 删除旧向量
→ 重新索引"] Compare -->|"无变化"| Skip[⏭️ 跳过] NewPipe --> Record["📝 更新 Data Table
记录文件名 + hash + 时间"] UpdatePipe --> Record style Schedule fill:#6366f1,stroke:#4f46e5,color:#fff
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 增量更新核心逻辑 (Code 节点)
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

const currentFiles = $input.all();            // 目录中的所有文件
const indexedFiles = $('Data Table').all();   // 已索引的文件记录

const newFiles = [];
const updatedFiles = [];

for (const file of currentFiles) {
  const record = indexedFiles.find(r => 
    r.json.fileName === file.json.fileName
  );
  
  if (!record) {
    newFiles.push(file);                      // 新增文件: 全量索引
  } else if (record.json.fileHash !== file.json.fileHash) {
    updatedFiles.push(file);                  // 已修改: 删旧 + 重建
  }
  // else: 文件未变化, 跳过
}

return [
  ...newFiles.map(f => ({ json: { ...f.json, action: 'insert' } })),
  ...updatedFiles.map(f => ({ json: { ...f.json, action: 'update' } }))
];

2. 过期文档清理

// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 清理策略: 删除 90 天前索引的文档向量
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

// 在 Code 节点中调用 Qdrant API 删除过期数据:
const ninetyDaysAgo = $now.minus({ days: 90 }).toISO();

const response = await fetch('http://qdrant:6333/collections/knowledge-base/points/delete', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    filter: {
      must: [{
        key: "metadata.indexedAt",
        range: {
          lt: ninetyDaysAgo              // 索引时间 < 90 天前
        }
      }]
    }
  })
});

// 同步清理 Data Table 中对应的记录
// → 确保索引记录与实际向量数据一致

3. 检索质量监控

graph TB
    subgraph "质量监控工作流"
        Test[📋 测试问答对
20 组标准 Q&A] --> Loop[Loop Over Items] Loop --> Search[🔍 执行 RAG 检索] Search --> Compare["🧠 LLM Judge
评分: 1-5 分"] Compare --> Record[📊 记录到 Data Table] Record --> Loop Loop -->|完成| Report[📈 生成质量报告] Report --> Alert{平均分 < 3?} Alert -->|"是"| Slack[🚨 Slack 告警] Alert -->|"否"| Log[✅ 记录日志] end style Compare fill:#8b5cf6,stroke:#7c3aed,color:#fff style Alert fill:#f59e0b,stroke:#d97706
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// LLM-as-Judge 评分 Prompt
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

const judgePrompt = `
你是一个 RAG 质量评估专家。请对以下检索结果进行评分。

## 标准问题
${testQuestion}

## 标准答案
${expectedAnswer}

## RAG 实际回答
${ragAnswer}

## 评分维度 (每项 1-5 分)
1. 事实准确性: RAG 回答的内容是否与标准答案一致?
2. 完整性: 是否覆盖了标准答案的所有关键点?
3. 无幻觉: 是否存在编造的、标准答案中没有的信息?

请以 JSON 格式输出:
{"accuracy": N, "completeness": N, "no_hallucination": N, "comment": "..."}
`;

4. 幻觉检测工作流

graph TB
    RAGAnswer[🤖 RAG Agent 回答] --> Extract[提取 Agent 引用的事实声明]
    
    Extract --> Verify["🔍 逐条验证
在 Qdrant 中搜索每条声明"] Verify --> Check{声明是否有文档支撑?} Check -->|"✅ 有支撑"| Safe[标记: 可信] Check -->|"❌ 无支撑"| Flag["⚠️ 标记: 可能幻觉"] Flag --> Notify["📧 通知管理员审核"] style Flag fill:#ef4444,stroke:#dc2626,color:#fff

模块四总结

mindmap
  root((模块四: 企业级 RAG))
    Ep 16 RAG 原理
      Embedding 机制
      分块策略
      向量数据库选型
    Ep 17 索引管道
      Qdrant Docker 部署
      文档加载 → 分块 → 嵌入 → 存储
      元数据标记
    Ep 18 RAG Agent
      Vector Store Tool
      检索 + 生成时序
      System Prompt for RAG
    Ep 19 高级优化
      Hybrid Search
      Re-Ranking
      Multi-Query
    Ep 20 知识库运维
      增量更新管道
      过期清理
      质量监控 + 幻觉检测

下一步

模块五将进入 MCP (Model Context Protocol) 生态系统——学习如何通过标准化协议让 Agent 连接任何外部服务,打开"无限工具箱"。