嵌入生成与向量化

向量化是 RAG 系统的核心环节,它将文本转换为高维向量表示,使语义相似的内容在向量空间中距离更近。本章将详细介绍如何选择合适的嵌入模型,包括千问和 Gemini 等主流模型的对比分析。我们将深入探讨文档向量化处理的完整流程,从 API 调用到批量处理优化,并重点讲解如何将生成的向量高效存储到 Cloudflare Vectorize 数据库中。通过掌握这些技术,您能够构建出高质量的向量索引,为后续的相似度检索奠定基础。

选择合适的嵌入模型

生成执行向量化的核心在于选择合适的嵌入模型:

  • 千问(Qwen)提供的模型:特别适合中文内容,支持丰富的上下文理解与生成。
  • Gemini 的通用模型:适合多模态内容,灵活应对多种格式的信息。

模型对比指南:

  • 如果你的内容主要是简体中文,或者需要良好的本地化处理,建议使用千问。
  • 若需处理多语言内容,或需从多个数据模态中提取信息,建议使用 Gemini。

常用嵌入模型及其维度

不同的嵌入模型输出不同维度的向量,创建 Vectorize 索引时需要与模型维度匹配:

模型/嵌入 API输出维度最大行数单行最大处理 Token 数支持语种单价(每百万输入 Token)
千问 text-embedding-v4
(Qwen3-Embedding 系列)
2,048、1,536、1,024(默认)、768、512、256、128、64108,192中文、英语、西班牙语、法语、葡萄牙语、印尼语、日语、韩语、德语、俄罗斯语等 100+ 主流语种$0.07(约合 0.0005 元/千 Token,按 1 美元 ≈ 7 元人民币换算)
Workers AI - @cf/baai/bge-base-en-v1.51,024-512英文文本$0.20
OpenAI - ada-0021,536--多语言文本$0.10
Gemini - gemini-embedding-001128~3,072(推荐:768、1,536、3,072)-2,048多语言文本$0.15
注意
此处记录的模型信息截止 2025 年 7 月 31 日,模型维度和 API 的支持情况因模型而异,请根据实际情况进行选择,且模型价格可能会随时间调整,建议在使用前查看最新的官方文档。

需要特别说明的是表格中的“最大行数”是指每次调用嵌入 API 时,允许同时提交的文本行(chunk)数量上限,并不是限制整个文件可以被拆分成多少份。比如千问模型的最大行数为 10,表示你可以一次性批量提交最多 10 个文本块(chunk)进行嵌入生成。如果你的文件被拆分成 30 个 chunk,可以分 3 次批量提交,每次不超过 10 个。单行最大处理 Token 数则是指每个 chunk 的最大长度限制,超出部分需要进一步切分。最大行数限制的是每次批量请求的 chunk 数量,不是文件总共能拆分多少 chunk。

文档向量化处理

在文档中,文本向量化是一项重要的操作:

  • API 调用与配置:根据 wrangler.toml 中的配置,调用嵌入模型 API。
  • 向量化步骤
    1. 加载文档片段和相关元数据。
    2. 调用嵌入 API,将文本片段转换成向量。
    3. 将向量存储在 Cloudflare Vectorize 中。

示例代码:

