构建AI知识库:ollama-python + Chroma向量数据库实战

【免费下载链接】ollama-python 【免费下载链接】ollama-python 项目地址: https://gitcode.com/GitHub_Trending/ol/ollama-python

你是否还在为海量文档检索效率低下而困扰?是否希望拥有一个本地化部署、数据隐私可控的AI知识库系统?本文将带你从零开始,通过ollama-python客户端与Chroma向量数据库的无缝集成,构建一个高性能的智能知识库解决方案。读完本文,你将掌握文本嵌入生成、向量存储、相似度检索的全流程实现,以及如何将知识库与大语言模型结合构建问答系统。

技术栈选型与架构设计

核心组件解析

组件 功能描述 核心优势 适用场景
ollama-python Ollama API的Python客户端 轻量级、异步支持、类型安全 本地LLM调用、文本嵌入生成
Chroma 开源向量数据库 零依赖部署、内存/磁盘双模式、Python原生API 向量存储、相似度搜索、知识库构建
llama3.2 Meta开源大语言模型 多模态支持、本地部署、高性能 文本嵌入、自然语言理解、问答生成

系统架构流程图

mermaid

环境准备与依赖安装

基础环境要求

  • Python 3.8+ 环境
  • 至少8GB内存(推荐16GB以上)
  • 支持AVX2指令集的CPU(或NVIDIA GPU)
  • 网络连接(用于模型下载)

依赖安装命令

# 创建虚拟环境
python -m venv ai-knowledge-base
source ai-knowledge-base/bin/activate  # Linux/Mac
# Windows: ai-knowledge-base\Scripts\activate

# 安装核心依赖
pip install ollama chromadb python-dotenv tqdm

Ollama服务部署

# 安装Ollama(Linux示例)
curl -fsSL https://ollama.com/install.sh | sh

# 启动Ollama服务
ollama serve &

# 拉取嵌入模型(首次运行需下载约4GB)
ollama pull llama3.2

核心功能实现

1. 文本预处理模块

import re
from typing import List, Tuple
from langchain.text_splitter import RecursiveCharacterTextSplitter

class TextProcessor:
    def __init__(self, chunk_size: int = 500, chunk_overlap: int = 50):
        """
        初始化文本处理器
        :param chunk_size: 每个文本块的字符数
        :param chunk_overlap: 文本块重叠字符数
        """
        self.splitter = RecursiveCharacterTextSplitter(
            chunk_size=chunk_size,
            chunk_overlap=chunk_overlap,
            separators=["\n\n", "\n", ". ", " ", ""]
        )
    
    def clean_text(self, text: str) -> str:
        """清理文本:去除多余空白和特殊字符"""
        text = re.sub(r'\s+', ' ', text).strip()
        text = re.sub(r'\u00a0', ' ', text)  # 处理非断行空格
        return text
    
    def split_document(self, text: str, metadata: dict = None) -> List[dict]:
        """
        将文档分割为带元数据的文本块
        :param text: 原始文本
        :param metadata: 文档元数据(如标题、来源等)
        :return: 分割后的文本块列表
        """
        if metadata is None:
            metadata = {}
            
        text = self.clean_text(text)
        chunks = self.splitter.split_text(text)
        
        return [
            {
                "id": f"chunk_{i}",
                "text": chunk,
                **metadata
            } 
            for i, chunk in enumerate(chunks)
        ]

2. Ollama嵌入生成器

import ollama
from typing import List, Union

class OllamaEmbeddingGenerator:
    def __init__(self, model: str = "llama3.2", host: str = None):
        """
        初始化嵌入生成器
        :param model: 嵌入模型名称
        :param host: Ollama服务地址(默认本地)
        """
        self.model = model
        self.client = ollama.Client(host=host) if host else ollama
    
    def generate_embeddings(self, texts: Union[str, List[str]]) -> List[List[float]]:
        """
        生成文本嵌入向量
        :param texts: 单个文本字符串或文本列表
        :return: 嵌入向量列表
        """
        if isinstance(texts, str):
            texts = [texts]
            
        try:
            response = self.client.embed(
                model=self.model,
                input=texts,
                # 对于长文本可启用自动截断
                truncate=True
            )
            return response["embeddings"]
        except Exception as e:
            print(f"嵌入生成失败: {str(e)}")
            raise

3. Chroma向量数据库管理器

