构建智能问答系统:ollama-python + 知识库检索

构建智能问答系统:ollama-python + 知识库检索

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

引言:告别通用AI的知识局限

你是否遇到过这样的困境:当使用AI模型回答专业领域问题时,它要么给出泛泛而谈的答案,要么编造看似合理却错误的信息?传统大语言模型(LLM)受限于训练数据的时效性和领域覆盖范围,在企业内部文档、专业知识库等特定场景下表现不佳。本文将带你构建一个基于ollama-python的智能问答系统,通过本地知识库检索大语言模型的结合,实现精准、可靠的问答能力。

读完本文后,你将掌握:

  • 如何使用ollama-python进行文本嵌入(Embedding)生成
  • 构建轻量级向量知识库的完整流程
  • 实现"检索-增强生成(RAG)"的核心逻辑
  • 开发支持上下文记忆的智能问答系统
  • 系统性能优化与部署最佳实践

技术架构概览

智能问答系统的核心在于将用户查询与知识库内容精准匹配,并结合上下文生成回答。以下是基于ollama-python的系统架构:

mermaid

核心技术组件

组件功能ollama-python实现
文本嵌入将文本转换为向量表示client.embed(model="llama3.2", input=text)
向量存储存储和检索文本向量可结合FAISS/Chroma等库实现
检索引擎相似文档匹配余弦相似度计算
对话管理上下文状态维护消息列表(messages)管理
生成模块基于检索结果生成回答client.chat(model="qwen3", messages=prompt)

环境准备与依赖安装

系统要求

  • Python 3.8+
  • Ollama服务端(0.1.26+)
  • 至少4GB内存(推荐8GB+)

快速安装

# 克隆仓库
git clone https://gitcode.com/GitHub_Trending/ol/ollama-python
cd ollama-python

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

# 安装依赖
pip install -r requirements.txt

核心依赖解析

requirements.txt中关键依赖:

httpx==0.28.1          # HTTP客户端,ollama API通信
pydantic==2.10.6       # 数据验证与模型定义
anyio==4.8.0           # 异步支持

构建知识库检索系统

1. 文本嵌入生成

使用ollama-python的embed方法将文本转换为向量:

from ollama import Client

client = Client()

def generate_embedding(text: str) -> list[float]:
    """生成文本嵌入向量"""
    response = client.embed(
        model="llama3.2",  # 使用适合嵌入的模型
        input=text,
        options={"temperature": 0}  # 嵌入生成不需要随机性
    )
    return response["embeddings"][0]  # 返回向量列表

# 示例
text = "ollama-python是Ollama的Python客户端库,支持对话、生成和嵌入等功能。"
vector = generate_embedding(text)
print(f"嵌入向量维度: {len(vector)}")
print(f"向量前5个值: {vector[:5]}")

注意:选择专为嵌入设计的模型(如llama3.2bge等)可获得更好效果。不同模型生成的向量维度可能不同,需统一处理。

2. 实现向量存储与检索

由于ollama-python本身不提供向量存储功能,我们可以实现一个轻量级向量数据库:

import numpy as np
from typing import List, Dict, Tuple

class SimpleVectorDB:
    def __init__(self):
        self.vectors = []  # 存储向量
        self.texts = []    # 存储原始文本
        self.metadata = [] # 存储元数据

    def add(self, text: str, vector: List[float], meta: Dict = None):
        """添加文本及其向量到数据库"""
        self.texts.append(text)
        self.vectors.append(vector)
        self.metadata.append(meta or {})

    def search(self, query_vector: List[float], top_k: int = 3) -> List[Tuple[str, float, Dict]]:
        """检索与查询向量最相似的文本"""
        if not self.vectors:
            return []
            
        # 转换为numpy数组进行计算
        vectors_np = np.array(self.vectors)
        query_np = np.array(query_vector)
        
        # 计算余弦相似度
        similarities = np.dot(vectors_np, query_np) / (
            np.linalg.norm(vectors_np, axis=1) * np.linalg.norm(query_np)
        )
        
        # 获取Top K结果
        top_indices = similarities.argsort()[-top_k:][::-1]
        return [
            (self.texts[i], similarities[i], self.metadata[i])
            for i in top_indices
        ]

# 使用示例
db = SimpleVectorDB()

# 添加示例文档
documents = [
    "ollama-python支持同步和异步两种调用方式。",
    "使用client.chat()方法可以进行多轮对话。",
    "embed接口能将文本转换为向量用于相似性搜索。",
    "工具调用功能允许模型调用外部函数获取信息。",
    "stream参数设为True可实现流式响应输出。"
]

for doc in documents:
    vec = generate_embedding(doc)
    db.add(doc, vec, {"length": len(doc)})

