Agent Skills vs AGENTS.md: Designing Modular LLM Agent Behaviors
Explore two powerful approaches to building maintainable AI systems: code-based agent skills versus declarative AGENTS.md specifications. Learn when to use each pattern.
Contents 12 sections
- What Are Agent Skills?
- What Is AGENTS.md?
- Why Modular Agent Design Matters
- Architecture Comparison
- Implementing Agent Skills
- Implementing AGENTS.md Specification
- Best Practices for Modular Agent Design
- Common Mistakes to Avoid
- Real-World Use Cases
- Hybrid Approaches
- Choosing the Right Approach
- Conclusion and Next Steps
As large language models become increasingly integrated into production systems, the challenge of designing maintainable, scalable agent architectures has emerged as a critical concern. Two distinct approaches have gained traction: agent skills and the AGENTS.md specification. Understanding the tradeoffs between these paradigms is essential for building robust AI systems.
What Are Agent Skills?
Agent skills represent a modular, function-based approach to defining agent capabilities. Each skill encapsulates a specific behavior, tool, or capability that an agent can invoke. Think of skills as reusable building blocks that agents can dynamically compose to solve complex tasks.
Core Characteristics
Modularity: Each skill is self-contained with its own:
- Input/output schema
- Execution logic
- Error handling
- Documentation
Composability: Skills can be chained, nested, or conditionally executed based on context.
Runtime Discovery: Agents can discover and load skills dynamically, enabling flexible behavior adaptation.
Example Skill Definition
class WebSearchSkill:
name = "web_search"
description = "Search the web for current information"
def execute(self, query: str, max_results: int = 5):
"""Execute web search and return structured results"""
return {
"results": search_engine.query(query, limit=max_results),
"timestamp": datetime.now(),
"source": "bing_api"
}What Is AGENTS.md?
The AGENTS.md specification is an emerging standard for defining agent behaviors through declarative markdown documents. Instead of code, developers describe agent capabilities, instructions, and constraints in a human-readable format.
Key Principles
Declarative Configuration: Agent behavior is specified through structured markdown rather than imperative code.
Human-Readable: Non-technical stakeholders can understand and contribute to agent definitions.
Version-Controlled: Agent configurations live alongside code in version control systems.
Example AGENTS.md Structure
# Customer Support Agent
## Purpose
Handle customer inquiries, resolve common issues, and escalate complex problems.
## Capabilities
- Query knowledge base
- Access order history
- Process refunds up to $100
- Escalate to human agent when needed
## Constraints
- Never promise refunds over $100
- Always verify customer identity
- Maintain professional tone
## Tools
- `search_kb(query: str)` - Search knowledge base
- `get_order(order_id: str)` - Retrieve order details
- `escalate(reason: str)` - Hand off to human agentWhy Modular Agent Design Matters
Modern LLM applications face several critical challenges that modular design addresses:
Maintainability at Scale
As agent systems grow, monolithic prompt engineering becomes unmanageable. Modular approaches allow teams to:
- Isolate changes to specific capabilities
- Test individual components independently
- Reduce regression risks
Team Collaboration
In large organizations, multiple teams contribute to agent capabilities. Modular design enables:
- Clear ownership boundaries
- Parallel development
- Specialized skill development
Performance Optimization
Different contexts require different capabilities. Modular systems can:
- Load only required skills, reducing token usage
- Implement capability-specific optimizations
- Cache frequently-used modules
Architecture Comparison
| Aspect | Agent Skills | AGENTS.md | |--------|-------------|-----------|| | Definition Method | Code-based (Python, TypeScript) | Markdown specification | | Learning Curve | Steeper (requires programming) | Gentle (readable by non-devs) | | Flexibility | High (full programmatic control) | Medium (constrained by spec) | | Type Safety | Strong (with typed languages) | Weak (text-based) | | Version Control | Standard code versioning | Markdown diffing | | Execution Model | Direct function calls | Interpreted by agent runtime | | Testing | Unit tests, integration tests | Behavioral validation | | Debugging | Stack traces, breakpoints | Log analysis, prompt tracing |
Implementing Agent Skills
Let's build a practical example demonstrating the skills-based approach.
Step 1: Define Skill Interface
from abc import ABC, abstractmethod
from typing import Dict, Any
class AgentSkill(ABC):
@property
@abstractmethod
def name(self) -> str:
pass
@property
@abstractmethod
def description(self) -> str:
pass
@abstractmethod
def execute(self, **kwargs) -> Dict[str, Any]:
passStep 2: Create Concrete Skills
class DataAnalysisSkill(AgentSkill):
name = "analyze_data"
description = "Perform statistical analysis on datasets"
def execute(self, data: list, analysis_type: str):
if analysis_type == "summary":
return {
"mean": sum(data) / len(data),
"min": min(data),
"max": max(data),
"count": len(data)
}
elif analysis_type == "distribution":
# Histogram logic here
passStep 3: Build Skill Registry
class SkillRegistry:
def __init__(self):
self._skills = {}
def register(self, skill: AgentSkill):
self._skills[skill.name] = skill
def get_skill(self, name: str) -> AgentSkill:
return self._skills.get(name)
def list_skills(self) -> list:
return [
{"name": s.name, "description": s.description}
for s in self._skills.values()
]Step 4: Agent Execution Engine
class SkillBasedAgent:
def __init__(self, llm, registry: SkillRegistry):
self.llm = llm
self.registry = registry
async def execute_task(self, task: str):
# LLM decides which skills to use
plan = await self.llm.generate(
f"Available skills: {self.registry.list_skills()}\n"
f"Task: {task}\n"
f"Generate execution plan as JSON."
)
results = []
for step in plan["steps"]:
skill = self.registry.get_skill(step["skill_name"])
result = skill.execute(**step["parameters"])
results.append(result)
return resultsImplementing AGENTS.md Specification
Now let's explore implementing a runtime for AGENTS.md files.
Step 1: Define the Spec Schema
# agents.schema.yml
agent:
required:
- name
- purpose
- capabilities
optional:
- constraints
- tools
- examples
- escalation_policyStep 2: Parser Implementation
import re
from dataclasses import dataclass
@dataclass
class AgentDefinition:
name: str
purpose: str
capabilities: list[str]
constraints: list[str]
tools: dict[str, str]
class AgentMDParser:
def parse(self, markdown_content: str) -> AgentDefinition:
sections = self._split_sections(markdown_content)
return AgentDefinition(
name=self._extract_title(markdown_content),
purpose=sections.get("Purpose", ""),
capabilities=self._parse_list(sections.get("Capabilities", "")),
constraints=self._parse_list(sections.get("Constraints", "")),
tools=self._parse_tools(sections.get("Tools", ""))
)
def _split_sections(self, content: str) -> dict:
sections = {}
current_section = None
for line in content.split("\n"):
if line.startswith("## "):
current_section = line[3:].strip()
sections[current_section] = ""
elif current_section:
sections[current_section] += line + "\n"
return sectionsStep 3: Runtime Executor
class AgentMDRuntime:
def __init__(self, agent_def: AgentDefinition, tool_registry):
self.definition = agent_def
self.tools = tool_registry
async def execute(self, user_input: str):
# Build context from AGENTS.md
context = f"""
You are: {self.definition.name}
Purpose: {self.definition.purpose}
You can:
{chr(10).join(f"- {cap}" for cap in self.definition.capabilities)}
Constraints:
{chr(10).join(f"- {con}" for con in self.definition.constraints)}
Available tools: {list(self.definition.tools.keys())}
"""
# Execute with LLM
response = await llm.chat(
system=context,
user=user_input
)
return responseBest Practices for Modular Agent Design
For Agent Skills
✅ Keep skills atomic: Each skill should do one thing well
✅ Define clear interfaces: Use type hints and schema validation
✅ Implement robust error handling: Skills should fail gracefully
✅ Write comprehensive tests: Unit test each skill independently
✅ Document expected inputs/outputs: Include examples in docstrings
✅ Version your skills: Use semantic versioning for skill APIs
For AGENTS.md
✅ Be explicit about constraints: Don't rely on implicit behavior
✅ Maintain consistency: Use standard formatting across all agents
✅ Include examples: Show expected behaviors with sample interactions
✅ Keep it concise: Verbose specs lead to token waste
✅ Review regularly: Agent definitions should evolve with requirements
✅ Validate at deploy time: Catch spec errors before runtime
Common Mistakes to Avoid
❌ Over-engineering skills: Start simple, add complexity as needed
❌ Tight coupling: Skills shouldn't depend on each other's implementation
❌ Ignoring performance: Monitor token usage and latency per skill
❌ Incomplete error handling: Always account for tool failures
❌ Lack of observability: Instrument skills for debugging
❌ Static agent definitions: Build mechanisms for runtime adaptation
❌ Mixing paradigms inconsistently: Choose one primary approach
Real-World Use Cases
E-Commerce Support Agent (Skills-Based)
skills = [
OrderLookupSkill(),
InventoryCheckSkill(),
RefundProcessingSkill(),
ShippingTrackingSkill(),
ProductRecommendationSkill()
]
agent = SkillBasedAgent(llm, SkillRegistry(skills))This approach works well when operations require complex business logic, database transactions, and integration with multiple backend systems.
Content Moderation Agent (AGENTS.md)
# Content Moderator
## Purpose
Review user-generated content for policy violations
## Capabilities
- Detect harmful content
- Identify spam patterns
- Flag copyright violations
- Assess sentiment
## Constraints
- Never auto-delete content without review
- Flag borderline cases for human review
- Respect regional content policiesThis specification approach excels when behavior is primarily rule-based and benefits from easy audibility by compliance teams.
Hybrid Approaches
Many production systems combine both paradigms:
AGENTS.md for High-Level Behavior: Define agent personality, constraints, and decision frameworks
Skills for Execution: Implement concrete capabilities as code-based skills
# Customer Service Agent
## Behavior
- Friendly and helpful tone
- Escalate after 3 failed resolution attempts
## Available Skills
- `search_knowledge_base()`
- `create_ticket()`
- `process_refund()`This hybrid model provides the readability of AGENTS.md with the power and type safety of programmatic skills.
Choosing the Right Approach
Use Agent Skills When:
- You need strong type safety and testing
- Your team is engineering-focused
- Capabilities involve complex logic or integrations
- Performance optimization is critical
Use AGENTS.md When:
- Non-technical stakeholders must understand agent behavior
- Behavior is primarily rule-based
- Rapid iteration on agent personality is needed
- Compliance and auditability are paramount
Use Hybrid When:
- You have diverse stakeholder requirements
- System complexity justifies multiple abstraction layers
- You want the best of both worlds
Conclusion and Next Steps
Both agent skills and AGENTS.md specifications are valuable tools for building modular LLM agent systems. Skills provide maximum flexibility and power, while AGENTS.md offers unmatched readability and accessibility.
Start with these steps:
- Audit your current agent architecture for modularity opportunities
- Identify capabilities that would benefit from isolation
- Choose the approach that best fits your team and requirements
- Build a small proof-of-concept before full migration
- Establish testing and observability practices early
The future of AI agent development lies in composability. Whether through code-based skills, declarative specifications, or hybrid approaches, modular design patterns will be essential for building maintainable, scalable AI systems.
The key is not which approach you choose, but that you design for modularity from day one.