text-embedding-v4 + LanceDB + OSS:从专业概念到销售知识库案例讲解

这一套组合,本质上是在搭建一个 RAG 知识库检索系统

所谓 RAG,可以简单理解为:

用户提问时,系统先从企业知识库里找出最相关的资料,再把这些资料交给大模型生成回答。

在这个链路里,text-embedding-v4LanceDBOSS 分别负责三件完全不同的事情:

text-embedding-v4:负责把文字变成向量
LanceDB:负责根据向量做相似度检索
OSS:负责存储向量、原文、元数据和索引文件

下面先讲专业概念,再讲销售知识库案例。


一、先理解几个核心专业概念

1. 什么是 Embedding?

Embedding,中文一般叫:

嵌入向量。

它的作用是:

把一段文字转换成一组数字。

例如:

客户觉得我们太贵了,怎么办?

经过 embedding 模型处理后,会变成类似这样的向量:

[0.021, -0.318, 0.772, ..., -0.056]

如果使用 text-embedding-v4 的默认配置,通常可以生成 1024 维向量

也就是说,一句话会被转换成:

1024 个浮点数

这些数字不是随机的,而是表达了这句话的语义特征。


2. 为什么文字要变成向量?

因为普通数据库只能做关键词匹配。

例如用户问:

客户嫌贵怎么接?

但文档里写的是:

当客户提出价格异议时,应优先强调 ROI 和长期价值,而不是直接降价。

这两句话没有完全相同的关键词,但语义非常接近。

传统搜索可能搜不到。

但是 embedding 可以识别:

客户嫌贵
价格异议
报价太高
竞品更便宜
预算不足

这些表达在语义上是接近的。

所以,向量的价值是:

让系统不再只看“字面是否一样”,而是看“意思是否接近”。


3. 什么是 text-embedding-v4?

text-embedding-v4 是阿里云百炼里的嵌入模型,属于 Qwen3-Embedding 系列。

它在这个系统里的定位是:

语义翻译官。

它不负责保存数据,也不负责搜索数据。

它只负责一件事:

中文 / 英文 / 混合文本
        ↓
语义向量

例如:

客户说我们比竞品贵 30%,怎么回复?

转换成:

[0.034, -0.217, ..., 0.891]

这个向量后续会交给 LanceDB 检索。


4. text-embedding-v4 常见关键参数

在销售知识库场景里,最重要的参数有几个。

1)dimensions:向量维度

text-embedding-v4 支持多种维度,例如:

2048、1536、1024、768、512、256、128、64

维度越高,表达能力通常越强,但也会带来几个问题:

存储更大
索引更大
检索更慢
成本更高

对于销售知识库这种通用语义检索场景,通常建议从:

1024 维

开始。

原因是:

精度够用
成本可控
索引体积不会过度膨胀

不要一开始就直接上 2048 维。数据量一旦变大,向量文件和索引文件都会明显膨胀。


2)batch size:批量条数

Embedding 接口通常支持批量输入。

你提供的信息里,text-embedding-v4 一次最多处理:

10 条文本

所以批量入库时,应该按 10 条一批调用接口。


3)单行 token 上限

单条文本不能无限长。

你提供的信息里,单行上限是:

8192 token

但在知识库里,不建议直接把一整篇 PDF 扔进去做 embedding。

更合理的做法是:

先切片,再 embedding

例如每段 200 到 500 字左右。


5. 什么是向量数据库?

向量数据库就是专门用来做这类查询的数据库:

给我一个向量
找出数据库里最相似的几个向量

这类查询叫:

向量相似度检索。

例如:

用户问题向量:
[0.034, -0.217, ..., 0.891]

数据库中有几万、几十万、几百万条资料,每条资料也都有一个向量。

向量数据库要做的事情是:

从海量向量中找出最相似的 Top-K 条

比如 Top 5:

第 1 条:客户价格异议处理话术
第 2 条:竞品贵 30% 的应对方式
第 3 条:ROI 价值证明案例
第 4 条:售后服务价值说明
第 5 条:总拥有成本 TCO 对比

6. LanceDB 是什么?

LanceDB 是一个开源向量数据库,底层基于 Lance 数据格式。

在这套系统里,它的定位是:

带索引能力的向量检索引擎。

它负责:

创建表
写入向量、原文、元数据
创建向量索引
执行向量相似度搜索
返回最相关结果

