fix(memory): fix qmd weights from pr review

- Use first-match logic for weight patterns
- Add zod validation for positive weight values
- Defer minscore filtering until after re-ranking

Ensures predictable weight application and proper config validation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Frad LEE 2026-02-26 15:02:33 +08:00
parent 5daa4b0b62
commit 6a717bfa06
2 changed files with 7 additions and 4 deletions

View File

@ -103,6 +103,7 @@ const MemoryQmdSchema = z
update: MemoryQmdUpdateSchema.optional(),
limits: MemoryQmdLimitsSchema.optional(),
scope: SessionSendPolicySchema.optional(),
weights: z.record(z.string(), z.number().positive()).optional(),
})
.strict();

View File

@ -633,6 +633,8 @@ export class QmdMemoryManager implements MemorySearchManager {
}
const qmdSearchCommand = this.qmd.searchMode;
const mcporterEnabled = this.qmd.mcporter.enabled;
// When weights are enabled, defer minScore filtering until after re-ranking
const upstreamMinScore = hasWeights ? 0 : (opts?.minScore ?? 0);
const runSearchAttempt = async (
allowMissingCollectionRepair: boolean,
): Promise<QmdQueryResult[]> => {
@ -644,13 +646,12 @@ export class QmdMemoryManager implements MemorySearchManager {
: qmdSearchCommand === "vsearch"
? "vector_search"
: "deep_search";
const minScore = opts?.minScore ?? 0;
if (collectionNames.length > 1) {
return await this.runMcporterAcrossCollections({
tool,
query: trimmed,
limit: fetchLimit,
minScore,
minScore: upstreamMinScore,
collectionNames,
});
}
@ -659,7 +660,7 @@ export class QmdMemoryManager implements MemorySearchManager {
tool,
query: trimmed,
limit: fetchLimit,
minScore,
minScore: upstreamMinScore,
collection: collectionNames[0],
timeoutMs: this.qmd.limits.timeoutMs,
});
@ -740,11 +741,12 @@ export class QmdMemoryManager implements MemorySearchManager {
const lines = this.extractSnippetLines(snippet);
let score = typeof entry.score === "number" ? entry.score : 0;
// Apply weights
// Apply weights (first matching pattern wins)
if (hasWeights) {
for (const [pattern, weight] of Object.entries(weights)) {
if (this.matchesPath(doc.rel, pattern)) {
score *= weight;
break;
}
}
}