all-MiniLM-L6-v2部署教程:Ollama + FastAPI封装REST接口,供Java/Go后端调用
all-MiniLM-L6-v2部署教程:Ollama + FastAPI封装REST接口,供Java/Go后端调用
你是不是也遇到过这样的问题?想在自己的Java或Go后端项目里用上强大的文本语义理解能力,比如做智能搜索、文档分类或者问答系统,但一看到动辄几个G的大模型就头疼?部署复杂、资源消耗大、调用不方便,让很多开发者望而却步。
今天,我就带你解决这个痛点。我们将使用一个轻量级但能力不俗的模型——all-MiniLM-L6-v2,通过Ollama来一键部署,再用FastAPI封装成标准的REST接口。这样一来,你的Java或Go后端服务,就能像调用普通HTTP API一样,轻松获得文本嵌入向量,实现各种AI功能。
整个过程非常简单,即使你之前没怎么接触过Python或AI模型部署,也能跟着一步步做下来。我们最终的目标是:让你在30分钟内,拥有一个高性能、可随时调用的文本嵌入服务。
1. 环境准备与工具介绍
在开始动手之前,我们先快速了解一下今天要用到的几个核心工具。别担心,它们都非常友好,安装和使用都很简单。
1.1 主角登场:all-MiniLM-L6-v2
首先是我们今天的主角,all-MiniLM-L6-v2。你可以把它理解为一个“文本理解专家”。
- 它是什么:一个专门将文本(比如一句话、一段描述)转换成一串数字(我们叫它“向量”或“嵌入”)的模型。这串数字就像是文本的“数字指纹”,包含了文本的语义信息。
- 它有多强:虽然它体积很小(只有大约23MB),但能力不容小觑。它基于著名的BERT架构,通过一种叫“知识蒸馏”的技术,从更大的老师模型那里学到了精髓,所以在很多任务上表现接近大模型,但速度要快得多。
- 为什么选它:对于大多数后端应用场景,比如计算用户查询和商品描述的相似度、给新闻文章自动分类、或者在海量文档中快速找到相关内容,这个模型的精度完全够用。最关键的是,它速度快、资源占用少,非常适合集成到在线服务中。
简单来说,你给它一段文字,它就还你一个能代表这段文字含义的“数字密码”。后续的相似度计算、分类等操作,都是基于这个“数字密码”来进行的。
1.2 得力助手:Ollama
接下来是Ollama,它是我们部署模型的“瑞士军刀”。
- 它做什么:Ollama专门用来在本地(或者你的服务器上)快速运行各种开源大语言模型。它把复杂的模型下载、环境配置、运行命令都打包好了,你只需要几条简单的命令就能让模型跑起来。
- 为什么用它:没有Ollama之前,部署一个模型可能要折腾各种Python环境、依赖库、版本冲突。有了Ollama,就像是给模型提供了一个即开即用的“运行容器”,大大降低了门槛。它本身也提供了一个基础的API,但我们今天要把它包装得更易用。
1.3 桥梁工程师:FastAPI
最后是FastAPI,它是我们搭建API服务的“快速框架”。
- 它做什么:FastAPI是一个现代的Python Web框架,用来构建API接口速度快如闪电。我们将用它来创建一个Web服务,这个服务接收来自Java或Go后端的HTTP请求,然后去调用Ollama运行的模型,最后把结果返回回去。
- 为什么用它:它代码简洁、性能高,并且能自动生成交互式API文档。这样,你的后端同事一看文档就知道怎么调用了,联调效率非常高。
好了,工具介绍完毕。总结一下我们的技术方案:用Ollama拉取并运行all-MiniLM-L6-v2模型,然后用FastAPI写一个简单的Web应用作为中间层,对外提供RESTful API。你的Java/Go服务通过HTTP调用这个API即可。
下面,我们就开始一步步实现它。
2. 第一步:使用Ollama部署模型服务
这是最基础的一步,我们要先把模型跑起来。确保你的机器上已经安装了Docker,这是运行Ollama最简单的方式。
2.1 拉取并运行Ollama
打开你的终端(Linux/Mac)或命令提示符/PowerShell(Windows),执行下面的命令。这会在后台启动一个Ollama服务。
docker run -d -v ollama:/root/.ollama -p 11434:11434 --name ollama ollama/ollama
命令解释:
docker run -d:在后台运行一个容器。-v ollama:/root/.ollama:把容器里的模型数据目录挂载到本地一个叫ollama的卷上,这样即使容器删除,下载的模型还在。-p 11434:11434:把容器内部的11434端口映射到本机的11434端口。Ollama的API就在这个端口上。--name ollama:给这个容器起个名字,方便管理。ollama/ollama:要运行的Ollama镜像。
运行成功后,你可以用 docker ps 命令看到这个名为ollama的容器正在运行。
2.2 拉取并运行all-MiniLM-L6-v2模型
Ollama服务跑起来后,它自己还不知道我们要用什么模型。我们需要告诉它去下载我们指定的模型。还是在终端里,执行:
docker exec -it ollama ollama pull nomic-embed-text
这里有个重要说明:在Ollama的官方模型库中,all-MiniLM-L6-v2这个模型被命名为 nomic-embed-text。所以我们是拉取这个名字的模型。这个过程会下载模型文件,因为模型很小,所以很快。
下载完成后,我们需要在Ollama容器内部启动这个模型的服务:
docker exec -d ollama ollama run nomic-embed-text
这个命令会在Ollama容器内部后台运行nomic-embed-text模型。现在,模型服务已经在Ollama内部准备就绪,并监听请求了。
到这一步,我们的“文本理解专家”已经上线了,它正待在Ollama这个“运行容器”里,等着我们通过端口11434去调用它。接下来,我们就来搭建一个更友好的“前台接待处”——FastAPI服务。
3. 第二步:用FastAPI封装REST接口
现在模型服务跑起来了,但它的接口可能不太符合你后端团队的习惯。我们用一个FastAPI应用来包装它,提供更标准、更清晰的REST API。
首先,创建一个新的项目目录,比如叫做 embedding_api,然后进入这个目录。
3.1 创建Python环境与依赖文件
在项目根目录下,创建一个名为 requirements.txt 的文件,里面写上我们需要的Python库:
fastapi==0.104.1
uvicorn[standard]==0.24.0
requests==2.31.0
pydantic==2.5.0
然后,建议你创建一个Python虚拟环境来安装这些依赖,避免污染全局环境。
# 创建虚拟环境(根据你的Python版本,这里以python3为例)
python3 -m venv venv
# 激活虚拟环境
# Linux/Mac:
source venv/bin/activate
# Windows:
# venv\Scripts\activate
# 安装依赖
pip install -r requirements.txt
3.2 编写核心的FastAPI应用代码
在项目根目录下,创建一个 main.py 文件,这就是我们服务的主文件。将下面的代码复制进去:
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from typing import List
import requests
import logging
# 配置日志,方便查看运行情况
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# 创建FastAPI应用实例
app = FastAPI(
title="文本嵌入向量服务 API",
description="基于Ollama和all-MiniLM-L6-v2模型,提供文本生成嵌入向量的REST接口。",
version="1.0.0"
)
# 定义请求体的数据模型:接收一个字符串列表
class EmbeddingRequest(BaseModel):
texts: List[str]
# 定义响应体的数据模型:返回向量列表和模型信息
class EmbeddingResponse(BaseModel):
embeddings: List[List[float]]
model: str
total_tokens: int
# Ollama服务的地址和端点
OLLAMA_BASE_URL = "http://localhost:11434"
EMBEDDING_ENDPOINT = "/api/embed"
@app.get("/")
async def root():
"""健康检查端点,返回服务状态。"""
return {"status": "ok", "message": "文本嵌入向量服务正在运行。"}
@app.post("/v1/embeddings", response_model=EmbeddingResponse)
async def create_embeddings(request: EmbeddingRequest):
"""
生成文本的嵌入向量。
- **texts**: 需要生成嵌入向量的文本列表,例如 ["今天天气真好", "我喜欢编程"]。
"""
if not request.texts:
raise HTTPException(status_code=400, detail="文本列表不能为空")
logger.info(f"收到嵌入请求,文本数量:{len(request.texts)}")
embeddings = []
total_tokens = 0
# 循环处理每个文本,调用Ollama接口
for text in request.texts:
payload = {
"model": "nomic-embed-text",
"prompt": text
}
try:
# 向Ollama服务发送请求
response = requests.post(
f"{OLLAMA_BASE_URL}{EMBEDDING_ENDPOINT}",
json=payload,
timeout=30 # 设置超时时间
)
response.raise_for_status() # 如果响应状态码不是200,抛出异常
result = response.json()
# 从Ollama响应中提取嵌入向量
if "embedding" in result:
embeddings.append(result["embedding"])
total_tokens += result.get("total_tokens", 0)
else:
logger.error(f"Ollama响应中未找到'embedding'字段: {result}")
raise HTTPException(status_code=500, detail="模型服务返回格式错误")
except requests.exceptions.RequestException as e:
logger.error(f"调用Ollama接口失败: {e}")
raise HTTPException(status_code=503, detail=f"模型服务暂时不可用: {str(e)}")
except Exception as e:
logger.error(f"处理文本时发生未知错误: {e}")
raise HTTPException(status_code=500, detail="内部服务器错误")
logger.info(f"嵌入向量生成成功,共处理{len(embeddings)}个文本。")
# 构造并返回标准响应
return EmbeddingResponse(
embeddings=embeddings,
model="all-MiniLM-L6-v2 (via Ollama)",
total_tokens=total_tokens
)
if __name__ == "__main__":
import uvicorn
# 启动服务,监听在本机的8000端口
uvicorn.run(app, host="0.0.0.0", port=8000)
代码关键点解读:
- 定义标准接口:我们创建了一个
POST /v1/embeddings的接口。这模仿了OpenAI等主流AI服务的API格式,让你的后端调用起来感觉更熟悉。 - 数据验证:使用Pydantic的
BaseModel来定义请求和响应的格式。这能自动验证传入的数据是否合法,比如texts字段是不是一个字符串列表。 - 错误处理:代码里包含了详细的错误处理。如果Ollama服务没响应,或者返回的数据不对,我们的API会返回明确的错误信息(如503服务不可用、500内部错误),而不是直接崩溃。
- 日志记录:加了日志功能,服务运行起来后,你可以在控制台看到谁调用了、处理了多少文本,方便排查问题。
- 超时设置:请求Ollama时设置了30秒超时,防止某个请求卡住整个服务。
3.3 启动FastAPI服务
代码写好了,现在让我们启动它。在项目根目录下(确保虚拟环境已激活),运行:
python main.py
你会看到类似下面的输出,说明服务启动成功了:
INFO: Started server process [12345]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
太好了!现在你的FastAPI服务已经在http://localhost:8000上运行了。打开浏览器,访问 http://localhost:8000/docs,你会看到一个自动生成的、非常漂亮的交互式API文档页面。你可以在这里直接测试接口,而不需要写任何客户端代码。
模型服务和API网关都准备好了,接下来,我们看看如何从你的Java或Go后端来调用这个服务。
4. 第三步:Java与Go后端调用示例
我们的FastAPI服务提供了一个标准的HTTP接口,这意味着任何能发送HTTP请求的语言都可以调用它。这里我分别给出Java(使用Spring Boot框架)和Go语言的简单调用示例。
4.1 Java (Spring Boot) 调用示例
假设你有一个Spring Boot项目,你可以使用RestTemplate或者更现代的WebClient来调用我们的嵌入服务。
首先,你需要在pom.xml中添加Spring Boot Web的依赖(如果还没有的话)。
然后,创建一个服务类EmbeddingService.java:
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.util.List;
@Service
public class EmbeddingService {
// 你的FastAPI服务地址
private static final String API_URL = "http://localhost:8000/v1/embeddings";
private final RestTemplate restTemplate;
public EmbeddingService(RestTemplateBuilder restTemplateBuilder) {
this.restTemplate = restTemplateBuilder.build();
}
/**
* 调用远程嵌入服务,获取文本的向量表示
* @param texts 文本列表
* @return 嵌入向量响应
*/
public EmbeddingResponse getEmbeddings(List<String> texts) {
// 1. 构造请求体
EmbeddingRequest request = new EmbeddingRequest();
request.setTexts(texts);
// 2. 设置请求头
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<EmbeddingRequest> entity = new HttpEntity<>(request, headers);
// 3. 发送POST请求
ResponseEntity<EmbeddingResponse> response = restTemplate.exchange(
API_URL,
HttpMethod.POST,
entity,
EmbeddingResponse.class
);
// 4. 返回响应体
if (response.getStatusCode() == HttpStatus.OK && response.getBody() != null) {
return response.getBody();
} else {
throw new RuntimeException("调用嵌入服务失败: " + response.getStatusCode());
}
}
// 内部类:定义请求体结构(对应Python端的EmbeddingRequest)
@Data
public static class EmbeddingRequest {
@JsonProperty("texts")
private List<String> texts;
}
// 内部类:定义响应体结构(对应Python端的EmbeddingResponse)
@Data
public static class EmbeddingResponse {
@JsonProperty("embeddings")
private List<List<Double>> embeddings;
@JsonProperty("model")
private String model;
@JsonProperty("total_tokens")
private Integer totalTokens;
}
}
使用方式:在你的Controller或业务逻辑里,注入这个EmbeddingService,然后调用getEmbeddings方法,传入一个字符串列表(比如["查询文本", "文档内容"]),就能得到一个向量列表。每个向量就是一个List<Double>。
4.2 Go语言调用示例
在Go语言中,我们可以使用标准库的net/http或者第三方库如github.com/go-resty/resty。这里展示标准库的方式。
创建一个文件,比如 embedding_client.go:
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
// 定义请求和响应的结构体
type EmbeddingRequest struct {
Texts []string `json:"texts"`
}
type EmbeddingResponse struct {
Embeddings [][]float64 `json:"embeddings"`
Model string `json:"model"`
TotalTokens int `json:"total_tokens"`
}
// GetEmbeddings 调用嵌入服务
func GetEmbeddings(texts []string) (*EmbeddingResponse, error) {
// 1. 构造请求体
request := EmbeddingRequest{Texts: texts}
jsonData, err := json.Marshal(request)
if err != nil {
return nil, fmt.Errorf("序列化请求失败: %v", err)
}
// 2. 创建HTTP请求
req, err := http.NewRequest("POST", "http://localhost:8000/v1/embeddings", bytes.NewBuffer(jsonData))
if err != nil {
return nil, fmt.Errorf("创建请求失败: %v", err)
}
req.Header.Set("Content-Type", "application/json")
// 3. 发送请求
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("发送请求失败: %v", err)
}
defer resp.Body.Close()
// 4. 检查响应状态
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("服务返回错误: %s, 详情: %s", resp.Status, string(body))
}
// 5. 解析响应体
var embeddingResp EmbeddingResponse
if err := json.NewDecoder(resp.Body).Decode(&embeddingResp); err != nil {
return nil, fmt.Errorf("解析响应失败: %v", err)
}
return &embeddingResp, nil
}
func main() {
// 示例:调用函数
texts := []string{"Go语言性能很高", "Python以简洁易用著称"}
result, err := GetEmbeddings(texts)
if err != nil {
fmt.Printf("调用失败: %v\n", err)
return
}
fmt.Printf("使用的模型: %s\n", result.Model)
fmt.Printf("消耗的总token数: %d\n", result.TotalTokens)
fmt.Printf("第一个文本的向量维度: %d\n", len(result.Embeddings[0]))
// 你可以在这里使用result.Embeddings进行后续操作,比如计算相似度
}
使用方式:将GetEmbeddings函数集成到你的Go项目中。调用时传入字符串切片,函数会返回一个包含向量切片的EmbeddingResponse结构体指针和一个错误。你可以用这些向量来做余弦相似度计算等操作。
看到这里,你的后端服务已经能够通过简单的HTTP调用,获得强大的文本语义向量了。无论是用Java还是Go,集成起来都非常方便。
5. 进阶使用与优化建议
基础服务搭建好了,但在实际生产环境中,我们还需要考虑更多。这里分享几个进阶思路和优化建议,让你的嵌入服务更健壮、更高效。
5.1 如何计算文本相似度?
拿到文本的嵌入向量后,最常用的操作就是计算相似度。这里给你一个Python示例,展示如何计算两个向量之间的余弦相似度(最常用的方法):
import numpy as np
def cosine_similarity(vec_a, vec_b):
"""
计算两个向量的余弦相似度。
值越接近1,表示越相似;越接近0,表示越不相关。
"""
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)
return dot_product / (norm_a * norm_b)
# 示例:假设从API拿到了两个文本的向量
vector1 = [0.1, 0.5, -0.2, ...] # 文本"A"的向量
vector2 = [0.12, 0.48, -0.19, ...] # 文本"B"的向量
similarity = cosine_similarity(vector1, vector2)
print(f"文本A和文本B的余弦相似度为: {similarity:.4f}")
你可以把这个计算逻辑放在FastAPI服务里,提供一个 /v1/similarity 的接口,直接返回相似度分数;也可以放在你的Java/Go后端里,拿到向量后自己算。
5.2 性能优化与生产部署建议
- 批处理支持:我们的示例代码是循环调用Ollama接口,一次处理一个文本。你可以修改FastAPI代码,尝试构造一个批量的prompt(如果模型支持)发送给Ollama,或者使用异步请求(
asyncio+aiohttp)来并发调用,这样可以大幅提升处理一批文本的速度。 - 服务高可用:
- 多实例负载均衡:使用Nginx或云负载均衡器,在后面部署多个FastAPI服务实例和Ollama实例。
- 健康检查:为FastAPI服务添加
/health端点,让负载均衡器能判断实例是否健康。 - 容器化部署:将Ollama和你的FastAPI服务都打包成Docker镜像,使用Docker Compose或Kubernetes来编排,管理起来更方便。
- 配置管理:不要把Ollama服务的地址(
localhost:11434)硬编码在代码里。使用环境变量或配置文件来管理,这样在不同环境(开发、测试、生产)部署时更容易切换。 - 限流与鉴权:生产环境的API一定要考虑安全。使用FastAPI的中间件,可以很容易地添加速率限制(防止被刷)、API密钥认证等机制。
5.3 常见问题排查(FAQ)
- Q: 调用FastAPI接口超时或失败?
- A: 首先检查Ollama容器是否在运行 (
docker ps)。然后检查FastAPI服务日志,看错误信息。最常见的问题是Ollama服务没启动,或者网络端口不通。
- A: 首先检查Ollama容器是否在运行 (
- Q: 返回的向量维度是多少?
- A: all-MiniLM-L6-v2模型生成的向量维度是384。你拿到手的每个
embedding列表长度就是384。
- A: all-MiniLM-L6-v2模型生成的向量维度是384。你拿到手的每个
- Q: 支持多长的文本?
- A: 这个模型最大支持256个token(约等于180-200个汉字)。超过长度的文本会被截断。对于长文档,常见的做法是分段处理,然后取各段向量的平均值或使用其他池化策略。
- Q: 如何更新模型?
- A: 如果需要更新Ollama中的模型,可以执行
docker exec ollama ollama pull nomic-embed-text拉取最新版,然后重启Ollama容器中的模型进程。
- A: 如果需要更新Ollama中的模型,可以执行
6. 总结
回顾一下我们今天完成的事情:我们成功地将一个轻量级但强大的文本嵌入模型all-MiniLM-L6-v2,通过Ollama和FastAPI,封装成了一个标准的、易于调用的REST API服务。
整个流程的核心价值在于:
- 简化部署:利用Ollama,我们避免了复杂的Python环境和模型依赖管理,一条命令就让模型跑了起来。
- 统一接口:通过FastAPI,我们为模型套上了一层标准化的“外衣”,提供了清晰的API文档和稳定的HTTP接口。
- 跨语言调用:无论你的主力后端是Java(Spring Boot)、Go,还是Python、Node.js,现在都可以通过简单的HTTP客户端,以相同的方式获取文本的语义向量。
这个方案特别适合那些不想在业务代码中直接耦合复杂Python AI栈的团队。AI模型服务被独立部署和运维,后端业务代码只需关注HTTP调用和业务逻辑,架构清晰,职责分离。
你现在拥有的,不再只是一个模型,而是一个随时待命的企业级文本理解微服务。你可以用它来增强搜索、优化推荐、分类内容、检测相似问题……想象空间很大。希望这篇教程能帮你顺利跨出AI能力落地的第一步。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。
更多推荐


所有评论(0)