Pattern 01. What is a Pattern? — Concepts and Architecture
Learning Objectives
After completing this tutorial, you will be able to:
- Explain what an euleragent pattern is and why it is needed
- Understand and read the core structure of a YAML pattern file
- Explain the processing pipeline from YAML to IR to Runtime
- Use the
pattern list,pattern show,pattern validate, andpattern compilecommands - Distinguish between the 3 node types (llm, judge, finalize) and edge conditions
Prerequisites
- Completion of
docs/tutorials/basic/01_getting_started.md - euleragent installation complete (
pip install -e .) euleragent doctorpassing
# Environment check
euleragent --version
euleragent doctor
1. What is a Pattern?
What happens when you tell an agent to "write a report"? The agent might write it all at once and finish, or it might follow steps like researching first, writing a draft, then reviewing it. A Pattern is a way to explicitly define this workflow.
A pattern is a directed graph defined in YAML. Nodes are work stages, and edges are transition conditions between stages.
Simple agent request:
"Write a report" → (agent decides on its own) → Result
Pattern-based agent request:
"Write a report" → [outline] → [gather] → [draft] → [evaluate] → [revise or finalize] → Result
↑__________________________|
(rewrite if evaluation score is low)
The core values of patterns are these three:
Predictability: You can know in advance what steps the agent will take. A transparent workflow instead of a black box.
Control: You can enforce human approval at specific steps, restrict tool usage, and cap the number of iterations.
Reusability: A pattern defined once can be reused across multiple agents and tasks.
2. YAML → IR → Runtime Pipeline
A pattern YAML file goes through several stages before execution.
┌─────────────────────────────────────────────────────────────────┐
│ Pattern Processing Pipeline │
├─────────────────────────────────────────────────────────────────┤
│ │
│ my_pattern.yaml │
│ │ │
│ ▼ │
│ ┌─────────────┐ │
│ │ Stage 1 │ JSON Schema Validation │
│ │ Schema │ - Required field existence │
│ │ Validator │ - Type validation (string, int, enum) │
│ └──────┬──────┘ │
│ │ Pass │
│ ▼ │
│ ┌─────────────┐ │
│ │ Stage 2 │ YAML Structural Validation │
│ │ YAML │ - Duplicate node IDs │
│ │ Structural │ - Edge reference validity │
│ │ Validator │ - Judge route declarations │
│ └──────┬──────┘ │
│ │ Pass │
│ ▼ │
│ ┌─────────────┐ │
│ │ Compiler │ YAML → IR (Intermediate Representation) │
│ │ │ Convert to internal JSON representation │
│ └──────┬──────┘ │
│ │ │
│ ▼ │
│ ┌─────────────┐ │
│ │ Stage 3 │ IR Analysis Validation │
│ │ IR │ - Entry point uniqueness │
│ │ Analyzer │ - Cycle boundary check │
│ │ │ - Unreachable nodes │
│ │ │ - Budget consistency │
│ └──────┬──────┘ │
│ │ Pass │
│ ▼ │
│ ┌─────────────┐ │
│ │ Runtime │ Load IR and execute agent │
│ │ Engine │ - Per-node LLM calls │
│ │ │ - HITL pause/resume │
│ │ │ - Event stream │
│ └─────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
The euleragent pattern validate command runs all of Stages 1, 2, and 3. The euleragent pattern compile command only performs compilation and outputs the IR JSON.
3. Core Structure: Nodes and Edges
3 Types of Nodes
llm node: Calls an LLM to perform actual work. Used for "production" tasks like research, draft writing, code generation, etc.
nodes:
- id: draft
kind: llm
runner:
mode: execute
artifacts:
primary: draft.md
judge node: Asks an LLM to evaluate the results of the previous step. Routes to different nodes based on the evaluation result.
nodes:
- id: evaluate
kind: judge
judge:
schema: evaluator_v1
route_values: [finalize, revise]
finalize node: The termination point of the graph. Saves the final result to the specified artifact path and completes execution.
finalize:
artifact: result.md
Note:
finalizeis not defined inside thenodesarray. It is declared separately as a top-level key.
Edges and when Conditions
Edges define transitions between nodes. The when field is the transition condition.
edges:
- from: draft
to: evaluate
when: "true" # Always transition
- from: evaluate
to: finalize
when: "judge.route == finalize" # When judge result is finalize
- from: evaluate
to: revise
when: "judge.route == revise" # When judge result is revise
- from: gather
to: draft
when: "approvals_resolved" # When HITL approval is completed
4 forms of the when DSL:
| Form | Example | When to Use |
|---|---|---|
"true" |
when: "true" |
Always transition |
"approvals_resolved" |
when: "approvals_resolved" |
After HITL approval completes |
"judge.route == <value>" |
when: "judge.route == finalize" |
Judge routing |
"judge.score >= <value>" |
when: "judge.score >= 0.9" |
Judge score threshold |
4. Entry Point and Final Node
Entry Point (Entry Node)
The only node with no incoming edges becomes the entry point. It is the first node executed when the pattern runs.
A validation error occurs if there is no entry point or if there are two or more.
Correct example:
[outline] → [gather] → [draft] → [evaluate]
↑
Entry point (no incoming edges)
Incorrect example (no entry point):
[outline] ⇄ [gather] # Both nodes point to each other, so both have incoming edges
Incorrect example (two entry points):
[outline] # No incoming edges ← Entry point 1
[gather] → [draft] # No incoming edges ← Entry point 2
Final Node (Finalize)
The artifact path declared in the finalize key is the final result. The runtime engine terminates execution and saves the result when it reaches the finalize node.
finalize:
artifact: result.md # Final result save path
5. Judge Routing and JUDGE_ROUTE_COVERAGE_ERROR
A Judge node selects one of the values declared in route_values. There must be a corresponding edge for every route_value. Otherwise, JUDGE_ROUTE_COVERAGE_ERROR will occur.
# Correct example
nodes:
- id: evaluate
kind: judge
judge:
schema: evaluator_v1
route_values: [finalize, revise] # Two declared
edges:
- from: evaluate
to: finalize
when: "judge.route == finalize" # finalize covered
- from: evaluate
to: revise
when: "judge.route == revise" # revise covered
# Incorrect example — JUDGE_ROUTE_COVERAGE_ERROR
nodes:
- id: evaluate
kind: judge
judge:
schema: evaluator_v1
route_values: [finalize, revise] # Two declared
edges:
- from: evaluate
to: finalize
when: "judge.route == finalize" # Only finalize covered
# No edge for revise! → Error
6. Cycle Boundaries and max_iterations
Patterns are not DAGs (Directed Acyclic Graphs). They allow cycles (loops). However, if a cycle exists, max_iterations must be declared to prevent infinite loops.
defaults:
max_iterations: 3 # Repeat evaluate-revise loop up to 3 times
If a cycle is detected without max_iterations, an UNBOUNDED_CYCLE error occurs.
7. Differences Between YAML and IR
YAML is a format that is easy for humans to read. IR (Intermediate Representation) is a format that is easy for the runtime engine to read.
Key transformations:
| YAML | IR |
|---|---|
| Node list + Edge list | Graph in adjacency list format |
when: "judge.route == finalize" |
Parsed AST-form condition expression |
runner.force_tool |
Execution config with HITL flags included |
judge.route_values |
Valid route set + coverage map |
defaults |
Resolved settings applied to each node |
8. Step-by-Step Hands-on
Step 1: Check Built-in Pattern List
euleragent pattern list
Expected output:
Built-in Patterns
─────────────────────────────────────────────────────────
report.evidence research Evidence-based report writing
code.tdd code Test-driven development workflow
ops.triage ops Operations ticket triage
research.broad_to_narrow research Broad-to-narrow research synthesis
4 patterns available. Use 'euleragent pattern show <id>' for details.
Step 2: Check Pattern Structure
euleragent pattern show report.evidence
Expected output:
Pattern: report.evidence
Category: research
Version: 1
Description: Evidence-based report writing with web research and quality loop
Defaults:
max_iterations: 3
max_total_tool_calls: 30
max_web_search_calls: 10
pass_threshold: 0.85
dedupe_web_search: true
Nodes:
[entry] outline llm mode=execute
gather llm mode=plan force_tool=web.search
draft llm mode=execute
evaluate judge schema=evaluator_v1 routes=[finalize, revise]
revise llm mode=execute
[fin] finalize finalize artifact=report.md
Edges:
outline → gather when: true
gather → draft when: approvals_resolved
draft → evaluate when: true
evaluate → finalize when: judge.route == finalize
evaluate → revise when: judge.route == revise
revise → evaluate when: true
Finalize:
artifact: report.md
Step 3: Validate Pattern
euleragent pattern validate report.evidence
Expected output:
Validating pattern: report.evidence
Stage 1 (Schema) PASS All required fields present, types valid
Stage 2 (Structural) PASS Nodes, edges, judge routes all consistent
Stage 3 (IR Analysis) PASS Entry node unique, cycles bounded, all nodes reachable
Validation complete: 0 errors, 0 warnings
Get results in JSON format:
euleragent pattern validate report.evidence --format json
Expected output:
{
"pattern_id": "report.evidence",
"stages": {
"schema": { "status": "pass", "errors": [] },
"structural": { "status": "pass", "errors": [] },
"ir_analysis": { "status": "pass", "errors": [], "warnings": [] }
},
"overall": "pass",
"error_count": 0,
"warning_count": 0
}
Step 4: Compile Pattern
euleragent pattern compile report.evidence
Expected output (IR JSON):
{
"id": "report.evidence",
"version": 1,
"category": "research",
"entry_node": "outline",
"nodes": {
"outline": {
"id": "outline",
"kind": "llm",
"runner": {
"mode": "execute",
"force_tool": null,
"exclude_tools": [],
"min_proposals": null,
"max_loops": null
},
"resolved_defaults": {
"max_iterations": 3,
"max_total_tool_calls": 30
},
"outgoing_edges": ["gather"]
},
"gather": {
"id": "gather",
"kind": "llm",
"runner": {
"mode": "plan",
"force_tool": "web.search",
"hitl_required": true
},
"outgoing_edges": ["draft"],
"edge_conditions": {
"draft": { "type": "approvals_resolved" }
}
}
},
"adjacency": {
"outline": ["gather"],
"gather": ["draft"],
"draft": ["evaluate"],
"evaluate": ["finalize", "revise"],
"revise": ["evaluate"]
},
"cycles": [
{ "path": ["evaluate", "revise", "evaluate"], "bounded_by": "max_iterations" }
],
"finalize": {
"artifact": "report.md"
}
}
9. Key Concept Explanations
YAML vs Agent Prompts
An agent without a pattern operates solely with a system prompt and a task. The agent decides on its own what steps to take. This is flexible but hard to predict.
With patterns, the workflow is fixed in YAML. The agent performs only its designated role at each node. Predictability increases, and it becomes easier to insert HITL at specific steps or control budgets.
Why IR is Needed
YAML is easy for humans to edit, but not suitable for the runtime to execute directly. IR pre-computes the following:
- Entry node identification
- Adjacency list (fast graph traversal)
- Condition parsing (when DSL to AST)
- Cycle detection results
- Resolved values of defaults inherited by each node
The runtime loads the IR directly without re-parsing the YAML.
evaluator_v1 Schema
judge.schema: evaluator_v1 is a built-in evaluation schema. Using this schema, the Judge node requests the LLM to evaluate with the following structure:
score: 0.0~1.0 quality scoreroute: One of theroute_valuesreason: Rationale for the evaluationsuggestions: List of improvement suggestions
10. Common Errors and Solutions
Error 1: NO_FINALIZE
ERROR [NO_FINALIZE] Pattern has no finalize section
Cause: The top-level finalize: key is missing.
Solution: Add a finalize: section at the YAML top level.
finalize:
artifact: result.md
Error 2: JUDGE_ROUTE_COVERAGE_ERROR
ERROR [JUDGE_ROUTE_COVERAGE_ERROR] Node 'evaluate': route_value 'revise' has no outgoing edge
Cause: There is no corresponding edge for a value declared in route_values.
Solution: Add an edge for the missing route_value.
Error 3: UNBOUNDED_CYCLE
ERROR [UNBOUNDED_CYCLE] Cycle detected: evaluate → revise → evaluate. Set defaults.max_iterations.
Cause: A cycle exists but max_iterations is not set.
Solution: Add max_iterations to the defaults section.
defaults:
max_iterations: 3
Error 4: MULTIPLE_ENTRY_NODES
ERROR [MULTIPLE_ENTRY_NODES] Multiple nodes with no incoming edges: outline, gather
Cause: Two or more nodes have no incoming edges.
Solution: Add an incoming edge to the node that is not meant to be the entry point, or consolidate to a single entry point.
11. Next Steps
Now that you understand the concepts, let's actually run built-in patterns.
- Next tutorial: 02_builtin_patterns.md — Run the 4 built-in patterns hands-on and check the results
- Create a pattern: 03_simple_linear.md — Write your first custom pattern from scratch
- Complete reference: 10_reference.md — Complete list of all fields and error codes