向量嵌入(Vector Embedding)是用于比较文本、图像、音频、实体等数据的学习型数值表示。它支撑语义搜索、推荐、聚类和 RAG,但单个向量不是稳定接口。向量含义取决于生成它的模型、Revision、任务模式、预处理、维度、归一化和距离函数。

本文只负责工程生命周期:模型选型、检索评测、索引、安全和迁移。术语页承担简明定义;语义搜索指南承担搜索系统设计;向量数据库指南承担存储引擎选型。

直接回答

生产级 Embedding 系统是一份版本化的转换与检索契约:

flowchart LR A["源内容 + 访问策略"] --> B["分块 + 预处理"] B --> C["模型 + 任务模式"] C --> D["向量 + 版本契约"] D --> E["精确 / ANN 索引"] E --> F["权限感知召回"] F --> G["可选重排"] G --> H["业务结果 + 评测"] H --> I["监控 + 迁移"]

只有属于兼容向量空间的向量才能比较。相似度分数不是概率,不是全局通用相关性阈值,也不能证明源文档有权被当前用户访问。完整契约必须作为数据保存,不能隐藏在 SDK 默认值中。

向量嵌入如何工作

嵌入模型学习一个映射函数,把输入转换为固定长度向量,使训练目标关心的关系可以在该空间中度量。模型不会保留源数据的所有属性,而是压缩特定信号;因此相关性、语言覆盖、领域行为和失败模式都受训练数据与任务设计影响。

静态、上下文与检索嵌入

表示类型 表示单元 什么会改变向量 常见用途
静态词向量 单词或 Token 词表中的学习结果 语言分析与传统 NLP
上下文 Token 向量 上下文中的 Token 周围序列 下游模型特征
句子或文档向量 一段文本 完整输入及 Pooling / 训练目标 检索、聚类、分类
多模态向量 文本、图像、音频或视频 模态编码器和对齐目标 跨模态检索与匹配

BERT 的基础 Hidden State 不会自动成为高质量句子检索向量。Sentence-BERT 通过 Bi-encoder 训练结构生成可以独立计算、再做相似度比较的句向量。现代检索模型在此基础上加入任务指令、非对称 Query/Document 编码、多语言训练或多种表示。

对称任务与非对称任务

语义相似度与信息检索不是同一目标。对称任务中的两个输入角色相同,例如比较两句是否互为改写;非对称检索则要求一个短问题找到一段更长、真正回答问题的内容。

Google Embeddings 官方文档通过 RETRIEVAL_QUERYRETRIEVAL_DOCUMENTSEMANTIC_SIMILARITY 等 Task Type 明确区分这些目标。其他模型可能使用 Prompt、Prefix,或单独的 encode_query / encode_document 方法表达同一契约。建立索引和处理查询时,必须持续遵循目标模型的文档约定。

相似度函数也是契约的一部分

余弦相似度、点积和欧氏距离只有在归一化满足特定条件时才会产生等价排序。

对向量 xy

text
cosine_similarity(x, y) = (x · y) / (||x|| ||y||)
dot_product(x, y)       = x · y
euclidean_distance(x,y) = ||x - y||₂

两个非零向量都完成 L2 归一化后,余弦相似度等于点积,欧氏距离平方则等于 2 - 2 * cosine_similarity,所以在精确计算下排序一致。未经归一化时,向量模长会改变点积和欧氏距离排序。

应使用模型推荐的距离函数,并为索引配置匹配的 Operator。OpenAI 当前指南在文本搜索中使用余弦相似度,并要求手工截断维度后重新归一化;这只是该 Provider 的具体用法,不是所有模型的通用规则。即使 Provider 默认处理归一化,也应把它显式写入契约。

不要复制其他数据集的相似度阈值。模型、语言、Chunk、语料、任务和 Hard Negative 变化后,分数分布都会移动。阈值应从带标签样本校准,并为模糊分数保留拒答或升级路径。

定义可复现的 Embedding Contract

每条向量都应保存足够信息,使系统能够复现、比较、迁移和删除:

json
{
  "sourceId": "policy-42",
  "sourceVersion": "sha256:...",
  "chunkId": "policy-42#section-7",
  "tenantId": "tenant-acme",
  "embeddingContract": {
    "modelId": "provider/model-name",
    "modelRevision": "immutable-revision",
    "task": "retrieval_document",
    "preprocessingVersion": "chunk-v4",
    "dimensions": 768,
    "normalization": "l2",
    "distance": "cosine"
  },
  "indexedAt": "2026-01-15T09:30:00Z",
  "accessPolicyVersion": "acl-v12"
}

