Agent patterns
Choose a proven Agent composition by the shape of the work, then implement it with the seven primitives and governed Resources.
Agent names describe a product; patterns describe its control flow. A legal assistant and a support assistant may share the same dialogue pattern. A research Agent and a coding Agent may both use planner–worker delegation. Choose the smallest structural pattern that matches the work, then add domain prompts, Resources, Memory, Channels, Credentials, and Policy.
Pattern catalog
| Family | Pattern | Use it when | Core composition |
|---|---|---|---|
| Dialogue | Conversational assistant | One bounded answer depends on session history | ledger → turn → commit |
| Dialogue | Content or generation Agent | The output is primarily a model-produced artifact | turn → commit |
| Dialogue | Router or triage Agent | The Agent classifies and hands work to a bounded destination | turn → commit or turn → spawn |
| Dialogue | Customer-support Agent | Conversation may read systems, act, or escalate | ledger → turn → await? → commit |
| Knowledge | Retrieval or knowledge Agent | Answers must be grounded in governed records | Resource search → turn → commit |
| Knowledge | Research Agent | Several independent questions or sources must be investigated | turn → spawn* → turn → commit |
| Knowledge | Document-intelligence Agent | Many documents need extraction, comparison, or synthesis | map → reduce → turn → commit |
| Data | Data analyst Agent | A question requires governed queries and interpretation | turn → Resource → turn → commit |
| Data | Batch enrichment or transformation Agent | The same bounded operation applies across large volume | map → reduce? → commit |
| Data | Evaluator or judge Agent | Outputs need a rubric, score, gate, or comparison | turn(gate) → commit |
| Action | ReAct Agent | The model must observe Tool results and continue until it can finish | react() from @constal/std |
| Action | Tool-using operator | The model chooses among bounded external operations | turn(Tools) → commit |
| Action | Workflow automation Agent | Progress crosses steps, retries, or invocations | durable state → commit |
| Action | Human-approval Agent | Authority must pause for a person or external decision | turn → await → commit |
| Action | Browser or computer-use Agent | A governed Resource exposes interactive operations | repeated turn → Tool, then commit |
| Engineering | Coding Agent | Repository inspection, edits, tests, and review form a loop | turn → Resources → turn → commit |
| Operations | Monitoring or incident Agent | Events trigger diagnosis, mitigation, and escalation | ledger → turn → await? → commit |
| Coordination | Planner–executor Agent | Planning and execution need separate durable work | turn → spawn* → turn → commit |
| Coordination | Supervisor or multi-Agent system | Specialized workers can proceed independently | spawn* → derived join → turn → commit |
| Autonomy | Long-horizon Agent | Work must resume across time with explicit progress | durable state composed from all seven |
These are structural archetypes, not an industry list. Sales, legal, healthcare, finance, education, and security Agents are domain applications of one or more rows. Combining patterns is normal; inventing a new runtime primitive for each label is not.
Start with the bounded core
import { agent } from "@constal/sdk";
export default agent({
id: "bounded-assistant", version: "1.0.0", model: "model",
async onMessage(input, ctx) {
const history = await ctx.ledger.view("history");
const answer = await ctx.turn({
system: "Complete one bounded objective and state uncertainty.",
objective: input,
context: { history },
});
await ctx.commit({ kind: "answer", text: answer.message.content });
return answer.message.content;
},
});Add await only when time or authority crosses the invocation. Add spawn when child work needs an independent durable identity. Add map and reduce when the cardinality is data volume rather than a bounded worker set. External search, databases, browsers, repositories, ticketing systems, and Memory stay Resources; they do not become primitives.
Build a ReAct Agent with the library
ReAct is a standard-library composition, not another Agent primitive. react() from @constal/std supplies the durable state machine, transcript handling, Tool loop, steering intake, and explicit final Tool. The Agent definition only adds its identity, Model binding, instructions, and offered Tools.
import { agent } from "@constal/sdk";
import { react, type ReactState } from "@constal/std";
const behavior = react({
system: "Research the request with the offered Tools. Call final only when the answer is supported.",
tools: ["search", "read_page"],
window: 40,
});
export default agent<ReactState>({
id: "research-assistant",
version: "1.0.0",
model: "model",
...behavior,
});search and read_page must be registered Tools exposed by the Agent deployment; react() does not create those capabilities or bypass their Resource and Policy boundaries.
Use window to bound the messages sent to each Model turn. Windowing does not rewrite the durable transcript. For long-running Agents, provide fold to compact older working context into a serializable summary while retaining the recent messages that still matter:
const behavior = react({
system: "Investigate the objective and call final with a supported answer.",
tools: ["search", "read_page"],
window: 40,
async fold(recent, summary, ctx) {
if (recent.length < 32) return { context: recent, summary };
const compacted = await ctx.turn({
system: "Preserve decisions, evidence, open questions, and source references. Remove repetition.",
objective: { previousSummary: summary, messages: recent },
});
return {
context: recent.slice(-12),
summary: compacted.message.content,
};
},
});Compaction changes the context prepared for later turns, not the Run's recorded history. Keep the fold deterministic in structure, treat its summary as model-generated context rather than business truth, and use commit for facts that must become authoritative.