Jina Code Embeddings: 为高质量代码搜索而生的 0.5B/1.5B 向量模型

Jina AI 开源了 jina-code-embeddings 系列代码向量模型(0.5B/1.5B),旨在解决传统代码向量模型面临的**高质量监督训练数据稀缺**这一核心瓶颈。该模型创新性地以代码生成大语言模型(如 Qwen2.5-Coder)为骨干网络,并通过对比学习进行高效微调,从而在紧凑参数规模下实现了领域顶尖的代码检索性能,超越了同等参数量级及部分闭源模型。模型原生支持自然语言搜代码、代码相似性搜索、代码生成文档、代码补全及技术问答等五种核心任务,并兼容超过 15 种主流编程语言。文章详细阐述了其训练方案,包括基座选择、全后训练策略、任务特定的指令前缀设计以及 last-token pooling 的优势,并提供了 GGUF 量化版本及通过 API、sentence-transformers、transformers 库的快速上手示例,同时强调了 Matryoshka 动态截断功能,实现了性能与效率的灵活平衡。这一成果验证了“正确的模型基座远比参数量更为关键”的技术理念。




Jina Code Embeddings: 为高质量代码搜索而生的 0.5B/1.5B 向量模型

我们今天正式开源 jina-code-embeddings,一套全新的代码向量模型。包含 0.5B 和 1.5B 两种参数规模,并同步推出了 1-4 bit 的 GGUF 量化版本,方便在各类端侧硬件上部署。

技术报告: https://arxiv.org/abs/2508.21290v1

模型介绍:https://jina.ai/models/jina-code-embeddings-1.5b/

🤗:https://huggingface.co/jinaai/jina-code-embeddings-1.5b

我们选择代码生成大语言模型(LLM)作为骨干网络进行训练,这使得模型在保持紧凑体积的同时,其代码检索性能达到了领域顶尖水平。

模型能支持 5 种核心检索任务,包括自然语言搜代码(nl2code)、代码间相似性搜索(code2code)、代码生成文档(code2nl)、代码补全(code2completions)以及技术问答(qa)。

同时,模型原生支持超过 15 种主流编程语言,覆盖了 Python, JavaScript, Java, C++, C#, Go, Rust, TypeScript, SQL, MATLAB, R, Swift, Kotlin, HTML/CSS, PHP, Ruby, Scala, Perl, 以及 Shell 脚本。

在 25 个代码检索基准测试的综合评估中,jina-code-embeddings 的 0.5B 模型取得了 78.41% 的平均分,1.5B 模型则达到了 79.04%。为了更清晰地展示其性能优势,我们进行了横向对比:

  • 我们的 0.5B 模型,参数量比 Qwen3-Embedding-0.6B 模型少 20%,但其性能反而高出 5 个百分点。

  • 我们的 1.5B 模型,其性能不仅与 voyage-code-3 (79.23%) 基本持平,还明显超过了顶级的闭源模型 gemini-embedding-001 (77.38%)。后两者均为架构未公开的专有模型。

下表汇总了详细的性能对比数据:

模型
参数量
综合平均性能
MTEB 代码任务平均性能
jina-code-embeddings-1.5b 1.54B 79.04% 78.94%
jina-code-embeddings-0.5b 494M 78.41% 78.72%
voyage-code-3
未知*
79.23%
79.84%
gemini-embedding-001
未知*
77.38%
76.48%
jina-embeddings-v4
3.8B
74.11%
74.87%
Qwen3-Embedding-0.6B
600M
73.49%
74.69%

注:标星号的模型为闭源模型,其具体架构未公开。

Jina Code Embeddings: 为高质量代码搜索而生的 0.5B/1.5B 向量模型


为了引导模型精准地处理不同类型的检索任务,我们在训练流程中设计了五种任务特定的指令前缀(instruction prefixes)。

📎: https://huggingface.co/jinaai/jina-code-embeddings-1.5b/blob/main/config_sentence_transformers.json

每一种指令都区分为查询(query)和文档(document)两种角色,以支持非对称检索。例如,在处理自然语言到代码的检索任务时,可以用 nl2code_query 指令来编码用户查询,并用 nl2code_document 指令来编码待检索的代码库。

任务标识
核心应用场景
指令前缀(引导模型执行特定任务)
nl2code
输入“如何读取CSV”,模型返回 pandas.read_csv()
“根据以下查询,查找最相关的代码片段:\n”
qa
检索技术问答
“根据以下问题,查找最相关的答案:\n”
code2code
查找功能相似的代码实现
“根据以下代码片段,查找功能等效的代码片段:\n”
code2nl
为代码生成文档
“根据以下代码片段,查找最相关的注释:\n”
code2completion
自动补全代码
“根据以下代码片段的开头,查找最相关的补全代码:\n”

