Skip to content

feat: Add dynamic system prompt override functionality #108

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
May 25, 2025

Conversation

Shubhamraut01
Copy link
Contributor

@Shubhamraut01 Shubhamraut01 commented May 25, 2025

System Prompt Override Implementation - Resolves Issue #103

Description

This PR implements the system prompt override functionality requested in issue #103, allowing users to dynamically override the system prompt on a per-call basis when invoking an agent. The implementation preserves backward compatibility while enabling powerful new use cases for dynamic prompt management.

Key Changes:

  • Modified Agent._execute_event_loop_cycle() to extract system_prompt from kwargs with fallback to instance value
  • Replaced problematic kwargs.pop("system_prompt", None) behavior that ignored user overrides
  • Added comprehensive test coverage for all system prompt override scenarios
  • Consolidated related tests for better maintainability

Before:

# system_prompt parameter was ignored
agent = Agent(system_prompt="Default prompt")
response = agent("Hello", system_prompt="Custom prompt")  # ❌ Ignored

After:

# system_prompt parameter works as expected
agent = Agent(system_prompt="Default prompt")
response = agent("Hello", system_prompt="Custom prompt")  # ✅ Works!

Related Issues

Fixes #103 - [BUG] Agent.call() removes kwargs preventing dynamic system_prompt override

Documentation PR

N/A - This is a bug fix with enhanced functionality, no separate documentation changes required.

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation update
  • Other (please describe):

This change is both a bug fix (resolving the ignored kwargs) and a new feature (enabling dynamic system prompt override functionality).

Testing

Automated Testing

  • hatch fmt --linter - ✅ All linting checks pass
  • hatch fmt --formatter - ✅ Code formatting verified
  • hatch test --all - ✅ Full test suite passes
  • Verified that the changes do not break functionality or introduce warnings in consuming repositories

Implementation Testing

Test Coverage includes:

  1. Default system prompt behavior - Uses instance system_prompt when no override provided
  2. Per-call override - Successfully overrides system_prompt for specific calls
  3. Revert behavior - Returns to default system_prompt after override
  4. Multiple overrides - Handles consecutive different overrides correctly
  5. None override - Properly handles system_prompt=None parameter
  6. Empty string override - Correctly processes system_prompt="" parameter
  7. No default prompt - Works when agent has no default system_prompt set

Test Results:

# Comprehensive system prompt override test
$ python -m pytest tests/strands/agent/test_agent.py -k "test_agent_system_prompt_overrides_all_cases" -v
================= test session starts ==================
collected 36 items / 35 deselected / 1 selected        
tests/strands/agent/test_agent.py::test_agent_system_prompt_overrides_all_cases PASSED [100%]
===== 1 passed, 35 deselected in 0.92s =====

# Full test suite validation
$ hatch test
================================ All tests pass ✅ ================================

Manual Testing Examples

from strands import Agent
from strands.models.openai import OpenAIModel

# Initialize agent with default system prompt
agent = Agent(
    model=OpenAIModel(), 
    system_prompt="You are a helpful assistant."
)

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

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

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

# 4. Revert to default (no override specified)
response4 = agent("Help me with math")

Real-World Use Cases Enabled

  1. Multi-purpose agents - Single agent instance with different expertise per interaction
  2. Dynamic context injection - System prompts that include conversation history or context
  3. CLI tools - Command-line interfaces that modify behavior based on flags/options
  4. Testing scenarios - Evaluation of different prompts without creating new agent instances
  5. Conversation management - Adaptive prompts based on conversation state

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • Any dependent changes have been merged and published

Technical Implementation Details

Core Changes in src/strands/agent/agent.py

Before (Problematic):

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 ignored user overrides
    kwargs.pop("tool_execution_handler", None)
    # ... rest of method uses self.system_prompt instead

After (Fixed):

def _execute_event_loop_cycle(self, callback_handler: Callable, kwargs: dict[str, Any]) -> AgentResult:
    # ... docstring ...
    # Extract parameters with fallbacks to instance values
    system_prompt = kwargs.pop("system_prompt", self.system_prompt)  # ✅ Respects user override
    model = kwargs.pop("model", self.model)
    tool_execution_handler = kwargs.pop("tool_execution_handler", self.thread_pool_wrapper)
    # ... rest of method uses extracted system_prompt value

Test Implementation

Added comprehensive test function test_agent_system_prompt_overrides_all_cases() in tests/strands/agent/test_agent.py:

def test_agent_system_prompt_overrides_all_cases():
    """Test all system prompt override scenarios in one function."""
    
    class MockModel(Model):
        def __init__(self):
            self.captured_system_prompts = []
        
        def converse(self, messages, tools=None, system_prompt=None, **kwargs):
            self.captured_system_prompts.append(system_prompt)
            # ... mock implementation
    
    # Test all 7 scenarios with proper verification

Backward Compatibility

