iT邦幫忙

2025 iThome 鐵人賽

DAY 27
0
生成式 AI

練習AI系列 第 28

Cross-Encoder Reranker(重排序模型)

  • 分享至 

  • xImage
  •  

🆕 程式碼

1) src/day27_reranker.js
// src/day27_reranker.js
import { openai } from "./aiClient.js";
import { answerWithRAG } from "./day16_rag_store.js";

/**
 * 使用 LLM 模擬 Cross-Encoder Reranker
 * @param {string} query 
 * @param {Array} chunks - [{id, text, docId}]
 */
export async function rerank(query, chunks) {
  // 對每個 chunk 做「查詢 + chunk」評分
  const prompt = chunks.map((c, i) => 
    `片段${i+1}:${c.text}`
  ).join("\n\n");

  const res = await openai.chat.completions.create({
    model: "gpt-4o-mini", // 模擬 cross-encoder
    temperature: 0,
    messages: [
      { role: "system", content: "你是 Reranker,請根據查詢與片段相關性,給每個片段 0~1 分數。" },
      { role: "user", content: `查詢:${query}\n\n${prompt}\n\n請輸出 JSON 陣列,例如 [{"id":"片段1","score":0.87},...]` }
    ]
  });

  let scores = [];
  try {
    scores = JSON.parse(res.choices[0].message.content);
  } catch {
    // fallback: 預設順序
    scores = chunks.map((c,i)=>({id:c.id,score:1-(i*0.05)}));
  }

  const byId = new Map(scores.map(s=>[s.id,s.score]));
  return chunks.map((c,i)=>({
    ...c,
    rerankScore: byId.get(`片段${i+1}`) || 0
  })).sort((a,b)=>b.rerankScore-a.rerankScore);
}

/**
 * 檢索 + Rerank
 */
export async function retrieveWithRerank({ tenant, ns, query, preK=20, finalK=6 }) {
  const { sources } = await answerWithRAG({ tenant, ns, query, topK: preK });
  const reranked = await rerank(query, sources);
  return reranked.slice(0, finalK);
}
  1. app/api/kb/[tenant]/[ns]/ask/route.js

新增 Rerank 策略:

import { retrieveWithRerank } from "../../../../../src/day27_reranker.js";

...

  if (strategy === "rerank") {
    const chunks = await retrieveWithRerank({ tenant, ns, query: q, preK: 20, finalK: 6 });
    const ctxText = chunks.map((h,i)=>`# 片段${i+1}(${h.docId})\n${h.text}`).join("\n\n");

    const res = await openai.chat.completions.create({
      model:"gpt-4o-mini", temperature:0.2,
      messages:[
        { role:"system", content:"你是知識庫助理,根據最佳片段回答。" },
        { role:"user", content:`問題:${q}\n\n片段:\n${ctxText}` }
      ]
    });

    const answer = res.choices?.[0]?.message?.content?.trim() || "沒有足夠資訊。";
    return NextResponse.json({ ok:true, strategy:"rerank", answer, sources: chunks });
  }
  1. app/studio/page.tsx

加一個策略選項:

<select ... value={strategy} onChange={e=>setStrategy(e.target.value as any)}>
   <option value="default">Default(Top-K)</option>
   <option value="section">Section-first</option>
   <option value="qrewrite">Query Rewrite</option>
   <option value="hybrid">Hybrid</option>
+  <option value="rerank">Rerank(Cross-Encoder)</option>
</select>

▶️ 驗收流程

/studio 選擇 Rerank。

問:「退貨要多久?」

預檢索抓 20 個候選。

Reranker 重新排序,把最相關的 6 個送進回答。

回傳 JSON:strategy: "rerank",sources 包含 rerankScore。


上一篇
索引壓縮 + 向量量化(Index Compression & Vector Quantization)
下一篇
跨語言檢索(Cross-lingual Retrieval, XLR) 🌍
系列文
練習AI29
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言