autosnippet 3.3.2 → 3.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/cli.js +27 -1
- package/dist/lib/cli/KnowledgeSyncService.d.ts +26 -0
- package/dist/lib/cli/KnowledgeSyncService.js +33 -1
- package/dist/lib/external/mcp/handlers/browse.d.ts +1 -0
- package/dist/lib/external/mcp/handlers/browse.js +2 -1
- package/dist/lib/external/mcp/handlers/consolidated.d.ts +1 -0
- package/dist/lib/external/mcp/handlers/panorama.d.ts +11 -11
- package/dist/lib/external/mcp/handlers/panorama.js +20 -20
- package/dist/lib/external/mcp/handlers/system.d.ts +1 -1
- package/dist/lib/external/mcp/handlers/task.js +2 -1
- package/dist/lib/external/mcp/tools.d.ts +12 -12
- package/dist/lib/external/mcp/tools.js +120 -118
- package/dist/lib/http/middleware/validate.js +7 -3
- package/dist/lib/infrastructure/database/drizzle/schema.d.ts +100 -0
- package/dist/lib/infrastructure/database/drizzle/schema.js +10 -0
- package/dist/lib/infrastructure/database/migrations/005_recipe_source_refs.d.ts +9 -0
- package/dist/lib/infrastructure/database/migrations/005_recipe_source_refs.js +24 -0
- package/dist/lib/infrastructure/vector/HnswVectorAdapter.js +18 -2
- package/dist/lib/injection/ServiceContainer.js +2 -0
- package/dist/lib/injection/modules/KnowledgeModule.d.ts +5 -0
- package/dist/lib/injection/modules/KnowledgeModule.js +80 -0
- package/dist/lib/service/bootstrap/UiStartupTasks.d.ts +45 -0
- package/dist/lib/service/bootstrap/UiStartupTasks.js +101 -0
- package/dist/lib/service/evolution/ConsolidationAdvisor.js +9 -9
- package/dist/lib/service/evolution/ContradictionDetector.js +2 -2
- package/dist/lib/service/evolution/RedundancyAnalyzer.js +2 -2
- package/dist/lib/service/knowledge/SourceRefReconciler.d.ts +68 -0
- package/dist/lib/service/knowledge/SourceRefReconciler.js +309 -0
- package/dist/lib/service/panorama/PanoramaService.d.ts +18 -1
- package/dist/lib/service/panorama/PanoramaService.js +148 -5
- package/dist/lib/service/search/BM25Scorer.d.ts +2 -2
- package/dist/lib/service/search/CoarseRanker.d.ts +7 -6
- package/dist/lib/service/search/CoarseRanker.js +11 -10
- package/dist/lib/service/search/FieldWeightedScorer.d.ts +81 -0
- package/dist/lib/service/search/FieldWeightedScorer.js +318 -0
- package/dist/lib/service/search/MultiSignalRanker.d.ts +2 -2
- package/dist/lib/service/search/MultiSignalRanker.js +1 -1
- package/dist/lib/service/search/SearchEngine.d.ts +8 -7
- package/dist/lib/service/search/SearchEngine.js +59 -10
- package/dist/lib/service/search/SearchTypes.d.ts +23 -3
- package/dist/lib/service/search/SearchTypes.js +6 -1
- package/dist/lib/service/task/IntentExtractor.d.ts +8 -0
- package/dist/lib/service/task/IntentExtractor.js +115 -1
- package/dist/lib/service/task/PrimeSearchPipeline.js +39 -24
- package/dist/lib/service/vector/VectorService.d.ts +3 -0
- package/dist/lib/service/vector/VectorService.js +38 -4
- package/package.json +1 -1
- package/skills/autosnippet-create/SKILL.md +98 -89
- package/skills/autosnippet-devdocs/SKILL.md +55 -60
- package/templates/guard-ci.yml +2 -2
- package/templates/recipes-setup/_template.md +39 -39
|
@@ -57,6 +57,23 @@ export interface PanoramaModuleDetail {
|
|
|
57
57
|
direction: 'in' | 'out';
|
|
58
58
|
weight: number;
|
|
59
59
|
}>;
|
|
60
|
+
/** File groups by subdirectory within the module */
|
|
61
|
+
fileGroups: Array<{
|
|
62
|
+
group: string;
|
|
63
|
+
files: string[];
|
|
64
|
+
count: number;
|
|
65
|
+
}>;
|
|
66
|
+
/** Recipes matched to this module (by category/trigger/file path) */
|
|
67
|
+
recipes: Array<{
|
|
68
|
+
id: string;
|
|
69
|
+
title: string;
|
|
70
|
+
trigger: string;
|
|
71
|
+
kind: string;
|
|
72
|
+
}>;
|
|
73
|
+
/** Files not covered by any matched recipe */
|
|
74
|
+
uncoveredFileCount: number;
|
|
75
|
+
/** Auto-generated structural summary for the agent */
|
|
76
|
+
summary: string;
|
|
60
77
|
}
|
|
61
78
|
export interface PanoramaHealth {
|
|
62
79
|
/** 多维度知识健康雷达 */
|
|
@@ -77,7 +94,7 @@ export declare class PanoramaService {
|
|
|
77
94
|
*/
|
|
78
95
|
getOverview(): PanoramaOverview;
|
|
79
96
|
/**
|
|
80
|
-
* 获取单模块详情
|
|
97
|
+
* 获取单模块详情 (enriched with file groups, recipes, and summary)
|
|
81
98
|
*/
|
|
82
99
|
getModule(moduleName: string): PanoramaModuleDetail | null;
|
|
83
100
|
/**
|
|
@@ -96,7 +96,7 @@ export class PanoramaService {
|
|
|
96
96
|
return overview;
|
|
97
97
|
}
|
|
98
98
|
/**
|
|
99
|
-
* 获取单模块详情
|
|
99
|
+
* 获取单模块详情 (enriched with file groups, recipes, and summary)
|
|
100
100
|
*/
|
|
101
101
|
getModule(moduleName) {
|
|
102
102
|
const result = this.#getOrCompute();
|
|
@@ -104,9 +104,16 @@ export class PanoramaService {
|
|
|
104
104
|
if (!mod) {
|
|
105
105
|
return null;
|
|
106
106
|
}
|
|
107
|
-
//
|
|
108
|
-
const layerName =
|
|
109
|
-
//
|
|
107
|
+
// Layer name: derive from module's own refinedRole (more accurate than level vote)
|
|
108
|
+
const layerName = PanoramaService.#roleToLayer(mod.refinedRole || mod.inferredRole);
|
|
109
|
+
// File groups: group by immediate subdirectory within the module
|
|
110
|
+
const fileGroups = PanoramaService.#groupFilesBySubdir(mod.files);
|
|
111
|
+
// Matched recipes from DB
|
|
112
|
+
const recipes = this.#findModuleRecipes(moduleName, mod);
|
|
113
|
+
// Uncovered file count estimate
|
|
114
|
+
const coveredFileCount = Math.min(recipes.length * 2, mod.fileCount); // rough heuristic
|
|
115
|
+
const uncoveredFileCount = Math.max(0, mod.fileCount - coveredFileCount);
|
|
116
|
+
// Neighbors from DB edges
|
|
110
117
|
const neighbors = [];
|
|
111
118
|
const outNeighbors = this.#db
|
|
112
119
|
.prepare(`SELECT DISTINCT to_id, weight FROM knowledge_edges
|
|
@@ -130,7 +137,143 @@ export class PanoramaService {
|
|
|
130
137
|
weight: Number(n.weight ?? 1),
|
|
131
138
|
});
|
|
132
139
|
}
|
|
133
|
-
|
|
140
|
+
// Generate summary
|
|
141
|
+
const summary = PanoramaService.#generateModuleSummary(mod, layerName, fileGroups, recipes, neighbors);
|
|
142
|
+
return { module: mod, layerName, neighbors, fileGroups, recipes, uncoveredFileCount, summary };
|
|
143
|
+
}
|
|
144
|
+
/* ─── Module detail helpers ─────────────────────── */
|
|
145
|
+
/** Role → layer name mapping (consistent with PanoramaAggregator) */
|
|
146
|
+
static #roleToLayer(role) {
|
|
147
|
+
const map = {
|
|
148
|
+
core: 'Foundation',
|
|
149
|
+
foundation: 'Foundation',
|
|
150
|
+
model: 'Model',
|
|
151
|
+
service: 'Service',
|
|
152
|
+
networking: 'Infrastructure',
|
|
153
|
+
storage: 'Infrastructure',
|
|
154
|
+
ui: 'UI',
|
|
155
|
+
feature: 'Feature',
|
|
156
|
+
config: 'Configuration',
|
|
157
|
+
test: 'Test',
|
|
158
|
+
app: 'Application',
|
|
159
|
+
};
|
|
160
|
+
return map[role] ?? 'Feature';
|
|
161
|
+
}
|
|
162
|
+
/** Group file paths by their immediate subdirectory within the module */
|
|
163
|
+
static #groupFilesBySubdir(files) {
|
|
164
|
+
if (files.length === 0) {
|
|
165
|
+
return [];
|
|
166
|
+
}
|
|
167
|
+
// Find common prefix to determine module root
|
|
168
|
+
const prefix = PanoramaService.#commonPathPrefix(files);
|
|
169
|
+
const groups = new Map();
|
|
170
|
+
for (const f of files) {
|
|
171
|
+
const relative = f.slice(prefix.length);
|
|
172
|
+
const firstSlash = relative.indexOf('/');
|
|
173
|
+
const group = firstSlash > 0 ? relative.slice(0, firstSlash) : '(root)';
|
|
174
|
+
if (!groups.has(group)) {
|
|
175
|
+
groups.set(group, []);
|
|
176
|
+
}
|
|
177
|
+
groups.get(group).push(f);
|
|
178
|
+
}
|
|
179
|
+
return [...groups.entries()]
|
|
180
|
+
.sort((a, b) => b[1].length - a[1].length)
|
|
181
|
+
.map(([group, groupFiles]) => ({ group, files: groupFiles, count: groupFiles.length }));
|
|
182
|
+
}
|
|
183
|
+
static #commonPathPrefix(paths) {
|
|
184
|
+
if (paths.length === 0) {
|
|
185
|
+
return '';
|
|
186
|
+
}
|
|
187
|
+
let prefix = paths[0];
|
|
188
|
+
for (const p of paths) {
|
|
189
|
+
while (!p.startsWith(prefix)) {
|
|
190
|
+
const lastSlash = prefix.lastIndexOf('/');
|
|
191
|
+
if (lastSlash < 0) {
|
|
192
|
+
return '';
|
|
193
|
+
}
|
|
194
|
+
prefix = prefix.slice(0, lastSlash + 1);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return prefix;
|
|
198
|
+
}
|
|
199
|
+
/** Find recipes related to this module by category, trigger, or title match */
|
|
200
|
+
#findModuleRecipes(moduleName, mod) {
|
|
201
|
+
try {
|
|
202
|
+
// Map refined role to typical recipe categories
|
|
203
|
+
const roleCategories = {
|
|
204
|
+
networking: ['Network', 'API', 'Http'],
|
|
205
|
+
storage: ['Storage', 'Database', 'Cache'],
|
|
206
|
+
ui: ['UI', 'View', 'Component'],
|
|
207
|
+
service: ['Service', 'Manager'],
|
|
208
|
+
model: ['Model', 'Entity'],
|
|
209
|
+
core: ['Core', 'Foundation', 'Utility'],
|
|
210
|
+
foundation: ['Core', 'Foundation', 'Utility'],
|
|
211
|
+
feature: ['Feature'],
|
|
212
|
+
};
|
|
213
|
+
const categories = roleCategories[mod.refinedRole] ?? [];
|
|
214
|
+
// Build a LIKE query that matches module name or related categories
|
|
215
|
+
const conditions = [];
|
|
216
|
+
const params = [];
|
|
217
|
+
// Match by module name in title or trigger
|
|
218
|
+
conditions.push('(title LIKE ? OR trigger LIKE ?)');
|
|
219
|
+
params.push(`%${moduleName}%`, `%${moduleName}%`);
|
|
220
|
+
// Match by category
|
|
221
|
+
for (const cat of categories) {
|
|
222
|
+
conditions.push('category = ?');
|
|
223
|
+
params.push(cat);
|
|
224
|
+
}
|
|
225
|
+
const whereClause = conditions.join(' OR ');
|
|
226
|
+
const rows = this.#db
|
|
227
|
+
.prepare(`SELECT id, title, trigger, kind FROM knowledge_entries
|
|
228
|
+
WHERE lifecycle IN ('active', 'staging', 'pending')
|
|
229
|
+
AND (${whereClause})
|
|
230
|
+
ORDER BY lifecycle ASC
|
|
231
|
+
LIMIT 20`)
|
|
232
|
+
.all(...params);
|
|
233
|
+
return rows.map((r) => ({
|
|
234
|
+
id: String(r.id ?? ''),
|
|
235
|
+
title: String(r.title ?? ''),
|
|
236
|
+
trigger: String(r.trigger ?? ''),
|
|
237
|
+
kind: String(r.kind ?? ''),
|
|
238
|
+
}));
|
|
239
|
+
}
|
|
240
|
+
catch {
|
|
241
|
+
return [];
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
/** Generate a structural summary for the agent */
|
|
245
|
+
static #generateModuleSummary(mod, layerName, fileGroups, recipes, neighbors) {
|
|
246
|
+
const lines = [];
|
|
247
|
+
// Identity
|
|
248
|
+
lines.push(`${mod.name} is a ${layerName} layer module (role: ${mod.refinedRole}, confidence: ${(mod.roleConfidence * 100).toFixed(0)}%).`);
|
|
249
|
+
// Structure
|
|
250
|
+
const groupDesc = fileGroups.map((g) => `${g.group}(${g.count})`).join(', ');
|
|
251
|
+
lines.push(`Contains ${mod.fileCount} files in ${fileGroups.length} groups: ${groupDesc}.`);
|
|
252
|
+
// Dependencies
|
|
253
|
+
const dependsOn = neighbors.filter((n) => n.direction === 'out').map((n) => n.name);
|
|
254
|
+
const usedBy = neighbors.filter((n) => n.direction === 'in').map((n) => n.name);
|
|
255
|
+
if (dependsOn.length > 0) {
|
|
256
|
+
lines.push(`Depends on: ${dependsOn.join(', ')}.`);
|
|
257
|
+
}
|
|
258
|
+
if (usedBy.length > 0) {
|
|
259
|
+
lines.push(`Used by: ${usedBy.join(', ')}.`);
|
|
260
|
+
}
|
|
261
|
+
if (dependsOn.length === 0 && usedBy.length === 0) {
|
|
262
|
+
lines.push('No dependency edges recorded (consider running a full bootstrap scan).');
|
|
263
|
+
}
|
|
264
|
+
// Knowledge coverage
|
|
265
|
+
lines.push(`Knowledge coverage: ${recipes.length} recipes matched, ${(mod.coverageRatio * 100).toFixed(0)}% estimated coverage.`);
|
|
266
|
+
if (recipes.length > 0) {
|
|
267
|
+
const recipeList = recipes
|
|
268
|
+
.slice(0, 5)
|
|
269
|
+
.map((r) => r.title)
|
|
270
|
+
.join('; ');
|
|
271
|
+
lines.push(`Key recipes: ${recipeList}.`);
|
|
272
|
+
}
|
|
273
|
+
if (mod.coverageRatio < 0.5) {
|
|
274
|
+
lines.push('Coverage is below 50% — consider submitting knowledge for uncovered file groups.');
|
|
275
|
+
}
|
|
276
|
+
return lines.join(' ');
|
|
134
277
|
}
|
|
135
278
|
/**
|
|
136
279
|
* 获取知识空白区
|
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
*
|
|
7
7
|
* @module BM25Scorer
|
|
8
8
|
*/
|
|
9
|
-
import type { BM25Document, BM25SearchResult } from './SearchTypes.js';
|
|
9
|
+
import type { BM25Document, BM25SearchResult, Scorer } from './SearchTypes.js';
|
|
10
10
|
/** BM25 评分器 */
|
|
11
|
-
export declare class BM25Scorer {
|
|
11
|
+
export declare class BM25Scorer implements Scorer {
|
|
12
12
|
_idIndex: Map<string, number>;
|
|
13
13
|
_totalLength: number;
|
|
14
14
|
avgLength: number;
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* CoarseRanker — 粗排器
|
|
3
|
-
*
|
|
3
|
+
* 多维加权排序(Recall + Semantic + Freshness + Popularity)
|
|
4
|
+
* Quality 维度保留但默认权重 0 — 待场景化区分后按需启用
|
|
4
5
|
*/
|
|
5
6
|
interface RankerCandidate {
|
|
6
|
-
|
|
7
|
+
recallScore?: number;
|
|
7
8
|
score?: number;
|
|
8
9
|
semanticScore?: number;
|
|
9
10
|
title?: string;
|
|
@@ -23,7 +24,7 @@ interface RankerCandidate {
|
|
|
23
24
|
export declare class CoarseRanker {
|
|
24
25
|
#private;
|
|
25
26
|
constructor(options?: {
|
|
26
|
-
|
|
27
|
+
recallWeight?: number;
|
|
27
28
|
semanticWeight?: number;
|
|
28
29
|
qualityWeight?: number;
|
|
29
30
|
freshnessWeight?: number;
|
|
@@ -31,19 +32,19 @@ export declare class CoarseRanker {
|
|
|
31
32
|
});
|
|
32
33
|
/**
|
|
33
34
|
* 粗排
|
|
34
|
-
* @param candidates 需有
|
|
35
|
+
* @param candidates 需有 recallScore、semanticScore 等字段
|
|
35
36
|
* @returns sorted with coarseScore
|
|
36
37
|
*/
|
|
37
38
|
rank(candidates: RankerCandidate[]): {
|
|
38
39
|
coarseScore: number;
|
|
39
40
|
coarseSignals: {
|
|
40
|
-
|
|
41
|
+
recall: number;
|
|
41
42
|
semantic: number;
|
|
42
43
|
quality: number;
|
|
43
44
|
freshness: number;
|
|
44
45
|
popularity: number;
|
|
45
46
|
};
|
|
46
|
-
|
|
47
|
+
recallScore?: number;
|
|
47
48
|
score?: number;
|
|
48
49
|
semanticScore?: number;
|
|
49
50
|
title?: string;
|
|
@@ -1,21 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* CoarseRanker — 粗排器
|
|
3
|
-
*
|
|
3
|
+
* 多维加权排序(Recall + Semantic + Freshness + Popularity)
|
|
4
|
+
* Quality 维度保留但默认权重 0 — 待场景化区分后按需启用
|
|
4
5
|
*/
|
|
5
6
|
export class CoarseRanker {
|
|
6
7
|
#weights;
|
|
7
8
|
constructor(options = {}) {
|
|
8
9
|
this.#weights = {
|
|
9
|
-
|
|
10
|
+
recall: options.recallWeight ?? 0.45,
|
|
10
11
|
semantic: options.semanticWeight ?? 0.3,
|
|
11
|
-
quality: options.qualityWeight ?? 0
|
|
12
|
-
freshness: options.freshnessWeight ?? 0.
|
|
12
|
+
quality: options.qualityWeight ?? 0,
|
|
13
|
+
freshness: options.freshnessWeight ?? 0.15,
|
|
13
14
|
popularity: options.popularityWeight ?? 0.1,
|
|
14
15
|
};
|
|
15
16
|
}
|
|
16
17
|
/**
|
|
17
18
|
* 粗排
|
|
18
|
-
* @param candidates 需有
|
|
19
|
+
* @param candidates 需有 recallScore、semanticScore 等字段
|
|
19
20
|
* @returns sorted with coarseScore
|
|
20
21
|
*/
|
|
21
22
|
rank(candidates) {
|
|
@@ -38,16 +39,16 @@ export class CoarseRanker {
|
|
|
38
39
|
}
|
|
39
40
|
effectiveWeights.semantic = 0;
|
|
40
41
|
}
|
|
41
|
-
//
|
|
42
|
-
const
|
|
42
|
+
// 召回分数 max-based 归一化(保留相对排序,避免 clamp 截断高分差异)
|
|
43
|
+
const maxRecall = candidates.reduce((m, c) => Math.max(m, c.recallScore || c.score || 0), 0) || 1;
|
|
43
44
|
return candidates
|
|
44
45
|
.map((c) => {
|
|
45
|
-
const
|
|
46
|
+
const recall = Math.min((c.recallScore || c.score || 0) / maxRecall, 1.0);
|
|
46
47
|
const semantic = this.#normalize(c.semanticScore || 0);
|
|
47
48
|
const quality = this.#computeQuality(c);
|
|
48
49
|
const freshness = this.#computeFreshness(c);
|
|
49
50
|
const popularity = this.#computePopularity(c);
|
|
50
|
-
const coarseScore =
|
|
51
|
+
const coarseScore = recall * effectiveWeights.recall +
|
|
51
52
|
semantic * effectiveWeights.semantic +
|
|
52
53
|
quality * effectiveWeights.quality +
|
|
53
54
|
freshness * effectiveWeights.freshness +
|
|
@@ -55,7 +56,7 @@ export class CoarseRanker {
|
|
|
55
56
|
return {
|
|
56
57
|
...c,
|
|
57
58
|
coarseScore,
|
|
58
|
-
coarseSignals: {
|
|
59
|
+
coarseSignals: { recall, semantic, quality, freshness, popularity },
|
|
59
60
|
};
|
|
60
61
|
})
|
|
61
62
|
.sort((a, b) => b.coarseScore - a.coarseScore);
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FieldWeightedScorer — 加权字段匹配评分器
|
|
3
|
+
*
|
|
4
|
+
* 替代 BM25Scorer 作为结构化知识库的默认搜索评分引擎。
|
|
5
|
+
*
|
|
6
|
+
* 设计动机:
|
|
7
|
+
* - BM25 将所有字段拼接为文本做统计评分,tokenize 去重导致 TF 恒为 1,BM25F boost 失效
|
|
8
|
+
* - 对于 ~50–500 条结构化知识条目,BM25 的大规模语料假设不成立
|
|
9
|
+
* - FieldWeightedScorer 对每个字段独立打分并加权合并,精确匹配 > token 重叠 > IDF 加权
|
|
10
|
+
*
|
|
11
|
+
* 字段权重:
|
|
12
|
+
* trigger (5.0) > title (3.0) > tags (2.0) > description (1.5) > content (1.0) > facets (0.5)
|
|
13
|
+
*
|
|
14
|
+
* @module FieldWeightedScorer
|
|
15
|
+
*/
|
|
16
|
+
import type { BM25SearchResult, Scorer } from './SearchTypes.js';
|
|
17
|
+
/** 字段加权文档内部表示 */
|
|
18
|
+
interface FieldWeightedDocument {
|
|
19
|
+
id: string;
|
|
20
|
+
fields: {
|
|
21
|
+
trigger: string;
|
|
22
|
+
title: string;
|
|
23
|
+
description: string;
|
|
24
|
+
tags: string[];
|
|
25
|
+
language: string;
|
|
26
|
+
category: string;
|
|
27
|
+
knowledgeType: string;
|
|
28
|
+
};
|
|
29
|
+
tokenizedFields: {
|
|
30
|
+
trigger: string[];
|
|
31
|
+
title: string[];
|
|
32
|
+
description: string[];
|
|
33
|
+
content: string[];
|
|
34
|
+
allUnique: Set<string>;
|
|
35
|
+
};
|
|
36
|
+
meta: Record<string, unknown>;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* FieldWeightedScorer — 加权字段匹配评分器
|
|
40
|
+
*
|
|
41
|
+
* 接口与 BM25Scorer 完全兼容(实现 Scorer 接口),可作为 drop-in 替换。
|
|
42
|
+
*/
|
|
43
|
+
export declare class FieldWeightedScorer implements Scorer {
|
|
44
|
+
avgLength: number;
|
|
45
|
+
docFreq: Record<string, number>;
|
|
46
|
+
documents: (FieldWeightedDocument | null)[];
|
|
47
|
+
totalDocs: number;
|
|
48
|
+
_idIndex: Map<string, number>;
|
|
49
|
+
_totalLength: number;
|
|
50
|
+
constructor();
|
|
51
|
+
/** 添加文档到索引 */
|
|
52
|
+
addDocument(id: string, text: string, meta?: Record<string, unknown>): void;
|
|
53
|
+
/**
|
|
54
|
+
* 移除文档(tombstone + 懒压缩)
|
|
55
|
+
* @returns 是否成功移除
|
|
56
|
+
*/
|
|
57
|
+
removeDocument(id: string): boolean;
|
|
58
|
+
/** 更新文档(remove + add) */
|
|
59
|
+
updateDocument(id: string, text: string, meta?: Record<string, unknown>): void;
|
|
60
|
+
/** 检查文档是否存在 */
|
|
61
|
+
hasDocument(id: string): boolean;
|
|
62
|
+
/** 清空索引 */
|
|
63
|
+
clear(): void;
|
|
64
|
+
/** 压缩 documents 数组,清除 tombstone 空洞 */
|
|
65
|
+
_compact(): void;
|
|
66
|
+
/** 搜索:对每个文档按字段加权评分,返回降序结果 */
|
|
67
|
+
search(query: string, limit?: number): BM25SearchResult[];
|
|
68
|
+
/** 字符串级别匹配评分(用于 trigger / title) */
|
|
69
|
+
_stringMatchScore(query: string, field: string): number;
|
|
70
|
+
/** Token 集合重叠率(查询侧召回) */
|
|
71
|
+
_tokenOverlap(queryTokens: string[], fieldTokens: string[]): number;
|
|
72
|
+
/** IDF 加权 token overlap(用于长文本字段) */
|
|
73
|
+
_idfWeightedOverlap(queryTokens: string[], fieldTokens: string[]): number;
|
|
74
|
+
/** Tag 匹配评分 */
|
|
75
|
+
_tagScore(queryTokens: string[], tags: string[]): number;
|
|
76
|
+
/** Facet 匹配评分(language / category / knowledgeType) */
|
|
77
|
+
_facetScore(queryTokens: string[], fields: FieldWeightedDocument['fields']): number;
|
|
78
|
+
/** 计算 IDF(平滑,始终为正) */
|
|
79
|
+
_idf(token: string): number;
|
|
80
|
+
}
|
|
81
|
+
export {};
|