大模型开发中,如何基于向量数据库实现知识库

66 阅读5分钟

一、向量数据库

向量库是一种专门用于存储和检索高维向量(embeddings)的数据库。它存储的是一种向量表示。主要功能是支持向量相似度查询。它解决了传统关系型数据库无法支持相似度查询的问题。在AI时代,这种支持近似检索场景大放光芒。

常见的向量数据库有Chroma、Milvus等。

举个例子,先基于chroma实现一个向量库的存放和读取功能。

from chromadb.utils import embedding_functions  
  
# 1️⃣ 初始化客户端(持久化到磁盘)  
client = chromadb.PersistentClient(path="./my_vector_db")  
  
# 2️⃣ 创建集合(自动使用嵌入模型)  
collection = client.get_or_create_collection(  
    name="company_policies",  
    embedding_function=embedding_functions.SentenceTransformerEmbeddingFunction(  
        model_name="all-MiniLM-L6-v2" # 免费、轻量、离线可用  
    )  
)  
  
# 3️⃣ 添加文档  
documents = [  
"员工每年享有15天带薪年假,入职满一年后生效。",  
"加班需提前申请,工作日加班按1.5倍工资计算。",  
"试用期为3个月,期间薪资为正式工资的80%。"  
]  
metadatas = [  
    {"category": "leave", "source": "HR手册_v1"},  
    {"category": "overtime", "source": "HR手册_v1"},  
    {"category": "probation", "source": "HR手册_v1"}  
    ]  
    ids = ["doc1", "doc2", "doc3"]  
  
collection.add(  
    documents=documents,  
    metadatas=metadatas,  
    ids=ids  
)  
  
# 4️⃣ 语义搜索  
results = collection.query(  
    query_texts=["年假有多少天?"],  
    n_results=2  
)  
  
print("检索结果:")  
for i, doc in enumerate(results['documents'][0]):  
    print(f"{i+1}. {doc}")

检索结果:

  1. 员工每年享有15天带薪年假,入职满一年后生效。
    1. 试用期为3个月,期间薪资为正式工资的80%。

第二条虽然不直接相关,但因“试用期”和“年假”都属于“员工权益”,在向量空间中距离较近(可通过调整 n_results 或使用更好模型优化)。

如果用传统mysql数据库来存放,则如下:

CREATE TABLE policy (  
id INT PRIMARY KEY,  
content TEXT,  
category VARCHAR(50),  
source VARCHAR(100)  
);  
  
-- 插入数据  
INSERT INTO policy VALUES  
(1, '员工每年享有15天带薪年假...', 'leave', 'HR手册'),  
(2, '加班需提前申请...', 'overtime', 'HR手册');  
  
-- 查询(只能基于关键词或字段)  
SELECT content FROM policy WHERE content LIKE '%年假%';  
-- 或  
SELECT content FROM policy WHERE category = 'leave';

传统关系型数据库的局限性体现在:

  • 必须精确匹配关键词(如用户问“一年能休几天假?”,LIKE '%年假%' 找不到)
  • 无法理解语义:“假期” ≠ “年假”(除非人工维护同义词)
  • 无法处理模糊意图:“我能休多久?” → 无法关联到年假政策

假设用户要问 "我一年能休多少天假?" ,在传统数据库中,很难用SQL语句去表达相关逻辑。

而 Chroma 自动理解语义,即使没出现“年假”二字,如下查询仍能返回“员工每年享有15天带薪年假...”

results = collection.query(query_texts=[query], n_results=1)

因为在向量库中,所有信息是分解为多维向量存放的(有各种算法将一个文本拆分为N维向量),而检索的过程是通过向量运算来获取近似度。与关系型数据库中的like查询具有完全不同的模式和算法。

二、知识库

知识库(knowledge base)是一个存储结构化或非结构化知识的系统,用于支持问答、推理、决策等智能任务。

知识库 ≠ 向量库,但向量库可以是知识库的一种实现方式。两者是 概念层级不同、用途有交集但不等同 的技术组件。

在 RAG(Retrieval-Augmented Generation)架构 中,向量库是知识库的检索引擎。向量库只是加速知识的检索。

可以简单如下对比:

维度知识库(Knowledge Base)向量库(Vector Store)
本质知识的集合(内容)向量的存储与检索系统(工具)
存储内容原始文本、事实、规则、文档数值向量(embeddings)+ 元数据
是否可读是(人类可理解)否(向量是数字,不可读)
用途提供事实依据、支持推理快速语义搜索、相似性匹配
依赖关系可独立存在通常依赖知识库提供原始数据

三、实现一个知识库

