Home
Back to Overview
GitHub Tutorial
View Full Lesson
Part 1: Foundation - Lesson 1

The Runnable Contract

Understanding the single pattern that powers most AI agent frameworks

The Runnable is the fundamental building block of our framework. It's a simple yet powerful abstraction that allows us to build complex AI systems from composable parts.

Overview

Think of Runnable as the "contract" that every component in the framework must follow. It's like LEGO blocks—each block has the same connection mechanism, which means any block can connect to any other block.

By the end of this lesson, you'll understand why frameworks like LangChain built everything around this single interface, and you'll implement your own Runnable components.

Why Does This Matter?

❌ The Problem Without Runnable

// Without a common interface, 
// every component is different:
const llmResponse = await llm.generate(prompt);
const parsedOutput = parser.parse(llmResponse);
const memorySaved = memory.store(parsedOutput);

// Different methods: generate(), 
// parse(), store()
// Hard to compose, hard to test, 
// hard to maintain

✅ The Solution With Runnable

// With Runnable, everything uses 
// the same interface:
const result = await prompt
  .pipe(llm)
  .pipe(parser)
  .pipe(memory)
  .invoke(input);

// Same method everywhere: invoke()
// Easy to compose, test, and maintain

Learning Objectives

By the end of this lesson, you will:

  • ✓ Understand what makes a good abstraction
  • ✓ Implement the base Runnable class
  • ✓ Create custom Runnable components
  • ✓ Know the three core execution patterns: invoke, stream, batch
  • ✓ Understand why this abstraction is powerful for AI systems

Core Concepts

What is a Runnable?

A Runnable is any component that can:

  1. Take input
  2. Do something with it
  3. Return output

That's it! But this simplicity is what makes it powerful.

The Three Execution Patterns

1. invoke() - Single Execution

Run once with one input, get one output.

const result = await runnable.invoke(input);
// Input: "Hello"
// Output: "Hello, World!"

Use case: Normal execution, when you have one thing to process.

2. stream() - Streaming Execution

Process input and receive output in chunks as it's generated.

for await (const chunk of runnable.stream(input)) {
  console.log(chunk); // Print each piece as it arrives
}
// Output: "H", "e", "l", "l", "o", "..."

Use case: LLM text generation, where you want to show results in real-time.

3. batch() - Parallel Execution

Process multiple inputs at once.

const results = await runnable.batch([input1, input2, input3]);
// Input: ["Hello", "Hi", "Hey"]
// Output: ["Hello, World!", "Hi, World!", "Hey, World!"]

Use case: Processing many items efficiently.

The Magic: Composition

The real power comes from combining Runnables:

const pipeline = runnableA.pipe(runnableB).pipe(runnableC);

Because everything is a Runnable, you can chain them together infinitely!

💡 Interesting Insight: The LangChain Connection

You might be wondering: "Why should I care about Runnable if I can just use LangChain directly?"

Here's the fascinating part: LangChain rebuilt their entire framework around Runnable in 2023. Before that, they had dozens of different interfaces—chains, agents, tools, each with their own methods. It was a mess.

Before Runnable (LangChain v1)

  • • Chains had run() method
  • • Agents had execute() method
  • • Tools had call() method
  • • LLMs had generate() method
  • • Each component was different
  • • Hard to compose, hard to test

After Runnable (LangChain v2)

  • • Everything has invoke()
  • • Everything has stream()
  • • Everything has batch()
  • • Everything can pipe()
  • • Universal composability
  • • Easy to test and maintain

The "Aha!" Moment

When LangChain introduced Runnable, they didn't just add a new feature—they fundamentally changed how the framework works. They realized that having one consistent interface was more powerful than having specialized interfaces for each component.

This is why you're learning Runnable first: it's the foundation that everything else is built on. Once you understand Runnable, you'll understand:

  • Why LangChain's API looks the way it does
  • How to compose complex workflows easily
  • What makes frameworks like LangChain powerful
  • How to build your own framework components

Fun fact: The Runnable pattern is so successful that other frameworks (like LlamaIndex) have adopted similar patterns. It's become the de facto standard for composable AI frameworks.

Mental Model

Single Runnable

Input
"Hello"
→
Runnable
_call()
Your logic
→
Output
"HELLO"

Piped Runnables (Composition)

Input
5
→
Runnable1
multiply
→
Runnable2
format
→
Runnable3
validate
→
Output
"10"

Each step processes the output of the previous step

prompt.pipe(llm).pipe(parser).pipe(memory).invoke(input)

Key Insight

Every Runnable follows the same pattern: Input → Process → Output. When you pipe them, the output of one becomes the input of the next. This creates a data transformation pipeline where each step does one thing well, and together they solve complex problems.

Key Takeaways

Runnable is a contract
Every component implements invoke(), stream(), and batch()
Override _call()
This is where your custom logic goes
Composition is powerful
Use pipe() to chain Runnables together
It's Runnables all the way down
Prompts, LLMs, Parsers, Chains, Agents - all Runnables!
Back to Phase 2 Overview
Next: Lesson 2 - Messages