


























@@ -338,6 +338,7 @@ export class QmdMemoryManager implements MemorySearchManager {
338338private attemptedDuplicateDocumentRepair = false;
339339private readonly sessionWarm = new Set<string>();
340340private collectionPatternFlag: QmdCollectionPatternFlag | null = "--mask";
341+private multiCollectionFilterSupported: boolean | null = null;
341342342343private constructor(params: {
343344agentId: string;
@@ -1150,16 +1151,17 @@ export class QmdMemoryManager implements MemorySearchManager {
11501151timeoutMs: this.qmd.limits.timeoutMs,
11511152});
11521153}
1153-if (collectionNames.length > 1) {
1154-return await this.runQueryAcrossCollections(
1154+const collectionGroups = await this.resolveCollectionSearchGroups(collectionNames);
1155+if (collectionGroups.length > 1) {
1156+return await this.runQueryAcrossCollectionGroups(
11551157trimmed,
11561158limit,
1157-collectionNames,
1159+collectionGroups,
11581160qmdSearchCommand,
11591161);
11601162}
11611163const args = this.buildSearchArgs(qmdSearchCommand, trimmed, limit);
1162-args.push(...this.buildCollectionFilterArgs(collectionNames));
1164+args.push(...this.buildCollectionFilterArgs(collectionGroups[0] ?? collectionNames));
11631165const result = await this.runQmd(args, { timeoutMs: this.qmd.limits.timeoutMs });
11641166return parseQmdQueryJson(result.stdout, result.stderr);
11651167} catch (err) {
@@ -1177,11 +1179,19 @@ export class QmdMemoryManager implements MemorySearchManager {
11771179`qmd ${qmdSearchCommand} does not support configured flags; retrying search with qmd query`,
11781180);
11791181try {
1180-if (collectionNames.length > 1) {
1181-return await this.runQueryAcrossCollections(trimmed, limit, collectionNames, "query");
1182+const collectionGroups = await this.resolveCollectionSearchGroups(collectionNames);
1183+if (collectionGroups.length > 1) {
1184+return await this.runQueryAcrossCollectionGroups(
1185+trimmed,
1186+limit,
1187+collectionGroups,
1188+"query",
1189+);
11821190}
11831191const fallbackArgs = this.buildSearchArgs("query", trimmed, limit);
1184-fallbackArgs.push(...this.buildCollectionFilterArgs(collectionNames));
1192+fallbackArgs.push(
1193+ ...this.buildCollectionFilterArgs(collectionGroups[0] ?? collectionNames),
1194+);
11851195const fallback = await this.runQmd(fallbackArgs, {
11861196timeoutMs: this.qmd.limits.timeoutMs,
11871197});
@@ -2884,24 +2894,53 @@ export class QmdMemoryManager implements MemorySearchManager {
28842894]);
28852895}
288628962887-private async runQueryAcrossCollections(
2897+private async resolveCollectionSearchGroups(collectionNames: string[]): Promise<string[][]> {
2898+if (collectionNames.length <= 1) {
2899+return [collectionNames];
2900+}
2901+if (!(await this.supportsQmdMultiCollectionFilters())) {
2902+return collectionNames.map((collectionName) => [collectionName]);
2903+}
2904+return this.groupCollectionNamesBySource(collectionNames);
2905+}
2906+2907+private async supportsQmdMultiCollectionFilters(): Promise<boolean> {
2908+if (this.multiCollectionFilterSupported !== null) {
2909+return this.multiCollectionFilterSupported;
2910+}
2911+try {
2912+const result = await this.runQmd(["--help"], {
2913+timeoutMs: Math.min(this.qmd.limits.timeoutMs, 5_000),
2914+});
2915+const helpText = `${result.stdout}\n${result.stderr}`;
2916+this.multiCollectionFilterSupported =
2917+/\b(?:one or more collections|collection\(s\)|multiple -c flags)\b/i.test(helpText);
2918+} catch (err) {
2919+this.multiCollectionFilterSupported = false;
2920+log.debug(`qmd multi-collection filter probe failed: ${String(err)}`);
2921+}
2922+return this.multiCollectionFilterSupported;
2923+}
2924+2925+private async runQueryAcrossCollectionGroups(
28882926query: string,
28892927limit: number,
2890-collectionNames: string[],
2928+collectionGroups: string[][],
28912929command: "query" | "search" | "vsearch",
28922930): Promise<QmdQueryResult[]> {
28932931log.debug(
2894-`qmd ${command} multi-collection workaround active (${collectionNames.length} collections)`,
2932+`qmd ${command} multi-source collection grouping active (${collectionGroups.length} groups)`,
28952933);
28962934const bestByResultKey = new Map<string, QmdQueryResult>();
2897-for (const collectionName of collectionNames) {
2935+for (const collectionNames of collectionGroups) {
28982936const args = this.buildSearchArgs(command, query, limit);
2899-args.push("-c", collectionName);
2937+args.push(...this.buildCollectionFilterArgs(collectionNames));
29002938const result = await this.runQmd(args, { timeoutMs: this.qmd.limits.timeoutMs });
29012939const parsed = parseQmdQueryJson(result.stdout, result.stderr);
29022940for (const entry of parsed) {
2941+const defaultCollection = collectionNames.length === 1 ? collectionNames[0] : undefined;
29032942const normalizedHints = this.normalizeDocHints({
2904-preferredCollection: entry.collection ?? collectionName,
2943+preferredCollection: entry.collection ?? defaultCollection,
29052944preferredFile: entry.file,
29062945});
29072946const normalizedDocId =
@@ -2911,7 +2950,7 @@ export class QmdMemoryManager implements MemorySearchManager {
29112950const withCollection = {
29122951 ...entry,
29132952docid: normalizedDocId,
2914-collection: normalizedHints.preferredCollection ?? entry.collection ?? collectionName,
2953+collection: normalizedHints.preferredCollection ?? entry.collection ?? defaultCollection,
29152954file: normalizedHints.preferredFile ?? entry.file,
29162955} satisfies QmdQueryResult;
29172956const resultKey = this.buildQmdResultKey(withCollection);
@@ -2932,6 +2971,17 @@ export class QmdMemoryManager implements MemorySearchManager {
29322971return [...bestByResultKey.values()].toSorted((a, b) => (b.score ?? 0) - (a.score ?? 0));
29332972}
293429732974+private groupCollectionNamesBySource(collectionNames: string[]): string[][] {
2975+const groups = new Map<string, string[]>();
2976+for (const collectionName of collectionNames) {
2977+const source = this.collectionRoots.get(collectionName)?.kind ?? collectionName;
2978+const group = groups.get(source) ?? [];
2979+group.push(collectionName);
2980+groups.set(source, group);
2981+}
2982+return [...groups.values()];
2983+}
2984+29352985private buildQmdResultKey(entry: QmdQueryResult): string | null {
29362986if (typeof entry.docid === "string" && entry.docid.trim().length > 0) {
29372987return `docid:${entry.docid}`;
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。