核心训练方案

我们直接选用预训练的代码生成模型作为向量模型的骨干网络(backbone)。我们的模型分别基于 Qwen2.5-Coder-0.5B 和 1.5B 构建,其核心技术规格如下:

特性
jina-code-embeddings-0.5b jina-code-embeddings-1.5b
基础模型
Qwen2.5-Coder-0.5B
Qwen2.5-Coder-1.5B
向量维度
896
1536
Matryoshka 维度
64, 128, 256, 512, 896
128, 256, 512, 1024, 1536
最大序列长度
32,768 tokens
32,768 tokens
池化策略
last-token pooling
last-token pooling
注意力机制
FlashAttention2
FlashAttention2
数据类型
BFloat16
BFloat16

传统代码向量模型的训练,长期受制于一个核心瓶颈:高质量的监督训练数据,即“注释-代码”配对,极度稀缺。为此,我们选择了一条不同的技术路径。

我们选用 Qwen2.5-Coder 作为预训练基座,该模型已在覆盖 92 种以上编程语言、总计 5.5 万亿 token 的海量数据上完成了预训练。使我们的模型得以直接继承其对编程结构的深层语义理解、跨语言的模式识别能力,以及对语法范式的内化知识。

在此基础上,我们仅需通过对比学习(contrastive fine-tuning)进行微调,就能将这些既有知识高效地迁移到代码检索任务上。这种方法只需极少的对齐数据,从而成功规避了传统 Encoder-only 模型因数据稀缺而普遍面临的性能瓶颈。

针对部分训练数据不足的任务,例如跨框架代码翻译,我们利用大语言模型生成了合成数据,并对每一条合成样本都进行了严格的人工校验,确保其质量。最终,我们的最终训练数据集,是 MTEB 代码任务现有的训练集,与我们从 CommitPackFT、SWE-Bench、Spider、MBPP 和 CodeSearchNet 等多个公开数据集中筛选并适配数据的有机结合。

与我们早前的 jina-embeddings-v3 和 v4 模型不同,这次我们放弃了 LoRA,转而采用完整的后训练(full post-training)。

我们做出此项决策,主要基于以下考量:对于 494M 和 1.54B 这样的小参数模型,LoRA 的参数效率优势并不明显。相反,适配器(adapter)的额外开销在模型容量有限时,反而可能拖累性能。我们需要调动模型的每一个参数,使其完全服务于向量化任务。

即便是处理多任务场景,我们发现采用任务特定的指令前缀,也比挂载多个 LoRA 适配器来得更简洁高效。无需切换权重配置,仅需在输入前添加不同指令。这种方式不仅更轻量,也更契合大语言模型处理条件化信息的内在机制。

整个训练过程极为高效。我们基于对比学习和 InfoNCE 损失函数,在 4 块 A100 80GB GPU 上完成了两个模型的训练。其中,0.5B 模型仅耗时 8.3 小时,1.5B 模型也只用了 12 小时。

最后,我们系统性地评测了不同的池化(pooling)策略。实验数据表明,last-token pooling(取最后一词元)策略取得了 78.41% 的综合平均分,在所有基准测试类别中都稳定优于 mean pooling(平均池化,77.20%)和 latent attention pooling(潜注意力池化,78.27%)。

这 1.2 个百分点的显著优势,促使我们放弃了在 jina-embeddings-v2v3 和 v4 中沿用的 mean pooling 传统。

我们认为,随着越来越多的检索模型转向基于 Decoder-only 架构的大语言模型构建,last-token pooling 正成为更自然的选择。因为它与单向注意力机制(unidirectional attention)的原理天然契合。虽然 mean pooling 也能工作,且在训练初期往往更容易收敛(这可能得益于其更平滑的优化曲面),但我们的实验反复证明,它的性能最终会停滞在某个天花板之下,而 last-token pooling 则能达到更高的性能上限。

快速上手

我们已将两个模型无缝集成到 Search Foundation API 中,并确保它们与 sentence-transformerstransformers 及 llama.cpp 等主流框架完全兼容,方便快速上手和集成。

通过 API 直接调用

您可以直接通过 Jina AI 的 Search Foundation API 端点调用模型。在请求中,只需指定模型名称(例如 jina-code-embeddings-1.5b)、输入文本以及相应的任务类型(例如 nl2code.passage),即可获取向量。

curl http://api.jina.ai/v1/embeddings \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $JINA_API_KEY" \
  -d @- <<EOF
  {
    "model""jina-code-embeddings-1.5b",
    "input": ["print hello world in python"],
    "task""nl2code.passage"
  }
EOF

通过 sentence-transformers 