import chromadb
from chromadb.config import Settings
from typing import List, Dict, Any, Optional

class ChromaVectorDB:
    def __init__(
        self, 
        collection_name: str = "ai_knowledge_base",
        persist_directory: str = "./chroma_db",
        embedding_dim: int = 4096  # llama3.2嵌入维度
    ):
        """
        初始化向量数据库
        :param collection_name: 集合名称
        :param persist_directory: 数据持久化目录
        :param embedding_dim: 嵌入向量维度
        """
        self.client = chromadb.Client(Settings(
            persist_directory=persist_directory,
            chroma_db_impl="duckdb+parquet",
            anonymized_telemetry=False  # 禁用遥测
        ))
        self.collection = self.client.get_or_create_collection(
            name=collection_name,
            metadata={"hnsw:space": "cosine"}  # 使用余弦相似度
        )
        self.persist_directory = persist_directory
    
    def add_documents(
        self, 
        texts: List[str], 
        metadatas: Optional[List[Dict[str, Any]]] = None,
        ids: Optional[List[str]] = None
    ) -> None:
        """
        添加文档到向量数据库
        :param texts: 文本内容列表
        :param metadatas: 元数据列表
        :param ids: 文档ID列表
        """
        if metadatas is None:
            metadatas = [{} for _ in texts]
            
        # 生成嵌入向量
        embedding_generator = OllamaEmbeddingGenerator()
        embeddings = embedding_generator.generate_embeddings(texts)
        
        # 添加到数据库
        self.collection.add(
            documents=texts,
            embeddings=embeddings,
            metadatas=metadatas,
            ids=ids if ids else [f"doc_{i}" for i in range(len(texts))]
        )
        
        # 持久化数据
        self.client.persist()
    
    def similarity_search(
        self, 
        query: str, 
        top_k: int = 5
    ) -> List[Dict[str, Any]]:
        """
        相似度搜索
        :param query: 查询文本
        :param top_k: 返回结果数量
        :return: 搜索结果列表
        """
        # 生成查询向量
        embedding_generator = OllamaEmbeddingGenerator()
        query_embedding = embedding_generator.generate_embeddings(query)[0]
        
        # 执行搜索
        results = self.collection.query(
            query_embeddings=[query_embedding],
            n_results=top_k
        )
        
        # 格式化结果
        formatted_results = []
        for i in range(len(results["ids"][0])):
            formatted_results.append({
                "id": results["ids"][0][i],
                "text": results["documents"][0][i],
                "metadata": results["metadatas"][0][i] if results["metadatas"] else {},
                "distance": results["distances"][0][i]
            })
            
        return formatted_results

完整项目实现

项目目录结构

ai-knowledge-base/
├── src/
│   ├── __init__.py
│   ├── text_processor.py    # 文本处理模块
│   ├── embedding_generator.py # 嵌入生成模块
│   ├── vector_db.py         # 向量数据库模块
│   └── knowledge_base.py    # 知识库主类
├── data/                    # 文档数据目录
│   └── sample_docs/
├── chroma_db/               # Chroma数据库目录
├── .env                     # 环境变量配置
├── requirements.txt         # 项目依赖
└── main.py                  # 示例入口

知识库主类实现

from src.text_processor import TextProcessor
from src.vector_db import ChromaVectorDB
import ollama
from typing import List, Dict, Any

