Skill Evolution
Skill Evolution is WASP's system for automatically creating new skills from frequently-occurring multi-skill patterns. When the agent repeatedly uses the same sequence of skills to accomplish a task type, it synthesizes a new composite skill that can be called directly.
How It Works
Message 1: web_search → fetch_url → python_exec (parse data)
Message 2: web_search → fetch_url → python_exec (parse data)
Message 3: web_search → fetch_url → python_exec (parse data)
Message 4: web_search → fetch_url → python_exec (parse data)
Message 5: web_search → fetch_url → python_exec (parse data)
↓
SkillEvolutionJob detects pattern (≥5 occurrences)
↓
LLM synthesizes: web_research_and_extract(query, selector)
↓
New skill registered and available
Pattern Detection
After every message, the skill sequence is recorded in the skill_patterns table:
INSERT INTO skill_patterns (sequence, skill_names, context_hash, chat_id, created_at)
VALUES ('web_search,fetch_url,python_exec', ...);
The SkillEvolutionJob (runs every 6 hours) queries:
SELECT skill_names, COUNT(*) as occurrences
FROM skill_patterns
GROUP BY skill_names
HAVING COUNT(*) >= 5 -- SKILL_PATTERN_THRESHOLD
ORDER BY occurrences DESC;
Code Generation
When a pattern qualifies, the LLM synthesizes a Python class:
class WebResearchAndExtractSkill(SkillBase):
def definition(self):
return SkillDefinition(
name='web_research_and_extract',
description='Search web, fetch the top result, and extract structured data',
parameters=[
SkillParameter(name='query', type='string', required=True),
SkillParameter(name='css_selector', type='string', required=False),
]
)
async def execute(self, query: str, css_selector: str = None) -> SkillResult:
# Implementation generated by LLM
...
Safety Validation
Generated code is validated by _validate_skill_code() using AST analysis:
Dangerous Imports Blocked
_DANGEROUS_IMPORTS = {
"subprocess", "os", "sys", "pty", "ctypes",
"pickle", "marshal", "importlib", "__import__",
"eval", "exec", "compile",
}
Additional Checks
- Must parse as valid Python
- Class name must match slug pattern
^[a-z][a-z0-9_]{1,48}$ - No
importstatements for dangerous modules - Must implement
SkillBaseinterface - Must have
definition()andexecute()methods
If validation fails, the evolution attempt is logged and skipped.
Persistence
Generated skills are saved to /data/skills/<slug>/skill.py and survive container restarts:
/data/skills/
└── web_research_and_extract/
└── skill.py
At startup, load_all_python_skills() scans /data/skills/ and registers all found skills.
Configuration
| Setting | Default | Description |
|---|---|---|
SKILL_EVOLUTION_ENABLED | true | Enable skill evolution |
SKILL_PATTERN_THRESHOLD | 5 | Min occurrences before synthesis |
Manual Skill Creation
You can also create skills manually via skill_manager:
skill_manager(action="create", name="my_skill", description="...", code="...")
These are also stored in /data/skills/ and loaded at startup.
Viewing Evolved Skills
# List custom skills
ls /home/agent/data/skills/
# View evolved skill count
docker exec agent-postgres psql -U agent -d agent \
-c "SELECT COUNT(*), skill_names FROM skill_patterns GROUP BY skill_names ORDER BY COUNT(*) DESC;"
In the dashboard, evolved skills appear as type python-custom in the Skills section.
Security Consideration
The skill evolution system generates and executes LLM-produced code. The safety measures are:
- AST validation (no dangerous imports)
- Runs in the same process (not a subprocess) — but this is by design for performance
- Skill name validation prevents path traversal
For maximum security, consider setting SKILL_EVOLUTION_ENABLED=false and creating skills manually.