未记录事实核验 · 状态:维护中
记录仅说明核验时间与维护状态,不保证适用于所有环境;使用前请留意正文中的版本、适用条件与风险提示。
warning 适用边界与维护说明
本文适合已有 Python、Weaviate 部署和 LLM API 使用经验的读者,用自备文档学习内置生成、框架集成、进阶检索及问答原型设计;本次仅作全文结构审阅,未进行外部事实核验或代码运行验证。当前查询类型导入与 LangChain 检索器配置仍有运行障碍,LlamaIndex 建索引与后续读取的存储衔接也未正确落实;接口 top_k 未传递,入库成功数及后台任务响应不能代表最终写入结果,示例并发包装不能替代实际限流。本地 Docker 与云端连接应分支使用,本地 Python v4 连接须具备可达的 gRPC 服务,使用 OpenAI 模块的本地项目还需部署端密钥。第六节未实现图示重排序,问答路径未接入独立检索模块的阈值过滤,也未实现访问控制、租户隔离、入库目录限制和任务结果查询,不能直接作为企业生产服务部署。依赖安装未锁定版本,使用前需确认客户端、服务端与框架接口兼容性;切片建议和检索效果需按实际语料、模型及评估集调整。四项进阶参考目前仅保留名称,不提供可用跳转,不阻断自备材料的原型练习。 已取消无法确定目标的可点击引用「08-查询与搜索详解」,保留原提及;Ambiguous link target: 08-查询与搜索详解 已取消无法确定目标的可点击引用「Milvus/00-Milvus学习路线图」,保留原提及;Missing link target: Milvus/00-Milvus学习路线图 已取消无法确定目标的可点击引用「Qdrant/00-Qdrant学习路线图」,保留原提及;Missing link target: Qdrant/00-Qdrant学习路线图 已取消无法确定目标的可点击引用「向量数据库/00-向量数据库选型指南」,保留原提及;Missing link target: 向量数据库/00-向量数据库选型指南
Weaviate RAG 集成实战
大多数向量数据库做 RAG 是"存数据 → 检索 → 调 LLM"三步走,每一步都在你自己的代码里串联。Weaviate 的野心更大——它想让你用一条查询同时完成检索和生成。这体现了检索与生成编排位置的差异,可简化应用侧代码;是否降低端到端延迟,取决于部署位置、网络路径、模型响应时间和检索配置,需在同等条件下测量。
目录
- 一、RAG 基础与 Weaviate 的核心优势
- 二、内置 Generative 模块 RAG(重点)
- 三、LangChain 集成
- 四、LlamaIndex 集成
- 五、生产级 RAG 架构设计
- 六、完整项目实战:企业知识问答助手
- 七、与其他向量数据库的 RAG 对比
- 八、Weaviate 系列学习路径回顾
一、RAG 基础与 Weaviate 的核心优势
1.1 什么是 RAG
RAG(Retrieval-Augmented Generation,检索增强生成)是一种把向量数据库的检索能力和LLM 的生成能力结合起来的技术范式。它通过引入外部文档补充模型训练知识,并可在文档可靠、检索有效的条件下缓解幻觉问题,但不能保证消除幻觉。
标准 RAG 的三步流程:
%%{init: {"theme":"base","themeVariables":{"fontFamily":"PingFang SC, -apple-system, Source Han Serif SC, serif","fontSize":"16px","background":"#FDFBF5","primaryColor":"#FDFBF5","primaryTextColor":"#2D2A26","primaryBorderColor":"#6E6A60","secondaryColor":"#E8EFE8","secondaryBorderColor":"#8FA890","secondaryTextColor":"#2D2A26","tertiaryColor":"#FDFBF5","tertiaryBorderColor":"#9A9488","tertiaryTextColor":"#6E6A60","lineColor":"#6E6A60","textColor":"#2D2A26","mainBkg":"#FDFBF5","nodeBorder":"#2D5A3D","clusterBkg":"#F3EEDF","clusterBorder":"#9A9488","edgeLabelBackground":"#FDFBF5"},"flowchart":{"htmlLabels":true,"curve":"basis","padding":20,"nodeSpacing":60,"rankSpacing":70,"useMaxWidth":false},"themeCSS":".node rect, .node polygon, .node circle { stroke-width: 1.5px; } .edgePath .path { stroke-width: 1.5px; } .cluster-label .nodeLabel, .cluster-label foreignObject { color: #2D2A26 !important; } .edgeLabel, .edgeLabel foreignObject { color: #2D2A26 !important; background-color: #FDFBF5; } .edgeLabel rect { fill: #FDFBF5; }"}}%%
flowchart LR
A[用户提问] --> B[检索 Retrieve\n在向量数据库中找相关文档]
B --> C[增强 Augment\n把检索结果注入 Prompt]
C --> D[生成 Generate\nLLM 基于上下文回答]
style A fill:#e8f4f8,stroke:#4a90d9
style B fill:#fff3cd,stroke:#d4a017
style C fill:#d4edda,stroke:#28a745
style D fill:#f8d7da,stroke:#dc3545
为什么需要 RAG?
纯 LLM 的局限:
- 知识截止于训练时间,不知道最新信息
- 对私有数据(公司内部文档、产品手册)完全无知
- 容易产生幻觉(编造听起来合理但错误的答案)
RAG 的价值:
- 把最新、最准确的文档注入 LLM 的上下文
- LLM 基于事实回答,大幅减少幻觉
- 可以追溯答案来源,具备可解释性
1.2 Weaviate 的内置 RAG 优势
大多数向量数据库的 RAG 实现需要三次独立的网络调用:
应用代码 → Embedding API(生成查询向量)
应用代码 → 向量数据库(检索文档)
应用代码 → LLM API(生成答案)
Weaviate 的 Generative Search 把后两步合并了:
应用代码 → Embedding API(生成查询向量)
应用代码 → Weaviate(检索 + 调用 LLM + 返回答案)
这带来了三个实质性好处:
| 优势 | 具体体现 |
|---|---|
| 应用侧调用更少 | 将检索与生成编排移至 Weaviate 服务端;服务端仍需调用 LLM,端到端延迟需在同等条件下测量 |
| 更简单的代码 | 一次查询返回检索结果 + 生成内容,不需要手动拼装 Prompt |
| 统一 Pipeline | 语义搜索和生成在同一个 Module 生命周期里,06-Modules模块系统详解 中的配置即生效 |
note 内置 RAG vs 外部框架 RAG
内置 RAG(Generative Search)和 LangChain/LlamaIndex 集成不是互斥的。内置方式胜在应用侧编排简洁,外部框架胜在灵活(支持 Agent、多步推理、复杂 Chain);延迟差异取决于部署与调用路径。根据你的场景选择,或者混合使用。
二、内置 Generative 模块 RAG(重点)
2.1 前置配置
内置 RAG 依赖 06-Modules模块系统详解 中介绍的 Generative 模块。使用前需要:
第一步:部署时启用模块
在 Docker Compose 的环境变量中指定:
# docker-compose.yml
version: '3.4'
services:
weaviate:
image: cr.weaviate.io/semitechnologies/weaviate:1.28.0
environment:
# 同时启用向量化和生成模块
ENABLE_MODULES: "text2vec-openai,generative-openai"
DEFAULT_VECTORIZER_MODULE: "text2vec-openai"
OPENAI_APIKEY: "${OPENAI_API_KEY}"
ports:
- "8080:8080"
第二步:Collection Schema 中声明 generativeConfig
import weaviate
import weaviate.classes.config as wvc
from weaviate.classes.config import Configure, Property, DataType
client = weaviate.connect_to_weaviate_cloud(
cluster_url="your-cluster-url",
auth_credentials=weaviate.auth.AuthApiKey("your-api-key"),
headers={"X-OpenAI-Api-Key": "your-openai-key"},
)
client.collections.create(
name="Article",
# 向量化:自动把 text 字段转为向量
vector_config=Configure.Vectors.text2vec_openai(
model="text-embedding-3-small",
),
# 生成:内置 RAG 的 LLM 配置
generative_config=Configure.Generative.openai(
model="gpt-4o-mini",
temperature=0.2, # RAG 场景建议低温,减少幻觉
max_tokens=1024,
),
properties=[
Property(name="title", data_type=DataType.TEXT),
Property(name="content", data_type=DataType.TEXT),
Property(name="source", data_type=DataType.TEXT, skip_vectorization=True),
],
)
2.2 singleResult 模式:对每条文档独立生成
single_prompt 模式对每条检索结果分别调用 LLM,生成 N 个独立的回答。
适用场景:批量摘要生成、对每条文档做独立分析、内容标注。
import weaviate
import weaviate.classes.config as wvc
client = weaviate.connect_to_weaviate_cloud(
cluster_url="your-cluster-url",
auth_credentials=weaviate.auth.AuthApiKey("your-api-key"),
headers={"X-OpenAI-Api-Key": "your-openai-key"},
)
collection = client.collections.get("Article")
# single_prompt:对每条文档分别生成摘要
# Prompt 模板中用 {字段名} 引用文档字段
response = collection.generate.near_text(
query="量子计算的应用",
limit=3,
single_prompt=(
"请用中文为以下文章生成一段 50 字以内的摘要。\n"
"标题:{title}\n"
"内容:{content}"
),
)
# 每条检索结果都有独立的生成内容
for obj in response.objects:
print(f"文章:{obj.properties['title']}")
print(f"AI 摘要:{obj.generated}") # 每条文档独立的生成结果
print("---")
client.close()
tip single_prompt 的 Prompt 模板语法
用 {字段名} 引用 Collection 中定义的 property。如 {title}、{content}、{source} 等。字段名必须和 Schema 中的 property name 完全一致(区分大小写)。
2.3 groupedResult 模式:标准 RAG 问答
grouped_task 模式把所有检索结果合并成一个上下文,LLM 综合生成一个统一的回答。这才是典型的 RAG 场景。
适用场景:知识问答(Q&A)、综合摘要、基于文档库的智能助手。
collection = client.collections.get("Article")
question = "量子计算在密码学领域有哪些应用?"
# grouped_task:把所有检索结果合并给 LLM,生成一个综合答案
response = collection.generate.hybrid(
query=question,
alpha=0.75, # 0=纯关键词,1=纯向量,0.75 偏向语义
limit=5, # 检索 Top-5 作为上下文
grouped_task=(
f"你是一个专业的技术问答助手。请根据以下检索到的文档内容,回答用户问题。\n"
f"要求:\n"
f"1. 答案必须基于提供的文档,不要编造\n"
f"2. 如文档中没有相关信息,明确说明'未找到相关内容'\n"
f"3. 回答简洁,不超过 300 字\n\n"
f"用户问题:{question}"
),
grouped_properties=["title", "content"], # 传给 LLM 的字段
return_metadata=wvc.query.MetadataQuery(score=True),
)
# grouped_task 的生成内容在 response.generated 里(整个请求只有一个)
print(f"AI 回答:\n{response.generated}")
print(f"\n参考来源(共 {len(response.objects)} 条):")
for i, obj in enumerate(response.objects, 1):
score = obj.metadata.score if obj.metadata else "N/A"
print(f" {i}. {obj.properties['title']} (相关度: {score:.3f})")
client.close()
2.4 generative-ollama:完全本地化 RAG 方案
不想依赖 OpenAI API?用 text2vec-transformers + generative-ollama 实现无需外部推理 API 的本地 RAG;容器和模型准备完成后可离线运行,但仍有硬件、存储和运行成本。
完整 docker-compose 配置:
# docker-compose-local-rag.yml
version: '3.4'
services:
weaviate:
image: cr.weaviate.io/semitechnologies/weaviate:1.28.0
ports:
- "8080:8080"
- "50051:50051"
environment:
# 使用本地推理容器,不依赖任何外部 API
ENABLE_MODULES: "text2vec-transformers,generative-ollama"
DEFAULT_VECTORIZER_MODULE: "text2vec-transformers"
TRANSFORMERS_INFERENCE_API: "http://t2v-transformers:8080"
GENERATIVE_OLLAMA_APIENDPOINT: "http://ollama:11434"
CLUSTER_HOSTNAME: "node1"
volumes:
- weaviate_data:/var/lib/weaviate
depends_on:
- t2v-transformers
- ollama
# 本地 Embedding 推理容器(Chinese-optimized BERT)
t2v-transformers:
image: cr.weaviate.io/semitechnologies/transformers-inference:sentence-transformers-paraphrase-multilingual-mpnet-base-v2
environment:
ENABLE_CUDA: "0" # 有 GPU 改为 "1"
# 如果用中文场景,推荐换成:
# image: cr.weaviate.io/semitechnologies/transformers-inference:sentence-transformers-paraphrase-multilingual-MiniLM-L12-v2
# 本地 LLM 服务(Ollama)
ollama:
image: ollama/ollama:latest
ports:
- "11434:11434"
volumes:
- ollama_data:/root/.ollama
# 启动后需要手动拉取模型:
# docker exec -it ollama ollama pull qwen2.5:7b
volumes:
weaviate_data:
ollama_data:
启动并初始化:
# 启动所有服务
docker compose -f docker-compose-local-rag.yml up -d
# 等待服务就绪后,拉取本地 LLM
# Qwen2.5-7B 中文效果好,约 4GB
docker exec -it <ollama-container-id> ollama pull qwen2.5:7b
# 或者 Llama3-8B(英文场景)
docker exec -it <ollama-container-id> ollama pull llama3:8b
本地 RAG 的 Python 代码:
import weaviate
from weaviate.classes.config import Configure, Property, DataType
# 连接本地 Weaviate(无需任何 API Key)
client = weaviate.connect_to_local(
host="localhost",
port=8080,
)
# 创建使用本地模型的 Collection
client.collections.create(
name="LocalKnowledgeBase",
# text2vec-transformers 使用本地推理容器,零 API 成本
vector_config=Configure.Vectors.text2vec_transformers(),
# generative-ollama 使用本地 Qwen2.5
generative_config=Configure.Generative.ollama(
api_endpoint="http://ollama:11434",
model="qwen2.5:7b",
),
properties=[
Property(name="title", data_type=DataType.TEXT),
Property(name="content", data_type=DataType.TEXT),
Property(name="source", data_type=DataType.TEXT, skip_vectorization=True),
],
)
# 入库和查询方式与云端版本完全相同
collection = client.collections.get("LocalKnowledgeBase")
response = collection.generate.near_text(
query="数据仓库的分层设计",
limit=3,
grouped_task="请用中文综合以下文档内容,回答:数据仓库分层设计的核心原则是什么?",
grouped_properties=["title", "content"],
)
print(response.generated)
client.close()
tip 本地 LLM 选型建议
- 中文场景:Qwen2.5-7B(效果好,7B 参数在 CPU 上可运行)
- 英文场景:Llama3-8B 或 Mistral-7B
- 硬件要求:7B 模型建议至少 16GB RAM;有 GPU(NVIDIA)效果提升明显
- 更大模型:Qwen2.5-14B 效果更好,需要 32GB RAM
三、LangChain 集成
LangChain 给了你更高的灵活性:自定义 Prompt、构建多步 Chain、接入 Agent 系统。
3.1 安装依赖
pip install langchain-weaviate langchain-openai langchain-community
pip install langchain pypdf
3.2 初始化 WeaviateVectorStore
import weaviate
from langchain_weaviate import WeaviateVectorStore
from langchain_openai import OpenAIEmbeddings
# 连接 Weaviate
weaviate_client = weaviate.connect_to_weaviate_cloud(
cluster_url="your-cluster-url",
auth_credentials=weaviate.auth.AuthApiKey("your-weaviate-api-key"),
)
# 初始化 Embedding 模型
embeddings = OpenAIEmbeddings(
model="text-embedding-3-small",
openai_api_key="your-openai-api-key",
)
# 创建 WeaviateVectorStore(对接已有的 Collection)
vector_store = WeaviateVectorStore(
client=weaviate_client,
index_name="Article", # Weaviate Collection 名称
text_key="content", # 存储文本的字段名
embedding=embeddings,
attributes=["title", "source"], # 额外返回的元数据字段
)
3.3 从文档构建索引
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.document_loaders import PyPDFLoader, DirectoryLoader
# 加载 PDF 文档
loader = DirectoryLoader(
"./documents/",
glob="**/*.pdf",
loader_cls=PyPDFLoader,
)
raw_docs = loader.load()
# 文本切片:默认按字符长度计算,块大小为 512,相邻块重叠 64;不是 token 计数
splitter = RecursiveCharacterTextSplitter(
chunk_size=512,
chunk_overlap=64,
separators=["\n\n", "\n", "。", "!", "?", ".", "!", "?", " ", ""],
)
docs = splitter.split_documents(raw_docs)
print(f"原始文档:{len(raw_docs)} 篇 → 切片后:{len(docs)} 个 chunk")
# 批量入库(自动调用 Embedding,自动写入 Weaviate)
vector_store.add_documents(docs)
print("入库完成")
3.4 构建完整 RAG Chain(LCEL 语法)
LangChain Expression Language(LCEL)是 LangChain 推荐的 Chain 构建方式,用管道符 | 串联各个组件。
from langchain_openai import ChatOpenAI
from langchain.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
# 1. 构建 Retriever:从 Weaviate 检索 Top-5 相关文档
retriever = vector_store.as_retriever(
search_type="hybrid", # 混合搜索(向量 + BM25)
search_kwargs={
"k": 5, # 返回 Top-5
"alpha": 0.75, # 向量权重(0=纯关键词,1=纯向量)
"score_threshold": 0.5, # 相关度阈值,过滤低质量结果
},
)
# 2. 定义 Prompt 模板
prompt_template = ChatPromptTemplate.from_template(
"""你是一个专业的技术问答助手。请根据以下检索到的上下文回答问题。
上下文:
{context}
用户问题:{question}
要求:
- 答案必须基于提供的上下文,不要凭空编造
- 如果上下文中没有相关信息,明确回答"根据现有文档,未找到相关信息"
- 回答简洁清晰,不超过 400 字
- 如有多个文档提供了不同角度的信息,请综合说明"""
)
# 3. LLM 配置
llm = ChatOpenAI(
model="gpt-4o-mini",
temperature=0.2,
openai_api_key="your-openai-api-key",
)
# 4. 构建辅助函数:格式化检索到的文档
def format_docs(docs):
formatted = []
for i, doc in enumerate(docs, 1):
title = doc.metadata.get("title", f"文档 {i}")
source = doc.metadata.get("source", "未知来源")
formatted.append(f"[{i}] {title}\n来源:{source}\n{doc.page_content}")
return "\n\n".join(formatted)
# 5. 用 LCEL 管道符组装完整 RAG Chain
rag_chain = (
{
"context": retriever | format_docs, # 检索 → 格式化
"question": RunnablePassthrough(), # 问题直接透传
}
| prompt_template # 注入 Prompt 模板
| llm # 调用 LLM
| StrOutputParser() # 解析输出为字符串
)
# 6. 调用 Chain
answer = rag_chain.invoke("数据仓库的 ODS 层和 DWD 层有什么区别?")
print(answer)
3.5 带来源引用的增强版 Chain
生产环境中,用户往往需要知道答案来自哪里。以下是返回引用来源的版本:
from langchain_core.runnables import RunnableParallel
# 并行运行:同时保留检索文档和最终答案
rag_chain_with_sources = RunnableParallel(
{
"answer": rag_chain,
"sources": retriever, # 同时返回原始检索结果
}
)
result = rag_chain_with_sources.invoke("Kafka 的分区策略有哪些?")
print(f"回答:\n{result['answer']}")
print(f"\n参考来源({len(result['sources'])} 条):")
for i, doc in enumerate(result["sources"], 1):
title = doc.metadata.get("title", f"文档 {i}")
source = doc.metadata.get("source", "")
print(f" {i}. {title} — {source}")
3.6 内置 Generative 模块 vs LangChain 对比
| 维度 | 内置 Generative 模块 | LangChain 集成 |
|---|---|---|
| 代码量 | 少,10-20 行完成 RAG | 多,需要组装 Chain |
| 灵活性 | 中(固定 Pipeline) | 高(完全自定义) |
| 延迟 | 应用侧编排调用较少,端到端延迟需测量 | 调用路径可自定义,端到端延迟需测量 |
| 多步推理 | 不支持 | 支持(Agent) |
| LLM 选择 | 依赖 Weaviate Module 支持 | 任意 LLM |
| 本地 LLM | generative-ollama | 任意本地 LLM |
| 适合场景 | 简单 Q&A、快速原型 | 复杂工作流、Agent 系统 |
四、LlamaIndex 集成
LlamaIndex 在文档解析和索引构建方面比 LangChain 更专注,适合以文档为中心的 RAG 场景。
4.1 安装依赖
pip install llama-index llama-index-vector-stores-weaviate
pip install llama-index-embeddings-openai llama-index-llms-openai
4.2 构建 VectorStoreIndex
import weaviate
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader, Settings
from llama_index.vector_stores.weaviate import WeaviateVectorStore
from llama_index.embeddings.openai import OpenAIEmbedding
from llama_index.llms.openai import OpenAI
# 全局配置 LLM 和 Embedding
Settings.llm = OpenAI(model="gpt-4o-mini", temperature=0.2)
Settings.embed_model = OpenAIEmbedding(model="text-embedding-3-small")
# 连接 Weaviate
weaviate_client = weaviate.connect_to_weaviate_cloud(
cluster_url="your-cluster-url",
auth_credentials=weaviate.auth.AuthApiKey("your-api-key"),
)
# 创建 WeaviateVectorStore
vector_store = WeaviateVectorStore(
weaviate_client=weaviate_client,
index_name="LlamaIndexDocs", # Weaviate Collection 名称
text_key="content",
)
# 加载文档并构建索引(自动切片、向量化、入库)
documents = SimpleDirectoryReader("./documents/").load_data()
index = VectorStoreIndex.from_documents(
documents,
vector_store=vector_store,
show_progress=True,
)
print(f"索引构建完成,共 {len(documents)} 篇文档")
4.3 作为 QueryEngine 进行问答
# 从已有索引创建 QueryEngine(不重新入库)
index = VectorStoreIndex.from_vector_store(
vector_store=vector_store,
)
query_engine = index.as_query_engine(
similarity_top_k=5, # 检索 Top-5
streaming=False,
)
# 执行问答
response = query_engine.query("数据湖和数据仓库的核心区别是什么?")
print(f"回答:{response.response}")
print(f"\n参考节点数:{len(response.source_nodes)}")
for node in response.source_nodes:
print(f" - 相关度:{node.score:.3f} | {node.metadata.get('file_name', 'unknown')}")
4.4 多文档问答(SubQuestion 模式)
LlamaIndex 的 SubQuestionQueryEngine 可以把一个复杂问题分解为多个子问题,分别查询,再综合回答。
from llama_index.core.query_engine import SubQuestionQueryEngine
from llama_index.core.tools import QueryEngineTool
# 创建工具
query_engine_tool = QueryEngineTool.from_defaults(
query_engine=query_engine,
name="knowledge_base",
description="包含企业内部技术文档、产品手册和最佳实践的知识库",
)
# SubQuestion 引擎:自动分解问题,多路检索
sub_question_engine = SubQuestionQueryEngine.from_defaults(
query_engine_tools=[query_engine_tool],
verbose=True, # 显示子问题分解过程
)
# 复杂问题:引擎会自动分解
response = sub_question_engine.query(
"比较数据湖和数据仓库的适用场景,并说明什么情况下应该选择 Lakehouse 架构"
)
print(response.response)
五、生产级 RAG 架构设计
5.1 文档处理 Pipeline
文档处理质量是影响 RAG 效果的重要因素。“80%”只能作为说明其重要性的假设性比例,不是本文提供证据支持的测量结论;具体影响需结合语料、检索配置和评估集判断。
%%{init: {"theme":"base","themeVariables":{"fontFamily":"PingFang SC, -apple-system, Source Han Serif SC, serif","fontSize":"16px","background":"#FDFBF5","primaryColor":"#FDFBF5","primaryTextColor":"#2D2A26","primaryBorderColor":"#6E6A60","secondaryColor":"#E8EFE8","secondaryBorderColor":"#8FA890","secondaryTextColor":"#2D2A26","tertiaryColor":"#FDFBF5","tertiaryBorderColor":"#9A9488","tertiaryTextColor":"#6E6A60","lineColor":"#6E6A60","textColor":"#2D2A26","mainBkg":"#FDFBF5","nodeBorder":"#2D5A3D","clusterBkg":"#F3EEDF","clusterBorder":"#9A9488","edgeLabelBackground":"#FDFBF5"},"flowchart":{"htmlLabels":true,"curve":"basis","padding":20,"nodeSpacing":60,"rankSpacing":70,"useMaxWidth":false},"themeCSS":".node rect, .node polygon, .node circle { stroke-width: 1.5px; } .edgePath .path { stroke-width: 1.5px; } .cluster-label .nodeLabel, .cluster-label foreignObject { color: #2D2A26 !important; } .edgeLabel, .edgeLabel foreignObject { color: #2D2A26 !important; background-color: #FDFBF5; } .edgeLabel rect { fill: #FDFBF5; }"}}%%
flowchart TD
A[原始文档\nPDF / Word / HTML / Markdown] --> B[文档解析\nExtract text, tables, images]
B --> C[文本清洗\n去噪、格式标准化]
C --> D[智能切片\nChunking Strategy]
D --> E[元数据提取\n标题、作者、日期、分类]
E --> F[向量化\nEmbedding]
F --> G[写入 Weaviate\nBatch Insert]
G --> H[索引验证\n随机抽样测试检索质量]
style A fill:#e8f4f8,stroke:#4a90d9
style G fill:#d4edda,stroke:#28a745
style H fill:#fff3cd,stroke:#d4a017
切片策略选择:
| 策略 | 切片方式 | 适用场景 | 推荐参数 |
|---|---|---|---|
| 固定大小切片 | 按字符数/token 数 | 结构均匀的文档 | 512 tokens,64 重叠 |
| 按段落切片 | 按换行符/段落标记 | 结构化文档(文章、报告) | 保留语义完整性 |
| 语义切片 | 按语义相似度分组 | 复杂多主题文档 | 需要额外 Embedding 开销 |
| 父子切片 | 小切片搜索 + 大切片上下文 | 需要精准检索 + 完整上下文 | 搜索 256 tokens,上下文 1024 tokens |
tip 切片大小的黄金法则
- 太小(< 256 tokens):检索精准但上下文不够,LLM 无法充分理解
- 太大(> 1024 tokens):上下文丰富但向量模糊,检索召回率下降
- 推荐区间:512-1024 tokens,根据文档类型调整
- 中文场景:1 个汉字约等于 1.5 个 token,512 tokens ≈ 340 个汉字
父子切片(Parent-Child Chunking)实现:
from langchain.text_splitter import RecursiveCharacterTextSplitter
def create_parent_child_chunks(text: str, source: str) -> tuple[list, list]:
"""
父子切片策略:
- 子切片(child):按默认字符长度切分,chunk_size=256,用于语义检索
- 父切片(parent):按默认字符长度切分,chunk_size=1024,包含更多上下文,传给 LLM
"""
# 子切片:小而精,用于检索
child_splitter = RecursiveCharacterTextSplitter(
chunk_size=256,
chunk_overlap=32,
separators=["\n\n", "\n", "。", ".", " "],
)
# 父切片:大而全,用于生成上下文
parent_splitter = RecursiveCharacterTextSplitter(
chunk_size=1024,
chunk_overlap=128,
separators=["\n\n", "\n", "。", ".", " "],
)
parent_chunks = parent_splitter.split_text(text)
all_child_chunks = []
for parent_idx, parent_chunk in enumerate(parent_chunks):
child_chunks = child_splitter.split_text(parent_chunk)
for child_idx, child_chunk in enumerate(child_chunks):
all_child_chunks.append({
"content": child_chunk, # 存入 Weaviate,用于检索
"parent_content": parent_chunk, # 对应的父切片,传给 LLM
"source": source,
"parent_index": parent_idx,
"child_index": child_idx,
})
return parent_chunks, all_child_chunks
5.2 检索策略进阶
RAG 的检索质量决定了生成质量的上限。三种递进的检索策略:
基础语义搜索 → 混合搜索 → 带 Reranker 的混合搜索
参考 08-查询与搜索详解 中的检索详解,生产环境建议使用混合搜索 + Reranker 的组合,在召回率和精准度之间取得最优平衡。
HyDE(假设文档嵌入)技术:
对于模糊或简短的查询,直接 Embedding 效果差。HyDE 让 LLM 先"假设"一篇相关文档,用这篇假设文档的向量去检索,效果通常更好。
from langchain_openai import ChatOpenAI
def hyde_query(question: str, collection) -> list:
"""
HyDE:先让 LLM 生成假设文档,再用假设文档向量检索
适合:模糊问题、专业术语不明确的查询
"""
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0.7)
# 第一步:让 LLM 生成一篇假设性回答文档
hypothetical_doc = llm.predict(
f"请写一段关于以下问题的技术说明(不需要完全准确,150字以内):\n{question}"
)
# 第二步:用假设文档的向量去检索(near_text 会自动向量化)
response = collection.query.near_text(
query=hypothetical_doc, # 用假设文档代替原始问题
limit=5,
)
return response.objects
多路召回(Multi-Query):
def multi_query_retrieval(question: str, collection) -> list:
"""
多路召回:同一问题生成多个查询角度,扩大召回范围
"""
llm = ChatOpenAI(model="gpt-4o-mini")
# 生成 3 个不同角度的查询
query_prompt = f"""请从以下三个角度重新表达这个问题(每行一个,不要编号):
1. 更专业的技术角度
2. 更简单的日常表达
3. 相关但不同的侧面
问题:{question}
"""
queries_text = llm.predict(query_prompt)
queries = [q.strip() for q in queries_text.strip().split("\n") if q.strip()]
queries.append(question) # 加上原始问题
# 多路检索,合并去重
all_results = {}
for query in queries:
response = collection.query.near_text(query=query, limit=3)
for obj in response.objects:
obj_id = str(obj.uuid)
if obj_id not in all_results:
all_results[obj_id] = obj
return list(all_results.values())
5.3 RAG 效果评估:RAGAS 框架
评估 RAG 系统不能只看"感觉回答得不错",需要量化指标。本文使用 RAGAS 作为 RAG 评估框架示例,不据此判断其市场地位。
RAGAS 的四个核心指标:
| 指标 | 含义 | 计算方式 |
|---|---|---|
| Context Recall | 上下文召回率:检索结果覆盖了多少正确信息 | 需要参考答案 |
| Context Precision | 上下文精准率:检索结果中有多少是真正有用的 | 需要参考答案 |
| Answer Relevancy | 答案相关性:生成答案与问题的相关程度 | 不需要参考答案 |
| Faithfulness | 忠实度:生成答案是否基于检索到的上下文(不幻觉) | 不需要参考答案 |
pip install ragas
from ragas import evaluate
from ragas.metrics import (
context_recall,
context_precision,
answer_relevancy,
faithfulness,
)
from datasets import Dataset
# 准备评估数据集
# 每条数据需要:question(问题)、answer(生成答案)、contexts(检索上下文)
# ground_truth(参考答案)用于 context_recall 和 context_precision
eval_data = {
"question": ["数据仓库分层的目的是什么?"],
"answer": ["数据仓库分层是为了..."], # 你的 RAG 系统的回答
"contexts": [["ODS 层是原始数据层...", "DWD 层是明细数据层..."]], # 检索到的上下文
"ground_truth": ["数据仓库分层是为了解耦业务逻辑..."], # 标准答案
}
dataset = Dataset.from_dict(eval_data)
result = evaluate(
dataset=dataset,
metrics=[context_recall, context_precision, answer_relevancy, faithfulness],
)
print(result)
# 输出格式:{'context_recall': 0.85, 'context_precision': 0.72, ...}
常见 RAG 失败模式及解决方案:
| 失败模式 | 现象 | 解决方案 |
|---|---|---|
| 上下文不够(Low Context Recall) | 答案缺失关键信息 | 增加 Top-K、改用父子切片、调整混合搜索 alpha |
| 幻觉(Low Faithfulness) | 答案超出上下文范围 | 降低 LLM temperature、在 Prompt 中严格要求引用来源 |
| 答非所问(Low Answer Relevancy) | 回答跑题 | 改进 Prompt 模板、使用 HyDE 改善检索 |
| 上下文噪音(Low Context Precision) | 检索结果不相关 | 加入 Reranker、提高相关度阈值 |
六、完整项目实战:企业知识问答助手
把前面知识整合为企业知识问答原型的项目示例;当前代码仍存在维护说明所列缺陷,不应按原样视为完整可运行交付。
6.1 项目架构
%%{init: {"theme":"base","themeVariables":{"fontFamily":"PingFang SC, -apple-system, Source Han Serif SC, serif","fontSize":"16px","background":"#FDFBF5","primaryColor":"#FDFBF5","primaryTextColor":"#2D2A26","primaryBorderColor":"#6E6A60","secondaryColor":"#E8EFE8","secondaryBorderColor":"#8FA890","secondaryTextColor":"#2D2A26","tertiaryColor":"#FDFBF5","tertiaryBorderColor":"#9A9488","tertiaryTextColor":"#6E6A60","lineColor":"#6E6A60","textColor":"#2D2A26","mainBkg":"#FDFBF5","nodeBorder":"#2D5A3D","clusterBkg":"#F3EEDF","clusterBorder":"#9A9488","edgeLabelBackground":"#FDFBF5"},"flowchart":{"htmlLabels":true,"curve":"basis","padding":20,"nodeSpacing":60,"rankSpacing":70,"useMaxWidth":false},"themeCSS":".node rect, .node polygon, .node circle { stroke-width: 1.5px; } .edgePath .path { stroke-width: 1.5px; } .cluster-label .nodeLabel, .cluster-label foreignObject { color: #2D2A26 !important; } .edgeLabel, .edgeLabel foreignObject { color: #2D2A26 !important; background-color: #FDFBF5; } .edgeLabel rect { fill: #FDFBF5; }"}}%%
flowchart TD
subgraph "数据层"
W[Weaviate\n向量数据库]
end
subgraph "检索层"
HS[混合搜索\nHybrid Search]
RR[Reranker\n结果重排序]
HS --> RR
end
subgraph "生成层"
GR[groupedResult\n内置 Generative]
LC[LangChain Chain\n备选方案]
end
subgraph "API 层"
FA[FastAPI\nPOST /ask\nPOST /ingest]
end
subgraph "文档入库"
PDF[PDF 解析] --> CK[文档切片] --> BI[批量入库]
end
FA --> HS
W --> HS
RR --> GR
BI --> W
style W fill:#fff3cd,stroke:#d4a017
style FA fill:#d4edda,stroke:#28a745
style GR fill:#f8d7da,stroke:#dc3545
6.2 项目结构
| 文件/目录 | 说明 |
|---|---|
main.py |
FastAPI 主程序 |
ingest.py |
文档入库 Pipeline |
retriever.py |
检索模块 |
generator.py |
生成模块 |
models.py |
Pydantic 数据模型 |
config.py |
配置管理 |
documents/ |
待入库的文档目录 |
requirements.txt |
依赖清单 |
6.3 配置管理(config.py)
# config.py
import os
from dataclasses import dataclass
@dataclass
class Config:
# Weaviate
weaviate_url: str = os.getenv("WEAVIATE_URL", "http://localhost:8080")
weaviate_api_key: str = os.getenv("WEAVIATE_API_KEY", "")
collection_name: str = os.getenv("COLLECTION_NAME", "EnterpriseKB")
# OpenAI
openai_api_key: str = os.getenv("OPENAI_API_KEY", "")
embedding_model: str = "text-embedding-3-small"
llm_model: str = "gpt-4o-mini"
llm_temperature: float = 0.2
# 检索参数
top_k: int = 5
hybrid_alpha: float = 0.75 # 向量权重
score_threshold: float = 0.5 # 最低相关度
# 切片参数
chunk_size: int = 512
chunk_overlap: int = 64
config = Config()
6.4 文档入库 Pipeline(ingest.py)
# ingest.py
import weaviate
from weaviate.classes.config import Configure, Property, DataType
from langchain_community.document_loaders import PyPDFLoader, DirectoryLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from datetime import datetime, timezone
import hashlib
from config import config
def get_client() -> weaviate.WeaviateClient:
"""初始化 Weaviate 客户端"""
if config.weaviate_api_key:
return weaviate.connect_to_weaviate_cloud(
cluster_url=config.weaviate_url,
auth_credentials=weaviate.auth.AuthApiKey(config.weaviate_api_key),
headers={"X-OpenAI-Api-Key": config.openai_api_key},
)
else:
return weaviate.connect_to_local(host="localhost", port=8080)
def ensure_collection(client: weaviate.WeaviateClient) -> None:
"""确保 Collection 存在(不存在则创建)"""
if client.collections.exists(config.collection_name):
print(f"Collection '{config.collection_name}' 已存在,跳过创建")
return
client.collections.create(
name=config.collection_name,
description="企业内部知识库,用于 RAG 问答",
vector_config=Configure.Vectors.text2vec_openai(
model=config.embedding_model,
),
generative_config=Configure.Generative.openai(
model=config.llm_model,
temperature=config.llm_temperature,
max_tokens=1024,
),
properties=[
Property(name="title", data_type=DataType.TEXT),
Property(name="content", data_type=DataType.TEXT),
Property(
name="source",
data_type=DataType.TEXT,
skip_vectorization=True,
),
Property(
name="doc_id",
data_type=DataType.TEXT,
skip_vectorization=True,
),
Property(
name="chunk_index",
data_type=DataType.INT,
skip_vectorization=True,
),
Property(
name="created_at",
data_type=DataType.DATE,
skip_vectorization=True,
),
],
)
print(f"Collection '{config.collection_name}' 创建成功")
def load_and_chunk_documents(doc_dir: str) -> list[dict]:
"""加载目录中的所有 PDF 并切片"""
loader = DirectoryLoader(
doc_dir,
glob="**/*.pdf",
loader_cls=PyPDFLoader,
show_progress=True,
)
raw_docs = loader.load()
print(f"加载 {len(raw_docs)} 页原始文档")
splitter = RecursiveCharacterTextSplitter(
chunk_size=config.chunk_size,
chunk_overlap=config.chunk_overlap,
separators=["\n\n", "\n", "。", "!", "?", ".", "!", "?", " ", ""],
)
chunks = splitter.split_documents(raw_docs)
print(f"切片完成:共 {len(chunks)} 个 chunk")
result = []
for i, chunk in enumerate(chunks):
source = chunk.metadata.get("source", "unknown")
doc_id = hashlib.md5(source.encode()).hexdigest()[:8]
result.append({
"title": f"{source.split('/')[-1]} - 片段 {i+1}",
"content": chunk.page_content,
"source": source,
"doc_id": doc_id,
"chunk_index": i,
"created_at": datetime.now(timezone.utc).isoformat(),
})
return result
def batch_ingest(doc_dir: str = "./documents") -> dict:
"""完整入库流程:加载 → 切片 → 写入 Weaviate"""
client = get_client()
try:
ensure_collection(client)
chunks = load_and_chunk_documents(doc_dir)
collection = client.collections.get(config.collection_name)
success_count = 0
error_count = 0
# 动态批处理:自动调节 batch size
with collection.batch.dynamic() as batch:
for chunk in chunks:
try:
batch.add_object(properties=chunk)
success_count += 1
except Exception as e:
error_count += 1
print(f"入库失败:{chunk['source']} - {e}")
return {
"total": len(chunks),
"success": success_count,
"errors": error_count,
}
finally:
client.close()
if __name__ == "__main__":
result = batch_ingest()
print(f"\n入库完成:总计 {result['total']} 条,成功 {result['success']} 条,失败 {result['errors']} 条")
6.5 检索模块(retriever.py)
# retriever.py
import weaviate
import weaviate.classes.config as wvc
from dataclasses import dataclass
from config import config
@dataclass
class RetrievedDoc:
title: str
content: str
source: str
score: float
def retrieve(
client: weaviate.WeaviateClient,
question: str,
top_k: int = None,
alpha: float = None,
) -> list[RetrievedDoc]:
"""
混合搜索检索:向量搜索 + BM25 关键词搜索
alpha 参数:
- 0.0:纯 BM25 关键词搜索(精确匹配优先)
- 1.0:纯向量搜索(语义相似优先)
- 0.75:偏向语义,同时保留关键词匹配(推荐)
"""
top_k = top_k or config.top_k
alpha = config.hybrid_alpha if alpha is None else alpha
collection = client.collections.get(config.collection_name)
response = collection.query.hybrid(
query=question,
alpha=alpha,
limit=top_k,
return_metadata=wvc.query.MetadataQuery(score=True),
return_properties=["title", "content", "source"],
)
results = []
for obj in response.objects:
score = obj.metadata.score if obj.metadata else 0.0
# 过滤低相关度结果
if score >= config.score_threshold:
results.append(RetrievedDoc(
title=obj.properties.get("title", ""),
content=obj.properties.get("content", ""),
source=obj.properties.get("source", ""),
score=score,
))
return results
6.6 生成模块(generator.py)
# generator.py
import weaviate
import weaviate.classes.config as wvc
from dataclasses import dataclass
from retriever import RetrievedDoc
from config import config
@dataclass
class QAResult:
answer: str
sources: list[RetrievedDoc]
question: str
def generate_answer_builtin(
client: weaviate.WeaviateClient,
question: str,
) -> QAResult:
"""
使用 Weaviate 内置 Generative 模块回答问题(应用侧编排简单,延迟需结合部署条件测量)
"""
collection = client.collections.get(config.collection_name)
prompt = (
f"你是一个企业知识问答助手。请根据以下检索到的文档内容,用中文回答问题。\n"
f"要求:基于文档内容作答,不要编造信息;如果文档中没有相关内容,说明'文档中未找到相关信息'。\n\n"
f"用户问题:{question}"
)
response = collection.generate.hybrid(
query=question,
alpha=config.hybrid_alpha,
limit=config.top_k,
grouped_task=prompt,
grouped_properties=["title", "content"],
return_metadata=wvc.query.MetadataQuery(score=True),
)
sources = []
for obj in response.objects:
score = obj.metadata.score if obj.metadata else 0.0
sources.append(RetrievedDoc(
title=obj.properties.get("title", ""),
content=obj.properties.get("content", ""),
source=obj.properties.get("source", ""),
score=score,
))
return QAResult(
answer=response.generated or "未能生成回答",
sources=sources,
question=question,
)
6.7 FastAPI 接口(main.py)
# main.py
from fastapi import FastAPI, HTTPException, BackgroundTasks
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
from contextlib import asynccontextmanager
import weaviate
from generator import generate_answer_builtin, QAResult
from ingest import batch_ingest, get_client
from config import config
# ---- 数据模型 ----
class AskRequest(BaseModel):
question: str
top_k: int = 5
class SourceItem(BaseModel):
title: str
source: str
score: float
class AskResponse(BaseModel):
answer: str
sources: list[SourceItem]
question: str
class IngestRequest(BaseModel):
doc_dir: str = "./documents"
class IngestResponse(BaseModel):
total: int
success: int
errors: int
message: str
# ---- 应用生命周期管理 ----
weaviate_client: weaviate.WeaviateClient = None
@asynccontextmanager
async def lifespan(app: FastAPI):
# 启动:初始化 Weaviate 连接
global weaviate_client
weaviate_client = get_client()
print("Weaviate 连接已建立")
yield
# 关闭:释放连接
if weaviate_client:
weaviate_client.close()
print("Weaviate 连接已关闭")
# ---- FastAPI 应用 ----
app = FastAPI(
title="企业知识问答助手",
description="基于 Weaviate RAG 的企业内部知识问答 API",
version="1.0.0",
lifespan=lifespan,
)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_methods=["*"],
allow_headers=["*"],
)
# ---- 接口定义 ----
@app.post("/ask", response_model=AskResponse)
async def ask(request: AskRequest) -> AskResponse:
"""
知识问答接口
输入:用户问题
输出:AI 生成的答案 + 参考来源列表
"""
if not request.question.strip():
raise HTTPException(status_code=400, detail="问题不能为空")
if len(request.question) > 500:
raise HTTPException(status_code=400, detail="问题过长,请控制在 500 字以内")
try:
result = generate_answer_builtin(
client=weaviate_client,
question=request.question,
)
return AskResponse(
answer=result.answer,
sources=[
SourceItem(
title=src.title,
source=src.source,
score=round(src.score, 4),
)
for src in result.sources
],
question=result.question,
)
except Exception as e:
raise HTTPException(status_code=500, detail=f"问答服务出错:{str(e)}")
@app.post("/ingest", response_model=IngestResponse)
async def ingest_documents(
request: IngestRequest,
background_tasks: BackgroundTasks,
) -> IngestResponse:
"""
文档入库接口
输入:文档目录路径
输出:入库统计(后台异步执行)
"""
def run_ingest():
result = batch_ingest(request.doc_dir)
print(f"后台入库完成:{result}")
background_tasks.add_task(run_ingest)
return IngestResponse(
total=0,
success=0,
errors=0,
message=f"入库任务已启动,正在处理 {request.doc_dir} 目录下的文档",
)
@app.get("/health")
async def health_check():
"""健康检查"""
try:
is_ready = weaviate_client.is_ready()
return {"status": "ok", "weaviate_ready": is_ready}
except Exception as e:
raise HTTPException(status_code=503, detail=f"服务不可用:{str(e)}")
# ---- 启动命令 ----
# uvicorn main:app --host 0.0.0.0 --port 8000 --reload
6.8 生产注意事项
warning 并发控制:LLM API 有 QPS 限制
若将 GPT-4o-mini 的请求额度假设为 500 RPM,可据此设计限流示例;实际额度以所用账号和模型的当前配额为准,本文不将该数字作为统一的 Tier 1 限制。高并发场景需要:
- 接入请求队列(推荐 Redis + Celery)
- 设置 FastAPI 的并发限制(
asyncio.Semaphore) - 监控 API 限流错误,自动退避重试
# 并发限制示例:最多同时 10 个 LLM 请求
import asyncio
llm_semaphore = asyncio.Semaphore(10)
async def ask_with_rate_limit(question: str):
async with llm_semaphore:
return generate_answer_builtin(weaviate_client, question)
tip 缓存策略:相同问题不重复计算
- Embedding 缓存:相同查询的向量不重复计算,用 Redis 缓存
- 答案缓存:相同问题(语义相似)的答案缓存,适合 FAQ 类场景
- 简单实现:用
functools.lru_cache或 Redis 的 TTL 缓存
import hashlib
import json
from functools import lru_cache
@lru_cache(maxsize=1000) # 缓存最近 1000 个问题的答案
def cached_answer(question_hash: str, question: str) -> str:
result = generate_answer_builtin(weaviate_client, question)
return result.answer
def ask_with_cache(question: str) -> str:
question_hash = hashlib.md5(question.encode()).hexdigest()
return cached_answer(question_hash, question)
note 引用来源:增加答案可信度
在回答中附上原文引用是生产级 RAG 系统的标配。在 Prompt 中明确要求 LLM 标注引用:
请在回答末尾用 [参考1]、[参考2] 的格式标注所用文档,然后列出参考文档的标题。
七、与其他向量数据库的 RAG 对比
| 维度 | Weaviate 内置 RAG | Milvus + LangChain | Qdrant + LangChain |
|---|---|---|---|
| 实现复杂度 | 低(内置,一次 API 调用) | 高(需组装 Chain) | 高(同 Milvus) |
| 代码量 | 约 20 行完成 RAG | 约 50-80 行 | 约 50-80 行 |
| 灵活性 | 中(固定 Pipeline) | 高(完全自定义) | 高 |
| 延迟 | 应用侧一次编排调用,端到端延迟需测量 | 取决于框架编排、部署与模型响应,需测量 | 取决于框架编排、部署与模型响应,需测量 |
| 本地化支持 | 好(generative-ollama) | 好(任意本地 LLM) | 好 |
| 多步推理 | 不支持 | 支持(Agent) | 支持 |
| 混合搜索 | 原生支持 | 需要额外配置 | 原生支持 |
| 适合场景 | 快速原型、标准 Q&A | 复杂工作流、Agent | 复杂工作流 |
选型建议:
- 需要快速构建原型:可考虑 Weaviate 内置 RAG;“一周内上线 MVP”可作为语料、环境和接口需求已准备就绪时的计划目标,不是交付保证
- 需要Agent / 多步推理:LangChain + 任意向量数据库
- 需要极致定制 Prompt:外部框架(LangChain / LlamaIndex)
- 需要完全本地化:Weaviate + generative-ollama(最简配置)或 Qdrant + Ollama
八、Weaviate 系列学习路径回顾
你完成了 Weaviate 系列全部 10 篇的学习。回顾一下走过的路径:
%%{init: {"theme":"base","themeVariables":{"fontFamily":"PingFang SC, -apple-system, Source Han Serif SC, serif","fontSize":"16px","background":"#FDFBF5","primaryColor":"#FDFBF5","primaryTextColor":"#2D2A26","primaryBorderColor":"#6E6A60","secondaryColor":"#E8EFE8","secondaryBorderColor":"#8FA890","secondaryTextColor":"#2D2A26","tertiaryColor":"#FDFBF5","tertiaryBorderColor":"#9A9488","tertiaryTextColor":"#6E6A60","lineColor":"#6E6A60","textColor":"#2D2A26","mainBkg":"#FDFBF5","nodeBorder":"#2D5A3D","clusterBkg":"#F3EEDF","clusterBorder":"#9A9488","edgeLabelBackground":"#FDFBF5"},"flowchart":{"htmlLabels":true,"curve":"basis","padding":20,"nodeSpacing":60,"rankSpacing":70,"useMaxWidth":false},"themeCSS":".node rect, .node polygon, .node circle { stroke-width: 1.5px; } .edgePath .path { stroke-width: 1.5px; } .cluster-label .nodeLabel, .cluster-label foreignObject { color: #2D2A26 !important; } .edgeLabel, .edgeLabel foreignObject { color: #2D2A26 !important; background-color: #FDFBF5; } .edgeLabel rect { fill: #FDFBF5; }"}}%%
flowchart LR
subgraph "入门阶段"
A1["01 概述与核心定位\nAI-Native 设计哲学"]
A2["02 架构深度解析\nHNSW + 分片机制"]
A3["03 核心概念详解\nClass / Property / Object"]
end
subgraph "动手阶段"
B1["04 安装与部署\nWCS / Docker / K8s"]
B2["05 第一个应用\n端到端跑通"]
B3["06 Modules 系统\nVectorizer + Generative"]
B4["07 Schema 设计\n数据建模最佳实践"]
end
subgraph "生产阶段"
C1["08 查询与搜索\n混合搜索 + Reranker"]
C2["09 生产运维\nRBAC + 监控 + 调优"]
end
subgraph "专家阶段"
D1["10 RAG 集成实战\n本篇,系列终篇"]
end
A1 --> A2 --> A3 --> B1 --> B2 --> B3 --> B4 --> C1 --> C2 --> D1
style D1 fill:#fce7f3,stroke:#db2777
你在本系列中学到的核心能力:
| 能力 | 对应篇章 |
|---|---|
| 理解 AI-Native 设计哲学,正确选型 | 01、02 |
| 搭建完整 Weaviate 环境(云 / 本地) | 03、04 |
| 配置 Modules 实现自动向量化 | 06 |
| 设计高质量的 RAG Schema | 02、07 |
| 混合搜索 + Reranker 精准检索 | 08 |
| 生产环境调优和运维 | 09 |
| 构建 RAG 原型并识别生产化所需能力 | 10(本篇) |
下一步推荐
完成 Weaviate 系列后,根据你的方向选择下一步:
方向一:扩展向量数据库视野
- Milvus 学习路线图 — 了解云原生架构、超大规模向量存储
- Qdrant 学习路线图 — 深入量化压缩、Rust 实现的性能优势
- 向量数据库选型指南 — 综合选型决策框架
方向二:深入 RAG 和 AI 应用工程
- LangChain 进阶:Agent、Tool Calling、多步推理
- LlamaIndex 进阶:知识图谱、结构化数据问答
- RAG 评估体系:RAGAS 框架的深度使用,建立 RAG 质量基准
方向三:生产化和 MLOps
- 向量数据库的监控方案(Prometheus + Grafana)
- RAG 系统的 A/B 测试方法论
- 大规模 RAG 的成本优化策略
quote 系列结语
Weaviate 的独特性在于它试图回答一个问题:如果数据库本身能理解数据的语义,世界会变成什么样?从 Modules 系统的自动向量化,到 Generative Search 的内置 RAG,Weaviate 始终在把 AI 能力往数据库这一层推。这是一个值得关注的方向——不是因为它一定是最终答案,而是因为它在探索一条真正不同的路。
学完这个系列,你可以尝试从零构建 RAG 原型,并通过真实项目检验所学;生产级交付仍需结合访问控制、并发与限流、评估、监控和故障处理等要求验证。接下来,去构建真实的东西吧。
#Weaviate #RAG #向量数据库 #LangChain #LlamaIndex #生成式AI #AI工程 #生产实践