Skip to content

[AI] Contextual skill loading — progressive disclosure for agent abilities #37

Description

@BoweFrankema

Goal

Implement a skill-based progressive disclosure system for the ReAct agent. Instead of sending all tool descriptions to the LLM on every request, the agent starts with a lightweight skill index and loads full instructions + tools on demand when it detects a relevant task.

This is critical for our 4096-token context window (Qwen 3 1.7B) — as the ability count grows beyond the current 13, the system prompt will consume an unsustainable share of available tokens.

Why: The current architecture eagerly loads all abilities into every LLM request (react-agent.js:787-790). The system prompt grows linearly with tool count. Progressive disclosure solves this — the agent sees a compact index of what it can do, and only loads the full details when it needs them. This is the same pattern used by Claude Code (deferred tools + skill loading), MCP tool discovery, and the laravel-ai-sdk-skills package.

How It Works

The Pattern (Progressive Disclosure)

The core idea: don't tell the agent everything upfront — let it discover and load capabilities on demand.

This pattern is well-established in AI tooling:

  • Claude Code uses deferred tools (agent sees tool names only, fetches full schemas on demand) and skills (markdown files with instructions loaded contextually)
  • MCP separates tool discovery (list available tools) from tool invocation (call a specific tool)
  • Laravel AI SDK Skills splits capabilities into markdown files with YAML frontmatter; agent gets lite tags and loads full content via a skill tool call

For our small local model with a 4096-token budget, this matters even more than for cloud LLMs.

  1. Lite mode (default): Agent sees only skill names + one-line descriptions as compact tags:

    <skill name="site-health" description="Check plugins, themes, updates, and disk usage" />
    <skill name="content-management" description="List, search, and manage posts and comments" />
    <skill name="security" description="Security scans, error logs, and user auditing" />
    
  2. On-demand loading: When the agent detects a task matches a skill, it calls a load_skill tool to get the full instructions and tool definitions for that skill only.

  3. Skill definition files: Each skill is a Markdown file with YAML frontmatter:

    ---
    name: site-health
    description: Check plugins, themes, updates, and disk usage
    abilities:
      - plugin-list
      - theme-list
      - update-check
      - disk-usage
    ---
    
    # Site Health
    You are checking the health of a WordPress site. Start with an overview...

New Tools for the Agent

  • list_skills — returns the lite index of all available skills
  • load_skill — loads full instructions + tool definitions for a specific skill
  • skill_read (stretch) — read reference files bundled with a skill (e.g., checklists, templates)

Requirements

  • Skills are defined as Markdown files (with YAML frontmatter) in a skills directory
  • Skill definitions group existing abilities by domain and include contextual instructions
  • Agent system prompt includes only skill tags in lite mode, not full tool definitions
  • Agent can load a skill mid-conversation via a load_skill tool call
  • Loaded skill tools become available for subsequent ReAct iterations
  • Support both "lite" (default) and "full" discovery modes
  • Skills should be extensible by third-party plugins (like abilities are today via wp.agenticAdmin.registerAbility())

Key Files

File Changes
src/extensions/services/react-agent.js System prompt refactor — skill tags instead of flat tool list
src/extensions/services/tool-registry.js Add skill-aware getBySkill() / getSkillIndex() methods
src/extensions/services/message-router.js Optionally route to skill before ReAct
src/extensions/skills/ (new) Skill definition Markdown files
src/extensions/services/skill-registry.js (new) Skill discovery, parsing, and loading

Reference Pattern

The progressive disclosure pattern for AI agents follows this general flow:

Agent Init → Register all abilities internally
           → Build lite skill index (name + description only)
           → System prompt includes skill tags, NOT full tool list

User Request → Agent sees skill tags
             → Recognizes relevant skill (e.g., "list plugins" → site-health)
             → Calls load_skill("site-health")
             → Gets full instructions + tool definitions for that skill
             → Calls the actual ability tool (e.g., plugin-list)
             → Returns result to user

Key design principles:

  • Skill = grouping layer on top of existing abilities, not a replacement
  • Markdown + YAML frontmatter for skill definitions (easy for devs to write, digestible for LLMs)
  • Three built-in tools (list_skills, load_skill, skill_read) give the agent self-serve access
  • Discoverable vs active — skills listed in the prompt are discoverable; skills become active only when loaded

Technical Notes

Skills Needed

  • AI/JS Dev: Skill registry, system prompt refactor, new tools
  • Writers: Define skill groupings and contextual instructions for each skill
  • LLM Tester: Validate that skill loading works reliably with Qwen 3 1.7B

Activity

  1. pluginslab commented on Mar 20, 2026

    @pluginslab
    Owner

    This is the implementation approach for the scaling concern raised in #20. Cross-linking for visibility.

  2. pluginslab commented on Mar 20, 2026

    @pluginslab
    Owner

    Contributor Notes

    Starting points

    • src/extensions/services/tool-registry.js is a simple Map-based singleton — you'll need to add getBySkill() and getSkillIndex() methods here
    • src/extensions/services/react-agent.js:787-790 is where all tools get dumped into the system prompt via toolRegistry.getAll() — this is the hot path to refactor
    • There are currently 14 abilities (12 plugin + 2 core) across the files in src/extensions/abilities/

    Key constraints

    • The 4096-token context window is the hard constraint driving this — currently ~300 tokens for 13 tools, but it grows linearly
    • The agent runs two modes: function-calling (Qwen 3) and prompt-based JSON (Qwen 2.5) — both system prompt builders need updating
    • load_skill adds an extra ReAct iteration per skill load — with the 10-iteration max, that's meaningful

    Practical advice

    • Start with the skill Markdown files and registry first (the grouping layer) before touching the agent
    • The existing keyword-based routing in message-router.js could inform skill matching — no need for vectors in v1
    • Test with the ability test runner (npm run test:abilities) since it uses a real Qwen 3 1.7B via Ollama
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions