基于RAG向量数据库的知识库AI问答助手设计与实现
引言
随着大语言模型(LLM)技术的快速发展,构建本地知识库AI问答助手已成为许多企业级应用的需求。本研究报告将详细介绍如何基于FLASK开发一个使用本地OLLAMA大模型底座的知识库AI问答助手,该系统能够整合两部分知识语料:网站博客(存储在SQLite数据库中)和后台配置的知识博客URL链接内容(通过爬虫获取)。系统采用RAG(检索增强生成)技术,在用户提问时能够从两部分知识库中检索相关信息并生成高质量回答。
RAG技术概述
RAG(检索增强生成)是一种结合了检索系统和生成模型的技术,它首先从大型文档集合中检索相关信息,然后使用这些信息来生成最终答案。RAG的核心思想是"检索+生成":前者主要利用向量数据库的高效存储和检索能力,召回目标知识;后者则是利用大模型和Prompt工程,将召回的知识合理利用,生成目标答案[1]。
 RAG架构通常使用转换器实现,包含编码器和解码器两部分。当用户提出问题时,输入文本被"编码"为捕获单词含义的向量,而向量被"解码"到我们的文档内容中[2]。

系统架构设计
整体架构
基于用户需求,我们设计了一个完整的AI问答助手系统架构,主要包含以下几个部分:
- 前端Web应用:基于FLASK开发,提供用户交互界面
 - 向量数据库:使用Milvus存储和检索向量表示的知识
 - 大模型底座:使用本地OLLAMA运行大模型
 - 知识处理组件:处理SQLite数据库中的博客和URL链接内容
 - RAG组件:实现检索和生成功能
整体技术细节为: - 嵌入模型使用Sentence Transformers
 - 本地使用OLLAMA部署大模型
 - 使用Milvus作为向量数据库
 - 使用LangChain作为框架集成各组件
 
技术选型
Milvus向量数据库
Milvus是一个专为处理和搜索大量向量数据而设计的强大向量数据库。它以高性能和可扩展性著称,非常适合机器学习、深度学习、相似性搜索任务和推荐系统[5]。Milvus的主要功能包括:
- 高性能向量检索能力,支持百亿级别的向量索引
 - 多种搜索方式(top-K & Range ANN搜索、稀疏和稠密向量搜索、多向量搜索、Grouping搜索)
 - 提供元数据过滤功能
 - 支持多租户架构
 - 支持数据分区分片、数据持久化、增量数据摄取
 - 支持标量向量混合查询和time travel功能
Milvus被广泛应用于智能客服、推荐系统、NLP服务、计算机视觉等领域,能够为大模型提供强大的知识库支持[6]。 
Ollama本地大模型
Ollama是一个开源平台,可简化大型语言模型(LLM)的本地运行和定制。它提供了用户友好的无云体验,无需高级技术技能即可轻松实现模型部署[10]。Ollama的主要特点包括:
- 本地部署:不依赖云端服务,用户可以在自己的设备上运行模型
 - 简化模型管理:提供便捷的模型管理功能
 - 丰富的预建模型库:支持多种主流模型
 - 跨平台支持:可在不同操作系统上运行
 - 灵活的自定义选项:允许用户根据需求调整模型
Ollama通过提供便捷的模型管理、丰富的预建模型库、跨平台支持以及灵活的自定义选项,使得开发者和研究人员能够轻松使用大型语言模型[15]。 
LangChain框架
LangChain是一个专门为LLM应用设计的框架,它允许开发人员将不同的组件像链一样串在一起,以围绕LLM创建更高级的应用。LangChain提供了一系列模块,这些模块是任何LLM应用的基础[49]。
 LangChain的核心思想是开发人员可以将不同的组件像链一样串在一起,以围绕LLM创建更高级的示例。LangChain提供了一系列模块,这些模块是作为任何LLM应用的基础[49]。
系统流程设计
系统的主要流程包括:
- 知识库构建: 
  
- 从SQLite数据库中读取博客内容
 - 爬取后台配置的URL链接内容
 - 使用文档加载器和拆分器处理文本内容
 - 使用嵌入模型生成向量表示
 - 将向量和元数据存储到Milvus数据库中
 
 - 用户提问处理: 
  
- 接收用户提问
 - 使用相同的嵌入模型生成提问向量
 - 从Milvus中检索相关知识
 - 使用大模型和检索到的知识生成回答
 
 - 大模型推理: 
  
- 使用OLLAMA运行大模型
 - 通过LangChain的ChatModel接口与大模型交互
 - 生成最终回答
 
 
