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:
+60
-11
@@ -271,12 +271,24 @@ export async function downloadAndDeploy(
|
||||
process.stdout.write(chalk.gray(' → 開語義查詢:確保 Vectorize index 存在...'));
|
||||
await ensureVectorizeIndex(ctx);
|
||||
// Arcrun#11 根因修復:光建 index 不夠——Vectorize 要 filter 某 metadata 欄位,該欄必須先建
|
||||
// metadata index,否則帶 owner_id/entry_type/source 過濾的語意查詢一律回 0。冪等,隨 index 一起確保。
|
||||
await ensureVectorizeMetadataIndexes(ctx);
|
||||
// metadata index,否則帶 owner_id/entry_type/source/library 過濾的語意查詢一律回 0。冪等,隨 index 一起確保。
|
||||
const created = await ensureVectorizeMetadataIndexes(ctx);
|
||||
console.log(chalk.green(' ✓'));
|
||||
// 新建的 metadata index **只收「建立之後 upsert」的向量** ⇒ 既有向量不重推就永遠 filter 不到。
|
||||
// 這一步不能靜默:leo21c 全盲事件裡,人看到「✓」就以為好了,實際上舊向量一筆都查不到。
|
||||
if (created.length > 0) {
|
||||
console.log(chalk.yellow(
|
||||
` ⚠ 新建了 metadata index(${created.join('/')})。Vectorize 只索引「建立之後寫入」的向量,\n` +
|
||||
' 既有向量必須重推才查得到 → 部署完成後打:\n' +
|
||||
' POST <kbdb>/embed/backfill {"reindex":true} (重複呼叫直到 remaining=0)',
|
||||
));
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(chalk.yellow(' ⚠'));
|
||||
failures.push(`Vectorize index (${KBDB_VECTORIZE_INDEX}): ${e instanceof Error ? e.message : String(e)}`);
|
||||
console.log(chalk.red(' ✗'));
|
||||
failures.push(
|
||||
`Vectorize index (${KBDB_VECTORIZE_INDEX}): ${e instanceof Error ? e.message : String(e)}` +
|
||||
' ⇒ 語意搜尋會「看起來有開、實際全盲」(帶歸屬條件的查詢一律 0 命中),請先修好這項再驗收語意搜尋。',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -463,8 +475,16 @@ async function ensureVectorizeIndex(ctx: DeployContext): Promise<void> {
|
||||
throw new Error(msg);
|
||||
}
|
||||
|
||||
/** embed 過濾用的 Vectorize metadata index 欄位(型別 string;對齊 embedOnWrite 寫入的 metadata)。 */
|
||||
export const KBDB_VECTORIZE_META_FIELDS = ['owner_id', 'entry_type', 'source'] as const;
|
||||
/**
|
||||
* embed 過濾用的 Vectorize metadata index 欄位(型別 string;對齊 embedOnWrite 寫入的 metadata)。
|
||||
*
|
||||
* 🔴 這份清單必須與 `kbdb/src/embed.ts` 的 upsert metadata 欄位**逐欄對齊**:少一欄,
|
||||
* 帶那一欄過濾的語意查詢就永遠回 0 命中(Vectorize 只認「已建 metadata index」的欄位),
|
||||
* **而且不會報錯**——與 bge-m3 換代那次同款的靜默漂移(wiki/mistakes.md「改 A 要連動 B」)。
|
||||
* `library` 是 2026-08-11 補的:portal-auth P1 的「庫」filter 早就拿它在查,清單卻一直停在
|
||||
* 三欄(`kbdb/wrangler.toml` 自己記著「library 待補進該清單」,那張欠條在這裡還掉)。
|
||||
*/
|
||||
export const KBDB_VECTORIZE_META_FIELDS = ['owner_id', 'entry_type', 'source', 'library'] as const;
|
||||
|
||||
/**
|
||||
* 確保 KBDB embed index 上的 metadata index(owner_id/entry_type/source)存在(Arcrun#11 根因修復)。
|
||||
@@ -472,16 +492,18 @@ export const KBDB_VECTORIZE_META_FIELDS = ['owner_id', 'entry_type', 'source'] a
|
||||
* REST `POST /accounts/{id}/vectorize/v2/indexes/{index}/metadata_index/create`(indexType=string)。
|
||||
* 冪等:已存在(409 / already exists)視為成功。async 生效(建立後才 upsert 的向量才會被收錄 → 既有向量另需 reindex)。
|
||||
*/
|
||||
async function ensureVectorizeMetadataIndexes(ctx: DeployContext): Promise<void> {
|
||||
const url = `https://api.cloudflare.com/client/v4/accounts/${ctx.accountId}/vectorize/v2/indexes/${KBDB_VECTORIZE_INDEX}/metadata_index/create`;
|
||||
async function ensureVectorizeMetadataIndexes(ctx: DeployContext): Promise<string[]> {
|
||||
const base = `https://api.cloudflare.com/client/v4/accounts/${ctx.accountId}/vectorize/v2/indexes/${KBDB_VECTORIZE_INDEX}`;
|
||||
const auth = { Authorization: `Bearer ${ctx.apiToken}`, 'Content-Type': 'application/json' };
|
||||
const created: string[] = [];
|
||||
for (const propertyName of KBDB_VECTORIZE_META_FIELDS) {
|
||||
const res = await fetch(url, {
|
||||
const res = await fetch(`${base}/metadata_index/create`, {
|
||||
method: 'POST',
|
||||
headers: { Authorization: `Bearer ${ctx.apiToken}`, 'Content-Type': 'application/json' },
|
||||
headers: auth,
|
||||
body: JSON.stringify({ propertyName, indexType: 'string' }),
|
||||
signal: AbortSignal.timeout(60_000),
|
||||
});
|
||||
if (res.ok) continue;
|
||||
if (res.ok) { created.push(propertyName); continue; }
|
||||
const json = (await res.json().catch(() => null)) as
|
||||
| { success?: boolean; errors?: Array<{ message?: string; code?: number }> }
|
||||
| null;
|
||||
@@ -489,6 +511,33 @@ async function ensureVectorizeMetadataIndexes(ctx: DeployContext): Promise<void>
|
||||
if (res.status === 409 || /already exists|duplicate|conflict/.test(msg)) continue;
|
||||
throw new Error(`metadata_index ${propertyName}: ${msg}`);
|
||||
}
|
||||
|
||||
// 🔴 建完一定要複驗(2026-08-11 立,Arcrun#85 D70 事故的直接教訓)。
|
||||
// leo21c 的現役 index 上**一個 metadata index 都沒有**,於是每一條帶 owner_id 的
|
||||
// 語意查詢(=所有真實使用者路徑,租戶隔離一律帶)都回 0 命中,語意搜尋全盲三天。
|
||||
// 真兇是 arcrun-rag 安裝器把端點寫成 `metadata-index/create`(連字號,CF 回 404,
|
||||
// 正解是底線 `metadata_index/create`),而那支把失敗降級成一行 ⚠ 就宣告安裝成功。
|
||||
// ⇒ **「我發過 create 請求」不等於「index 真的在」**。這一段就是那個等號。
|
||||
// 複驗失敗一律 throw:呼叫端會把它收進 failures 讓部署誠實標紅,而不是
|
||||
// 「語意搜尋開起來了、但全盲」這種最貴的假綠(mindset §7 禁假綠)。
|
||||
const listRes = await fetch(`${base}/metadata_index/list`, { headers: auth, signal: AbortSignal.timeout(60_000) });
|
||||
if (!listRes.ok) {
|
||||
throw new Error(`metadata_index 複驗失敗:list HTTP ${listRes.status}(無法確認 index 是否真的建起來,不當作成功)`);
|
||||
}
|
||||
const listJson = (await listRes.json().catch(() => null)) as
|
||||
| { result?: { metadataIndexes?: Array<{ propertyName?: string }> } }
|
||||
| null;
|
||||
const present = new Set(
|
||||
(listJson?.result?.metadataIndexes ?? []).map(m => String(m.propertyName ?? '')),
|
||||
);
|
||||
const missing = KBDB_VECTORIZE_META_FIELDS.filter(f => !present.has(f));
|
||||
if (missing.length > 0) {
|
||||
throw new Error(
|
||||
`metadata_index 複驗不通過:${missing.join('/')} 不在 ${KBDB_VECTORIZE_INDEX} 上。` +
|
||||
'沒有這些 index,帶 owner_id/library 等條件的語意查詢會一律回 0 命中(不會報錯,只是全盲)。',
|
||||
);
|
||||
}
|
||||
return created;
|
||||
}
|
||||
|
||||
/** 下載 Gitea archive tarball 解壓到暫存目錄,回傳解壓出的 repo root 路徑。
|
||||
|
||||
Reference in New Issue
Block a user