Agents, nodes and edges
In AEL, the Agent Engineering Language, agents, nodes (business steps) and edges (typed two-way connections) are first-class building blocks, each in its own named file, so a project's structure reads at a glance. This page introduces all three; the rest of this guide describes each part in detail.
The three building blocks
| Component | File | What it is |
|---|---|---|
| Node | <name>.node.ael, declaring node <name>(…) | One business step: deterministic logic, or a model call with its tools. |
| Edge | <name>.edge.ael, declaring edge <name>(…) | A typed, two-way channel that carries one node's output to another's input and the reply back, with its delivery and reply policy. |
| Agent | <name>.agent.ael, declaring agent <name>(…) | A named workflow: which nodes and edges it runs, where work starts and what completes it. |
Each file holds exactly one primary declaration, whose name matches the file name, plus private helper functions. Files and roles gives the rules for every file role.
A node
A node takes a typed input and returns a typed output. This one asks a model for a summary, following a system prompt:
# nodes/summarize.node.ael
node summarize(input: Request) -> Summary {
config {
prompt: file("prompts/summarize.md");
model: binding("primary_model");
max_attempts: 2;
}
return write_summary(input);
}
fn write_summary(input: Request) -> Summary {
# A private helper: only this node can call it.
}
Helper bodies on this page that hold only a comment are placeholders; A first agent explains why, and where the call to a node's bound model is described.
A node that needs no model is ordinary code:
# nodes/verify.node.ael
node verify(input: VerifyInput) -> Verification {
return check_summary(input);
}
fn check_summary(input: VerifyInput) -> Verification {
# Your own rules, such as required facts and a maximum length.
}
A node can call models, tools, services, retrieval and memory, and your own functions. Declaring a node does nothing by itself: it makes no model call and starts no timer or server until a run reaches it.
An edge
An edge is a typed, two-way channel: it carries one node's output to another node's input, and the reply back. AEL generates the communication from your edge declarations, with no client or server code for you to write. The edge's body maps the value it receives to the input the next node expects:
# edges/summary_to_verify.edge.ael
edge summary_to_verify(summary: Summary) -> VerifyInput {
return VerifyInput(summary);
}
- The mapping is deterministic and bounded: it selects, moves and converts fields. Model calls, tools and network access belong in a node.
- An edge also declares how the value travels: as an event, as a request that expects a reply, with a callback, or as a stream. Workflows describes each mode.
- The same edge runs in one process, between processes or over the network, as your deployment places it, with no change to your source. Communication between agents describes the channel.
An agent
An agent puts nodes and edges together:
# agents/support.agent.ael
agent support(input: Request) -> Decision {
config {
nodes: [summarizer = summarize, verifier = verify];
edges: [summary_to_verify(summarizer, verifier)];
starts: [summarizer];
completion: summary_to_verify.callback_result;
}
}
nodesnames the node instances this workflow uses.edgesconnects them: here fromsummarizertoverifier.startssays where work begins. AEL never guesses a starting point from file order or from a node without incoming edges.completionsays which result ends the run and becomes the agent's output.
An agent with a single node needs no edge. An agent can also contain other agents; see Teams of agents.
A subagent is never one of these static children. An agent spawns subagents at run time: short-lived agents that each do one task, return a typed result to the agent that spawned them and end, leaving nothing running. Subagents describes them.
Definitions, instances and runs
- A definition is what a file declares, such as the node
summarize. - An instance is a configured use of a definition inside an agent, such as
summarizer = summarize. The same definition can appear more than once under different names, each instance with its own settings: one node reading two cameras, for example. - A run is one execution of an agent for one input. It has its own run ID and budget, and its configuration is resolved and locked into a snapshot before it starts, so settings cannot change underneath it (see Configuration).
One file per definition never limits a project to one agent or one run at a time.
Model-driven and deterministic decisions
Each node decides in one of two ways:
- Model-driven: the node has a model binding, and usually a system prompt, and a model proposes the next result or tool call.
- Deterministic: the node runs your code on its typed input. It needs no prompt, model binding or credentials.
One workflow mixes both kinds, so it can route sensor readings or events with deterministic nodes and call a model only where one is needed, without paying for a model call at every step.
From main to an answer
The program's entry point, main, takes no parameters and carries your application's name, the name in the project's metadata.ael, which is also the built program's default name: here the application is support. It reads its input and calls the agent like a function:
# main.ael
main support() {
let request: Request = read_request();
let decision: Decision = support(request);
show(decision);
}
fn read_request() -> Request {
# A private helper that reads the request from the console.
}
fn show(decision: Decision) -> () {
# A private helper that writes the decision to the console.
}
The check refuses main main(): the entry point always carries the application's name.
A console agent needs no server: it reads its input, runs, and writes its answer. Add the HTTP package and declare routes to make it a REST API, or remove them to go back to console-only, as Services and APIs shows. The first agent walk-through builds a console agent step by step.
What the check refuses
- A file whose name does not match its declaration.
- A
mainthat does not carry the application's name, such asmain main(). - An instance of a node or agent that does not exist.
- An edge between a node's output and an input of a different type.
- An agent with no starting point or no completion, and a node instance that nothing connects.
- A node or edge that reaches a private helper of another file.