352 lines
12 KiB
Markdown
352 lines
12 KiB
Markdown
# Didactyl — Skills
|
|
|
|
See also: [CONTEXT.md](CONTEXT.md) · [TOOLS.md](TOOLS.md)
|
|
|
|
## Overview
|
|
|
|
A skill is a **set of instructions for the LLM** stored as a Nostr event. Skills teach the agent how to accomplish tasks — the LLM reads the instructions, reasons about them, and uses tools to take action.
|
|
|
|
Think of it like a woodshop: a **skill** is knowing how to carve — the technique, the judgment, the decision-making. A **tool** is the chisel. The skill never directly uses the chisel without the craftsperson (the LLM) in the loop. If you want a hardcoded program that runs without reasoning, that's a tool or an external program — not a skill.
|
|
|
|
Skills are portable, shareable, and discoverable — they live on Nostr relays as standard events. Every skill execution involves the LLM.
|
|
|
|
---
|
|
|
|
## Skill Events
|
|
|
|
| Kind | Purpose | Replaceable? |
|
|
|---|---|---|
|
|
| `31123` | Public skill definition | Yes, by d-tag |
|
|
| `31124` | Private skill definition | Yes, by d-tag |
|
|
| `10123` | Skill adoption list | Yes, single per pubkey |
|
|
|
|
---
|
|
|
|
## Skill Content
|
|
|
|
The skill event's `content` field is a JSON object that defines the complete execution specification:
|
|
|
|
```json
|
|
{
|
|
"kind": 31123,
|
|
"content": {
|
|
"description": "Check spelling and grammar",
|
|
"context_mode": "full",
|
|
"llm": "openai/gpt-4o-mini, cheap",
|
|
"tools": false,
|
|
"max_tokens": 2000,
|
|
"temperature": 0.1,
|
|
"template": "system:\nYou are a spelling and grammar checker.\n\nRules:\n- Fix spelling errors\n- Fix grammar errors\n- Preserve original formatting\n- Ignore: API, JSON, HTTP, nostr, pubkey, npub, nsec, NIP\n- Canadian English preferred\n- Return ONLY the corrected text, no explanations\n\nuser:\n{{message}}"
|
|
},
|
|
"tags": [
|
|
["d", "spellcheck"],
|
|
["scope", "public"],
|
|
["description", "Spelling and grammar checker"]
|
|
]
|
|
}
|
|
```
|
|
|
|
### Content Fields
|
|
|
|
| Field | Type | Default | Description |
|
|
|-------|------|---------|-------------|
|
|
| `description` | string | — | Human-readable description |
|
|
| `context_mode` | string | `inject` | `inject`, `full`, or `override` |
|
|
| `llm` | string | `default` | LLM fallback chain |
|
|
| `tools` | bool/array | `true` | `false` = no tools, `true` = all tools, array = specific tool names |
|
|
| `template` | string | — | Context template (required for `full` mode) |
|
|
| `soul` | bool | `true` | Whether to include the agent's soul in context |
|
|
| `max_tokens` | int | — | Override max tokens for this skill |
|
|
| `temperature` | float | — | Override temperature for this skill |
|
|
|
|
---
|
|
|
|
## Context Modes
|
|
|
|
Skills control how the LLM context window is assembled using the `context_mode` field:
|
|
|
|
| Mode | Description | Use Case |
|
|
|------|-------------|----------|
|
|
| `inject` | Skill instructions appended to the agent's default context | Behavioral rules, knowledge additions |
|
|
| `full` | Skill provides its own complete context template | Spellcheck, translation, focused analysis |
|
|
| `override` | Skill replaces the default system prompt, keeps standard context structure | Different personality, same capabilities |
|
|
|
|
Default is `inject`. See the agent's soul/personality documentation for details on how context assembly works.
|
|
|
|
### Template Variables Are Tool Calls
|
|
|
|
Template variables are tool calls. When the template engine encounters a variable like `{{admin_profile}}`, it runs the corresponding tool and inserts the result into the context that gets sent to the LLM.
|
|
|
|
This is the unified resolution model: the soul prompt template uses `tool:` section directives, and skill templates use `{{tool_name}}` inline syntax, but both resolve through `tools_execute()`. There is no separate variable-resolver layer — every variable is backed by a tool.
|
|
|
|
| Variable | Tool Called | Description |
|
|
|----------|-----------|-------------|
|
|
| `{{soul}}` | *(built-in)* | The agent's identity/system context |
|
|
| `{{admin_profile}}` | `nostr_admin_profile` | Admin kind 0 profile |
|
|
| `{{admin_notes}}` | `nostr_admin_notes` | Admin recent notes |
|
|
| `{{admin_relays}}` | `nostr_admin_relays` | Admin relay list |
|
|
| `{{adopted_skills}}` | `adopted_skills` | Other adopted skill instructions |
|
|
| `{{dm_history}}` | *(expand directive)* | Recent DM conversation |
|
|
| `{{message}}` | *(built-in)* | Current user message |
|
|
| `{{triggering_event}}` | `trigger_event` | For triggered skills, the event JSON |
|
|
|
|
This means new data sources are added by adding tools, not by extending the template engine. If you can call it as a tool, you can use it as a template variable.
|
|
|
|
---
|
|
|
|
## LLM Specification with Fallback Chains
|
|
|
|
Each skill specifies which LLM to use with CSS font-family-style fallbacks. The runtime tries each model in order; if one is unavailable (API error, rate limit, not configured), it falls back to the next.
|
|
|
|
### Format
|
|
|
|
```
|
|
model-spec := model-ref ["," model-ref]*
|
|
model-ref := provider "/" model-name
|
|
| category-alias
|
|
```
|
|
|
|
### Examples
|
|
|
|
| Skill | LLM Spec |
|
|
|-------|----------|
|
|
| Spelling checker | `openai/gpt-4o-mini, cheap` |
|
|
| Complex analysis | `anthropic/claude-sonnet-4-20250514, openai/gpt-4o, smart` |
|
|
| Translation | `openai/gpt-4o-mini, fast` |
|
|
| Default behavior | `default` |
|
|
|
|
### Category Aliases
|
|
|
|
Aliases map to models in the agent's config:
|
|
|
|
```json
|
|
{
|
|
"llm": {
|
|
"provider": "openai",
|
|
"model": "gpt-4o",
|
|
"aliases": {
|
|
"fast": "openai/gpt-4o-mini",
|
|
"cheap": "openai/gpt-4o-mini",
|
|
"smart": "anthropic/claude-sonnet-4-20250514"
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
If all specified models fail, the agent's default model is used as a last resort.
|
|
|
|
```mermaid
|
|
sequenceDiagram
|
|
participant Skill as Skill Spec
|
|
participant Resolver as LLM Resolver
|
|
participant API1 as Model 1
|
|
participant API2 as Model 2
|
|
participant Default as Default Model
|
|
|
|
Skill->>Resolver: "anthropic/claude-sonnet, openai/gpt-4o-mini, cheap"
|
|
Resolver->>API1: try anthropic/claude-sonnet
|
|
alt Available
|
|
API1-->>Resolver: ✅
|
|
else Unavailable
|
|
API1-->>Resolver: ❌
|
|
Resolver->>API2: try openai/gpt-4o-mini
|
|
alt Available
|
|
API2-->>Resolver: ✅
|
|
else Unavailable
|
|
Resolver->>Default: resolve "cheap" alias
|
|
end
|
|
end
|
|
```
|
|
|
|
---
|
|
|
|
## Triggered Skills
|
|
|
|
A triggered skill has a trigger source attached. Didactyl supports `nostr-subscription`, `webhook`, `cron`, and `chain` trigger types.
|
|
|
|
### Trigger Tags
|
|
|
|
| Tag | Required | Description |
|
|
|---|---|---|
|
|
| `trigger` | Yes | Trigger type: `nostr-subscription`, `webhook`, `cron`, or `chain` |
|
|
| `filter` | Yes | Type-specific filter — see Trigger Types below |
|
|
| `enabled` | No | Whether active (default: `true`) |
|
|
|
|
> **Note:** The `action` tag is deprecated. All triggered skills are LLM-mediated. If you need a fast hardcoded action without LLM reasoning, use a tool or an external program — not a skill. Skills are instructions for the LLM; the LLM is always in the loop.
|
|
|
|
### Triggered Skill Execution
|
|
|
|
When a trigger fires, the skill content is sent to the LLM as instructions along with the triggering event JSON. The LLM reads both, reasons about what to do, and uses tools to take action.
|
|
|
|
This is the same execution model as DM-invoked skills — the only difference is the input source. A DM-invoked skill receives a user message; a triggered skill receives a triggering event.
|
|
|
|
```json
|
|
{
|
|
"kind": 31124,
|
|
"content": "You monitor Nostr mentions. When the triggering event mentions Bitcoin or Lightning, summarize it and DM the admin. Otherwise, ignore silently.",
|
|
"tags": [
|
|
["d", "mention-monitor"],
|
|
["trigger", "nostr-subscription"],
|
|
["filter", "{\"#p\":[\"<admin_pubkey>\"],\"kinds\":[1]}"],
|
|
["enabled", "true"]
|
|
]
|
|
}
|
|
```
|
|
|
|
A cron-triggered skill:
|
|
|
|
```json
|
|
{
|
|
"kind": 31124,
|
|
"content": "DM the admin a short heartbeat status message with the current time.",
|
|
"tags": [
|
|
["d", "heartbeat"],
|
|
["trigger", "cron"],
|
|
["filter", "*/5 * * * *"],
|
|
["enabled", "true"]
|
|
]
|
|
}
|
|
```
|
|
|
|
### Trigger Types
|
|
|
|
#### `nostr-subscription`
|
|
|
|
`filter` is a JSON-encoded Nostr subscription filter.
|
|
|
|
```json
|
|
["trigger", "nostr-subscription"],
|
|
["filter", "{\"#p\":[\"<admin_pubkey>\"],\"kinds\":[1]}"]
|
|
```
|
|
|
|
#### `webhook`
|
|
|
|
`filter` is required for schema compatibility and can be a simple placeholder like `{}`; webhook firing happens via HTTP.
|
|
|
|
```json
|
|
["trigger", "webhook"],
|
|
["filter", "{}"]
|
|
```
|
|
|
|
#### `cron`
|
|
|
|
`filter` is a standard 5-field cron expression: `minute hour day-of-month month day-of-week`.
|
|
|
|
```json
|
|
["trigger", "cron"],
|
|
["filter", "0 * * * *"]
|
|
```
|
|
|
|
#### `chain`
|
|
|
|
`filter` is the source skill `d` tag to chain from.
|
|
|
|
```json
|
|
["trigger", "chain"],
|
|
["filter", "source-skill-d-tag"]
|
|
```
|
|
|
|
### Trigger Lifecycle
|
|
|
|
```mermaid
|
|
flowchart TD
|
|
subgraph Creation
|
|
ADMIN_CMD[Admin: 'Warn me when @jack posts'] --> LLM_REASON[LLM resolves pubkey + builds skill]
|
|
LLM_REASON --> SKILL_CREATE[skill_create with trigger tags]
|
|
SKILL_CREATE --> PUBLISHED[Skill published to Nostr]
|
|
end
|
|
|
|
subgraph Activation
|
|
STARTUP[Didactyl starts up] --> LOAD_SKILLS[Load adopted skills from kind 10123]
|
|
LOAD_SKILLS --> FIND_TRIGGERS[Find skills with trigger tags]
|
|
FIND_TRIGGERS --> REGISTER[Register trigger by type]
|
|
REGISTER --> NOSTR_SUB[nostr-subscription: create Nostr subscriptions]
|
|
REGISTER --> CRON_REG[cron: keep expression for poll loop]
|
|
REGISTER --> WEBHOOK_REG[webhook: route via /api/trigger/:d_tag]
|
|
REGISTER --> CHAIN_REG[chain: wait for source skill completion]
|
|
end
|
|
|
|
subgraph Execution
|
|
EVENT_IN[Matching Nostr event] --> LOOKUP[Find associated skill]
|
|
WEBHOOK_IN[POST /api/trigger/:d_tag] --> LOOKUP
|
|
CRON_TICK[cron poll match] --> LOOKUP
|
|
LOOKUP --> RESOLVE[Resolve LLM + assemble context + run with tools]
|
|
RESOLVE --> CHAIN_CHECK[Check chain triggers]
|
|
CHAIN_CHECK --> CHAIN_FIRE[Fire matching chain triggers]
|
|
end
|
|
|
|
PUBLISHED --> LOAD_SKILLS
|
|
```
|
|
|
|
---
|
|
|
|
## Execution Flow
|
|
|
|
```mermaid
|
|
sequenceDiagram
|
|
participant Input as Message/Trigger
|
|
participant Dispatch as Dispatcher
|
|
participant Skill as Skill Resolver
|
|
participant LLM_Res as LLM Resolver
|
|
participant Ctx as Context Assembler
|
|
participant LLM as LLM API
|
|
|
|
Input->>Dispatch: message or trigger event
|
|
Dispatch->>Skill: which skill handles this?
|
|
|
|
alt No specific skill
|
|
Skill-->>Dispatch: use default soul context
|
|
Dispatch->>LLM_Res: resolve "default" LLM
|
|
else Skill with context_mode=inject
|
|
Skill-->>Dispatch: soul + skill instructions
|
|
Dispatch->>LLM_Res: resolve skill.llm or "default"
|
|
else Skill with context_mode=full
|
|
Skill-->>Dispatch: skill template only
|
|
Dispatch->>LLM_Res: resolve skill.llm
|
|
end
|
|
|
|
LLM_Res->>LLM_Res: try models in fallback order
|
|
LLM_Res-->>Dispatch: resolved model
|
|
|
|
Dispatch->>Ctx: assemble context per mode
|
|
Ctx->>LLM: request with resolved model + context
|
|
LLM-->>Input: response
|
|
```
|
|
|
|
---
|
|
|
|
## Limits and Safety
|
|
|
|
| Limit | Default | Description |
|
|
|---|---|---|
|
|
| Max concurrent triggers | 16 | Prevents resource exhaustion |
|
|
| Trigger cooldown | 60s per skill | Prevents rapid-fire execution |
|
|
| LLM action rate limit | 10/min | Prevents runaway LLM costs |
|
|
|
|
---
|
|
|
|
## Storage on Nostr
|
|
|
|
| Data | Storage |
|
|
|---|---|
|
|
| Skills | Kind 31123/31124 events |
|
|
| Adopted skills | Kind 10123 event |
|
|
| Trigger definitions | Tags on skill events |
|
|
|
|
---
|
|
|
|
## Future Extensions
|
|
|
|
| Extension | Description |
|
|
|---|---|
|
|
| Skill composition | Pipeline multiple skills |
|
|
| Agent-to-agent sharing | Discover and adopt skills across agents |
|
|
| Trigger marketplace | Popular triggers rise via adoption count |
|
|
|
|
---
|
|
|
|
## Related Documentation
|
|
|
|
- Tool architecture and complete tool catalog: [TOOLS.md](TOOLS.md)
|
|
- Project overview and runtime behavior: [README.md](../README.md)
|