系统组件实现
知识库构建
1. 从SQLite数据库读取博客内容
首先需要从SQLite数据库中读取博客内容。可以使用Python的sqlite3模块来实现:
import sqlite3
from typing import List, Dict
def fetch_blog_content(db_path: str) -> List[Dict[str, str]]:
    """从SQLite数据库中读取博客内容"""
    conn = sqlite3.connect(db_path)
    cursor = conn.cursor()
    
    # 假设博客表名为blogs,包含id、title、content等字段
    cursor.execute("SELECT id, title, content FROM blogs")
    rows = cursor.fetchall()
    
    blogs = []
    for row in rows:
        blog = {
            "id": row[0],
            "title": row[1],
            "content": row[2]
        }
        blogs.append(blog)
    
    conn.close()
    return blogs
 
2. 爬取后台配置的URL链接内容
需要爬取后台配置的URL链接内容。可以使用Python的requests和BeautifulSoup库来实现:
import requests
from bs4 import BeautifulSoup
from typing import List, Dict
def crawl_url_content(urls: List[str]) -> List[Dict[str, str]]:
    """爬取指定URL的内容"""
    crawled_content = []
    
    for url in urls:
        try:
            response = requests.get(url)
            response.raise_for_status()
            
            soup = BeautifulSoup(response.text, 'html.parser')
            # 假设我们只关心正文内容
            content = soup.get_text()
            
            crawled_content.append({
                "url": url,
                "content": content
            })
        except Exception as e:
            print(f"Error crawling {url}: {e}")
    
    return crawled_content
 
3. 文档加载和拆分
使用LangChain的文档加载器和拆分器来处理文本内容。对于结构化数据(如博客),可以使用Pydantic加载器;对于非结构化数据(如网页内容),可以使用Text加载器。然后使用RecursiveCharacterTextSplitter将文本拆分为适当的块大小:
from langchain.document_loaders import Pydantic, Text
from langchain.text_splitter import RecursiveCharacterTextSplitter
from typing import List, Dict, Any
def process_documents(documents: List[Dict[str, str]]) -> List[Dict[str, Any]]:
    """处理文档内容,包括加载和拆分"""
    processed_docs = []
    
    for doc in documents:
        # 根据文档类型选择合适的加载器
        if "blog" in doc:  # 假设我们用"blog"标记博客内容
            loader = Pydantic(BlogDocument)
        else:
            loader = Text()
        
        # 加载文档
        loaded_doc = loader.load(doc)
        
        # 使用递归字符拆分器拆分文本
        text_splitter = RecursiveCharacterTextSplitter(
            chunk_size=1000,
            chunk_overlap=200
        )
        
        splits = text_splitter.split_documents([loaded_doc])
        
        for split in splits:
            processed_docs.append({
                "content": split.page_content,
                "metadata": split.metadata
            })
    
    return processed_docs
 
4. 向量化和存储到Milvus
使用嵌入模型(如Sentence Transformers)将文本内容转换为向量,并存储到Milvus数据库中:
from sentence_transformers import SentenceTransformer
import numpy as np
from milvus import Milvus
def embed_and_store_to_milvus(documents: List[Dict[str, str]], 
                             milvus_host: str, 
                             milvus_port: int, 
                             collection_name: str):
    """将文档内容嵌入向量并存储到Milvus"""
    # 加载嵌入模型
    model = SentenceTransformer('all-MiniLM-L6-v2')
    
    # 连接到Milvus
    milvus = Milvus(host=milvus_host, port=milvus_port)
    
    # 检查集合是否存在,如果不存在则创建
    if collection_name not in milvus.list_collections():
        milvus.create_collection(collection_name, 
                                 dim=model.get_sentence_embedding_dimension(),
                                 index_params={'index_type': 'IVF_FLAT',
                                              'params': {'nlist': 100},
                                              'metric_type': 'L2'})
    
    # 准备向量和元数据
    embeddings = []
    metadatas = []
    
    for doc in documents:
        # 生成嵌入向量
        embedding = model.encode(doc["content"]).astype(np.float32).tolist()
        embeddings.append(embedding)
        
        # 准备元数据
        metadata = {"source": doc["source"], 
                    "chunk": doc["chunk"]}
        metadatas.append(metadata)
    
    # 插入到Milvus
    status, ids = milvus.insert(collection_name=collection_name,
                                records=embeddings,
                                metadatas=metadatas)
    
    if status.OK():
        print(f"Successfully inserted {len(ids)} documents into Milvus collection {collection_name}")
    else:
        print(f"Error inserting documents into Milvus: {status}")
 
用户提问处理
1. 提问向量化
使用相同的嵌入模型将用户提问转换为向量:
from sentence_transformers import SentenceTransformer
import numpy as np
def vectorize_query(query: str, model: SentenceTransformer) -> List[float]:
    """将用户提问向量化"""
    query_vector = model.encode(query).astype(np.float32).tolist()
    return query_vector
 
