Build a Modular Skill-Based Agent System for LLMs with Dynamic Tool Routing in Python

In this tutorial, we build a complete skill-based agent system for large language models and explore how modular capabilities can be structured like an operating system for AI agents. We define reusable skills, attach metadata and schemas to them, register them in a central registry, and enable dynamic orchestration through tool calling and multi-step reasoning. As we move through the implementation, we show how an agent can select the right skill for a task, compose multiple skills for more advanced workflows, hot-load new capabilities at runtime, and track everything through an observability dashboard.

Copy CodeCopiedUse a different Browserimport os
import sys
import json
import time
import getpass
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from datetime import datetime
from enum import Enum
from typing import Any, Dict, List, Optional, Type

try:
from openai import OpenAI
from rich.console import Console
from rich.panel import Panel
from rich.table import Table
from rich.tree import Tree
except ImportError:
print(“Missing dependencies. Run: pip install openai pydantic rich”)
sys.exit(1)

def get_api_key() -> str:
“””Prompt for the OpenAI API key securely (input hidden in terminal).”””
key = os.environ.get(“OPENAI_API_KEY”, “”).strip()
if key:
print(” Using OPENAI_API_KEY from environment.”)
return key
print(“n Enter your OpenAI API key (input hidden):”)
key = getpass.getpass(” API Key: “).strip()
if not key:
print(” No API key provided. Exiting.”)
sys.exit(1)
os.environ[“OPENAI_API_KEY”] = key
return key

API_KEY = get_api_key()
client = OpenAI(api_key=API_KEY)
console = Console()
MODEL = “gpt-4o-mini”

class SkillCategory(Enum):
DATA = “data”
REASONING = “reasoning”
GENERATION = “generation”
SYSTEM = “system”
INTEGRATION = “integration”
META = “meta”

@dataclass
class SkillMetadata:
name: str
description: str
category: SkillCategory
version: str = “1.0.0”
author: str = “system”
tags: List[str] = field(default_factory=list)
requires_skills: List[str] = field(default_factory=list)
output_type: str = “text”
cost_estimate: float = 0.001
created_at: str = field(default_factory=lambda: datetime.now().isoformat())

class Skill(ABC):
“””
Abstract base for every agent capability.
Each Skill is: self-describing · versioned · testable · composable.
“””

def __init__(self):
self.metadata = self._define_metadata()
self.schema = self._define_schema()
self._call_count = 0
self._total_latency = 0.0

@abstractmethod
def _define_metadata(self) -> SkillMetadata: …

@abstractmethod
def _define_schema(self) -> dict: …

@abstractmethod
def execute(self, **kwargs) -> Any: …

def __call__(self, **kwargs) -> Any:
t0 = time.time()
result = self.execute(**kwargs)
self._call_count += 1
self._total_latency += (time.time() – t0) * 1000
return result

def to_openai_tool(self) -> dict:
return {
“type”: “function”,
“function”: {
“name”: self.metadata.name,
“description”: self.metadata.description,
“parameters”: self.schema,
}
}

@property
def stats(self) -> dict:
avg = round(self._total_latency / self._call_count, 2) if self._call_count else 0
return {“calls”: self._call_count, “avg_latency_ms”: avg}

class SkillRegistry:
“””
Central catalog of all agent capabilities.
Analogue: OS process/syscall table.
“””

def __init__(self):
self._skills: Dict[str, Skill] = {}
self._category_index: Dict[str, List[str]] = {}
self._tag_index: Dict[str, List[str]] = {}

def register(self, skill: Skill) -> “SkillRegistry”:
name = skill.metadata.name
self._skills[name] = skill
self._category_index.setdefault(skill.metadata.category.value, []).append(name)
for tag in skill.metadata.tags:
self._tag_index.setdefault(tag, []).append(name)
return self

def get(self, name: str) -> Optional[Skill]:
return self._skills.get(name)

def list_all(self) -> List[Skill]:
return list(self._skills.values())

