How Deep Agents Implements Skills: Progressive Disclosure & Middleware Ordering
This article analyzes Deep Agents' Skills implementation, revealing a three-layer progressive disclosure loading pattern where metadata loads at startup, full instructions load on activation, and resources load on demand. It explains how before_agent and wrap_model_call hooks inject skill metadata into system prompts, and why SkillsMiddleware positioning differs between main agents (early for coordination) and sub-agents (late for separation of concerns).
1. The Initial Question
Skills are implemented in SkillsMiddleware, initialized inside create_deep_agent(). A key observation: the middleware order differs between the main agent and sub-agents.
Main Agent Middleware Order
TodoListMiddleware
SkillsMiddleware <-- early position
FilesystemMiddleware
SubAgentMiddleware
SummarizationMiddleware
PatchToolCallsMiddleware
AnthropicPromptCachingMiddleware
MemoryMiddleware
HumanInTheLoopMiddleware
Sub-Agent Middleware Order
TodoListMiddleware
FilesystemMiddleware
SummarizationMiddleware
PatchToolCallsMiddleware
SkillsMiddleware <-- later position
Custom Middleware
AnthropicPromptCachingMiddleware
Since middleware order affects hook execution sequence ( before_agent, wrap_model_call), the article investigates SkillsMiddleware to understand the impact.
2. Skill Basic Structure
A skill directory contains at minimum a SKILL.md file:
skill-name/
├── SKILL.md # Required: metadata + instructions
├── scripts/ # Optional: executable code
├── references/ # Optional: documentation
├── assets/ # Optional: templates, resources
└── ... # Any additional files or directories SKILL.mdmust start with YAML frontmatter metadata followed by Markdown instructions. Example metadata:
---
name: pdf-processing
description: Extract PDF text, fill forms, merge files. Use when handling PDFs.
license: Apache-2.0
metadata:
author: example-org
version: "1.0"
---In Deep Agents the metadata maps to a SkillMetadata TypedDict:
class SkillMetadata(TypedDict):
"""Metadata for a skill per Agent Skills specification (https://agentskills.io/specification)."""
path: str
name: str
description: str
license: str | None
compatibility: str | None
metadata: dict[str, str]
allowed_tools: list[str]3. Skill Loading Process
3.1 Progressive Disclosure
Skills use a "progressive disclosure" loading pattern to efficiently use context:
Metadata (~100 tokens) : name and description fields load for all skills at startup.
Instructions (recommended <5000 tokens) : Full SKILL.md body loads when the skill is activated.
Resources (on-demand) : Files in scripts/, references/, or assets/ load only when needed.
3.2 before_agent Hook
SkillsMiddlewareloads skills in its before_agent hook, writing the first progressive-disclosure layer ( SkillMetadata) to state.
def before_agent(self, state: SkillsState, runtime: Runtime, config: RunnableConfig):
# Step 1: Check cache - skip if skills_metadata already present
if "skills_metadata" in state:
return None
# Step 2: Resolve backend
backend = self._get_backend(state, runtime, config)
# Step 3: Initialize empty dict for merging skills (keyed by skill name)
all_skills: dict[str, SkillMetadata] = {}
# Step 4: Iterate all skill sources, load and merge in order
for source_path in self.sources:
# Load skill list from current source
source_skills = _list_skills(backend, source_path)
# Add to dict; same-name skills are overridden (later overrides earlier)
for skill in source_skills:
all_skills[skill["name"]] = skill
# Step 5: Convert dict values to list
skills = list(all_skills.values())
# Step 6: Return state update object
return SkillsStateUpdate(skills_metadata=skills)This runs once at agent startup. Note that skills_metadata state is isolated between the main agent and each sub-agent.
4. Skill System Prompt Injection
The wrap_model_call hook formats the loaded skill metadata and injects it into the system prompt so the LLM knows available skills.
def modify_request(self, request: ModelRequest[ContextT]) -> ModelRequest[ContextT]:
# Step 1: Get skill metadata from request state
skills_metadata = request.state.get("skills_metadata", [])
# Step 2: Format skill source location info
skills_locations = self._format_skills_locations()
# Step 3: Format skill list (metadata to readable text)
skills_list = self._format_skills_list(skills_metadata)
# Step 4: Generate skill documentation section via template
skills_section = self.system_prompt_template.format(
skills_locations=skills_locations,
skills_list=skills_list,
)
# Step 5: Append skill section to existing system message
new_system_message = append_to_system_message(request.system_message, skills_section)
# Step 6: Return request copy with new system message
return request.override(system_message=new_system_message)Step 2 produces entries like:
- **{skill['name']}**: {skill['description']} -> Allowed tools: {', '.join(skill['allowed_tools'])} -> Read `{skill['path']}` for full instructionsStep 4 uses the following template (condensed):
## Skills System
You have access to a skills library that provides specialized capabilities and domain knowledge.
{skills_locations}
**Available Skills:**
{skills_list}
**How to Use Skills (Progressive Disclosure):**
Skills follow a **progressive disclosure** pattern - you see their name and description above, but only read full instructions when needed:
1. **Recognize when a skill applies**: Check if the user's task matches a skill's description
2. **Read the skill's full instructions**: Use the path shown in the skill list above
3. **Follow the skill's instructions**: SKILL.md contains step-by-step workflows, best practices, and examples
4. **Access supporting files**: Skills may include helper scripts, configs, or reference docs - use absolute paths
**When to Use Skills:**
- User's request matches a skill's domain (e.g., "research X" -> web-research skill)
- You need specialized knowledge or structured workflows
- A skill provides proven patterns for complex tasks
**Executing Skill Scripts:**
Skills may contain Python scripts or other executable files. Always use absolute paths from the skill list.
**Example Workflow:**
User: "Can you research the latest developments in quantum computing?"
1. Check available skills -> See "web-research" skill with its path
2. Read the skill using the path shown
3. Follow the skill's research workflow (search -> organize -> synthesize)
4. Use any helper scripts with absolute paths
Remember: Skills make you more capable and consistent. When in doubt, check if a skill exists for the task!This teaches the LLM the progressive-disclosure four-step workflow and usage scenarios (domain match, specialized knowledge, complex task patterns).
5. Revisiting the Initial Question
The difference in SkillsMiddleware position boils down to where the skill system prompt appears in the full system prompt.
Main Agent Prompt (Coordinator Role):
When the user asks you to do something:
1. **Understand first** — read relevant files, check existing patterns. Quick but thorough — gather enough evidence to start, then iterate.
2. **Act** — implement the solution. Work quickly but accurately.
3. **Verify** — check your work against what was asked, not against your own output. Your first attempt is rarely correct — iterate.
Keep working until the task is fully complete. Don't stop partway and explain what you would do — just do it. Only yield back to the user when the task is done or you're genuinely blocked.Sub-Agent Prompt (Executor Role):
In order to complete the objective that the user asks of you, you have access to a number of standard tools.Therefore, SkillsMiddleware placed before
SubAgentMiddleware in the main agent ensures the coordinator has full capability awareness <strong>before</strong> delegating to sub-agents. Sub-agents receive their own separate skills configuration, placing <code>SkillsMiddleware later to achieve <strong>separation of concerns and resource optimization</strong> at the execution layer.</code>Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