# 查询相似文档
query = "如何进行多轮对话?"
query_vec = generate_embedding(query)
results = db.search(query_vec, top_k=2)

print(f"查询: {query}")
for i, (text, score, meta) in enumerate(results, 1):
    print(f"结果{i} (相似度: {score:.4f}): {text}")

3. 构建知识库索引

实际应用中,我们需要处理大量文档,可按以下流程构建索引:

import os
from pathlib import Path

def load_documents(docs_dir: str) -> List[str]:
    """从目录加载文档"""
    documents = []
    for ext in ['.txt', '.md', '.pdf']:  # 支持多种格式
        for file in Path(docs_dir).glob(f'**/*{ext}'):
            try:
                with open(file, 'r', encoding='utf-8') as f:
                    documents.append(f.read())
            except Exception as e:
                print(f"无法读取文件 {file}: {e}")
    return documents

def split_text(text: str, chunk_size: int = 500, chunk_overlap: int = 50) -> List[str]:
    """文本分块处理"""
    chunks = []
    for i in range(0, len(text), chunk_size - chunk_overlap):
        chunks.append(text[i:i+chunk_size])
    return chunks

# 完整索引构建流程
def build_knowledge_base(docs_dir: str, db: SimpleVectorDB):
    """构建知识库索引"""
    print(f"从{docs_dir}加载文档...")
    documents = load_documents(docs_dir)
    print(f"加载了{len(documents)}个文档")
    
    total_chunks = 0
    for doc in documents:
        chunks = split_text(doc)
        total_chunks += len(chunks)
        for chunk in chunks:
            vec = generate_embedding(chunk)
            db.add(chunk, vec, {"source": "local"})
    
    print(f"知识库构建完成,共{total_chunks}个文本块")

# 使用示例
# build_knowledge_base("./docs", db)

实现智能问答系统

1. 核心问答逻辑

结合检索与生成功能,实现智能问答:

def build_prompt(query: str, context_chunks: List[str]) -> List[Dict]:
    """构建带上下文的提示词"""
    system_prompt = """你是一个基于知识库的问答助手。使用以下提供的上下文信息来回答用户问题。
如果上下文信息不足以回答,直接说"根据提供的知识库,我无法回答该问题",不要编造信息。
回答要简洁明了,基于事实。"""
    
    context = "\n\n".join([f"[{i+1}] {chunk}" for i, chunk in enumerate(context_chunks)])
    
    return [
        {"role": "system", "content": system_prompt},
        {"role": "user", "content": f"上下文信息:\n{context}\n\n用户问题: {query}"}
    ]

def qa_system(query: str, db: SimpleVectorDB, model: str = "qwen3") -> str:
    """智能问答系统主函数"""
    # 1. 生成查询向量
    query_vec = generate_embedding(query)
    
    # 2. 检索相关上下文
    results = db.search(query_vec, top_k=3)
    context_chunks = [text for text, _, _ in results]
    
    # 3. 构建提示词
    prompt = build_prompt(query, context_chunks)
    
    # 4. 调用LLM生成回答
    response = client.chat(model=model, messages=prompt)
    return response.message.content

# 使用示例
query = "ollama-python如何实现多轮对话?"
answer = qa_system(query, db)
print(f"Q: {query}\nA: {answer}")

2. 对话历史管理

扩展系统以支持多轮对话:

class ChatManager:
    """对话管理器,维护对话历史"""
    def __init__(self, db: SimpleVectorDB, model: str = "qwen3", max_history: int = 10):
        self.db = db
        self.model = model
        self.max_history = max_history
        self.history = []  # 存储对话历史
    
    def add_message(self, role: str, content: str):
        """添加消息到历史"""
        self.history.append({"role": role, "content": content})
        # 限制历史长度
        if len(self.history) > self.max_history * 2:  # 每条对话包含user和assistant
            self.history = self.history[-self.max_history*2:]
    
    def query(self, user_query: str) -> str:
        """处理用户查询并返回回答"""
        # 1. 生成查询向量并检索
        query_vec = generate_embedding(user_query)
        results = self.db.search(query_vec, top_k=3)
        context_chunks = [text for text, _, _ in results]
        
        # 2. 构建带历史和上下文的提示词
        system_prompt = """你是一个基于知识库的问答助手。使用提供的上下文信息和对话历史来回答用户问题。
如果上下文信息不足以回答,直接说"根据提供的知识库,我无法回答该问题",不要编造信息。"""
        
        context = "\n\n".join([f"[{i+1}] {chunk}" for i, chunk in enumerate(context_chunks)])
        
        # 构建完整消息列表
        messages = [{"role": "system", "content": system_prompt}]
        # 添加对话历史
        messages.extend(self.history)
        # 添加当前查询(带上下文)
        messages.append({
            "role": "user", 
            "content": f"上下文信息:\n{context}\n\n用户问题: {user_query}"
        })
        
        # 3. 获取回答
        response = client.chat(model=self.model, messages=messages)
        answer = response.message.content
        
        # 4. 更新对话历史
        self.add_message("user", user_query)
        self.add_message("assistant", answer)
        
        return answer

