A query-conditional benchmark (R3-Skill) and a two-stage retriever (R3-Embedding + R3-Reranker) for LLM agent skill routing.
- π Paper: Skill Is Not Document: A Query-Conditional Benchmark and Two-Stage Retriever for LLM Agent Skill Routing
- π§© This repository contains the code, docs, R3-Skill data, and a toy example. Model weights are
hosted on Hugging Face β R3-Embedding-0.6B /
R3-Rerank-0.6B β and should be placed under
models/for reproduction.
βββ infer.py two-stage retrieval (embedding recall β reranker rerank)
βββ reproduce.py reproduce the paper's R3-Skill test numbers
βββ example_skills.jsonl toy skill library for a quick test
βββ requirements.txt
βββ README.md / CONTRIBUTING.md / CODE_OF_CONDUCT.md / LICENSE
βββ models/ not tracked by Git; place downloaded model weights here
β βββ r3-embedding/ bi-encoder (fine-tuned from Qwen3-Embedding-0.6B)
β βββ r3-reranker/ cross-encoder (fine-tuned from Qwen3-Reranker-0.6B)
βββ data/ R3-Skill benchmark data
βββ test.jsonl 5,696 test queries
βββ skills_test.jsonl 2,050 test-pool skills
Skill identifiers are released as opaque ids (skill-00000, β¦); each id maps consistently
across the query and skill-pool files. Train and test skill pools are disjoint.
The skill pool and query set draw on the following public resources:
- https://huggingface.co/datasets/ThakiCloud/SKILLRET
- https://huggingface.co/datasets/pipizhao/SkillRouter-Eval-Core
- https://gitcode.com/zhoukc42/AiSkill
- https://github.com/jnMetaCode/agency-agents-zh
query
β
βΌ
βββββββββββββββββββββββββββββββ
β Stage 1 Β· R3-Embedding β bi-encoder, cosine over the whole pool
β (recall) β prompt: "Instruct: ... \nQuery: "
βββββββββββββββ¬ββββββββββββββββ
β top-N candidates (default N=20)
βΌ
βββββββββββββββββββββββββββββββ
β Stage 2 Β· R3-Reranker β cross-encoder, scores each (query, skill) pair
β (rerank) β prompt: "Given a user request, ..."
βββββββββββββββ¬ββββββββββββββββ
β reordered
βΌ
top-K skills
- Stage 1 (recall) embeds the query and every skill independently and ranks by cosine similarity β cheap, scales to the full skill pool.
- Stage 2 (rerank) re-scores only the top-N candidates with a cross-encoder that reads the query and skill jointly, so it can judge query-conditional compatibility that a bi-encoder cannot.
- Why two stages. A bi-encoder is fast but scores each skill in isolation; whether a set of skills should be retrieved together depends on the query (skill compatibility). The cross-encoder reranker repairs this on a small candidate set.
- SKIP-as-resource. LLM-rejected skill combinations are kept as negative supervision for the reranker during training, rather than discarded β this is the paper's core idea.
| File | Role |
|---|---|
infer.py |
End-to-end retrieval over an arbitrary skill library; CLI in / jsonl out. |
reproduce.py |
Loads data/test.jsonl + data/skills_test.jsonl, runs both stages, prints metrics next to the paper values. |
models/r3-embedding/ |
SentenceTransformer bi-encoder. Download and place here. |
models/r3-reranker/ |
CrossEncoder cross-encoder. Download and place here. |
data/ |
R3-Skill test queries and the test skill pool. |
pip install -r requirements.txtDownload the released model weights and place them under:
pip install -U huggingface_hub
hf download tencent/R3-embedding-0.6b --local-dir models/r3-embedding
hf download tencent/R3-rerank-0.6b --local-dir models/r3-rerankerpython3 infer.py --query "I need to compose music" --skills example_skills.jsonl --recall_n 6 --top_k 3Expected top-1: music-composer.
The R3-Skill data is included under data/. Run:
python3 reproduce.pyPrints embedding-only (Table 5) and embedding+reranker (Table 7) metrics next to the paper values.
Uses all GPUs visible via CUDA_VISIBLE_DEVICES.
python3 infer.py --query_file my_queries.txt --skills my_skills.jsonl --top_k 10 --out results.jsonlSkill library (--skills), one JSON per line:
{"id": "music-composer", "text": "name | description | body"}The text field is recommended to follow name | description | body (matches training; the
reranker truncates only the body segment).
Query input β either --query "..." (single) or --query_file FILE (txt one-per-line, or
jsonl with a query field).
Output (--out), one JSON per line:
{"query": "...", "top_k": [{"id": "...", "score": 0.83}, ...]}from sentence_transformers import SentenceTransformer
m = SentenceTransformer("models/r3-embedding", trust_remote_code=True)
qv = m.encode(["Instruct: Given a user request, retrieve the agent skill that solves it.\nQuery: " + q],
normalize_embeddings=True)| flag | default | meaning |
|---|---|---|
--recall_n |
20 | embedding recall depth fed to the reranker |
--top_k |
10 | final results after rerank |
--batch_size |
32 | lower it on OOM |
See CONTRIBUTING.md and CODE_OF_CONDUCT.md.
Apache License 2.0 (see LICENSE).
For any questions regarding the use of the released models or datasets, please contact the author at jawnrwen@tencent.com.
@inproceedings{r3skill2026,
title = {Skill Is Not Document: A Query-Conditional Benchmark and Two-Stage Retriever for LLM Agent Skill Routing},
author = {Wang, Zifei and Wen, Wei and Ji, Qiang and Qiao, Ruizhi and Sun, Xing},
year = {2026},
url = {https://arxiv.org/abs/2606.03565},
}