最小契约包括:

  • 不可变模型 Revision,而不是可漂移的 Alias
  • Query / Document 的任务模式或指令
  • Tokenization、清洗、分块和截断版本
  • 输出维度与 Dtype
  • 归一化方式与距离函数
  • 源内容哈希和生命周期状态
  • 租户、Owner 和访问策略引用
  • 索引代次和摄取时间

这不是可选的运维元数据。缺少契约时,重建任务无法判断哪些向量已经过期,查询服务可能悄悄使用错误模型,数据删除也无法证明所有派生记录已经清理。

如何选择 Embedding 模型

不存在对所有任务都最优的向量嵌入模型。MTEB 原论文横跨检索、重排、语义文本相似度、分类、聚类、Pair Classification、Bitext Mining 和 Summarization,结论之一就是没有单一方法统治全部任务。

公开 Benchmark 只用于筛选候选模型,最终选择必须来自真实负载。

决策维度 需要回答的问题 忽略后的失败
任务目标 检索、相似度、聚类、分类、代码还是多模态? STS 强模型可能不擅长检索
Query/Document 非对称性 是否需要 Prefix、Instruction 或 Task Type? 查询和文档落入不匹配区域
语言与领域 哪些语言、术语、代码和实体最重要? 中文、混合语言或专业语料静默退化
输入行为 Token 上限、截断、Pooling 和长文档策略? 关键证据被截断或稀释
输出契约 维度、Dtype、归一化和原生缩短能力? 索引不兼容或排序漂移
部署方式 API、自托管、硬件、Batch 和吞吐? 成本、延迟或可靠性不达标
治理要求 数据驻留、许可证、留存和 Provider 日志? 合规或隐私失败
生命周期 Revision、可用性和迁移机制? 被迫重建且无法回滚

不要把某一时点的模型排行榜复制进常青文章。排名、模型可用性、价格和硬件行为都会变化。可审计的选型记录应保存模型 Revision、数据集版本、评测指标、运行测量和最终决策。

构建检索评测集

Embedding 评测必须覆盖真实检索任务,而不是只比较句子相似度。先收集真实查询或经过授权的合成查询,再标注哪些文档与查询相关。

至少包含:

  1. 直接关键词匹配和语义改写
  2. 缩写、别名、拼写错误和低频实体
  3. 答案不会重复问题措辞的问答
  4. 关键词相似但实际回答不同问题的 Hard Negative
  5. 业务涉及的中文、多语言和 Code-switching 查询
  6. 权限受限文档及其预期排除结果
  7. 语料中没有答案、应返回空结果的情况
  8. 更新、删除和不同 Source Version 的内容

使用开发集调模型与索引参数,使用独立测试集完成最终比较。近重复文档不能跨集合泄漏。

检索指标

没有一个指标可以单独证明系统有效:

  • Recall@k: 相关内容是否至少有一条进入候选集,适合评估重排之前的召回。
  • MRR: 第一条相关结果出现得有多靠前。
  • nDCG@k: 当相关性存在多个等级时,评估整体排序。
  • Precision@k: 返回结果中有多少真正相关。
  • 无答案准确性: 语料没有答案时,系统是否停止返回相似但错误的内容。
  • 业务结果: 搜索、推荐或 RAG 回答是否基于有效证据完成任务。

编码延迟、检索延迟、索引构建时间、内存、存储和成本应与相关性分开测量。更快但无法召回必要证据的模型,不是优化。

可运行的本地评测

下面的 Sentence Transformers 5.x 示例评测真实召回行为,而不是只打印一串没有解释的向量。安装命令为 pip install "sentence-transformers>=5,<6" "numpy>=2,<3"。示例模型用于演示评测闭环,不代表对生产选型的推荐。

python
from __future__ import annotations

from dataclasses import dataclass

import numpy as np
from sentence_transformers import SentenceTransformer


MODEL_ID = "sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2"
# 生产环境必须固定经过批准的模型 Revision。


@dataclass(frozen=True)
class Document:
    id: str
    text: str


DOCUMENTS = [
    Document("refund", "商品购买后 30 天内可以申请退款。"),
    Document("password", "忘记密码时,请在账户安全设置中发起重置。"),
    Document("shipping", "国际配送通常需要经过海关处理。"),
    Document("invoice", "电子发票可以从账单历史页面下载。"),
]

QUERIES = {
    "q1": ("订单最晚多久可以退?", {"refund"}),
    "q2": ("报税用的购买凭证在哪里下载?", {"invoice"}),
    "q3": ("忘记登录密码怎么办?", {"password"}),
}


