在开发推荐系统时,我们常常会面临这样的挑战:如何高效地处理海量非结构化数据,让推荐结果既符合用户语义偏好,又能结合具体业务条件进行精准筛选?最近在实践中,我发现将 Milvus 向量数据库与 OpenAI 的文本 Embedding 能力结合,能很好地解决这些问题。今天就和大家分享一套完整的电影推荐系统实现方案,适合刚接触向量检索的开发者上手实践。

一、技术准备:搭建基础开发环境

1. 核心依赖安装

首先需要安装四个关键库:

bash

pip install openai pymilvus datasets tqdm

  • openai:用于生成文本 Embedding,这里我们使用轻量级模型text-embedding-3-small,在保持精度的同时提升处理速度
  • pymilvus:Milvus 的 Python 客户端,支持本地文件、服务器、云服务等多种连接方式
  • datasets:从 Hugging Face 直接加载netflix-shows数据集,包含 8000 + 电影元数据
  • tqdm:显示数据处理进度条,方便监控批量操作状态

2. 配置 OpenAI 密钥

在代码中添加:

python

运行

import os
os.environ["OPENAI_API_KEY"] = "[你的API密钥]"  # 注意:密钥需在OpenAI官网申请并妥善保管

首次使用需在 OpenAI 控制台创建 API 密钥,建议通过环境变量管理敏感信息,避免硬编码在代码中。

3. 初始化 Milvus 连接

python

运行

from pymilvus import MilvusClient
# 使用Milvus Lite本地存储(适合小规模数据)
client = MilvusClient("./milvus_demo.db") 
# 若部署在服务器,改为:client = MilvusClient(uri="http://服务器IP:19530")
# 云服务版本:client = MilvusClient(uri="Zilliz Cloud端点", token="你的API密钥")

这里推荐先用本地文件模式快速验证功能,数据量超过百万级时再切换到服务器部署。

二、数据处理:从原始数据到向量入库

1. 定义数据结构

我们需要存储电影的基础信息和向量数据,创建包含 6 个字段的集合:

python

运行

from pymilvus import DataType
COLLECTION_NAME = "movie_search"
DIMENSION = 1536  # 与OpenAI模型输出维度一致

# 定义Schema,注意字符串字段需设置最大长度
schema = MilvusClient.create_schema()
schema.add_field("id", DataType.INT64, is_primary=True)  # 主键ID
schema.add_field("title", DataType.VARCHAR, 64000)  # 电影标题
schema.add_field("type", DataType.VARCHAR, 64000)  # 类型(电影/剧集)
schema.add_field("release_year", DataType.INT64)  # 发行年份
schema.add_field("rating", DataType.VARCHAR, 64000)  # 分级信息
schema.add_field("description", DataType.VARCHAR, 64000)  # 剧情描述
schema.add_field("embedding", DataType.FLOAT_VECTOR, DIMENSION)  # 向量字段

# 创建集合并设置索引
client.create_collection(COLLECTION_NAME, schema)
client.create_index(
    COLLECTION_NAME, 
    field_name="embedding", 
    metric_type="IP",  # 内积相似度,适合文本场景
    index_type="AUTOINDEX"  # 自动选择最优索引类型
)
client.load_collection(COLLECTION_NAME)  # 加载集合到内存以便查询

关键点:字符串字段必须指定max_length,向量字段维度要与 Embedding 生成结果一致,索引选择AUTOINDEX能简化配置,适合新手入门。

2. 加载 Hugging Face 数据集

直接获取预处理好的 Netflix 电影数据集:

python

运行

from datasets import load_dataset
dataset = load_dataset("hugginglearners/netflix-shows", split="train")

数据包含title、description等关键字段,但需要处理缺失值:

  • 数值型字段(如release_year)用-1填充
  • 字符串字段用空字符串""填充
    确保后续 Embedding 生成和数据插入稳定。

3. 批量生成 Embedding 并入库

使用 OpenAI 接口生成剧情描述的向量表示,注意批量处理优化性能:

python

运行

from openai import OpenAI
openai_client = OpenAI()

def emb_texts(texts):
    """批量生成文本Embedding"""
    response = openai_client.embeddings.create(
        input=texts,
        model="text-embedding-3-small"
    )
    return [data.embedding for data in response.data]

BATCH_SIZE = 1000  # 调整批次大小避免内存溢出
batch = []

for i in tqdm(range(len(dataset))):
    item = {
        "title": dataset[i].get("title", ""),
        "type": dataset[i].get("type", ""),
        "release_year": dataset[i].get("release_year", -1),
        "rating": dataset[i].get("rating", ""),
        "description": dataset[i].get("description", "")
    }
    batch.append(item)
    
    # 达到批次大小或处理最后一条数据时提交
    if len(batch) == BATCH_SIZE or i == len(dataset)-1:
        texts = [x["description"] for x in batch]
        embeddings = emb_texts(texts)  # 生成向量
        for x, emb in zip(batch, embeddings):
            x["embedding"] = emb  # 合并向量数据
        
        client.insert(COLLECTION_NAME, batch)  # 插入Milvus
        batch = []  # 清空批次

