Skip to main content

What Are Tool Pipelines?

Tool Pipelines let you compose multiple tools into a sequential workflow where the output of one tool flows into the next. Think of it like Unix pipes for your AI tools - powerful, composable, and elegant!

Sequential Execution

Tools execute in order, each building on the previous result

Data Transformation

Transform data between steps with custom mappers

Conditional Logic

Skip steps or branch based on intermediate results

Built-in Tracing

Automatic tracing for debugging and monitoring

Using Pipelines in Agents

The most common way to use pipelines is within a tool that orchestrates other tools. This “composite tool” pattern lets you expose a single capability to your agent while handling complex multi-step logic internally.

Composite Tool Pattern

Create a tool that chains multiple tools together:
app/Tools/ProcessOrderTool.php
Then add it to your agent like any other tool:
app/Agents/OrderAgent.php
The composite tool pattern keeps your agent’s tool list clean while hiding complex orchestration logic. The LLM sees one simple tool, but behind the scenes a full pipeline executes.

Pipeline in Agent Methods

You can also use pipelines directly in custom agent methods:
app/Agents/OnboardingAgent.php

Creating Your First Pipeline

Pipelines are created using the ToolChain class. Here’s a simple example:
Basic Pipeline

Named Pipelines

Give your pipeline a name for better debugging and tracing:
Named Pipeline

Step Types

Pipelines support four types of steps, each serving a specific purpose.

pipe() - Execute Tools

The pipe() method adds a tool to the pipeline. Each tool receives the output from the previous step:
Using pipe()
You can pass a tool class name or an instance:
Tool Instance

transform() - Transform Data

Use transform() to modify data between tools without executing a full tool:
Using transform()
Transforms are perfect for reshaping data, extracting specific fields, or converting between formats.

when() - Conditional Execution

Add conditional logic to your pipeline with when(). If the condition returns false, remaining steps are skipped:
Using when()
You can provide an otherwise callback for when the condition is false:
Conditional with Otherwise

tap() - Side Effects

The tap() method executes a callback without modifying the result. Use it for logging, debugging, or triggering side effects:
Using tap()
The tap callback receives the current result and step index. The return value is ignored - the pipeline continues with the unchanged result.

Argument Mappers

When tools need different argument structures, use argument mappers to transform the previous output:
Custom Argument Mapping
The argument mapper receives two parameters:
  • $previousResult - The output from the previous step
  • $initialArgs - The original arguments passed to execute()

Error Handling

Default Behavior (Stop on Error)

By default, pipelines stop execution when a step throws an exception:
Default Error Behavior

Continue on Error

Use continueOnError() to keep the pipeline running even when steps fail:
Continue on Error

Throwing Errors

Use throw() or valueOrThrow() for exception-based error handling:
Throwing Errors

Lifecycle Callbacks

Hook into the pipeline execution with before and after callbacks:
Lifecycle Callbacks

Working with Results

The ToolChainResult class provides rich access to execution details.

Basic Result Access

Accessing Results

Step-by-Step Results

Inspecting Steps

Timing Information

Timing Data

Export Results

Exporting Results

Chainable Tools

While any tool implementing ToolInterface works in pipelines, you can create tools specifically designed for chaining by implementing ChainableToolInterface.

The ChainableToolInterface

app/Tools/ChainableUserFetchTool.php

Using the ChainableTool Trait

For simpler cases, use the ChainableTool trait for sensible defaults:
app/Tools/SimpleChainableTool.php
The ChainableTool trait provides these defaults:
  • getInputSchema() - Returns ['type' => 'object']
  • getOutputSchema() - Returns ['type' => 'string']
  • transformOutputForChain() - Auto JSON decodes or returns raw string
  • acceptChainInput() - Merges previous output with initial arguments

Real-World Example

Here’s a complete example of an order processing pipeline:
app/Services/OrderPipeline.php

Best Practices

Name Your Pipelines

Always use ToolChain::create('name') for easier debugging and tracing

Transform Early

Parse JSON responses with transform() immediately after tools for cleaner data flow

Use Argument Mappers

When tools have different argument structures, use argument mappers rather than modifying tools

Handle Errors Gracefully

Check $result->failed() and handle errors appropriately
Avoid side effects in transforms - Use tap() for logging, metrics, or events. Keep transform() pure for data transformation only.

API Reference

ToolChain Methods

ToolChainResult Methods

Callback Signatures


Tools

Learn the basics of creating tools

Tracing

Debug pipelines with execution traces