嵌入模型与语义检索

通过兼容 OpenAI 与 Ollama 的 API 在本地生成归一化向量。Snowflake Arctic Embed 与 MiniLM 编码文本/代码;EmbeddingGemma 2 还可把文本、图像、音频与视频组合为一个向量。以下反映当前源码;旧发布包可能未包含这些功能。

模型

模型输出维度输入窗口池化
Snowflake Arctic Embed L v2.0 Q8_010248192CLS
all-MiniLM-L6-v2 Q8_0384此 GGUF 为 512均值
EmbeddingGemma 2 Q8_0 / BF16768;MRL 512 / 256 / 1288192,含特殊与媒体 token均值 + 学习得到的投影

架构键支持 bert、nomic-bert 与 gemma-embedding2;Vulkan 嵌入尚未实现。BERT/XLM-R 路径支持纯 C# cpu 与原生 ggml_cpu / ggml_metal / ggml_cuda。EmbeddingGemma 2 另有直接 mlx 和 cuda 实现,共六个后端;实现可用不代表六后端多模态质量或性能对等。

启动服务

一个进程驻留一个嵌入编码器;聊天服务另用端口。config/embeddinggemma2.json 提供固定 SHA-256 的原生 CPU 文本配方:

dotnet run --project TensorSharp.Server.Host -- \
  --config config/embeddinggemma2.json --host 127.0.0.1 --port 5000 --no-webui

EmbeddingGemma 2 的图像/音频/视频需要同版本媒体投影器;省略 --mmproj 即只加载文本。使用固定版本文件:

mkdir -p models/embeddings
curl -fL --retry 3 -o models/embeddings/embeddinggemma-2-Q8_0.gguf \
  https://huggingface.co/unsloth/embeddinggemma-2-GGUF/resolve/031f0d4b35536f69ab3509d4893c923264fcf253/embeddinggemma-2-Q8_0.gguf
curl -fL --retry 3 -o models/embeddings/embeddinggemma-2-mmproj-Q8_0.gguf \
  https://huggingface.co/unsloth/embeddinggemma-2-GGUF/resolve/031f0d4b35536f69ab3509d4893c923264fcf253/mmproj-Q8_0.gguf
dotnet run --project TensorSharp.Server.Host -- \
  --model models/embeddings/embeddinggemma-2-Q8_0.gguf --embeddings --backend ggml_metal \
  --mmproj models/embeddings/embeddinggemma-2-mmproj-Q8_0.gguf --no-webui

HTTP API 与多模态序列

POST /v1/embeddings 接受字符串、字符串数组或 token-ID 数组;token 输入须包含模型的特殊 token。encoding_format 支持 float 与小端 Float32 base64。POST /api/embed 支持字符串/数组,文本 truncate 默认 true;OpenAI 超限时拒绝。两者在加载 EmbeddingGemma 2 投影器后,还接受一个 {"content":[...]} 对象或其数组,每个对象按顺序编码全部内容并产生一个向量:

{
  "model": "embeddinggemma-2-Q8_0",
  "input": [{"content": [
    {"type": "text", "text": "Waterproof running shoes. "},
    {"type": "image_url", "image_url": {"url": "data:image/png;base64,..."}}
  ]}],
  "dimensions": 256
}

媒体使用 base64 data URI。音频内容为 {"type":"input_audio","input_audio":{"data":"...","format":"wav"}}(也支持 MP3/OGG/FLAC);视频为 {"type":"input_video","input_video":{"url":"data:video/mp4;base64,..."}}(也支持 WebM)。视频按 1 fps 采样,最多 32 帧;图像预处理遵循 HF 的宽高比与 280 soft-token 目标。音频解码为 16 kHz 单声道;实际媒体 token 均计入同一 8192 窗口。媒体超限会被拒绝,文本截断开关不会截断媒体;格式还取决于主机解码器。

每请求最多 2048 个输入、262144 总 token,嵌入/模型说明 JSON 上限为 16 MiB(超限 HTTP 413),编码媒体也受限。--embedding-context-size N 可降低单输入窗口;0 使用模型默认。嵌入进程拒绝生成端点;/v1/models、/api/tags 与 /api/show 报告其能力。

检索约定与实际验证

EmbeddingGemma 2 搜索查询使用 task: search result | query: ,无标题文档使用 title: none | text: ;代码检索与句子相似度各有专用任务前缀。API 保留调用方文字,不自动插入前缀。dimensions 截取向量后重新 L2 归一化;查询与文档须使用相同模型和维数。Snowflake 查询使用 query: ,文档不加前缀。

当前 Q8 文本证据来自 M5 Pro/48 GiB、相同模型与输入、顺序运行的稳态 HTTP 请求(不含模型加载):原生 Metal 的 16 个场景都快于 llama.cpp,MLX 的 16 个场景都在 5% 延迟容差内。另有独立 NumPy 八组精确 token 与 8192-token 门限、四种维数下前 200 对 STS-B 开发样本回归;这是子集,不是完整检索评测。

默认 HF 多模态检查覆盖两种 API 的七个合成场景、22 个非法请求、临时文件清理、退出与独立完整音频池化参考。受控 llama.cpp 媒体比较需要对齐图像/视频预处理和音频 mel 下限;默认预处理不同。受控 Metal 的七个数值检查通过,但只有文本通过 5% 性能容差;直接 CPU/CUDA 的完整比较也有失败或较慢场景。不能从文本或组件结果推导六后端多模态对等。

上述运行使用未修改的 ggml 3612c0af93f2cfc8f048295300a61187723b12ae。缺少模型或设备时明确跳过,不能算通过。固定资产、算子门限、失败报告与性能限制见完整嵌入指南与复现工具;生成证据保留在忽略的 docs/validation/。

C# 集成

文本使用 EmbeddingModel.Load;多模态使用 EmbeddingGemma2MultimodalEncoder.Load(textGguf, projectorGguf, options),通过 EmbeddingGemma2InputPart.Text/Image/Audio/Video 组合本地输入。纯 C# 后端名为 CPU;其他选择为 GGML_CPU、METAL、CUDA、MLX 与 GGML_CUDA,具体映射见指南。