Skip to contents

Introduction

Workflows in llmr allow you to chain together multiple processing steps to create sophisticated data processing pipelines. The most fundamental workflow pattern is the linear chain, where data flows sequentially through a series of steps.

This vignette introduces the basics of workflows by showing how to create simple linear chains using both regular R functions and AI agents as steps.

What You’ll Learn

By the end of this guide, you’ll understand how to:

  • Create workflow steps from R functions
  • Create workflow steps from AI agents
  • Chain steps together using the %->% operator
  • Execute workflows with input data
  • Understand the flexibility of mixing functions and agents

Prerequisites

library(llmr)
#> 
#> Attaching package: 'llmr'
#> The following object is masked from 'package:stats':
#> 
#>     step

Basic Workflow Concepts

Steps

A step is the fundamental building block of a workflow. Steps can be created from: - R functions - For deterministic processing - AI agents - For intelligent, context-aware processing

Chains

A chain connects steps together using the %->% operator, creating a pipeline where the output of one step becomes the input of the next.

Execution

Execution runs the entire workflow with your input data, passing results through each step in sequence.

Example 1: Function-Based Linear Workflow

Let’s start with a simple workflow using regular R functions:

# Define simple processing functions
add_ten <- function(x) {
  cat("Adding 10 to", x, "\n")
  x + 10
}

multiply_by_two <- function(x) {
  cat("Multiplying", x, "by 2\n")
  x * 2
}

subtract_five <- function(x) {
  cat("Subtracting 5 from", x, "\n")
  x - 5
}

# Create workflow steps and chain them together inline
math_workflow <- step(add_ten, name = "add_ten") %->%
  step(multiply_by_two, name = "multiply_two") %->%
  step(subtract_five, name = "subtract_five")

# View the workflow structure
print(math_workflow)
#> Workflow: <unnamed> 
#> Nodes: 3 | Edges: 2 
#> 
#> ┌─ ⚙️ add_ten
#> │
#> ├─ ⚙️ multiply_two
#> │
#> └─ ⚙️ subtract_five

# Execute the workflow
input_value <- 5
result <- execute(math_workflow, input_value)
#> > 2025-06-19 18:55:22 [WORKFLOW] Executing node: add_ten 
#> Adding 10 to 5 
#> > 2025-06-19 18:55:22 [WORKFLOW] Executing node: multiply_two 
#> Multiplying 15 by 2
#> > 2025-06-19 18:55:22 [WORKFLOW] Executing node: subtract_five 
#> Subtracting 5 from 30
print(result)  # Should be 25: ((5 + 10) * 2) - 5 = 25
#> [1] 25

Key Points: - Each function takes one input and returns one output - The step() function wraps your function for use in workflows - The %->% operator chains steps left-to-right - execute() runs the entire workflow

Example 2: Agent-Based Linear Workflow

Now let’s create a workflow using AI agents for text processing:

# Create specialized agents using the factory pattern
summarizer <- new_agent("summarizer", new_anthropic)
analyzer <- new_agent("analyzer", new_anthropic)
formatter <- new_agent("formatter", new_anthropic)

# Configure each agent with specific instructions
set_system_prompt(
  summarizer,
  "You are a text summarizer. Provide concise, one-sentence summaries."
)
set_system_prompt(
  analyzer,
  "You are a sentiment analyzer. Analyze the emotional tone and return just the sentiment (positive/negative/neutral)."
)
set_system_prompt(
  formatter,
  "You are a formatter. Take the previous analysis and format it as: 'Summary: [summary] | Sentiment: [sentiment]'"
)

# Create workflow by chaining agent steps inline
text_workflow <- step(summarizer, name = "summarize") %->%
  step(analyzer, name = "analyze_sentiment") %->%
  step(formatter, name = "format_output")

# View the workflow structure
print(text_workflow)

# Execute the workflow
input_text <- "The new product launch was incredibly successful, exceeding all our expectations.
  Customers are thrilled with the innovative features and the team is celebrating this major milestone."

result <- execute(text_workflow, input_text)
print(result)

Key Points: - Agents work just like functions in workflows - Each agent processes the output from the previous step - System prompts define each agent’s specific role - The workflow creates a sophisticated text processing pipeline

Example 3: Mixed Function and Agent Workflow