2. 从Milvus中检索相关知识
使用Milvus从向量数据库中检索最相关的知识:
from milvus import Milvus
def retrieve_from_milvus(query_vector: List[float],
                        milvus_host: str,
                        milvus_port: int,
                        collection_name: str,
                        top_k: int = 5) -> List[Dict[str, Any]]:
    """从Milvus中检索相关知识"""
    # 连接到Milvus
    milvus = Milvus(host=milvus_host, port=milvus_port)
    
    # 检索
    status, results = milvus.search(
        collection_name=collection_name,
        query_records=[query_vector],
        top_k=top_k,
        params={'nprobe': 10}
    )
    
    if status.OK():
        # 解析结果
        retrieved_docs = []
        for result in results[0]:
            doc = {
                "content": result.id,  # 假设id存储了内容
                "score": result.distance,
                "metadata": result.metadata
            }
            retrieved_docs.append(doc)
        
        return retrieved_docs
    else:
        print(f"Error retrieving from Milvus: {status}")
        return []
 
3. 使用大模型生成回答
使用OLLAMA运行大模型,并使用检索到的知识生成回答:
import requests
from typing import Dict, Any
def query_ollama_model(query: str,
                      context: str,
                      model_name: str,
                      ollama_host: str,
                      ollama_port: int) -> str:
    """使用OLLAMA查询大模型"""
    prompt = f"根据以下上下文回答问题:\n\n{context}\n\n问题:{query}\n\n回答:"
    
    endpoint = f"http://{ollama_host}:{ollama_port}/api/generate"
    
    payload = {
        "model": model_name,
        "prompt": prompt,
        "stream": False
    }
    
    response = requests.post(endpoint, json=payload)
    
    if response.status_code == 200:
        return response.json()["response"]
    else:
        print(f"Error querying Ollama model: {response.status_code} {response.text}")
        return "无法生成回答,请稍后再试。"
 
系统集成
将各个组件集成到一个完整的FLASK应用中:
from flask import Flask, request, jsonify
import sqlite3
import requests
from bs4 import BeautifulSoup
from sentence_transformers import SentenceTransformer
import numpy as np
from milvus import Milvus
app = Flask(__name__)
# 配置参数
SQLITE_DB_PATH = "path/to/blog.db"
MILVUS_HOST = "localhost"
MILVUS_PORT = 19530
MILVUS_COLLECTION_NAME = "knowledge_base"
OLLAMA_HOST = "localhost"
OLLAMA_PORT = 11434
MODEL_NAME = "deepseek"
# 初始化Milvus连接
milvus = Milvus(host=MILVUS_HOST, port=MILVUS_PORT)
# 初始化嵌入模型
model = SentenceTransformer('all-MiniLM-L6-v2')
@app.route("/build-kb", methods=["POST"])
def build_knowledge_base():
    """构建知识库"""
    # 从SQLite中读取博客内容
    blogs = fetch_blog_content(SQLITE_DB_PATH)
    
    # 爬取URL内容
    urls = request.json.get("urls", [])
    url_content = crawl_url_content(urls)
    
    # 处理文档
    documents = process_documents(blogs + url_content)
    
    # 向量化并存储到Milvus
    embed_and_store_to_milvus(documents, 
                             MILVUS_HOST, 
                             MILVUS_PORT, 
                             MILVUS_COLLECTION_NAME)
    
    return jsonify({"status": "success", 
                    "message": "知识库构建完成"}), 200
@app.route("/query", methods=["POST"])
def query_knowledge_base():
    """查询知识库"""
    query = request.json.get("query", "")
    
    if not query:
        return jsonify({"status": "error", 
                        "message": "查询不能为空"}), 400
    
    # 向量化查询
    query_vector = vectorize_query(query, model)
    
    # 从Milvus中检索
    retrieved_docs = retrieve_from_milvus(query_vector,
                                        MILVUS_HOST,
                                        MILVUS_PORT,
                                        MILVUS_COLLECTION_NAME,
                                        top_k=5)
    
    if not retrieved_docs:
        return jsonify({"status": "error", 
                        "message": "未找到相关知识"}), 404
    
    # 组合上下文
    context = "\n".join([doc["content"] for doc in retrieved_docs])
    
    # 使用大模型生成回答
    answer = query_ollama_model(query, context, MODEL_NAME, OLLAMA_HOST, OLLAMA_PORT)
    
    return jsonify({
        "status": "success",
        "answer": answer,
        "sources": [doc["metadata"] for doc in retrieved_docs]
    }), 200
if __name__ == "__main__":
    app.run(debug=True)
 
系统部署方案
环境要求
- 硬件要求: 
  
- CPU:支持AVX2指令集
 - 内存:至少16GB RAM
 - 存储:足够存储知识库和模型参数
 - GPU:可选,用于加速大模型推理
 
 - 软件要求: 
  
