Typed inputs, checks and policies
In AEL, the Agent Engineering Language, the model proposes and your code decides. This tutorial builds a document-review node and shows each place where you, the author, keep control: the business prompt, the types, the checks, the hooks, the policies and the connectors.
Typed parameters
The node's input is an ordinary structure, and every field has a type and a maximum size:
struct ReviewRequest {
strict: bool,
max_findings: u16,
pages: u32,
}
enum ReviewError {
TooManyPages(u32),
NoFindingsAllowed,
}
fn check_review(request: ReviewRequest) -> Result<ReviewRequest, ReviewError> {
if request.pages > 200 {
return Result::Err(ReviewError::TooManyPages(request.pages));
}
if request.max_findings == 0 {
return Result::Err(ReviewError::NoFindingsAllowed);
}
return Result::Ok(request);
}
check_review is an input check: a helper that accepts or rejects a typed input before any work starts, with a typed error. An unknown field, a value out of range and text that is too long are errors that name the field, wherever the input comes from: the console, an HTTP route or another agent.
The business prompt and the input builder
# nodes/review.node.ael
node review(request: ReviewRequest) -> Review {
config {
prompt: file("prompts/review.md");
model: binding("primary_model");
max_attempts: 2;
}
return write_review(request);
}
fn write_review(request: ReviewRequest) -> Review {
# Builds the model's input from the typed request, sends it and
# returns the checked review.
}
- The prompt file holds the business policy. It guides the model's decisions; it cannot grant a permission, raise a limit or change who the caller is.
- An input builder, a helper you write, turns the typed request into what the model receives.
- The body of
write_reviewis a placeholder, as A first agent explains.
Raw or typed results
Agents take typed inputs and return typed outputs, with your own input builders and output checks and a choice of raw or typed results:
- Typed. The model's answer is decoded into
Review, checked for structure, passed through your checks in the order you declare, and only then accepted. - Raw. The node takes the model's raw output and parses it with your own code. Raw text is never presented as a checked value: what your parser returns goes through the structural check first.
An answer that was cut off never counts as complete. Typed inputs and outputs lists the seven steps in order.
Your own output checks
Each output check returns one of five results: accept; reject, with a reason; a retryable failure; unavailable; or pending review, with a deadline. A rejected answer is repaired if the node has repairs left; otherwise the node returns a typed error, uses a typed fallback or escalates, as you declare. A check that calls a model or a service declares that effect and draws on the same budget and deadline.
Hooks around the node
Hooks attach authorization, auditing or cleanup before or after any component, function or statement, run in ordered or parallel groups, and fail closed when authorization fails:
@auth(order = 0)
@rate_limit(order = 0)
agent reviewer(request: ReviewRequest) -> Review {
# The agent's workflow runs only after both hooks allow the call.
}@audit
auth and rate_limit run in parallel in group 0; audit runs after the agent, whatever its outcome. If auth denies the call, fails or times out, the agent does not run. Hooks describes groups and contexts.
Retry and repair policies
max_attemptscounts the first attempt:2is the first attempt and at most one retry.- A repair is an extra model call that asks the model to fix an answer that failed its checks. Repairs have their own limit, separate from attempts.
- A failure is classified before any retry: invalid input and denied permissions are never retried, temporary failures of work that is safe to repeat are, and work whose outcome is uncertain is reconciled by your code, never retried blindly.
- You can write your own retry decision, a function that sees the error, the attempt and the remaining budget, and decides within your limits.
Model policies
Model policies cover exactly one call, a bounded loop, routing, ordered fallback, parallel fan-out and an independent verifier model. A verifier gives an opinion, not proof: type checks and permission checks still apply whatever it says. Models and providers describes each policy.
One shared budget
Retries, output repair, timeouts and cancellation draw on one shared budget, so nested retries cannot multiply cost. Token, time, CPU, memory and concurrency budgets apply across every child agent. An answer repaired twice has used three model calls, all counted on that one budget.
Permissions and connectors
Compile-time effect and capability checks let code do only what it has been allowed to do. A hook, an output check or a tool cannot hide an effect, and a prompt, a model's output or any other text cannot grant a capability. You write your own connectors for services, models, knowledge sources, transports and log sinks: a connector declares its types, its permissions and its limits like any other code, and runs within the same budget and checks as the node that uses it. Tools and MCP describes connectors.
Honest limits
- An output check confirms what you wrote it to confirm; it does not make a model's answer true.
- A verifier model's verdict is an opinion. Your typed checks and permissions are what hold.
- The bodies of the helpers and of the agent on this page are placeholders, so the agent excerpt shows how hooks attach, not a complete workflow.