societyai 0.0.1

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.
Files changed (114) hide show
  1. package/CHANGELOG.md +111 -0
  2. package/LICENSE +21 -0
  3. package/README.md +879 -0
  4. package/dist/builder.d.ts +181 -0
  5. package/dist/builder.d.ts.map +1 -0
  6. package/dist/builder.js +667 -0
  7. package/dist/builder.js.map +1 -0
  8. package/dist/config.d.ts +43 -0
  9. package/dist/config.d.ts.map +1 -0
  10. package/dist/config.js +11 -0
  11. package/dist/config.js.map +1 -0
  12. package/dist/context.d.ts +107 -0
  13. package/dist/context.d.ts.map +1 -0
  14. package/dist/context.js +319 -0
  15. package/dist/context.js.map +1 -0
  16. package/dist/errors.d.ts +31 -0
  17. package/dist/errors.d.ts.map +1 -0
  18. package/dist/errors.js +85 -0
  19. package/dist/errors.js.map +1 -0
  20. package/dist/events.d.ts +219 -0
  21. package/dist/events.d.ts.map +1 -0
  22. package/dist/events.js +395 -0
  23. package/dist/events.js.map +1 -0
  24. package/dist/graph.d.ts +104 -0
  25. package/dist/graph.d.ts.map +1 -0
  26. package/dist/graph.js +366 -0
  27. package/dist/graph.js.map +1 -0
  28. package/dist/index.d.ts +28 -0
  29. package/dist/index.d.ts.map +1 -0
  30. package/dist/index.js +113 -0
  31. package/dist/index.js.map +1 -0
  32. package/dist/logger.d.ts +13 -0
  33. package/dist/logger.d.ts.map +1 -0
  34. package/dist/logger.js +78 -0
  35. package/dist/logger.js.map +1 -0
  36. package/dist/memory.d.ts +146 -0
  37. package/dist/memory.d.ts.map +1 -0
  38. package/dist/memory.js +353 -0
  39. package/dist/memory.js.map +1 -0
  40. package/dist/metrics.d.ts +143 -0
  41. package/dist/metrics.d.ts.map +1 -0
  42. package/dist/metrics.js +271 -0
  43. package/dist/metrics.js.map +1 -0
  44. package/dist/middleware.d.ts +147 -0
  45. package/dist/middleware.d.ts.map +1 -0
  46. package/dist/middleware.js +484 -0
  47. package/dist/middleware.js.map +1 -0
  48. package/dist/models.d.ts +32 -0
  49. package/dist/models.d.ts.map +1 -0
  50. package/dist/models.js +211 -0
  51. package/dist/models.js.map +1 -0
  52. package/dist/patterns.d.ts +6 -0
  53. package/dist/patterns.d.ts.map +1 -0
  54. package/dist/patterns.js +68 -0
  55. package/dist/patterns.js.map +1 -0
  56. package/dist/pipeline.d.ts +84 -0
  57. package/dist/pipeline.d.ts.map +1 -0
  58. package/dist/pipeline.js +569 -0
  59. package/dist/pipeline.js.map +1 -0
  60. package/dist/retry.d.ts +5 -0
  61. package/dist/retry.d.ts.map +1 -0
  62. package/dist/retry.js +70 -0
  63. package/dist/retry.js.map +1 -0
  64. package/dist/society.d.ts +94 -0
  65. package/dist/society.d.ts.map +1 -0
  66. package/dist/society.js +721 -0
  67. package/dist/society.js.map +1 -0
  68. package/dist/strategies.d.ts +55 -0
  69. package/dist/strategies.d.ts.map +1 -0
  70. package/dist/strategies.js +678 -0
  71. package/dist/strategies.js.map +1 -0
  72. package/dist/tools.d.ts +88 -0
  73. package/dist/tools.d.ts.map +1 -0
  74. package/dist/tools.js +366 -0
  75. package/dist/tools.js.map +1 -0
  76. package/dist/types.d.ts +213 -0
  77. package/dist/types.d.ts.map +1 -0
  78. package/dist/types.js +19 -0
  79. package/dist/types.js.map +1 -0
  80. package/dist/validation.d.ts +64 -0
  81. package/dist/validation.d.ts.map +1 -0
  82. package/dist/validation.js +334 -0
  83. package/dist/validation.js.map +1 -0
  84. package/dist/worker-pool.d.ts +17 -0
  85. package/dist/worker-pool.d.ts.map +1 -0
  86. package/dist/worker-pool.js +80 -0
  87. package/dist/worker-pool.js.map +1 -0
  88. package/docs/README.md +468 -0
  89. package/docs/advanced.md +616 -0
  90. package/docs/aggregation-strategies.md +926 -0
  91. package/docs/api-reference.md +771 -0
  92. package/docs/architecture.md +648 -0
  93. package/docs/context-system.md +642 -0
  94. package/docs/event-system.md +1047 -0
  95. package/docs/examples.md +576 -0
  96. package/docs/getting-started.md +564 -0
  97. package/docs/graph-execution.md +389 -0
  98. package/docs/memory-system.md +497 -0
  99. package/docs/metrics-observability.md +560 -0
  100. package/docs/middleware-system.md +1038 -0
  101. package/docs/migration.md +296 -0
  102. package/docs/pipeline-patterns.md +761 -0
  103. package/docs/structured-output.md +612 -0
  104. package/docs/tool-calling.md +491 -0
  105. package/docs/workflows.md +740 -0
  106. package/examples/README.md +234 -0
  107. package/examples/advanced-patterns.ts +115 -0
  108. package/examples/complete-integration.ts +327 -0
  109. package/examples/graph-workflow.ts +161 -0
  110. package/examples/memory-system.ts +155 -0
  111. package/examples/metrics-tracking.ts +243 -0
  112. package/examples/structured-output.ts +231 -0
  113. package/examples/tool-calling.ts +163 -0
  114. package/package.json +94 -0
