Ollama模型调用实战:从向量检索到对话生成

AI 时代程序员必备技能

Codex、Claude Code、Cursor、Hermes Agent、OpenClaw等工程化实战专栏 ,讲透 AI 如何接管脏活累活

1. 环境准备:为什么选择Ollama作为你的AI模型管家

如果你和我一样,曾经为了跑一个开源大模型,折腾过Python环境、CUDA版本、显存分配这些让人头疼的问题,那你一定会喜欢上Ollama。简单来说,Ollama就像是一个专门为AI模型打造的“应用商店”和“运行环境”二合一的工具。它把那些复杂的安装、配置、依赖管理全部打包好了,你只需要一句简单的命令,就能把各种主流的大语言模型、嵌入模型下载到本地并运行起来,整个过程丝滑得不像是在玩AI。

我第一次接触Ollama是因为想快速测试几个不同的嵌入模型。之前用传统的Python库,光是配环境就花了大半天,各种版本冲突让人崩溃。而用Ollama,我只需要在终端里输入 ollama pull bge-m3,然后泡杯咖啡的功夫,模型就下好了,随时可以调用。这种体验,对于想快速上手做原型验证、或者不想在环境问题上浪费时间的开发者来说,简直是福音。它特别适合这几类人:想学习大模型应用开发的学生、需要快速搭建AI能力验证Demo的创业者、以及像我这样讨厌环境配置、只想专注在业务逻辑上的开发者。

安装Ollama的过程也极其简单。根据你的操作系统,去官网下载对应的安装包,基本上就是一路“下一步”。安装完成后,打开终端,输入 ollama --version 确认安装成功。这里有个小坑我踩过:在某些网络环境下,直接拉取模型可能会很慢或者失败。我的经验是,可以尝试使用镜像源,或者检查一下你的网络代理设置(如果你的开发环境需要特定的网络配置)。确保你的机器有足够的磁盘空间,因为一个模型动辄几个G,像 llama3.1:8b 这样的模型大概需要4-5GB的空间。

2. 模型管理:查看与拉取你的AI武器库

安装好Ollama之后,第一件事就是看看它这个“商店”里都有什么“货”。这里完全不需要图形界面,所有操作在命令行里完成,效率非常高。

2.1 查看本地已安装的模型

打开你的终端(Windows用CMD或PowerShell,Mac/Linux用Terminal),输入下面这个命令:

ollama list

回车之后,你会看到一个表格,列出了你本地已经下载并可以随时调用的所有模型。如果这是你第一次使用,这个列表可能是空的,这很正常。这个命令的输出会包含模型名称、模型ID、大小、修改日期等信息,一目了然。我习惯在开始一个新项目前先跑一下这个命令,看看手头有哪些模型可以直接用,避免重复下载。

2.2 探索与拉取新模型

本地模型列表是空的怎么办?别急,Ollama提供了一个官方模型库,里面集成了几十个热门的开源模型。虽然Ollama本身没有像 ollama search 这样的直接搜索命令,但你可以直接访问其官方GitHub仓库的模型列表页面,那里有所有可用模型的详细清单,包括 llama3.1mistralqwen2.5deepseek-r1 以及我们后面会用到的嵌入模型 bge-m3nomic-embed-text

当你决定要使用某个模型时,拉取命令非常简单。比如,我想拉取一个中等尺寸的对话模型和一个专门做文本嵌入的模型,我会依次执行:

ollama pull deepseek-r1:1.5b
ollama pull bge-m3

这里需要注意模型名称的格式。deepseek-r1:1.5b 中的 :1.5b 是指定该模型的1.5B参数版本,这是一个比较轻量的版本,适合在普通电脑上快速测试。而 bge-m3 则是一个多功能嵌入模型,它没有指定标签,默认会拉取最新或最推荐的版本。拉取过程中,终端会显示进度条。根据你的网速和模型大小,这个过程可能需要几分钟到几十分钟。我建议在网速好的时候一次性把可能用到的模型都拉下来。

拉取完成后,再次运行 ollama list,你就能看到它们已经安静地躺在你的本地仓库里了。这时候,这些模型文件通常存储在用户目录下的 .ollama/models 文件夹中(路径因操作系统而异)。管理它们也很方便,如果你觉得某个模型暂时用不上了,想节省磁盘空间,可以使用 ollama rm <model-name> 来删除它,比如 ollama rm deepseek-r1:1.5b。删除操作很干净,但记得确认一下,别删错了。

