Embedding 降级方案:Hashing Embedding

用字符 n-gram + SHA256 hash 生成确定性向量的降级方案:零模型下载、零 API 调用,适用于开发阶段和离线环境。讨论语义质量和适用边界。

#type / concept #status / growing #tech / ai

[!info] related notes

Embedding 降级方案:Hashing Embedding

核心问题

开发 RAG 系统时,embedding 的选择是个前置问题:

方案优点缺点
OpenAI API质量最好需要 API Key、网络、有成本
sentence-transformers质量好、免费需要下载模型(几百 MB)、首次慢
Hashing零依赖、零成本、确定性语义质量差

Hashing embedding 的定位是开发阶段的降级方案:让你在没有 API Key、没有 GPU、没有网络的情况下也能跑通 RAG 管线。

算法原理

def hashing_embedding(text: str, dimension: int = 1536) -> list[float]:
    # 1. 归一化
    text = " ".join(text.lower().split())

    # 2. 提取特征:完整词 + 字符 n-gram
    features = []
    for token in text.split():
        features.append((token, 1.0))  # 完整词

    for n in (1, 2, 3):  # unigram, bigram, trigram
        for i in range(len(text) - n + 1):
            gram = text[i:i+n]
            features.append((gram, 1.0 / n))  # n 越大权重越低

    # 3. SHA256 hash 映射到向量桶
    vector = [0.0] * dimension
    for feature, weight in features:
        digest = hashlib.sha256(feature.encode()).digest()
        bucket = int.from_bytes(digest[:8], "big") % dimension
        sign = 1.0 if digest[8] % 2 == 0 else -1.0
        vector[bucket] += sign * weight

    # 4. L2 归一化
    norm = math.sqrt(sum(v*v for v in vector))
    return [v/norm for v in vector] if norm > 0 else vector

核心思路:每个字符 n-gram 通过 SHA256 hash 映射到向量的一个位置,正负号随机。相同文本永远映射到相同位置(确定性)。

语义质量对比

能力Hashingsentence-transformersOpenAI
相同文本匹配✅ 完全一致
相似文本区分⚠️ 有一定区分度✅ 好✅ 很好
同义词识别
跨语言✅(multilingual)
语序理解部分

关键限制:“肩膀疼” 和 “肩部疼痛” 在 hashing 下可能完全不同,但在真实模型下很接近。

什么时候用

场景推荐方案
开发调试、跑通管线Hashing
知识库术语集中、查询和文档用同一套词Hashing 勉强够用
需要同义词、跨语言sentence-transformers
生产环境至少 sentence-transformers,最好 OpenAI

切换策略

EMBEDDING_PROVIDER=hashing          # 开发:零下载
EMBEDDING_PROVIDER=local_transformer # 本地模型
EMBEDDING_PROVIDER=openai            # API 调用

代码中统一接口:

async def generate(self, text: str) -> list[float]:
    if self.provider == "hashing":
        return self._hash_embedding(text)
    if self.provider == "local_transformer":
        return self._local_model.encode([text])[0].tolist()
    return await self._api_generate(text)

常见错误

生产环境还在用 hashing

# ❌ 部署时忘了切到真实模型
EMBEDDING_PROVIDER=hashing  # 检索质量很差

hashing 和真实模型的 embedding 混用

# ❌ 用 hashing 入库,用 OpenAI 检索(或反过来)
# 向量空间完全不同,相似度没有意义

切换模型后必须重新生成所有 embedding

创建于 2026/6/25 更新于 2026/6/25