Files
didactyl/docs/SKILLS.md
T

318 lines
8.7 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 — technique, judgment, decision-making. A **tool** is the chisel. The skill never directly uses the chisel without the craftsperson (the LLM) in the loop.
Skills are portable, shareable, and discoverable as Nostr events.
---
## 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
Skill `content` is JSON and should focus on **instructions**, not transport/runtime controls.
```json
{
"kind": 31123,
"content": {
"description": "Check spelling and grammar",
"template": "system:\nYou are a spelling and grammar checker.\n\nRules:\n- Fix spelling errors\n- Fix grammar errors\n- Preserve original formatting\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 |
| `template` | string | — | Skill instructions/template text (recommended) |
| `base` | bool | `false` | Optional hint that this skill is intended as base/default behavior |
> Execution parameters (`llm`, `max_tokens`, `temperature`, `seed`, `tools`) are defined on **trigger tags**, not in content.
---
## Composition Model (No Context Modes)
Skills do **not** use `context_mode`.
Context is assembled from kind `10123` adoption list order:
1. Resolve adopted skills in list order.
2. Expand each skill template/tool variables.
3. Append each resolved skill as context messages in that same order.
4. Append live user/trigger input.
The adoption list itself is the context definition.
- One adopted skill = single-skill behavior.
- Multiple adopted skills = layered behavior in explicit order.
- Reordering `10123` changes precedence naturally.
### Ordering Convention
- Earlier adopted skills generally set broader tone/policy.
- Later adopted skills can narrow/specialize behavior.
- If multiple skills strongly conflict, normal prompt-order effects apply.
### Template Variables Are Tool Calls
Template variables are tool calls.
When the engine encounters `{{admin_profile}}`, it runs the corresponding tool and inserts the result into context.
| Variable | Tool Called | Description |
|----------|-----------|-------------|
| `{{agent_identity}}` | `agent_identity` | Agent identity block |
| `{{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` | Triggering event JSON |
Unknown variables should resolve to empty values for portability.
---
## Triggered Skills
A triggered skill has a trigger source attached.
Didactyl trigger types:
- `nostr-subscription`
- `webhook`
- `cron`
- `chain`
- `dm`
### Trigger Tags
| Tag | Required | Description |
|---|---|---|
| `trigger` | Yes | Trigger type: `nostr-subscription`, `webhook`, `cron`, `chain`, `dm` |
| `filter` | Yes | Type-specific filter |
| `enabled` | No | Whether active (default: `true`) |
| `llm` | No | Model spec fallback chain (e.g., `openai/gpt-4o-mini, cheap`) |
| `max_tokens` | No | Max output tokens for this trigger execution |
| `temperature` | No | Sampling temperature for this trigger execution |
| `seed` | No | Optional deterministic seed where supported |
| `tools` | No | `true` for all tools, `false` for none, or CSV list of allowed tool names |
### Execution Parameter Resolution
When a trigger fires:
1. Start with agent defaults.
2. Apply execution tags from that trigger (`llm`, `max_tokens`, `temperature`, `seed`, `tools`).
3. Execute skill with those effective runtime settings.
4. Restore defaults after the run.
### Adopted vs Triggered Behavior
- **Adopted skill (`10123`)**: contributes instructions/template to context.
- **Triggered skill**: contributes instructions **and** may define execution parameters via trigger tags.
---
## Trigger Types
### `nostr-subscription`
`filter` is a JSON-encoded Nostr subscription filter.
```json
["trigger", "nostr-subscription"],
["filter", "{\"#p\":[\"<admin_pubkey>\"],\"kinds\":[1]}"],
["llm", "openai/gpt-4o-mini, cheap"],
["temperature", "0"],
["tools", "nostr_query,nostr_dm"],
["enabled", "true"]
```
### `webhook`
`filter` is required and can be `{}`; webhook firing happens via HTTP.
```json
["trigger", "webhook"],
["filter", "{}"],
["llm", "default"],
["tools", "true"],
["enabled", "true"]
```
### `cron`
`filter` is a standard 5-field cron expression: `minute hour day-of-month month day-of-week`.
```json
["trigger", "cron"],
["filter", "*/5 * * * *"],
["llm", "openai/gpt-4o-mini"],
["max_tokens", "300"],
["enabled", "true"]
```
### `chain`
`filter` is the source skill `d` tag to chain from.
```json
["trigger", "chain"],
["filter", "source-skill-d-tag"],
["llm", "default"],
["enabled", "true"]
```
### `dm`
`filter` is JSON with sender scope:
- `{"from":"admin"}`
- `{"from":"wot"}`
- `{"from":"any"}`
```json
["trigger", "dm"],
["filter", "{\"from\":\"admin\"}"],
["llm", "default"],
["tools", "true"],
["enabled", "true"]
```
---
## Private Skill Encoding (`31124`)
Private skills use NIP-44 encryption on event `content`.
Rules for kind `31124`:
- Keep `d` tag exposed so the event stays addressable/replaceable.
- Move non-`d` metadata into plaintext payload before encryption.
- Encrypt full payload with NIP-44 and store ciphertext in event `content`.
- On receive: resolve by `d`, decrypt `content`, then read content + private tags.
### Private Skill Event (on relay)
```json
{
"kind": 31124,
"content": "<nip44-ciphertext>",
"tags": [
["d", "mention-monitor"]
]
}
```
### Decrypted Private Payload (application-level JSON)
```json
{
"content": {
"description": "Monitor mentions and DM summaries",
"template": "When {{triggering_event}} includes Bitcoin or Lightning, summarize and DM admin."
},
"private_tags": [
["scope", "private"],
["trigger", "nostr-subscription"],
["filter", "{\"#p\":[\"<admin_pubkey>\"],\"kinds\":[1]}"],
["llm", "openai/gpt-4o-mini, fast"],
["temperature", "0"],
["seed", "42"],
["tools", "nostr_query,nostr_dm"],
["enabled", "true"]
]
}
```
---
## Execution Flow
```mermaid
sequenceDiagram
participant Input as Message/Trigger
participant Dispatch as Dispatcher
participant Adopt as Adoption Resolver (10123)
participant Ctx as Context Assembler
participant Trig as Trigger Runtime Params
participant LLM as LLM API
Input->>Dispatch: message or trigger event
Dispatch->>Adopt: load adopted skills in list order
Adopt-->>Ctx: ordered skill templates
Ctx->>Ctx: resolve template variables via tools
Dispatch->>Trig: resolve trigger execution tags
Trig-->>LLM: model + max_tokens + temperature + seed + tool policy
Ctx->>LLM: composed messages
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 + execution params | Tags on skill events |
---
## Portability Guidelines
To keep skills reusable across agents/clients:
- Prefer generic instructions over implementation-specific assumptions.
- Treat tool names as capabilities, not platform internals.
- Resolve unknown variables safely (empty result, no hard failure).
- Keep app-specific tags optional (`["app","didactyl"]`).
A skill should still be useful even when some variables/tools are unavailable.
---
## Related Documentation
- Tool architecture and complete tool catalog: [TOOLS.md](TOOLS.md)
- Context assembly model: [CONTEXT.md](CONTEXT.md)
- Project overview/runtime behavior: [README.md](../README.md)