3. 调用嵌入模型:让机器理解文本的“含义”

现在我们的“武器库”里有了模型,是时候让它们干活了。我们首先从嵌入模型开始。什么是嵌入?你可以把它想象成一种“翻译”,把人类能读懂的文本(比如一句话、一段文章),翻译成机器能理解的、固定长度的一串数字(也就是向量)。这个向量神奇的地方在于,语义相近的文本,它们的向量在数学空间里的距离也会很近。这就为我们做语义搜索、文本分类、聚类打下了基础。

3.1 通过API调用bge-m3模型

Ollama为所有模型提供了统一的HTTP API接口,默认运行在 http://localhost:11434。这意味着你可以用任何能发送HTTP请求的工具或编程语言来调用模型,比如Python的requests库、JavaScript的fetch,甚至用curl命令在终端里直接测试。

下面我用一个完整的Python示例,来演示如何调用我们刚才拉取的 bge-m3 模型,为一段文本生成嵌入向量。

import requests
import json

def get_embedding(text, model="bge-m3"):
    """
    调用本地Ollama服务的嵌入API,为输入文本生成向量表示。
    
    参数:
        text (str): 需要向量化的文本。
        model (str): 要使用的嵌入模型名称,默认为'bge-m3'。
    
    返回:
        list: 文本对应的嵌入向量(浮点数列表)。如果出错,返回None。
    """
    # Ollama嵌入API的端点
    url = "http://localhost:11434/api/embeddings"
    
    # 构造请求头和数据体
    headers = {"Content-Type": "application/json"}
    # 对于bge-m3模型,按照其最佳实践,在文本前加上特定的指令前缀
    data = {
        "model": model,
        "prompt": f"为这个句子生成检索表示:{text}"
    }
    
    try:
        # 发送POST请求
        response = requests.post(url, headers=headers, json=data, timeout=30)
        # 检查HTTP状态码,非200则抛出异常
        response.raise_for_status()
        
        # 解析返回的JSON,提取嵌入向量
        result = response.json()
        embedding = result.get('embedding')
        
        if embedding:
            print(f"文本 '{text[:50]}...' 的嵌入向量生成成功,维度:{len(embedding)}")
            return embedding
        else:
            print("API响应中未找到'embedding'字段。")
            return None
            
    except requests.exceptions.ConnectionError:
        print("错误:无法连接到Ollama服务。请确保Ollama正在运行(终端输入 'ollama serve')。")
    except requests.exceptions.Timeout:
        print("错误:请求超时。可能是模型加载较慢或网络问题。")
    except requests.exceptions.HTTPError as e:
        print(f"HTTP错误:{e}")
        # 可以打印更多响应信息帮助调试
        if response.text:
            print(f"响应内容:{response.text[:200]}")
    except json.JSONDecodeError:
        print("错误:无法解析API返回的JSON数据。")
    except Exception as e:
        print(f"发生未知错误:{e}")
    
    return None

# 实际调用示例
if __name__ == "__main__":
    # 首先,确保你的Ollama服务已经启动。可以在终端运行 `ollama serve` 来启动服务。
    query_text = "人工智能的未来发展趋势是什么?"
    embedding_vector = get_embedding(query_text)
    
    if embedding_vector:
        # 打印向量的前10个维度和总长度,感受一下
        print(f"向量预览(前10维): {embedding_vector[:10]}")
        print(f"向量总维度: {len(embedding_vector)}")

把这段代码保存为一个 .py 文件并运行。如果一切顺利,你会看到终端输出生成的向量信息。bge-m3 生成的向量默认是1024维,也就是一个包含1024个浮点数的列表。这个列表本身看起来是一堆无意义的数字,但它却精准地编码了输入文本的语义信息。我第一次跑通这个的时候,感觉特别神奇,一句人话就这么变成了机器能处理的数学对象。

3.2 实战:用向量相似度实现语义搜索

光生成向量还不够,我们要用它来解决实际问题。最常见的场景就是语义搜索:给定一个问题(查询句),从一堆文档中找出和它意思最接近的那一个。传统的搜索基于关键词匹配,而语义搜索基于意思匹配。比如,你搜索“苹果公司最新产品”,传统搜索可能找不到标题为“iPhone 16发布”的文档,但语义搜索就能找到,因为它们意思相近。

