191 lines
7.9 KiB
Markdown
191 lines
7.9 KiB
Markdown
# Didactyl — LLM Context
|
|
|
|
See also: [SKILLS.md](SKILLS.md) · [TOOLS.md](TOOLS.md)
|
|
|
|
## What Is Context?
|
|
|
|
Every time Didactyl talks to an LLM, it sends a **context** — the complete package of information the model needs to reason and respond. Understanding context is fundamental to understanding how Didactyl works.
|
|
|
|
Context is not just "the prompt." It is everything the LLM receives in a single request:
|
|
|
|
1. **Messages** — the conversation: system prompts, user messages, assistant responses, tool results
|
|
2. **Tool schemas** — the JSON descriptions of every tool the LLM can call
|
|
3. **Model parameters** — temperature, max tokens, etc.
|
|
|
|
The LLM sees all of this together and produces its response based on the totality of what it was given.
|
|
|
|
---
|
|
|
|
## The OpenAI Chat Completion Format
|
|
|
|
Didactyl uses the OpenAI-compatible chat completion API format, which is the industry standard. Every LLM request looks like this:
|
|
|
|
```json
|
|
{
|
|
"model": "claude-opus-4.6",
|
|
"messages": [
|
|
{"role": "system", "content": "You are Didactyl, a sovereign AI agent..."},
|
|
{"role": "system", "content": "Admin profile: ..."},
|
|
{"role": "user", "content": "Post a note about Bitcoin"},
|
|
{"role": "assistant", "content": null, "tool_calls": [...]},
|
|
{"role": "tool", "content": "{\"success\":true,...}", "tool_call_id": "..."},
|
|
{"role": "assistant", "content": "Done! I posted your note."}
|
|
],
|
|
"tools": [
|
|
{
|
|
"type": "function",
|
|
"function": {
|
|
"name": "nostr_post",
|
|
"description": "Publish a Nostr event",
|
|
"parameters": { ... }
|
|
}
|
|
}
|
|
],
|
|
"temperature": 0.7,
|
|
"max_tokens": 512
|
|
}
|
|
```
|
|
|
|
### Messages
|
|
|
|
Messages are an ordered array. Each message has a `role`:
|
|
|
|
| Role | Purpose |
|
|
|------|---------|
|
|
| `system` | Instructions, identity, context data — the LLM reads these but the user didn't write them |
|
|
| `user` | What the human (or trigger event) said |
|
|
| `assistant` | What the LLM previously said or did (including tool calls) |
|
|
| `tool` | Results from tool executions, fed back to the LLM |
|
|
|
|
The LLM processes all messages in order and generates the next response.
|
|
|
|
### Tool Schemas
|
|
|
|
The `tools` array describes every tool the LLM can call. Each tool has a name, description, and JSON Schema for its parameters. The LLM uses these descriptions to decide when and how to call tools.
|
|
|
|
Tools are not part of the message history — they are a separate capability declaration sent alongside the messages.
|
|
|
|
### Model Parameters
|
|
|
|
Temperature, max tokens, and other settings control how the LLM generates its response. These can be set globally or overridden per-skill.
|
|
|
|
---
|
|
|
|
## How Didactyl Builds Context
|
|
|
|
Didactyl assembles context dynamically for each request. The context varies depending on what triggered the request (DM, trigger event) and which skill is active.
|
|
|
|
### Context Assembly Flow
|
|
|
|
```mermaid
|
|
flowchart TD
|
|
INPUT[Input: DM message or trigger event] --> MODE{Skill context_mode?}
|
|
|
|
MODE -->|inject| INJECT[Start with agent soul/personality]
|
|
INJECT --> ADD_ADMIN[Add admin identity + profile]
|
|
ADD_ADMIN --> ADD_SKILLS[Add adopted skill instructions]
|
|
ADD_SKILLS --> ADD_HISTORY[Add conversation history]
|
|
ADD_HISTORY --> ADD_SKILL_INST[Append this skill instructions]
|
|
ADD_SKILL_INST --> ADD_USER[Add user message or trigger event]
|
|
|
|
MODE -->|full| FULL[Start with skill template only]
|
|
FULL --> RESOLVE[Resolve template variables via tool calls]
|
|
RESOLVE --> ADD_USER2[Add user message or trigger event]
|
|
|
|
MODE -->|override| OVERRIDE[Replace soul with skill prompt]
|
|
OVERRIDE --> ADD_ADMIN2[Add admin identity + profile]
|
|
ADD_ADMIN2 --> ADD_SKILLS2[Add adopted skill instructions]
|
|
ADD_SKILLS2 --> ADD_HISTORY2[Add conversation history]
|
|
ADD_HISTORY2 --> ADD_USER3[Add user message or trigger event]
|
|
|
|
ADD_USER --> TOOLS[Attach tool schemas]
|
|
ADD_USER2 --> TOOLS
|
|
ADD_USER3 --> TOOLS
|
|
|
|
TOOLS --> LLM[Send to LLM]
|
|
```
|
|
|
|
### Context Parts
|
|
|
|
These are the building blocks that get assembled into the messages array:
|
|
|
|
| Part | Source | Description |
|
|
|------|--------|-------------|
|
|
| **Soul/Personality** | Kind 31120 event | The agent's identity, rules, and behavior instructions |
|
|
| **Admin Identity** | Config / kind 3 | Who the admin is, their pubkey |
|
|
| **Admin Profile** | Kind 0 event | Admin's display name, about, picture |
|
|
| **Admin Relay List** | Kind 10002 event | Admin's preferred relays |
|
|
| **Admin Notes** | Kind 1 events | Admin's recent public posts — gives the agent awareness of what the admin is doing |
|
|
| **Adopted Skills** | Kind 31123/31124 events | Instructions from other adopted skills |
|
|
| **Conversation History** | Kind 4/14 events | Recent DM exchanges between admin and agent |
|
|
| **Skill Instructions** | Active skill content | The specific skill being executed |
|
|
| **User Message** | DM or trigger event | The input that triggered this execution |
|
|
| **Tool Schemas** | Tool registry | JSON descriptions of available tools (sent separately from messages) |
|
|
|
|
### Template Variables Are Tool Calls
|
|
|
|
The soul's template system uses `tool:` directives to populate context sections. Each section can specify a tool to call, and the tool's output becomes that section's content:
|
|
|
|
```yaml
|
|
- section: admin_profile
|
|
role: system
|
|
tool: nostr_admin_profile
|
|
skip_if_empty: true
|
|
```
|
|
|
|
This calls `nostr_admin_profile`, gets the result, and inserts it as a system message. Template variables like `{{admin_profile}}` in skill templates work the same way — they resolve to tool calls. There is one unified resolution mechanism: `tools_execute()`.
|
|
|
|
This means new context sources are added by adding tools, not by modifying the template engine.
|
|
|
|
---
|
|
|
|
## Context Modes
|
|
|
|
Skills control how much of the default context the LLM sees using the `context_mode` field:
|
|
|
|
| Mode | What the LLM sees | Use case |
|
|
|------|-------------------|----------|
|
|
| `inject` | Everything: soul + admin context + history + skill instructions | Default. Agent stays itself, gains new knowledge. |
|
|
| `full` | Only what the skill template provides | Focused tasks: spellcheck, translation. Minimal context = cheaper, faster. |
|
|
| `override` | Skill replaces soul, but admin context + history still included | Different personality, same capabilities. |
|
|
|
|
Most skills use `inject` (the default). `full` is for specialized tasks where you want to minimize token cost. `override` is rare.
|
|
|
|
---
|
|
|
|
## Context for Triggered Skills
|
|
|
|
When a trigger fires (Nostr subscription match, webhook POST, cron timer, chain completion), the context is assembled the same way as for a DM — the only difference is the user message:
|
|
|
|
| Input Source | User Message Content |
|
|
|---|---|
|
|
| Admin DM | The DM text |
|
|
| Nostr subscription trigger | The matching Nostr event as JSON |
|
|
| Webhook trigger | The HTTP POST payload as JSON |
|
|
| Cron trigger | A synthetic event with timestamp and cron expression |
|
|
| Chain trigger | A synthetic event with the source skill's d_tag and output |
|
|
|
|
The LLM receives the skill instructions as system context and the triggering event as the user message, then reasons about what to do and which tools to use.
|
|
|
|
---
|
|
|
|
## Token Budget
|
|
|
|
Context has a cost — every token sent to the LLM costs money and takes time. Didactyl manages this through:
|
|
|
|
- **Context mode selection** — `full` mode skills send minimal context
|
|
- **Skip-if-empty sections** — template sections with no content are omitted
|
|
- **History limits** — conversation history is capped (default: 12 turns)
|
|
- **Skill content limits** — adopted skill instructions are truncated to prevent context overflow
|
|
- **Per-skill model selection** — cheap models for simple tasks, expensive models for complex ones
|
|
|
|
The `/api/context/current` and `/api/context/parts` API endpoints let you inspect exactly what context would be sent right now, including character counts and token estimates.
|
|
|
|
---
|
|
|
|
## Related Documentation
|
|
|
|
- Skills — how to define LLM execution units: [SKILLS.md](SKILLS.md)
|
|
- Tools — what the LLM can do: [TOOLS.md](TOOLS.md)
|
|
- API — inspect context at runtime: [API.md](API.md)
|