Getting started
An Effect Agent has five pieces: input and output Schemas, instructions, an Effect AI Toolkit, a finite policy, and an explicit Model Binding.
1. Model the boundary
Use Effect Schema for data the application accepts and data the Agent may return.
import { Schema } from "effect";
class TriageInput extends Schema.Class<TriageInput>("TriageInput")({
repo: Schema.NonEmptyString,
issueNumber: Schema.Int,
}) {}
class TriageResult extends Schema.Class<TriageResult>("TriageResult")({
severity: Schema.Literals(["low", "medium", "high", "critical"]),
explanation: Schema.NonEmptyString,
}) {}2
3
4
5
6
7
8
9
10
11
The input is decoded before instructions run. The final output is decoded before the Run can succeed.
2. Define a native Effect AI Tool
Tool handlers can require ordinary application services. Those requirements remain visible in the eventual Run type.
import { Context, Effect, Schema } from "effect";
import { Tool, Toolkit } from "effect/unstable/ai";
class IssueUnavailable extends Schema.TaggedError<IssueUnavailable>()("IssueUnavailable", {
message: Schema.String,
}) {}
class IssueRepo extends Context.Service<
IssueRepo,
{
readonly inspect: (
repo: string,
number: number,
) => Effect.Effect<{ title: string; sensitive: boolean }, IssueUnavailable>;
}
>()("IssueRepo") {}
const InspectIssue = Tool.make("inspect_issue", {
description: "Inspect one issue",
parameters: Schema.Struct({
repo: Schema.String,
number: Schema.Int,
}),
success: Schema.Struct({
title: Schema.String,
sensitive: Schema.Boolean,
}),
failure: IssueUnavailable,
failureMode: "error",
dependencies: [IssueRepo],
});
const TriageToolkit = Toolkit.make(InspectIssue);
const TriageToolkitLive = TriageToolkit.toLayer({
inspect_issue: ({ repo, number }) => Effect.flatMap(IssueRepo, (_) => _.inspect(repo, number)),
});2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
The Definition stays pure. Live clients enter through TriageToolkitLive and the IssueRepo Layer supplied by your application.
3. Define a finite Agent
import { Agent, AgentPolicy } from "@effect-agent/core";
const TriageDefinition = Agent.define("triage", {
input: TriageInput,
output: TriageResult,
instructions: ({ repo, issueNumber }) =>
`Triage ${repo}#${issueNumber}. Escalate sensitive issues.`,
toolkit: TriageToolkit,
policy: AgentPolicy.make({
maxTurns: 6,
maxToolCalls: 10,
maxDuration: "2 minutes",
toolConcurrency: 2,
}),
});2
3
4
5
6
7
8
9
10
11
12
13
14
15
The Agent ID is stable identity, not a display name. Every default policy is finite. Invalid or unbounded policy values fail at construction.
4. Bind one Model
Definitions are model-agnostic. A Model becomes part of the runtime contract only at the explicit binding seam.
import { OpenAiLanguageModel } from "@effect/ai-openai";
const Triage = Agent.withModel(TriageDefinition, OpenAiLanguageModel.model("gpt-4.1-mini"));2
3
The framework does not choose a Model from ambient configuration. The Effect AI Model's Layer requirements join the Run's R.
5. Interpret the Binding
import { Effect } from "effect";
import { IdGenerator } from "@effect-agent/core";
import { AgentRuntime } from "@effect-agent/engine";
const program = AgentRuntime.run(Triage, {
repo: "Effect-TS/effect",
issueNumber: 4123,
}).pipe(
Effect.provide(TriageToolkitLive),
Effect.provide(IssueRepoLive),
Effect.provide(IdGenerator.layer),
Effect.provide(OpenAiClientLive),
Effect.scoped,
);2
3
4
5
6
7
8
9
10
11
12
13
14
IssueRepoLive and OpenAiClientLive are application Layers whose exact construction is intentionally outside the Definition. IdGenerator.layer is the framework's default identity authority backed by Web Crypto's randomUUID; tests replace it with a deterministic Layer.
Deterministic first
The repository's ordinary tests bind the same Definitions to ScriptedModel, not a live provider. The testing guide shows the offline path.
What the type tells you
- A
- AgentResult<TriageResult>
- E
- IssueUnavailable | AiError | decode/policy failures
- R
- IssueRepo | Tool handlers | IDs | Model client | Scope
If you omit a Tool handler Layer or provider client, the Effect cannot run. If a Tool fails with IssueUnavailable, that failure remains in the error channel unless the Tool explicitly opts into Effect AI's failureMode: "return".
Next
- Agent Definitions explains inference and immutable identity.
- Tools & Layers explains scheduling and failure behavior.
- Run & stream covers all three execution views.