一、向量数据库
向量库是一种专门用于存储和检索高维向量(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}")
检索结果:
- 员工每年享有15天带薪年假,入职满一年后生效。
-
- 试用期为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的模式中加入知识库,其结构如下。
基于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模式。