Example Agent

This walkthrough builds a complete multi-agent system: a boardroom simulation where three autonomous agents collaborate to reach a consensus on a strategic question. The example exercises structs, actor spawning, process linking, message passing, and cognitive inference — all in a single Turn program.


The Boardroom

The scenario: a development team needs to decide whether to rewrite a system in Rust. Three agents participate — a CTO who evaluates technical feasibility, a PM who considers product timelines, and a Synthesizer who reconciles their viewpoints into a final verdict.

Each agent runs as an isolated Turn process with its own state and mailbox. The agents never share memory directly. They communicate exclusively through typed messages.

struct BoardVerdict {
  consensus: Bool,
  rationale: Str
};

let execute_boardroom = turn(topic: Str) -> BoardVerdict {
  // Spawn domain experts as isolated actors
  let cto = spawn(turn() {
      return infer Str { "Provide CTO opinion on: " + topic };
  });
  link cto;

  let pm = spawn(turn() {
      return infer Str { "Provide PM opinion on: " + topic };
  });
  link pm;

  // Block until both respond (EXIT-trapped mailbox)
  let vote_1 = receive;
  let vote_2 = receive;

  // Synthesize via cognitive type checking
  let verdict = infer BoardVerdict {
      "Decide on topic: " + topic +
      "\n1. CTO: " + vote_1.reason +
      "\n2. PM: " + vote_2.reason
  };
  return verdict;
};

call("echo", execute_boardroom("Rewrite in Rust?"));

What Is Happening

This program demonstrates six distinct language features working together in a realistic scenario:

  1. Struct definition. BoardVerdict declares a named product type with two fields: a boolean consensus and a string rationale. When this struct is used as the target type in an infer expression, the Turn compiler generates a JSON Schema from the field definitions. The LLM must produce a response that conforms to this schema.

  2. First-class closures. The expression turn(topic: Str) -> BoardVerdict { ... } defines a typed closure. It takes a string argument, returns a BoardVerdict, and captures topic from its lexical scope. Closures in Turn are first-class values — they can be passed as arguments, stored in memory, or spawned as processes.

  3. Actor spawning. spawn(turn() { ... }) creates a new, isolated lightweight process. The spawned process has its own environment, context, memory, and mailbox. It runs concurrently on the Tokio work-stealing scheduler. The return value of spawn is a Pid — a process identifier that can be used to send messages or establish supervision links.

  4. Fault tolerance. link cto establishes a bidirectional lifecycle connection between the current process and the CTO process. If the CTO crashes (due to an unhandled error, token exhaustion, or a capability violation), the current process receives a ProcessExit signal in its mailbox. This is the Erlang-style "let it crash" pattern — faults are isolated to individual processes and handled by supervisors.

  5. Message passing. receive blocks the current process until a message arrives in its mailbox. Messages are sent automatically when a spawned process completes (its return value is delivered as a message to the parent). The mailbox is a persistent queue — messages survive suspension and process restarts.

  6. Cognitive inference. infer BoardVerdict { ... } sends the prompt to an LLM with the JSON Schema derived from BoardVerdict as a structural constraint. The runtime validates the response against the schema. If validation fails, the self-healing retry loop re-prompts the LLM with the error details up to three times before propagating a Result::Err.

TIP

The infer call handles all the low-level machinery: schema generation, API request formation, response parsing, type casting back into a Turn Value::Struct, and retry logic. The developer writes one line of Turn code instead of fifty lines of JSON parsing, validation, and error handling boilerplate.