@falai/agent 1.0.0 → 1.0.2
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 +19 -4
- package/dist/cjs/core/Agent.d.ts +10 -0
- package/dist/cjs/core/Agent.d.ts.map +1 -1
- package/dist/cjs/core/Agent.js +22 -3
- package/dist/cjs/core/Agent.js.map +1 -1
- package/dist/cjs/core/BatchExecutor.d.ts.map +1 -1
- package/dist/cjs/core/BatchExecutor.js +8 -0
- package/dist/cjs/core/BatchExecutor.js.map +1 -1
- package/dist/cjs/core/BatchPromptBuilder.d.ts.map +1 -1
- package/dist/cjs/core/BatchPromptBuilder.js +16 -0
- package/dist/cjs/core/BatchPromptBuilder.js.map +1 -1
- package/dist/cjs/core/ResponseEngine.d.ts.map +1 -1
- package/dist/cjs/core/ResponseEngine.js +71 -62
- package/dist/cjs/core/ResponseEngine.js.map +1 -1
- package/dist/cjs/core/ResponseModal.d.ts.map +1 -1
- package/dist/cjs/core/ResponseModal.js +71 -13
- package/dist/cjs/core/ResponseModal.js.map +1 -1
- package/dist/cjs/core/RoutingEngine.d.ts +8 -9
- package/dist/cjs/core/RoutingEngine.d.ts.map +1 -1
- package/dist/cjs/core/RoutingEngine.js +29 -23
- package/dist/cjs/core/RoutingEngine.js.map +1 -1
- package/dist/cjs/types/agent.d.ts +11 -0
- package/dist/cjs/types/agent.d.ts.map +1 -1
- package/dist/cjs/types/routing.d.ts +0 -4
- package/dist/cjs/types/routing.d.ts.map +1 -1
- package/dist/core/Agent.d.ts +10 -0
- package/dist/core/Agent.d.ts.map +1 -1
- package/dist/core/Agent.js +22 -3
- package/dist/core/Agent.js.map +1 -1
- package/dist/core/BatchExecutor.d.ts.map +1 -1
- package/dist/core/BatchExecutor.js +8 -0
- package/dist/core/BatchExecutor.js.map +1 -1
- package/dist/core/BatchPromptBuilder.d.ts.map +1 -1
- package/dist/core/BatchPromptBuilder.js +17 -1
- package/dist/core/BatchPromptBuilder.js.map +1 -1
- package/dist/core/ResponseEngine.d.ts.map +1 -1
- package/dist/core/ResponseEngine.js +71 -62
- package/dist/core/ResponseEngine.js.map +1 -1
- package/dist/core/ResponseModal.d.ts.map +1 -1
- package/dist/core/ResponseModal.js +71 -13
- package/dist/core/ResponseModal.js.map +1 -1
- package/dist/core/RoutingEngine.d.ts +8 -9
- package/dist/core/RoutingEngine.d.ts.map +1 -1
- package/dist/core/RoutingEngine.js +29 -23
- package/dist/core/RoutingEngine.js.map +1 -1
- package/dist/types/agent.d.ts +11 -0
- package/dist/types/agent.d.ts.map +1 -1
- package/dist/types/routing.d.ts +0 -4
- package/dist/types/routing.d.ts.map +1 -1
- package/docs/README.md +7 -17
- package/docs/api/README.md +28 -16
- package/docs/api/overview.md +4 -0
- package/docs/architecture/data-extraction-flow.md +0 -1
- package/docs/core/agent/README.md +36 -0
- package/docs/core/agent/context-management.md +6 -6
- package/docs/core/agent/rules-and-prohibitions.md +113 -0
- package/docs/core/agent/session-management.md +3 -4
- package/docs/core/routing/intelligent-routing.md +12 -8
- package/docs/guides/getting-started/README.md +10 -13
- package/docs/guides/migration/README.md +6 -2
- package/docs/guides/migration/multi-step-execution.md +70 -0
- package/docs/guides/migration/response-modal-refactor.md +2 -2
- package/package.json +1 -1
- package/src/core/Agent.ts +25 -3
- package/src/core/BatchExecutor.ts +10 -0
- package/src/core/BatchPromptBuilder.ts +19 -1
- package/src/core/ResponseEngine.ts +91 -91
- package/src/core/ResponseModal.ts +85 -27
- package/src/core/RoutingEngine.ts +63 -50
- package/src/types/agent.ts +11 -0
- package/src/types/routing.ts +0 -5
|
@@ -2,7 +2,6 @@ import type {
|
|
|
2
2
|
Event,
|
|
3
3
|
AgentOptions,
|
|
4
4
|
StructuredSchema,
|
|
5
|
-
RoutingDecision,
|
|
6
5
|
SessionState,
|
|
7
6
|
AiProvider,
|
|
8
7
|
TemplateContext,
|
|
@@ -36,9 +35,12 @@ export interface RoutingDecisionOutput {
|
|
|
36
35
|
}
|
|
37
36
|
|
|
38
37
|
export interface RoutingEngineOptions {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
/**
|
|
39
|
+
* Score margin the best alternative route must exceed the current route's score
|
|
40
|
+
* by before the agent switches routes. Prevents flip-flopping on marginal differences.
|
|
41
|
+
* @default 15
|
|
42
|
+
*/
|
|
43
|
+
routeSwitchMargin?: number;
|
|
42
44
|
}
|
|
43
45
|
|
|
44
46
|
export interface BuildStepSelectionPromptParams<
|
|
@@ -746,7 +748,8 @@ export class RoutingEngine<TContext = unknown, TData = unknown> {
|
|
|
746
748
|
const optimalRoute = this.selectOptimalRoute(
|
|
747
749
|
eligibleRoutes,
|
|
748
750
|
updatedSession.data || {},
|
|
749
|
-
routingResult.structured.routes
|
|
751
|
+
routingResult.structured.routes,
|
|
752
|
+
updatedSession.currentRoute?.id
|
|
750
753
|
);
|
|
751
754
|
|
|
752
755
|
// If no optimal route found, check why
|
|
@@ -908,42 +911,68 @@ export class RoutingEngine<TContext = unknown, TData = unknown> {
|
|
|
908
911
|
* @returns Route that should be prioritized for continuation
|
|
909
912
|
*/
|
|
910
913
|
selectOptimalRoute(
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
914
|
+
routes: Route<TContext, TData>[],
|
|
915
|
+
data: Partial<TData>,
|
|
916
|
+
routeScores: Record<string, number>,
|
|
917
|
+
currentRouteId?: string
|
|
918
|
+
): Route<TContext, TData> | undefined {
|
|
919
|
+
const completionStatus = this.getRouteCompletionStatus(routes, data);
|
|
920
|
+
const switchMargin = this.options?.routeSwitchMargin ?? 15;
|
|
921
|
+
|
|
922
|
+
// Create weighted scores combining AI intent scores with completion progress
|
|
923
|
+
const weightedScores: Array<{ route: Route<TContext, TData>; score: number }> = [];
|
|
924
|
+
|
|
925
|
+
for (const route of routes) {
|
|
926
|
+
const aiScore = routeScores[route.id] || 0;
|
|
927
|
+
const completionProgress = completionStatus.get(route.id) || 0;
|
|
928
|
+
|
|
929
|
+
// ALWAYS skip fully completed routes to prevent re-entering finished tasks
|
|
930
|
+
if (completionProgress >= 1.0) {
|
|
931
|
+
logger.debug(
|
|
932
|
+
`[RoutingEngine] Excluding completed route: ${route.title} (100% complete)`
|
|
933
|
+
);
|
|
934
|
+
continue;
|
|
935
|
+
}
|
|
919
936
|
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
937
|
+
// Boost partially complete routes that match user intent
|
|
938
|
+
let weightedScore = aiScore;
|
|
939
|
+
if (completionProgress > 0 && completionProgress < 1.0) {
|
|
940
|
+
weightedScore += (completionProgress * 20); // Up to 20 point boost
|
|
941
|
+
}
|
|
923
942
|
|
|
924
|
-
|
|
925
|
-
// Users should not be forced back into completed routes
|
|
926
|
-
if (completionProgress >= 1.0) {
|
|
927
|
-
logger.debug(
|
|
928
|
-
`[RoutingEngine] Excluding completed route: ${route.title} (100% complete)`
|
|
929
|
-
);
|
|
930
|
-
continue;
|
|
943
|
+
weightedScores.push({ route, score: weightedScore });
|
|
931
944
|
}
|
|
932
945
|
|
|
933
|
-
//
|
|
934
|
-
|
|
935
|
-
if (completionProgress > 0 && completionProgress < 1.0) {
|
|
936
|
-
// Boost score for partially complete routes
|
|
937
|
-
weightedScore += (completionProgress * 20); // Up to 20 point boost
|
|
938
|
-
}
|
|
946
|
+
// Sort by weighted score descending
|
|
947
|
+
weightedScores.sort((a, b) => b.score - a.score);
|
|
939
948
|
|
|
940
|
-
weightedScores.
|
|
941
|
-
|
|
949
|
+
if (weightedScores.length === 0) {
|
|
950
|
+
return undefined;
|
|
951
|
+
}
|
|
942
952
|
|
|
943
|
-
|
|
944
|
-
|
|
953
|
+
// Apply sticky routing: if there's a current route, only switch if the
|
|
954
|
+
// best alternative exceeds the current route's score by the configured margin
|
|
955
|
+
if (currentRouteId) {
|
|
956
|
+
const currentEntry = weightedScores.find(e => e.route.id === currentRouteId);
|
|
957
|
+
const bestEntry = weightedScores[0];
|
|
958
|
+
|
|
959
|
+
if (currentEntry && bestEntry.route.id !== currentRouteId) {
|
|
960
|
+
if (bestEntry.score < currentEntry.score + switchMargin) {
|
|
961
|
+
logger.debug(
|
|
962
|
+
`[RoutingEngine] Staying on current route: ${currentEntry.route.title} ` +
|
|
963
|
+
`(current: ${currentEntry.score}, best alternative: ${bestEntry.score}, ` +
|
|
964
|
+
`margin required: ${switchMargin})`
|
|
965
|
+
);
|
|
966
|
+
return currentEntry.route;
|
|
967
|
+
}
|
|
968
|
+
logger.debug(
|
|
969
|
+
`[RoutingEngine] Switching route: ${currentEntry.route.title} → ${bestEntry.route.title} ` +
|
|
970
|
+
`(current: ${currentEntry.score}, alternative: ${bestEntry.score}, ` +
|
|
971
|
+
`margin: ${switchMargin})`
|
|
972
|
+
);
|
|
973
|
+
}
|
|
974
|
+
}
|
|
945
975
|
|
|
946
|
-
if (weightedScores.length > 0) {
|
|
947
976
|
logger.debug(
|
|
948
977
|
`[RoutingEngine] Selected optimal route: ${weightedScores[0].route.title} ` +
|
|
949
978
|
`(AI: ${routeScores[weightedScores[0].route.id]}, ` +
|
|
@@ -953,9 +982,6 @@ export class RoutingEngine<TContext = unknown, TData = unknown> {
|
|
|
953
982
|
return weightedScores[0].route;
|
|
954
983
|
}
|
|
955
984
|
|
|
956
|
-
return undefined;
|
|
957
|
-
}
|
|
958
|
-
|
|
959
985
|
/**
|
|
960
986
|
* Build prompt for step selection within a single route
|
|
961
987
|
* @private
|
|
@@ -1380,17 +1406,4 @@ export class RoutingEngine<TContext = unknown, TData = unknown> {
|
|
|
1380
1406
|
return pc.build();
|
|
1381
1407
|
}
|
|
1382
1408
|
|
|
1383
|
-
decideRouteFromScores(output: RoutingDecision): {
|
|
1384
|
-
routeId: string;
|
|
1385
|
-
maxScore: number;
|
|
1386
|
-
} {
|
|
1387
|
-
// Optionally limit candidates and apply switching threshold
|
|
1388
|
-
const entries = Object.entries(output.routes).sort((a, b) => b[1] - a[1]);
|
|
1389
|
-
const limited = this.options?.maxCandidates
|
|
1390
|
-
? entries.slice(0, this.options.maxCandidates)
|
|
1391
|
-
: entries;
|
|
1392
|
-
const [topId, topScore] = limited[0] || ["", 0];
|
|
1393
|
-
// switchThreshold is enforced by caller when a current route exists
|
|
1394
|
-
return { routeId: topId, maxScore: topScore };
|
|
1395
|
-
}
|
|
1396
1409
|
}
|
package/src/types/agent.ts
CHANGED
|
@@ -106,10 +106,21 @@ export interface AgentOptions<TContext = unknown, TData = unknown> {
|
|
|
106
106
|
persistence?: PersistenceConfig<TData>;
|
|
107
107
|
/** Knowledge base containing any JSON structure the AI should know */
|
|
108
108
|
knowledgeBase?: Record<string, unknown>;
|
|
109
|
+
/** Absolute rules the agent must follow across all routes */
|
|
110
|
+
rules?: Template<TContext, TData>[];
|
|
111
|
+
/** Absolute prohibitions the agent must never do across all routes */
|
|
112
|
+
prohibitions?: Template<TContext, TData>[];
|
|
109
113
|
/** Agent-level data schema defining the complete data structure for collection */
|
|
110
114
|
schema?: StructuredSchema;
|
|
111
115
|
/** Initial data to pre-populate when creating the agent */
|
|
112
116
|
initialData?: Partial<TData>;
|
|
117
|
+
/**
|
|
118
|
+
* Margin (0-100) the best alternative route must exceed the current route's score
|
|
119
|
+
* by before the agent switches. Higher values make the agent "stickier" to the
|
|
120
|
+
* current route. Set to 0 to switch whenever any route scores higher.
|
|
121
|
+
* @default 15
|
|
122
|
+
*/
|
|
123
|
+
routeSwitchMargin?: number;
|
|
113
124
|
}
|
|
114
125
|
|
|
115
126
|
/**
|
package/src/types/routing.ts
CHANGED
|
@@ -8,11 +8,6 @@ export interface RoutingDecision {
|
|
|
8
8
|
contextUpdate?: Record<string, unknown>;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
export interface RoutingDecisionWithRoute extends RoutingDecision {
|
|
12
|
-
selectedRouteId: string;
|
|
13
|
-
maxScore: number;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
11
|
export interface RoutingSchemaOptions {
|
|
17
12
|
extrasSchema?: StructuredSchema;
|
|
18
13
|
}
|