需要注意一个细节:

LanceDB 不是 OSS,OSS 也不是 LanceDB。

LanceDB 负责组织数据、管理索引、执行检索。

OSS 负责保存 LanceDB 产生的数据文件。

也就是说:

LanceDB 是干活的
OSS 是放东西的

7. 什么是 OSS?

OSS 是阿里云对象存储。

你可以把它理解成:

云上的大硬盘。

它适合存:

PDF
图片
视频
日志
数据文件
索引文件
.lance 文件

但是 OSS 不擅长做计算。

它不会帮你做:

向量相似度计算
Top-K 排序
语义搜索
索引召回

所以不要理解成:

把向量放进 OSS,然后 OSS 帮我搜

这是错误理解。

正确理解是:

LanceDB 负责搜索
OSS 负责存储

8. LanceDB 为什么可以接 OSS?

LanceDB 支持使用 S3 兼容存储作为后端。

阿里云 OSS 也提供 S3 兼容接口。

所以可以通过类似下面的方式连接:

s3://your-bucket/sales-kb/lancedb

但实际 endpoint 指向的是阿里云 OSS:

https://oss-cn-hangzhou.aliyuncs.com

这里有一个关键点:

LanceDB 不能直接识别 oss:// 协议,要通过 S3 兼容方式访问 OSS。

所以路径写的是:

s3://bucket/path

但 endpoint 配的是:

阿里云 OSS 的 S3 兼容 endpoint

9. 什么是索引?

如果没有索引,LanceDB 每次查询都可能需要和所有向量逐个比较。

假设有:

100 万条销售资料片段

用户问一句话,系统就要比较:

1 次问题向量 × 100 万条资料向量

这会很慢。

索引的作用是:

提前把向量组织起来,让查询时不用全量扫描。

可以把索引理解成图书馆目录。

没有目录时,你要一本一本找。

有目录时,你先定位到几个可能相关的书架,再从这些书架中找最相关的书。


10. IVF-PQ 是什么?

LanceDB 可以使用 IVF-PQ 这类向量索引。

不用先被名字吓住。

可以拆开理解。

IVF:先分区

IVF 的思路是:

先把所有向量分成很多个区

比如:

价格异议相关
售后服务相关
产品功能相关
合同条款相关
竞品对比相关

真实系统不是按文字标签这么分,而是按向量空间自动聚类。

查询时,系统先判断用户问题最接近哪些分区。

这样就不用查所有数据。


PQ:压缩向量

PQ 的作用是:

把原始高维向量压缩成更小的表示

为什么要压缩?

因为向量太大。

例如:

100 万条 × 1024 维

数据量会很大。

PQ 可以减少内存和存储压力,加快粗筛速度。

但是压缩会有一定精度损失,所以通常检索过程会分两步:

先用压缩向量快速粗筛
再用原始向量做精确重排

11. 什么是粗筛和精排?

这是向量检索里非常重要的概念。

粗筛

粗筛就是:

先快速找出一批可能相关的候选结果

例如从 100 万条中先筛出 100 条。

这个阶段追求的是:

不是绝对精确。


精排

精排就是:

对粗筛出来的候选结果重新精确计算相似度

例如从 100 条里选出最相关的 5 条。

这个阶段追求的是:

所以完整流程是:

100 万条
   ↓ 粗筛
100 条候选
   ↓ 精排
Top 5

二、三者的准确分工

现在把三个角色钉死。

1. text-embedding-v4:翻译官

它负责:

把人类语言翻译成向量语言

例如:

客户嫌贵怎么办?

变成:

[0.034, -0.217, ..., 0.891]

它不负责:

保存数据
创建索引
查询数据库
返回原文

2. LanceDB:检索管理员

它负责:

保存向量表结构
写入向量、原文、元数据
创建索引
根据 query vector 做相似度搜索
返回 Top-K 结果

它像一个仓库管理员。

它知道:

哪些资料在哪些区域
哪些资料和用户问题最接近
应该先查哪些候选
最后返回哪几条

3. OSS:数据仓库

它负责:

保存 LanceDB 的物理数据文件

例如:

向量数据
原文片段
元数据
索引文件
.lance 文件

它不负责:

语义理解
向量计算
索引调度
Top-K 排序

三、销售知识库案例:从 0 到 1 跑完整链路

1. 业务场景

