Skip to content
TechBits
Go back

Building Coding Agent Part 5: Skill Loading – On-Demand Domain Knowledge

Edit page

Welcome back to the series!

Where we left off: In Part 4, we tackled the context window problem for active tasks. By spawning isolated Subagents with clean slate messaging histories, we prevented token bloat when exploring codebases.

But what about the passive knowledge? Our system prompts are starting to get crowded. Today, we’re fixing the bloat of instructions. The core philosophy of this lesson is: “Load knowledge when you need it, not upfront.”

The Token Trap of Static System Prompts

When you start building a coding agent, the natural first step is to shove everything into the system prompt. You tell the agent:

This approach works for a while, but it creates a massive Token Trap. If you have 10 domain skills, and each contains 2,000 tokens of strict instructions, you are injecting 20,000 tokens of “dead weight” into the system prompt for every single turn.

If the user asks the agent to parse a CSV file in Python, the agent doesn’t need to know the frontend React architecture rules. Passing those irrelevant rules degrades the model’s performance, causes it to lose focus, and burns through your token limits.

The Two-Layer Solution

To fix this, we’re shifting from a monolithic chunk of text to a dynamic Skill Loading architecture. It acts as a two-layer system:

Layer 1 (The Menu): Instead of sending the heavy, multi-page instruction manual to the system prompt, we only pass a lightweight menu. The static system prompt simply lists the names and a one-sentence description of available skills. It’s incredibly cheap on tokens.

Layer 2 (The Delivery): We give the agent a specialized base tool called load_skill. Whenever the agent decides it needs to execute a particular workflow, it runs load_skill("git_conventions"). The system executes the underlying Python logic, fetching the detailed 2,000-token instruction manual and injecting it straight into the tool_result. This on-demand knowledge injection is exactly what we need.

Structuring Skills in the Filesystem

How does the agent know what skills exist? We need an organized, physical structure.

We can create a skills/ directory inside our agent codebase. Each skill gets its own folder, accompanied by a SKILL.md file.

Inside a SKILL.md file, we use YAML frontmatter to separate the metadata (Layer 1 menu) from the Markdown body (Layer 2 payload).

---
name: python-testing
description: Use this skill for writing robust tests using pytest.
---
# Python Testing Best Practices
1. Ensure you create a `tests/` directory if missing...
[...2,000 words of testing patterns...]

Implementing the SkillLoader

To orchestrate this mechanism in our backend, we implement a custom Python SkillLoader class.

Here is an example setup that dynamically reads the filesystem and builds out our two-layer approach:

import os
from pathlib import Path
import yaml

class SkillLoader:
    def __init__(self, skills_dir: Path):
        self.skills = {}
        # 1. Scan the filesystem for skill definitions
        for f in sorted(skills_dir.rglob("SKILL.md")):
            text = f.read_text()
            # Assuming _parse_frontmatter separates YAML from body
            meta, body = self._parse_frontmatter(text) 
            name = meta.get("name", f.parent.name)
            
            # Store both elements 
            self.skills[name] = {"meta": meta, "body": body}

    # Used for Layer 1 - The System Prompt Menu
    def get_descriptions(self) -> str:
        lines = []
        for name, skill in self.skills.items():
            desc = skill["meta"].get("description", "")
            lines.append(f"  - {name}: {desc}")
        return "\n".join(lines)

    # Used for Layer 2 - The Tool Result Payload
    def get_content(self, name: str) -> str:
        skill = self.skills.get(name)
        if not skill:
            return f"Error: Unknown skill '{name}'."
        return f'<skill name="{name}">\n{skill["body"]}\n</skill>'

# Initialize directory scanner
SKILL_LOADER = SkillLoader(Path("skills"))

Now, we bridge the gap in our agent application:

# The Cheap System Prompt
SYSTEM_PROMPT = f"""You are a coding agent.
You have the following skills available to load on-demand:
{SKILL_LOADER.get_descriptions()}
"""

# The Payload Tool Delivery
TOOL_HANDLERS = {
    # ...other base tools...
    "load_skill": lambda **kw: SKILL_LOADER.get_content(kw["name"]),
}

Conclusion

By treating domain knowledge as an on-demand tool rather than bulk, upfront instructions, you keep your system prompt clean.

Your agent stays sharp, lightning-fast, and heavily token-efficient. Best of all, this architecture allows infinite scaling. You can teach your agent 500 different specialized workflows without hitting the dreaded context window limit!

Next time, we’ll explore what happens when we let our agent remember past interactions, giving it access to external memory over long-term projects. See you then!


Edit page
Share this post on:

Previous Post
Building Coding Agent Part 6: Context Compaction – Infinite Sessions Without the Bloat
Next Post
Building Coding Agent Part 4: Subagents – Clean Context per Subtask