# 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)