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.1、mistral、qwen2.5、deepseek-r1 以及我们后面会用到的嵌入模型 bge-m3 和 nomic-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:8b、qwen2.5:7b、deepseek-r1:1.5b(轻量)都是不错的选择。llama3.1在逻辑推理和指令遵循上表现均衡;qwen2.5对中文支持非常好;deepseek-r1系列则比较轻快。 - 代码生成:
codellama:7b、deepseek-coder:6.7b是专门为代码调优的模型。如果你主要做编程助手,可以优先考虑它们。 - 嵌入(向量化):
bge-m3是通用性很强的选择,支持多语言,在检索任务上表现优异。nomic-embed-text则更轻量,速度可能更快。你可以根据你的语料(主要是中文还是英文)和精度要求来选择。 - 特定领域:如果有医学、法律、金融等垂直领域需求,可以寻找并在Ollama中加载对应的专业模型。
一个实用的策略是“小模型检索 + 大模型生成”。用 bge-m3 或 nomic-embed-text 这种专门的嵌入模型处理海量文本的向量化,因为它们的唯一任务就是生成高质量的向量,效率高、效果好。然后用一个参数规模稍大的对话模型(如 llama3.1:8b)来基于检索结果生成最终答案,因为它更擅长理解和组织语言。这种组合既能保证检索质量,又能获得不错的生成效果,而且对硬件资源的要求相对友好。
最后,记得定期去Ollama的官方社区或GitHub页面看看,经常会有新的模型发布,或者现有模型更新了更好的版本。管理本地模型就像管理你的工具箱,用 ollama pull 添加新工具,用 ollama list 和 ollama rm 整理工具箱,保持一个干净、高效的开发环境。

1560

被折叠的 条评论
为什么被折叠?



