Self-Improving AI Agents: The System Prompt Evolution Pattern 🧬
Published: January 10, 2025
What if your AI agent could learn and evolve across sessions? This post explores the system prompt evolution pattern used by both DevDuck and Strands-Coder to enable continuous self-improvement.
The Core Concept
flowchart TB
subgraph "Session N"
Q1[User Query] --> E1[Execute]
E1 --> L1[Learn Something]
L1 --> U1[Update System Prompt]
U1 --> S1[Store in KB]
end
subgraph "Session N+1"
Q2[User Query] --> R2[Retrieve from KB]
R2 --> P2[Load Updated Prompt]
P2 --> E2[Execute with New Knowledge]
end
S1 -->|Persisted| R2
U1 -->|GitHub Variable| P2
style L1 fill:#f9f,stroke:#333,stroke-width:4px
style U1 fill:#bbf,stroke:#333,stroke-width:4px
Key Insight
Every agent execution is an opportunity to learn. The system prompt isn't static—it's a living document that grows with the agent's experience.
The Evolution Tools
1. System Prompt Management
@tool
def system_prompt(
action: str, # view, update, add_context, reset
prompt: str = None,
context: str = None,
repository: str = None,
variable_name: str = "SYSTEM_PROMPT"
) -> dict:
Actions:
- view: See current prompt
- update: Replace entire prompt
- add_context: Append new learning
- reset: Clear and start fresh
2. Knowledge Base Storage
Stores execution summaries for future retrieval.
The Self-Improvement Loop
sequenceDiagram
participant Agent
participant KB as Knowledge Base
participant SP as System Prompt
participant GH as GitHub Variable
Note over Agent: Start Execution
Agent->>KB: retrieve(current_task)
KB->>Agent: Related past learnings
Agent->>Agent: Execute with context
Note over Agent: Discover something new!
Agent->>SP: system_prompt(action="add_context")
SP->>GH: Update SYSTEM_PROMPT variable
Agent->>KB: store_in_kb(execution_summary)
Note over Agent: Next session starts with<br/>updated knowledge
Implementation Pattern
Step 1: Retrieve Before Acting
# Always check what you've learned before
kb_id = os.getenv("STRANDS_KNOWLEDGE_BASE_ID")
if kb_id:
agent.tool.retrieve(
text=user_query,
knowledgeBaseId=kb_id
)
Step 2: Execute with Context
# System prompt already includes:
# - Base instructions
# - GitHub event context
# - Project state
# - Self-awareness (own code)
# - Past learnings (from KB retrieve)
result = agent(enhanced_prompt)
Step 3: Learn and Store
# After execution, store what happened
agent.tool.store_in_kb(
content=f"Input: {query}\nResult: {result}",
title=f"Execution: {datetime.now()}"
)
# If learned something significant
if significant_learning:
agent.tool.system_prompt(
action="add_context",
context=f"Learning: {what_was_learned}",
repository="owner/repo" # Persist to GitHub
)
Real-World Example: Strands-Coder
From the actual SYSTEM_PROMPT.md:
## 🧠 CORE DIRECTIVE: Self-Update Every Turn
**MANDATORY**: At the END of every execution, I MUST:
1. **Reflect** on what I learned or did
2. **Store** execution summary in KB via `store_in_kb()`
3. **Update** system prompt if I learned something important
via `system_prompt(action="add_context")`
This makes me continuously improve.
Evolution Log Example
## 📝 Evolution Log
| Date | Change |
|------|--------|
| 2026-02-01 | Rewrote from scratch - quality over quantity |
| 2026-01-15 | Memory & learning protocol added |
| 2026-01-13 | Shell timeout best practices (by @cagataycali) |
| 2026-01-13 | Comment quality guidelines (by @zastrowm) |
| 2026-01-12 | AI disclosure footer mandatory |
The Learning Categories
mindmap
root((Agent Learnings))
Anti-Patterns
Comment Noise
Untested Code
Duplicate PRs
Quality Patterns
Test First
One PR Per Issue
Inline Reviews
Tool Usage
Token Selection
Timeout Settings
Project Board
Community Feedback
User Preferences
Review Guidelines
Disclosure Rules
Types of Learnings
-
Anti-Patterns: Things to NEVER do
-
Quality Patterns: Best practices discovered
-
User Feedback: Corrections and preferences
Two-Tier Memory System
flowchart TB
subgraph "Short-Term Memory"
KB[(Knowledge Base)]
Conv[Conversation Context]
Events[Recent Events]
end
subgraph "Long-Term Memory"
SP[System Prompt]
GH[GitHub Variable]
Code[Embedded in Code]
end
KB -->|retrieve| Agent
Conv -->|session| Agent
Events -->|dynamic| Agent
Agent -->|store_in_kb| KB
Agent -->|add_context| SP
SP -->|sync| GH
style SP fill:#f9f,stroke:#333,stroke-width:4px
Short-Term (KB)
- Execution summaries
- Recent conversations
- Temporary context
- Search-based retrieval
Long-Term (System Prompt)
- Core behaviors
- Learned rules
- Anti-patterns
- Quality standards
DevDuck Implementation
def _build_system_prompt(self):
"""Build adaptive system prompt."""
# 1. Load base prompt from env/file
base = _get_system_prompt()
# 2. Add environment context
context = f"""
Environment: {platform.system()} {platform.machine()}
Python: {sys.version_info}
Model: {self.model}
"""
# 3. Add own source code (self-awareness)
own_code = get_own_source_code()
# 4. Add conversation history
recent = get_last_messages()
# 5. Add recent logs
logs = get_recent_logs()
return f"{base}\n\n{context}\n\n{own_code}\n\n{recent}\n\n{logs}"
The GitHub Variable Pattern
sequenceDiagram
participant Agent
participant Local as Local Env
participant API as GitHub API
participant GH as Repository Variable
Agent->>API: PATCH /actions/variables/SYSTEM_PROMPT
API->>GH: Update variable
Note over GH: Persisted across<br/>all runs
GH->>Local: Next workflow loads
Local->>Agent: Updated prompt available
Why GitHub Variables?
- Persistence: Survives workflow runs
- Version Control: Changes are visible
- Access Control: Uses same permissions
- Multi-Repo: Can sync across repos
def _update_github_repository_variable(repository, name, value, token):
"""Update a GitHub repository variable."""
url = f"https://api.github.com/repos/{repository}/actions/variables/{name}"
headers = {
"Authorization": f"Bearer {token}",
"Accept": "application/vnd.github+json",
}
response = requests.patch(url, headers=headers, json={
"name": name,
"value": value
})
return response.status_code == 204
Best Practices
Self-Improvement Tips
1. Be Selective Don't add every observation. Only persistent, reusable learnings.
2. Be Specific Bad: "Be careful with PRs" Good: "Never create duplicate PRs for same issue. Iterate on one branch."
3. Include Source "Learning from @username on 2025-01-15: ..."
4. Organize by Category Use headers: Anti-Patterns, Quality Patterns, Tool Usage
5. Prune Regularly Review and remove outdated learnings
Avoid Prompt Bloat
System prompts have token limits. Periodically consolidate:
- Merge related learnings
- Remove redundant entries
- Move detailed context to KB
- Keep prompt focused on rules
Measuring Evolution
graph LR
V1[v1: Basic Rules] --> V2[v2: + Anti-Patterns]
V2 --> V3[v3: + Quality Gates]
V3 --> V4[v4: + User Feedback]
V4 --> V5[v5: + Tool Practices]
style V5 fill:#bfb,stroke:#333,stroke-width:4px
Track your agent's evolution: - Version count: How many updates? - Learning types: What categories grow? - User feedback: How many corrections? - Success rate: Are learnings helping?
The Continuous Learning Mindset
🧠 EVERY EXECUTION = LEARNING OPPORTUNITY
1. Retrieve → What do I already know?
2. Execute → Apply knowledge
3. Observe → What happened?
4. Reflect → What did I learn?
5. Store → Persist for future
6. Evolve → Update my rules
An agent that doesn't learn is just a script. An agent that learns becomes a partner. 🧬