用 Jev 做 RAG 重排:答案更准、成本更低
更新于 适用版本 Jev 1.13
TL;DR: 召回负责找到”看起来相关”的 chunk,重排(rerank)负责判断”哪个真的能回答问题”。用 Jev 的打分题(scoring)给每个候选 chunk 的相关性打 1-10 分,排序、按阈值截断,只让幸存者进入生成模型。用几次廉价的打分调用换来更少、更干净的上下文 token——答案更准,生成成本更低。文末附 50 行以内的最小实现。
这里的”重排”指什么
- 输入:用户问题 + 第一阶段召回器(向量、BM25 或混合检索)返回的 N 个候选 chunk。
- 任务类型:对每个 chunk 做一次
scoring——在固定分制上评估相关性。 - 输出:每个 chunk 一个结构化分数。示例数据(example fixture):
{"answer":8,"scale":[1,10],"confidence":0.86,"rationale":"直接定义了问题所问的重试策略。"}
answer 是相关性分,confidence 表示模型对自己这个分数有多大把握,rationale 在你回头排查检索质量时非常有用。
流程图
用户问题
|
v
第一阶段召回(top-50 候选)
|
v
逐 chunk 调 Jev scoring(相关性,1-10 分制)
|
v
按分数降序排序
|
+--> 分数 >= 7 且置信度可接受 --> 保留
|
+--> 其余 --> 丢弃
|
v
取前 k 个幸存 chunk 作为生成上下文
重活仍然在召回器,Jev 只负责判断”已经找到的东西够不够好”。
最小实现
import os, json, requests
URL = "https://openrouter.ai/api/v1/chat/completions"
# Confirm the exact model slug on the OpenRouter model page.
MODEL = "typesafe/jev-1.13"
def score_chunk(query: str, chunk: str) -> dict:
resp = requests.post(
URL,
headers={"Authorization": f"Bearer {os.environ['OPENROUTER_API_KEY']}"},
json={
"model": MODEL,
"messages": [
{"role": "system", "content":
"Score how well this passage answers the query, 1-10. "
"1 = irrelevant, 10 = directly and completely answers it."},
{"role": "user", "content": f"Query: {query}\n\nPassage: {chunk[:2000]}"},
],
"temperature": 0,
},
timeout=30,
)
resp.raise_for_status()
# 示例数据(example fixture):
# {"answer":8,"scale":[1,10],"confidence":0.86,"rationale":"..."}
return json.loads(resp.json()["choices"][0]["message"]["content"])
def rerank(query, chunks, min_score=7, top_k=5):
scored = [(score_chunk(query, c), c) for c in chunks]
scored.sort(key=lambda x: x[0]["answer"], reverse=True)
return [c for s, c in scored if s["answer"] >= min_score][:top_k]
rerank() 返回清洗后的上下文列表,RAG 栈其余部分原样不动。官方 API 的端点与字段以 typesafe.ai 官方文档为准。
阈值建议
| 配置项 | 建议 | 理由 |
|---|---|---|
| 分制 | [1, 10] | 足以区分”沾边”和”能回答” |
| 收录线 | 分数 >= 7 | 低于此线的 chunk 很少改变最终答案 |
| top-k 上限 | 5(在 3-8 之间调) | 即使大量 chunk 过线,生成 token 也有上限 |
| 打分低置信度 | 丢弃,或触发更宽召回重试 | 模型自己都没把握的分数,本身就是弱证据 |
| 批量说明 | 串行或小并发打分 | 批量重排案例讨论了吞吐与限流的平衡 |
本场景依赖 Jev 的三原语(three primitives)——scoring 是三种任务类型之一;成本与延迟(cost and latency)指南解释了为什么”廉价判断调用放在昂贵生成模型前面”通常在总花费上更划算。RAG chunk 批量重排的案例里给出了实测数据。
本文适用版本:Jev 1.13。
FAQ
- Jev 在 RAG 重排里做什么? 召回之后,对每个候选 chunk 调一次 scoring 打相关性分,按分排序、按阈值截断,只把留下的 chunk 交给生成模型。
- 分数阈值怎么设? 1-10 分制上以 7 分为收录线比较合理,同时加 top-k 上限,避免生成模型拿到过多上下文。
- 比只用向量检索更贵吗? 每个候选多一次廉价打分调用,但喂给生成模型的 chunk 更少更准;批量重排案例里有具体成本对比。