公司有 5000 份销售资料,包括:

产品手册 PDF
销售 FAQ
竞品对比文档
客户异议处理话术
成功案例
培训 Markdown

现在销售员希望直接提问:

客户说我们比竞品贵 30%,怎么接?

系统返回最相关的 5 段原文,例如:

1. 当客户提出价格异议时,先不要急于降价,应引导客户关注 ROI。
2. 如果客户认为竞品更便宜,可以从售后响应、实施成功率、长期维护成本三个维度对比。
3. 对于预算敏感型客户,可以提供分阶段采购方案。

这就是一个典型销售知识库检索系统。


2. 阶段一:文档抽取

原始资料可能是:

PDF
Word
Markdown
HTML
Excel

第一步要做的是:

把这些文档里的正文抽出来

例如从 PDF 中抽出:

当客户说价格太高时,先不要急着降价,而是引导客户关注 ROI。

从 Markdown 中抽出:

Q:你们比竞品贵 30% 凭什么?
A:我们的售后响应是 2 小时内,竞品通常是 24 小时内。

3. 阶段二:文本切片

不要把一整篇文档直接丢给 embedding 模型。

应该先切片。

比如:

chunks = [
    {
        "text": "当客户说价格太高时,先不要急着降价,而是引导客户关注 ROI...",
        "source": "价格异议处理手册.pdf",
        "page": 12,
        "category": "销售话术"
    },
    {
        "text": "Q: 你们比竞品贵 30% 凭什么?A: 我们的售后响应是 2 小时...",
        "source": "竞品对比FAQ.md",
        "page": 3,
        "category": "竞品对比"
    }
]

每个 chunk 最好保存:

原文 text
来源 source
页码 page
分类 category
更新时间 updated_at
权限信息 permission

不要只存 text。

否则后续返回结果时,你不知道这段话来自哪里,也不好做权限控制。


4. 阶段三:调用 text-embedding-v4 生成向量

每个切片都要转换成向量。

示例代码:

from openai import OpenAI
import os

emb_client = OpenAI(
    api_key=os.getenv("DASHSCOPE_API_KEY"),
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1"
)

def embed(texts):
    """
    texts: list[str]
    建议每批最多 10 条
    """
    resp = emb_client.embeddings.create(
        model="text-embedding-v4",
        input=texts,
        dimensions=1024,
        encoding_format="float"
    )
    return [d.embedding for d in resp.data]

然后批量处理:

all_data = []

for i in range(0, len(chunks), 10):
    batch = chunks[i:i + 10]
    batch_texts = [c["text"] for c in batch]

    batch_vectors = embed(batch_texts)

    for chunk, vector in zip(batch, batch_vectors):
        all_data.append({
            "vector": vector,
            "text": chunk["text"],
            "source": chunk["source"],
            "page": chunk["page"],
            "category": chunk["category"]
        })

此时每条数据长这样:

{
    "vector": [0.034, -0.217, ..., 0.891],
    "text": "当客户说价格太高时,先不要急着降价...",
    "source": "价格异议处理手册.pdf",
    "page": 12,
    "category": "销售话术"
}

这一步结束后,销售资料已经从“普通文字”变成了“可以被语义检索的数据”。


5. 阶段四:LanceDB 连接 OSS

现在要把这些数据写入 LanceDB。

但我们不想把数据存在本地磁盘,而是存到阿里云 OSS。

所以连接时要使用 S3 兼容方式。

示例:

import lancedb

db = lancedb.connect(
    "s3://your-bucket/sales-kb/lancedb",
    storage_options={
        "region": "oss-cn-hangzhou",
        "endpoint": "https://oss-cn-hangzhou.aliyuncs.com",
        "aws_access_key_id": "你的OSS_ACCESS_KEY",
        "aws_secret_access_key": "你的OSS_SECRET_KEY",
    }
)

这里要注意:

s3://your-bucket/sales-kb/lancedb

只是 LanceDB 使用的 S3 兼容路径。

真正访问的是:

阿里云 OSS endpoint

也就是:

https://oss-cn-hangzhou.aliyuncs.com

6. 阶段五:创建 LanceDB 表并写入数据

创建表:

table = db.create_table(
    "sales_knowledge",
    data=all_data,
    mode="overwrite"
)

这一步之后,OSS 中会出现 LanceDB 相关数据文件。

