Example: Email Assistant
Alpha (v0.1). Breaking changes expected.
A complete email agent that triages, auto-replies, and escalates.
type Urgency = low | medium | high | critical
task triage(email: {body: str, from: str, subject: str}) -> Urgency {
Ai.classify(email.body,
as: Urgency,
considering: {
"from a known VIP or executive": Urgency.critical,
"mentions a deadline within 24h": Urgency.high,
"asks a direct question": Urgency.medium,
"newsletter or automated message": Urgency.low
},
using: "fast"
) ?? Urgency.medium
}
task brief(email: {body: str}) -> str {
Ai.summarize(email.body,
in: 1, unit: sentence,
using: "fast"
) ?? "(no summary)"
}
task compose(email: {body: str, from: str}, guidance: str? = none) -> str {
if guidance != none {
Ai.draft("response to {email.body}", tone: "professional", guidance: guidance)
} else {
Ai.draft("response to {email.body}", tone: "friendly", max_length: 150)
} ?? "(draft failed)"
}
agent EmailAssistant {
@role "You are a professional email assistant"
@tools [Email]
@memory persistent
state {
handled_count: int = 0
}
task handle(email: {body: str, from: str, subject: str}) {
urgency = triage(email)
summary = brief(email)
when urgency {
low => {
Io.notify("Archived: {email.subject} [{urgency}]")
Email.archive(email)
}
medium => {
reply = compose(email)
if Io.confirm("Auto-reply to '{email.subject}':\n\n{reply}") {
Email.send(reply, to: email.from)
}
}
high, critical => {
Io.notify("{urgency} email from {email.from}")
Io.show({
from: email.from,
subject: email.subject,
summary: summary,
urgency: urgency
})
guidance = Io.ask("How should I respond?")
reply = compose(email, guidance)
if Io.confirm(reply) {
Email.send(reply, to: email.from)
}
}
}
Memory.remember({
contact: email.from,
subject: email.subject,
urgency: urgency,
handled_at: now
})
self.handled_count = self.handled_count + 1
}
@on_start {
Schedule.every(5.minutes, () => {
emails = Email.fetch(unread: true)
Io.notify("{emails.count} new emails")
for email in emails {
self.handle(email)
}
})
}
}
run(EmailAssistant)
Setup
export IMAP_HOST=imap.gmail.com
export EMAIL_USER=you@gmail.com
export EMAIL_PASS=your-app-password
export KEEL_OLLAMA_MODEL=gemma4
keel run email_agent.keel
How it works
- Every 5 minutes,
Email.fetch(unread: true)pulls new messages. triageclassifies each by urgency using a fast model.low→ auto-archive.medium→ draft a reply, confirm before sending.high/critical→ show a summary, ask for guidance, draft with it, confirm.- Each interaction is remembered for future context.
Zero imports. Ai, Io, Email, Schedule, and Memory are all auto-imported via the prelude.