WIP(kbdb): 語意搜尋零命中的排查——⚠️ 被總管中途叫停,未完成驗證
leo 2026-08-11 判斷「如果是 Vectorize 沒完成就不用查了」,總管據此停線。 真因已經寫在 repo 自己的註解裡(kbdb/wrangler.toml:43-51,Arcrun#11): metadata index 只收「建立後 upsert」的向量,既有向量須 reindex, 否則帶 owner_id filter 一律 0 命中——與實測每一格吻合 (805 筆在、關鍵字搜得到、語意 0、拿自己查自己也 0 ⇒ 不是分數門檻)。 ⚠️ 這批改動是排查途中的產物,**沒有走完驗證**,不要當成可用的修法。 保留只是不讓它憑空消失(總管中斷造成,不是它做壞)。 接手的人請先讀 Arcrun#85 上的結論再決定要不要用。 真正的補救是 reindex,而 reindex 要燒 AI 額度 ⇒ 卡在 Arcrun#85 的每日額度閘上線之後才能做。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -35,6 +35,41 @@ function fireAndForget(c: { executionCtx?: ExecutionContext }, p: Promise<unknow
|
||||
else void p.catch(() => {});
|
||||
}
|
||||
|
||||
/**
|
||||
* 「這次零命中,是不是因為 Vectorize 的 metadata 過濾整個是死的?」
|
||||
*
|
||||
* 🔴 2026-08-11 立(Arcrun#85 D70,leo21c 實撞):那台實例的現役 index
|
||||
* `arcrun-kbdb-embed-m3` 上 **一個 metadata index 都沒有**(換代時漏建),於是
|
||||
* Vectorize 對 owner_id/source/entry_type/library 下任何 filter 都回 0 筆。
|
||||
* 而**每一條真實使用者路徑都會帶 owner_id 做租戶隔離**(portal、MCP、workflow 搜尋皆然)
|
||||
* ⇒ 語意搜尋 100% 全盲,但系統只會回「沒有找到符合的內容,換個說法再試試看」。
|
||||
*
|
||||
* 判法不靠猜、也不查 Cloudflare 設定(KBDB 這面牆內打不到那支 API):
|
||||
* **同一句查詢,把 metadata filter 全部拿掉再打一次**。
|
||||
* 有命中 → 向量在 index 裡,死的是 filter(回 true)
|
||||
* 仍零命中 → 就是這次查詢真的沒撞到東西(回 false,維持 no_match)
|
||||
*
|
||||
* 成本紀律:只在「已有嵌入資料卻零命中」這個**本來就已經降級**的分支才會被呼叫,
|
||||
* 正常有結果的查詢一次都不會多花。多的是一次 AI.run + 一次 Vectorize query。
|
||||
* 沒帶任何 filter 的查詢直接回 false(沒有 filter 可以怪,也不必多打一次)。
|
||||
* 探針自己出錯一律回 false——診斷絕不能把查詢本身弄壞(誠實限制,mindset §7)。
|
||||
*/
|
||||
async function filterIsBlind(
|
||||
env: Bindings,
|
||||
q: string,
|
||||
f: { owner_id?: string; source?: string; entry_type?: string; library?: string[] },
|
||||
): Promise<boolean> {
|
||||
const hasFilter = !!(f.owner_id || f.source || f.entry_type || (f.library && f.library.length > 0));
|
||||
if (!hasFilter) return false;
|
||||
try {
|
||||
// min_score:0 + 小 topK:只問「拿掉 filter 到底有沒有東西」,不問品質。
|
||||
const probe = await semanticSearch(env, q, { topK: 5, min_score: 0 });
|
||||
return (probe ?? []).length > 0;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// library 多值參數(逗號分隔,portal-auth P1,design §3.3)。空值/全空白 → undefined(=不過濾,
|
||||
// 行為與未帶參數一字不變——向後相容硬驗收)。
|
||||
function parseLibraryParam(raw: string | undefined): string[] | undefined {
|
||||
@@ -289,7 +324,7 @@ entryRoutes.get('/search', async (c) => {
|
||||
// 三態都給人話 capability_hint(給使用者)+ admin_hint(技術細節,給維運者/CC)。
|
||||
// 正常有結果(entries.length>0)完全不受影響,回應形狀不變。
|
||||
if (entries.length === 0) {
|
||||
let empty_reason: 'no_index' | 'no_match' | 'stale_index';
|
||||
let empty_reason: 'no_index' | 'no_match' | 'stale_index' | 'filter_blind';
|
||||
let capability_hint: string;
|
||||
let admin_hint: string;
|
||||
if (hits.length === 0) {
|
||||
@@ -308,6 +343,26 @@ entryRoutes.get('/search', async (c) => {
|
||||
capability_hint =
|
||||
'這個知識庫還沒有整理好的內容可以搜尋——通常是剛裝好、資料還沒同步進來。等同步小幫手跑完再來搜就有了。';
|
||||
admin_hint = `owner_id=${owner_id ?? '(all)'} 範圍 embedded=0 且 pending=0:沒有任何標記 embed:true 的 entry——多半是 ingest 還沒跑(正常的空),少數情況是 ingest 管線沒標 embed 旗標(要查管線)。`;
|
||||
} else if (await filterIsBlind(c.env, q, { owner_id, source, entry_type, library })) {
|
||||
// 🔴 2026-08-11(Arcrun#85 D70,leo21c 實撞,三小時才挖出來的那個病):
|
||||
// 「有 N 筆嵌入資料卻零命中」在這裡曾一律被歸成 no_match,回給使用者
|
||||
// 「換個說法再試試看」——但那台實例的真相是 **Vectorize 的 metadata index
|
||||
// 一個都沒建**(換 index 世代時漏了),所以**每一次**帶 owner_id 的語意查詢
|
||||
// 都回 0,換幾種說法都一樣。把系統故障說成使用者的問題,正是 leo 08-09
|
||||
// 直令禁止的那件事;而且它是靜默的——沒人會因為「搜不到」去查 Vectorize 設定。
|
||||
// 判法不靠猜:**同一句查詢拿掉 metadata filter 再打一次**,有命中就證明
|
||||
// 向量在索引裡、死的是 filter(見 filterIsBlind)。
|
||||
empty_reason = 'filter_blind';
|
||||
capability_hint =
|
||||
'語意搜尋目前故障——你的資料都在,是我們的索引設定壞了,所以每一次語意搜尋都會空手而回。' +
|
||||
'這不是你打的字有問題,換個說法也不會有用。請先用關鍵字搜尋,我們會修好它。';
|
||||
admin_hint =
|
||||
`owner_id=${owner_id ?? '(all)'} 已有 ${status.embedded} 筆嵌入資料;帶 metadata filter 零命中,` +
|
||||
'但同一句查詢拿掉 filter 後有命中 ⇒ 向量在 index 裡,死的是 Vectorize metadata 過濾。' +
|
||||
'成因:該 index 上沒有對應的 metadata index(換 index 世代/改名時最常漏),' +
|
||||
'或既有向量早於 metadata index 的建立時間。修法有先後:**先**建 metadata index' +
|
||||
'(owner_id/entry_type/source/library,acr 的 ensureVectorizeMetadataIndexes 會冪等建),' +
|
||||
'**再** POST /embed/backfill {"reindex":true} 重推到 remaining=0。只做 reindex 不會生效。';
|
||||
} else {
|
||||
empty_reason = 'no_match';
|
||||
capability_hint = '沒有找到符合的內容,換個說法或更具體的關鍵字再試試看。';
|
||||
|
||||
Reference in New Issue
Block a user