---
title: Agents
description: Code that watches a collection and acts on each change, with a model or without.
section: Server
order: 3
---

# Agents

<p class="lead">An agent is a file in <code>agents/</code> that watches a collection and acts on each change to it, the way a person at another screen would. Its memory is rows, and what it does depends only on them.</p>

```ts title="agents/tutor.ts"
// no watch: a helper, not an agent
import { type Act, isAgent, conversation } from "./room.ts";

export const watch = { collection: "study_events" };

export async function act({ change, server, ai, agent }: Act) {
  const said = change.record;

  // never answer yourself
  if (change.action !== "create" || isAgent(said)) return;
  const room = conversation(server, said.page);

  const written = await ai(
    "You are a friendly tutor. Ask ONE short open question.",
    room.map((e) => `${e.author_name}: ${e.text}`).join("\n"),
    { type: "object", properties: { question: { type: "string" } }, required: ["question"] },
  );
  server.collection("study_events").create({
    page: said.page, author: `agent:${agent}`, author_name: "Tutor", kind: "question",
    // no model: still an answer
    text: written?.question ?? "What do you already know about this?",
  });
}
```

- **One call per change,** in order, in the functions sandbox. The file is read again each time, so an edit takes effect at the next change.
- **It acts as the system** and says who it is in what it writes. It must ignore its own writes, or it would keep answering itself.
- **`ai(system, prompt, schema)`** asks the model set up in Settings → AI and returns JSON in the schema's shape, or `null` when no model is set up. Always have an answer for that case.
- **What it writes is announced** like any other write. People see it through [sync](/docs/sync), and other agents watching that collection hear it.

## Several agents, one room

A study room might have three agents. The Tutor asks the pupil it knows least about. The Scribe reads each answer and writes what it learnt to `study_facts`. The Coach watches the facts rather than the conversation, and gives a tip once a pupil has three. No agent calls another; they communicate only through the rows they write.

Agents start with the server. If a collection is added while the server is running, restart it before that collection's agents will hear anything.