# 使用示例
chatbot = ChatManager(db)
queries = [
    "ollama-python有哪些核心功能?",
    "如何使用embed方法生成文本向量?",
    "这些向量可以用来做什么?"
]

for q in queries:
    print(f"Q: {q}")
    print(f"A: {chatbot.query(q)}\n")

3. 工具调用增强(高级功能)

结合ollama-python的工具调用能力,实现更强大的问答系统:

from typing import List, Callable, Dict, Any

class ToolAugmentedChat(ChatManager):
    """支持工具调用的增强型对话管理器"""
    def __init__(self, db: SimpleVectorDB, tools: List[Callable], model: str = "qwen3"):
        super().__init__(db, model)
        self.tools = {tool.__name__: tool for tool in tools}
        self._register_tools()
    
    def _register_tools(self):
        """注册工具到系统提示词"""
        tool_descriptions = []
        for name, tool in self.tools.items():
            docstring = tool.__doc__ or "无描述"
            tool_descriptions.append(f"- {name}: {docstring}")
        
        tool_prompt = "\n你可以使用以下工具来获取额外信息(如果需要):\n" + "\n".join(tool_descriptions)
        tool_prompt += "\n如果需要调用工具,使用和<|FunctionCallEnd|>包裹,格式为:"
        tool_prompt += "\n<|FunctionCallBegin|>[{"name": "工具名", "parameters": {"参数名": "参数值"}}]<|FunctionCallEnd|>"
        
        # 更新系统提示词
        self.system_prompt = super().system_prompt + tool_prompt
    
    def _process_tool_calls(self, response_content: str) -> str:
        """处理工具调用并返回结果"""
        import json
        import re
        
        # 提取工具调用指令
        pattern = r"<\|FunctionCallBegin\|>(.*?)<\|FunctionCallEnd\|>"
        matches = re.findall(pattern, response_content, re.DOTALL)
        
        if not matches:
            return response_content
            
        # 执行工具调用
        tool_results = []
        for match in matches:
            try:
                call = json.loads(match)
                tool_name = call["name"]
                params = call["parameters"]
                
                if tool_name not in self.tools:
                    tool_results.append(f"错误:工具 {tool_name} 不存在")
                    continue
                    
                # 调用工具
                result = self.tools[tool_name](**params)
                tool_results.append(f"工具 {tool_name} 返回结果:{result}")
                
            except Exception as e:
                tool_results.append(f"工具调用错误:{str(e)}")
        
        # 将工具结果添加到对话历史
        self.add_message("system", "\n".join(tool_results))
        return "\n".join(tool_results)

# 示例工具
def current_time() -> str:
    """获取当前时间"""
    from datetime import datetime
    return datetime.now().strftime("%Y-%m-%d %H:%M:%S")

# 使用示例
# augmented_chat = ToolAugmentedChat(db, tools=[current_time])
# print(augmented_chat.query("现在是什么时间?"))

系统优化与最佳实践

1. 性能优化策略

优化方向实现方法效果
嵌入缓存使用字典缓存已生成的文本嵌入减少重复计算,提升检索速度
批量处理批量生成文本嵌入降低API调用次数,提升效率
模型选择嵌入用轻量级模型(如nomic-embed-text)减少资源占用,提升速度
分块优化根据文本长度动态调整分块大小平衡检索精度和效率

2. 部署建议

对于生产环境部署,建议:

# 使用异步API提高并发性能
import asyncio
from ollama import AsyncClient

async def async_embed(text: str):
    """异步嵌入生成"""
    async with AsyncClient() as client:
        response = await client.embed(model="llama3.2", input=text)
        return response["embeddings"][0]

# FastAPI部署示例
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel

app = FastAPI(title="Ollama知识库问答API")
db = SimpleVectorDB()  # 实际部署时应使用更高效的向量数据库
chat_manager = ChatManager(db)

class QueryRequest(BaseModel):
    question: str
    session_id: str = "default"

class QueryResponse(BaseModel):
    answer: str
    sources: List[str] = []

@app.post("/query", response_model=QueryResponse)
async def api_query(request: QueryRequest):
    try:
        answer = chat_manager.query(request.question)
        return {"answer": answer}
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

# 启动命令: uvicorn main:app --host 0.0.0.0 --port 8000

3. 常见问题解决

