@eucoder/rag 0.2.0
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/README.md +384 -0
- package/dist/ab-testing.d.ts +52 -0
- package/dist/ab-testing.d.ts.map +1 -0
- package/dist/ab-testing.js +144 -0
- package/dist/ab-testing.js.map +1 -0
- package/dist/ab-testing.test.d.ts +2 -0
- package/dist/ab-testing.test.d.ts.map +1 -0
- package/dist/ab-testing.test.js +147 -0
- package/dist/ab-testing.test.js.map +1 -0
- package/dist/agentic-rag.d.ts +23 -0
- package/dist/agentic-rag.d.ts.map +1 -0
- package/dist/agentic-rag.js +170 -0
- package/dist/agentic-rag.js.map +1 -0
- package/dist/agentic-rag.test.d.ts +2 -0
- package/dist/agentic-rag.test.d.ts.map +1 -0
- package/dist/agentic-rag.test.js +174 -0
- package/dist/agentic-rag.test.js.map +1 -0
- package/dist/corrective-rag.d.ts +16 -0
- package/dist/corrective-rag.d.ts.map +1 -0
- package/dist/corrective-rag.js +85 -0
- package/dist/corrective-rag.js.map +1 -0
- package/dist/corrective-rag.test.d.ts +2 -0
- package/dist/corrective-rag.test.d.ts.map +1 -0
- package/dist/corrective-rag.test.js +140 -0
- package/dist/corrective-rag.test.js.map +1 -0
- package/dist/feedback.d.ts +77 -0
- package/dist/feedback.d.ts.map +1 -0
- package/dist/feedback.js +44 -0
- package/dist/feedback.js.map +1 -0
- package/dist/feedback.test.d.ts +2 -0
- package/dist/feedback.test.d.ts.map +1 -0
- package/dist/feedback.test.js +202 -0
- package/dist/feedback.test.js.map +1 -0
- package/dist/hybrid-search.d.ts +14 -0
- package/dist/hybrid-search.d.ts.map +1 -0
- package/dist/hybrid-search.js +70 -0
- package/dist/hybrid-search.js.map +1 -0
- package/dist/hybrid-search.test.d.ts +2 -0
- package/dist/hybrid-search.test.d.ts.map +1 -0
- package/dist/hybrid-search.test.js +93 -0
- package/dist/hybrid-search.test.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/knowledge-graph.d.ts +24 -0
- package/dist/knowledge-graph.d.ts.map +1 -0
- package/dist/knowledge-graph.js +131 -0
- package/dist/knowledge-graph.js.map +1 -0
- package/dist/knowledge-graph.test.d.ts +2 -0
- package/dist/knowledge-graph.test.d.ts.map +1 -0
- package/dist/knowledge-graph.test.js +140 -0
- package/dist/knowledge-graph.test.js.map +1 -0
- package/dist/llm-grader.d.ts +19 -0
- package/dist/llm-grader.d.ts.map +1 -0
- package/dist/llm-grader.js +63 -0
- package/dist/llm-grader.js.map +1 -0
- package/dist/metrics.d.ts +26 -0
- package/dist/metrics.d.ts.map +1 -0
- package/dist/metrics.js +100 -0
- package/dist/metrics.js.map +1 -0
- package/dist/optimizer.d.ts +52 -0
- package/dist/optimizer.d.ts.map +1 -0
- package/dist/optimizer.js +228 -0
- package/dist/optimizer.js.map +1 -0
- package/dist/optimizer.test.d.ts +2 -0
- package/dist/optimizer.test.d.ts.map +1 -0
- package/dist/optimizer.test.js +201 -0
- package/dist/optimizer.test.js.map +1 -0
- package/dist/self-improving.d.ts +85 -0
- package/dist/self-improving.d.ts.map +1 -0
- package/dist/self-improving.js +163 -0
- package/dist/self-improving.js.map +1 -0
- package/dist/self-improving.test.d.ts +2 -0
- package/dist/self-improving.test.d.ts.map +1 -0
- package/dist/self-improving.test.js +234 -0
- package/dist/self-improving.test.js.map +1 -0
- package/dist/types.d.ts +117 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +42 -0
- package/src/ab-testing.test.ts +239 -0
- package/src/ab-testing.ts +214 -0
- package/src/agentic-rag.test.ts +201 -0
- package/src/agentic-rag.ts +220 -0
- package/src/corrective-rag.test.ts +166 -0
- package/src/corrective-rag.ts +115 -0
- package/src/feedback.test.ts +227 -0
- package/src/feedback.ts +118 -0
- package/src/hybrid-search.test.ts +107 -0
- package/src/hybrid-search.ts +86 -0
- package/src/index.ts +57 -0
- package/src/knowledge-graph.test.ts +170 -0
- package/src/knowledge-graph.ts +182 -0
- package/src/llm-grader.ts +69 -0
- package/src/metrics.ts +121 -0
- package/src/optimizer.test.ts +232 -0
- package/src/optimizer.ts +307 -0
- package/src/self-improving.test.ts +341 -0
- package/src/self-improving.ts +239 -0
- package/src/types.ts +139 -0
- package/tsconfig.json +9 -0
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from "vitest";
|
|
2
|
+
import { SelfImprovingRag } from "./self-improving.js";
|
|
3
|
+
import { InMemoryFeedbackStorage } from "./feedback.js";
|
|
4
|
+
import type { RagStrategyConfig } from "./ab-testing.js";
|
|
5
|
+
|
|
6
|
+
describe("SelfImprovingRag", () => {
|
|
7
|
+
let selfImproving: SelfImprovingRag;
|
|
8
|
+
let storage: InMemoryFeedbackStorage;
|
|
9
|
+
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
storage = new InMemoryFeedbackStorage();
|
|
12
|
+
selfImproving = new SelfImprovingRag(
|
|
13
|
+
{
|
|
14
|
+
minFeedbackForOptimization: 10,
|
|
15
|
+
minConfidenceForSuggestion: 0.7,
|
|
16
|
+
minSamplesForABTest: 15,
|
|
17
|
+
autoApplySuggestions: false,
|
|
18
|
+
},
|
|
19
|
+
storage
|
|
20
|
+
);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("should register strategies with parameters", () => {
|
|
24
|
+
const config: RagStrategyConfig = {
|
|
25
|
+
name: "strategy-a",
|
|
26
|
+
type: "corrective",
|
|
27
|
+
params: {},
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const params = {
|
|
31
|
+
minScore: 0.5,
|
|
32
|
+
topK: 10,
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
selfImproving.registerStrategy(config, params);
|
|
36
|
+
|
|
37
|
+
const retrievedParams = selfImproving.getParams("strategy-a");
|
|
38
|
+
expect(retrievedParams).toEqual(params);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("should select best strategy based on metrics", async () => {
|
|
42
|
+
const configA: RagStrategyConfig = {
|
|
43
|
+
name: "strategy-a",
|
|
44
|
+
type: "corrective",
|
|
45
|
+
params: {},
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const configB: RagStrategyConfig = {
|
|
49
|
+
name: "strategy-b",
|
|
50
|
+
type: "hybrid",
|
|
51
|
+
params: {},
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
selfImproving.registerStrategy(configA, { minScore: 0.5 });
|
|
55
|
+
selfImproving.registerStrategy(configB, { semanticWeight: 0.6 });
|
|
56
|
+
|
|
57
|
+
// Add feedback for strategy A (lower quality)
|
|
58
|
+
for (let i = 0; i < 20; i++) {
|
|
59
|
+
await selfImproving.recordFeedback(
|
|
60
|
+
`query ${i}`,
|
|
61
|
+
{ text: `answer ${i}`, citations: [], steps: [], rewrites: 0 },
|
|
62
|
+
{
|
|
63
|
+
rating: 3,
|
|
64
|
+
relevance: 0.6,
|
|
65
|
+
completeness: 0.5,
|
|
66
|
+
citationsQuality: 0.7,
|
|
67
|
+
},
|
|
68
|
+
"strategy-a"
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Add feedback for strategy B (higher quality)
|
|
73
|
+
for (let i = 0; i < 20; i++) {
|
|
74
|
+
await selfImproving.recordFeedback(
|
|
75
|
+
`query ${i}`,
|
|
76
|
+
{ text: `answer ${i}`, citations: [], steps: [], rewrites: 0 },
|
|
77
|
+
{
|
|
78
|
+
rating: 5,
|
|
79
|
+
relevance: 0.9,
|
|
80
|
+
completeness: 0.85,
|
|
81
|
+
citationsQuality: 0.95,
|
|
82
|
+
},
|
|
83
|
+
"strategy-b"
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const best = await selfImproving.selectBestStrategy();
|
|
88
|
+
expect(best).not.toBeNull();
|
|
89
|
+
expect(best?.strategy).toBe("strategy-b");
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it("should record feedback and trigger optimization check", async () => {
|
|
93
|
+
const config: RagStrategyConfig = {
|
|
94
|
+
name: "strategy-a",
|
|
95
|
+
type: "corrective",
|
|
96
|
+
params: {},
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
selfImproving.registerStrategy(config, { minScore: 0.3 });
|
|
100
|
+
|
|
101
|
+
let optimizationTriggered = false;
|
|
102
|
+
selfImproving = new SelfImprovingRag(
|
|
103
|
+
{
|
|
104
|
+
minFeedbackForOptimization: 10,
|
|
105
|
+
onOptimization: () => {
|
|
106
|
+
optimizationTriggered = true;
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
storage
|
|
110
|
+
);
|
|
111
|
+
selfImproving.registerStrategy(config, { minScore: 0.3 });
|
|
112
|
+
|
|
113
|
+
// Add 15 feedback with low relevance to trigger optimization
|
|
114
|
+
for (let i = 0; i < 15; i++) {
|
|
115
|
+
await selfImproving.recordFeedback(
|
|
116
|
+
`query ${i}`,
|
|
117
|
+
{ text: `answer ${i}`, citations: [], steps: [], rewrites: 0 },
|
|
118
|
+
{
|
|
119
|
+
rating: 3,
|
|
120
|
+
relevance: 0.5,
|
|
121
|
+
completeness: 0.6,
|
|
122
|
+
citationsQuality: 0.7,
|
|
123
|
+
},
|
|
124
|
+
"strategy-a"
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
expect(optimizationTriggered).toBe(true);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it("should auto-apply suggestions when configured", async () => {
|
|
132
|
+
const config: RagStrategyConfig = {
|
|
133
|
+
name: "strategy-a",
|
|
134
|
+
type: "corrective",
|
|
135
|
+
params: {},
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
selfImproving = new SelfImprovingRag(
|
|
139
|
+
{
|
|
140
|
+
minFeedbackForOptimization: 10,
|
|
141
|
+
autoApplySuggestions: true,
|
|
142
|
+
minConfidenceForSuggestion: 0.7,
|
|
143
|
+
},
|
|
144
|
+
storage
|
|
145
|
+
);
|
|
146
|
+
selfImproving.registerStrategy(config, { minScore: 0.3 });
|
|
147
|
+
|
|
148
|
+
// Add 15 feedback with low relevance
|
|
149
|
+
for (let i = 0; i < 15; i++) {
|
|
150
|
+
await selfImproving.recordFeedback(
|
|
151
|
+
`query ${i}`,
|
|
152
|
+
{ text: `answer ${i}`, citations: [], steps: [], rewrites: 0 },
|
|
153
|
+
{
|
|
154
|
+
rating: 3,
|
|
155
|
+
relevance: 0.5,
|
|
156
|
+
completeness: 0.6,
|
|
157
|
+
citationsQuality: 0.7,
|
|
158
|
+
},
|
|
159
|
+
"strategy-a"
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const params = selfImproving.getParams("strategy-a");
|
|
164
|
+
// Parameters should be updated if suggestions were applied
|
|
165
|
+
expect(params).toBeDefined();
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it("should run A/B test", async () => {
|
|
169
|
+
const configA: RagStrategyConfig = {
|
|
170
|
+
name: "strategy-a",
|
|
171
|
+
type: "corrective",
|
|
172
|
+
params: {},
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
const configB: RagStrategyConfig = {
|
|
176
|
+
name: "strategy-b",
|
|
177
|
+
type: "hybrid",
|
|
178
|
+
params: {},
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
selfImproving.registerStrategy(configA, {});
|
|
182
|
+
selfImproving.registerStrategy(configB, {});
|
|
183
|
+
|
|
184
|
+
// Add sufficient samples
|
|
185
|
+
for (let i = 0; i < 20; i++) {
|
|
186
|
+
await selfImproving.recordFeedback(
|
|
187
|
+
`query ${i}`,
|
|
188
|
+
{ text: `answer ${i}`, citations: [], steps: [], rewrites: 0 },
|
|
189
|
+
{
|
|
190
|
+
rating: 3,
|
|
191
|
+
relevance: 0.6,
|
|
192
|
+
completeness: 0.5,
|
|
193
|
+
citationsQuality: 0.7,
|
|
194
|
+
},
|
|
195
|
+
"strategy-a"
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
await selfImproving.recordFeedback(
|
|
199
|
+
`query ${i}`,
|
|
200
|
+
{ text: `answer ${i}`, citations: [], steps: [], rewrites: 0 },
|
|
201
|
+
{
|
|
202
|
+
rating: 5,
|
|
203
|
+
relevance: 0.9,
|
|
204
|
+
completeness: 0.85,
|
|
205
|
+
citationsQuality: 0.95,
|
|
206
|
+
},
|
|
207
|
+
"strategy-b"
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const result = await selfImproving.runABTest("strategy-a", "strategy-b");
|
|
212
|
+
expect(result).not.toBeNull();
|
|
213
|
+
expect(result.winner).toBe("B");
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it("should update parameters manually", () => {
|
|
217
|
+
const config: RagStrategyConfig = {
|
|
218
|
+
name: "strategy-a",
|
|
219
|
+
type: "corrective",
|
|
220
|
+
params: {},
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
selfImproving.registerStrategy(config, { minScore: 0.5 });
|
|
224
|
+
|
|
225
|
+
selfImproving.updateParams("strategy-a", { minScore: 0.7, topK: 15 });
|
|
226
|
+
|
|
227
|
+
const params = selfImproving.getParams("strategy-a");
|
|
228
|
+
expect(params?.minScore).toBe(0.7);
|
|
229
|
+
expect(params?.topK).toBe(15);
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
it("should get metrics for strategy", async () => {
|
|
233
|
+
const config: RagStrategyConfig = {
|
|
234
|
+
name: "strategy-a",
|
|
235
|
+
type: "corrective",
|
|
236
|
+
params: {},
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
selfImproving.registerStrategy(config, {});
|
|
240
|
+
|
|
241
|
+
for (let i = 0; i < 5; i++) {
|
|
242
|
+
await selfImproving.recordFeedback(
|
|
243
|
+
`query ${i}`,
|
|
244
|
+
{ text: `answer ${i}`, citations: [], steps: [], rewrites: 0 },
|
|
245
|
+
{
|
|
246
|
+
rating: 4,
|
|
247
|
+
relevance: 0.8,
|
|
248
|
+
completeness: 0.7,
|
|
249
|
+
citationsQuality: 0.9,
|
|
250
|
+
},
|
|
251
|
+
"strategy-a"
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const metrics = await selfImproving.getMetrics("strategy-a");
|
|
256
|
+
expect(metrics).not.toBeNull();
|
|
257
|
+
expect(metrics?.totalQueries).toBe(5);
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
it("should generate report", async () => {
|
|
261
|
+
const config: RagStrategyConfig = {
|
|
262
|
+
name: "strategy-a",
|
|
263
|
+
type: "corrective",
|
|
264
|
+
params: {},
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
selfImproving.registerStrategy(config, { minScore: 0.5 });
|
|
268
|
+
|
|
269
|
+
for (let i = 0; i < 10; i++) {
|
|
270
|
+
await selfImproving.recordFeedback(
|
|
271
|
+
`query ${i}`,
|
|
272
|
+
{ text: `answer ${i}`, citations: [], steps: [], rewrites: 0 },
|
|
273
|
+
{
|
|
274
|
+
rating: 4,
|
|
275
|
+
relevance: 0.7,
|
|
276
|
+
completeness: 0.6,
|
|
277
|
+
citationsQuality: 0.8,
|
|
278
|
+
},
|
|
279
|
+
"strategy-a"
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const report = await selfImproving.generateReport("strategy-a");
|
|
284
|
+
expect(report).toContain("Report Ottimizzazione: strategy-a");
|
|
285
|
+
expect(report).toContain("Metriche Attuali");
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
it("should get suggestions", async () => {
|
|
289
|
+
const config: RagStrategyConfig = {
|
|
290
|
+
name: "strategy-a",
|
|
291
|
+
type: "corrective",
|
|
292
|
+
params: {},
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
selfImproving.registerStrategy(config, { minScore: 0.3 });
|
|
296
|
+
|
|
297
|
+
for (let i = 0; i < 15; i++) {
|
|
298
|
+
await selfImproving.recordFeedback(
|
|
299
|
+
`query ${i}`,
|
|
300
|
+
{ text: `answer ${i}`, citations: [], steps: [], rewrites: 0 },
|
|
301
|
+
{
|
|
302
|
+
rating: 3,
|
|
303
|
+
relevance: 0.5,
|
|
304
|
+
completeness: 0.6,
|
|
305
|
+
citationsQuality: 0.7,
|
|
306
|
+
},
|
|
307
|
+
"strategy-a"
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
const suggestions = await selfImproving.getSuggestions("strategy-a");
|
|
312
|
+
expect(suggestions.length).toBeGreaterThan(0);
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
it("should reset feedback", async () => {
|
|
316
|
+
const config: RagStrategyConfig = {
|
|
317
|
+
name: "strategy-a",
|
|
318
|
+
type: "corrective",
|
|
319
|
+
params: {},
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
selfImproving.registerStrategy(config, {});
|
|
323
|
+
|
|
324
|
+
await selfImproving.recordFeedback(
|
|
325
|
+
"query",
|
|
326
|
+
{ text: "answer", citations: [], steps: [], rewrites: 0 },
|
|
327
|
+
{
|
|
328
|
+
rating: 4,
|
|
329
|
+
relevance: 0.8,
|
|
330
|
+
completeness: 0.7,
|
|
331
|
+
citationsQuality: 0.9,
|
|
332
|
+
},
|
|
333
|
+
"strategy-a"
|
|
334
|
+
);
|
|
335
|
+
|
|
336
|
+
await selfImproving.reset("strategy-a");
|
|
337
|
+
|
|
338
|
+
const metrics = await selfImproving.getMetrics("strategy-a");
|
|
339
|
+
expect(metrics).toBeNull();
|
|
340
|
+
});
|
|
341
|
+
});
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import type { RagAnswer } from "./types.js";
|
|
2
|
+
import type { FeedbackStorage, OptimizationSuggestion } from "./feedback.js";
|
|
3
|
+
import { InMemoryFeedbackStorage } from "./feedback.js";
|
|
4
|
+
import { ABTestFramework, type RagStrategyConfig } from "./ab-testing.js";
|
|
5
|
+
import { ParameterOptimizer, type OptimizableParams } from "./optimizer.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Configurazione per il sistema self-improving
|
|
9
|
+
*/
|
|
10
|
+
export interface SelfImprovingConfig {
|
|
11
|
+
// Soglie per attivazione ottimizzazione
|
|
12
|
+
minFeedbackForOptimization: number;
|
|
13
|
+
minConfidenceForSuggestion: number;
|
|
14
|
+
|
|
15
|
+
// Soglie per A/B testing
|
|
16
|
+
minSamplesForABTest: number;
|
|
17
|
+
|
|
18
|
+
// Auto-apply suggestions
|
|
19
|
+
autoApplySuggestions: boolean;
|
|
20
|
+
|
|
21
|
+
// Callback per notifiche
|
|
22
|
+
onOptimization?: (suggestions: OptimizationSuggestion[]) => void;
|
|
23
|
+
onABTestComplete?: (result: any) => void;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Sistema self-improving per RAG
|
|
28
|
+
* Integra feedback collection, A/B testing e ottimizzazione automatica
|
|
29
|
+
*/
|
|
30
|
+
export class SelfImprovingRag {
|
|
31
|
+
private storage: FeedbackStorage;
|
|
32
|
+
private abFramework: ABTestFramework;
|
|
33
|
+
private optimizer: ParameterOptimizer;
|
|
34
|
+
private config: Required<SelfImprovingConfig>;
|
|
35
|
+
private currentParams: Map<string, OptimizableParams> = new Map();
|
|
36
|
+
|
|
37
|
+
constructor(config?: Partial<SelfImprovingConfig>, storage?: FeedbackStorage) {
|
|
38
|
+
this.storage = storage || new InMemoryFeedbackStorage();
|
|
39
|
+
this.abFramework = new ABTestFramework(this.storage);
|
|
40
|
+
this.optimizer = new ParameterOptimizer(this.storage);
|
|
41
|
+
|
|
42
|
+
this.config = {
|
|
43
|
+
minFeedbackForOptimization: config?.minFeedbackForOptimization ?? 20,
|
|
44
|
+
minConfidenceForSuggestion: config?.minConfidenceForSuggestion ?? 0.7,
|
|
45
|
+
minSamplesForABTest: config?.minSamplesForABTest ?? 30,
|
|
46
|
+
autoApplySuggestions: config?.autoApplySuggestions ?? false,
|
|
47
|
+
onOptimization: config?.onOptimization ?? (() => {}),
|
|
48
|
+
onABTestComplete: config?.onABTestComplete ?? (() => {}),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Registra una strategia RAG
|
|
54
|
+
*/
|
|
55
|
+
registerStrategy(config: RagStrategyConfig, params: OptimizableParams): void {
|
|
56
|
+
this.abFramework.registerStrategy(config);
|
|
57
|
+
this.currentParams.set(config.name, params);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Seleziona la migliore strategia basata su metriche storiche
|
|
62
|
+
*/
|
|
63
|
+
async selectBestStrategy(): Promise<{ strategy: string; params: OptimizableParams } | null> {
|
|
64
|
+
const allMetrics = await this.abFramework.getAllMetrics();
|
|
65
|
+
|
|
66
|
+
if (allMetrics.length === 0) {
|
|
67
|
+
// Nessuna metrica disponibile, usa prima strategia registrata
|
|
68
|
+
const strategies = Array.from(this.currentParams.keys());
|
|
69
|
+
if (strategies.length === 0) return null;
|
|
70
|
+
|
|
71
|
+
const strategy = strategies[0]!;
|
|
72
|
+
return {
|
|
73
|
+
strategy,
|
|
74
|
+
params: this.currentParams.get(strategy)!,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Seleziona strategia con score più alto e confidence sufficiente
|
|
79
|
+
const validMetrics = allMetrics.filter(m => m.confidence >= 0.5);
|
|
80
|
+
if (validMetrics.length === 0) {
|
|
81
|
+
// Usa quella con più feedback
|
|
82
|
+
const best = allMetrics[0]!;
|
|
83
|
+
return {
|
|
84
|
+
strategy: best.strategy,
|
|
85
|
+
params: this.currentParams.get(best.strategy) || {},
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const best = validMetrics[0]!;
|
|
90
|
+
return {
|
|
91
|
+
strategy: best.strategy,
|
|
92
|
+
params: this.currentParams.get(best.strategy) || {},
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Registra feedback per una risposta
|
|
98
|
+
*/
|
|
99
|
+
async recordFeedback(
|
|
100
|
+
query: string,
|
|
101
|
+
answer: RagAnswer,
|
|
102
|
+
feedback: {
|
|
103
|
+
rating: number;
|
|
104
|
+
relevance: number;
|
|
105
|
+
completeness: number;
|
|
106
|
+
citationsQuality: number;
|
|
107
|
+
comments?: string;
|
|
108
|
+
},
|
|
109
|
+
strategy: string
|
|
110
|
+
): Promise<void> {
|
|
111
|
+
await this.abFramework.recordFeedback(
|
|
112
|
+
query,
|
|
113
|
+
answer,
|
|
114
|
+
feedback.rating,
|
|
115
|
+
feedback.relevance,
|
|
116
|
+
feedback.completeness,
|
|
117
|
+
feedback.citationsQuality,
|
|
118
|
+
strategy,
|
|
119
|
+
feedback.comments
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
// Controlla se è tempo di ottimizzare
|
|
123
|
+
await this.checkAndOptimize(strategy);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Controlla se è tempo di ottimizzare e applica suggerimenti
|
|
128
|
+
*/
|
|
129
|
+
private async checkAndOptimize(strategy: string): Promise<void> {
|
|
130
|
+
const feedbacks = await this.storage.getFeedback(strategy);
|
|
131
|
+
|
|
132
|
+
if (feedbacks.length < this.config.minFeedbackForOptimization) {
|
|
133
|
+
return; // Non abbastanza feedback
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const currentParams = this.currentParams.get(strategy);
|
|
137
|
+
if (!currentParams) return;
|
|
138
|
+
|
|
139
|
+
const suggestions = await this.optimizer.analyzeAndSuggest(strategy, currentParams);
|
|
140
|
+
|
|
141
|
+
if (suggestions.length > 0) {
|
|
142
|
+
// Notifica suggerimenti
|
|
143
|
+
this.config.onOptimization(suggestions);
|
|
144
|
+
|
|
145
|
+
// Auto-applica se configurato
|
|
146
|
+
if (this.config.autoApplySuggestions) {
|
|
147
|
+
const optimizedParams = this.optimizer.applySuggestions(
|
|
148
|
+
currentParams,
|
|
149
|
+
suggestions,
|
|
150
|
+
this.config.minConfidenceForSuggestion
|
|
151
|
+
);
|
|
152
|
+
this.currentParams.set(strategy, optimizedParams);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Esegue A/B test tra due strategie
|
|
159
|
+
*/
|
|
160
|
+
async runABTest(strategyA: string, strategyB: string): Promise<any> {
|
|
161
|
+
try {
|
|
162
|
+
const result = await this.abFramework.runABTest(
|
|
163
|
+
strategyA,
|
|
164
|
+
strategyB,
|
|
165
|
+
this.config.minSamplesForABTest
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
this.config.onABTestComplete(result);
|
|
169
|
+
|
|
170
|
+
// Se c'è un vincitore chiaro, aggiorna parametri
|
|
171
|
+
if (result.winner !== "tie" && result.statisticalSignificance > 0.8) {
|
|
172
|
+
const winnerStrategy = result.winner === "A" ? strategyA : strategyB;
|
|
173
|
+
const winnerMetrics = result.winner === "A" ? result.metricsA : result.metricsB;
|
|
174
|
+
|
|
175
|
+
// Log vittoria
|
|
176
|
+
console.log(
|
|
177
|
+
`A/B Test: ${winnerStrategy} vince con score ${winnerMetrics.overallScore.toFixed(3)} ` +
|
|
178
|
+
`(miglioramento: ${(result.improvement * 100).toFixed(1)}%)`
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return result;
|
|
183
|
+
} catch (error) {
|
|
184
|
+
console.warn("A/B test non può essere eseguito:", (error as Error).message);
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Ottiene parametri correnti per una strategia
|
|
191
|
+
*/
|
|
192
|
+
getParams(strategy: string): OptimizableParams | undefined {
|
|
193
|
+
return this.currentParams.get(strategy);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Aggiorna parametri per una strategia
|
|
198
|
+
*/
|
|
199
|
+
updateParams(strategy: string, params: OptimizableParams): void {
|
|
200
|
+
this.currentParams.set(strategy, params);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Ottiene metriche per una strategia
|
|
205
|
+
*/
|
|
206
|
+
async getMetrics(strategy: string) {
|
|
207
|
+
return this.abFramework.getStrategyMetrics(strategy);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Ottiene tutte le metriche
|
|
212
|
+
*/
|
|
213
|
+
async getAllMetrics() {
|
|
214
|
+
return this.abFramework.getAllMetrics();
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Genera report completo
|
|
219
|
+
*/
|
|
220
|
+
async generateReport(strategy: string): Promise<string> {
|
|
221
|
+
const currentParams = this.currentParams.get(strategy) || {};
|
|
222
|
+
return this.optimizer.generateReport(strategy, currentParams);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Ottiene suggerimenti di ottimizzazione
|
|
227
|
+
*/
|
|
228
|
+
async getSuggestions(strategy: string): Promise<OptimizationSuggestion[]> {
|
|
229
|
+
const currentParams = this.currentParams.get(strategy) || {};
|
|
230
|
+
return this.optimizer.analyzeAndSuggest(strategy, currentParams);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Resetta tutti i feedback (per testing)
|
|
235
|
+
*/
|
|
236
|
+
async reset(strategy?: string): Promise<void> {
|
|
237
|
+
await this.storage.clearFeedback(strategy);
|
|
238
|
+
}
|
|
239
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import type { SearchHit } from "@eucode/indexer";
|
|
2
|
+
|
|
3
|
+
export interface Retriever {
|
|
4
|
+
search(query: string, topK: number): Promise<SearchHit[]>;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface KeywordRetriever {
|
|
8
|
+
search(query: string, topK: number): Promise<SearchHit[]>;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface GraderResult {
|
|
12
|
+
relevant: boolean;
|
|
13
|
+
score: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface Grader {
|
|
17
|
+
grade(query: string, hit: SearchHit): Promise<GraderResult>;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface QueryRewriter {
|
|
21
|
+
rewrite(query: string, context: string): Promise<string>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface CorrectiveRagOptions {
|
|
25
|
+
retriever: Retriever;
|
|
26
|
+
grader: Grader;
|
|
27
|
+
rewriter: QueryRewriter;
|
|
28
|
+
maxRewrites?: number;
|
|
29
|
+
minGoodHits?: number;
|
|
30
|
+
minScore?: number;
|
|
31
|
+
webFallback?: (query: string) => Promise<SearchHit[]>;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface RagCitation {
|
|
35
|
+
path: string;
|
|
36
|
+
startLine: number;
|
|
37
|
+
endLine: number;
|
|
38
|
+
text: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface RagAnswer {
|
|
42
|
+
text: string;
|
|
43
|
+
citations: RagCitation[];
|
|
44
|
+
steps: string[];
|
|
45
|
+
rewrites: number;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Hybrid Search Types
|
|
49
|
+
export interface HybridSearchOptions {
|
|
50
|
+
semanticRetriever: Retriever;
|
|
51
|
+
keywordRetriever: KeywordRetriever;
|
|
52
|
+
semanticWeight?: number;
|
|
53
|
+
keywordWeight?: number;
|
|
54
|
+
rrfK?: number;
|
|
55
|
+
topK?: number;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface HybridSearchResult {
|
|
59
|
+
hits: SearchHit[];
|
|
60
|
+
fusionMethod: "rrf" | "weighted";
|
|
61
|
+
semanticCount: number;
|
|
62
|
+
keywordCount: number;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Knowledge Graph Types
|
|
66
|
+
export interface Entity {
|
|
67
|
+
id: string;
|
|
68
|
+
name: string;
|
|
69
|
+
type: string;
|
|
70
|
+
properties: Record<string, unknown>;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface Relation {
|
|
74
|
+
id: string;
|
|
75
|
+
source: string;
|
|
76
|
+
target: string;
|
|
77
|
+
type: string;
|
|
78
|
+
properties: Record<string, unknown>;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface KnowledgeGraph {
|
|
82
|
+
entities: Map<string, Entity>;
|
|
83
|
+
relations: Relation[];
|
|
84
|
+
addEntity(entity: Entity): void;
|
|
85
|
+
addRelation(relation: Relation): void;
|
|
86
|
+
getEntity(id: string): Entity | undefined;
|
|
87
|
+
getRelations(entityId: string): Relation[];
|
|
88
|
+
getConnectedEntities(entityId: string, depth?: number): Entity[];
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export interface EntityExtractor {
|
|
92
|
+
extract(text: string): Promise<Entity[]>;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export interface RelationExtractor {
|
|
96
|
+
extract(text: string, entities: Entity[]): Promise<Relation[]>;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export interface KnowledgeGraphRagOptions {
|
|
100
|
+
retriever: Retriever;
|
|
101
|
+
entityExtractor: EntityExtractor;
|
|
102
|
+
relationExtractor: RelationExtractor;
|
|
103
|
+
graph: KnowledgeGraph;
|
|
104
|
+
topK?: number;
|
|
105
|
+
maxDepth?: number;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export interface KnowledgeGraphRagResult {
|
|
109
|
+
answer: string;
|
|
110
|
+
citations: RagCitation[];
|
|
111
|
+
entities: Entity[];
|
|
112
|
+
relations: Relation[];
|
|
113
|
+
graphContext: string;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Agentic RAG Types
|
|
117
|
+
export interface ReasoningStep {
|
|
118
|
+
thought: string;
|
|
119
|
+
action: string;
|
|
120
|
+
observation: string;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export interface AgenticRagOptions {
|
|
124
|
+
retriever: Retriever;
|
|
125
|
+
grader: Grader;
|
|
126
|
+
rewriter: QueryRewriter;
|
|
127
|
+
maxIterations?: number;
|
|
128
|
+
enableSelfReflection?: boolean;
|
|
129
|
+
enableChainOfThought?: boolean;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export interface AgenticRagResult {
|
|
133
|
+
answer: string;
|
|
134
|
+
citations: RagCitation[];
|
|
135
|
+
reasoningSteps: ReasoningStep[];
|
|
136
|
+
selfReflection?: string;
|
|
137
|
+
iterations: number;
|
|
138
|
+
confidence: number;
|
|
139
|
+
}
|