跳转至

RAG 知识库构建与运维

适用场景:将企业文档、运维手册、FAQ 沉淀为可检索的知识库,通过 RAG 实现精准问答。


1. RAG 架构概览

┌─────────────┐     ┌──────────────┐     ┌─────────────┐
│  文档源      │ ──▶ │  预处理       │ ──▶ │  Embedding   │
│ (PDF/MD/DB) │     │ (解析/切分)   │     │  向量化      │
└─────────────┘     └──────────────┘     └──────┬──────┘
                                                │
                                                ▼
┌─────────────┐     ┌──────────────┐     ┌─────────────┐
│  用户查询    │ ──▶ │  检索         │ ──▶ │  LLM 生成    │
│             │     │ (向量相似度)   │     │  带上下文回答 │
└─────────────┘     └──────────────┘     └─────────────┘

2. Embedding 模型选型

2.1 主流模型对比

模型 维度 最大 token 中文支持 部署方式
text-embedding-3-small 1536/512/256 8191 ✅ API / Ollama
bge-m3 1024 8192 ✅✅ 本地 Ollama
text-embedding-3-large 3072 8191 ✅ API
GPTFuzz/bge-large-zh 1024 512 ✅✅ 本地

2.2 Ollama 部署 Embedding

# 下载 bge-m3
ollama pull bge-m3

# 测试生成
curl http://localhost:11434/api/embeddings -d '{
  "model": "bge-m3",
  "prompt": "Kubernetes Pod 处于 Pending 状态的原因"
}'

2.3 OpenAI 兼容接口

若使用 LangChain / LlamaIndex,配置统一接口:

from langchain.embeddings import OllamaEmbeddings

embeddings = OllamaEmbeddings(
    model="bge-m3",
    base_url="http://localhost:11434"
)

3. 文档预处理

3.1 格式支持

格式 解析工具 注意事项
Markdown markdown 原生 保留标题层级
PDF pypdf / pdfplumber 表格需特殊处理
Word python-docx 样式信息丢失
HTML BeautifulSoup 清洗脚本标签
JSON 原生 嵌套结构展平

3.2 切分策略

from langchain.text_splitter import RecursiveCharacterTextSplitter

splitter = RecursiveCharacterTextSplitter(
    chunk_size=512,           # 每块最大 token
    chunk_overlap=64,         # 块间重叠,避免语义断裂
    length_function=len,
    separators=["\n\n", "\n", "。", " ", ""]
)

chunks = splitter.split_text(document_content)

关键参数: - chunk_size:根据 Embedding 模型最大 token 调整(bge-m3 支持 8192) - chunk_overlap:建议 10-15% 的 chunk_size - separators:按语义边界切分,优先使用 \n\n(段落)


4. 向量数据库配置

4.1 ChromaDB(轻量级,适合小规模)

import chromadb
from chromadb.config import Settings

client = chromadb.PersistentClient(path="/data/chroma/knowledge-base")

collection = client.get_or_create_collection(
    name="ops-manuals",
    metadata={"hnsw:space": "cosine"}
)

# 批量插入
collection.add(
    documents=chunks,
    embeddings=embeddings.embed_documents(chunks),
    ids=[f"doc_{i}" for i in range(len(chunks))]
)

4.2 Milvus(生产级,适合大规模)

from pymilvus import connections, Collection, utility

connections.connect("default", host="milvus", port="19530")

# 创建集合(若不存在)
if not utility.has_collection("ops_knowledge"):
    from pymilvus import CollectionSchema, FieldSchema, Collection
    fields = [
        FieldSchema(name="id", dtype=DataType.INT64, is_primary=True),
        FieldSchema(name="embedding", dtype=DataType.FLOAT_VECTOR, dim=1024),
        FieldSchema(name="content", dtype=DataType.VARCHAR, max_length=65535),
        FieldSchema(name="source", dtype=DataType.VARCHAR, max_length=512),
    ]
    schema = CollectionSchema(fields, "Ops knowledge base")
    collection = Collection("ops_knowledge", schema)

    # 创建索引
    index = {"index_type": "IVF_FLAT", "metric_type": "COSINE", "params": {"nlist": 128}}
    collection.create_index("embedding", index)

collection.load()

5. 检索与生成

5.1 检索优化

# 相似度阈值过滤
results = collection.query(
    query_embeddings=embeddings.embed_queries([question]),
    n_results=5,
    where={"source": {"$eq": "network"}}
)

