Skip to content

[BUG] Agent.__call__() removes kwargs preventing dynamic system_prompt override #103

Closed
@cagataycali

Description

@cagataycali

Description

The current implementation of Agent._execute_event_loop_cycle() removes kwargs parameters including system_prompt, making it impossible to dynamically override the system prompt when calling the agent.

See agent-builder: https://github.com/strands-agents/agent-builder/blob/main/src/strands_agents_builder/strands.py#L178

Current Behavior

In src/strands/agent/agent.py, the _execute_event_loop_cycle method removes several kwargs:

def _execute_event_loop_cycle(self, callback_handler: Callable, kwargs: dict[str, Any]) -> AgentResult:
    # ... docstring ...
    kwargs.pop("agent", None)
    kwargs.pop("model", None)
    kwargs.pop("system_prompt", None)  # <-- This prevents dynamic override
    kwargs.pop("tool_execution_handler", None)
    kwargs.pop("event_loop_metrics", None)
    kwargs.pop("callback_handler", None)
    kwargs.pop("tool_handler", None)
    kwargs.pop("messages", None)
    kwargs.pop("tool_config", None)
    
    # ... rest of the method uses self.system_prompt instead

This means calls like agent(query, system_prompt="new prompt") are ignored.

Expected Behavior

Users should be able to dynamically override the system prompt (and other parameters) on a per-call basis:

agent = Agent(system_prompt="Default prompt")
response = agent("Hello", system_prompt="Custom prompt for this call")

Proposed Solution

Instead of removing the kwargs, extract them with fallbacks to instance values:

def _execute_event_loop_cycle(self, callback_handler: Callable, kwargs: dict[str, Any]) -> AgentResult:
    # Extract parameters with fallbacks to instance values
    system_prompt = kwargs.pop("system_prompt", self.system_prompt)
    model = kwargs.pop("model", self.model)
    tool_execution_handler = kwargs.pop("tool_execution_handler", self.thread_pool_wrapper)
    event_loop_metrics = kwargs.pop("event_loop_metrics", self.event_loop_metrics)
    callback_handler = kwargs.pop("callback_handler", callback_handler)
    tool_handler = kwargs.pop("tool_handler", self.tool_handler)
    messages = kwargs.pop("messages", self.messages)
    tool_config = kwargs.pop("tool_config", self.tool_config)
    kwargs.pop("agent", None)  # Remove agent to avoid conflicts
    
    try:
        # Execute the main event loop cycle with extracted parameters
        stop_reason, message, metrics, state = event_loop_cycle(
            model=model,
            system_prompt=system_prompt,  # Use extracted value
            messages=messages,
            tool_config=tool_config,
            callback_handler=callback_handler,
            tool_handler=tool_handler,
            tool_execution_handler=tool_execution_handler,
            event_loop_metrics=event_loop_metrics,
            agent=self,
            event_loop_parent_span=self.trace_span,
            **kwargs,
        )
        # ... rest of method

Use Case Example

This enhancement would enable powerful patterns like dynamic system prompt switching:

#!/usr/bin/env python3
from strands import Agent

agent = Agent(system_prompt="You are a helpful assistant.")

# Use default system prompt
response1 = agent("Hello")

# Override system prompt for specific interaction
response2 = agent("Analyze this code", system_prompt="You are a senior code reviewer.")

# Use different prompt for creative tasks
response3 = agent("Write a poem", system_prompt="You are a creative poet.")

Real-World Impact

This feature is particularly valuable for:

  1. Multi-purpose agents that need different personas/expertise per interaction
  2. Conversation management where system prompts include dynamic context
  3. CLI tools that want to inject conversation history into system prompts
  4. Testing scenarios where different prompts need to be evaluated

Current Workaround

Currently, users must create new Agent instances for different system prompts, which is inefficient and loses conversation context.

Files Affected

  • src/strands/agent/agent.py - Main fix in _execute_event_loop_cycle method
  • Potentially related test files for validation

Breaking Changes

This change should be backwards compatible as it maintains existing behavior while adding new functionality.

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingenhancementNew feature or request

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions