Views
No views yet
KaLM-Reranker-V1, a fast but not late-interaction (FBNL) reranker that decouples query and passage computation while retaining expressive relevance modeling.Nano, Small, and Large, with 0.27B, 1B, and 4B activated parameters, respectively.
| Models | Activated Params. | Non-Embedding Params. | Embedding Params. | #Layers | Sequence Length | Document Token Dim. | MEP Support | Instruction Aware |
|---|---|---|---|---|---|---|---|---|
| KaLM-Reranker-V1-Nano | 0.27B | 100M | 168M | 18 | 128K | 640 | 1x-32x | Yes |
| KaLM-Reranker-V1-Small | 1B | 698M | 302M | 26 | 128K | 1152 | 1x-32x | Yes |
| KaLM-Reranker-V1-Large | 4B | 3209M | 675M | 34 | 128K | 2560 | 1x-32x | Yes |
1 f"<Document>: {document}"
21(
2 f"<bos><start_of_turn>user\n"
3 f"Judge whether the Document meets the requirements based on the Query and the Instruct provided. Note that the answer can only be \"yes\" or \"no\".\n\n"
4 f"<Instruct>: {task_instruction}\n"
5 f"<Query>: {query}<end_of_turn>\n"
6 f"<start_of_turn>model\n\n\n\n"
7)
8