可以理解为:

LanceDB 负责把向量、原文、元数据组织成表
OSS 负责保存这些表对应的物理文件

后续有新资料时,可以增量追加:

table.add(more_data)

7. 阶段六:创建向量索引

数据写入后,如果数据量较大,需要创建索引。

例如:

table.create_index(
    vector_column_name="vector",
    config=lancedb.Index.ivf_pq(
        num_partitions=256,
        num_sub_vectors=64,
    )
)

这里两个参数先简单理解。

num_partitions

表示把向量空间分成多少个分区。

可以粗略理解为:

仓库分成多少个区域

数据越多,通常分区数可以适当增加。

几万条数据,不需要太大。

几百万条数据,就要认真调参。


num_sub_vectors

这是 PQ 压缩相关参数。

可以粗略理解为:

把一个大向量拆成多少段来压缩

它影响:

索引大小
检索速度
召回效果

不要一开始乱调,建议先用中等配置跑通,再通过测试集评估效果。


8. 阶段七:销售员提问

销售员输入:

客户说我们比竞品贵 30%,怎么接?

系统第一步不是直接查数据库,而是先把这个问题也转换成向量。

注意:

入库文档用什么 embedding 模型,查询问题也要用同一个 embedding 模型。

示例:

def search(question: str, top_k=5):
    q_vector = embed([question])[0]

    results = (
        table
        .search(q_vector)
        .limit(top_k)
        .to_pandas()
    )

    return results[["text", "source", "page", "category"]]

调用:

hits = search("客户说我们比竞品贵30%,怎么接", top_k=5)

for i, row in hits.iterrows():
    print(f"{i + 1}. {row['text']}")
    print(f"来源:{row['source']} 第 {row['page']} 页")

四、完整查询链路拆解

现在把整个查询链路拆开。

用户问:

客户说我们比竞品贵 30%,怎么接?

第一步:text-embedding-v4 把问题转成向量

客户说我们比竞品贵 30%,怎么接?
        ↓
[0.034, -0.217, ..., 0.891]

这一步发生在阿里云百炼 embedding API。


第二步:LanceDB 拿到 query vector

LanceDB 收到的不是文字,而是:

1024 维 query vector

LanceDB 不理解“贵不贵”这个中文意思。

它只知道:

我要找和这个向量最接近的向量

第三步:LanceDB 用索引粗筛

LanceDB 会根据 IVF-PQ 索引快速判断:

这个问题最可能落在哪些向量分区

例如:

256 个分区
        ↓
挑出最接近的若干个分区
        ↓
在这些分区里快速扫描候选

这个阶段的目标是:

快点缩小范围

不是马上得到最终答案。


第四步:LanceDB 从 OSS 读取候选数据

粗筛后,LanceDB 可能得到几十条或几百条候选。

然后它会从 OSS 中读取这些候选对应的数据,包括:

原始向量
原文 text
来源 source
页码 page
分类 category

这里一定要理解:

不是 OSS 在做向量搜索,而是 LanceDB 决定要读哪些数据,然后去 OSS 取。

OSS 的角色只是:

你要哪个文件、哪段数据,我给你。

第五步:LanceDB 做全精度重排

候选数据取回来后,LanceDB 会做更精确的相似度计算。

例如:

候选 100 条
        ↓
精确计算相似度
        ↓
排序
        ↓
取 Top 5

最后返回:

最相关的 5 段销售资料

五、用一张图记住完整链路

销售员提问:
“客户说我们比竞品贵 30%,怎么接?”
        │
        ▼
text-embedding-v4
把问题转成 1024 维向量
        │
        ▼
LanceDB
拿 query vector 查 IVF-PQ 索引
        │
        ▼
LanceDB
粗筛出几十条 / 几百条候选
        │
        ▼
OSS
按需读取候选数据
        │
        ▼
LanceDB
对候选做全精度重排
        │
        ▼
返回 Top 5 原文片段

六、入库链路和查询链路要分开理解

很多小白会把这两个流程混在一起。

其实知识库有两条链路。


1. 入库链路

入库链路发生在:

准备知识库数据时

流程是:

PDF / Markdown / Word
        ↓
抽取正文
        ↓
文本切片
        ↓
text-embedding-v4 生成向量
        ↓
LanceDB 写表
        ↓
OSS 保存 .lance 数据文件
        ↓
