导语:把企业知识变成可对话的 AI 系统
你公司有多少知识躺在 PDF、Word、Notion 和 Slack 里?员工遇到问题要翻半天文档,新人上手慢,核心人员离职后经验流失。传统的关键词搜索解决不了「根据这份合同,我们的退款政策是什么」这类问题。
RAG(检索增强生成) 就是答案:把你的文档喂给 AI,让它基于你的私有知识回答,而不是靠训练数据里的过时信息。
2026 年,搭建一套完整的 RAG 系统不再需要 GPU 集群。一台 $10/月的 VPS 就能跑:文档解析流水线、向量数据库、Embedding 模型、检索调优工具和对话界面。
本文不是教你装一个 ChromaDB 容器就完事——我们会深入每个环节的最佳实践,从文档切分策略到检索质量评估,帮你搭建一个真正可用的生产级 RAG 系统。
说明:本文包含 VPS 服务商 affiliate 链接。通过链接购买,我们可能获得佣金,但不会影响你的价格。我们只推荐适合实际部署场景的海外服务。
RAG 系统架构全景
一套完整的 RAG 系统包含以下核心组件:
┌─────────────────────────────────────────────────────────┐
│ 用户对话界面 │
│ (Open WebUI / Dify / 自建) │
└──────────────────────┬──────────────────────────────────┘
│ 用户提问
┌──────────────────────▼──────────────────────────────────┐
│ 检索层 (Retrieval) │
│ ┌──────────┐ ┌──────────┐ ┌──────────────────────┐ │
│ │ 查询重写 │ │ 混合检索 │ │ 重排序 (Reranker) │ │
│ │ HyDE/ │ │ Dense+ │ │ BGE-Reranker │ │
│ │ 扩展检索 │ │ Sparse │ │ │ │
│ └──────────┘ └──────────┘ └──────────────────────┘ │
┌──────────────────────┬──────────────────────────────────┐
│ 向量数据库 │
│ ┌──────────┐ ┌──────────┐ │
│ │ ChromaDB │ │ Qdrant │ (二选一或并存) │
│ └──────────┘ └──────────┘ │
└──────────────────────┬──────────────────────────────────┘
│ 嵌入向量
┌──────────────────────▼──────────────────────────────────┐
│ Embedding 模型 │
│ (bge-large-zh / text-embedding-3-large) │
└──────────────────────┬──────────────────────────────────┘
│ 原始文档
┌──────────────────────▼──────────────────────────────────┐
│ 文档解析流水线 │
│ PDF → 文本 | DOCX → 文本 | HTML → 清洗文本 │
│ PPTX → 文本 | 图片 OCR | 表格结构化提取 │
└─────────────────────────────────────────────────────────┘
关键设计决策:
- 文档解析是 RAG 质量的上限——再好的检索模型也救不回被切碎的表格
- 混合检索(向量 + 关键词)显著优于单一向量检索,尤其在专业术语场景
- 重排序是性价比最高的检索优化手段,用一个小模型重新打分 Top-K 结果
- VPS 选型取决于你的文档量和并发需求,但入门级完全够用
VPS 选型:RAG 系统需要多大配置?
RAG 系统对 VPS 的需求主要来自三个部分:向量数据库、Embedding 模型和文档解析服务。
配置参考表
| 规模 | vCPU | RAM | 存储 | 月价区间 | 适用场景 |
|---|---|---|---|---|---|
| 微型 | 2核 | 4GB | 40GB NVMe | $5-8 | 个人知识库,<1000 文档,单 Embedding 模型 |
| 小型 | 4核 | 8GB | 80GB NVMe | $8-15 | 团队知识库,<10000 文档,混合检索 + 重排序 |
| 中型 | 4核 | 16GB | 160GB NVMe | $20-30 | 企业知识库,高并发,多模型切换 |
推荐服务商
RackNerd 在入门级 VPS 市场性价比突出,4核8GB 年付约 $40-60(折合 $3-5/月):
Hostinger 的 KVM 2 计划(2核4GB)月付约 $6,KVM 4(4核8GB)约 $12,管理面板友好:
Vultr 提供灵活的按小时计费,4核8GB 纽约/新加坡节点约 $15/月,适合需要快速扩展的场景:
我的建议:RAG 系统的向量数据库和 Embedding 模型对内存有一定要求。如果你的文档总量超过 5000 份或需要同时跑重排序模型,直接上 8GB RAM 起步。RackNerd 的年付方案能把成本压到极低,适合先跑起来验证。
Step 1:文档解析流水线 —— RAG 质量的基石
大多数 RAG 系统的瓶颈不在检索模型,而在文档解析质量。PDF 里的表格被切成碎片、扫描件没有 OCR、HTML 标签残留——这些问题会让后续所有环节功亏一篑。
核心工具链
# 使用 Docker Compose 部署文档解析服务
cat > ~/rag-doc-parser/docker-compose.yml << 'EOF'
version: '3.8'
services:
parser:
image: ghcr.io/nickhould/docling-server:latest
ports:
- "5001:5000"
volumes:
- ./uploads:/app/uploads
- ./output:/app/output
environment:
- MAX_WORKERS=2
- TIMEOUT=300
# 可选:OCR 服务(处理扫描件 PDF)
ocr:
image: ghcr.io/nickhould/docling-server:latest
command: ["--ocr"]
ports:
- "5002:5000"
EOF
cd ~/rag-doc-parser && docker compose up -d
文档处理策略
不同格式的文档需要不同的处理方式:
| 文档类型 | 处理方式 | 注意事项 |
|---|---|---|
| PDF(文本型) | 直接提取文本 + 结构化 | 注意分页符,避免跨页段落被切断 |
| PDF(扫描型) | OCR → 文本 | 使用 Docling 或 Tesseract,中文需下载语言包 |
| Word (.docx) | 直接解析 XML | 保留标题层级用于文档结构切分 |
| PowerPoint | 逐页提取文本和图片 | 备注栏内容也要提取 |
| HTML/网页 | 读取 + 清理标签 | 使用 Readability 算法提取正文 |
| Excel/CSV | 结构化表格处理 | 保持行列关系,不要扁平化为纯文本 |
| Markdown | 直接使用 | 天然适合 RAG 的文档格式 |
文档切分策略
切分(Chunking)是 RAG 中最容易被低估的环节。切得太细会丢失上下文,切得太粗会引入噪声。
# 推荐的智能切分策略
from langchain_text_splitters import RecursiveCharacterTextSplitter
splitter = RecursiveCharacterTextSplitter(
chunk_size=512, # 每块 512 tokens
chunk_overlap=64, # 相邻块重叠 64 tokens(保留上下文)
separators=[ # 优先在这些位置切分
"\n\n", # 段落之间
"\n", # 行之间
". ", # 句子之间
" ", # 单词之间
"", # 强制切分
],
length_function=len,
)
高级技巧:元数据增强切分
在切分时保留文档来源、章节标题、页码等元数据,检索时可以优先匹配相关章节:
# 每个 chunk 携带元数据
chunks = [
{
"text": "退款政策:订单签收后 7 天内可申请全额退款...",
"metadata": {
"source": "company-handbook.pdf",
"section": "财务政策 > 退款流程",
"page": 12,
"document_type": "policy"
}
}
]
Step 2:向量数据库选型与部署
向量数据库是 RAG 的核心存储层,负责索引和检索文档的嵌入向量。
ChromaDB vs Qdrant 对比
| 特性 | ChromaDB | Qdrant |
|---|---|---|
| 安装复杂度 | 极简,单容器 | 中等,支持集群 |
| 查询性能 | 适合中小规模 | 大规模高性能 |
| 过滤检索 | 支持 | 支持(更强大) |
| 混合检索 | 插件支持 | 原生支持(稀疏 + 密集) |
| 中文优化 | 依赖 Embedding 模型 | 依赖 Embedding 模型 |
| 内存占用 | ~500MB | ~300MB |
| 适合场景 | 个人/小团队知识库 | 企业级/高并发场景 |
ChromaDB 部署(推荐入门)
mkdir -p ~/rag-chroma && cd ~/rag-chroma
cat > docker-compose.yml << 'EOF'
version: '3.8'
services:
chroma:
image: chromadb/chroma:latest
ports:
- "8000:8000"
volumes:
- chroma_data:/chroma/chroma
environment:
- ANONYMIZED_TELEMETRY=false
- CHROMA_SERVER_AUTH_CREDENTIALS=admin:secure-password
- CHROMA_SERVER_AUTH_CREDENTIALS_PROVIDER=chromadb.auth.simple_authn.SimpleAuthServerCredentialsProvider
- CHROMA_SERVER_AUTH_PROVIDER=chromadb.auth.simple_authn.SimpleAuthServerAuthenticationProvider
volumes:
chroma_data:
EOF
docker compose up -d
Qdrant 部署(推荐生产)
mkdir -p ~/rag-qdrant && cd ~/rag-qdrant
cat > docker-compose.yml << 'EOF'
version: '3.8'
services:
qdrant:
image: qdrant/qdrant:latest
ports:
- "6333:6333" # REST API
- "6334:6334" # gRPC
volumes:
- qdrant_data:/qdrant/storage
environment:
- QDRANT__SERVICE__API_KEY=your-secret-key
# Qdrant 面板(可选)
qdrant_dashboard:
image: ghcr.io/qdrant/dashboard:latest
ports:
- "8080:80"
environment:
- QDRANT__API_KEY=your-secret-key
volumes:
qdrant_data:
EOF
docker compose up -d
集合(Collection)配置
无论选择哪个向量数据库,合理的集合配置对检索质量影响巨大:
from qdrant_client import QdrantClient
from qdrant_client.models import (
Distance,
VectorParams,
PayloadSchemaType,
)
client = QdrantClient(url="http://localhost:6333", api_key="your-secret-key")
client.create_collection(
collection_name="knowledge_base",
vectors_config=VectorParams(
size=1024, # Embedding 维度(bge-large 是 1024)
distance=Distance.COSINE, # 余弦相似度
),
)
# 为常用过滤字段创建_payload_ 索引
client.create_payload_index(
collection_name="knowledge_base",
field_name="document_type",
field_schema=PayloadSchemaType.KEYWORD,
)
client.create_payload_index(
collection_name="knowledge_base",
field_name="source",
field_schema=PayloadSchemaType.KEYWORD,
)
Step 3:Embedding 模型部署
Embedding 模型的质量直接决定了检索的准确度。对于中文知识库,推荐使用专门针对中文优化的模型。
模型选型
| 模型 | 语言 | 维度 | 推荐场景 |
|---|---|---|---|
| bge-large-zh-v1.5 | 中文 | 1024 | 中文知识库首选,综合性能最佳 |
| bge-m3 | 多语言 | 1024 | 中英混合文档,支持稠密+稀疏+多粒度 |
| text-embedding-3-large (OpenAI) | 多语言 | 3072 | 预算充足时,质量最高 |
| m3e-base | 中文 | 768 | 轻量级中文场景 |
| bge-small-zh-v1.5 | 中文 | 512 | 资源受限时的快速方案 |
自托管 Embedding 服务
# 使用 FastAPI + Sentence Transformers 部署 Embedding API
mkdir -p ~/rag-embedding && cd ~/rag-embedding
cat > requirements.txt << 'EOF'
fastapi==0.115.0
uvicorn==0.30.6
sentence-transformers==3.2.0
pydantic==2.9.0
EOF
cat > main.py << 'PYEOF'
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from sentence_transformers import SentenceTransformer
from typing import List
import numpy as np
app = FastAPI(title="RAG Embedding Service")
# 加载模型(首次启动会下载,约 1.3GB)
model = SentenceTransformer("BAAI/bge-large-zh-v1.5")
class EmbedRequest(BaseModel):
texts: List[str]
normalize: bool = True
class EmbedResponse(BaseModel):
embeddings: List[List[float]]
model: str = "bge-large-zh-v1.5"
@app.post("/embed", response_model=EmbedResponse)
def embed_texts(request: EmbedRequest):
try:
embeddings = model.encode(
request.texts,
normalize_embeddings=request.normalize,
batch_size=32,
)
return EmbedResponse(embeddings=embeddings.tolist())
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@app.get("/health")
def health():
return {"status": "ok", "model": "bge-large-zh-v1.5"}
PYEOF
# 启动服务
pip install -r requirements.txt
uvicorn main:app --host 0.0.0.0 --port 8001 --workers 2
# 测试 Embedding 服务
curl http://localhost:8001/embed \
-H "Content-Type: application/json" \
-d '{"texts": ["什么是退款政策?", "公司年假规定"], "normalize": true}'
Docker 化部署
cat > docker-compose.yml << 'EOF'
version: '3.8'
services:
embedding:
build: .
ports:
- "8001:8001"
volumes:
- ~/.cache/huggingface:/root/.cache/huggingface # 缓存模型文件
deploy:
resources:
limits:
memory: 4G
EOF
cat > Dockerfile << 'EOF'
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 预下载模型,避免首次请求超时
RUN python -c "from sentence_transformers import SentenceTransformer; SentenceTransformer('BAAI/bge-large-zh-v1.5')"
COPY main.py .
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8001", "--workers", "2"]
EOF
Step 4:构建完整的 RAG 检索管线
文档解析 → Embedding → 存入向量库 → 检索 → 重排序 → 生成回答,这是一条完整的 RAG 管线。
使用 LangChain 编排管线
mkdir -p ~/rag-pipeline && cd ~/rag-pipeline
cat > requirements.txt << 'EOF'
langchain==0.3.0
langchain-community==0.3.0
langchain-core==0.3.0
langchain-text-splitters==0.3.0
chromadb==0.5.0
openai==1.40.0
tiktoken==0.7.0
EOF
cat > rag_pipeline.py << 'PYEOF'
"""
完整的 RAG 检索管线
"""
import os
from langchain_community.vectorstores import Chroma
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_core.documents import Document
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain.chains import RetrievalQA
from langchain.retrievers import EnsembleRetriever
# 配置
CHROMA_PATH = "./chroma_db"
EMBEDDING_URL = "http://embedding-service:8001" # 自托管 Embedding
CHAT_MODEL = "gpt-4o-mini" # 或使用自托管的 Qwen2.5-7B
class RAGPipeline:
def __init__(self):
# 1. 初始化 Embedding(优先使用自托管)
self.embeddings = OpenAIEmbeddings(
openai_api_key="self-hosted",
base_url=EMBEDDING_URL,
model="bge-large-zh-v1.5",
dimensions=1024,
)
# 2. 文档切分器
self.splitter = RecursiveCharacterTextSplitter(
chunk_size=512,
chunk_overlap=64,
separators=["\n\n", "\n", ". ", " ", ""],
)
# 3. 向量数据库
self.vectorstore = Chroma(
persist_directory=CHROMA_PATH,
embedding_function=self.embeddings,
)
def ingest_documents(self, texts: List[str], metadata: List[dict]):
"""导入文档到向量库"""
documents = [
Document(page_content=text, metadata=meta)
for text, meta in zip(texts, metadata)
]
chunks = self.splitter.split_documents(documents)
self.vectorstore.add_documents(chunks)
print(f"✅ 已导入 {len(chunks)} 个文档块")
def retrieve(self, query: str, top_k: int = 5) -> list:
"""
检索相关文档块
使用混合检索:向量检索 + 关键词检索
"""
# 向量检索
vector_retriever = self.vectorstore.as_retriever(
search_type="similarity",
search_kwargs={"k": top_k},
)
# 相似度检索(考虑相关性)
mmr_retriever = self.vectorstore.as_retriever(
search_type="mmr",
search_kwargs={"k": top_k, "fetch_k": 20},
)
# 混合检索
ensemble = EnsembleRetriever(
retrievers=[vector_retriever, mmr_retriever],
weights=[0.7, 0.3],
)
return ensemble.invoke(query)
def qa_chain(self, query: str, temperature: float = 0.1) -> str:
"""生成基于检索结果的回答"""
docs = self.retrieve(query)
# 构建上下文
context = "\n\n".join([doc.page_content for doc in docs])
# 调用 LLM 生成回答
llm = ChatOpenAI(
model=CHAT_MODEL,
temperature=temperature,
openai_api_key=os.getenv("OPENAI_API_KEY", "sk-placeholder"),
base_url=os.getenv("OPENAI_API_BASE", "https://api.openai.com/v1"),
)
prompt = f"""基于以下参考资料回答问题。如果资料中没有相关信息,请如实说明。
参考资料:
{context}
问题:{query}
请给出详细、准确的回答,并在回答末尾标注引用的文档来源。"""
response = llm.invoke(prompt)
return response.content
# 使用示例
if __name__ == "__main__":
pipeline = RAGPipeline()
# 导入文档
pipeline.ingest_documents(
texts=["公司的退款政策是签收后 7 天内可申请全额退款..."],
metadata=[{"source": "handbook.pdf", "document_type": "policy"}],
)
# 检索并回答
answer = pipeline.qa_chain("我们的退款政策是什么?")
print(answer)
PYEOF
检索质量评估
不要盲目相信检索效果。建立评估闭环:
def evaluate_retrieval(query: str, expected_docs: List[str]) -> dict:
"""
评估检索质量
- Precision@K: 检索结果中有多少是相关的
- Recall@K: 所有相关文档中有多少被检索到了
- MRR: 第一个相关文档的排名倒数平均
"""
retrieved = pipeline.retrieve(query, top_k=5)
relevant_count = sum(1 for doc in retrieved if doc.metadata.get("relevant"))
precision = relevant_count / len(retrieved) if retrieved else 0
return {
"precision_at_5": precision,
"retrieved_count": len(retrieved),
"documents": [doc.metadata for doc in retrieved],
}
Step 5:前端对话界面
有了后端管线,需要一个友好的界面让用户与知识库对话。
方案 A:Open WebUI(推荐)
Open WebUI 是一个功能丰富的开源 Web 界面,支持对话历史、多轮对话、文件上传:
mkdir -p ~/rag-webui && cd ~/rag-webui
cat > docker-compose.yml << 'EOF'
version: '3.8'
services:
open-webui:
image: ghcr.io/open-webui/open-webui:main
ports:
- "3000:8080"
environment:
- OLLAMA_BASE_URL=http://embedding:11434
- WEBUI_SECRET_KEY=your-secret-key
- ENABLE_RAG_HYBRID_SEARCH=true
- RAG_TOP_K=5
- RAG_CHUNK_SIZE=512
- RAG_CHUNK_OVERLAP=64
volumes:
- webui_data:/app/backend/data
volumes:
webui_data:
EOF
docker compose up -d
访问 http://your-vps-ip:3000 即可使用。
方案 B:Dify(适合团队)
Dify 提供更完整的 AI 应用开发平台,内置 RAG 引擎:
# Dify 部署需要 4GB+ RAM
curl -o dify-env.txt https://raw.githubusercontent.com/langgenius/dify/main/docker/.env
# 修改配置指向你的自托管 Embedding 服务和向量数据库
sed -i 's/EMBEDDING_API_KEY=.*/EMBEDDING_API_KEY=your-key/' dify-env.txt
sed -i 's/VECTOR_STORE=.*/VECTOR_STORE=qdrant/' dify-env.txt
sed -i 's/QDRANT_URI=.*/QDRANT_URI=http://qdrant:6333/' dify-env.txt
docker compose -f docker/Compose.yaml up -d
方案 C:自建轻量界面
如果你只需要最简单的问答界面:
<!-- simple-rag.html - 直接部署到 Nginx -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>AI 知识库</title>
<style>
body {
font-family: -apple-system, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
.chat {
border: 1px solid #ddd;
border-radius: 8px;
padding: 16px;
min-height: 400px;
}
.message {
margin: 8px 0;
padding: 8px 12px;
border-radius: 4px;
}
.user {
background: #e3f2fd;
text-align: right;
}
.ai {
background: #f5f5f5;
}
input {
width: 100%;
padding: 12px;
border: 1px solid #ddd;
border-radius: 4px;
box-sizing: border-box;
}
button {
margin-top: 8px;
padding: 12px 24px;
background: #1976d2;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
</style>
</head>
<body>
<h1>📚 AI 知识库</h1>
<div class="chat" id="chat"></div>
<input
type="text"
id="query"
placeholder="输入你的问题..."
onkeypress="if(event.key==='Enter')ask()"
/>
<button onclick="ask()">提问</button>
<script>
async function ask() {
const query = document.getElementById("query").value;
if (!query) return;
// 显示用户问题
addMessage(query, "user");
document.getElementById("query").value = "";
// 调用 RAG API
const resp = await fetch("/api/rag/query", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ query }),
});
const data = await resp.json();
addMessage(data.answer, "ai");
}
function addMessage(text, role) {
const chat = document.getElementById("chat");
const div = document.createElement("div");
div.className = `message ${role}`;
div.textContent = text;
chat.appendChild(div);
chat.scrollTop = chat.scrollHeight;
}
</script>
</body>
</html>
配合 Nginx 反向代理即可部署。
成本对比:自托管 vs 云服务
| 方案 | 月成本 | 数据隐私 | 定制性 | 维护成本 |
|---|---|---|---|---|
| 自建 RAG(VPS) | $8-15/月 | ✅ 完全私有 | 极高 | 中 |
| ChatGPT Custom Instructions | $20/月 | ❌ 数据上云 | 低 | 无 |
| Dify Cloud 版 | $29/月起 | ⚠️ 服务商可控 | 中 | 低 |
| Pinecone + GPT API | $10-50/月 | ❌ 数据分散 | 高 | 高 |
自建 RAG 的核心优势:
- 数据不出域——合同、财务报表、客户数据完全留在你的 VPS
- 成本可预测——不管调用多少次,VPS 费用固定
- 模型可替换——今天用 bge-large-zh,明天换 bge-m3,随时切换
- 管线可定制——文档解析、切分策略、检索逻辑完全掌控
进阶优化:让 RAG 真正好用
1. 查询重写(Query Rewriting)
用户的原始问题往往不够精确。在检索前先改写查询:
def rewrite_query(original_query: str) -> List[str]:
"""
将用户问题改写为多个角度的查询
例如:"退款政策" → ["如何申请退款", "退款条件是什么", "退款流程"]
"""
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0.5)
prompt = f"""
用户的问题是:"{original_query}"
请将其改写为 3 个不同角度但意图相同的问题,用于文档检索。
只输出问题列表,每行一个问题,不要其他内容。
"""
response = llm.invoke(prompt)
return [q.strip() for q in response.content.strip().split("\n")]
2. HyDE(假设性文档嵌入)
生成一个假设性的答案,用这个答案去做向量检索,通常比直接用问题检索更准确:
def hyde_retrieve(query: str) -> List[Document]:
"""
HyDE: Hypothetical Document Embeddings
先生成一个假设性文档,再用它做检索
"""
llm = ChatOpenAI(model="gpt-4o-mini")
prompt = f"""
假设你有一份关于以下问题的完美文档,请生成这个文档的关键段落。
问题:{query}
只输出文档内容,不要解释。
"""
hypothetical_doc = llm.invoke(prompt).content
# 用假设性文档做检索
return vectorstore.similarity_search(hypothetical_doc, k=5)
3. 重排序(Reranking)
先用向量检索取 Top-50,再用重排序模型精选 Top-5:
# 部署 BGE Reranker
docker run -d --name reranker \
-p 8002:8000 \
-e MODEL_PATH="BAAI/bge-reranker-v2-m3" \
bge-reranker:latest
# 检索 + 重排序管线
def retrieve_with_rerank(query: str, top_k: int = 5) -> List[Document]:
# 第一步:向量检索召回 Top-50
candidates = vectorstore.similarity_search(query, k=50)
# 第二步:用重排序模型精选 Top-5
passages = [{"query": query, "passage": doc.page_content} for doc in candidates]
scores = reranker_service.rank(passages)
# 按分数排序,取 Top-K
ranked_indices = sorted(range(len(scores)), key=lambda i: scores[i], reverse=True)
return [candidates[i] for i in ranked_indices[:top_k]]
4. 文档时效性管理
知识库中的文档会过期,需要定期更新:
def manage_document_lifecycle():
"""
文档生命周期管理
- 新文档:解析 → Embed → 入库
- 更新文档:删除旧版本 → 重新入库
- 过期文档:标记为 deprecated,降低检索权重
"""
# 检查文档最后更新时间
# 超过 90 天的文档标记为待审核
# 超过 180 天的文档归档
pass
常见问题
Q:4核8GB 的 VPS 能跑多大的知识库?
对于 ChromaDB + bge-large-zh,8GB RAM 可以支撑约 5-10 万条文档块的向量索引。如果文档总量更大,建议切换到 Qdrant 并使用磁盘索引(DiskANN),可以将内存占用降低 60% 以上。
Q:需要 GPU 吗?
不需要。bge-large-zh 的 Embedding 推理在 CPU 上完全够用,单条文本约 50ms。如果你要同时处理大量文档的批量 Embedding,可以考虑 GPU 加速,但对日常检索来说 CPU 已经绰绰有余。
Q:中英文混合文档怎么处理?
推荐 bge-m3 模型,它原生支持多语言混合检索,不需要为中文和英文分别部署不同的 Embedding 模型。如果使用 bge-large-zh,建议对英文文档额外部署一个 English embedding 模型(如 text-embedding-3-small)。
Q:如何保证回答的准确性?
- 设置置信度阈值——检索结果的相关度低于阈值时,回答"我不知道"
- 引用来源——在每个回答中标注引用的文档片段
- 人工审核——对高频问题建立人工审核的知识条目
- 定期评估——用固定测试集评估检索和回答质量
Q:向量数据库的数据怎么备份?
# ChromaDB 备份
tar czf chroma-backup-$(date +%Y%m%d).tar.gz ./chroma_db/
# Qdrant 备份(在线热备)
curl -X POST http://localhost:6333/collections/knowledge_base/backup
# 备份文件存储在 ./backups/ 目录
# 建议:每日自动备份到对象存储(S3 兼容)
aws s3 cp ./backups/ s3://rag-backups/ --recursive
总结:用 $10/月搭建企业级 RAG 知识库
RAG 系统的核心价值不在于某个单一组件,而在于整条管线的协同:
- 文档解析质量决定了信息的完整性
- 向量数据库决定了检索效率
- Embedding 模型决定了语义理解的准确度
- 检索策略(混合检索 + 重排序)决定了最终召回质量
- 前端界面决定了用户体验
一台 $8-15/月的 VPS(RackNerd / Hostinger / Vultr)完全可以承载这套系统的全部组件。数据完全私有,成本可控,模型随时可换。
快速起步建议:
- 买一台 RackNerd 4核8GB VPS(年付约 $40-60)
- 用 Docker Compose 部署 ChromaDB + Embedding 服务 + Open WebUI
- 导入第一批文档,调整切分参数
- 用 HyDE + 重排序优化检索质量
- 建立定期更新和备份机制
👉 在 RackNerd 上搭建你的 RAG 知识库 👉 试试 Hostinger VPS(易用面板) 👉 Vultr 按小时计费,灵活扩展