模型已原生集成至 sentence-transformers 库,调用过程十分便捷。以下代码演示了如何加载模型,并利用 prompt_name 参数自动添加任务指令前缀,从而为查询(query)和文档(document)生成针对性的向量。

from sentence_transformers import SentenceTransformer

# 加载模型 (可选择 0.5b 或 1.5b 版本)
model = SentenceTransformer(
    "jinaai/jina-code-embeddings-1.5b",
    model_kwargs={"torch_dtype""bfloat16"},
    tokenizer_kwargs={"padding_side""left"}
)

# 定义自然语言查询与待检索的代码文档
queries = ["print hello world in python""initialize array of 5 zeros in c++"]
documents = ["print('Hello World!')""int arr[5] = {0, 0, 0, 0, 0};"]

# 使用任务指令前缀生成向量
query_embeddings = model.encode(queries, prompt_name="nl2code_query")
document_embeddings = model.encode(documents, prompt_name="nl2code_document")

# 计算相似度
similarity = model.similarity(query_embeddings, document_embeddings)

通过 transformers

如果需要更底层的控制,您也可以直接使用 transformers 库。由于我们的模型采用 last-token pooling 策略,因此需要自定义一个池化函数来准确提取向量。我们提供的以下函数已妥善处理了分词器(tokenizer)在处理批量数据时可能产生的左填充(left-padding)问题。

在代码实现中,你需要手动将任务指令前缀拼接到输入文本的前面,然后依次执行分词、模型推理和池化操作。

from transformers import AutoModel, AutoTokenizer
import torch
import torch.nn.functional as F

def last_token_pool(last_hidden_states, attention_mask):
    left_padding = (attention_mask[:, -1].sum() == attention_mask.shape[0])
    if left_padding:
        return last_hidden_states[:, -1]
    else:
        sequence_lengths = attention_mask.sum(dim=1) - 1
        batch_size = last_hidden_states.shape[0]
        return last_hidden_states[torch.arange(batch_size), sequence_lengths]

tokenizer = AutoTokenizer.from_pretrained('jinaai/jina-code-embeddings-1.5b')
model = AutoModel.from_pretrained('jinaai/jina-code-embeddings-1.5b')

# 手动添加任务指令前缀
query = "Find the most relevant code snippet given the following query:\nprint hello world"
code = "Candidate code snippet:\nprint('Hello World!')"

# 执行分词和模型推理
batch_dict = tokenizer([query, code], padding=True, truncation=True, return_tensors="pt")
outputs = model(**batch_dict)

# 应用 last-token pooling 提取向量
embeddings = last_token_pool(outputs.last_hidden_state, batch_dict['attention_mask'])

Matryoshka 动态截断

我们的两个模型在训练时都集成了 Matryoshka 表示学习(MRL)技术。模型生成的完整向量,其前缀本身就是低维度的、经过优化的有效表示。

您在获得完整的向量后,无需重新计算,可以直接截取其前缀,从而获得一个维度更低但依然高质量的向量。例如,从 1536 维的向量中直接截取前 256 维,就能得到一个可直接用于下游任务的 256 维向量。0.5B 模型支持的最低维度为 64,1.5B 模型则为 128。

# 生成完整的向量,维度为 896 (0.5B) 或 1536 (1.5B)
full_embedding = model.encode(text)

# 无需重计算,直接截断以获得更小的维度,提升效率
small_embedding = full_embedding[:256]  # 两个模型均支持
tiny_embedding = full_embedding[:128]   # 两个模型均支持

这样,您可以根据具体的应用场景,在模型性能、内存占用和检索效率之间做出最优的权衡。

结论

jina-code-embeddings 的实践证明:构建顶尖的代码向量模型,并不需要依赖巨大的参数规模。我们选择以强大的代码生成模型为基座,再施以目标明确的微调,最终在 1.5B 以下的参数规模上,成功实现了业界顶尖的性能。

这次的成果,也清晰地验证了我们的核心技术理念:正确的模型基座远比参数量更为关键。 代码生成模型在其海量数据的预训练阶段,已经内化了对代码的深层语义理解。我们的工作表明,这种理解能力能够别直接、高效地迁移至代码表示任务中。

这一技术路径与 Jina AI 的长远愿景完全契合。我们致力于构建统一的基座模型,让强大的向量(Embedding)能力与生成(Generation)能力,最终能够源自同一个基础模型。我们相信,这种融合将持续推动搜索基础模型的技术边界,开创新的可能。


AI 前线

Gemini 机器人 1.5 将 AI 智能体带入物理世界

2025-12-23 12:59:05

AI 前线

来自 2000 万个 Pull Requests 的数据揭示了 AI 转型的实际情况 — Nicholas Arcolano, Jellyfish

2025-12-23 12:59:12

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索