318 lines
8.7 KiB
Markdown
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)
|