def get_by_category(self, cat: SkillCategory) -> List[Skill]:
return [self._skills[n] for n in self._category_index.get(cat.value, [])]

def to_openai_tools(self, names: Optional[List[str]] = None) -> List[dict]:
skills = ([self._skills[n] for n in names if n in self._skills]
if names else list(self._skills.values()))
return [s.to_openai_tool() for s in skills]

def display(self):
tbl = Table(title=” Skill Registry”, header_style=”bold cyan”, show_lines=True)
tbl.add_column(“Name”, style=”bold”)
tbl.add_column(“Category”, style=”yellow”)
tbl.add_column(“Version”)
tbl.add_column(“Tags”, style=”dim”)
tbl.add_column(“Description”)
for s in self._skills.values():
m = s.metadata
tbl.add_row(m.name, m.category.value, m.version,
“, “.join(m.tags[:3]),
m.description[:65] + (“…” if len(m.description) > 65 else “”))
console.print(tbl)

def __len__(self):
return len(self._skills)

We set up the core environment and initialize the system required to build our skill-based agent framework. We define fundamental abstractions such as Skill, SkillMetadata, and SkillCategory, which allow us to represent each capability as a structured, self-describing module. We also configure the OpenAI client, secure the API key input, and establish the base architecture that all system skills will inherit.

Copy CodeCopiedUse a different Browserclass CalculatorSkill(Skill):
def _define_metadata(self):
return SkillMetadata(
name=”calculator”,
description=”Evaluate mathematical expressions. Supports arithmetic, powers, and ”
“math functions: sqrt, abs, round, log, sin, cos, tan.”,
category=SkillCategory.REASONING,
tags=[“math”, “arithmetic”, “compute”],
output_type=”text”, cost_estimate=0.0,
)