下面我们来构建一个微型的智能问答系统雏形。假设我们有一个关于几个人物关系的简单知识库,用户问一个问题,我们要从知识库中找到最相关的陈述。

import numpy as np

def cosine_similarity(vec_a, vec_b):
    """
    计算两个向量的余弦相似度。
    余弦相似度范围在[-1, 1]之间,1表示完全相同,0表示无关,-1表示完全相反。
    """
    # 转换为numpy数组便于计算
    a = np.array(vec_a)
    b = np.array(vec_b)
    
    # 点积
    dot_product = np.dot(a, b)
    # 模长
    norm_a = np.linalg.norm(a)
    norm_b = np.linalg.norm(b)
    
    # 避免除以零
    if norm_a == 0 or norm_b == 0:
        return 0.0
    
    similarity = dot_product / (norm_a * norm_b)
    return similarity

def semantic_search(query, documents, model="bge-m3"):
    """
    执行语义搜索:在文档集合中找到与查询最相似的文档。
    
    参数:
        query (str): 查询文本。
        documents (list of str): 文档文本列表。
        model (str): 嵌入模型。
    
    返回:
        list: 排序后的结果列表,每个元素是(文档索引, 相似度得分, 文档内容)的元组。
    """
    print(f"开始语义搜索,查询:'{query}'")
    print(f"文档库中共有 {len(documents)} 个文档。")
    
    # 1. 为查询文本生成嵌入
    print("正在为查询生成嵌入向量...")
    query_embedding = get_embedding(query, model)
    if not query_embedding:
        print("查询向量生成失败,终止搜索。")
        return []
    
    # 2. 为所有文档生成嵌入(在实际应用中,文档向量可以预先计算并存储,这里为了演示实时计算)
    print("正在为文档生成嵌入向量...")
    doc_embeddings = []
    valid_docs = []
    
    for i, doc in enumerate(documents):
        emb = get_embedding(doc, model)
        if emb:
            doc_embeddings.append(emb)
            valid_docs.append((i, doc))
        else:
            print(f"警告:文档 {i} 向量化失败,已跳过。")
    
    if not doc_embeddings:
        print("错误:所有文档向量化均失败。")
        return []
    
    # 3. 计算查询与每个文档的相似度
    print("正在计算相似度...")
    results = []
    for (doc_idx, doc_text), doc_emb in zip(valid_docs, doc_embeddings):
        score = cosine_similarity(query_embedding, doc_emb)
        results.append((doc_idx, score, doc_text))
    
    # 4. 按相似度得分从高到低排序
    results.sort(key=lambda x: x[1], reverse=True)
    
    print("搜索完成!")
    return results

# 模拟一个简单的知识库
knowledge_base = [
    "小明的父亲是小刚。",
    "小刚是一名软件工程师。",
    "小明的母亲是李华。",
    "李华是一名教师。",
    "小明家养了一只叫旺财的狗。",
    "小刚和李华是在大学里认识的。"
]

# 用户提出的问题
user_question = "小明的爸爸是做什么工作的?"

# 执行搜索
search_results = semantic_search(user_question, knowledge_base)

# 输出结果
print("\n=== 语义搜索结果 ===")
for rank, (idx, score, doc) in enumerate(search_results, 1):
    print(f"{rank}. [相似度: {score:.4f}] 文档{idx}: {doc}")

运行这段代码,你会看到虽然用户问的是“爸爸是做什么工作的”,而知识库里最相关的句子是“小刚是一名软件工程师”。因为“小明的父亲是小刚”和“小明的爸爸”是语义等价的,所以我们的系统通过向量相似度,成功地将“小刚”和“软件工程师”关联了起来,间接回答了问题。这就是语义检索的威力——它理解含义,而不只是匹配字词。

在实际项目中,你不会每次都实时计算所有文档的向量,那样太慢了。通常的做法是:预先将知识库的所有文档通过嵌入模型向量化,然后把向量存入专门的向量数据库(比如Chroma、Milvus、Qdrant)。当用户查询时,只计算查询句的向量,然后让向量数据库快速进行相似度检索,返回最相关的几个文档。Ollama负责提供高质量、稳定的嵌入向量生成服务,是整个流程的基石。