class AIKnowledgeBase:
    def __init__(
        self,
        collection_name: str = "ai_knowledge_base",
        persist_directory: str = "./chroma_db",
        llm_model: str = "llama3.2"
    ):
        """
        初始化AI知识库
        :param collection_name: 向量数据库集合名称
        :param persist_directory: 数据库持久化目录
        :param llm_model: 用于生成回答的LLM模型
        """
        self.text_processor = TextProcessor()
        self.vector_db = ChromaVectorDB(
            collection_name=collection_name,
            persist_directory=persist_directory
        )
        self.llm_model = llm_model
    
    def ingest_document(
        self, 
        text: str, 
        metadata: Dict[str, Any] = None
    ) -> None:
        """
        处理并入库单篇文档
        :param text: 文档文本内容
        :param metadata: 文档元数据
        """
        if metadata is None:
            metadata = {}
            
        # 文本分块
        chunks = self.text_processor.split_document(text, metadata)
        
        # 提取文本和元数据
        chunk_texts = [chunk["text"] for chunk in chunks]
        chunk_metadatas = [chunk for chunk in chunks]
        chunk_ids = [chunk["id"] for chunk in chunks]
        
        # 添加到向量数据库
        self.vector_db.add_documents(
            texts=chunk_texts,
            metadatas=chunk_metadatas,
            ids=chunk_ids
        )
        
        print(f"成功入库 {len(chunk_texts)} 个文本块")
    
    def query(
        self, 
        question: str, 
        top_k: int = 5,
        stream: bool = False
    ) -> Union[str, Dict[str, Any]]:
        """
        查询知识库
        :param question: 用户问题
        :param top_k: 检索相关文档数量
        :param stream: 是否流式返回结果
        :return: 回答结果
        """
        # 检索相关文档
        relevant_docs = self.vector_db.similarity_search(question, top_k)
        
        # 构建提示词
        context = "\n\n".join([doc["text"] for doc in relevant_docs])
        
        prompt = f"""基于以下上下文信息回答用户问题。如果无法从上下文中找到答案,直接说"没有找到相关信息"。

上下文:
{context}

用户问题: {question}
回答:"""
        
        # 调用LLM生成回答
        if stream:
            response = ollama.chat(
                model=self.llm_model,
                messages=[{"role": "user", "content": prompt}],
                stream=True
            )
            return response
        else:
            response = ollama.chat(
                model=self.llm_model,
                messages=[{"role": "user", "content": prompt}]
            )
            
            # 添加引用来源
            sources = [
                {
                    "id": doc["id"],
                    "metadata": doc["metadata"],
                    "distance": doc["distance"]
                } 
                for doc in relevant_docs
            ]
            
            return {
                "answer": response["message"]["content"],
                "sources": sources
            }

使用示例

# main.py
from src.knowledge_base import AIKnowledgeBase

def main():
    # 初始化知识库
    knowledge_base = AIKnowledgeBase(
        collection_name="my_docs",
        persist_directory="./chroma_db",
        llm_model="llama3.2"
    )
    
    # 示例文档内容
    sample_document = """
    人工智能(AI)是计算机科学的一个分支,致力于创建能够模拟人类智能的系统。AI的发展可以追溯到20世纪50年代,
    当时艾伦·图灵提出了著名的"图灵测试"来评估机器是否具有智能。
    
    AI主要分为两大类:弱人工智能(ANI)和强人工智能(AGI)。弱人工智能是为特定任务设计的系统,如语音识别、
    图像分类等。强人工智能则指具有与人类相当的通用智能系统,目前仍处于理论研究阶段。
    
    机器学习是AI的一个重要子领域,它使系统能够通过数据学习而不是显式编程。深度学习是机器学习的一个分支,
    使用多层神经网络处理复杂数据,已在计算机视觉、自然语言处理等领域取得突破性进展。
    
    AI的应用领域非常广泛,包括医疗诊断、自动驾驶、智能推荐、金融分析等。随着技术的不断进步,AI正在
    改变各行各业的运作方式,并对社会产生深远影响。
    """
    
    # 入库文档
    knowledge_base.ingest_document(
        text=sample_document,
        metadata={
            "title": "人工智能基础概述",
            "source": "AI科普手册",
            "timestamp": "2024-01-15"
        }
    )
    
    # 查询知识库
    while True:
        question = input("请输入你的问题(输入q退出): ")
        if question.lower() == 'q':
            break
            
        result = knowledge_base.query(question)
        print("\n回答:", result["answer"])
        
        print("\n参考来源:")
        for i, source in enumerate(result["sources"], 1):
            print(f"{i}. ID: {source['id']}, 相似度: {1 - source['distance']:.4f}")
        print("\n" + "-"*50 + "\n")

if __name__ == "__main__":
    main()

性能优化与最佳实践

文本分块策略对比

分块策略 优势 劣势 适用场景
固定长度 实现简单,性能稳定 可能切断语义单元 非结构化文本
语义感知 保持完整语义单元 实现复杂,耗时 结构化文档、代码
混合策略 平衡语义完整性和效率 参数调优复杂 通用场景

向量数据库优化

# 优化的Chroma配置
def create_optimized_chroma_client():
    return chromadb.Client(Settings(
        persist_directory="./chroma_db_optimized",
        chroma_db_impl="duckdb+parquet",
        anonymized_telemetry=False,
        # 索引优化参数
        chroma_collection_metadata={
            "hnsw:space": "cosine",  # 余弦相似度适合文本
            "hnsw:construction_ef": 128,  # 构建索引时的候选节点数
            "hnsw:m": 16  # 每个节点的邻居数量
        }
    ))