# 仅返回相似度 > 0.6 的结果
filtered = [r for r, score in zip(results['documents'], results['distances']) if score < 0.4]

5.2 Prompt 模板

RAG_PROMPT = """你是一个运维专家助手。基于以下上下文回答问题:

<context>
{context}
</context>

问题:{question}

要求:
1. 仅基于上下文回答,不编造信息
2. 若上下文不足,明确说明
3. 提供可操作的解决方案
"""

5.3 完整流程

from langchain.chains import RetrievalQA
from langchain.llms import Ollama
from langchain.vectorstores import Chroma

# 初始化
db = Chroma(persist_directory="/data/chroma/knowledge-base", embedding_function=embeddings)
llm = Ollama(model="qwen2.5:7b", base_url="http://localhost:11434")

# 检索 QA
qa_chain = RetrievalQA.from_chain_type(
    llm=llm,
    retriever=db.as_retriever(
        search_kwargs={"k": 5, "score_threshold": 0.6}
    ),
    return_source_documents=True
)

response = qa_chain({"query": "Nginx 502 错误如何排查?"})
print(response["result"])

6. 运维监控

6.1 索引健康检查

# ChromaDB
ls -lh /data/chroma/
sqlite3 /data/chroma/chroma.sqlite3 "SELECT COUNT(*) FROM documents;"

# Milvus
milvus-cli> show collections
milvus-cli> show entities ops_knowledge

6.2 性能指标

指标 监控方式 告警阈值
检索延迟 Prometheus histogram P99 > 500ms
召回率 RAGAS faithfulness < 0.7
缓存命中率 Redis stats < 80%
向量库磁盘 node_disk_usage > 85%

6.3 定期更新策略

#!/bin/bash
# update_rag_index.sh
COLLECTION="ops_knowledge"
DATE=$(date +%Y%m%d)

# 增量更新
python3 scripts/incremental_ingest.py \
    --collection $COLLECTION \
    --since "$DATE-7" \
    --embed-model bge-m3

# 全量重建(每周日)
if [ "$(date +%u)" -eq 7 ]; then
    python3 scripts/full_rebuild.py --collection $COLLECTION
fi

7. 常见问题排查

问题 1:检索结果与查询无关

原因:Embedding 模型不匹配或相似度阈值过高。

解决:

# 降低阈值
search_kwargs={"k": 10, "score_threshold": 0.3}

# 切换 Embedding 模型
embeddings = OllamaEmbeddings(model="bge-m3")

问题 2:长文档切分后语义断裂

原因:chunk_size 过小或 separator 设置不当。

解决:

splitter = RecursiveCharacterTextSplitter(
    chunk_size=1024,
    chunk_overlap=128,
    separators=["\n## ", "\n# ", "\n\n", "\n", " "]
)

问题 3:向量库内存溢出

原因:向量数量过大,索引未优化。

解决: - Milvus:增加 nlist 参数,调整索引类型(HNSW / IVF_PQ) - Chroma:升级到 0.5+ 版本,启用持久化

问题 4:多语言混合检索效果差

原因:单一 Embedding 模型无法覆盖所有语言。

解决:使用多语言模型(bge-m3、mxbai-embed-large)或分语言建库。


8. 延伸阅读


FAQ

Embedding 模型如何选择? 中文场景推荐:bge-m3(多语言、支持长文本)、text-embedding-3-small(OpenAI 兼容)。英文场景:text-embedding-3-large。本地部署:GPTFuzz/bge-large-zh。

文档切分(Chunking)的最佳实践是什么? 按语义边界切分(段落/章节),而非固定字符数。使用 LangChain 的 RecursiveCharacterTextSplitter,chunk_size=512,chunk_overlap=64。代码文档可用 Language='python' 参数保持结构。

向量检索召回率低怎么办? 检查:① Embedding 模型是否匹配语言 ② chunk_size 是否合理 ③ 相似度阈值是否过高(默认 0.5,可调至 0.3)④ 是否启用混合检索(稀疏 + 稠密)。

RAG 系统中如何评估效果? 使用 RAGAS 框架:faithfulness(忠实度)、answer_relevancy(答案相关性)、context_precision(上下文精确度)。搭建测试集后运行评估脚本,定期回归。

向量数据库如何备份与恢复? Milvus:定期导出 .vec 文件;Chroma:备份 chroma.sqlite3 + chroma-data/ 目录。恢复后重建索引,验证向量数量一致。