diff --git a/retrieval/shared-ranking.js b/retrieval/shared-ranking.js index 31014f1..994623e 100644 --- a/retrieval/shared-ranking.js +++ b/retrieval/shared-ranking.js @@ -435,6 +435,7 @@ export async function vectorPreFilter( topK, activeNodes, signal, + { readOnly: true }, ); } catch (error) { if (isAbortError(error)) { diff --git a/tests/shared-ranking.mjs b/tests/shared-ranking.mjs index 64582f3..288f43c 100644 --- a/tests/shared-ranking.mjs +++ b/tests/shared-ranking.mjs @@ -52,6 +52,7 @@ const { addEdge, addNode, createEdge, createEmptyGraph, createNode } = await imp "../graph/graph.js" ); const { rankNodesForTaskContext } = await import("../retrieval/shared-ranking.js"); +const { deriveVectorSpace } = await import("../vector/vector-space.js"); function setTestOverrides(overrides = {}) { globalThis.__stBmeTestOverrides = overrides; @@ -110,6 +111,22 @@ addEdge( }), ); +const config = { + mode: "direct", + source: "direct", + apiUrl: "https://example.com/v1", + apiKey: "", + model: "test-embedding", +}; +const currentVectorSpace = deriveVectorSpace(config, 3); +graph.vectorIndexState.currentVectorSpace = currentVectorSpace; +graph.vectorIndexState.manifest = { + status: "clean", + vectorSpaceId: currentVectorSpace.vectorSpaceId, + observedDim: currentVectorSpace.observedDim, + model: currentVectorSpace.model, +}; + const graphBefore = JSON.stringify(graph); const restore = setTestOverrides({ embedding: { @@ -127,13 +144,6 @@ const restore = setTestOverrides({ }); try { - const config = { - mode: "direct", - source: "direct", - apiUrl: "https://example.com/v1", - apiKey: "", - model: "test-embedding", - }; const first = await rankNodesForTaskContext({ graph, userMessage: "[user]: 中文告白后的关系进展", diff --git a/vector/vector-index.js b/vector/vector-index.js index 11e2484..e26b6bb 100644 --- a/vector/vector-index.js +++ b/vector/vector-index.js @@ -1427,9 +1427,11 @@ export async function findSimilarNodesByText( topK = 10, candidates = null, signal = undefined, + options = {}, ) { if (!text || !graph || !config) return []; throwIfAborted(signal); + const readOnly = options?.readOnly === true; const candidateNodes = Array.isArray(candidates) ? candidates @@ -1441,6 +1443,7 @@ export async function findSimilarNodesByText( ? "direct" : "backend"; const recordSearchTimings = (patch = {}) => { + if (readOnly) return; const state = graph?.vectorIndexState; if (!state || typeof state !== "object" || Array.isArray(state)) return; state.lastSearchTimings = { @@ -1480,7 +1483,7 @@ export async function findSimilarNodesByText( reason: "vector-space-mismatch", resultCount: 0, }); - if (state) { + if (!readOnly && state) { state.dirty = true; state.dirtyReason = "vector-space-mismatch"; state.lastWarning = "向量空间不匹配,已切换到非向量召回并等待重建"; @@ -1508,11 +1511,13 @@ export async function findSimilarNodesByText( queryEmbedMs: roundMs(queryEmbedMs), resultCount: 0, }); - state.dirty = true; - state.dirtyReason = "query-dimension-mismatch"; - state.lastWarning = `查询向量维度 ${queryVec.length} 与索引维度 ${currentDim} 不一致,已切换到非向量召回`; - if (state.manifest) { - state.manifest = { ...state.manifest, status: "stale", lastError: "query-dimension-mismatch" }; + if (!readOnly) { + state.dirty = true; + state.dirtyReason = "query-dimension-mismatch"; + state.lastWarning = `查询向量维度 ${queryVec.length} 与索引维度 ${currentDim} 不一致,已切换到非向量召回`; + if (state.manifest) { + state.manifest = { ...state.manifest, status: "stale", lastError: "query-dimension-mismatch" }; + } } return []; } @@ -1564,9 +1569,11 @@ export async function findSimilarNodesByText( reason: "authority-vector-space-mismatch", resultCount: 0, }); - state.dirty = true; - state.dirtyReason = "authority-vector-space-mismatch"; - state.lastWarning = "Authority 向量空间不匹配,已切换到非向量召回并等待重建"; + if (!readOnly) { + state.dirty = true; + state.dirtyReason = "authority-vector-space-mismatch"; + state.lastWarning = "Authority 向量空间不匹配,已切换到非向量召回并等待重建"; + } return []; } } @@ -1618,13 +1625,15 @@ export async function findSimilarNodesByText( } const message = error?.message || String(error) || "Authority Trivium 查询失败"; const errorPatch = getAuthorityDiagnosticsErrorPatch(error); - markAuthorityVectorStateDirty( - graph, - config, - "authority-trivium-query-failed", - `Authority Trivium 查询失败(${message}),已标记待重建`, - errorPatch, - ); + if (!readOnly) { + markAuthorityVectorStateDirty( + graph, + config, + "authority-trivium-query-failed", + `Authority Trivium 查询失败(${message}),已标记待重建`, + errorPatch, + ); + } recordSearchTimings({ success: false, reason: "authority-trivium-query-failed",