常见问题解决方案

问题 解决方案 实施难度
嵌入生成速度慢 1. 使用更轻量的模型
2. 启用批量处理
3. 优化Ollama服务配置
查询准确率低 1. 调整top_k参数(3-10)
2. 使用更好的嵌入模型
3. 优化文本分块策略
内存占用过高 1. 使用磁盘模式而非内存模式
2. 减少同时加载的模型数量
3. 增加swap空间
中文支持不佳 1. 使用专为中文优化的模型
2. 调整文本分块策略
3. 添加中文停用词过滤

高级功能扩展

多模态知识库支持

def add_image_to_knowledge_base(image_path: str, description: str, metadata: dict = None):
    """添加图片到知识库"""
    if metadata is None:
        metadata = {}
    
    # 使用ollama生成图像描述(需要支持多模态的模型)
    response = ollama.chat(
        model="llava",  # 多模态模型
        messages=[
            {"role": "user", "content": f"描述这张图片的内容: ![]({image_path})"}
        ]
    )
    
    image_description = response["message"]["content"]
    
    # 组合描述和用户提供的信息
    combined_text = f"图片描述: {image_description}\n用户备注: {description}"
    
    # 添加到知识库
    knowledge_base = AIKnowledgeBase()
    knowledge_base.ingest_document(combined_text, {**metadata, "type": "image"})
    
    return image_description

自动文档更新机制

from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler

class DocumentWatcher(FileSystemEventHandler):
    def __init__(self, knowledge_base, watch_dir="./docs_to_watch"):
        self.knowledge_base = knowledge_base
        self.watch_dir = watch_dir
        
    def on_modified(self, event):
        if not event.is_directory and event.src_path.endswith(('.txt', '.md', '.pdf')):
            print(f"检测到文件更新: {event.src_path}")
            # 读取文件内容并更新知识库
            with open(event.src_path, 'r', encoding='utf-8') as f:
                content = f.read()
            self.knowledge_base.ingest_document(
                content, 
                {"source": event.src_path, "last_modified": datetime.now().isoformat()}
            )

# 使用示例
observer = Observer()
event_handler = DocumentWatcher(knowledge_base)
observer.schedule(event_handler, path="./docs_to_watch", recursive=False)
observer.start()

部署与扩展

Docker容器化部署

FROM python:3.10-slim

WORKDIR /app

# 安装依赖
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# 复制项目文件
COPY . .

# 暴露端口(如需要API服务)
EXPOSE 8000

# 启动命令
CMD ["python", "main.py"]

分布式部署架构

mermaid

总结与未来展望

项目成果回顾

本文构建的AI知识库系统具有以下特点:

  1. 本地化部署:所有数据和模型均运行在本地环境,确保数据隐私安全
  2. 低成本高效:基于开源软件栈,无需昂贵的云服务费用
  3. 可扩展性强:模块化设计支持功能扩展和性能优化
  4. 多模态支持:通过扩展可支持文本、图像等多种数据类型

性能指标对比

指标 本系统 传统检索系统 云服务方案
响应延迟 300-800ms 50-200ms 200-500ms
部署成本 低(开源软件) 高(按调用计费)
隐私保护 高(本地部署) 低(数据上云)
定制化程度

未来发展方向

  1. 模型优化:探索更高效的嵌入模型和量化技术
  2. 知识图谱融合:结合知识图谱增强推理能力
  3. 自动更新机制:实现文档变更检测和增量更新
  4. 多语言支持:优化多语言文档的处理和检索能力
  5. 边缘计算支持:适配低功耗设备的轻量化版本

通过本文介绍的方案,你可以快速构建一个功能完善、性能优异的本地化AI知识库系统,为企业或个人提供安全可控的智能问答服务。随着开源生态的不断发展,这一方案的功能和性能还将持续提升,成为处理和利用知识的强大工具。


如果你觉得本文对你有帮助,请点赞、收藏并关注作者,获取更多AI工程实践内容。下一期我们将探讨如何构建支持百万级文档的分布式知识库系统。

【免费下载链接】ollama-python 【免费下载链接】ollama-python 项目地址: https://gitcode.com/GitHub_Trending/ol/ollama-python

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