Skip to main content

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 import statements for dangerous modules
  • Must implement SkillBase interface
  • Must have definition() and execute() 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

SettingDefaultDescription
SKILL_EVOLUTION_ENABLEDtrueEnable skill evolution
SKILL_PATTERN_THRESHOLD5Min 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:

  1. AST validation (no dangerous imports)
  2. Runs in the same process (not a subprocess) — but this is by design for performance
  3. Skill name validation prevents path traversal

For maximum security, consider setting SKILL_EVOLUTION_ENABLED=false and creating skills manually.