When to use a skill vs. a custom agent
| Scenario | Skill | Custom agent |
|---|---|---|
| Reusable behavior shared across multiple agents | ✅ | ❌ |
| Standalone, end-to-end workflow | ❌ | ✅ |
| Quickly testable in isolation | ✅ | ✅ |
| Versioned + auditable | ✅ | ✅ |
| Has its own avatar / persona | ❌ | ✅ |
Skill shape
ASkill row has:
| Field | Type | Purpose |
|---|---|---|
skill_id | UUID | Stable id. |
client_id | string | Tenant scope. |
name | string | Display name. |
description | string | What the skill does. |
prompt | text | System prompt fragment, prepended to the agent’s prompt when the skill is active. |
tools | JSON list | Tool keys this skill expects/uses. |
output_template | text | null | Optional Jinja-style template for shaping the skill’s response. |
version | int | Auto-incremented on save. |
is_active | bool | |
created_by, created_at, updated_at | meta |
Markdown skills
skills/markdown.py provides a renderer that converts Skill prompts written in lightweight markdown into the format LangGraph expects. Operators write skills like:
## Tools and ## Output sections are parsed and applied to the underlying Skill model. The ## Output block becomes the output_template.
Skill chaining + run replay
Two endpoints make skills production-grade:POST /api/skills/<id>/clone/— fork a skill so you can iterate without breaking the in-use version.POST /api/skills/<id>/run-replay/— re-run a previous invocation through the current skill version (not the version that ran originally). Surfaces gains from a prompt tweak retroactively.
Invocation paths
Skills are passed into the LangGraph swarm at run time. The agent’s tool list is augmented with the skill’s tools (deduplicated), and the skill’s prompt is prepended to the agent’s system prompt. Three places this happens:- In-chat — a user runs
/skill <name>(the chat parser detects the slash command and loads the skill into the next turn’s context). - Custom agent — a custom agent’s tool list can include
run_skill:<skill_id>, which is a meta-tool that the agent calls when it wants to invoke a skill mid-conversation. - Automation — the
run_agentaction accepts askill_idfield; if set, the skill is loaded into the one-shot run.
Endpoints
List skills the caller can see. Tenant-scoped.
POST /api/skills/
Create a new skill. Body:
{ name, description, prompt, tools, output_template? }.Fetch a single skill (latest version).
List historical versions of a skill.
POST /api/skills/<id>/clone/
Fork a skill into a new id. Useful for A/B-testing prompt variants.
POST /api/skills/<id>/run-replay/
Replay a prior run through the skill’s current version. Returns the new run’s output for diffing.
shared.permissions.AgentSurfaceAllowed.
Worked example
A “Hook Diagnoser” skill that any creative-savvy agent can call:tools: ['run_skill:hook-diagnoser', ...]. When a user says “diagnose creative-abc-123”, Space Bob calls the skill, which calls find_similar_creatives + recommend_next_creative_brief and shapes the response per the skill’s output template.
Where the code lives
| Concern | Path |
|---|---|
| Model | skills/models.py |
| Markdown parser | skills/markdown.py |
| API views | skills/views.py |
| URL routes | skills/urls.py |
| Frontend skill editor | l5ui/src/app/components/skills/ |
| Swarm wiring | ai_chat/graph.py |
.png?fit=max&auto=format&n=Frm2GFbmok4D-yJA&q=85&s=93c3ebd47542af65d1cd06d8563a7f6e)