def _define_schema(self):
return {“type”: “object”,
“properties”: {“expression”: {“type”: “string”,
“description”: “A Python math expression e.g. ‘2**10 + sqrt(144)'”}},
“required”: [“expression”]}

def execute(self, expression: str) -> str:
import math
safe = {“__builtins__”: {}, “sqrt”: math.sqrt, “abs”: abs, “round”: round,
“pow”: pow, “log”: math.log, “pi”: math.pi, “e”: math.e,
“sin”: math.sin, “cos”: math.cos, “tan”: math.tan}
try:
return f”Result: {eval(expression, safe)}”
except Exception as ex:
return f”Error: {ex}”

class TextSummarizerSkill(Skill):
def _define_metadata(self):
return SkillMetadata(
name=”text_summarizer”,
description=”Summarize text at three verbosity levels: brief (1-2 sentences), ”
“standard (1 paragraph), or detailed (structured bullets).”,
category=SkillCategory.GENERATION,
tags=[“summarize”, “nlp”, “text”, “writing”],
)

def _define_schema(self):
return {“type”: “object”,
“properties”: {
“text”: {“type”: “string”},
“mode”: {“type”: “string”, “enum”: [“brief”, “standard”, “detailed”],
“default”: “standard”}},
“required”: [“text”]}

def execute(self, text: str, mode: str = “standard”) -> str:
instructions = {“brief”: “in 1-2 sentences”, “standard”: “in one paragraph”,
“detailed”: “as structured bullet points covering main ideas, key details, and conclusions”}
r = client.chat.completions.create(
model=MODEL, max_tokens=300,
messages=[
{“role”: “system”, “content”: f”Summarize {instructions.get(mode, instructions[‘standard’])}. Be concise.”},
{“role”: “user”, “content”: text}])
return r.choices[0].message.content

class DataAnalystSkill(Skill):
def _define_metadata(self):
return SkillMetadata(
name=”data_analyst”,
description=”Analyse structured data (JSON or CSV) and extract statistical insights, ”
“trends, or answer specific questions.”,
category=SkillCategory.DATA,
tags=[“data”, “analysis”, “statistics”, “csv”, “json”],
)

def _define_schema(self):
return {“type”: “object”,
“properties”: {
“data”: {“type”: “string”, “description”: “Data as JSON array or CSV”},
“question”: {“type”: “string”, “description”: “Analytical question to answer”}},
“required”: [“data”, “question”]}

def execute(self, data: str, question: str) -> str:
r = client.chat.completions.create(
model=MODEL, max_tokens=400,
messages=[
{“role”: “user”, “content”: f”Data:n{data}nnQuestion: {question}”}])
return r.choices[0].message.content

class CodeGeneratorSkill(Skill):
def _define_metadata(self):
return SkillMetadata(
name=”code_generator”,
description=”Generate clean, commented Python code for a given task with a brief explanation.”,
category=SkillCategory.GENERATION,
tags=[“code”, “python”, “programming”, “script”],
)

def _define_schema(self):
return {“type”: “object”,
“properties”: {
“task”: {“type”: “string”},
“language”: {“type”: “string”, “default”: “python”}},
“required”: [“task”]}

def execute(self, task: str, language: str = “python”) -> str:
r = client.chat.completions.create(
model=MODEL, max_tokens=500,
messages=[
{“role”: “system”, “content”: f”Expert {language} developer. Write clean, commented code with a one-line explanation.”},
{“role”: “user”, “content”: task}])
return r.choices[0].message.content

We implement the SkillRegistry, which acts as the central catalog for all available agent capabilities. We create mechanisms to register, retrieve, categorize, and expose skills as tools that the language model can call dynamically. We also introduce the first concrete skill implementations such as the calculator and summarizer, demonstrating how real capabilities extend the abstract skill interface.

Copy CodeCopiedUse a different Browserclass FactCheckerSkill(Skill):
def _define_metadata(self):
return SkillMetadata(
name=”fact_checker”,
description=”Assess the factual accuracy of a claim. Returns verdict, confidence score, and explanation.”,
category=SkillCategory.REASONING,
tags=[“fact”, “verify”, “truth”, “accuracy”],
output_type=”json”,
)

def _define_schema(self):
return {“type”: “object”,
“properties”: {“claim”: {“type”: “string”}},
“required”: [“claim”]}

def execute(self, claim: str) -> str:
r = client.chat.completions.create(
model=MODEL, max_tokens=250,
messages=[
{“role”: “system”, “content”: ‘Fact checker. Respond ONLY with JSON: {“verdict”:”true|false|uncertain”,”confidence”:0.0-1.0,”explanation”:”…”}’},
{“role”: “user”, “content”: f”Fact-check: {claim}”}])
return r.choices[0].message.content

class TranslationSkill(Skill):
def _define_metadata(self):
return SkillMetadata(
name=”translator”,
description=”Translate text into any target language with optional formality control.”,
category=SkillCategory.GENERATION,
tags=[“translate”, “language”, “multilingual”],
)

def _define_schema(self):
return {“type”: “object”,
“properties”: {
“text”: {“type”: “string”},
“target_language”: {“type”: “string”},
“formality”: {“type”: “string”, “enum”: [“formal”, “informal”], “default”: “formal”}},
“required”: [“text”, “target_language”]}

def execute(self, text: str, target_language: str, formality: str = “formal”) -> str:
r = client.chat.completions.create(
model=MODEL, max_tokens=300,
messages=[
{“role”: “system”, “content”: f”Translate to {target_language} ({formality}). Output only the translation.”},
{“role”: “user”, “content”: text}])
return r.choices[0].message.content

class SentimentAnalyzerSkill(Skill):
def _define_metadata(self):
return SkillMetadata(
name=”sentiment_analyzer”,
description=”Classify sentiment of text: polarity, intensity, and confidence score.”,
category=SkillCategory.REASONING,
version=”2.0.0″,
tags=[“sentiment”, “emotion”, “nlp”, “classification”],
output_type=”json”,
)

def _define_schema(self):
return {“type”: “object”,
“properties”: {
“text”: {“type”: “string”},
“detailed”: {“type”: “boolean”, “default”: False}},
“required”: [“text”]}

def execute(self, text: str, detailed: bool = False) -> str:
extra = ‘ Also add “emotions”:{“joy”:0-1,”anger”:0-1,”fear”:0-1,”sadness”:0-1}’ if detailed else “”
r = client.chat.completions.create(
model=MODEL, max_tokens=200,
messages=[
{“role”: “system”, “content”: f’Respond ONLY with JSON: {{“polarity”:”positive|negative|neutral”,”intensity”:0.0-1.0,”confidence”:0.0-1.0}}.{extra}’},
{“role”: “user”, “content”: text}])
return r.choices[0].message.content

class SkillIntrospectorSkill(Skill):
“””Meta-skill — agents can query their own capability registry.”””

def __init__(self, registry: SkillRegistry):
self._registry = registry
super().__init__()

def _define_metadata(self):
return SkillMetadata(
name=”skill_introspector”,
description=”List all available skills or get details about a specific skill. ”
“Use when unsure which skill handles a task.”,
category=SkillCategory.META,
tags=[“meta”, “help”, “discover”, “list”],
)

def _define_schema(self):
return {“type”: “object”,
“properties”: {
“action”: {“type”: “string”, “enum”: [“list”, “describe”]},
“skill_name”: {“type”: “string”}},
“required”: [“action”]}

def execute(self, action: str, skill_name: str = None) -> str:
if action == “list”:
lines = [“Available skills:”]
for s in self._registry.list_all():
lines.append(f” • {s.metadata.name} [{s.metadata.category.value}]: {s.metadata.description[:80]}”)
return “n”.join(lines)
if action == “describe” and skill_name:
s = self._registry.get(skill_name)
if not s:
return f”Skill ‘{skill_name}’ not found.”
m = s.metadata
return (f”Skill: {m.name} v{m.version}nCategory: {m.category.value}n”
f”Description: {m.description}nTags: {‘, ‘.join(m.tags)}n”
f”Schema:n{json.dumps(s.schema, indent=2)}”)
return “Invalid action.”

We expand the system by adding more specialized skills that enable reasoning and natural language processing tasks. We implement tools for data analysis, code generation, translation, sentiment analysis, and fact checking, enabling the agent to solve a broader range of problems. We also add the SkillIntrospector, which enables the agent to inspect its own abilities and discover available skills.

Copy CodeCopiedUse a different Browserclass ResearchReportSkill(Skill):
“””
Composite skill — orchestrates TextSummarizer + DataAnalyst + CodeGenerator.
Demonstrates fractal skill architecture from the research paper.
“””

def __init__(self, registry: SkillRegistry):
self._registry = registry
super().__init__()

def _define_metadata(self):
return SkillMetadata(
name=”research_report”,
description=”Generate a structured research report by composing summarization, ”
“data analysis, and code generation sub-skills.”,
category=SkillCategory.META,
tags=[“research”, “report”, “composite”, “writing”],
requires_skills=[“text_summarizer”, “data_analyst”, “code_generator”],
)

def _define_schema(self):
return {“type”: “object”,
“properties”: {
“topic”: {“type”: “string”},
“data”: {“type”: “string”},
“include_code”: {“type”: “boolean”, “default”: True}},
“required”: [“topic”, “data”]}

def execute(self, topic: str, data: str, include_code: bool = True) -> str:
parts = [f”# Research Report: {topic}n”]

console.print(” [dim]→ Composite → text_summarizer[/dim]”)
summary = self._registry.get(“text_summarizer”)(text=data, mode=”detailed”)
parts.append(f”## Summaryn{summary}n”)

console.print(” [dim]→ Composite → data_analyst[/dim]”)
analysis = self._registry.get(“data_analyst”)(
data=data, question=f”Key quantitative insights about {topic}?”)
parts.append(f”## Quantitative Analysisn{analysis}n”)

if include_code:
console.print(” [dim]→ Composite → code_generator[/dim]”)
code = self._registry.get(“code_generator”)(
task=f”Python function to process and visualize data about {topic}”)
parts.append(f”## Code Samplen{code}n”)

parts.append(f”## Metadatan- Generated: {datetime.now().strftime(‘%Y-%m-%d %H:%M’)}n”
f”- Skills used: text_summarizer, data_analyst”
+ (“, code_generator” if include_code else “”))
return “n”.join(parts)

class SkillLoader:
“””Package-manager analogue for the Agent OS.”””

def __init__(self, registry: SkillRegistry):
self.registry = registry

def load(self, skill_class: Type[Skill], *args, **kwargs) -> str:
instance = skill_class(*args, **kwargs)
self.registry.register(instance)
console.print(f”[green] Hot-loaded skill: {instance.metadata.name}[/green]”)
return instance.metadata.name

def unload(self, name: str) -> bool:
if name in self.registry._skills:
del self.registry._skills[name]
console.print(f”[yellow] Unloaded skill: {name}[/yellow]”)
return True
return False

class SkillBasedAgent:
“””
LLM-driven agent that selects and chains skills dynamically.

Architecture:
User Request

LLM Reasoning ──→ tool_call

Skill Registry lookup

Skill Execution Engine

LLM Response Synthesis

Final Answer
“””

def __init__(self, registry: SkillRegistry, name: str = “AgentOS”,
max_iterations: int = 6, verbose: bool = True):
self.registry = registry
self.name = name
self.max_iterations = max_iterations
self.verbose = verbose
self.system_prompt = (
f”You are {name}, an AI agent with a dynamic skill registry.nn”
“PRINCIPLES:n”
“1. Use the most appropriate skill for each sub-task.n”
“2. Chain multiple skills sequentially for complex requests.n”
“3. Use skill_introspector if unsure which skill to pick.n”
“4. Synthesize all skill outputs into a coherent final answer.nn”
f”Loaded skills: {[s.metadata.name for s in registry.list_all()]}”
)

def _log(self, msg, **kw):
if self.verbose:
console.print(msg, **kw)

def _dispatch(self, name: str, args: dict) -> str:
skill = self.registry.get(name)
if not skill:
return f”Error: skill ‘{name}’ not in registry.”
self._log(f” [bold cyan]{name}[/bold cyan] args={json.dumps(args)[:120]}”, style=””)
try:
result = skill(**args)
self._log(f” [green]{str(result)[:160]}[/green]”)
return str(result)
except Exception as ex:
msg = f”Execution error: {ex}”
self._log(f” [red]{msg}[/red]”)
return msg

def run(self, user_input: str) -> str:
self._log(Panel(f”[bold]User:[/bold] {user_input}”,
title=f” {self.name}”, border_style=”blue”))

messages = [
{“role”: “system”, “content”: self.system_prompt},
{“role”: “user”, “content”: user_input},
]
tools = self.registry.to_openai_tools()

for i in range(self.max_iterations):
self._log(f”n[dim]── iteration {i+1} ──[/dim]”)

resp = client.chat.completions.create(
model=MODEL, messages=messages, tools=tools,
tool_choice=”auto”, max_tokens=700)
msg = resp.choices[0].message
reason = resp.choices[0].finish_reason

assistant_msg: dict = {“role”: “assistant”, “content”: msg.content}
if msg.tool_calls:
assistant_msg[“tool_calls”] = [tc.model_dump() for tc in msg.tool_calls]
messages.append(assistant_msg)

if reason == “stop” or not msg.tool_calls:
answer = msg.content or “(no response)”
self._log(Panel(f”[bold green]{answer}[/bold green]”,
title=” Final Answer”, border_style=”green”))
return answer

for tc in msg.tool_calls:
result = self._dispatch(tc.function.name,
json.loads(tc.function.arguments))
messages.append({“role”: “tool”,
“tool_call_id”: tc.id,
“content”: result})

return “Max iterations reached.”

We demonstrate advanced concepts, including skill composition and runtime extensibility. We create a composite ResearchReportSkill that orchestrates multiple underlying skills to automatically produce a structured research report. We also implement the SkillLoader and the SkillBasedAgent, which together enable dynamic skill loading, tool selection, and multi-step reasoning through the LLM-driven execution loop.

Copy CodeCopiedUse a different Browserdef show_dashboard(registry: SkillRegistry):
console.print(“n”)
console.print(Panel(“[bold]Agent OS — Observability Dashboard[/bold]”, border_style=”blue”))

tree = Tree(” [bold]Skill Tree[/bold]”)
for cat in SkillCategory:
skills = registry.get_by_category(cat)
if skills:
branch = tree.add(f”[yellow]{cat.value.upper()}[/yellow] ({len(skills)})”)
for s in skills:
st = s.stats
branch.add(f”[cyan]{s.metadata.name}[/cyan] v{s.metadata.version} | ”
f”calls={st[‘calls’]} | avg={st[‘avg_latency_ms’]} ms”)
console.print(tree)

tbl = Table(title=” Performance”, header_style=”bold magenta”, show_lines=True)
tbl.add_column(“Skill”, style=”bold”)
tbl.add_column(“Calls”, justify=”right”)
tbl.add_column(“Avg Latency ms”, justify=”right”)
tbl.add_column(“Output Type”)
tbl.add_column(“Deps”)

for s in sorted(registry.list_all(), key=lambda x: x._call_count, reverse=True):
st = s.stats
col = “green” if st[“avg_latency_ms”] < 1000 else (“yellow” if st[“avg_latency_ms”] < 3000 else “red”)
tbl.add_row(s.metadata.name, str(st[“calls”]),
f”[{col}]{st[‘avg_latency_ms’]}[/{col}]”,
s.metadata.output_type,
“, “.join(s.metadata.requires_skills) or “—”)
console.print(tbl)

total = sum(s._call_count for s in registry.list_all())
used = sum(1 for s in registry.list_all() if s._call_count > 0)
console.print(f”n[bold]Summary:[/bold] {len(registry)} registered | {used} used | {total} total callsn”)

def main():
console.rule(“[bold magenta]Skill-Based Agent Systems Tutorial[/bold magenta]”)

registry = SkillRegistry()
loader = SkillLoader(registry)

(registry
.register(CalculatorSkill())
.register(TextSummarizerSkill())
.register(DataAnalystSkill())
.register(CodeGeneratorSkill())
.register(FactCheckerSkill())
.register(TranslationSkill())
.register(SkillIntrospectorSkill(registry))
.register(ResearchReportSkill(registry)))

registry.display()

agent = SkillBasedAgent(registry, name=”AgentOS-Alpha”, verbose=True)

console.rule(“[bold yellow]DEMO 1 — Calculator Skill[/bold yellow]”)
agent.run(“What is (2**16 + sqrt(1764)) * 0.5?”)

console.rule(“[bold yellow]DEMO 2 — Text Summarizer Skill[/bold yellow]”)
text = (
“The transformer architecture, introduced in ‘Attention Is All You Need’ (2017), ”
“revolutionised NLP by replacing RNNs with parallel self-attention. Every token attends ”
“to every other token, capturing long-range dependencies efficiently. Multi-head attention ”
“learns diverse relationship types simultaneously. Positional encodings inject order ”
“information. The encoder-decoder design underpins BERT, GPT-3, GPT-4, and all modern LLMs.”
)
agent.run(f”Give me a brief summary: {text}”)

console.rule(“[bold yellow]DEMO 3 — Fact Checker Skill[/bold yellow]”)

console.rule(“[bold yellow]DEMO 4 — Multi-Skill Chain (Data + Calculator)[/bold yellow]”)
sales = json.dumps([
{“month”: “Jan”, “revenue”: 12500}, {“month”: “Feb”, “revenue”: 14200},
{“month”: “Mar”, “revenue”: 11800}, {“month”: “Apr”, “revenue”: 18900},
{“month”: “May”, “revenue”: 22100}, {“month”: “Jun”, “revenue”: 19300},
])
agent.run(
f”Analyse this sales data: {sales}. ”
“Find total revenue, best/worst months, and average monthly revenue. ”
“Then calculate: projected revenue if June grows 15%.”
)

console.rule(“[bold yellow]DEMO 5 — Code Generator Skill[/bold yellow]”)
agent.run(
“Generate a Python function that implements binary search on a sorted list, ”
“then translate the function’s docstring into Spanish.”
)

console.rule(“[bold yellow]DEMO 6 — Hot-Swap Skill Loading[/bold yellow]”)
loader.load(SentimentAnalyzerSkill)
agent.system_prompt += f”nAdditionally loaded: sentiment_analyzer”
agent.run(
“Analyse the sentiment of: ‘This product completely exceeded my expectations! ”
“Absolutely outstanding quality and the support team was incredibly responsive.'”
)

console.rule(“[bold yellow]DEMO 7 — Composite Research Report Skill[/bold yellow]”)
ml_data = (
“LLM Benchmarks 2025:n”
“GPT-4o: MMLU 88.7%, HumanEval 90.2%, $5/1M tokensn”
“Claude 3 Opus: MMLU 86.8%, HumanEval 84.9%, $15/1M tokensn”
“Gemini 1.5 Pro: MMLU 85.9%, HumanEval 71.9%, $3.5/1M tokensn”
“Llama 3.1 70B: MMLU 83.6%, HumanEval 80.5%, $0.72/1M tokensn”
)
report_skill = registry.get(“research_report”)
report = report_skill(topic=”LLM Model Performance Comparison”, data=ml_data, include_code=True)
console.print(Panel(report, title=” Research Report”, border_style=”cyan”))

console.rule(“[bold yellow]DEMO 8 — Full Agent OS Pipeline[/bold yellow]”)
agent.run(
“1. Fact-check: ‘Python was created by Linus Torvalds in 1989’.n”
“2. Calculate monthly cost: 1.5M tokens/day at $0.002 per 1000 tokens (30 days).n”
“3. Summarise your findings in one brief sentence.”
)

show_dashboard(registry)

console.print(Panel(
“[bold green]Tutorial complete![/bold green]n”
“Key concepts demonstrated:n”
” • Skill abstraction & self-describing metadatan”
” • Skill Registry as Agent OS kerneln”
” • MCP-compatible tool protocol (OpenAI function calling)n”
” • Dynamic skill routing via LLM ReAct loopn”
” • Skill composition (fractal architecture)n”
” • Hot-swap runtime loadingn”
” • Observability dashboard”,
title=” Summary”, border_style=”magenta”
))

if __name__ == “__main__”:
main()

We build the observability layer and the execution pipeline that demonstrates the system in action. We construct a dashboard to monitor skill usage, latency, and dependencies to understand how the agent operates internally. Finally, we assemble the registry, load all skills, run multiple demos, and execute a full multi-step agent workflow to showcase the complete skill-based architecture.

In conclusion, we created a fully working skill-based agent framework that demonstrates how large language model agents can become more modular, extensible, and interpretable. We showed how individual skills can function as self-contained building blocks, how a registry can manage and expose those capabilities, and how an agent can reason over them to solve simple and composite tasks. Through runtime skill loading, chained execution, and monitoring of usage statistics, we presented a practical architecture that helps us think of agent systems not as a single monolithic model but as an evolving ecosystem of specialized, composable intelligence.

Check out the Codes with Notebook here and Paper. Also, feel free to follow us on Twitter and don’t forget to join our 130k+ ML SubReddit and Subscribe to our Newsletter. Wait! are you on telegram? now you can join us on telegram as well.

Need to partner with us for promoting your GitHub Repo OR Hugging Face Page OR Product Release OR Webinar etc.? Connect with us
The post Build a Modular Skill-Based Agent System for LLMs with Dynamic Tool Routing in Python appeared first on MarkTechPost.