ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

多模态本地搜索实战:Chinese-CLIP与Faiss图片视频检索

多模态本地搜索实战:Chinese-CLIP与Faiss图片视频检索 你有没有经历过这种崩溃手机相册里躺着上万张照片电脑硬盘里堆着几十个视频素材某天突然想找“去年冬天小区里那只戴项圈的橘猫”翻遍所有文件夹却一无所获。文件名救不了你iPhone 导出的都是 IMG_2034.JPG时间轴也救不了你你只记得大概月份但不知道那一帧关键画面到底藏在第几分钟。想真正按照“画面内容”来搜索素材只能让机器同时理解图像、视频和文本这就是多模态搜索。腾讯开源的 Chinese-CLIP 多模态模型把这件事的门槛降了下来它能把图片和文字映射到同一个向量空间配合 Faiss 这类开源向量检索工具我们完全可以自己搭一套既能搜图、也能搜视频关键帧的本地搜索工具。这篇文章不会停在概念层面我会给你一套可以直接运行的源码、完整步骤、验证方法和常见排错清单。1. 这篇文章真正要解决的问题1.1 本地素材为什么越来越难找很多技术人都有本地素材管理需求不只是自媒体从业者。程序员会收集 UI 设计稿、架构图、产品原型截图测试人员会保存 App 界面回归对比图做音视频的同学手里全是随拍片段和监控录像普通人手机相册里则是海量照片和视频。这些素材共同特征是没有规范命名。相册导出的文件通常是IMG_2034.JPG监控视频是ch01_20250610_180000.mp4设计稿是最终版2_new_2.png。靠文件名搜索基本等于没有索引。更关键的是用户往往记得的是“画面里发生了什么”而不是“文件名里有什么”。1.2 传统搜索为什么不够用目前大多数本地搜索方案可以归纳为三类方案原理痛点文件管理器搜索匹配文件名、扩展名无法命中“画面内容”相册/媒体库标签时间、地点、人脸聚类标签维度有限自定义成本高OCR 全文搜索识别图片和视频帧里的文字无法理解真实语义画面主体是“猫”而非“文字”这些方案本质上还是“元数据搜索”不是“内容搜索”。你要搜的“戴项圈的橘猫”在机器眼里只是一组像素。如果没有有效的内容理解所有本地素材就是一堆死数据。1.3 多模态搜索让机器看懂画面多模态搜索的核心是让模型同时理解文本和视觉内容并计算二者在语义上的相似度。以 CLIP 为代表的双塔模型把图片和文本编码到同一个高维向量空间输入“一只戴项圈的橘猫”模型生成一个文本向量输入对应的图片模型生成一个图像向量。两者在向量空间里距离越近说明语义越匹配。这种方案可以做到真正的“用话搜图”甚至“用话搜视频里的某一帧”。更重要的是整个过程可以完全在本地完成数据不出机器满足隐私和版权方面的需求。1.4 谁适合读这篇这篇文章适合三类读者第一本地图片和视频素材非常多但苦于找不到一个能按内容检索工具的人第二想入门多模态检索、CLIP、Faiss 向量数据库的开发者第三需要在业务系统里实现图片或视频语义搜索功能的工程师。你不需要专家级的算法背景但需要会一点 Python 和基础命令行操作。2. 多模态本地搜索的核心原理2.1 双塔模型如何统一图片和文本CLIP 类模型采用“双塔”结构左边一个视觉编码器负责把图片转为向量右边一个文本编码器负责把文字转为向量。训练时通过对比学习让“匹配的图文对”向量距离更近让“不匹配的图文对”距离更远。预训练完成后图像和文本被映射到同一个向量空间于是可以计算跨模态相似度。这就是多模态本地搜索工具最重要的一块地基我们把所有图片塞进视觉编码器得到每个图片的向量搜索时只把用户输入的文本塞进文本编码器然后做一次向量检索。整个过程不需要给图片打任何人工标签。2.2 视频搜索为什么难关键帧抽取视频和图片不一样很难把整段视频直接编码成一个稳定向量。当前工程实践里最常见的方案是“先抽帧、再索引”把视频按时间间隔抽取为多张关键帧再把关键帧当作图片处理。比如 60 秒的视频每隔 1 秒抽一帧会得到 60 个画面。每个画面都对应视频路径、时间戳两个元信息。当用户搜索“红色汽车经过收费站”时系统会先找到最匹配的关键帧再告诉用户这个画面出现在哪个视频的第几秒。2.3 完整检索流程拆解整个本地多模态搜索流程可以分成五步素材准备准备一个目录里面放图片文件以及待索引的视频文件。特征提取图片走视觉编码器视频先抽帧再对关键帧走视觉编码器。向量入库把所有向量写入 Faiss 索引并保存“向量 ID 到素材元数据”的映射表。查询编码用户输入一句中文描述走文本编码器得到查询向量。向量检索与返回用 Faiss 找到 Top-K 个最接近的向量映射回图片路径或视频帧时间戳。这里最容易被新手忽略的是“归一化”CLIP 特征在计算相似度时通常需要先做 L2 归一化再使用内积检索否则检索结果会出现明显的排序偏差。2.4 为什么选择腾讯开源的 Chinese-CLIPCLIP 原生是英文模型直接处理中文文本效果很差。腾讯 AI Lab 开源的 Chinese-CLIP 在中文图文对上做了大规模预训练对中文搜索场景更友好。它同样支持图片编码、文本编码和跨模态相似度计算且权重可以在本地加载非常适合构建本地搜索工具。从项目可行性角度看选择 Chinese-CLIP 还有一个好处社区资料多、模型规模适中ViT-B-16级别权重在消费级 GPU 上就能跑CPU 环境也能完成构建只是速度慢一些。这套工具也可以替换成其他开源多模态模型但本文统一以 Chinese-CLIP 为例讲解保证步骤可复现。3. 环境准备与前置条件3.1 硬件与操作系统本文示例不支持特定操作系统Windows、Linux、macOS 均可运行。建议顺序是资源最低配置推荐配置CPU4 核8 核以上内存8 GB16 GB 以上GPU不需要NVIDIA 显卡 8 GB 以上磁盘10 GB根据素材量扩展如果不确定 GPU 是否可用可以先用 CPU 跑通整个流程再根据性能瓶颈考虑升级。3.2 Python 环境推荐使用 Python 3.10 及以上版本并建议使用 conda 创建独立环境避免污染系统 Python。版本请以实际项目环境为准本文重点演示通用思路。conda create -n multi_search python3.10 -y conda activate multi_search3.3 下载 Chinese-CLIP 源码与依赖建议以源码方式使用 Chinese-CLIP这样cn_clip模块可以直接被项目导入。git clone https://github.com/OFA-Sys/Chinese-CLIP.git cd Chinese-CLIP # 安装基础依赖 pip install -r requirements.txt # 本文额外需要 pip install faiss-cpu opencv-python pillow tqdm gradio说明faiss-cpu足够本地演示生产环境可换成faiss-gpu。gradio用于搭建 Web 搜索界面如果只想使用命令行可以不安装。3.4 验证模型可以加载先写一段最简代码确认 Chinese-CLIP 模型可以正常下载加载。# 文件路径test_model.py import torch import cn_clip.clip as clip from cn_clip.clip import load_from_name device cuda if torch.cuda.is_available() else cpu model, preprocess load_from_name(ViT-B-16, devicedevice, download_root./checkpoints) model.eval() text clip.tokenize([一只戴项圈的橘猫]).to(device) with torch.no_grad(): text_features model.encode_text(text) text_features / text_features.norm(dim-1, keepdimTrue) print(文本特征维度:, text_features.shape) print(模型加载成功)运行后能看到输出类似文本特征维度: [1, 512]说明模型可以正常使用。如果这一步失败优先检查网络环境和依赖版本不要直接进入后续步骤。4. 构建图片与视频的关键帧索引4.1 目录与数据组织建议先按下面的目录结构组织素材方便脚本统一扫描data/ ├── images/ │ ├── dog_snow_001.jpg │ ├── ui_design_001.png │ └── street_001.jpg ├── videos/ │ ├── holiday.mp4 │ └── monitor_clip.mp4 └── index/ ├── faiss.index └── meta.pkl所有图片建议是常见格式.jpg、.jpeg、.png、.bmp、.webp。视频格式建议使用 ffmpeg 支持良好的.mp4、.mov、.mkv。4.2 视频关键帧抽取工具视频抽帧方法很多本文用 OpenCV 实现一个简单稳定的按时间间隔抽帧方案。每间隔指定时间取一帧同时限制最大帧数防止超长视频把索引撑爆。# 文件路径src/extract_video_frames.py import cv2 def extract_keyframes(video_path, interval_sec1.0, max_frames300): 按固定时间间隔抽取视频关键帧。 返回 frames: list[np.ndarray]BGR 格式帧 timestamps: list[float]每帧对应秒数 cap cv2.VideoCapture(video_path) if not cap.isOpened(): raise RuntimeError(f无法打开视频文件: {video_path}) fps cap.get(cv2.CAP_PROP_FPS) if fps 0 or fps ! fps: # 防止异常值 fps 25.0 frame_interval max(1, int(fps * interval_sec)) frames [] timestamps [] frame_id 0 while True: ret, frame cap.read() if not ret: break if frame_id % frame_interval 0: frames.append(frame) timestamps.append(round(frame_id / fps, 2)) if len(frames) max_frames: break frame_id 1 cap.release() return frames, timestamps如果你的视频画面切换很快比如短视频剪辑、监控抓拍可以把interval_sec调小到 0.5 甚至 0.3。如果视频是长时间静止画面比如课堂录播可以把间隔调大到 2 至 5 秒减少重复帧冗余。4.3 建立完整索引接下来是核心脚本它会扫描图片目录提取所有图片特征扫描视频目录先抽帧再提取关键帧特征最后统一写入 Faiss 索引。# 文件路径src/build_index.py import os import pickle import argparse from PIL import Image import cv2 import faiss import numpy as np import torch from tqdm import tqdm import cn_clip.clip as clip from cn_clip.clip import load_from_name from extract_video_frames import extract_keyframes IMAGE_EXTS {.jpg, .jpeg, .png, .bmp, .webp} VIDEO_EXTS {.mp4, .mov, .mkv, .avi} def encode_images(model, preprocess, pil_images, device, batch_size16): 将一批 PIL.Image 编码为归一化特征向量 feats [] for i in range(0, len(pil_images), batch_size): batch [preprocess(img) for img in pil_images[i:i batch_size]] batch torch.stack(batch).to(device) with torch.no_grad(): feat model.encode_image(batch) feat / feat.norm(dim-1, keepdimTrue) feats.append(feat.cpu().numpy()) return np.concatenate(feats, axis0) def collect_files(root_dir, exts): 递归扫描目录下所有指定扩展名的文件 files [] for dirpath, _, filenames in os.walk(root_dir): for name in filenames: if os.path.splitext(name)[1].lower() in exts: files.append(os.path.join(dirpath, name)) return sorted(files) def main(): parser argparse.ArgumentParser() parser.add_argument(--image-root, default./data/images) parser.add_argument(--video-root, default./data/videos) parser.add_argument(--output-dir, default./data/index) parser.add_argument(--interval-sec, typefloat, default1.0) parser.add_argument(--max-frames, typeint, default300) args parser.parse_args() device cuda if torch.cuda.is_available() else cpu print(device:, device) model, preprocess load_from_name(ViT-B-16, devicedevice, download_root./checkpoints) model.eval() all_vecs [] all_meta [] # 1. 图片特征 img_files collect_files(args.image_root, IMAGE_EXTS) print(f发现图片 {len(img_files)} 张) for p in tqdm(img_files, descimages): image Image.open(p).convert(RGB) vecs encode_images(model, preprocess, [image], device, batch_size1) all_vecs.append(vecs[0]) all_meta.append({type: image, path: p}) # 2. 视频关键帧特征 video_files collect_files(args.video_root, VIDEO_EXTS) print(f发现视频 {len(video_files)} 个) for vpath in tqdm(video_files, descvideos): frames, timestamps extract_keyframes(vpath, args.interval_sec, args.max_frames) pil_frames [Image.fromarray(cv2.cvtColor(f, cv2.COLOR_BGR2RGB)) for f in frames] vecs encode_images(model, preprocess, pil_frames, device, batch_size16) for ts, vec in zip(timestamps, vecs): all_vecs.append(vec) all_meta.append({ type: video, video_path: vpath, timestamp: ts }) if len(all_vecs) 0: raise RuntimeError(没有提取到任何特征请检查图片和视频目录) vectors np.stack(all_vecs).astype(float32) index faiss.IndexFlatIP(vectors.shape[1]) index.add(vectors) os.makedirs(args.output_dir, exist_okTrue) faiss.write_index(index, os.path.join(args.output_dir, faiss.index)) with open(os.path.join(args.output_dir, meta.pkl), wb) as f: pickle.dump(all_meta, f) print(f索引完成共 {len(all_meta)} 条向量维度 {vectors.shape[1]}) print(f索引保存至: {args.output_dir}) if __name__ __main__: main()这段代码有几个关键点。第一encode_images内部做了 L2 归一化所以后续可以放心使用IndexFlatIP内积索引。第二meta列表和向量顺序完全一致Faiss 返回的索引 ID 就是meta里的下标这是整条检索链路能够正确映射回素材路径的核心。第三视频抽帧之后每一帧都会作为独立向量入库所以视频体积再大只要限制max_frames索引量就可控。4.4 元数据为什么和向量同样重要如果只存向量不存元数据搜索结果只是一堆“第 5 条向量最像”没有任何工程意义。meta.pkl保存了每个向量的来源图片路径或者视频路径和时间戳。后续查询时一定要保证 Faiss 索引和 meta 文件用同一份数据构建否则索引错位会造成“搜到相似向量但返回的素材文件和搜索词完全无关”。5. 文本查询与结果展示5.1 查询函数实现搜索引擎的核心是查询脚本。它加载模型、Faiss 索引和 meta 文件把用户输入的中文文本编码成向量再执行 Top-K 检索。# 文件路径src/search.py import argparse import pickle import faiss import torch import cn_clip.clip as clip from cn_clip.clip import load_from_name device cuda if torch.cuda.is_available() else cpu model, preprocess load_from_name(ViT-B-16, devicedevice, download_root./checkpoints) model.eval() index faiss.read_index(./data/index/faiss.index) with open(./data/index/meta.pkl, rb) as f: meta pickle.load(f) def search(query, top_k5): 根据中文文本描述返回 Top-K 个最匹配素材 text_tokens clip.tokenize([query]).to(device) with torch.no_grad(): text_feat model.encode_text(text_tokens) text_feat / text_feat.norm(dim-1, keepdimTrue) scores, indices index.search(text_feat.cpu().numpy().astype(float32), top_k) return scores[0], indices[0] def main(): parser argparse.ArgumentParser() parser.add_argument(--query, typestr, requiredTrue, help中文搜索描述) parser.add_argument(--top-k, typeint, default5) args parser.parse_args() scores, indices search(args.query, args.top_k) print(f搜索词: {args.query}) print( * 60) for score, idx in zip(scores, indices): info meta[idx] if info[type] image: print(f[图片] 相似度{score:.4f} 路径{info[path]}) else: print(f[视频] 相似度{score:.4f} 文件{info[video_path]} f时间{info[timestamp]}s) if __name__ __main__: main()这里要注意faiss.read_index读取的是上一节生成的索引文件如果索引目录或文件名称不一致需要同步修改路径。命令行方式适合测试真正日常使用可以继续看 Web 界面方案。5.2 用 Gradio 搭建 Web 搜索界面对于不喜欢命令行的人可以用 Gradio 写一个极简 Web 界面。搜索词输入框、Top-K 条数滑块、结果展示区一屏搞定。视频命中结果会先展示关键帧图片并附带视频路径与时间戳。# 文件路径src/app.py import os import pickle import tempfile import cv2 import faiss import gradio as gr import torch import cn_clip.clip as clip from cn_clip.clip import load_from_name from search import search device cuda if torch.cuda.is_available() else cpu model, preprocess load_from_name(ViT-B-16, devicedevice, download_root./checkpoints) model.eval() index faiss.read_index(./data/index/faiss.index) with open(./data/index/meta.pkl, rb) as f: meta pickle.load(f) def search_ui(query, top_k): scores, indices search(query, int(top_k)) results [] for score, idx in zip(scores, indices): info meta[idx] if info[type] image: results.append( (info[path], f图片 | 相似度 {score:.4f}) ) else: # 从原视频抽取对应时间帧输出临时图片 cap cv2.VideoCapture(info[video_path]) cap.set(cv2.CAP_PROP_POS_MSEC, info[timestamp] * 1000) ok, frame cap.read() cap.release() tmp_path os.path.join(tempfile.gettempdir(), fframe_{idx}.jpg) if ok: cv2.imwrite(tmp_path, frame) else: tmp_path None results.append( (tmp_path, f视频 | 相似度 {score:.4f} | {os.path.basename(info[video_path])} f| {info[timestamp]}s) ) return results demo gr.Interface( fnsearch_ui, inputs[ gr.Textbox(label中文搜索描述), gr.Slider(1, 20, value5, step1, labelTop-K), ], outputsgr.Gallery(label搜索结果), title本地多模态素材搜索工具, description输入一句话搜索本地图片和视频关键帧, ) if __name__ __main__: demo.launch()注意search_ui中视频结果需要临时抽帧展示这对长视频来说会增加一点 IO 开销但对本地演示完全够用。如果结果图片为空路径说明该视频时间点抽帧失败大概率是视频编码不被 OpenCV 支持需要检查 ffmpeg 环境。6. 运行结果与效果验证6.1 准备测试素材建议你先找 10 张图片和 2 个短视频放入data/images与data/videos内容尽量多样化。比如图片雪地里的狗、城市街景、UI 设计稿、自动售货机、人脸合影等视频一段小区门口监控、一段户外旅行记录这样便于验证不同语义的搜索效果。如果素材涉及他人信息请确保你有合法使用权不要拿未经授权的监控画面、私人照片做实验。6.2 构建索引在Chinese-CLIP项目根目录执行python src/build_index.py \ --image-root ./data/images \ --video-root ./data/videos \ --output-dir ./data/index \ --interval-sec 1.0 \ --max-frames 300正常会看到类似输出device: cuda 发现图片 12 张 发现视频 2 个 images: 100%|████████████| 12/12 [00:0300:00, 3.10it/s] videos: 100%|████████████| 2/2 [00:1500:00, 15.20s/it] 索引完成共 245 条向量维度 512 索引保存至: ./data/index这里 245 条 12 张图片向量 2 个视频抽出的 233 帧向量。如果输出行里device: cpu说明当前环境没有可用 GPU构建速度会慢但流程仍然正确。6.3 执行搜索命令行方式验证最简单python src/search.py --query 雪地里奔跑的狗 --top-k 5输出可能类似于搜索词: 雪地里奔跑的狗 [图片] 相似度0.8531 路径./data/images/dog_snow_001.jpg [视频] 相似度0.7724 文件./data/videos/holiday.mp4 时间12.50s [图片] 相似度0.6410 路径./data/images/snow_006.jpg [视频] 相似度0.5207 文件./data/videos/holiday.mp4 时间35.20s [图片] 相似度0.4832 路径./data/images/street_001.jpg实际数值会因素材、模型权重、抽帧时间不同而变化但排序逻辑是固定的相似度最高的一定是语义上与“雪地里奔跑的狗”最接近的素材。6.4 如何判断搜索是否成功判断标准不是“相似度无限接近 1”而是 Top-K 结果里是否出现了你心中预期的素材。如果第一屏结果和搜索词明显相关说明整套流程已经跑通。如果相关素材完全没有出现不用先怀疑模型按下面的顺序排查确认素材目录确实被扫描到检查构建索引时的“发现图片 N 张”“发现视频 N 个”是否合理。确认查询词是中文而不是英文否则建议使用 Chinese-CLIP 之外的原生 CLIP 模型。确认索引构建时没有抛出抽帧失败、编码异常等告警。确认 meta.pkl 是否和 faiss.index 在同一批构建中生成不要混用不同索引的元数据。7. 常见问题与排查方法问题现象可能原因排查方式解决方案启动时找不到cn_clip模块没有在 Chinese-CLIP 仓库根目录运行或依赖未安装检查当前目录是否存在cn_clip包把脚本放到仓库根目录或pip install -r requirements.txt后使用模型加载时网络超时模型权重下载不稳定观察报错是否卡在下载阶段提前下载权重到download_root指定目录离线加载GPU 显存溢出批量编码帧数过多查看报错是否包含CUDA out of memory把batch_size调小到 8 或 4必要时改用 CPU视频搜索不到相关帧抽帧间隔太大关键画面被跳过查看命中视频的总帧数和时间戳分布调小interval_sec或改用镜头检测抽帧搜索结果里重复帧过多视频静止或变化很小每帧都相似检查相似度是不是非常接近在抽帧后增加帧间相似度去重相似度过高则丢弃中文搜索效果明显差误用了英文原版 CLIP 权重查看模型名称是否包含chinese-clip使用 Chinese-CLIP 官方中文权重新增素材后搜索不到索引是构建时的静态快照检查索引文件时间戳是否早于素材时间对新增素材增量提取向量后index.add()再保存索引删除素材后仍能搜到元数据和向量索引不同步检查结果返回的路径是否已不存在清理对应向量后重建索引或做软删除标记如果遇到上表没有覆盖的问题最有效的办法是打开build_index.py逐段打印每个文件处理前后的状态而不是直接怀疑模型能力。绝大多数“搜不到”都是数据路径、依赖路径或索引文件不一致造成的工程问题。8. 最佳实践与工程建议8.1 用 SQLite 管理元数据演示用的meta.pkl在素材量少时没有大问题但真实项目中建议换成 SQLite甚至直接用一个 JSON Lines 文件。原因很简单pickle 一旦结构变化可能无法反序列化而且无法灵活查询。更稳妥的元数据表结构如下CREATE TABLE media_meta ( id INTEGER PRIMARY KEY, type TEXT NOT NULL, -- image / video path TEXT NOT NULL, -- 图片绝对或相对路径 video_path TEXT, -- 视频路径图片类型为空 timestamp REAL, -- 视频帧时间戳图片类型为 NULL vector_id INTEGER UNIQUE NOT NULL, -- 对应对应 Faiss 索引中的向量 ID created_at TEXT DEFAULT (datetime(now)) );这样搜索完拿到向量 ID 后直接查表就能拿到素材信息并且方便后续做素材删除、状态标记和统计分析。8.2 增量索引与全量重建静态索引在实际使用中很快会过时。推荐的做法是记录每个素材文件的mtime或 hash定期扫描目录新增文件做特征提取并执行index.add()删除文件时在 SQLite 中标记删除再定期做一次全量 compact 或全量重建。全量重建虽然成本高但对本地百万级向量来说仍然可控。8.3 抽帧策略应该按场景调整固定间隔抽帧简单但会漏掉快速切换的画面。更专业的方案是检测镜头切换比如计算相邻帧直方图差异差异超过阈值就保留。如果视频量很大还可以先用场景检测挑出候选帧再用 CLIP 为候选帧打分去重这样索引量会小一个数量级。8.4 安全与隐私边界这类工具可以扫描并索引本机磁盘内容因此使用边界必须明确只处理你自己拥有或已获得明确授权的素材不要对同事、他人的设备或受控网络里的视频做未经授权的扫描和特征提取。涉及敏感画面的检索服务部署时应做访问控制和操作审计避免检索功能被滥用。8.5 模型选型与精度调优如果素材画面包含大量中文文字比如 PPT 截图、网页设计稿可以配合 OCR 做一层文本召回再与 CLIP 向量做重排。如果业务领域非常垂直比如工业质检图片通用 Chinese-CLIP 效果可能不够可以考虑用少量领域数据对模型做 LoRA 微调。微调不是必须但通常是提升特定场景效果最直接的手段。8.6 与多模态大模型结合很多人会问腾讯元宝这类多模态大模型也能看图猜内容为什么还要用 CLIP 双塔方案核心区别在于多模态大模型更适合“理解单张/少数几张图片并生成文本”而双塔模型的优势是“一次性把海量图片/视频帧向量化然后毫秒级检索”。生产系统中常见组合是先用 CLIP 向量召回 Top-100 候选再用多模态大模型对候选结果做精排、打标签或生成说明。两者不是替代关系而是互补关系。9. 总结与后续学习方向这篇内容把“腾讯开源多模态本地搜索工具”从概念掰开揉碎讲到了落地我们用腾讯开源的 Chinese-CLIP 做图文统一编码用 OpenCV 给视频抽关键帧用 Faiss 构建向量索引最后实现了一个既能搜图片、也能搜视频时间点的本地搜索工具。核心代码全部可以复制运行目录结构、依赖、运行命令、验证标准和排错表格也都给了。下一步你可以从三个方向继续深入。第一把命令行演示升级成带增量更新的服务化应用比如用 FastAPI 提供搜索接口用 SQLite 管理素材元数据配合定时任务扫描新文件。第二研究更高质量的视频抽帧方案比如基于场景切换的动态抽帧减小索引冗余。第三关注多模态大模型的最新进展尝试把向量召回和多模态大模型精排结合起来。手头素材越多这套工具的价值就越明显但务必记住只在你有权处理的数据上运行它。
返回列表