iT邦幫忙

2025 iThome 鐵人賽

DAY 29
0
生成式 AI

練習AI系列 第 30

知識庫快取(Cache & TTL) 🧩

  • 分享至 

  • xImage
  •  
  1. src/day29_cache.js
// src/day29_cache.js

// 簡單快取 (Map 存 {key: {data, expire}})
const cache = new Map();

/**
 * 生成快取 key
 */
function makeKey({ tenant, ns, query, strategy }) {
  return `${tenant}:${ns}:${strategy}:${query}`;
}

/**
 * 讀取快取
 */
export function getCache({ tenant, ns, query, strategy }) {
  const key = makeKey({ tenant, ns, query, strategy });
  const entry = cache.get(key);
  if (!entry) return null;
  if (Date.now() > entry.expire) {
    cache.delete(key);
    return null;
  }
  return entry.data;
}

/**
 * 寫入快取
 */
export function setCache({ tenant, ns, query, strategy, data, ttlMs=10*60*1000 }) {
  const key = makeKey({ tenant, ns, query, strategy });
  cache.set(key, { data, expire: Date.now() + ttlMs });
}

/**
 * 清除全部快取
 */
export function clearCache() {
  cache.clear();
}
  1. app/api/kb/[tenant]/[ns]/ask/route.js

在每個策略進入檢索前加快取判斷:

import { getCache, setCache } from "../../../../../src/day29_cache.js";

export const POST = withAuth(async (req, ctx) => {
  const { tenant, ns } = ctx.params;
  const { q, highlight = true, strategy = "default" } = await req.json();

  if (!q || !q.trim()) return NextResponse.json({ ok:false, error:"q 必填" }, { status:400 });

+  // 先檢查快取
+  const cached = getCache({ tenant, ns, query: q, strategy });
+  if (cached) {
+    return NextResponse.json({ ok:true, strategy, cached:true, ...cached });
+  }

  if (strategy === "default") {
    ...
    const out = await answerWithRAGHighlighted({ tenant, ns, query: q, topK: 4 });
+    setCache({ tenant, ns, query: q, strategy, data: out });
    return NextResponse.json({ ok:true, strategy, ...out });
  }

  if (strategy === "section") {
    ...
+    setCache({ tenant, ns, query: q, strategy, data: { answer, answerHtml: aligned.html, sources, spans } });
    return NextResponse.json({ ok:true, strategy:"section", answer, answerHtml:aligned.html, ... });
  }

  if (strategy === "qrewrite") {
    ...
+    setCache({ tenant, ns, query: q, strategy, data: { answer, sources: chunks } });
    return NextResponse.json({ ok:true, strategy:"qrewrite", answer, sources: chunks });
  }

  if (strategy === "hybrid") {
    ...
+    setCache({ tenant, ns, query: q, strategy, data: { answer, sources: chunks } });
    return NextResponse.json({ ok:true, strategy:"hybrid", answer, sources: chunks });
  }

  if (strategy === "rerank") {
    ...
+    setCache({ tenant, ns, query: q, strategy, data: { answer, sources: chunks } });
    return NextResponse.json({ ok:true, strategy:"rerank", answer, sources: chunks });
  }

  if (strategy === "xlingual") {
    ...
+    setCache({ tenant, ns, query: q, strategy, data: { answer, sources: chunks } });
    return NextResponse.json({ ok:true, strategy:"xlingual", answer, sources: chunks });
  }
}, ["viewer","editor","admin"]);
  1. app/studio/page.tsx

在回答顯示快取提示:

const [cached, setCached] = useState(false);

...

const j = await r.json();
if (!j.ok) throw new Error(j.error);
setAnswer(j.answer);
setAnswerHtml(j.answerHtml || "");
setSources(j.sources || []);
+setCached(j.cached || false);

...

<div className="mt-2 text-sm opacity-70">
  {strategy.toUpperCase()} 完成
+ {cached && <span className="ml-2 text-green-600">⚡ 來自快取</span>}
</div>

▶️ 驗收流程

問「退貨要多久?」 → 第一次花 1-2 秒。

再問一次同樣問題 → 秒回,顯示「⚡來自快取」。

過了 10 分鐘 TTL 再問 → 會重新跑檢索。


上一篇
跨語言檢索(Cross-lingual Retrieval, XLR) 🌍
下一篇
End-to-End Demo(總整展示版)
系列文
練習AI31
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言