FAISS知识库搭建

构建基于嵌入向量(Embedding)的知识库是构建智能问答系统或语义搜索引擎的核心步骤。以下是系统化的流程和代码示例,帮助你围绕问题主体(如产品信息、技术文档等)高效实现知识库:


一、核心步骤

1. 数据准备

  • 收集文档:整理知识库原始数据(如PDF、网页、FAQ、手册等),拆分为独立段落/句子。
  • 预处理:清洗文本(去噪、分句、统一格式),示例:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    import re

    def preprocess_text(text):
    text = re.sub(r'\s+', ' ', text).strip() # 去除多余空格
    return text.lower() # 可选:统一小写

    documents = [
    "黄瓜的价格是五磅十五便士。产地为英国农场。",
    "西红柿每公斤售价3.5英镑。有机种植。",
    # ...其他文档
    ]
    processed_docs = [preprocess_text(doc) for doc in documents]

2. 生成嵌入向量

  • 选择模型:使用高精度模型(如 qwen2.5:0.5btext-embedding-ada-002)。
  • 批量生成向量
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    import ollama

    def generate_embeddings(texts, model='qwen2.5:0.5b'):
    embeddings = []
    for text in texts:
    response = ollama.embeddings(model=model, prompt=text)
    embeddings.append(response.data) # 假设response.data是向量
    return embeddings

    doc_embeddings = generate_embeddings(processed_docs)

3. 构建向量数据库

  • 存储向量与元数据:使用FAISS、Pinecone或Milvus。
  • FAISS示例(本地部署):
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    import faiss
    import numpy as np

    # 初始化FAISS索引(L2距离)
    dim = len(doc_embeddings[0]) # 向量维度
    index = faiss.IndexFlatL2(dim)

    # 添加向量和文档ID
    doc_ids = list(range(len(documents))) # 原始文档索引
    index.add(np.array(doc_embeddings).astype('float32'))

    # 保存索引(可选)
    faiss.write_index(index, "knowledge_base.faiss")

4. 查询与检索

  • 用户查询处理:生成查询向量并检索相似文档。
  • 完整流程示例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    def query_knowledge_base(query, index, documents, doc_embeddings, top_k=3):
    # 生成查询向量
    query_response = ollama.embeddings(model='qwen2.5:0.5b', prompt=query)
    query_embedding = query_response.data

    # 检索相似文档
    D, I = index.search(np.array([query_embedding]).astype('float32'), k=top_k)

    # 返回结果
    results = []
    for i, (dist, idx) in enumerate(zip(D[0], I[0])):
    results.append({
    "rank": i + 1,
    "document": documents[idx],
    "distance": dist # 距离越小越相似
    })
    return results

    # 示例查询
    query = "黄瓜的产地和价格?"
    results = query_knowledge_base(query, index, documents, doc_embeddings)
    for res in results:
    print(f"排名 {res['rank']}: {res['document']} (距离: {res['distance']:.2f})")

二、关键优化点

1. 提升检索精度

  • 归一化向量:在添加到数据库前对向量做L2归一化。
    1
    2
    3
    4
    def normalize_embeddings(embeddings):
    return [emb / np.linalg.norm(emb) for emb in embeddings]

    doc_embeddings = normalize_embeddings(doc_embeddings)
  • 使用余弦相似度:FAISS默认支持L2距离,但可通过自定义索引实现余弦相似度。

2. 处理大规模数据

  • 分片存储:将数据按主题/时间分片,避免单个索引过大。
  • 云向量数据库:使用Pinecone、Weaviate等托管服务,支持动态扩展。

3. 动态更新知识库

  • 增量更新:定期爬取新数据,生成向量并追加到索引。
  • 删除旧数据:通过重建索引或使用支持删除的数据库(如Milvus)。

三、完整代码框架

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
import ollama
import faiss
import numpy as np
from typing import List, Dict, Any

class KnowledgeBase:
def __init__(self, model_name: str = 'qwen2.5:0.5b'):
self.model_name = model_name
self.index = None
self.documents = []
self.doc_embeddings = []

def add_documents(self, documents: List[str]):
"""添加文档并生成向量"""
processed_docs = [self._preprocess(doc) for doc in documents]
embeddings = self._generate_embeddings(processed_docs)

if self.index is None:
dim = len(embeddings[0])
self.index = faiss.IndexFlatL2(dim)

self.documents.extend(documents)
self.doc_embeddings.extend(embeddings)
self.index.add(np.array(embeddings).astype('float32'))

def query(self, query: str, top_k: int = 3) -> List[Dict[str, Any]]:
"""查询相似文档"""
query_embedding = self._generate_embeddings([self._preprocess(query)])[0]
D, I = self.index.search(np.array([query_embedding]).astype('float32'), k=top_k)

results = []
for dist, idx in zip(D[0], I[0]):
results.append({
"rank": len(results) + 1,
"document": self.documents[idx],
"distance": dist
})
return results

@staticmethod
def _preprocess(text: str) -> str:
"""文本预处理"""
return re.sub(r'\s+', ' ', text).strip().lower()

def _generate_embeddings(self, texts: List[str]) -> List[List[float]]:
"""生成嵌入向量"""
embeddings = []
for text in texts:
response = ollama.embeddings(model=self.model_name, prompt=text)
embeddings.append(response.data)
return embeddings


# 使用示例
if __name__ == "__main__":
kb = KnowledgeBase()
kb.add_documents([
"黄瓜的价格是五磅十五便士。产地为英国农场。",
"西红柿每公斤售价3.5英镑。有机种植。",
"苹果每袋10英镑,来自法国果园。"
])

query = "英国的蔬菜价格?"
results = kb.query(query)
for res in results:
print(f"排名 {res['rank']}: {res['document']} (距离: {res['distance']:.2f})")

四、应用场景

  1. 智能客服:自动回答产品规格、政策等问题。
  2. 企业文档检索:快速定位技术手册中的相关章节。
  3. 个性化推荐:根据用户查询推荐相似商品或内容。

五、注意事项

  • 模型选择:平衡精度与成本(如 qwen2.5:0.5b 适合中小规模,text-embedding-ada-002 适合英文)。
  • 性能调优:通过压缩向量(如PCA降维)或使用GPU加速大规模检索。
  • 隐私合规:敏感数据需本地化部署,避免云端泄露。

通过以上步骤,你可以快速构建一个基于嵌入向量的高效知识库,支持语义级别的精准检索。

链接

Donate
  • Copyright: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.
  • Copyrights © 2023-2025 John Doe
  • Visitors: | Views:

请我喝杯茶吧~

支付宝
微信