def evaluate_recall_at_k(k: int = 2) -> float:
    if k < 1 or k > len(DOCUMENTS):
        raise ValueError("k 必须在 1 和文档数量之间")

    model = SentenceTransformer(MODEL_ID)
    document_vectors = np.asarray(
        model.encode(
            [document.text for document in DOCUMENTS],
            normalize_embeddings=True,
            show_progress_bar=False,
        )
    )

    hits = 0
    for query_id, (query, relevant_ids) in QUERIES.items():
        query_vector = np.asarray(
            model.encode(query, normalize_embeddings=True)
        )
        scores = document_vectors @ query_vector
        top_indices = np.argsort(-scores)[:k]
        retrieved_ids = [DOCUMENTS[index].id for index in top_indices]
        hit = bool(relevant_ids.intersection(retrieved_ids))
        hits += int(hit)
        print(f"{query_id}: top{k}={retrieved_ids}, hit={hit}")

    return hits / len(QUERIES)


try:
    recall = evaluate_recall_at_k(k=2)
    print(f"Recall@2={recall:.3f}")
except Exception as error:
    raise RuntimeError(f"Embedding 评测失败: {error}") from error

这个 Fixture 只用于展示最小闭环。生产评测需要读取版本化 Query-Document 标注,记录模型 Revision,并使用置信区间或逐查询回归分析比较候选系统。不能用同一组样本同时调参和批准发布。

检索架构:精确、ANN、混合与重排

Embedding 只是检索信号之一,不等于完整搜索系统。

阶段 作用 优势 主要权衡
精确向量检索 计算全部已授权向量 可作为 Recall 参考基线 成本随语料和查询量增长
近似最近邻(ANN) 搜索 HNSW、IVF 等索引 大规模下延迟更低 用 Recall 换速度和内存
关键词检索 匹配术语、字段、ID 和低频实体 精确词汇命中 难以理解改写
混合检索 合并关键词与向量候选或分数 同时覆盖语义和精确匹配 需要融合策略与评测
Cross-encoder 重排 联合计算 Query-Candidate 相关性 提升最终排序 无法承受全库逐对计算

Sentence Transformers 官方文档给出的常用结构是 Retrieve-and-Rerank:Bi-encoder 或关键词系统先召回候选,再由 CrossEncoder 联合计算 Query 与候选。候选阶段必须保证足够的 Recall@k,因为重排器无法找回从未进入候选集的相关文档。

Google BigQuery 和 pgvector 都同时提供精确与近似搜索路径,这也说明专用向量数据库并非前置条件。优先选择能满足过滤、一致性、规模、恢复和运维要求的最简单存储。

权限感知的 pgvector Schema

下面的结构把 Embedding Contract 与向量一起保存。固定的 vector(384) 只属于当前索引代次;维度不同的模型应建立独立代次或表。

sql
CREATE EXTENSION IF NOT EXISTS vector;

CREATE TABLE document_embeddings_v1 (
  tenant_id text NOT NULL,
  document_id text NOT NULL,
  chunk_id text NOT NULL,
  source_version text NOT NULL,
  model_id text NOT NULL,
  model_revision text NOT NULL,
  preprocessing_version text NOT NULL,
  access_groups text[] NOT NULL,
  embedding vector(384) NOT NULL,
  PRIMARY KEY (
    tenant_id,
    document_id,
    chunk_id,
    model_id,
    model_revision,
    preprocessing_version
  )
);

CREATE INDEX document_embeddings_v1_tenant
  ON document_embeddings_v1 (tenant_id);

CREATE INDEX document_embeddings_v1_access_groups
  ON document_embeddings_v1 USING gin (access_groups);

CREATE INDEX document_embeddings_v1_embedding_hnsw
  ON document_embeddings_v1
  USING hnsw (embedding vector_cosine_ops);

SELECT document_id, chunk_id, source_version,
       1 - (embedding <=> $3::vector) AS cosine_similarity
FROM document_embeddings_v1
WHERE tenant_id = $1
  AND access_groups && $2::text[]
ORDER BY embedding <=> $3::vector
LIMIT $4;

该 Predicate 只是示例,不是完整授权边界。必须由可信服务确定租户身份,使用数据库 Row-Level Security 或等价服务策略,并禁止客户端直接声明可访问组。

近似索引可能只扫描部分候选后再应用过滤,导致符合租户或元数据条件的结果不足。必须用真实 Filter 评测 Recall;再按基数选择分区、Partial Index、Iterative Scan,或扩大重排前候选集。

维度与存储规划

维度更高不等于效果更好。维度属于模型训练与输出契约,不能通过比较无关模型的数字大小推断质量。

稠密浮点向量的基础容量估算为:

text
vector_bytes = item_count * dimensions * bytes_per_element

完整预算还要加上 ID、Metadata、索引图或质心、Allocator 开销、副本、快照、WAL,以及迁移期间并存的旧索引代次。

只有模型经过相应训练并明确支持时,才能使用原生缩短能力。例如 OpenAI 当前第三代 Embedding 模型提供基于 Matryoshka 类训练的 dimensions 参数。这不意味着其他模型也可以任意截断。