4. 调用大语言模型:从检索到生成完整答案

好了,我们现在已经能从知识库里找到相关的文档片段了。但直接把这些片段扔给用户,体验还不够好。用户问“小明的爸爸是做什么工作的?”,我们返回“小刚是一名软件工程师。”,虽然正确,但不够自然。更好的方式是,让一个大语言模型(LLM)来整合检索到的信息,生成一个完整、通顺的答案。这就是目前流行的“检索增强生成”(RAG)系统的基本思路。

4.1 使用Ollama Python库进行对话

Ollama不仅提供了HTTP API,还有一个官方的Python库,用起来更简洁。首先确保安装了它:pip install ollama。然后,我们就可以像下面这样轻松地调用DeepSeek模型进行对话了。

import ollama

def simple_chat_with_llm():
    """
    一个最简单的与大语言模型对话的例子。
    """
    try:
        # 调用ollama.chat函数
        # model参数指定我们之前拉取的 deepseek-r1:1.5b 模型
        # messages参数是一个消息列表,通常以用户消息开始
        response = ollama.chat(
            model='deepseek-r1:1.5b',
            messages=[
                {
                    'role': 'user',
                    'content': '请用一句话介绍一下你自己。'
                }
            ]
        )
        
        # 从响应中提取模型生成的内容
        answer = response['message']['content']
        print("模型回复:", answer)
        
        # 响应中还包含其他有用信息,比如token使用量
        print(f"本次对话消耗token数: {response.get('prompt_eval_count', 'N/A')} (提示) + {response.get('eval_count', 'N/A')} (生成)")
        
        return answer
        
    except Exception as e:
        print(f"调用大模型时发生错误: {e}")
        return None

# 运行对话
if __name__ == "__main__":
    simple_chat_with_llm()

运行这个脚本,你应该会收到DeepSeek模型的一段自我介绍。通过这个简单的交互,你就完成了一次本地大模型调用。整个过程没有复杂的服务器部署,没有令人头疼的GPU内存管理,Ollama都帮你处理好了。你可以尝试修改 content 里的问题,看看模型如何回答。

4.2 构建完整的RAG流程:智能问答系统

现在,让我们把前面学的两招——向量检索对话生成——组合起来,打造一个真正的简易版智能问答系统。这个系统的流程是:用户提问 -> 在知识库中语义检索相关片段 -> 将问题和检索到的片段一起交给大模型 -> 大模型生成最终答案。

import ollama
from typing import List, Tuple

