Your First Pipeline¶
This tutorial walks through building an AgentLang pipeline from scratch — a support triage workflow that classifies a message, routes it to the right queue, and generates a response.
By the end you'll have a working .agent file and understand the relationship between agents, tasks, and pipelines.
Pipeline vs workflow
pipeline is the explicit low-level execution language. For most multi-agent authoring, prefer workflow and let AgentLang lower it to a pipeline automatically. This tutorial stays with pipeline because it teaches the core execution model directly.
The goal¶
Given a user message like "urgent refund request", we want to:
- Extract the intent and urgency
- Route to the correct support queue
- Generate a reply
This mirrors the included examples/support.agent — but we'll build it step by step.
Step 1: Declare an agent¶
Create a new file my_support.agent:
An agent block binds a name to a model and tool list. You reference it later with by triage in run statements.
Step 2: Declare tasks¶
Tasks are typed signatures — they declare what goes in and what comes out:
agent triage {
model: "gpt-4.1-mini"
, tools: []
}
task extract_intent(message: String) -> Obj{intent: String, urgency: String} {}
task route(intent: String, urgency: String) -> Obj{queue: String} {}
task respond(intent: String, queue: String) -> Obj{reply: String} {}
Task bodies are always empty
The {} body is intentional. Task signatures live in the DSL; task behavior is supplied by Python handlers at runtime. This separation keeps the language simple and the runtime extensible.
Step 3: Write the pipeline¶
agent triage {
model: "gpt-4.1-mini"
, tools: []
}
task extract_intent(message: String) -> Obj{intent: String, urgency: String} {}
task route(intent: String, urgency: String) -> Obj{queue: String} {}
task respond(intent: String, queue: String) -> Obj{reply: String} {}
pipeline support_reply(message: String) -> String {
let i = run extract_intent with { message: message } by triage;
let q = run route with { intent: i.intent, urgency: i.urgency } by triage;
let r = run respond with { intent: i.intent, queue: q.queue } by triage;
return r.reply;
}
The pipeline:
- takes a
message: Stringas input - runs three tasks in sequence, threading outputs into subsequent inputs
- returns
r.replywhich has the declared return typeString
Step 4: Run it¶
Step 5: Add error handling¶
What if route fails? Add a retry and fallback:
let q = run route
with { intent: i.intent, urgency: i.urgency }
by triage
retries 2
on_fail use { queue: "general" };
If route fails after 3 total attempts, execution continues with queue: "general" instead of aborting.
What the type checker catches¶
The type checker runs before execution. Try breaking something — for example, passing the wrong field:
-- wrong: respond expects intent: String, not message: String
let r = run respond with { intent: message, queue: q.queue } by triage;
TypeError: argument 'intent' expected String, got String ✓
-- (this actually would pass since message is String)
Try a real type mismatch:
The pipeline never executes — the error is caught statically.
Next steps¶
- Add a
parallelblock to run tasks concurrently → Parallel Execution - Use
if/elseto branch on task output → Pipelines - Connect to real LLMs → Adapters