async function embedText(text: string, apiKey: string, apiUrl: string): Promise<number[]> {
  const response = await fetch(apiUrl, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${apiKey}`
    },
    body: JSON.stringify({ text })
  });

  if (!response.ok) {
    throw new Error('API 请求失败');
  }

  const result = await response.json();
  return result.vector;
}

批量向量化处理

为了提高效率,通常会批量处理文档块。这段代码实现了根据不同的嵌入提供者(如 Cloudflare Vectorize)调用相应的 API,获取文本的嵌入向量。

async function batchEmbedTexts(texts: string[], provider: string, config: any): Promise<number[][]> {
  switch(provider) {
    case 'qwen':
      // 调用千问批量嵌入 API
      const response = await fetch(`${config.baseUrl}/embeddings`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'Authorization': `Bearer ${config.apiKey}`
        },
        body: JSON.stringify({ 
          input: texts,
          model: config.embeddingModel 
        })
      });
      
      if (!response.ok) {
        throw new Error(`嵌入 API 请求失败:${response.status}`);
      }
      
      const data = await response.json();
      return data.data; // 返回嵌入向量数组
      
    default:
      throw new Error(`不支持的嵌入提供者:${provider}`);
  }
}

主要流程

  1. 通过 fetch 发送 POST 请求,将输入文本和模型参数传递给嵌入服务。
  2. 检查响应状态码,如果请求失败则抛出异常,便于后续错误处理。
  3. 请求成功后解析 JSON 响应,并返回嵌入向量数组(data.data)。
  4. 如果传入了不支持的 provider,会抛出异常提醒开发者。

注意事项

  • 需要确保 config.embeddingModel 配置正确,且 texts 为待处理的文本数组。
  • 嵌入向量通常用于后续的向量检索或相似度计算场景。

向量存储与索引

在 RAG 系统中,向量的高效存储与检索是实现智能问答和内容发现的基础。通过 Cloudflare Vectorize,可以将生成的文本向量安全地存储在向量数据库中,并利用其对余弦相似度的高效支持,实现快速的相似内容检索。为了保证检索性能和系统的可扩展性,通常需要对大规模数据集进行合理切分,使每次检索请求都能在有限的数据范围内完成,从而提升响应速度。此外,随着文档内容的不断更新,索引也需要定期维护和刷新,以确保检索结果始终反映最新的数据状态。通过科学的存储与索引策略,可以为后续的语义搜索和智能推荐打下坚实的基础。

Vectorize 向量格式

在将文本向量存储到 Cloudflare Vectorize 之前,需要遵循其特定的数据结构。下面的 TypeScript 接口定义了向量对象的基本格式,包括唯一标识符、向量值数组、可选的元数据和命名空间字段。

interface VectorizeVector {
  id: string;           // 向量唯一标识符
  values: number[];     // 向量值数组
  metadata?: Record<string, any>; // 可选元数据
  namespace?: string;   // 可选命名空间
}

说明id 字段用于唯一标识每个向量,values 存储实际的嵌入向量数据。metadata 可用于存储检索和过滤所需的附加信息,namespace 便于对不同类型或语言的向量进行分组管理。

向量元数据设计

为了提升检索效率和灵活性,建议为每个向量对象设计合理的元数据结构。以下示例展示了常用的元数据字段,涵盖了文档定位、内容描述、分块信息和标签等。

const vectorMetadata = {
  url: "/blog/article-title",        // 文档 URL
  title: "文章标题",                 // 文档标题
  sourcePath: "content/zh/blog/article-title/index.md", // 源文件路径
  chunkIndex: 0,                     // 块索引
  chunkTitle: "章节标题",            // 块标题(如果有)
  wordCount: 256,                    // 字数统计
  language: "zh",                    // 语言标识
  tags: ["技术", "AI", "RAG"],        // 标签
  text: "文章内容片段",                // 文本内容
  createdAt: "2025-07-31T10:00:00Z", // 创建时间
  updatedAt: "2025-07-31T10:00:00Z"  // 更新时间
};

说明:实际应用中,推荐至少包含 urltitlesourcePath 等字段。这些元数据有助于后续的向量检索、过滤和内容追溯。

批量上传到 Vectorize

在处理大规模文档时,批量上传向量可以显著提升效率。下面的代码演示了如何将多个向量对象分批上传到 Cloudflare Vectorize,每批最多支持 1000 个向量。

async function uploadToVectorize(vectors: VectorizeVector[], vectorize: Vectorize) {
  // Vectorize 支持最多 1000 个向量的批量操作
  const batchSize = 1000;
  
  for (let i = 0; i < vectors.length; i += batchSize) {
    const batch = vectors.slice(i, i + batchSize);
    
    try {
      // 使用 upsert 确保更新已存在的向量
      const result = await vectorize.upsert(batch);
      console.log(`上传批次 ${Math.floor(i/batchSize) + 1} 成功,处理了 ${batch.length} 个向量`);
    } catch (error) {
      console.error(`上传批次 ${Math.floor(i/batchSize) + 1} 失败:`, error);
    }
  }
}

注意事项

  • 建议根据数据量合理设置批次大小,避免单次请求过大导致超时或失败。
  • 使用 upsert 方法可以自动更新已存在的向量,便于数据同步和维护。

命名空间管理

合理使用命名空间(namespace)有助于对不同语言、内容类型或业务场景的向量进行分组管理。以下示例展示了如何为向量批量分配命名空间。

// 按语言分组
const chineseVectors = vectors.map(v => ({
  ...v,
  namespace: "zh"
}));

const englishVectors = vectors.map(v => ({
  ...v,
  namespace: "en"
}));

// 按内容类型分组
const blogVectors = vectors.map(v => ({
  ...v,
  namespace: "blog"
}));

const docsVectors = vectors.map(v => ({
  ...v,
  namespace: "docs"
}));

说明:通过为向量指定不同的 namespace,可以在检索时快速筛选目标数据集,提升查询效率和系统可扩展性。

向量更新策略

随着文档内容的不断变化,向量数据也需要同步更新。常见的向量更新策略包括增量更新、全量更新和混合策略。下面的代码示例演示了如何实现增量更新,仅针对发生变化的文档块生成和上传新的向量。

// 增量更新示例
async function updateDocumentVectors(
  documentPath: string, 
  vectorize: Vectorize,
  embeddingFunction: Function
) {
  // 1. 处理文档生成新的向量块
  const newChunks = await processDocument(documentPath);
  
  // 2. 生成向量
  const vectors = await Promise.all(
    newChunks.map(async (chunk, index) => {
      const embedding = await embeddingFunction(chunk.content);
      return {
        id: generateVectorId(documentPath, index),
        values: embedding,
        metadata: {
          ...chunk.metadata,
          updatedAt: new Date().toISOString()
        }
      };
    })
  );
  
  // 3. 使用 upsert 更新向量数据库
  await vectorize.upsert(vectors);
}

实践建议

  • 增量更新适用于内容变动频繁的场景,可减少不必要的全量重算。
  • 建议结合文档的更新时间戳和内容哈希,判断是否需要重新生成向量。
  • 定期执行全量更新,可确保整体数据一致性,适用于大规模内容重构或模型升级场景。

通过上述方法,可以高效地将生成的向量存储到 Cloudflare Vectorize,支撑 RAG 系统的高性能语义检索和内容发现能力。

文章导航

独立页面

这是书籍中的独立页面。

书籍首页