- 操作系统:Linux/Windows/macOS(推荐Linux)
 - Python:3.8或更高版本
 - 依赖库:Milvus、OLLAMA、LangChain等
 
 
安装步骤
- 安装Milvus: 
  
- 下载并安装Milvus:
curl -L https://github.com/milvus-io/milvus/releases/download/v2.5.1/milvus-2.5.1-linux-amd64.tar.gz | tar xz - 启动Milvus:
bin/milvus_server start 
 - 下载并安装Milvus:
 - 安装OLLAMA: 
  
- 下载并安装OLLAMA:
curl -L https://github.com/ollama/ollama/releases/download/v1.3.0/ollama_1.3.0_Linux_x86_64.tar.gz | tar xz - 启动OLLAMA:
./ollama serve 
 - 下载并安装OLLAMA:
 - 安装Python依赖:
pip install flask requests beautifulsoup4 sentence-transformers numpy milvus-langchain-connector 
配置步骤
- 配置Milvus: 
  
- 确保Milvus服务已启动并监听在默认端口(19530)
 - 创建知识库集合(如果尚不存在)
 
 - 配置OLLAMA: 
  
- 确保OLLAMA服务已启动并监听在默认端口(11434)
 - 下载并加载所需模型:
ollama pull deepseek 
 - 配置FLASK应用: 
  
- 设置SQLite数据库路径
 - 设置Milvus连接参数
 - 设置OLLAMA连接参数
 - 设置默认模型名称
 
 
运行和测试
- 构建知识库: 
  
- 从SQLite数据库中读取博客内容
 - 爬取后台配置的URL链接内容
 - 处理文档并存储到Milvus
 
 - 进行问答测试: 
  
- 发送用户提问
 - 系统从Milvus中检索相关知识
 - 使用大模型生成回答
 
 
系统优化与扩展
性能优化
- 向量索引优化: 
  
- 调整IVF_FLAT索引的nlist参数
 - 考虑使用更高效的索引类型,如HNSW
 - 为不同类型的文档使用不同的索引策略
 
 - 模型优化: 
  
- 选择合适的嵌入模型(如all-MiniLM-L6-v2或更大模型)
 - 调整大模型的参数(如temperature、top_p等)
 - 考虑使用量化技术减少模型大小和推理时间
 
 - 文档处理优化: 
  
- 调整文本拆分的chunk_size和chunk_overlap
 - 使用更智能的文档重要性评估方法
 - 考虑使用多语言处理技术
 
 
功能扩展
- 支持更多知识源: 
  
- 添加PDF、PPT、DOCX等文件格式的支持
 - 集成企业内部知识管理系统
 - 支持实时数据源的自动更新
 
 - 增强用户体验: 
  
- 提供更友好的Web界面
 - 添加语音交互功能
 - 实现多轮对话上下文记忆
 
 - 监控和分析: 
  
- 添加API调用日志记录
 - 实现性能监控和分析
 - 提供知识库使用统计报告
 
 
结论
本报告详细介绍了如何基于FLASK开发一个使用本地OLLAMA大模型底座的知识库AI问答助手。该系统通过RAG技术整合了两部分知识语料:网站博客(存储在SQLite数据库中)和后台配置的知识博客URL链接内容(通过爬虫获取)。系统主要由前端Web应用、向量数据库Milvus、本地大模型OLLAMA和知识处理组件组成,使用LangChain作为框架集成各组件。
 通过本设计方案,企业可以构建一个高效、安全、可定制的AI问答系统,能够快速响应用户问题并提供基于本地知识库的支持。随着技术的不断发展,该系统可以通过多种方式进行优化和扩展,以满足更复杂的应用需求。
参考资料
[1] 一文读懂:大模型RAG(检索增强生成)含高级方法 - 知乎专栏. https://zhuanlan.zhihu.com/p/675509396.
 [2] 15-检索增强生成(RAG) 和向量数据库 - 飞书文档. https://docs.feishu.cn/article/wiki/OinzwWLXGi2cQUkiNYjceAVunVg.
 [5] 高性能向量数据库,为规模而构建 - Milvus. https://milvus.io/zh.
 [6] 向量数据库Milvus_功能优势 - 金山云. https://www.ksyun.com/nv/product/Milvus.html.
 [10] 使用Milvus 和Ollama 构建RAG. https://milvus.io/docs/zh/build_RAG_with_milvus_and_ollama.md.
 [15] Ollama本地部署大模型及应用原创 - CSDN博客. https://blog.csdn.net/qq_43548590/article/details/142546580.
 [49] 使用OpenAI、LangChain 和LlamaIndex 构建Knowledge. https://developer.aliyun.com/article/1394419.



