LanceDB 创建索引

入库不是每次用户提问都做。

通常是:

新增资料
修改资料
定期同步

才做。


2. 查询链路

查询链路发生在:

用户每次提问时

流程是:

用户问题
        ↓
text-embedding-v4 生成问题向量
        ↓
LanceDB 查索引
        ↓
OSS 读取候选
        ↓
LanceDB 精排
        ↓
返回 Top-K 原文

用户每问一次,就会走一次查询链路。


七、为什么这套组合适合销售知识库?

1. 中文语义检索能力强

销售话术里经常有大量中文表达:

太贵了
预算不够
竞品便宜
价格异议
ROI 不明显
投入产出比低

这些话字面不同,但业务意思接近。

Embedding 模型可以把它们映射到接近的向量空间。


2. LanceDB 适合轻量级知识库场景

销售知识库一般不是每秒大量写入的交易系统。

它更像:

文档相对稳定
按天或按周更新
查询频繁

LanceDB 这种嵌入式向量数据库比较适合这种场景。


3. OSS 适合存放持续增长的知识库数据

销售资料会不断增加:

新产品资料
新竞品对比
新客户案例
新话术模板

如果全放本地磁盘,后期迁移、扩容、备份都会麻烦。

放到 OSS 后:

存储容量弹性更好
成本更低
多计算节点可以共享同一份数据

4. 存算分离,后期更容易扩展

OSS 负责存储。

LanceDB 运行在应用服务或检索服务里。

当查询并发上升时,可以扩多个检索服务实例:

检索服务 A
检索服务 B
检索服务 C
        ↓
共同访问同一个 OSS bucket

这就是存算分离的好处。

数据不用搬。

计算节点可以横向扩展。


八、几个容易踩坑的点

1. 不要直接用 oss://

LanceDB 连接 OSS 时,不是直接写:

oss://your-bucket/path

而是使用 S3 兼容方式:

s3://your-bucket/path

然后在 storage_options 里配置 OSS endpoint。


2. 入库和查询必须用同一个 embedding 模型

如果入库时用:

text-embedding-v4,1024 维

查询时也要用:

text-embedding-v4,1024 维

不要入库用 1024 维,查询用 768 维。

也不要入库用一个模型,查询用另一个模型。

否则向量空间不一致,检索效果会很差,甚至直接报错。


3. 文档不要切得太碎,也不要太长

切得太碎:

上下文不完整
答案容易断裂

切得太长:

噪声太多
embedding 表达不聚焦
召回不精准

销售知识库可以先从:

200 到 500 字一段

开始试。


4. 元数据一定要存

不要只存:

vector
text

建议至少存:

source:来源文件
page:页码
category:分类
updated_at:更新时间
permission:权限标识

否则后期会遇到问题:

不知道答案来自哪个文件
无法展示出处
无法按部门做权限隔离
无法做文档更新
无法删除旧版本

5. 不要以为向量检索等于最终答案

LanceDB 返回的是:

相关资料片段

不是完整回答。

如果你要让系统像智能客服一样回答,还需要把 Top-K 片段交给大模型,例如:

请根据以下资料回答用户问题,不要编造。

这一步才是完整 RAG 的生成环节。


九、最终用一句话总结

这套系统可以这样理解:

text-embedding-v4 是翻译官:
把销售问题和销售资料都翻译成向量。

LanceDB 是检索管理员:
根据向量相似度快速找到最相关的资料。

OSS 是仓库:
负责保存 LanceDB 的数据文件、原文、向量、元数据和索引。

销售员问:

客户嫌贵怎么办?

系统实际做的是:

先把问题转成向量
再用 LanceDB 查相似向量
再从 OSS 取回候选资料
最后返回最相关的销售话术

真正要记住的是:

OSS 只负责存储,LanceDB 负责检索,text-embedding-v4 负责把文字变成向量。

这三者合起来,才构成一个可落地的销售知识库语义检索系统。

发表回复

Your email address will not be published. Required fields are marked *.

*
*

关于我

全栈开发者与创业合伙人,拥有十余年技术实战经验。​AI编程践行者,擅长以产品思维打造解决实际问题的工具,如书签系统、Markdown转换工具及在线课表系统。信仰技术以人为本,专注氛围编程与高效协作。

2026 年 7 月
 12345
6789101112
13141516171819
20212223242526
2728293031