The real power comes from mixing functions and agents in the same workflow:

# Function to preprocess text
clean_text <- function(text) {
  # Remove extra whitespace and convert to lowercase
  cleaned <- trimws(tolower(text))
  cat("Cleaned text:", substr(cleaned, 1, 50), "...\n")
  cleaned
}

# Function to add metadata
add_metadata <- function(analysis) {
  # Add timestamp and word count to the analysis
  timestamp <- Sys.time()
  word_count <- length(strsplit(analysis, "\\s+")[[1]])
  
  result <- paste0(
    "Analysis: ", analysis, "\n",
    "Generated: ", timestamp, "\n",
    "Word count: ", word_count
  )
  
  cat("Added metadata\n")
  result
}

# Create an agent for the core analysis
analyzer <- new_agent("text_analyzer", new_anthropic)
set_system_prompt(
  analyzer,
  "Analyze the given text and provide insights about its main themes, tone, and key messages. Be concise but thorough."
)

# Create mixed workflow: function -> agent -> function
mixed_workflow <- step(clean_text, name = "preprocess") %->%
  step(analyzer, name = "analyze") %->%
  step(add_metadata, name = "finalize")

# View the workflow structure
print(mixed_workflow)

# Execute the mixed workflow
input_text <- "   THE QUARTERLY RESULTS show STRONG GROWTH across all sectors!   "
result <- execute(mixed_workflow, input_text)
print(result)

Key Points: - Functions and agents can be mixed freely in workflows - Functions handle deterministic processing (cleaning, formatting) - Agents handle intelligent analysis and interpretation - This creates powerful hybrid processing pipelines

Understanding Workflow Execution

Data Flow

# Input -> Step 1 -> Step 2 -> Step 3 -> Output
#   5   ->   15   ->   30   ->   25   -> Final Result

Step Naming

# Steps can be named for clarity
my_workflow <- step(my_function, name = "descriptive_name") %->%
  step(another_function, name = "another_step")

# Names appear in workflow output and debugging
print(my_workflow)  # Shows step names and visual diagram

Error Handling

If any step fails, the entire workflow stops:

# Function that might fail
risky_function <- function(x) {
  if (x < 0) stop("Cannot process negative numbers")
  x * 2
}

# Workflow will stop if risky_function fails
risky_workflow <- step(risky_function, name = "risky_step")

Best Practices

1. Keep Steps Simple

Each step should have a single, clear responsibility:

# Good: Single responsibility
validate_input <- function(x) {
  if (!is.numeric(x)) stop("Input must be numeric")
  x
}

# Good: Single transformation
double_value <- function(x) x * 2

# Better than one complex function doing both

2. Use Descriptive Names

# Good: Clear what each step does
workflow <- step(validate_input, name = "validate") %->%
  step(double_value, name = "double") %->%
  step(format_output, name = "format")

3. Design for Reusability

# Create reusable steps
validation_step <- step(validate_input, name = "validate")
doubling_step <- step(double_value, name = "double")

# Use in multiple workflows
workflow_a <- validation_step %->% doubling_step
workflow_b <- validation_step %->% other_step %->% doubling_step

Common Patterns

Preprocessing Pipeline

preprocessing <- step(validate_data, name = "validate") %->%
  step(clean_data, name = "clean") %->%
  step(normalize_data, name = "normalize")

Analysis Pipeline

analysis <- step(analyzer_agent, name = "analyze") %->%
  step(extract_insights, name = "extract") %->%
  step(format_results, name = "format")

Full Processing Pipeline

complete_pipeline <- preprocessing %->% analysis

What’s Next?

Now that you understand linear workflows, you can explore:

  • Conditional workflows - Branching based on data or conditions
  • Parallel workflows - Processing multiple paths simultaneously
  • Complex workflows - Combining linear, conditional, and parallel patterns
  • Workflow composition - Building larger workflows from smaller ones

Summary

Linear workflows provide a powerful way to chain together processing steps:

  • Steps can be created from both functions and agents
  • Chains use the %->% operator to connect steps
  • Mixed workflows combine the deterministic power of functions with the intelligence of agents
  • Simple patterns can be composed into sophisticated processing pipelines

The flexibility to mix functions and agents makes llmr workflows incredibly versatile for building everything from simple data processing pipelines to complex AI-powered analysis systems.