class SimpleRAGSystem:
    """
    一个简单的检索增强生成(RAG)系统。
    结合了嵌入模型检索和大语言模型生成。
    """
    def __init__(self, embedding_model="bge-m3", llm_model="deepseek-r1:1.5b"):
        self.embedding_model = embedding_model
        self.llm_model = llm_model
        # 这里模拟一个已向量化的知识库。实际应用中,这部分数据应来自向量数据库。
        # 格式: [(文档文本, 预计算好的嵌入向量), ...]
        self.knowledge_base = []  # 本示例中我们先留空,在后续方法中模拟
        
    def retrieve_relevant_docs(self, query: str, top_k: int = 3) -> List[str]:
        """
        模拟检索过程:根据查询,返回最相关的top_k个文档。
        在实际系统中,这里会调用向量数据库进行近似最近邻搜索。
        """
        # 为了演示,我们假设一个简单的知识库和检索结果。
        # 真实情况下,你需要用3.2节的方法计算query的向量,然后查询向量数据库。
        simulated_knowledge = [
            "小刚,男,35岁,职业是软件工程师,就职于某互联网公司。",
            "李华,女,33岁,职业是小学语文教师,就职于第一实验小学。",
            "小明,男,10岁,是小刚和李华的儿子,就读于实验小学四年级。",
            "小刚和李华于2010年在清华大学读书时相识相恋。",
            "小明家养了一只金毛犬,名字叫旺财,今年3岁。",
            "小刚擅长Python和Go语言,主要工作是后端系统开发。",
            "李华曾获得市级优秀教师称号,主要教授语文和书法课。"
        ]
        
        # 模拟一个基于关键词的简单检索(真实场景应为向量语义检索)
        # 这里我们根据查询中的关键词匹配来模拟语义检索的结果
        query_lower = query.lower()
        relevant_docs = []
        
        if "爸爸" in query_lower or "父亲" in query_lower or "小刚" in query_lower:
            relevant_docs.append(simulated_knowledge[0])  # 小刚的职业信息
            relevant_docs.append(simulated_knowledge[5])  # 小刚的技能信息
        if "妈妈" in query_lower or "母亲" in query_lower or "李华" in query_lower:
            relevant_docs.append(simulated_knowledge[1])  # 李华的职业信息
            relevant_docs.append(simulated_knowledge[6])  # 李华的获奖信息
        if "小明" in query_lower:
            relevant_docs.append(simulated_knowledge[2])  # 小明的信息
        if "狗" in query_lower or "宠物" in query_lower:
            relevant_docs.append(simulated_knowledge[4])  # 宠物的信息
            
        # 去重并返回前top_k个
        unique_docs = list(dict.fromkeys(relevant_docs))
        return unique_docs[:top_k]
    
    def generate_answer_with_context(self, query: str, context_docs: List[str]) -> str:
        """
        基于检索到的上下文文档,让大语言模型生成答案。
        """
        # 构建给模型的提示词(Prompt)。提示词工程是RAG效果的关键。
        context_str = "\n".join([f"- {doc}" for doc in context_docs])
        
        system_prompt = """你是一个乐于助人的AI助手。请严格根据提供的信息来回答问题。
如果提供的信息足以回答问题,请基于信息给出准确、简洁的答案。
如果提供的信息不足以完全回答问题,你可以基于常识进行合理推断,但必须明确指出哪些部分是推断的。
如果信息完全与问题无关,请直接说“根据已有信息无法回答这个问题”。"""
        
        user_prompt = f"""请根据以下信息回答问题。

相关信息:
{context_str}

问题:{query}

请直接给出答案:"""
        
        messages = [
            {'role': 'system', 'content': system_prompt},
            {'role': 'user', 'content': user_prompt}
        ]
        
        try:
            response = ollama.chat(model=self.llm_model, messages=messages)
            answer = response['message']['content'].strip()
            return answer
        except Exception as e:
            return f"生成答案时出错:{e}"
    
    def answer_question(self, query: str) -> Tuple[str, List[str]]:
        """
        回答问题的完整流程。
        返回:(最终答案, 使用的参考文档列表)
        """
        print(f"\n用户提问:{query}")
        print("="*50)
        
        # 步骤1:检索相关文档
        print("[步骤1] 正在从知识库检索相关信息...")
        relevant_docs = self.retrieve_relevant_docs(query, top_k=2)
        if not relevant_docs:
            return "抱歉,知识库中没有找到与您问题相关的信息。", []
        print(f"检索到 {len(relevant_docs)} 条相关信息。")
        for i, doc in enumerate(relevant_docs, 1):
            print(f"  参考{i}: {doc}")
        
        # 步骤2:基于检索结果生成答案
        print("[步骤2] 正在基于检索信息生成回答...")
        final_answer = self.generate_answer_with_context(query, relevant_docs)
        
        print("[步骤3] 生成完成!")
        print("="*50)
        return final_answer, relevant_docs

# 使用我们的RAG系统
if __name__ == "__main__":
    rag_bot = SimpleRAGSystem()
    
    # 测试几个问题
    test_questions = [
        "小明的爸爸是做什么工作的?",
        "李华在哪里工作?",
        "小明家养了什么宠物?",
        "小刚和李华是怎么认识的?"
    ]
    
    for q in test_questions:
        answer, refs = rag_bot.answer_question(q)
        print(f"问:{q}")
        print(f"答:{answer}")
        print("-" * 60)

运行这个完整的示例,你会看到系统的工作流程:对于每个问题,它先“检索”出相关的知识片段(这里用模拟逻辑代替了真实的向量检索),然后把这些片段和问题一起组织成一段清晰的提示词,发送给DeepSeek模型。模型会阅读这些上下文,并生成一个友好、准确的答案。比如对于“小明的爸爸是做什么工作的?”,模型结合检索到的“小刚是软件工程师”这条信息,可能会生成“小明的爸爸小刚是一名软件工程师,就职于某互联网公司,擅长Python和Go语言开发。”这样的答案,比直接返回原始文档要友好得多。

5. 进阶技巧与避坑指南