CrossEncoder. This integration requires sentence-transformers>=5.6,<6,
transformers>=5.3,<6, and the PyTorch backend. The repository contains custom
modeling code, so load only trusted revisions and pass trust_remote_code=True.pip install "sentence-transformers>=5.6,<6" "transformers>=5.3,<6"1import torch
2from sentence_transformers import CrossEncoder
3
4model = CrossEncoder(
5 "KaLM-Embedding/KaLM-Reranker-V1-Nano",
6 trust_remote_code=True,
7 device="cuda",
8 model_kwargs={"dtype": torch.bfloat16, "chunk_size": 4},
9)
10
11query = "What is the capital of China?"
12documents = [
13 "The capital of China is Beijing.",
14 "Gravity attracts bodies toward one another.",
15]
16pairs = [(query, document) for document in documents]
17
18# The default output is P(yes).
19scores = model.predict(pairs)
20rankings = model.rank(query, documents, return_documents=True)
21
22# CrossEncoder prompts are interpreted as KaLM task instructions.
23instruction = "Given a web search query, retrieve passages that answer the query."
24custom_scores = model.predict(pairs, prompt=instruction)
25custom_rankings = model.rank(query, documents, prompt=instruction)
26
27# Use Identity to return yes_logit - no_logit instead of P(yes).
28margins = model.predict(pairs, activation_fn=torch.nn.Identity())
29
30print(f"scores: {scores}")
31print(f"rankings: {rankings}")
32print(f"custom_scores: {custom_scores}")
33print(f"custom_rankings: {custom_rankings}")
34print(f"margins: {margins}")
35
36'''
37scores: [9.9984646e-01 6.8936106e-07]
38rankings: [{'corpus_id': 0, 'score': 0.99984646, 'text': 'The capital of China is Beijing.'}, {'corpus_id': 1, 'score': 6.8936106e-07, 'text': 'Gravity attracts bodies toward one another.'}]
39custom_scores: [9.972850e-01 5.896418e-07]
40custom_rankings: [{'corpus_id': 0, 'score': 0.997285}, {'corpus_id': 1, 'score': 5.896418e-07}]
41margins: [ 8.78125 -14.1875 ]
42'''
43(query, document). By default, queries are
truncated to 512 tokens and documents to 1024 tokens. chunk_size=4 performs a
mask-aware mean over each consecutive group of four encoder token states before
passing the compressed encoder output to the decoder. Set chunk_size=None to
disable compression, or change model[0].chunk_size after loading.device="cpu" and
model_kwargs={"dtype": torch.float32, "chunk_size": 4}. Only the PyTorch
inference backend is currently supported; training, ONNX, and OpenVINO are not
included in this release.1import argparse
2from typing import Optional
3
4
5def optional_positive_int(value: str) -> Optional[int]:
6 if value.lower() == "none":
7 return None
8 try:
9 parsed = int(value)
10 except ValueError as error:
11 raise argparse.ArgumentTypeError(
12 "must be a positive integer or 'none'"
13 ) from error
14 if parsed <= 0:
15 raise argparse.ArgumentTypeError("must be a positive integer or 'none'")
16 return parsed
17
18
19def build_parser() -> argparse.ArgumentParser:
20 parser = argparse.ArgumentParser(
21 formatter_class=argparse.ArgumentDefaultsHelpFormatter,
22 )
23 parser.add_argument(
24 "--model",
25 default="KaLM-Embedding/KaLM-Reranker-V1-Large",
26 help="Hugging Face model ID or local checkpoint path.",
27 )
28 parser.add_argument(
29 "--device",
30 default=None,
31 help="Inference device, such as 'cuda', 'cuda:0', or 'cpu'.",
32 )
33 parser.add_argument(
34 "--dtype",
35 default=None,
36 choices=("bfloat16", "bf16", "float16", "fp16", "float32", "fp32"),
37 help="Model parameter dtype. By default, use BF16 on CUDA and FP32 on CPU.",
38 )
39 parser.add_argument(
40 "--batch-size",
41 type=int,
42 default=32,
43 help="Number of query-document pairs scored per inference batch.",
44 )
45 parser.add_argument(
46 "--query-max-length",
47 type=int,
48 default=512,
49 help=(
50 "Maximum tokens in the raw query before it is inserted into the "
51 "decoder prompt; prompt tokens are not included in this limit."
52 ),
53 )
54 parser.add_argument(
55 "--reranker-max-length",
56 type=int,
57 default=1024,
58 help=(
59 "Maximum encoder tokens for '<Document>: {passage}'. This is not a "
60 "combined query-document context limit."
61 ),
62 )
63 parser.add_argument(
64 "--chunk-size",
65 type=optional_positive_int,
66 default=4,
67 metavar="N|none",
68 help=(
69 "Number of encoder token hidden states per mean-pooled chunk; use "
70 "'none' to disable encoder chunk pooling."
71 ),
72 )
73 return parser
74
75
76def main() -> None:
77 args = build_parser().parse_args()
78
79 from kalm_reranker import KaLMReranker
80
81 reranker = KaLMReranker(
82 args.model,
83 device=args.device,
84 dtype=args.dtype,
85 batch_size=args.batch_size,
86 query_max_length=args.query_max_length,
87 max_length=args.reranker_max_length,
88 chunk_size=args.chunk_size,
89 )
90 query = "What is the capital of China?"
91 documents = [
92 "The capital of China is Beijing.",
93 "Gravity attracts bodies toward one another.",
94 ]
95 instruction = "Given a query, retrieve documents that answer the query."
96
97 pairs = [(query, document) for document in documents]
98 print("scores:", reranker.predict(pairs, instruction=instruction))
99 print("rankings:", reranker.rank(query, documents, instruction=instruction))
100
101
102if __name__ == "__main__":
103 main()
104
105'''
106scores: [0.9998205304145813, 4.7850949158601e-06]
107rankings: [{'corpus_id': 0, 'score': 0.9998205304145813}, {'corpus_id': 1, 'score': 4.7850949158601e-06}]
108'''
109LLM.classify() reranking and optional FastAPI serving. It reuses the original
checkpoint without adding or modifying model weights.1conda create -n kalm-vllm python=3.12 -y
2conda activate kalm-vllm
3pip install "vllm==0.19.1" "transformers==5.6.2"
4
5hf download KaLM-Embedding/KaLM-Reranker-V1-Large \
6 --local-dir ./KaLM-Reranker-V1-Large
7pip install ./KaLM-Reranker-V1-Large/vllm_support --no-deps
8export VLLM_PLUGINS=kalm_t5gemma21from kalm_t5gemma2_vllm_plugin import KaLMVLLMReranker
2
3query = "What is the capital of China?"
4documents = [
5 "The capital of China is Beijing.",
6 "Gravity attracts bodies toward one another.",
7]
8
9with KaLMVLLMReranker(
10 "KaLM-Embedding/KaLM-Reranker-V1-Large",
11 query_max_length=512,
12 document_max_length=1024,
13 encoder_chunk_size=4,
14) as reranker:
15 print(reranker.rank(query, documents))kalm-vllm-rerank --return-margin1pip install "fastapi>=0.136,<0.137" "uvicorn>=0.46,<0.47"
2export CUDA_VISIBLE_DEVICES=0
3export VLLM_PLUGINS=kalm_t5gemma2
4
5kalm-vllm-serve \
6 --host 0.0.0.0 \
7 --port 8000 \
8 --model KaLM-Embedding/KaLM-Reranker-V1-Large \
9 --query-max-length 512 \
10 --document-max-length 1024 \
11 --encoder-chunk-size 4 \
12 --max-model-len 20481conda activate kalm-vllm
2kalm-vllm-client --base-url http://127.0.0.1:8000 --health/rerank for one query and a list of documents. Results are sorted by
score:1kalm-vllm-client \
2 --base-url http://127.0.0.1:8000 \
3 --endpoint rerank \
4 --json-file ./KaLM-Reranker-V1-Large/vllm_support/examples/rerank_request.json \
5 --return-margin \
6 --top-k 10/score to score a batch of independent query-document pairs. Results
preserve the input order and optional IDs:1kalm-vllm-client \
2 --base-url http://127.0.0.1:8000 \
3 --endpoint score \
4 --json-file ./KaLM-Reranker-V1-Large/vllm_support/examples/score_request.json \
5 --return-marginP(yes). Set return_margin=true to also receive
yes_logit - no_logit; the client flag --return-margin applies the same
setting to a JSON file request. The supported encoder chunk sizes are
1, 2, 4, 8, 16, 32, with 4 as the default./score implementation or a complete vLLM-native kernel port.
See the complete installation, API and troubleshooting guide.jina-reranker-v3 and Qwen3-Reranker for their valuable inspiration and contributions to the reranking community, from which we have learned a lot.@misc{zhao2026kalmrerankerv1,
title={KaLM-Reranker-V1: Fast but Not Late Interaction for Compressed Document Reranking},
author={Xinping Zhao and Jiaxin Xu and Ziqi Dai and Xin Zhang and Shouzheng Huang and Danyu Tang and Xinshuo Hu and Meishan Zhang and Baotian Hu and Min Zhang},
year={2026},
eprint={2606.22807},
archivePrefix={arXiv},
primaryClass={cs.CL},
url={https://arxiv.org/abs/2606.22807},
}
@inproceedings{zhao2026kalmembeddingv2,
title={KaLM-Embedding-V2: Superior Training Techniques and Data Inspire A Versatile Embedding Model},
author={Xinping Zhao and Xinshuo Hu and Zifei Shan and Shouzheng Huang and Yao Zhou and Xin Zhang and Zetian Sun and Zhenyu Liu and Dongfang Li and Xinyuan Wei and Youcheng Pan and Yang Xiang and Meishan Zhang and Haofen Wang and Jun Yu and Baotian Hu and Min Zhang},
booktitle={The Fourteenth International Conference on Learning Representations},
year={2026},
url={https://openreview.net/forum?id=Y7qzhvWhcz}
}
@misc{hu2025kalmembedding,
title={KaLM-Embedding: Superior Training Data Brings A Stronger Embedding Model},
author={Xinshuo Hu and Zifei Shan and Xinping Zhao and Zetian Sun and Zhenyu Liu and Dongfang Li and Shaolin Ye and Xinyuan Wei and Qian Chen and Baotian Hu and Haofen Wang and Jun Yu and Min Zhang},
year={2025},
eprint={2501.01028},
archivePrefix={arXiv},
primaryClass={cs.CL},
url={https://arxiv.org/abs/2501.01028},
}