在我上一篇文章介绍的LLM+MCP+AI-agent的模式中加入知识库,其结构如下。

image.png

基于chroma构建知识库,代码如下:

import os  
import logging  
from typing import List, Dict, Optional  
import chromadb  
from chromadb.utils import embedding_functions  
  
logger = logging.getLogger("KnowledgeBase")  
  
  
class KnowledgeBase:  
def __init__(  
    self,  
    persist_directory: str = "./chroma_db",  
    collection_name: str = "docs",  
    embedding_model: str = "all-MiniLM-L6-v2"  
    ):  
    self.persist_directory = persist_directory  
    self.collection_name = collection_name  
  
    # 使用 SentenceTransformer 嵌入模型(无需联网)  
    self.embedding_fn = embedding_functions.SentenceTransformerEmbeddingFunction(  
        model_name=embedding_model  
    )  
  
    # 初始化 Chroma 客户端(持久化到磁盘)  
    self.client = chromadb.PersistentClient(path=persist_directory)  
    self.collection = self.client.get_or_create_collection(  
    name=collection_name,  
    embedding_function=self.embedding_fn,  
    metadata={"hnsw:space": "cosine"}  
    )  
    logger.info(f"📚 KnowledgeBase loaded. Docs count: {self.collection.count()}")  
  
def add_documents(self, documents: List[str], metadatas: Optional[List[Dict]] = None,  
ids: Optional[List[str]] = None):  
    """添加文档到知识库"""  
    if ids is None:  
        ids = [f"doc_{i}" for i in range(len(documents))]  
    if metadatas is None:  
        metadatas = [{} for _ in documents]  
  
    self.collection.add(  
        documents=documents,  
        metadatas=metadatas,  
        ids=ids  
    )  
    logger.info(f"✅ Added {len(documents)} documents to knowledge base.")  
  
def search(self, query: str, top_k: int = 3) -> List[Dict]:  
    """语义搜索,返回相关片段"""  
    results = self.collection.query(  
    query_texts=[query],  
    n_results=top_k  
    )  
  
    # 整理结果  
    matches = []  
    for i in range(len(results["ids"][0])):  
        matches.append({  
            "content": results["documents"][0][i],  
            "metadata": results["metadatas"][0][i] if results["metadatas"] else {},  
            "score": results["distances"][0][i] # 越小越相似  
        })  
    return matches  
  
  
# 全局实例(单例)  
kb = KnowledgeBase()

完成了知识库的创建,具体引入到原来的架构中,可以在原来智能体的代码中,增加知识库的引用。

async def _build_rag_context(self, query: str) -> str:  
    """构建 RAG 上下文"""  
    if not self.rag_enabled:  
        return ""  
  
    try:  
        results = kb.search(query, top_k=3)  
        if not results:  
            return ""  
  
        context = "Relevant knowledge:\n"  
        for i, res in enumerate(results, 1):  
            content = res["content"].strip()  
            source = res["metadata"].get("source", "unknown")  
            context += f"[{i}] {content} (Source: {source})\n"  
        return context + "\n"  
    except Exception as e:  
        self.logger.warning(f"⚠️ RAG retrieval failed: {e}")  
        return ""  
  
  
async def chat(self, user_input: str, conversation_history: List[Dict] = None) -> str:  
    # Step 1: 检索知识  
    rag_context = await self._build_rag_context(user_input)  
  
    # Step 2: 构造完整 prompt  
    system_prompt = (  
        "You are a helpful AI assistant. "  
        "Use the following relevant knowledge to answer the question accurately. "  
        "If the knowledge is irrelevant, ignore it and answer based on your general knowledge.\n\n"  
        f"{rag_context}"  
    )  
  
    messages = [{"role": "system", "content": system_prompt}]  
    if conversation_history:  
        messages.extend(conversation_history)  
        messages.append({"role": "user", "content": user_input})  
  
    # Step 3: 调用 LLM  
    response = await self.ollama_client.chat_complete(  
        model=self.model,  
        messages=messages  
    )  
    return response

也可以把知识库的检索作为一个MCP服务添加到MCP SERVER中。

@register_tool("search_knowledge", "Search internal knowledge base for relevant information")  
def search_knowledge(query: str, top_k: int = 3) -> str:  
    results = kb.search(query, top_k=top_k)  
    if not results:  
        return "No relevant information found."  
  
    output = ""  
    for i, res in enumerate(results, 1):  
        output += f"{i}. {res['content']} (Source: {res['metadata'].get('source', 'N/A')})\n"  
    return output.strip()

这样在我上一篇文章的基础上,我们添加了知识库,实现了RAG模式。