-
Notifications
You must be signed in to change notification settings - Fork 0
Understanding Agent Executor Interceptors
The Agent Executor Interceptors feature is a crucial component of the LLM (Language Model) Agents system. It implements a flexible pipeline architecture that allows for modifying the execution flow of an agent's task. This feature enables developers to add pre-processing, post-processing, or alter the behavior of the executor without changing its core implementation.
The interceptors work on the principle of a chain of responsibility pattern. Each interceptor in the pipeline can perform operations on the execution input, decide whether to pass the execution to the next interceptor, or short-circuit the chain and return a result immediately.
This system is particularly useful for tasks such as:
- Logging
- Modifying context or options
- Implementing complex execution strategies
- Error handling
- Performance monitoring
- Implement a logging interceptor to automatically log all agent interactions.
final readonly class LoggingInterceptor implements ExecutorInterceptorInterface
{
public function __construct(
private LoggerInterface $logger
) {}
public function execute(ExecutionInput $input, InterceptorHandler $next): Execution
{
$this->logger->info('Executing agent: ' . $input->agent);
$startTime = microtime(true);
$execution = $next($input);
$duration = microtime(true) - $startTime;
$this->logger->info('Execution completed', [
'agent' => $input->agent,
'duration' => $duration,
'resultType' => $execution->result::class,
]);
return $execution;
}
}
- Create an interceptor that selects the most appropriate language model based on the input complexity.
final class DynamicModelSelector implements ExecutorInterceptorInterface
{
public function execute(ExecutionInput $input, InterceptorHandler $next): Execution
{
$complexity = $this->analyzeComplexity($input->prompt);
$model = match (true) {
$complexity > 0.8 => OpenAIModel::Gpt4o,
$complexity > 0.5 => OpenAIModel::Gpt4oMini,
default => OpenAIModel::Gpt3Turbo,
};
$newInput = $input->withOptions($input->options->withModel($model));
return $next($newInput);
}
private function analyzeComplexity(string|\Stringable $prompt): float
{
// Implement complexity analysis logic here
}
}
- Implement a rate-limiting interceptor to prevent overuse of the LLM API.
final class RateLimitInterceptor implements ExecutorInterceptorInterface
{
public function __construct(
private RateLimiterInterface $limiter
) {}
public function execute(ExecutionInput $input, InterceptorHandler $next): Execution
{
if (!$this->limiter->consume(1)->isAccepted()) {
throw new RateLimitException('API rate limit exceeded');
}
return $next($input);
}
}
- Create an interceptor that enriches the execution context with additional information.
final class ContextEnrichmentInterceptor implements ExecutorInterceptorInterface
{
public function __construct(
private UserRepositoryInterface $userRepository,
private PreferencesServiceInterface $preferencesService
) {}
public function execute(ExecutionInput $input, InterceptorHandler $next): Execution
{
$user = $this->userRepository->findByUuid($input->context->getUserUuid());
$preferences = $this->preferencesService->getForUser($user);
$enrichedContext = $input->context->withUserPreferences($preferences);
$newInput = $input->withContext($enrichedContext);
return $next($newInput);
}
}
- Implement an interceptor that attempts to recover from certain types of errors.
final class ErrorRecoveryInterceptor implements ExecutorInterceptorInterface
{
public function execute(ExecutionInput $input, InterceptorHandler $next): Execution
{
try {
return $next($input);
} catch (TokenLimitExceededException $e) {
$newInput = $input->withOptions(
$input->options->with(Option::MaxTokens, $e->currentLimit + 500)
);
return $next($newInput);
} catch (TimeoutException) {
sleep(5);
return $next($input);
}
}
}
-
Interceptor Order: The order in which interceptors are added to the pipeline is significant.
-
Option Name:
withInterceptor
- Default Value:
[]
(empty array) - Description: Adds one or more interceptors to the executor's pipeline.
$executor = $executor->withInterceptor( new LoggingInterceptor($logger), new RateLimitInterceptor($limiter), new ErrorRecoveryInterceptor() );
- Default Value:
-
-
Execution Options: Various options can be set for each execution.
-
Option Name:
withOptions
- Default Value: Depends on the specific option
- Description: Sets options for the execution, such as model, temperature, max tokens, etc.
$input = $input->withOptions( $input->options ->withModel(OpenAIModel::Gpt4oMini) ->withTemperature(0.7) ->withMaxTokens(150) );
-
- Purpose and Functionality: Defines the contract for agent executors.
-
Interaction with Main Feature: Provides the main method
execute
that the interceptors wrap around.
- Purpose and Functionality: Represents the immutable input for an agent execution.
- Interaction with Main Feature: Passed through the interceptor chain, allowing modifications at each step.
- Purpose and Functionality: Represents the result of an agent's execution.
- Interaction with Main Feature: Returned by the execute method and can be modified by interceptors.
- Purpose and Functionality: Handles the invocation of the next interceptor in the chain.
- Interaction with Main Feature: Passed to each interceptor to allow continuation of the chain.
- Purpose and Functionality: Implements the execution pipeline, managing the flow through interceptors.
- Interaction with Main Feature: Core class that orchestrates the interceptor chain.
classDiagram
class ExecutorInterface {
+execute(agent: string, prompt: string|Stringable|Prompt, context: ContextInterface, options: OptionsInterface, promptContext: PromptContextInterface) Execution
+withInterceptor(interceptor: ExecutorInterceptorInterface) ExecutorInterface
}
class ExecutorInterceptorInterface {
+execute(input: ExecutionInput, next: InterceptorHandler) Execution
}
class ExecutionInput {
+agent: string
+prompt: string|Stringable|PromptInterface
+context: ContextInterface
+options: OptionsInterface
+promptContext: PromptContextInterface
}
class Execution {
+result: Response
+prompt: PromptInterface
}
class InterceptorHandler {
-executor: ExecutorInterface
+__invoke(input: ExecutionInput) Execution
}
class ExecutorPipeline {
-interceptors: ExecutorInterceptorInterface[]
-offset: int
+execute(agent: string, prompt: string|Stringable|Prompt, context: ContextInterface, options: OptionsInterface, promptContext: PromptContextInterface) Execution
+withInterceptor(interceptor: ExecutorInterceptorInterface) ExecutorInterface
}
ExecutorInterface <|-- ExecutorPipeline
ExecutorPipeline o-- ExecutorInterceptorInterface
ExecutorInterceptorInterface ..> ExecutionInput
ExecutorInterceptorInterface ..> Execution
ExecutorInterceptorInterface ..> InterceptorHandler
InterceptorHandler o-- ExecutorInterface
This class diagram illustrates the relationships between the main components of the Agent Executor Interceptors feature. The ExecutorPipeline
implements the ExecutorInterface
and manages a collection of ExecutorInterceptorInterface
objects. Each interceptor works with ExecutionInput
and Execution
objects, using the InterceptorHandler
to manage the flow of execution through the pipeline.