This change is 100% backward compatible:

  • Existing code continues to work exactly as before
  • No changes to method signatures or public APIs
  • Default behavior unchanged when no override is provided
  • All existing tests pass without modification

Performance Impact

  • Positive impact: Eliminates need to create new agent instances for different prompts
  • Negligible overhead: Simple parameter extraction with fallback pattern
  • Memory efficiency: Reuses single agent instance across different prompt scenarios

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

- Modified Agent._execute_event_loop_cycle() to support system_prompt parameter override
- Enables per-call system prompt customization via agent('Hello', system_prompt='Custom prompt')
- Maintains backward compatibility with existing agent configurations
- Added comprehensive test suite with 8 test cases covering all scenarios
- Resolves GitHub issue strands-agents#103
@Shubhamraut01 Shubhamraut01 requested a review from a team as a code owner May 25, 2025 13:13
Copy link
Member

@awsarron awsarron left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @Shubhamraut01, thank you for your contribution!

A couple of comments on the changes. Please also use the pull request template as it includes some extra useful details about clarifying how the changes have been tested, along with some other information.

…ide.py into a single comprehensive test function test_agent_system_prompt_overrides_all_cases() in test_agent.py
Copy link
Contributor Author

@Shubhamraut01 Shubhamraut01 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello,
Thanks for reviewing my PR.
I have done the needful.

@Shubhamraut01 Shubhamraut01 requested a review from awsarron May 25, 2025 16:06
Copy link
Member

@awsarron awsarron left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the updates @Shubhamraut01.

A couple of small comments and then we'll get this merged 🚀

@Shubhamraut01 Shubhamraut01 requested a review from awsarron May 25, 2025 17:49
cagataycali
cagataycali previously approved these changes May 25, 2025
Copy link
Member

@cagataycali cagataycali left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks great to me!

Thank you for your contribution and making your first contribution! 🚀

@Shubhamraut01
Copy link
Contributor Author

You're welcome — glad to be part of it! Excited for more contributions ahead! 🚀

@awsarron awsarron self-assigned this May 25, 2025
…cution_handler, event_loop_metrics, callback_handler, tool_handler, messages, tool_config)
@Shubhamraut01
Copy link
Contributor Author

@awsarron I think it looks good now.

Copy link
Member

@awsarron awsarron left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Last couple of nits and then we'll merge this. Thank you so much @Shubhamraut01 !

Works great, I gave it a test locally as well ☺️

@Shubhamraut01 Shubhamraut01 requested a review from awsarron May 25, 2025 20:10
@Shubhamraut01
Copy link
Contributor Author

Last couple of nits and then we'll merge this. Thank you so much @Shubhamraut01 !

Works great, I gave it a test locally as well ☺️

Welcome @awsarron,

Sounds Good. I have done the final changes.

@awsarron awsarron enabled auto-merge (squash) May 25, 2025 20:19
@awsarron awsarron merged commit f2d2cb6 into strands-agents:main May 25, 2025
10 checks passed
@awsarron
Copy link
Member

Awesome, thank you @Shubhamraut01. We'll get these improvements published in the next SDK release 🚀

@Shubhamraut01
Copy link
Contributor Author

Awesome, thank you @Shubhamraut01. We'll get these improvements published in the next SDK release 🚀

Thank you so much @awsarron and @cagataycali ! 🎉

I'm thrilled to have contributed to the Strands SDK and excited to see these system prompt override improvements help developers build more flexible and dynamic AI agents.

awsarron pushed a commit to awsarron/sdk-python that referenced this pull request May 26, 2025
awsarron added a commit that referenced this pull request May 26, 2025
* models - openai - argument none (#97)

* docs(readme): add open PRs badge + link to samples repo + change 'Docs' to 'Documentation' (#100)

* docs(readme): add logo (#101)

* docs(readme): add logo, title, badges, links to other repos, standardize headings (#102)

* style(readme): use dark logo for clearer visibility when system is using light color scheme (#104)

* fix(readme): use logo that changes color automatically depending on user's color preference scheme (#105)

* feat(handlers): add reasoning text to callback handler and related tests (#109)

* feat(handlers): add reasoning text to callback handler and related tests

* feat(handlers): removed redundant comment in .gitignore file

* feat(handlers): Updated reasoningText type as (Optional[str]

* feat: Add dynamic system prompt override functionality (#108)

* Modularizing Event Loop (#106)

* fix(telemetry): fix agent span start and end when using Agent.stream_async() (#119)

* feat: Update SlidingWindowConversationManager (#120)

* v0.1.5

---------

Co-authored-by: Patrick Gray <pgrayy@amazon.com>
Co-authored-by: Gokhan (Joe) Gultekin <joseph.gueltekin@gmail.com>
Co-authored-by: Shubham Raut <shubhamrao12321@gmail.com>
Co-authored-by: fede-dash <fede.kamelhar@doordash.com>
Co-authored-by: Nick Clegg <nac542@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[BUG] Agent.__call__() removes kwargs preventing dynamic system_prompt override
3 participants