通过前面的步骤,你已经能用Ollama搭建一个可运行的RAG原型了。但在实际项目中,想要系统稳定、高效、好用,还需要注意下面这些我踩过坑才总结出来的细节。

5.1 性能优化与参数调整

直接调用默认配置的模型有时可能响应较慢,或者生成的内容不尽如人意。Ollama的API和库提供了一些参数可以调整。

控制生成内容:ollama.chat() 函数中,你可以通过 options 参数来控制生成过程,这对于优化回答质量和速度非常关键。

response = ollama.chat(
    model='deepseek-r1:1.5b',
    messages=[{'role': 'user', 'content': '写一首关于春天的五言绝句。'}],
    options={
        'temperature': 0.7,      # 控制随机性:0.0最确定,1.0最随机。创作类可调高,事实类调低。
        'top_p': 0.9,            # 核采样参数,影响词的选择范围。
        'num_predict': 150,      # 生成的最大token数,防止回答过长。
        'seed': 42,              # 随机种子,设置后可使生成结果可复现。
        'stream': False          # 是否流式输出,True时会边生成边返回,适合长文本。
    }
)
  • temperature:这是我调整最多的参数。如果你需要模型进行创意写作、头脑风暴,可以调到0.8甚至更高。但如果你在做事实性问答、代码生成,希望答案稳定准确,最好调到0.1到0.3之间。我做过测试,同一个问题,temperature=0.1时,多次问答结果几乎一致;temperature=0.9时,每次答案都天马行空。
  • num_predict:务必根据场景设置。如果是简短问答,设50-100就够了;如果是写文章、总结长文档,可能需要512或更多。不设限制的话,模型有时会一直说下去。

嵌入模型批处理: 如果你需要一次性向量化大量文本(比如初始化知识库),逐条调用API效率极低。虽然Ollama的嵌入API本身不支持批量请求,但你可以通过多线程或异步编程来显著提升速度。

import concurrent.futures
import threading

def batch_get_embeddings(texts, model="bge-m3", max_workers=5):
    """
    使用线程池批量获取文本嵌入,大幅提升处理速度。
    """
    embeddings = []
    errors = []
    # 创建一个线程安全的列表来存储结果,因为多个线程会同时写入
    embeddings_dict = {}
    lock = threading.Lock()
    
    def _get_one_embedding(idx, text):
        try:
            emb = get_embedding(text, model)
            with lock:
                embeddings_dict[idx] = emb
        except Exception as e:
            with lock:
                errors.append((idx, text, str(e)))
    
    with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor:
        # 提交所有任务
        future_to_idx = {executor.submit(_get_one_embedding, idx, text): idx for idx, text in enumerate(texts)}
        
        # 等待所有任务完成(可选进度显示)
        for future in concurrent.futures.as_completed(future_to_idx):
            idx = future_to_idx[future]
            # 这里可以添加进度条逻辑
    
    # 按照原始文本顺序整理结果
    for i in range(len(texts)):
        if i in embeddings_dict:
            embeddings.append(embeddings_dict[i])
        else:
            embeddings.append(None)  # 标记失败项
    
    return embeddings, errors

# 使用示例
documents = ["文本1", "文本2", "文本3", ...]  # 你的文档列表
embeddings, errors = batch_get_embeddings(documents, max_workers=3)
print(f"成功处理 {len([e for e in embeddings if e is not None])} 条,失败 {len(errors)} 条。")

使用多线程后,处理上百条文本的时间可以从几分钟缩短到几十秒。但要注意 max_workers 不要设置得太大,否则可能把本地Ollama服务打垮,一般设置为3-5个线程比较稳妥。

5.2 错误处理与服务稳定性

在实际开发中,网络波动、服务重启、模型加载失败都是家常便饭。健壮的程序必须考虑这些情况。

服务健康检查: 在调用任何模型之前,先检查Ollama服务是否可用。

import requests
import time

def check_ollama_health(retries=3, delay=2):
    """
    检查Ollama服务是否健康运行。
    """
    health_url = "http://localhost:11434/api/tags"  # 一个轻量级的API端点
    for attempt in range(retries):
        try:
            resp = requests.get(health_url, timeout=5)
            if resp.status_code == 200:
                print("Ollama服务运行正常。")
                return True
        except requests.exceptions.ConnectionError:
            print(f"第 {attempt+1} 次连接Ollama服务失败。")
        except requests.exceptions.Timeout:
            print(f"第 {attempt+1} 次请求超时。")
        
        if attempt < retries - 1:
            print(f"{delay}秒后重试...")
            time.sleep(delay)
    
    print(f"经过{retries}次重试后仍无法连接Ollama服务。")
    print("请检查:")
    print("1. 是否已安装并启动了Ollama?(终端运行 'ollama serve')")
    print("2. 服务是否运行在默认端口11434?")
    print("3. 防火墙是否阻止了连接?")
    return False

# 在程序开始处调用
if not check_ollama_health():
    # 可以考虑自动启动服务的逻辑,或者直接退出
    exit(1)

调用重试与降级策略: 对于重要的生成或检索请求,实现重试机制。

def robust_model_call(messages, model='deepseek-r1:1.5b', max_retries=2):
    """
    带重试机制的大模型调用。
    """
    for retry in range(max_retries + 1):  # 尝试 max_retries 次重试
        try:
            response = ollama.chat(model=model, messages=messages)
            return response['message']['content']
        except Exception as e:
            error_msg = str(e)
            print(f"模型调用失败 (尝试 {retry+1}/{max_retries+1}): {error_msg}")
            
            # 如果是连接错误,可能是服务暂时不可用,等待后重试
            if "connection" in error_msg.lower() or "timeout" in error_msg.lower():
                if retry < max_retries:
                    wait_time = (retry + 1) * 3  # 退避等待:3秒,6秒...
                    print(f"等待{wait_time}秒后重试...")
                    time.sleep(wait_time)
                continue
            else:
                # 其他错误(如模型不存在、参数错误)重试无意义,直接抛出
                raise e
    
    # 所有重试都失败
    raise Exception(f"模型调用失败,已重试{max_retries}次。")

# 使用降级策略:如果主要模型失败,尝试备用模型
def get_answer_with_fallback(question, primary_model='deepseek-r1:1.5b', fallback_model='llama3.1:8b'):
    messages = [{'role': 'user', 'content': question}]
    try:
        return robust_model_call(messages, model=primary_model)
    except Exception as e1:
        print(f"主模型({primary_model})调用失败,尝试备用模型({fallback_model})...")
        try:
            return robust_model_call(messages, model=fallback_model)
        except Exception as e2:
            return f"无法获取答案。错误:主模型-{e1},备用模型-{e2}"

5.3 模型选择与组合策略

Ollama的强大之处在于可以轻松切换和组合不同的模型。不同的任务适合不同的模型。

  • 对话与通用问答llama3.1:8bqwen2.5:7bdeepseek-r1:1.5b(轻量)都是不错的选择。llama3.1在逻辑推理和指令遵循上表现均衡;qwen2.5对中文支持非常好;deepseek-r1系列则比较轻快。
  • 代码生成codellama:7bdeepseek-coder:6.7b 是专门为代码调优的模型。如果你主要做编程助手,可以优先考虑它们。
  • 嵌入(向量化)bge-m3 是通用性很强的选择,支持多语言,在检索任务上表现优异。nomic-embed-text 则更轻量,速度可能更快。你可以根据你的语料(主要是中文还是英文)和精度要求来选择。
  • 特定领域:如果有医学、法律、金融等垂直领域需求,可以寻找并在Ollama中加载对应的专业模型。

一个实用的策略是“小模型检索 + 大模型生成”。用 bge-m3nomic-embed-text 这种专门的嵌入模型处理海量文本的向量化,因为它们的唯一任务就是生成高质量的向量,效率高、效果好。然后用一个参数规模稍大的对话模型(如 llama3.1:8b)来基于检索结果生成最终答案,因为它更擅长理解和组织语言。这种组合既能保证检索质量,又能获得不错的生成效果,而且对硬件资源的要求相对友好。

最后,记得定期去Ollama的官方社区或GitHub页面看看,经常会有新的模型发布,或者现有模型更新了更好的版本。管理本地模型就像管理你的工具箱,用 ollama pull 添加新工具,用 ollama listollama rm 整理工具箱,保持一个干净、高效的开发环境。

AI 时代程序员必备技能

Codex、Claude Code、Cursor、Hermes Agent、OpenClaw等工程化实战专栏 ,讲透 AI 如何接管脏活累活

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值