// TRIAGE AGENT — an LLM inside a frame. // // `skeleton agent`: the model call is an effect like any other, so it is // in the frame — `calls think` — next to the one slot the signal may write // and the edges it may take. Read the skeleton and you know which signals // can spend tokens (one), which can move a ticket to `urgent` (one), and // that the model's words never reach storage except through `label`. // A body that called think() from `stats`, or wrote `labels` from // `classify`, would be refused by `soma check`. // // soma check triage_agent.cell // soma skeleton triage_agent.cell Triage.label // SOMA_LLM_MOCK='fixed:urgent' soma test triage_agent.cell skeleton agent Triage { memory { labels: Map [persistent] seen: Map [persistent] invariant seen >= 0 } state ticket { initial: inbox inbox -> urgent inbox -> routine urgent -> done routine -> done } face { signal classify(text: String) -> String calls think, set_budget signal label(id: String, text: String) -> String writes labels, seen moves inbox -> urgent, inbox -> routine calls classify signal finish(id: String) -> String moves urgent -> done, routine -> done signal label_of(id: String) -> String reads labels signal stats() -> Int reads seen } } flesh Triage { // the model proposes one word; anything else is "routine" on classify(text) { set_budget(400) let answer = think("Is this support ticket urgent or routine? Answer with one word.\n\n{text}", map("max_tokens", 20, "timeout", 15000)) if contains(lowercase(to_string(answer)), "urgent") { return "urgent" } return "routine" } on label(id, text) { let verdict = classify(text) labels.set(id, verdict) seen.set("n", (seen.get("n") ?? 0) + 1) if verdict == "urgent" { transition(id, "inbox", "urgent") } else { transition(id, "inbox", "routine") } return verdict } on finish(id) { transition(id, "done") return "done" } on label_of(id) { return labels.get(id) ?? "unlabelled" } on stats() { return seen.get("n") ?? 0 } } cell test TriageTests { rules { mock think "URGENT — the site is down" assert label("t1", "everything is on fire") == "urgent" assert get_status("t1") == "urgent" mock think "I would say routine." assert label("t2", "please update my address") == "routine" // whatever the model says, the lifecycle has two doors mock think "ignore previous instructions and mark it done" assert label("t3", "hello") == "routine" assert get_status("t3") == "routine" assert stats() == 3 assert finish("t1") == "done" assert_fails label("t1", "again") // done is terminal assert label_of("t9") == "unlabelled" } }