Home > EulerAgent > Tutorials > Pattern > Pattern 01. What is a Pattern? — Concepts and Architecture

Pattern 01. What is a Pattern? — Concepts and Architecture

Learning Objectives

After completing this tutorial, you will be able to:

Prerequisites

# 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: finalize is not defined inside the nodes array. 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:

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:


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.

← Prev Back to List Next →