| Model | Base Model | Parameters | HuggingFace |
|---|---|---|---|
| MemReranker-4B | Qwen3-Reranker-4B | 4B | IAAR-Shanghai/MemReranker-4B |
💡 No GPU? Use our hosted API directly! Both models are available via the Memos Rerank API — no deployment needed. See the Memos API section below.
| Model | MAP | MRR | NDCG@1 | NDCG@3 | NDCG@10 | NDCG | R@3 | R@5 | R@20 | F1 |
|---|---|---|---|---|---|---|---|---|---|---|
| BGE-v2-m3 | 0.671 | 0.699 | 0.607 | 0.672 | 0.714 | 0.736 | 0.716 | 0.768 | 0.863 | 0.504 |
| Qwen3-Reranker-0.6B | 0.643 | 0.673 | 0.576 | 0.638 | 0.689 | 0.714 | 0.681 | 0.748 | 0.857 | 0.472 |
| Qwen3-Reranker-4B | 0.689 | 0.716 | 0.623 | 0.691 | 0.732 | 0.750 | 0.735 | 0.796 | 0.873 | 0.522 |
| Qwen3-Reranker-8B | 0.721 | 0.748 | 0.666 | 0.724 | 0.759 | 0.775 | 0.763 | 0.813 | 0.880 | 0.552 |
| GPT-4o-mini | 0.715 | 0.742 | 0.657 | 0.719 | 0.753 | 0.770 | 0.760 | 0.812 | 0.868 | 0.544 |
| Gemini-3-Flash | 0.777 | 0.797 | 0.737 | 0.778 | 0.807 | 0.816 | 0.805 | 0.847 | 0.892 | 0.622 |
| MemReranker-0.6B | 0.715 | 0.738 | 0.650 | 0.717 | 0.754 | 0.770 | 0.758 | 0.809 | 0.885 | 0.555 |
| MemReranker-4B | 0.737 | 0.760 | 0.679 | 0.739 | 0.773 | 0.786 | 0.777 | 0.824 | 0.889 | 0.577 |
MemReranker-0.6B matches GPT-4o-mini and open-source 4B/8B models on key metrics. MemReranker-4B achieves 0.737 MAP, with several metrics on par with Gemini-3-Flash, while maintaining inference latency at only 10–20% of large models (~200ms).
memos-reranker-0.6b and memos-reranker-4b are available through the Memos Rerank API.1import os
2import requests
3import json
4
5os.environ["MEMOS_API_KEY"] = "YOUR_API_KEY" # Get from https://memos-dashboard.openmem.net/apikeys/
6os.environ["MEMOS_BASE_URL"] = "https://memos.memtensor.cn/api/openmem/v1"
7
8url = f"{os.environ['MEMOS_BASE_URL']}/rerank"
9
10payload = {
11 "model": "memos-reranker-4b", # or "memos-reranker-0.6b"
12 "query": "用户有什么兴趣爱好",
13 "documents": [
14 "用户喜欢打羽毛球",
15 "用户在杭州做后端开发",
16 "用户偏好简洁的回复风格",
17 "用户比较喜欢酱香型白酒",
18 "用户下周三要去北京出差"
19 ],
20 "top_n": 3
21}
22
23headers = {
24 "Content-Type": "application/json",
25 "Authorization": f"Token {os.environ['MEMOS_API_KEY']}"
26}
27
28response = requests.post(url, headers=headers, data=json.dumps(payload))
29print(response.json())1import torch
2from sentence_transformers import CrossEncoder
3
4model = CrossEncoder(
5 "IAAR-Shanghai/MemReranker-4B",
6 model_kwargs={"torch_dtype": torch.bfloat16, "device_map": "cuda"},
7)
8
9query = "What is the capital of China?"
10documents = [
11 "The capital of China is Beijing.",
12 "Beijing has been the capital of various Chinese dynasties.",
13 "Python is a popular programming language.",
14]
15
16# Get relevance scores
17scores = model.predict([(query, doc) for doc in documents])
18print(scores)
19# tensor([2.969, 1.328, -3.141])
20
21# Get 0-1 probability via sigmoid
22probs = model.predict(
23 [(query, doc) for doc in documents],
24 activation_fn=torch.nn.Sigmoid(),
25)
26
27# Or get a sorted ranking directly
28results = model.rank(query, documents)
29print(results)"query", which injects the instruction "Given a web search query, retrieve relevant passages that answer the query". A custom instruction can be passed via prompts={"my_task": "..."} and default_prompt_name="my_task" on CrossEncoder(...).Requirestransformers>=4.51.0
1import torch
2from transformers import AutoTokenizer, AutoModelForCausalLM
3
4def format_instruction(instruction, query, doc):
5 if instruction is None:
6 instruction = 'Given a web search query, retrieve relevant passages that answer the query'
7 output = "<Instruct>: {instruction}\n<Query>: {query}\n<Document>: {doc}".format(
8 instruction=instruction, query=query, doc=doc
9 )
10 return output
11
12def process_inputs(pairs):
13 inputs = tokenizer(
14 pairs, padding=False, truncation='longest_first',
15 return_attention_mask=False,
16 max_length=max_length - len(prefix_tokens) - len(suffix_tokens)
17 )
18 for i, ele in enumerate(inputs['input_ids']):
19 inputs['input_ids'][i] = prefix_tokens + ele + suffix_tokens
20 inputs = tokenizer.pad(inputs, padding=True, return_tensors="pt", max_length=max_length)
21 for key in inputs:
22 inputs[key] = inputs[key].to(model.device)
23 return inputs
24
25def compute_logits(inputs):
26 batch_scores = model(**inputs).logits[:, -1, :]
27 true_vector = batch_scores[:, token_true_id]
28 false_vector = batch_scores[:, token_false_id]
29 batch_scores = torch.stack([false_vector, true_vector], dim=1)
30 batch_scores = torch.nn.functional.log_softmax(batch_scores, dim=1)
31 scores = batch_scores[:, 1].exp().tolist()
32 return scores
33
34tokenizer = AutoTokenizer.from_pretrained(
35 "IAAR-Shanghai/MemReranker-4B", padding_side='left'
36)
37model = AutoModelForCausalLM.from_pretrained(
38 "IAAR-Shanghai/MemReranker-4B"
39).eval()
40
41# We recommend enabling flash_attention_2 for better acceleration and memory saving:
42# model = AutoModelForCausalLM.from_pretrained(
43# "IAAR-Shanghai/MemReranker-4B",
44# torch_dtype=torch.float16,
45# attn_implementation="flash_attention_2"
46# ).cuda().eval()
47
48token_false_id = tokenizer.convert_tokens_to_ids("no")
49token_true_id = tokenizer.convert_tokens_to_ids("yes")
50max_length = 8192
51
52prefix = '<|im_start|>system\nJudge whether the Document meets the requirements based on the Query and the Instruct provided. Note that the answer can only be "yes" or "no".<|im_end|>\n<|im_start|>user\n'
53suffix = '<|im_end|>\n<|im_start|>assistant\n<think>\n\n</think>\n\n'
54prefix_tokens = tokenizer.encode(prefix, add_special_tokens=False)
55suffix_tokens = tokenizer.encode(suffix, add_special_tokens=False)
56
57task = 'Given a web search query, retrieve relevant passages that answer the query'
58queries = ["What is the capital of China?", "Explain gravity"]
59documents = [
60 "The capital of China is Beijing.",
61 "Gravity is a force that attracts two bodies towards each other. It gives weight to physical objects and is responsible for the movement of planets around the sun.",
62]
63
64pairs = [format_instruction(task, query, doc) for query, doc in zip(queries, documents)]
65inputs = process_inputs(pairs)
66scores = compute_logits(inputs)
67print("scores:", scores)Requiresvllm>=0.8.5
1import math
2import torch
3from transformers import AutoTokenizer
4from vllm import LLM, SamplingParams
5from vllm.inputs.data import TokensPrompt
6
7def format_instruction(instruction, query, doc):
8 text = [
9 {"role": "system", "content": '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".'},
10 {"role": "user", "content": f"<Instruct>: {instruction}\n\n<Query>: {query}\n\n<Document>: {doc}"}
11 ]
12 return text
13
14def process_inputs(pairs, instruction, max_length, suffix_tokens):
15 messages = [format_instruction(instruction, query, doc) for query, doc in pairs]
16 messages = tokenizer.apply_chat_template(
17 messages, tokenize=True, add_generation_prompt=False, enable_thinking=False
18 )
19 messages = [ele[:max_length] + suffix_tokens for ele in messages]
20 messages = [TokensPrompt(prompt_token_ids=ele) for ele in messages]
21 return messages
22
23def compute_logits(model, messages, sampling_params, true_token, false_token):
24 outputs = model.generate(messages, sampling_params, use_tqdm=False)
25 scores = []
26 for output in outputs:
27 final_logits = output.outputs[0].logprobs[-1]
28 true_logit = final_logits[true_token].logprob if true_token in final_logits else -10
29 false_logit = final_logits[false_token].logprob if false_token in final_logits else -10
30 true_score = math.exp(true_logit)
31 false_score = math.exp(false_logit)
32 score = true_score / (true_score + false_score)
33 scores.append(score)
34 return scores
35
36number_of_gpu = torch.cuda.device_count()
37tokenizer = AutoTokenizer.from_pretrained("IAAR-Shanghai/MemReranker-4B")
38model = LLM(
39 model="IAAR-Shanghai/MemReranker-4B",
40 tensor_parallel_size=number_of_gpu,
41 max_model_len=10000,
42 enable_prefix_caching=True,
43 gpu_memory_utilization=0.8,
44)
45tokenizer.padding_side = "left"
46tokenizer.pad_token = tokenizer.eos_token
47
48suffix = '<|im_end|>\n<|im_start|>assistant\n<think>\n\n</think>\n\n'
49max_length = 8192
50suffix_tokens = tokenizer.encode(suffix, add_special_tokens=False)
51true_token = tokenizer("yes", add_special_tokens=False).input_ids[0]
52false_token = tokenizer("no", add_special_tokens=False).input_ids[0]
53sampling_params = SamplingParams(
54 temperature=0,
55 max_tokens=1,
56 logprobs=20,
57 allowed_token_ids=[true_token, false_token],
58)
59
60task = 'Given a web search query, retrieve relevant passages that answer the query'
61queries = ["What is the capital of China?", "Explain gravity"]
62documents = [
63 "The capital of China is Beijing.",
64 "Gravity is a force that attracts two bodies towards each other.",
65]
66
67pairs = list(zip(queries, documents))
68inputs = process_inputs(pairs, task, max_length - len(suffix_tokens), suffix_tokens)
69scores = compute_logits(model, inputs, sampling_params, true_token, false_token)
70print("scores:", scores)1python -m vllm.entrypoints.openai.api_server \
2 --model IAAR-Shanghai/MemReranker-4B \
3 --tensor-parallel-size 1 \
4 --gpu-memory-utilization 0.9 \
5 --served-model-name MemReranker-4B \
6 --host 0.0.0.0 \
7 --port 8089 \
8 --chat-template /path/to/qwen3_reranker.jinja \
9 --hf_overrides '{"architectures": ["Qwen3ForSequenceClassification"], "classifier_from_token": ["no", "yes"], "is_original_qwen3_reranker": true}'yes (token id: 9693) and no (token id: 2152) are the two classification tokens.1@article{li2026memreranker,
2 title={MemReranker: Reasoning-Aware Reranking for Agent Memory Retrieval},
3 author={Li, Chunyu and Kang, Jingyi and Chen, Ding and Zhang, Mengyuan and Shen, Jiajun and Tang, Bo and Zhou, Xuanhe and Xiong, Feiyu and Li, Zhiyu},
4 journal={arXiv preprint arXiv:2605.06132},
5 year={2026}
6}