踩坑经验:刚开始没做缺失值处理,导致部分数据生成 Embedding 失败,后来统一用默认值填充解决。建议在数据入库前做严格清洗,避免后续查询异常。

三、智能查询:结合语义检索与条件过滤

核心查询函数设计

实现一个支持语义搜索和元数据过滤的通用函数:

python

运行

import textwrap

def query_movies(query_text, filter_expr, top_k=5):
    """
    query_text: 搜索文本(如"关于毛茸茸动物的电影")
    filter_expr: 过滤表达式(如"release_year < 2019 and rating like 'PG%'")
    top_k: 返回结果数量
    """
    # 生成查询文本的Embedding
    query_emb = emb_texts([query_text])[0]
    
    # 执行向量搜索,同时应用元数据过滤
    result = client.search(
        collection_name=COLLECTION_NAME,
        data=[query_emb],
        filter=filter_expr,
        limit=top_k,
        output_fields=["title", "type", "release_year", "rating", "description"],
        search_params={
            "metric_type": "IP",
            "params": {}
        }
    )
    
    # 解析结果并格式化输出
    print(f"搜索文本:{query_text}")
    print(f"过滤条件:{filter_expr}\n")
    
    for hit_group in result:
        for rank, hit in enumerate(hit_group, 1):
            entity = hit["entity"]
            score = hit["distance"]  # 距离越小相似度越高
            
            print(f"排名 {rank} | 相似度:{score:.4f} | 标题:{entity['title']}")
            print(f"类型:{entity['type']} | 年份:{entity['release_year']} | 分级:{entity['rating']}")
            print("剧情简介:")
            print(textwrap.fill(entity['description'], width=80))  # 自动换行
            print("-" * 80)

实战查询示例

比如查找 2019 年前适合儿童观看(PG 分级)的毛茸茸动物主题电影:

python

运行

my_query = (
    "movie about a fluffy animal",  # 英文查询文本(实际可用中文,需模型支持)
    "release_year < 2019 and rating like 'PG%'"  # Milvus支持的SQL-like过滤语法
)
query_movies(*my_query)

输出结果:

plaintext

搜索文本:movie about a fluffy animal
过滤条件:release_year < 2019 and rating like 'PG%'

排名 1 | 相似度:0.4221 | 标题:The Adventures of Tintin
类型:Movie | 年份:2011 | 分级:PG
剧情简介:
This 3-D motion capture adapts Georges Remi's classic comic strip about the adventures
of fearless young journalist Tintin and his trusty dog, Snowy.
------------------------
...(后续结果)

过滤语法解析

支持常见的 SQL 条件表达式:

  • 数值比较:release_year > 2010
  • 字符串匹配:rating like 'R%'(支持 % 通配符)
  • 逻辑组合:(type == 'Movie') and (release_year between 2015 and 2020)
    通过元数据过滤,能有效缩小搜索范围,提升推荐相关性。

四、实践总结与优化方向

1. 开发中的关键收获

  • 向量数据库优势:Milvus 的 AUTOINDEX 功能自动选择最佳索引(如 IVF-FLAT),无需手动调参,对新手非常友好
  • 批量处理技巧:设置合适的 BATCH_SIZE(建议 500-1000),结合 tqdm 进度条,既能提升插入效率,又能监控任务状态
  • 数据清洗重要性:缺失值处理不当会导致整个流程失败,建议在数据加载阶段添加严格校验

2. 生产环境优化建议

  • 模型升级:若追求更高精度,可切换为text-embedding-3或自定义微调模型
  • 分布式部署:数据量超过 10 万条时,建议使用 Docker 部署 Milvus 集群,提升检索性能
  • 用户交互优化:可添加前端界面,支持用户输入中文查询,后端通过翻译 API 转为英文再生成 Embedding(需模型支持多语言)

3. 常见问题解决方案

问题现象可能原因解决方法
插入数据报错字段类型不匹配检查 Schema 定义,确保字符串长度、数值范围正确
搜索结果为空过滤条件过严简化过滤表达式,先验证基础功能
响应速度慢未加载集合到内存确保调用load_collection,或升级硬件 / 使用云服务

五、写在最后

这套方案不仅适用于电影推荐,稍作调整就能应用到图书、商品等领域的推荐系统。在实践中,我深刻体会到向量数据库与大语言模型结合的强大潜力 —— 既能处理文本语义,又能通过元数据精准筛选,真正实现 "智能 + 可控" 的推荐效果。

如果你在搭建过程中遇到问题,欢迎在评论区留言交流。觉得文章有用的话,别忘了点击关注和收藏,后续会分享更多 Milvus 进阶技巧,包括大规模数据分片、索引优化、与 LLM 深度整合等实战内容。让我们一起在向量检索的世界里,探索更多可能性!

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