问题解决方案
回答不准确增加top_k检索数量,优化分块策略
响应速度慢使用缓存,优化模型选择
内存占用高减少历史对话长度,使用更轻量模型
知识库更新实现增量索引更新机制

总结与未来展望

本文详细介绍了如何使用ollama-python构建结合知识库检索的智能问答系统,涵盖从文本嵌入生成、向量存储构建到问答逻辑实现的完整流程。通过这种架构,我们能够克服传统LLM的知识局限,实现基于私有知识库的精准问答。

未来可以进一步探索:

  • 多模态知识库(支持图片、表格等)
  • 知识图谱增强的检索能力
  • 模型微调与领域适应
  • 用户反馈机制与系统迭代优化

通过不断优化和扩展,这个系统可以应用于企业内部文档问答、客户支持、智能教育等多个领域,为用户提供更精准、可靠的AI助手服务。

附录:完整代码示例

可在项目的examples目录中找到更多示例代码:

  • embed.py: 文本嵌入生成示例
  • chat-with-history.py: 对话历史管理示例
  • multi-tool.py: 工具调用功能示例
  • structured-outputs.py: 结构化输出示例
# 完整的智能问答系统示例代码(整合版)
from ollama import Client
import numpy as np
from typing import List, Dict, Tuple, Callable

# 初始化客户端
client = Client()

# 向量数据库实现
class SimpleVectorDB:
    def __init__(self):
        self.vectors = []
        self.texts = []
        self.metadata = []
    
    def add(self, text: str, vector: List[float], meta: Dict = None):
        self.texts.append(text)
        self.vectors.append(vector)
        self.metadata.append(meta or {})
    
    def search(self, query_vector: List[float], top_k: int = 3) -> List[Tuple[str, float, Dict]]:
        if not self.vectors:
            return []
            
        vectors_np = np.array(self.vectors)
        query_np = np.array(query_vector)
        
        similarities = np.dot(vectors_np, query_np) / (
            np.linalg.norm(vectors_np, axis=1) * np.linalg.norm(query_np)
        )
        
        top_indices = similarities.argsort()[-top_k:][::-1]
        return [
            (self.texts[i], similarities[i], self.metadata[i])
            for i in top_indices
        ]

# 对话管理器
class ChatManager:
    def __init__(self, db: SimpleVectorDB, model: str = "qwen3", max_history: int = 10):
        self.db = db
        self.model = model
        self.max_history = max_history
        self.history = []
    
    def add_message(self, role: str, content: str):
        self.history.append({"role": role, "content": content})
        if len(self.history) > self.max_history * 2:
            self.history = self.history[-self.max_history*2:]
    
    def query(self, user_query: str) -> str:
        # 生成查询向量并检索
        query_vec = client.embed(model="llama3.2", input=user_query)["embeddings"][0]
        results = self.db.search(query_vec, top_k=3)
        context_chunks = [text for text, _, _ in results]
        
        # 构建提示词
        system_prompt = """你是一个基于知识库的问答助手。使用提供的上下文信息回答用户问题。
如果上下文信息不足以回答,直接说"根据提供的知识库,我无法回答该问题",不要编造信息。"""
        
        context = "\n\n".join([f"[{i+1}] {chunk}" for i, chunk in enumerate(context_chunks)])
        
        messages = [{"role": "system", "content": system_prompt}]
        messages.extend(self.history)
        messages.append({
            "role": "user", 
            "content": f"上下文信息:\n{context}\n\n用户问题: {user_query}"
        })
        
        # 获取回答
        response = client.chat(model=self.model, messages=messages)
        answer = response.message.content
        
        # 更新对话历史
        self.add_message("user", user_query)
        self.add_message("assistant", answer)
        
        return answer

# 使用示例
if __name__ == "__main__":
    # 初始化知识库
    db = SimpleVectorDB()
    
    # 添加示例文档
    sample_docs = [
        "ollama-python是Ollama的Python客户端,支持同步和异步调用。",
        "主要功能包括对话(chat)、生成(generate)、嵌入(embed)等。",
        "使用client.chat()方法可以进行多轮对话,支持上下文管理。",
        "嵌入功能通过client.embed()实现,可将文本转换为向量用于检索。",
        "工具调用功能允许模型根据需要调用外部函数获取信息。"
    ]
    
    for doc in sample_docs:
        vec = client.embed(model="llama3.2", input=doc)["embeddings"][0]
        db.add(doc, vec)
    
    # 启动对话
    chatbot = ChatManager(db)
    print("知识库问答系统已启动,输入'退出'结束对话")
    
    while True:
        user_input = input("你: ")
        if user_input.lower() in ["退出", "q", "quit"]:
            print("再见!")
            break
        response = chatbot.query(user_input)
        print(f"AI: {response}")

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

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值