Simon Yu¹ · Gang Li² · Weiyan Shi¹ · Peng Qi² ¹Northeastern University · ²Uniphore
Important
This is a clean-room re-release: the original code drop depended on internal infrastructure that could not be open-sourced, so the harness has been rebuilt from scratch on public BrowserGym + litellm — zero internal dependencies. WebArena requires self-hosting the Dockerized sites; Mind2Web runs on the live web with your API keys. Issues and feedback are very welcome!
Install | Quickstart | Polymorphic Module | Reproduce | Architecture | Citation
PolySkill enables web agents to learn generalizable, compositional skills by separating a skill's abstract goal (what it accomplishes) from its concrete implementation (how it's executed on a given website) — polymorphism, applied to agent skill learning. Agents learn skills that transfer across websites, compose complex behaviors from reusable parts, improve success rates with 1.3–1.8× skill-reuse gains over prior methods, and self-improve through autonomous exploration.
- [2026-07] 🔧 Clean-room re-release: harness rebuilt on public BrowserGym + litellm; Mind2Web live-eval splits bundled; runnable no-key polymorphic-induction demo.
- [2025-11-10] 🎉 PolySkill paper and code released! Check out the arXiv paper.
# Clone with baseline submodules
git clone --recursive https://github.com/simonucl/PolySkill.git
cd PolySkill
# Create an environment (Python 3.10+)
conda create -n polyskill python=3.10 -y
conda activate polyskill
# Install
pip install -r requirements.txt
pip install -e .
playwright install chromium # browser used by BrowserGymThen copy .env.example to .env and fill in what you need:
cp .env.example .env# Proprietary model providers
OPENAI_API_KEY=your_openai_key
ANTHROPIC_API_KEY=your_anthropic_key
# Open-source models (Qwen3-Coder / GLM-4.5) via SGLang or OpenRouter
POLYSKILL_OSS_API_BASE=http://localhost:30000/v1 # or https://openrouter.ai/api/v1
POLYSKILL_OSS_API_KEY=EMPTY # or your OpenRouter key
# WebArena site URLs (self-hosted — see docs/WEBARENA.md)
WA_SHOPPING=http://localhost:7770
# ... (full list in .env.example)All four paper models route through one litellm client (polyskill.model.FoundationModel):
GPT-4.1, Claude-3.7-Sonnet, Qwen3-Coder-480B-A35B, GLM-4.5.
The polymorphic induction demo feeds a canned model response through the real induction pipeline (prompt → abstract interface + concrete subclass → stored skill):
python examples/demo_polymorphic_induction.py # offline, keyless
python examples/demo_polymorphic_induction.py --live # real LLM call (OPENAI_API_KEY)Verify the full install the same way — the offline suite includes a stubbed end-to-end pipeline test (real agent + harness + skill induction):
pytest -m "not llm" tests/Stand up the Dockerized WebArena sites and export the WA_* URLs first — full guide in
docs/WEBARENA.md. Then:
python -m polyskill.experiments.webarena.run_webarena \
--config examples/configs/webarena_polyskill_gpt4.yaml \
--category shopping \
--model gpt-4.1--category: shopping · admin · reddit · gitlab · map · cross · all.
Tasks execute on the live web and success is the GPT-4.1 WebJudge verdict, as in the
paper. Task splits are bundled in data/mind2web/ (rebuild or
override start URLs with python scripts/prepare_mind2web.py):
python -m polyskill.experiments.mind2web.run_mind2web \
--config examples/configs/mind2web_polyskill_gpt4.yaml \
--setting cross-task \
--model gpt-4.1--setting: cross-task · cross-website · cross-domain (177 / 142 / 694 bundled
tasks). Start-URL caveats and judge costs: docs/MIND2WEB.md.
The agent proposes its own goals from the abstract-class schema and learns skills from its successes (WebArena shopping backend):
python -m polyskill.experiments.self_proposing.run_self_proposing \
--config examples/configs/self_proposing_shopping_gpt4.yaml \
--domain shopping \
--iterations 150 \
--model gpt-4.1To self-host Qwen3-Coder-480B-A35B / GLM-4.5 with SGLang instead of using
OpenRouter (multi-GPU; adjust TP_SIZE in the scripts):
pip install uv && uv pip install "sglang" --prerelease=allow
./scripts/host_qwen3_coder.sh # port 30000
./scripts/host_glm4.sh # port 30001Keep the servers running (e.g. in tmux) before starting experiments.
The paper's central mechanism — inducing an abstract domain interface first, then a site-specific concrete implementation — is implemented and tested. Where to look:
| What | Where |
|---|---|
Inducer (PolymorphicInducer) |
polyskill/core/inducers/polymorphic_inducer.py |
| Abstract-first induction prompt | polyskill/prompts/induction/polymorphic.py |
| Online selection (config-driven) | polyskill/core/online_hook.py — used when skill_induction.use_polymorphism: true |
| Runnable demo (no API key needed) | examples/demo_polymorphic_induction.py |
| Unit tests | tests/core/test_polymorphic_inducer.py |
From Python:
from polyskill import PolymorphicInducer # top-level exportWhat an induced skill looks like — the abstract class defines the interface and holds compositional skills; concrete subclasses implement it per website:
# Abstract class defines the interface
class AbstractShoppingSite:
def search_product(self, query: str):
"""Searches for a product."""
def add_to_cart(self, item_id: str, quantity: int):
"""Adds item to shopping cart."""
# Compositional skill — relies only on the abstract methods
def find_and_purchase(self, query: str, item_id: str):
self.search_product(query)
self.add_to_cart(item_id)
self.checkout()
# Concrete implementation for a specific website
class AmazonWebsite(AbstractShoppingSite):
def search_product(self, query: str):
click(search_box_id)
fill(search_box_id, query)
keyboard_press('Enter')for model in gpt-4.1 claude-3-7-sonnet-20250219 qwen3-coder-480b-a35b glm-4.5; do
python -m polyskill.experiments.webarena.run_webarena \
--config examples/configs/webarena_polyskill_gpt4.yaml \
--category shopping \
--model $model
doneLive-execution setting: tasks run on the real web; the reported success rate is the GPT-4.1 WebJudge verdict (live tasks have no gold reward).
python scripts/prepare_mind2web.py # one-time: build/refresh the task splits
for model_cfg in gpt4 claude qwen glm; do
python -m polyskill.experiments.mind2web.run_mind2web \
--config examples/configs/mind2web_polyskill_${model_cfg}.yaml \
--setting cross-task \
--model "$(python -c "print({'gpt4':'gpt-4.1','claude':'claude-3-7-sonnet-20250219','qwen':'qwen3-coder-480b-a35b','glm':'glm-4.5'}['${model_cfg}'])")"
doneThe bundled splits come from the openly redistributable Multimodal-Mind2Web test release (177 / 142 / 694 tasks — a subset of the original 252 / 177 / 912); see docs/MIND2WEB.md for details, start-URL caveats, and judge costs.
# Shopping domain
python -m polyskill.experiments.self_proposing.run_self_proposing \
--config examples/configs/self_proposing_shopping_gpt4.yaml \
--domain shopping --iterations 150 --model gpt-4.1
# Developer tools
python -m polyskill.experiments.self_proposing.run_self_proposing \
--config examples/configs/self_proposing_git_gpt4.yaml \
--domain dev_tools --iterations 100 --model gpt-4.1ASI and SkillWeaver are included as git submodules. Each is an independent project with
its own environment and WebArena URL configuration — follow the submodule READMEs
(baselines/ASI/README.md,
baselines/SkillWeaver/README.md); the commands
below are their real entrypoints at the pinned commits:
git submodule update --init --recursive # if not cloned with --recursive
# ASI — online skill induction on WebArena shopping
cd baselines/ASI && pip install -r requirements.txt && cd asi
python run_online.py --experiment "asi" --website "shopping" --task_ids "21-25"
# SkillWeaver — attempt a task with a learned skill library
cd baselines/SkillWeaver && pip install -r requirements.txt
python -m skillweaver.attempt_task __SHOPPING__ "your task description" \
--knowledge-base-path-prefix skill_library/shopping/kbOverview: baselines/README.md. PolySkill integrates and extends
ASI components in polyskill/core/inducers/asi_inducer.py and
polyskill/agents/agent/asi_utils/.
polyskill/
├── core/ # Skill induction engine
│ ├── core.py # Orchestrator (SkillInductionCore)
│ ├── skill_storage.py # Skill storage (PolySkillStorage)
│ ├── simple_inducer.py # Heuristic online inducer
│ ├── online_hook.py # Online induction hook (selects the inducer)
│ ├── inducers/ # Skill induction strategies
│ │ ├── polymorphic_inducer.py # ★ Polymorphic abstraction (paper core)
│ │ ├── pattern_inducer.py # Pattern-based induction
│ │ └── asi_inducer.py # ASI-based induction (adapted)
│ └── judge/ # LLM/VLM task-success judge (WebJudge)
├── model/ # Unified litellm model layer (FoundationModel)
├── evaluation/ # BrowserGym eval harness (config / loop / runner)
├── trajectory/ # BrowserGym-native trajectory format
├── agents/ # Agent implementations
│ ├── base.py # Agent / BasicFMAgent base classes
│ ├── agent/ # HSM hierarchical agent + induction wrapper
│ └── planner/ # LLM task planner
├── exploration/ # Self-proposing exploration (proposer/curriculum/loop)
├── skill_induction/ # Back-compat import shims
├── experiments/ # Entrypoints (webarena / mind2web / self_proposing)
├── prompts/ # Prompt templates (incl. induction/polymorphic.py)
└── utils/ # Utility functions
data/mind2web/ # Bundled Mind2Web test splits (task metadata)
baselines/ # Baseline methods (git submodules)
├── ASI/ # Agent Skill Induction baseline
└── SkillWeaver/ # SkillWeaver baseline
Beyond task success rate, PolySkill introduces:
- Skill Reusability — fraction of learned skills reused in new tasks
- Task Coverage — fraction of tasks that used at least one skill
- Skill Compositionality — how often new skills build on earlier ones
- Number of Steps — actions per solved task (skill calls count as one step)
# examples/configs/my_experiment.yaml
skill_induction:
enabled: true
use_polymorphism: true # Enable polymorphic abstraction
judge_method: webjudge_general # or webjudge_online_mind2web
storage_path: ./my_skills/
model_configs:
default: {provider: openai, name: gpt-4.1, temperature: 0.1, max_tokens: 4096}
benchmarks:
wa-shopping: {dataset: webarena, category: shopping, max_steps: 30}
agents:
default: {name: hsm_v3, model_config_name: default}
runner: {threads: 1, output_dir: ./results, seed: 42, timeout_secs: 600}See examples/configs/ for complete, validated examples
(per-model WebArena/Mind2Web configs and the self-proposing setup).
pytest -m "not llm" tests/ # offline suite (no API keys)
pytest -m llm tests/ # tests that make real LLM calls
bash scripts/dev/scrub_check.sh # release-hygiene gate
bash scripts/dev/import_check.sh # entrypoint import gateContributions are welcome — please open an issue or PR.
If you use PolySkill in your research, please cite:
@article{yu2025polyskill,
title={PolySkill: Learning Generalizable Skills Through Polymorphic Abstraction},
author={Yu, Simon and Li, Gang and Shi, Weiyan and Qi, Peng},
journal={arXiv preprint arXiv:2510.15863},
year={2025}
}We acknowledge Orby (now part of Uniphore) for providing the computational resources and infrastructure support that made the experiments in this paper possible. We thank the authors of ASI (Agent Skill Induction) and SkillWeaver for their pioneering work on skill induction in web agents, which inspired and informed this research. We are grateful to SGLang for their exceptional day-0 support and rapid resolution of issues when hosting Qwen3-Coder and GLM-4.5 models. This work builds on BrowserGym for web agent evaluation, and leverages the Mind2Web and WebArena benchmarks for rigorous evaluation.
This project is licensed under the MIT License — see the LICENSE file for details.
- Simon Yu: yu.chi@northeastern.edu
- GitHub Issues: https://github.com/simonucl/PolySkill/issues
- Paper: https://arxiv.org/abs/2510.15863 · Docs: WebArena · Mind2Web · Quickstart