Skill Architecture¶
A skill is a reusable capability package that helps an agent perform a class of tasks consistently. In architecture terms, the important question is not only what a skill contains, but where it belongs, what it may depend on and how the runtime discovers and executes it without coupling the whole application to one agent framework.
A useful mental model is:
Skill Contract
├── purpose
├── input/output contract
├── instructions
├── context requirements
├── allowed capabilities
├── policy requirements
├── evaluation contract
└── version
↓
Skill Runtime Adapter
↓
Application Ports / Capabilities
↓
Infrastructure Adapters
The skill describes a reusable behavior. It should not become a hidden service layer that bypasses the application.
Skill vs tool vs application use case¶
These are related but different.
Application use case
= deterministic business operation
Tool / capability
= operation the runtime can execute
Skill
= reusable guidance + contracts for solving a task, potentially using multiple capabilities
Example:
ReviewPullRequest skill
├── read diff capability
├── read tests capability
├── architecture-review instructions
└── structured findings output
The skill does not own GitHub access. It depends on application capabilities that provide repository data.
Where should skills live?¶
A common temptation is one global folder:
skills/
├── billing.md
├── support.md
├── github.md
├── legal.md
└── everything_else.md
This quickly becomes a cross-domain dumping ground.
For domain-specific skills, prefer ownership near the business capability that understands the domain:
support/
├── application/
├── domain/
├── skills/
│ ├── triage_ticket/
│ └── draft_response/
└── infrastructure/
agent_runtime/
├── skill_registry.py
└── skill_executor.py
The shared runtime knows how to discover and execute skills; the support module owns support-specific skill semantics.
A cross-cutting skill such as generic summarization may live in a shared AI capability module if it truly has no domain owner.
Skill descriptor¶
A skill should have a machine-readable descriptor instead of relying only on a file name and prose description.
{
"id": "support.triage_ticket",
"version": "2.1",
"input_schema": "TicketTriageInputV2",
"output_schema": "TicketTriageResultV2",
"required_capabilities": [
"support.ticket.read",
"customer.profile.read"
],
"risk": "READ_ONLY",
"owner": "support"
}
This allows the runtime to validate compatibility before execution.
Skills should depend on ports, not providers¶
Bad:
TriageSkill
↓
Zendesk SDK
Better:
TriageSkill
↓
TicketReader capability / port
↓
Zendesk adapter
The skill cares about tickets, not Zendesk.
Likewise:
CodeReviewSkill
↓
RepositoryReader
↓
GitHub / GitLab / local repo adapter
This makes skills testable and reusable across infrastructure changes.
Skill registry and discovery¶
The agent runtime may maintain a registry:
Skill Registry
├── code.review
├── support.triage
├── support.response_draft
├── billing.explain_invoice
└── incident.analyze
A descriptor should contain enough information for filtering without loading all instructions into context.
Useful discovery metadata:
id
version
short description
input type
output type
required capabilities
risk class
domain/owner
cost hints
supported models/runtime features
The runtime can first narrow candidates deterministically and only then let a model choose when semantic routing is required.
Skill implementation vs runtime configuration¶
Avoid mixing the stable skill definition with deployment-specific values.
Stable skill:
Review pull requests for correctness, architecture and backward compatibility.
Runtime configuration:
model = gpt-x
max_tokens = 8000
repository = foo/bar
timeout = 60s
The same skill may run under different models, budgets or providers without redefining its semantic contract.
Input and output contracts¶
Skills should not accept an unstructured bag of runtime state unless the task genuinely requires it.
Prefer:
class ReviewPullRequestInput:
repository_id: str
pull_request_id: int
review_focus: list[str]
class ReviewPullRequestResult:
findings: list[Finding]
summary: str
confidence: float
The runtime maps canonical state into the skill input and maps the skill result back into application state.
This protects the rest of the application from prompt-specific output shapes.
Skill dependencies¶
Skills may depend on:
capabilities
domain knowledge
retrieval sources
other deterministic services
model features
Be cautious with skill-to-skill dependencies.
A deeply nested graph like:
Skill A
↓
Skill B
↓
Skill C
↓
Skill D
creates hidden execution flow, budget multiplication and circular dependency risk.
Prefer composition at an explicit workflow/runtime layer when multiple substantial skills are coordinated.
Workflow / agent runtime
├── Skill A
├── Skill B
└── aggregation step
A skill may call a small reusable sub-capability, but orchestration should remain visible.
Skill execution adapter¶
The runtime can translate a skill definition into a provider-specific model request:
Skill contract
↓
Context builder
↓
Skill executor
↓
Model gateway
↓
Provider API
This prevents skill definitions from embedding direct OpenAI/Anthropic/etc. SDK calls.
Provider-specific concerns such as tool-call syntax, response parsing or tracing belong in the execution infrastructure.
Versioning¶
There are at least three different versions to distinguish:
Skill contract version
Skill implementation/instruction version
Runtime/provider version
A change to wording that preserves input/output semantics may be an implementation revision.
A change from:
findings: list[str]
to:
findings: list[Finding]
is a contract change and may require a new major version.
Track versions in traces so regressions can be tied to a concrete skill/model/runtime combination.
Compatibility and rollout¶
Skills should support normal software rollout patterns:
offline eval
↓
shadow / replay
↓
canary
↓
production
↓
monitor
↓
rollback if needed
Do not overwrite a production skill silently and lose the ability to explain which version generated a decision.
Permission architecture¶
A skill should declare what capabilities it may request, not possess permanent credentials itself.
Skill declares requirement
↓
Runtime resolves allowed capability
↓
Policy engine checks current run/user
↓
Execution adapter receives scoped credential
This follows least privilege and prevents a skill package from becoming a secret container.
Context ownership¶
A skill can declare context requirements:
needs:
- pull request diff
- repository architecture guide
- failing test summary
But the context builder should resolve and assemble those sources.
The skill should not independently scrape random systems to create its own hidden context universe.
That preserves provenance, security policy and observability.
Evaluation belongs to the skill contract¶
A reusable skill should define how quality is evaluated.
For example, a PR review skill might have:
correctness finding precision
critical issue recall
false-positive rate
schema validity
tool-call efficiency
latency
cost
The eval definition is part of maintaining the skill as a software artifact.
Skill package example¶
skills/code_review/
├── skill.yaml
├── instructions.md
├── examples/
│ ├── good_review.md
│ └── missed_bug.md
├── schemas/
│ ├── input.json
│ └── output.json
├── knowledge/
│ └── review_principles.md
└── evals/
└── cases.yaml
Provider-specific code does not need to live here.
Common anti-patterns¶
One giant skill for the whole application¶
A mega-skill accumulates unrelated tools, instructions and context and becomes impossible to evaluate reliably.
Skill definitions tied to one model provider¶
A skill that directly calls a provider SDK is harder to test, route and replace.
Hidden tool access¶
If a skill silently gains write capabilities, the runtime cannot reason about risk accurately.
Recursive skill graphs¶
Skills calling skills calling skills often hide control flow that belongs in an explicit workflow or orchestrator.
Skills as business logic replacement¶
Domain invariants should remain deterministic application/domain logic.
Everything is a skill¶
Simple deterministic transformations do not need an LLM skill wrapper.
Engineering takeaways¶
- Treat a skill as a versioned reusable behavior contract, not a magic prompt file.
- Domain-specific skills should have clear module ownership.
- Skills depend on application capabilities/ports rather than provider SDKs.
- Discovery metadata should be cheap to load; full instructions are loaded only when selected.
- Significant skill composition belongs in explicit orchestration rather than hidden recursive calls.
- Skills declare capability requirements; the runtime grants current scoped access.
- Context assembly, provider integration and credentials remain runtime/infrastructure responsibilities.
- Evals, versioning and rollout make skills maintainable software artifacts.