多模型支持、模型推荐、批量处理、智能重试、结果持久化、历史记录与进度反馈。我想解决的不是“调一次 Embedding API”,而是“怎么把分块结果稳定地变成可复用的向量资产”。
前两篇我分别写了文档加载和文档分块。
如果说文档加载解决的是“内容从哪里来”,分块解决的是“内容应该怎么切”,那这一篇要聊的,就是第三个关键问题:
这些块,应该怎么被稳定地转换成向量表示。
很多人聊 RAG 的时候,会把向量化这一步说得很轻:
选个 Embedding 模型,调一下接口,把返回的向量塞进库里,就结束了。
但如果你真的把它放进工程项目里,很快就会发现,向量化其实远远不只是“调一个模型”:
- 模型到底怎么选
- 中文、英文、长文本、多模态是不是都适合同一个模型
- 批量处理失败了怎么办
- API 限流和超时怎么办
- 结果怎么落盘复用
- 进度怎么反馈给用户
- 怎么避免每次都盲选模型、重复做一样的计算
所以在 RAG Pipeline Hub 里,我把向量化模块做成了一个独立工作台。
这篇文章,我就聊聊这个模块背后的设计思路。
项目地址:
- GitHub:
https://github.com/qingni/rag-pipeline-hub
为什么说向量化不只是调用 Embedding API
从技术定义上看,Embedding 确实就是把文本转换成高维向量。
但在真实 RAG 项目里,这一步承担的责任其实更重:
- 它决定了文本在向量空间里的语义表达方式
- 它直接影响后续索引维度、存储成本和搜索效果
- 它会决定检索是更偏速度,还是更偏语义精度
- 它会把前面的分块结果变成后面所有模块都依赖的数据资产
所以它不是一个“纯调用层”,而是一个承上启下的基础模块。
我现在越来越认同一个判断:
RAG 里很多“检索效果不好”的问题,根源并不一定在检索逻辑,也可能在向量表示质量。
如果 chunk 切得不错,但嵌入表达不合适,那后面的索引、召回、排序,其实也只是对不够理想的表示做补救。
真实项目里,向量化会遇到哪些问题
在这个模块里,我觉得至少有 5 类问题是绕不过去的。
1. 模型选择本身就不简单
不是所有文档都适合同一个 Embedding 模型。
比如:
- 中文知识库和英文知识库,侧重点可能不同
- 普通文本和术语密集文档,需求可能不同
- 纯文本和图文混合文档,更不该用同一套处理思路
- 对精度要求高和对成本敏感的场景,也不该一视同仁
如果把模型选择交给用户盲选,通常会很难用。
2. 批量处理很容易出问题
一两个 chunk 做向量化不难,但一份文档、一个分块结果、一个批次的块列表,情况就完全不同了。
你会开始碰到:
- 限流
- 超时
- 部分成功
- 某几条文本异常
- 网络抖动
如果没有一套明确的重试和失败处理机制,这一步很容易变成不稳定点。
3. 向量结果不能只存在内存里
如果向量化结束后直接把结果传给索引模块、不做持久化,那你会失去很多关键能力:
- 无法回看某次向量化到底用了什么模型
- 无法比较不同模型的结果
- 无法重复利用已有结果
- 无法做后续索引推荐和管理
4. 用户需要知道它现在执行到了哪一步
向量化不是一个用户愿意“盲等”的动作。
尤其在批量处理时,如果没有进度、状态和统计,用户体验会很差。
5. 模型接入方式需要可扩展
如果一开始把模型能力写死在某个服务里,后面每加一个模型,整个系统都会越来越难维护。
所以对我来说,向量化模块的核心问题从来不只是“怎么拿到向量”,而是:
怎么把模型选择、批量处理、失败恢复、结果复用和可观察性整合成一个稳定的工程模块。
我给这个模块定的目标
围绕上面这些问题,我给向量化模块定了几个明确目标:
- 支持多种 Embedding 模型
- 支持直接从分块结果进行向量化
- 支持批量处理和部分成功
- 支持限流、超时、网络错误的智能重试
- 支持结果持久化与历史查看
- 支持模型推荐,而不是完全靠用户盲选
- 支持后续索引模块直接复用结果
如果只用一句话概括,就是:
把“向量化”做成一个可复用、可观测、可扩展的向量资产生产模块。
当前模块在整条链路里的位置
这个模块位于文档分块之后、向量索引之前。
它处理的对象不再是原始文档,而是已经被切好的块级内容。
从流程上看,可以简单理解成:
选择分块结果
-> 选择模型
-> 配置批量处理参数
-> 执行向量化
-> 保存结果到数据库与 JSON
-> 供索引模块继续消费
也就是说,向量化模块的输出并不是“某次调用返回了一堆数组”,而是一份完整的向量化结果记录。
这份记录至少应该包含:
- 使用的模型
- 向量维度
- 成功数 / 失败数
- 向量结果内容
- 批量执行统计
- 对应的分块任务来源
这也是为什么我更愿意把它叫做“向量资产”而不是“向量结果”。
当前支持了哪些模型
这个项目里目前支持的核心模型包括:
bge-m3qwen3-embedding-8bqwen3-vl-embedding-8b
它们不是简单的“几个选项”,而是各自适合不同场景。
bge-m3
它更像一个稳健、通用、偏多语言的基础型选择。
适合:
- 多语言文档
- 通用检索场景
- 希望维度和成本比较平衡
qwen3-embedding-8b
它更偏高精度、高维度、长文本支持强。
适合:
- 中文场景
- 语义要求更高的检索
- 更看重表达能力而不是极致成本
qwen3-vl-embedding-8b
这是我觉得很有意思的一类能力,因为它把向量化从“纯文本模块”扩展到了多模态。
适合:
- 图文混合文档
- 图片较多的知识库
- 想为后续多模态检索留空间的场景
所以在这个项目里,我不想让模型选择变成“拍脑袋看名字”,而是尽量把不同模型的能力边界和适用场景明确下来。
为什么我要做模型推荐
如果系统里只有一个 Embedding 模型,那推荐当然没必要。
但一旦你支持多模型,问题就来了:
用户怎么知道该选哪个?
很多系统在这个地方会把责任完全推给用户。
表面看是“灵活”,实际上经常意味着:
- 用户不知道差异
- 用户不知道成本
- 用户不知道维度变化会影响什么
- 用户只会反复选自己最熟悉的那个
所以我在这个项目里加入了模型推荐能力。
推荐逻辑会考虑几类信号:
- 文档语言
- 文档领域特征
- 是否存在多模态内容
- 对精度和成本的权衡
最终给出的是:
- 推荐模型
- 推荐理由
- 可能的替代选项
这件事的价值不是“更智能”这么简单,而是:
让模型选择从经验主义,变成有依据的默认决策。
尤其是对第一次上手项目的人来说,这能显著降低试错门槛。
为什么批量处理、重试和部分成功是必须的
如果你只向量化一段文本,大多数接口看起来都很稳。
但在真实场景里,你经常面对的是:
- 一个文档对应几十个 chunk
- 一个任务里上百个 chunk
- 一次要处理很多历史结果
这时候问题就会快速出现:
- 某一批请求超时
- 某几条文本不合法
- 某次调用被限流
- 网络临时抖动
所以这个项目在向量化模块里专门做了几件事:
1. 批量处理
把大量 chunk 按批次送进模型,而不是一条条串行处理。
2. 智能重试
对限流、超时、网络错误采用指数退避重试,而不是立刻整批失败。
3. 部分成功
某些条目失败,不代表整个任务失败。
我希望系统能保留成功部分,并把失败项明确标出来,方便后续重试。
我很看重这一点,因为很多工程系统真正难做的,不是 happy path,而是:
当外部服务不稳定时,系统还能不能优雅地继续工作。
为什么结果一定要持久化
向量化还有一个经常被简化的问题:
很多项目做完 embedding 之后,直接进入索引,不保存中间结果。
这样虽然流程短,但长期来看会带来很多问题:
- 没办法复用已有向量化结果
- 没办法对比不同模型效果
- 没办法查看某次任务的具体统计
- 没办法给索引模块做清晰的输入管理
所以这个项目会把向量化结果同时保存到:
- 数据库
- JSON 文件
这么做的意义很大:
- 后续索引模块可以直接选择已有向量化结果
- 用户可以查看历史任务
- 可以对比模型之间的差异
- 可以为失败重试和结果审计提供依据
也就是说,向量化模块的输出不是“临时数据”,而是系统里的长期资产。
为什么进度反馈和历史记录不能省
我一直觉得,工程化系统和 demo 的差别,很多时候不在核心算法,而在这些“看起来不那么炫”的能力上。
比如向量化模块,如果没有:
- 执行面板
- 成功 / 失败统计
- 向量维度展示
- 历史列表
- 结果详情
用户会非常难用。
因为他根本不知道:
- 当前到底在执行什么
- 这次用了什么模型
- 成功了多少、失败了多少
- 结果是否可以继续拿去建索引
- 之前有没有做过类似任务
所以我在前端里单独做了:
- 文档选择器
- 模型选择器
- 参数配置面板
- 执行面板
- 结果展示
- 历史记录列表
这件事本质上不是“把 UI 做完整”,而是:
让向量化模块从一个后台调用,变成一个用户可理解、可回看、可管理的流程。
这一层和普通 Embedding demo 最大的区别是什么
如果只看最小实现,Embedding 模块可以非常简单:
输入文本 -> 调 API -> 返回向量
但在这个项目里,我更看重的是下面这些差异:
- 不只是支持一个模型,而是支持多模型和多模态
- 不只是人工选择,而是支持模型推荐
- 不只是单条调用,而是支持批量处理
- 不只是失败就报错,而是支持重试和部分成功
- 不只是内存返回,而是支持结果持久化
- 不只是一次执行,而是支持历史查看和复用
这也是我对“工程化向量化模块”的理解:
不是把模型接上,而是把向量资产的生产、管理和复用流程搭起来。
我对这个模块的一个核心判断
如果只让我总结一句话,我会说:
Embedding 不是把文本交给模型这么简单,而是在决定一套知识库最终会以什么语义形态进入检索系统。
这一步做得好,后面的索引和检索才有高质量基础。
这一步做不好,后面的优化就会越来越像“带着偏差继续优化偏差”。
所以在 RAG Pipeline Hub 里,我才会愿意把向量化单独做成一整套模块,而不是一个工具函数。
下一篇写什么
向量化解决的是“怎么把 chunk 变成向量”。
下一步的关键问题就是:
这些向量,应该怎么组织、存储、索引,才能真正支撑后续高效检索?
所以下一篇我会继续写:
《向量写进库里只是开始:RAG 向量索引与 Collection 管理实践》
项目地址:
- GitHub:
https://github.com/qingni/rag-pipeline-hub
如果这篇文章对你有帮助,欢迎:
- 点个
star - 提个
issue - 留言说说你最常用的 Embedding 模型
如果你也做过多模型选型、批量向量化、缓存复用或者多模态 Embedding,欢迎交流你踩过的坑。
我越来越觉得,很多系统在索引和检索之前,其实就已经在“向量表示”这一步拉开差距了。