PCA 是另一套需要学习的后处理变换。它需要代表性拟合数据、持久化 Projection Version、归一化规则、评测和全量重建索引。旧版文章曾用 6 条样本拟合 256 个 PCA Component,这段代码无法运行,因为 PCA Component 数不能超过样本可用秩。

量化可以减少存储和索引内存,也可能改变邻居排序。应把量化候选召回与全精度重排作为一套系统共同评测。

安全迁移 Embedding 模型

不能把不同 Embedding Contract 的向量混入同一个搜索空间。安全迁移使用并行索引代次:

  1. 冻结旧契约: 记录模型 Revision、任务模式、预处理、维度、归一化和距离函数。
  2. 定义新契约: 写明迁移假设和批准指标。
  3. 回填独立索引: 保留 Source ID、Version、访问策略和删除状态。
  4. 双模型编码查询: 每个查询分别使用与各索引匹配的模型。
  5. Shadow 对比: 不影响用户地比较质量、延迟、失败和 Top Result 变化。
  6. Canary 流量: 给有限用户放量,并保留回滚。
  7. 切换读写: 在批准的回滚窗口内保持旧索引只读。
  8. 安全退役: 传播删除,清理旧向量与快照,并记录完成证据。

原地覆盖会失去结果对比和回滚能力。后台任务若使用可漂移的模型 Alias,也可能把多个向量空间写入所谓“新索引”。

安全与数据生命周期

Embedding 是派生数据,不是匿名数据。OWASP 把未授权访问、跨上下文泄漏、Embedding Inversion 和数据投毒列为 RAG 向量与嵌入风险。

向量化前后都要实施控制:

  • 对数据源做身份校验,拒绝隐藏或不可信摄取内容。
  • 每个 Chunk 保留 Tenant、Owner、分类和 Provenance。
  • 在检索阶段执行授权,不能等文本返回后再过滤。
  • 共享 ANN 行为可能造成不可接受的泄漏或 Recall 干扰时,隔离不兼容租户。
  • 加密传输和存储,限制原始向量导出和批量近邻查询。
  • 把源数据更新、Legal Hold、过期和删除传播到向量、索引、副本、缓存和评测 Fixture。
  • 记录检索 ID 和策略决策,但不要把敏感正文复制到遥测中。
  • 测试数据投毒、跨租户查询、过期 ACL、已删除内容和检索段落中的 Prompt Injection。

相关性不能授予权限。即使私有文档与查询高度相关,也不能进入未授权候选集。

常见失败模式

失败 根因 工程处理
全站共用一个相似度阈值 分数分布随模型、语言、Query 和语料变化 按任务校准并保留拒答
一个索引混入多个模型 Revision Alias 漂移或回填不完整 版本化契约并隔离索引代次
Query 与 Document 模式用反 忽略非对称指令 分别测试并保存任务模式
ANN 很快但丢失证据 只调速度,没有精确 Recall 基线 在带标签查询上对比精确搜索
Metadata Filter 后没有候选 近似扫描后才过滤 调大候选深度、分区或 Iterative Scan
Dense Search 找不到 ID 和专名 语义模型弱化精确词项 增加关键词检索并融合候选
相关候选排序过低 Bi-encoder 压缩了 Query-Document 交互 添加 Reranker 并测量 Uplift
返回过期或已删除内容 摄取链路没有 Source Version 与删除传播 建立可审计生命周期 Journal
跨租户召回 向量检索后才做授权 在暴露候选前过滤或分区
公开 Benchmark 胜出但线上失败 评测任务与真实负载不一致 用版本化领域测试集做发布门禁

生产监控

质量、运行与生命周期指标应分别监控:

  • Recall@k、MRR、nDCG@k、无答案准确性和 Reranker Uplift
  • 下游回答有据性、点击、转化或问题解决结果
  • 按模型与索引代次拆分的编码和检索 P50/P95 延迟
  • 授权过滤前后的候选数量
  • 索引覆盖率、Stale Vector 延迟、Embedding 失败和删除积压
  • Query 与 Document 的语言和领域漂移
  • ANN 相对抽样精确搜索的 Recall
  • 存储、内存、吞吐、Provider Usage 与重建成本

除服务故障外,还要对契约不匹配告警。HTTP 请求成功但使用了错误模型 Revision,属于数据损坏事件。

参考资料

总结

向量嵌入只有进入受控且经过评测的检索系统后才有价值。模型选择应来自真实任务证据;系统必须保存完整向量空间契约,评测精确与近似 Recall,按负载组合关键词检索和重排,并把授权与删除纳入索引生命周期。

长期稳定的工程单元不是一组浮点数,而是源数据、模型行为、检索策略、评测证据和生命周期之间的版本化关系。

相关资源