Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Your First Agent

Let’s build a task prioritizer that classifies items and reports them by urgency.

1. Scaffold

keel init task-prioritizer
cd task-prioritizer

This creates main.keel.

2. Define types

type Priority = low | medium | high | critical

type Task {
  title: str
  description: str
}

Types are either enums (a set of variants, optionally with data) or structs (named fields). The type checker enforces exhaustive matching on enums.

3. A classification task

use std/ai

task prioritize(t: Task) -> Priority {
  ai.classify(t.description,
    as: Priority,
    considering: {
      "blocks other people":       Priority.critical,
      "has a deadline this week":  Priority.high,
      "nice to have":              Priority.low
    }
  ) ?? Priority.medium
}

ai.classify sends t.description to the LLM with the hints and parses the response into the enum. ?? Priority.medium supplies a default when the LLM is unavailable or returns nothing parseable.

4. Build the agent

use std/io
use std/schedule

agent Prioritizer {
  @tools [io]
  @role "You help prioritize a task list"

  state {
    processed: int = 0
  }

  task run_batch(tasks: list[Task]) {
    for t in tasks {
      priority = prioritize(t)
      self.processed = self.processed + 1

      when priority {
        critical => io.notify("CRITICAL: {t.title}")
        high     => io.notify("HIGH: {t.title}")
        medium   => io.notify("MEDIUM: {t.title}")
        low      => io.notify("LOW: {t.title}")
      }
    }
    io.notify("Processed {self.processed} tasks total")
  }

  @on_start {
    schedule.every(1.hour, () => {
      tasks = [
        {title: "Fix login bug", description: "Users can't log in, blocks the team"},
        {title: "Update README",  description: "Nice to have, not urgent"},
        {title: "Deploy v2.0",    description: "Release deadline is Friday"}
      ]
      self.run_batch(tasks)
    })
  }
}

run(Prioritizer)

5. Run it

KEEL_OLLAMA_MODEL=gemma4 keel run main.keel

6. Type-check

keel check main.keel

Forget a when variant and the compiler stops you:

  × Type error
   ╭─[main.keel:18:7]
 18 │       when priority {
   ·       ─────┬─────
   ·            ╰── Non-exhaustive match on Priority: missing high, medium, low
 19 │         critical => io.notify("CRITICAL")
   ╰────

Key takeaways

ConceptWhat it does
type Priority = low | medium | high | criticalEnum — the type checker enforces exhaustive matching
ai.classify(x, as: T) ?? VLLM-powered classification into an enum; ?? supplies a default when the result is absent
considering: [...]Hints to the LLM per variant
when value { ... }Exhaustive pattern matching, checked at compile time
state { field: T = default }Mutable agent state, accessed via self.field
@on_start { ... }Runs once when the agent starts
schedule.every(duration, () => { ... })Recurring execution, from the stdlib
io.notify(...)Terminal notification from the stdlib
run(MyAgent)Starts the agent

Three imports — use std/ai, use std/io, use std/schedule — declare everything this program touches. A file’s imports are its capability surface. See The Standard Library.

Next: Language Guide →