@@ -0,0 +1,926 @@
1
+ # Aggregation Strategies
2
+
3
+ Les stratégies d'agrégation de SocietyAI permettent de combiner les résultats de plusieurs agents de manière flexible et personnalisable.
4
+
5
+ ## Table des Matières
6
+
7
+ - [Vue d'ensemble](#vue-densemble)
8
+ - [Built-in Strategies](#built-in-strategies)
9
+ - [Custom Strategies](#custom-strategies)
10
+ - [Strategy Composition](#strategy-composition)
11
+ - [Exemples Complets](#exemples-complets)
12
+
13
+ ## Vue d'ensemble
14
+
15
+ Les stratégies d'agrégation définissent comment combiner les résultats de plusieurs agents. Elles permettent de:
16
+
17
+ - **Fusionner** plusieurs réponses en une seule
18
+ - **Voter** pour sélectionner la meilleure réponse
19
+ - **Atteindre un consensus** basé sur la similarité
20
+ - **Pondérer** les contributions selon l'expertise
21
+ - **Filtrer** et sélectionner les meilleurs résultats
22
+ - **Composer** des stratégies complexes
23
+
24
+ ### Principes de Design
25
+
26
+ - **Composable**: Les stratégies peuvent être combinées
27
+ - **Type-safe**: Support complet TypeScript
28
+ - **Model-agnostic**: Fonctionne avec n'importe quel format de résultat
29
+ - **Extensible**: Facile de créer des stratégies personnalisées
30
+
31
+ ## Built-in Strategies
32
+
33
+ ### Concat Strategy
34
+
35
+ Concatène tous les résultats réussis.
36
+
37
+ ```typescript
38
+ import { Strategies } from 'societyai';
39
+
40
+ // Concat avec séparateur par défaut (\n\n)
41
+ const merged = Strategies.concat().aggregate(results);
42
+
43
+ // Concat avec séparateur personnalisé
44
+ const merged = Strategies.concat('\n---\n').aggregate(results);
45
+
46
+ // Avec métadonnées complètes
47
+ const result = Strategies.concat().aggregateFull(results);
48
+ console.log(result.output);
49
+ console.log(result.metadata.successCount);
50
+ console.log(result.contributions);
51
+ ```
52
+
53
+ **Cas d'usage:**
54
+
55
+ - Combiner des perspectives multiples
56
+ - Rassembler des analyses différentes
57
+ - Fusion de rapports
58
+
59
+ ### Merge Strategy
60
+
61
+ Alias pour concat, fusionne les résultats.
62
+
63
+ ```typescript
64
+ const merged = Strategies.merge().aggregate(results);
65
+ ```
66
+
67
+ ### First Strategy
68
+
69
+ Retourne seulement le premier résultat réussi.
70
+
71
+ ```typescript
72
+ const first = Strategies.first().aggregate(results);
73
+ ```
74
+
75
+ **Cas d'usage:**
76
+
77
+ - Première réponse valide suffit
78
+ - Optimisation de performance
79
+ - Fallback en cascade
80
+
81
+ ### Last Strategy
82
+
83
+ Retourne seulement le dernier résultat réussi.
84
+
85
+ ```typescript
86
+ const last = Strategies.last().aggregate(results);
87
+ ```
88
+
89
+ ### Best Of Strategy
90
+
91
+ Sélectionne le meilleur résultat selon un critère.
92
+
93
+ ```typescript
94
+ // Meilleur par longueur (plus long)
95
+ const best = Strategies.bestOf((r) => r.content.length).aggregate(results);
96
+
97
+ // Meilleur par score personnalisé
98
+ const best = Strategies.bestOf((r) => {
99
+ const quality = calculateQuality(r.content);
100
+ const relevance = calculateRelevance(r.content);
101
+ return quality * 0.6 + relevance * 0.4;
102
+ }).aggregate(results);
103
+ ```
104
+
105
+ **Cas d'usage:**
106
+
107
+ - Sélection par qualité
108
+ - Optimisation de pertinence
109
+ - Métriques personnalisées
110
+
111
+ ### Voting Strategy
112
+
113
+ Vote majoritaire simple - sélectionne la réponse la plus fréquente.
114
+
115
+ ```typescript
116
+ const winner = Strategies.voting().aggregate(results);
117
+
118
+ // Avec option pour gérer les égalités
119
+ const winner = Strategies.voting({
120
+ tieBreaker: 'first', // ou 'last', 'random', 'all'
121
+ }).aggregate(results);
122
+ ```
123
+
124
+ **Cas d'usage:**
125
+
126
+ - Décisions démocratiques
127
+ - Validation par consensus
128
+ - Détection d'anomalies
129
+
130
+ ### Weighted Vote Strategy
131
+
132
+ Vote avec pondérations par agent.
133
+
134
+ ```typescript
135
+ // Pondérations égales au nombre d'agents
136
+ const weights = [0.5, 0.3, 0.2]; // Total = 1.0
137
+ const winner = Strategies.weightedVote(weights).aggregate(results);
138
+
139
+ // Pondérations par expertise
140
+ const weights = {
141
+ 'expert-agent': 0.5,
142
+ 'senior-agent': 0.3,
143
+ 'junior-agent': 0.2,
144
+ };
145
+ const winner = Strategies.weightedVote(weights).aggregate(results);
146
+ ```
147
+
148
+ **Cas d'usage:**
149
+
150
+ - Expertise variable
151
+ - Priorisation par rôle
152
+ - Hiérarchies de confiance
153
+
154
+ ### Consensus Strategy
155
+
156
+ Atteint un consensus basé sur la similarité des réponses.
157
+
158
+ ```typescript
159
+ // Consensus avec seuil de 70%
160
+ const consensus = Strategies.consensus(0.7).aggregate(results);
161
+
162
+ // Avec fonction de similarité personnalisée
163
+ const consensus = Strategies.consensus(0.8, {
164
+ similarity: (a, b) => {
165
+ // Calcul de similarité personnalisé
166
+ return calculateSimilarity(a, b);
167
+ },
168
+ }).aggregate(results);
169
+ ```
170
+
171
+ **Comment ça marche:**
172
+
173
+ 1. Compare toutes les paires de résultats
174
+ 2. Calcule la similarité moyenne
175
+ 3. Si similarité ≥ seuil, fusion des résultats
176
+ 4. Sinon, retourne indication de désaccord
177
+
178
+ **Cas d'usage:**
179
+
180
+ - Validation croisée
181
+ - Détection de consensus
182
+ - Assurance qualité
183
+
184
+ ### Majority Strategy
185
+
186
+ Sélectionne les résultats approuvés par la majorité.
187
+
188
+ ```typescript
189
+ // Majorité simple (>50%)
190
+ const majority = Strategies.majority().aggregate(results);
191
+
192
+ // Majorité qualifiée (≥66%)
193
+ const majority = Strategies.majority(0.66).aggregate(results);
194
+ ```
195
+
196
+ ### Average Strategy
197
+
198
+ Moyenne des résultats numériques.
199
+
200
+ ```typescript
201
+ // Pour résultats numériques
202
+ const avg = Strategies.average().aggregate(numericResults);
203
+
204
+ // Avec extraction personnalisée
205
+ const avg = Strategies.average({
206
+ extract: (result) => parseFloat(result.content),
207
+ }).aggregate(results);
208
+ ```
209
+
210
+ **Cas d'usage:**
211
+
212
+ - Estimations numériques
213
+ - Scores moyens
214
+ - Agrégation de métriques
215
+
216
+ ### Filter Strategy
217
+
218
+ Filtre les résultats avant agrégation.
219
+
220
+ ```typescript
221
+ // Filtrer par condition
222
+ const filtered = Strategies.filter((r) => r.success && r.content.length > 10).aggregate(results);
223
+
224
+ // Combiner avec autre stratégie
225
+ const filtered = Strategies.compose(
226
+ Strategies.filter((r) => r.success),
227
+ Strategies.bestOf((r) => r.content.length)
228
+ );
229
+ ```
230
+
231
+ ### Transform Strategy
232
+
233
+ Transforme les résultats avant agrégation.
234
+
235
+ ```typescript
236
+ // Transformer le contenu
237
+ const transformed = Strategies.transform((r) => r.content.toUpperCase()).aggregate(results);
238
+
239
+ // Extraction de données
240
+ const transformed = Strategies.transform((r) => {
241
+ const data = JSON.parse(r.content);
242
+ return data.summary;
243
+ }).aggregate(results);
244
+ ```
245
+
246
+ ### Ranking Strategy
247
+
248
+ Classe les résultats selon un critère.
249
+
250
+ ```typescript
251
+ // Classement par score
252
+ const ranked = Strategies.ranking((r) => calculateScore(r.content), {
253
+ top: 3, // Garder top 3
254
+ order: 'desc', // Ordre décroissant
255
+ }).aggregate(results);
256
+ ```
257
+
258
+ ### Unanimous Strategy
259
+
260
+ Requiert l'unanimité de tous les agents.
261
+
262
+ ```typescript
263
+ // Tous doivent être d'accord
264
+ const unanimous = Strategies.unanimous().aggregate(results);
265
+
266
+ // Avec fonction de comparaison personnalisée
267
+ const unanimous = Strategies.unanimous({
268
+ compare: (a, b) => normalizeText(a) === normalizeText(b),
269
+ }).aggregate(results);
270
+ ```
271
+
272
+ ## Custom Strategies
273
+
274
+ ### Strategy Builder
275
+
276
+ ```typescript
277
+ import { StrategyBuilder } from 'societyai';
278
+
279
+ const customStrategy = StrategyBuilder.create()
280
+ .withName('custom-merge')
281
+ .withDescription('Merges results with custom logic')
282
+ .withAggregator((results) => {
283
+ // Logique personnalisée
284
+ const successful = results.filter((r) => r.success);
285
+
286
+ // Traiter les résultats
287
+ const processed = successful.map((r) => processResult(r.content));
288
+
289
+ // Combiner
290
+ return processed.join('\n\n');
291
+ })
292
+ .build();
293
+
294
+ // Utiliser
295
+ const result = customStrategy.aggregate(results);
296
+ ```
297
+
298
+ ### Function Strategy
299
+
300
+ ```typescript
301
+ import { createStrategy } from 'societyai';
302
+
303
+ // Créer une stratégie depuis une fonction
304
+ const myStrategy = createStrategy('my-strategy', (results) => {
305
+ // Implémentation
306
+ return results
307
+ .filter((r) => r.success)
308
+ .map((r) => r.content)
309
+ .join(' | ');
310
+ });
311
+ ```
312
+
313
+ ### Class-Based Strategy
314
+
315
+ ```typescript
316
+ import { AggregationStrategy, StepResult, AggregationResult } from 'societyai';
317
+
318
+ class SmartMergeStrategy implements AggregationStrategy {
319
+ name = 'smart-merge';
320
+ description = 'Intelligently merges results';
321
+
322
+ constructor(
323
+ private options: {
324
+ minLength?: number;
325
+ maxResults?: number;
326
+ } = {}
327
+ ) {}
328
+
329
+ aggregate(results: StepResult[]): string {
330
+ let successful = results.filter((r) => r.success);
331
+
332
+ // Filtrer par longueur minimum
333
+ if (this.options.minLength) {
334
+ successful = successful.filter((r) => r.content.length >= this.options.minLength!);
335
+ }
336
+
337
+ // Limiter le nombre
338
+ if (this.options.maxResults) {
339
+ successful = successful.slice(0, this.options.maxResults);
340
+ }
341
+
342
+ // Fusionner
343
+ return successful.map((r) => r.content).join('\n\n');
344
+ }
345
+
346
+ aggregateFull(results: StepResult[]): AggregationResult {
347
+ const output = this.aggregate(results);
348
+ const successful = results.filter((r) => r.success);
349
+
350
+ return {
351
+ output,
352
+ metadata: {
353
+ successCount: successful.length,
354
+ failedCount: results.length - successful.length,
355
+ strategy: this.name,
356
+ avgLength: successful.reduce((sum, r) => sum + r.content.length, 0) / successful.length,
357
+ },
358
+ contributions: results.map((r) => ({
359
+ agentId: r.agentId,
360
+ included: r.success,
361
+ weight: 1.0 / successful.length,
362
+ })),
363
+ };
364
+ }
365
+ }
366
+
367
+ // Utilisation
368
+ const strategy = new SmartMergeStrategy({ minLength: 50, maxResults: 5 });
369
+ const result = strategy.aggregate(results);
370
+ ```
371
+
372
+ ## Strategy Composition
373
+
374
+ ### Chaîner des Stratégies
375
+
376
+ ```typescript
377
+ import { chainStrategies } from 'societyai';
378
+
379
+ // Exécuter des stratégies en séquence
380
+ const chained = chainStrategies([
381
+ Strategies.filter((r) => r.success),
382
+ Strategies.transform((r) => r.content.trim()),
383
+ Strategies.bestOf((r) => r.content.length),
384
+ ]);
385
+
386
+ const result = chained.aggregate(results);
387
+ ```
388
+
389
+ ### Stratégies Parallèles
390
+
391
+ ```typescript
392
+ import { parallelStrategies } from 'societyai';
393
+
394
+ // Exécuter plusieurs stratégies et combiner
395
+ const parallel = parallelStrategies(
396
+ [Strategies.bestOf((r) => r.content.length), Strategies.consensus(0.7), Strategies.voting()],
397
+ Strategies.voting()
398
+ ); // Stratégie finale pour combiner
399
+
400
+ const result = parallel.aggregate(results);
401
+ ```
402
+
403
+ ### Compose Helper
404
+
405
+ ```typescript
406
+ import { Strategies } from 'societyai';
407
+
408
+ // Composer plusieurs stratégies
409
+ const composed = Strategies.compose(
410
+ // 1. Filtrer les succès
411
+ Strategies.filter((r) => r.success),
412
+
413
+ // 2. Garder ceux avec contenu substantiel
414
+ Strategies.filter((r) => r.content.length > 100),
415
+
416
+ // 3. Voter pour le meilleur
417
+ Strategies.weightedVote([0.5, 0.3, 0.2])
418
+ );
419
+ ```
420
+
421
+ ### Conditional Strategy
422
+
423
+ ```typescript
424
+ function conditionalStrategy(
425
+ condition: (results: StepResult[]) => boolean,
426
+ strategyA: AggregationStrategy,
427
+ strategyB: AggregationStrategy
428
+ ): AggregationStrategy {
429
+ return {
430
+ name: 'conditional',
431
+ aggregate: (results) => {
432
+ if (condition(results)) {
433
+ return strategyA.aggregate(results);
434
+ } else {
435
+ return strategyB.aggregate(results);
436
+ }
437
+ },
438
+ aggregateFull: (results) => {
439
+ const strategy = condition(results) ? strategyA : strategyB;
440
+ return strategy.aggregateFull(results);
441
+ },
442
+ };
443
+ }
444
+
445
+ // Utilisation
446
+ const strategy = conditionalStrategy(
447
+ (results) => results.length > 5,
448
+ Strategies.voting(), // Beaucoup de résultats -> vote
449
+ Strategies.consensus(0.8) // Peu de résultats -> consensus
450
+ );
451
+ ```
452
+
453
+ ## Exemples Complets
454
+
455
+ ### Exemple 1: Stratégie de Review Multi-Niveau
456
+
457
+ ```typescript
458
+ class MultiLevelReviewStrategy implements AggregationStrategy {
459
+ name = 'multi-level-review';
460
+
461
+ constructor(
462
+ private levels: Array<{
463
+ threshold: number;
464
+ strategy: AggregationStrategy;
465
+ }>
466
+ ) {
467
+ // Trier par threshold décroissant
468
+ this.levels.sort((a, b) => b.threshold - a.threshold);
469
+ }
470
+
471
+ aggregate(results: StepResult[]): string {
472
+ const successRate = results.filter((r) => r.success).length / results.length;
473
+
474
+ // Sélectionner la stratégie selon le taux de succès
475
+ for (const level of this.levels) {
476
+ if (successRate >= level.threshold) {
477
+ return level.strategy.aggregate(results);
478
+ }
479
+ }
480
+
481
+ // Fallback - premier résultat réussi
482
+ return Strategies.first().aggregate(results);
483
+ }
484
+
485
+ aggregateFull(results: StepResult[]): AggregationResult {
486
+ const output = this.aggregate(results);
487
+ const successRate = results.filter((r) => r.success).length / results.length;
488
+
489
+ return {
490
+ output,
491
+ metadata: {
492
+ successCount: results.filter((r) => r.success).length,
493
+ failedCount: results.filter((r) => !r.success).length,
494
+ strategy: this.name,
495
+ successRate,
496
+ levelUsed: this.getLevelUsed(successRate),
497
+ },
498
+ };
499
+ }
500
+
501
+ private getLevelUsed(successRate: number): string {
502
+ for (const level of this.levels) {
503
+ if (successRate >= level.threshold) {
504
+ return level.strategy.name;
505
+ }
506
+ }
507
+ return 'fallback';
508
+ }
509
+ }
510
+
511
+ // Utilisation
512
+ const strategy = new MultiLevelReviewStrategy([
513
+ { threshold: 0.9, strategy: Strategies.consensus(0.9) }, // Excellent
514
+ { threshold: 0.7, strategy: Strategies.consensus(0.7) }, // Bon
515
+ { threshold: 0.5, strategy: Strategies.voting() }, // Moyen
516
+ { threshold: 0.3, strategy: Strategies.bestOf(scoreFunc) }, // Faible
517
+ ]);
518
+ ```
519
+
520
+ ### Exemple 2: Stratégie avec Validation
521
+
522
+ ```typescript
523
+ class ValidatedMergeStrategy implements AggregationStrategy {
524
+ name = 'validated-merge';
525
+
526
+ constructor(
527
+ private validator: (content: string) => boolean,
528
+ private minValidResults: number = 2
529
+ ) {}
530
+
531
+ aggregate(results: StepResult[]): string {
532
+ // Filtrer les résultats valides
533
+ const validResults = results.filter((r) => r.success).filter((r) => this.validator(r.content));
534
+
535
+ if (validResults.length < this.minValidResults) {
536
+ throw new Error(
537
+ `Insufficient valid results: ${validResults.length} < ${this.minValidResults}`
538
+ );
539
+ }
540
+
541
+ // Fusionner les résultats valides
542
+ return validResults.map((r) => r.content).join('\n\n');
543
+ }
544
+
545
+ aggregateFull(results: StepResult[]): AggregationResult {
546
+ const validResults = results.filter((r) => r.success).filter((r) => this.validator(r.content));
547
+
548
+ const output = this.aggregate(results);
549
+
550
+ return {
551
+ output,
552
+ metadata: {
553
+ successCount: results.filter((r) => r.success).length,
554
+ failedCount: results.filter((r) => !r.success).length,
555
+ strategy: this.name,
556
+ validCount: validResults.length,
557
+ invalidCount: results.filter((r) => r.success).length - validResults.length,
558
+ },
559
+ contributions: results.map((r) => {
560
+ const isValid = r.success && this.validator(r.content);
561
+ return {
562
+ agentId: r.agentId,
563
+ included: isValid,
564
+ weight: isValid ? 1.0 / validResults.length : 0,
565
+ };
566
+ }),
567
+ };
568
+ }
569
+ }
570
+
571
+ // Utilisation
572
+ const strategy = new ValidatedMergeStrategy(
573
+ (content) => {
574
+ // Valider le contenu
575
+ return content.length > 50 && !content.includes('error') && isValidJSON(content);
576
+ },
577
+ 3 // Au moins 3 résultats valides requis
578
+ );
579
+ ```
580
+
581
+ ### Exemple 3: Stratégie de Scoring Avancée
582
+
583
+ ```typescript
584
+ interface ScoringCriteria {
585
+ name: string;
586
+ weight: number;
587
+ score: (content: string) => number;
588
+ }
589
+
590
+ class ScoringStrategy implements AggregationStrategy {
591
+ name = 'advanced-scoring';
592
+
593
+ constructor(
594
+ private criteria: ScoringCriteria[],
595
+ private threshold?: number
596
+ ) {
597
+ // Normaliser les poids
598
+ const totalWeight = criteria.reduce((sum, c) => sum + c.weight, 0);
599
+ this.criteria = criteria.map((c) => ({
600
+ ...c,
601
+ weight: c.weight / totalWeight,
602
+ }));
603
+ }
604
+
605
+ private calculateScore(content: string): number {
606
+ return this.criteria.reduce((total, criterion) => {
607
+ const score = criterion.score(content);
608
+ return total + score * criterion.weight;
609
+ }, 0);
610
+ }
611
+
612
+ aggregate(results: StepResult[]): string {
613
+ const scored = results
614
+ .filter((r) => r.success)
615
+ .map((r) => ({
616
+ content: r.content,
617
+ agentId: r.agentId,
618
+ score: this.calculateScore(r.content),
619
+ }))
620
+ .sort((a, b) => b.score - a.score);
621
+
622
+ if (this.threshold !== undefined) {
623
+ // Filtrer par threshold
624
+ const qualified = scored.filter((s) => s.score >= this.threshold!);
625
+ if (qualified.length === 0) {
626
+ throw new Error('No results meet the quality threshold');
627
+ }
628
+ return qualified.map((s) => s.content).join('\n\n');
629
+ }
630
+
631
+ // Retourner le meilleur
632
+ return scored[0]?.content || '';
633
+ }
634
+
635
+ aggregateFull(results: StepResult[]): AggregationResult {
636
+ const scored = results
637
+ .filter((r) => r.success)
638
+ .map((r) => ({
639
+ result: r,
640
+ score: this.calculateScore(r.content),
641
+ }))
642
+ .sort((a, b) => b.score - a.score);
643
+
644
+ const output = this.aggregate(results);
645
+
646
+ return {
647
+ output,
648
+ metadata: {
649
+ successCount: scored.length,
650
+ failedCount: results.length - scored.length,
651
+ strategy: this.name,
652
+ topScore: scored[0]?.score,
653
+ avgScore: scored.reduce((sum, s) => sum + s.score, 0) / scored.length,
654
+ scores: scored.map((s) => ({
655
+ agentId: s.result.agentId,
656
+ score: s.score,
657
+ })),
658
+ },
659
+ contributions: results.map((r) => {
660
+ const scoredResult = scored.find((s) => s.result.agentId === r.agentId);
661
+ return {
662
+ agentId: r.agentId,
663
+ included: scoredResult !== undefined,
664
+ weight: scoredResult?.score,
665
+ };
666
+ }),
667
+ };
668
+ }
669
+ }
670
+
671
+ // Utilisation
672
+ const strategy = new ScoringStrategy(
673
+ [
674
+ {
675
+ name: 'length',
676
+ weight: 0.3,
677
+ score: (content) => Math.min(content.length / 1000, 1),
678
+ },
679
+ {
680
+ name: 'keywords',
681
+ weight: 0.4,
682
+ score: (content) => {
683
+ const keywords = ['important', 'relevant', 'accurate'];
684
+ return keywords.filter((k) => content.toLowerCase().includes(k)).length / keywords.length;
685
+ },
686
+ },
687
+ {
688
+ name: 'structure',
689
+ weight: 0.3,
690
+ score: (content) => {
691
+ const hasIntro = content.includes('Introduction');
692
+ const hasConclusion = content.includes('Conclusion');
693
+ const hasSections = (content.match(/\n## /g) || []).length >= 3;
694
+ return (hasIntro ? 0.33 : 0) + (hasConclusion ? 0.33 : 0) + (hasSections ? 0.34 : 0);
695
+ },
696
+ },
697
+ ],
698
+ 0.6
699
+ ); // Threshold: 60%
700
+ ```
701
+
702
+ ### Exemple 4: Stratégie ML-Based
703
+
704
+ ```typescript
705
+ class MLBasedStrategy implements AggregationStrategy {
706
+ name = 'ml-based';
707
+
708
+ constructor(
709
+ private model: PredictionModel,
710
+ private features: FeatureExtractor
711
+ ) {}
712
+
713
+ aggregate(results: StepResult[]): string {
714
+ // Extraire les features
715
+ const candidates = results
716
+ .filter((r) => r.success)
717
+ .map((r) => ({
718
+ content: r.content,
719
+ features: this.features.extract(r.content),
720
+ }));
721
+
722
+ // Prédire les scores
723
+ const scored = candidates.map((c) => ({
724
+ content: c.content,
725
+ score: this.model.predict(c.features),
726
+ }));
727
+
728
+ // Sélectionner le meilleur
729
+ const best = scored.sort((a, b) => b.score - a.score)[0];
730
+
731
+ return best?.content || '';
732
+ }
733
+
734
+ aggregateFull(results: StepResult[]): AggregationResult {
735
+ const output = this.aggregate(results);
736
+
737
+ return {
738
+ output,
739
+ metadata: {
740
+ successCount: results.filter((r) => r.success).length,
741
+ failedCount: results.filter((r) => !r.success).length,
742
+ strategy: this.name,
743
+ modelUsed: this.model.name,
744
+ },
745
+ };
746
+ }
747
+ }
748
+
749
+ // Utilisation avec un modèle de qualité
750
+ const qualityModel = loadQualityModel();
751
+ const featureExtractor = new TextFeatureExtractor();
752
+
753
+ const strategy = new MLBasedStrategy(qualityModel, featureExtractor);
754
+ ```
755
+
756
+ ## Bonnes Pratiques
757
+
758
+ ### 1. Choisir la Bonne Stratégie
759
+
760
+ ```typescript
761
+ // ✅ Bon - stratégie adaptée au cas d'usage
762
+
763
+ // Pour validation factuelle
764
+ Strategies.consensus(0.8); // Forte similarité requise
765
+
766
+ // Pour opinions diverses
767
+ Strategies.concat('\n\n'); // Garder toutes les perspectives
768
+
769
+ // Pour décision binaire
770
+ Strategies.voting(); // Majorité simple
771
+
772
+ // Pour expertise variable
773
+ Strategies.weightedVote(weights); // Pondération par expertise
774
+ ```
775
+
776
+ ### 2. Gestion des Échecs
777
+
778
+ ```typescript
779
+ // ✅ Bon - gérer les cas où tous échouent
780
+ const safeStrategy = Strategies.compose(
781
+ Strategies.filter((r) => r.success),
782
+ Strategies.bestOf((r) => r.content.length),
783
+ // Fallback si aucun succès
784
+ {
785
+ name: 'fallback',
786
+ aggregate: (results) => {
787
+ if (results.length === 0) {
788
+ return 'No results available';
789
+ }
790
+ return Strategies.first().aggregate(results);
791
+ },
792
+ }
793
+ );
794
+ ```
795
+
796
+ ### 3. Validation des Résultats
797
+
798
+ ```typescript
799
+ // ✅ Bon - valider avant agrégation
800
+ const validatedStrategy = StrategyBuilder.create()
801
+ .withName('validated')
802
+ .withAggregator((results) => {
803
+ const valid = results.filter(
804
+ (r) => r.success && r.content.length > 0 && isValidFormat(r.content)
805
+ );
806
+
807
+ if (valid.length === 0) {
808
+ throw new Error('No valid results to aggregate');
809
+ }
810
+
811
+ return valid.map((r) => r.content).join('\n\n');
812
+ })
813
+ .build();
814
+ ```
815
+
816
+ ### 4. Métadonnées Riches
817
+
818
+ ```typescript
819
+ // ✅ Bon - inclure métadonnées utiles
820
+ const strategy: AggregationStrategy = {
821
+ name: 'rich-metadata',
822
+ aggregate: (results) => {
823
+ // ... logique d'agrégation
824
+ },
825
+ aggregateFull: (results) => {
826
+ const output = this.aggregate(results);
827
+ const successful = results.filter((r) => r.success);
828
+
829
+ return {
830
+ output,
831
+ metadata: {
832
+ successCount: successful.length,
833
+ failedCount: results.length - successful.length,
834
+ strategy: 'rich-metadata',
835
+ // Métadonnées additionnelles
836
+ totalLength: successful.reduce((sum, r) => sum + r.content.length, 0),
837
+ avgLength: successful.reduce((sum, r) => sum + r.content.length, 0) / successful.length,
838
+ timestamp: Date.now(),
839
+ agents: successful.map((r) => r.agentId),
840
+ },
841
+ contributions: results.map((r) => ({
842
+ agentId: r.agentId,
843
+ included: r.success,
844
+ weight: r.success ? 1.0 / successful.length : 0,
845
+ })),
846
+ };
847
+ },
848
+ };
849
+ ```
850
+
851
+ ### 5. Performance
852
+
853
+ ```typescript
854
+ // ✅ Bon - optimiser pour performance
855
+ const efficientStrategy = Strategies.filter((r) => r.success && r.content.length > 50).aggregate(
856
+ results
857
+ );
858
+
859
+ // ❌ Mauvais - opérations coûteuses répétées
860
+ results.forEach((r) => {
861
+ if (expensiveValidation(r)) {
862
+ // Appelé N fois
863
+ // ...
864
+ }
865
+ });
866
+
867
+ // ✅ Mieux - valider une fois
868
+ const validated = results.filter((r) => expensiveValidation(r));
869
+ const result = Strategies.concat().aggregate(validated);
870
+ ```
871
+
872
+ ## API Reference
873
+
874
+ ### `Strategies`
875
+
876
+ **Méthodes statiques:**
877
+
878
+ - `concat(separator?: string): AggregationStrategy`
879
+ - `merge(): AggregationStrategy`
880
+ - `first(): AggregationStrategy`
881
+ - `last(): AggregationStrategy`
882
+ - `bestOf(scorer: Function): AggregationStrategy`
883
+ - `voting(options?: VotingOptions): AggregationStrategy`
884
+ - `weightedVote(weights: number[] | Record<string, number>): AggregationStrategy`
885
+ - `consensus(threshold: number, options?: ConsensusOptions): AggregationStrategy`
886
+ - `majority(threshold?: number): AggregationStrategy`
887
+ - `average(options?: AverageOptions): AggregationStrategy`
888
+ - `filter(predicate: Function): AggregationStrategy`
889
+ - `transform(transformer: Function): AggregationStrategy`
890
+ - `ranking(scorer: Function, options?: RankingOptions): AggregationStrategy`
891
+ - `unanimous(options?: UnanimousOptions): AggregationStrategy`
892
+ - `compose(...strategies: AggregationStrategy[]): AggregationStrategy`
893
+
894
+ ### `StrategyBuilder`
895
+
896
+ **Méthodes:**
897
+
898
+ - `static create(): StrategyBuilder`
899
+ - `withName(name: string): this`
900
+ - `withDescription(description: string): this`
901
+ - `withAggregator(fn: (results: StepResult[]) => string): this`
902
+ - `build(): AggregationStrategy`
903
+
904
+ ### `AggregationStrategy` Interface
905
+
906
+ ```typescript
907
+ interface AggregationStrategy {
908
+ name: string;
909
+ description?: string;
910
+ aggregate: (results: StepResult[]) => string;
911
+ aggregateFull: (results: StepResult[]) => AggregationResult;
912
+ }
913
+ ```
914
+
915
+ ### Helper Functions
916
+
917
+ - `createStrategy(name: string, fn: Function): AggregationStrategy`
918
+ - `chainStrategies(strategies: AggregationStrategy[]): AggregationStrategy`
919
+ - `parallelStrategies(strategies: AggregationStrategy[], final: AggregationStrategy): AggregationStrategy`
920
+
921
+ ## Voir Aussi
922
+
923
+ - [Workflows](./workflows.md) - Utilisation dans les workflows
924
+ - [Pipeline Patterns](./pipeline-patterns.md) - Patterns de pipeline
925
+ - [Graph Execution](./graph-execution.md) - Utilisation dans les graphes
926
+ - [Advanced Patterns](./advanced.md) - Patterns avancés