生产工程 · 2024年8月17日 · 2 分钟

RAG 实战:构建一个私有知识库问答系统

基于向量检索 + 大模型的 RAG 方案设计,涵盖文档切割、向量库选型与 Prompt 模板优化。

去年年底开始尝试用大模型做企业内部知识库问答,走的是 RAG(Retrieval-Augmented Generation)路线。不需要微调模型本身,而是让模型在回答之前先去"翻文档",把相关段落和问题一起喂给 LLM。

RAG 解决了什么问题

大模型的训练数据有截止时间,而且不知道公司内部的文档。直接问它"我们公司的支付接口怎么调",它只会给你一个八竿子打不着的通用回答。

RAG 的思路很直觉:先搜索,再回答。

用户: "支付接口超时时间怎么设?"
           │
           ▼
    ┌──────────────┐
    │  1. 向量检索  │  从知识库找相关的文档片段
    │  Embedding    │
    └──────┬───────┘
           │
           ▼
    ┌──────────────┐
    │  2. 拼 Prompt │  把文档片段 + 用户问题一起
    │              │  组装成完整 prompt
    └──────┬───────┘
           │
           ▼
    ┌──────────────┐
    │  3. LLM 生成  │  基于找到的文档回答问题
    │              │  并引用来源
    └──────────────┘

系统架构

┌────────────────────────────────────────────┐
│            RAG 系统架构                    │
│                                            │
│  ┌──────────────────────┐                  │
│  │   离线索引管线        │                  │
│  │                      │                  │
│  │  PDF/Word/MD         │                  │
│  │    ↓                 │                  │
│  │  文本切割 (Chunking)  │                  │
│  │    ↓                 │                  │
│  │  Embedding 向量化     │  ←→ Vector DB   │
│  │  (text[0.1, ...])(Milvus/    │
│  │                      │      Chroma)    │
│  └──────────────────────┘                  │
│                                            │
│  ┌──────────────────────┐                  │
│  │   在线查询管线        │                  │
│  │                      │                  │
│  │  用户问题            │                  │
│  │    ↓                 │                  │
│  │  Embedding 向量化     │                  │
│  │    ↓                 │                  │
│  │  向量相似度搜索       │────→ Top-K 结果  │
│  │    ↓                 │                  │
│  │  拼装 Prompt         │                  │
│  │    ↓                 │                  │
│  │  LLM 生成回答        │                  │
│  └──────────────────────┘                  │
└────────────────────────────────────────────┘

关键细节

文本切割

文档不能整篇塞进 Embedding——太长的文本向量表达会"模糊"。要切成小块(Chunk),大小一般在 300-800 tokens。但切得太碎会丢失上下文。

实践下来最好的方式是有重叠的滑动窗口切割

原文: "Redis 是一个开源的内存数据库。它支持多种数据结构。
        Redis 通常用作缓存、消息代理和数据库。"

Chunk1: "Redis 是一个开源的内存数据库。它支持多种数据结构。"
Chunk2: "它支持多种数据结构。Redis 通常用作缓存、消息代理和数据库。"
                         ↑ 重叠部分

重叠 100-200 tokens 保证边界处的信息不会因为切分而丢失。

向量数据库选型

方案 适用场景
Chroma 原型开发、本地测试、小规模
Milvus 生产环境、百万级以上向量
pgvector 已有 PostgreSQL,不想引入新组件
FAISS 纯本地、不需要持久化

初期用 Chroma 快速验证,数据量上去后换 Milvus。

Prompt 模板

检索到的片段和用户问题要拼成一个结构化的 prompt:

你是一个技术支持助手。根据以下参考文档回答问题。
如果文档中没有相关信息,请诚实地说不知道,不要编造。

参考文档:
---[文档1]---
Redis 连接超时参数为 connectTimeout,默认值 2000ms。
---[文档2]---
建议 connectTimeout 不要低于 1000ms,网络抖动可能导致频繁断连。

问题:支付接口超时时间怎么设?

回答:

关键是明确指令:不知道就说不知道——这比任何技术细节都重要。大模型在没有信息时容易"张口就来"(幻觉)。

实际运行的效果与问题

检索质量是系统的天花板——检索不到相关文档,LLM 再强也没用。常见的检索失败原因:

  1. 用户问题和文档用词不一致(用户问"支付超时",文档写"连接超时参数")
  2. 问题太短,向量表示信息量不足
  3. 多跳问题(需要综合多个文档回答)

优化方向包括混合检索(向量 + 关键词)、HyDE(先让 LLM 扩展查询再检索)、以及 reranker 重排序。但每加一层,延迟就上来了。实际项目里建议先跑通最简单的方案,再根据真实用户的 bad case 迭代。

总结

RAG 是目前把私有知识和大模型能力结合起来最务实的方案。不需要训模型、不需要太多算力,一台机器加一个 Embedding 服务就能跑起来。真正的难点不在技术,而在文档质量和检索精度——垃圾进垃圾出,和搜索引擎一个道理。

继续阅读

评论