Replace agent handoff system with planning-based router (#16003)
## Overview This PR replaces the dynamic agent handoff system with a more predictable planning-based router that decides upfront how to handle multi-agent coordination. ## Major Changes ### 🔄 Architecture Shift: Handoffs → Planning **Removed:** - `AgentHandoffEntity` and handoff tracking system - `AgentHandoffService` and `AgentHandoffExecutorService` - Dynamic agent-to-agent transfers during execution - Handoff tool generation and description templates **Added:** - `AiRouterService` with two strategies: `simple` (single agent) and `planned` (multi-agent) - `AgentPlanExecutorService` for executing multi-step plans - Plan validation (cycle detection, dependency resolution) - `UnifiedRouterResult` type with discriminated union ### 🤖 New Standard Agents Added two new specialized agents: - **Researcher Agent**: Web search, fact-finding, competitive intelligence - **Code Agent**: TypeScript function generation for serverless workflows ### 🏗️ Router Refactoring (Latest) Split router responsibilities into focused services: - `AiRouterStrategyDeciderService`: Decides simple vs planned strategy - `AiRouterPlanGeneratorService`: Generates and validates execution plans - `AiRouterService`: Coordinates between services (reduced from 426→275 lines) ### ⚙️ Configuration Improvements - Added `outputStrategy` to agent definitions (`direct` vs `synthesize`) - Removed hardcoded special cases for workflow-builder - Added `plannerModel` field to workspace entity - Increased `MAX_STEPS` from 10 to 25 for complex workflows ### 📝 Agent Prompt Refinements Significantly simplified prompts for better clarity: - Workflow Builder: 51→36 lines - Helper: 49→28 lines - Data Manipulator: Enhanced with sorting guidance ### 🔍 Enhanced Debugging - Plan reasoning and step count in data message parts - Router debug info with token usage tracking - Better logging throughout execution pipeline ## Benefits 1. **Simpler Mental Model**: Router decides upfront vs dynamic transfers 2. **Better Predictability**: Users see the plan before execution 3. **Cleaner Architecture**: SRP with focused services 4. **Configuration Over Code**: Agent behavior via config, not hardcoded logic 5. **Plan Validation**: Catches invalid dependencies and cycles ## Migration Notes - Database migration removes `agentHandoff` table - Adds `plannerModel` column to workspace table - No API breaking changes (agent endpoints unchanged) ## Testing - Integration tests updated to remove handoff dependencies - Agent tool test utilities simplified - Plan validation covered by new logic ## Next Steps (Future PRs) - Parallel execution of independent plan steps - Dynamic re-planning based on results - Plan caching for common routing patterns - Error recovery strategies in plan executor
This commit is contained in:
+1
-1
@@ -5,7 +5,7 @@ import { SupportDriver } from 'src/engine/core-modules/twenty-config/interfaces/
|
||||
import {
|
||||
type ModelId,
|
||||
ModelProvider,
|
||||
} from 'src/engine/core-modules/ai/constants/ai-models.const';
|
||||
} from 'src/engine/metadata-modules/ai-models/constants/ai-models.const';
|
||||
import { ClientConfigService } from 'src/engine/core-modules/client-config/services/client-config.service';
|
||||
|
||||
import { ClientConfigController } from './client-config.controller';
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ import { SupportDriver } from 'src/engine/core-modules/twenty-config/interfaces/
|
||||
import {
|
||||
ModelId,
|
||||
ModelProvider,
|
||||
} from 'src/engine/core-modules/ai/constants/ai-models.const';
|
||||
} from 'src/engine/metadata-modules/ai-models/constants/ai-models.const';
|
||||
import { BillingTrialPeriodDTO } from 'src/engine/core-modules/billing/dtos/billing-trial-period.dto';
|
||||
import { CaptchaDriverType } from 'src/engine/core-modules/captcha/interfaces';
|
||||
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ import { Test, type TestingModule } from '@nestjs/testing';
|
||||
import { NodeEnvironment } from 'src/engine/core-modules/twenty-config/interfaces/node-environment.interface';
|
||||
import { SupportDriver } from 'src/engine/core-modules/twenty-config/interfaces/support.interface';
|
||||
|
||||
import { AiModelRegistryService } from 'src/engine/core-modules/ai/services/ai-model-registry.service';
|
||||
import { AiModelRegistryService } from 'src/engine/metadata-modules/ai-models/services/ai-model-registry.service';
|
||||
import { CaptchaDriverType } from 'src/engine/core-modules/captcha/interfaces';
|
||||
import { ClientConfigService } from 'src/engine/core-modules/client-config/services/client-config.service';
|
||||
import { DomainServerConfigService } from 'src/engine/core-modules/domain/domain-server-config/services/domain-server-config.service';
|
||||
|
||||
+44
-13
@@ -5,12 +5,6 @@ import { isNonEmptyString } from '@sniptt/guards';
|
||||
import { NodeEnvironment } from 'src/engine/core-modules/twenty-config/interfaces/node-environment.interface';
|
||||
import { SupportDriver } from 'src/engine/core-modules/twenty-config/interfaces/support.interface';
|
||||
|
||||
import {
|
||||
AI_MODELS,
|
||||
ModelProvider,
|
||||
} from 'src/engine/core-modules/ai/constants/ai-models.const';
|
||||
import { AiModelRegistryService } from 'src/engine/core-modules/ai/services/ai-model-registry.service';
|
||||
import { convertCentsToBillingCredits } from 'src/engine/core-modules/ai/utils/convert-cents-to-billing-credits.util';
|
||||
import {
|
||||
type ClientAIModelConfig,
|
||||
type ClientConfig,
|
||||
@@ -18,6 +12,14 @@ import {
|
||||
import { DomainServerConfigService } from 'src/engine/core-modules/domain/domain-server-config/services/domain-server-config.service';
|
||||
import { PUBLIC_FEATURE_FLAGS } from 'src/engine/core-modules/feature-flag/constants/public-feature-flag.const';
|
||||
import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service';
|
||||
import { convertCentsToBillingCredits } from 'src/engine/metadata-modules/ai-billing/utils/convert-cents-to-billing-credits.util';
|
||||
import {
|
||||
AI_MODELS,
|
||||
DEFAULT_FAST_MODEL,
|
||||
DEFAULT_SMART_MODEL,
|
||||
ModelProvider,
|
||||
} from 'src/engine/metadata-modules/ai-models/constants/ai-models.const';
|
||||
import { AiModelRegistryService } from 'src/engine/metadata-modules/ai-models/services/ai-model-registry.service';
|
||||
|
||||
@Injectable()
|
||||
export class ClientConfigService {
|
||||
@@ -62,13 +64,42 @@ export class ClientConfigService {
|
||||
);
|
||||
|
||||
if (aiModels.length > 0) {
|
||||
aiModels.unshift({
|
||||
modelId: 'auto',
|
||||
label: 'Auto',
|
||||
provider: ModelProvider.NONE,
|
||||
inputCostPer1kTokensInCredits: 0,
|
||||
outputCostPer1kTokensInCredits: 0,
|
||||
});
|
||||
const defaultSpeedModel =
|
||||
this.aiModelRegistryService.getDefaultSpeedModel();
|
||||
const defaultSpeedModelConfig = AI_MODELS.find(
|
||||
(m) => m.modelId === defaultSpeedModel?.modelId,
|
||||
);
|
||||
const defaultSpeedModelLabel =
|
||||
defaultSpeedModelConfig?.label ||
|
||||
defaultSpeedModel?.modelId ||
|
||||
'Default';
|
||||
|
||||
const defaultPerformanceModel =
|
||||
this.aiModelRegistryService.getDefaultPerformanceModel();
|
||||
const defaultPerformanceModelConfig = AI_MODELS.find(
|
||||
(m) => m.modelId === defaultPerformanceModel?.modelId,
|
||||
);
|
||||
const defaultPerformanceModelLabel =
|
||||
defaultPerformanceModelConfig?.label ||
|
||||
defaultPerformanceModel?.modelId ||
|
||||
'Default';
|
||||
|
||||
aiModels.unshift(
|
||||
{
|
||||
modelId: DEFAULT_SMART_MODEL,
|
||||
label: `Smart (${defaultPerformanceModelLabel})`,
|
||||
provider: ModelProvider.NONE,
|
||||
inputCostPer1kTokensInCredits: 0,
|
||||
outputCostPer1kTokensInCredits: 0,
|
||||
},
|
||||
{
|
||||
modelId: DEFAULT_FAST_MODEL,
|
||||
label: `Fast (${defaultSpeedModelLabel})`,
|
||||
provider: ModelProvider.NONE,
|
||||
inputCostPer1kTokensInCredits: 0,
|
||||
outputCostPer1kTokensInCredits: 0,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
const clientConfig: ClientConfig = {
|
||||
|
||||
Reference in New Issue
Block a user