Skip to content

Playbooks Programming Guide

Purpose: This document provides comprehensive guidance for writing effective, optimal, and idiomatic Playbooks programs.

Playbooks is a Software 3.0 language where LLMs act as CPUs. Programs are written in natural language with optional Python, compiled to Playbooks Assembly Language (PBAsm), and executed by LLMs. This guide will help you become an expert Playbooks programmer building sophisticated AI agents.


Table of Contents

  1. Core Concepts
  2. Language Syntax and Formatting
  3. The Five Playbook Types
  4. Decomposing Tasks into Playbooks
  5. Natural Language vs Explicit Syntax
  6. Multi-Agent Programs
  7. Triggers: Event-Driven Programming
  8. Description Placeholders
  9. Artifacts and Memory
  10. Common Patterns and Best Practices
  11. Editing Existing Programs
  12. Understanding PBAsm Compilation

Core Concepts

Philosophy: Software 3.0

  • LLMs as CPUs: Natural language instructions are the program; the runtime compiles them to PBAsm and executes on LLM
  • 10x less code: Remove boilerplate; describe behavior at high abstraction level
  • Soft + Hard logic: Run soft logic (NL) on LLM, hard logic (Python) on CPU, on same call stack
  • Verifiable execution: Programs compile to structured PBAsm for auditing and debugging
  • First principles thinking: Remove unnecessary complexity from agent frameworks

Key Abstractions

  • Agents (H1 #) = Classes with state and methods
  • Playbooks (H2 ## or @playbook) = Methods/functions
  • Multi-agent communication = Message passing + direct calls
  • Triggers = Event-driven interrupts (like CPU interrupts)
  • Variables = Prefixed with $, typed at runtime

Language Syntax and Formatting

File Structure

# AgentName
Agent description and personality. Define capabilities and behavioral traits here.

## PlaybookName($param1, $param2)
Playbook description explaining what it does and when to use it.

### Triggers
- When to execute this playbook

### Steps
- Step 1
- Step 2

### Notes
- Additional guidance for LLM

```python
# Python code blocks can appear anywhere
@playbook
async def PythonPlaybook(param: str) -> dict:
    """Docstring becomes playbook description"""
    return result
```

# SecondAgent
Another agent in the same program file

Formatting Rules

H1 Tags (#) - Agent Definitions

# AgentName
Description of agent's purpose and personality

H2 Tags (##) - Playbook Definitions

## PlaybookName($param1, $param2)

H3 Tags (###) - Special Sections - ### Triggers - When playbook executes - ### Steps - Execution instructions (only for Markdown playbooks) - ### Notes - Business rules and guidance

Metadata - Key-value pairs at start of description

## PlaybookName
execution_mode: raw
public: true
meeting: true

Description text here

Variables - Always prefixed with $

- Ask user for their $name
- $count = 10
- $result = GetData($name)

Type Annotations - Optional but helpful for clarity

- Ask user for their $name:str
- $count:int = 10
- $results:list = []

Comments

<!-- This is a comment in Playbooks -->


The Five Playbook Types

Decision Framework

START: What type of playbook do I need?
├─ Do I need deterministic logic, external APIs, or complex calculations?
│  └─ YES → Python Playbook
│       └─ Will I have 4+ Python playbooks in this agent and will this Python playbook NOT need to call a Markdown/ReAct/Raw Prompt playbook?
│           └─ YES → Extract into MCP Server instead
├─ Do I know the exact steps in advance?
│  └─ YES → Markdown Playbook
├─ Do steps depend on dynamic research/reasoning?
│  └─ YES → ReAct Playbook
├─ Do I need exact prompt control for single-shot task?
│  └─ YES → Raw Prompt Playbook
└─ Am I integrating external MCP server tools?
   └─ YES → External Playbook (via MCP agent)

1. Markdown Playbooks - Structured Workflows

Use when: Steps are known, process is repeatable, need explicit control flow

Structure:

## PlaybookName($param1, $param2)
Description

### Triggers
- When condition is met

### Steps
- Step with natural language instruction
- $variable = Assign from another step
- If condition
  - Nested step
  - While another condition
    - Deeply nested step
- Otherwise
  - Alternative path
- Return result

### Notes
- Business rules that apply throughout execution

Example:

## ProcessOrder($order_id)
Process a customer order from validation through fulfillment

### Steps
- Get $order from database using $order_id
- If $order was not found
  - Tell user order not found
  - Return error
- Validate payment for $order
- If payment is valid
  - Update order status to processing
  - Send confirmation email to customer
  - Return success
- Otherwise
  - Tell user payment failed
  - Return payment error

Control Flow: - Sequential: Just list steps in order - Conditional: If condition, Otherwise, Else if condition - Loops: While condition, For each $item in $list - Variable assignment: $var = value or Get $var from source - Return: Return value or Return $variable - End program: End program (terminates execution)

2. ReAct Playbooks - Dynamic Reasoning

Use when: Steps can't be predetermined, need research, adaptive behavior

Structure:

## PlaybookName($param)
execution_mode: react  # Optional - inferred if no Steps section

Detailed description of task, goals, constraints, and expected output.

<planning_rules>
- Rule 1 for how to approach the task
- Rule 2 for verification
</planning_rules>

<style_guide>
- Tone and communication style
- Formatting preferences
</style_guide>

<output_format>
Expected structure of final output
</output_format>

### Triggers
- When to execute

Key Points: - No ### Steps section - Framework provides default think-plan-act loop - All playbooks available as tools - LLM decides what actions to take

Example:

## ResearchCompany($company_name)
Conduct comprehensive competitive analysis with market positioning,
products, pricing, and recent developments.

<planning_rules>
- Start by identifying primary products and services
- Search for recent news and financials (last 6 months)
- Verify information across multiple sources
- Compare with top 3 competitors
</planning_rules>

<output_format>
# Company Analysis: [Name]
## Overview
## Products
## Market Position
## Recent Developments
## Conclusion
</output_format>

3. Raw Prompt Playbooks - Full Control

Use when: Need exact prompt control, single-shot tasks, minimal overhead

Structure:

## PlaybookName
execution_mode: raw

Exact prompt text. This goes directly to LLM.
Use {$variable} and {PlaybookCall()} for dynamic content.

Key Points: - Single LLM call, no loops - Cannot call other playbooks during execution - No automatic context management - Use for classification, extraction, formatting

Example:

## CategorizeTicket
execution_mode: raw

Categorize this support ticket: {$ticket_message}

Categories:
- Technical Support
- Billing  
- Account Management
- General Inquiry

Respond with ONLY the category name.
Category:

4. Python Playbooks - Hard Logic

Use when: Need deterministic logic, external APIs, complex calculations

Structure:

```python
from typing import Dict, List

@playbook
async def PlaybookName(param1: str, param2: int = 10) -> float:
    """
    Docstring becomes playbook description.

    Args:
        param1: Description
        param2: Description

    Returns:
        Description
    """
    # Your Python code
    result = compute(param1, param2)

    # Can call other playbooks (Markdown or Python)
    summary = await SummarizeResult(result)

    return summary

@playbook(triggers=["When user provides PIN"], public=True)
async def ValidatePIN(pin: str) -> bool:
    """Validate PIN format and check database."""
    return len(pin) == 4 and pin.isdigit()
```

Decorator Options: - @playbook - Basic playbook - @playbook(public=True) - Callable by other agents - @playbook(export=True) - Allow implementation export - @playbook(triggers=["condition"]) - Add triggers - @playbook(metadata={...}) - Custom metadata

Key Points: - Full Python power: any library, complex logic, external APIs - Use await to call other playbooks - Return user-readable strings/dicts when called from Markdown - Can call Markdown playbooks: await MarkdownPlaybook(args)

When to Extract Python Playbooks to MCP Server

⚠️ Important Rule: If you have more than 3 Python playbooks in a single agent, extract them to a separate MCP server instead.

Why Extract to MCP: - Keeps agent code focused on behavior, not implementation - Python tools can be reused across multiple agents - Better separation of concerns - Easier testing and maintenance - Can be developed and deployed independently

How to Extract:

  1. Create MCP server file (e.g., mcp.py):
from fastmcp import FastMCP

mcp = FastMCP("My Tools Server")

@mcp.tool
def GetUserInfo():
    """
    Get information about the user.

    Returns:
        A dictionary containing user information.
    """
    return {"name": "John", "email": "john@example.com"}

@mcp.tool
def CalculateSleepEfficiency(time_in_bed: int, time_asleep: int) -> float:
    """
    Calculate sleep efficiency percentage.

    Args:
        time_in_bed: Total minutes in bed
        time_asleep: Total minutes asleep

    Returns:
        Sleep efficiency as percentage
    """
    return round((time_asleep * 100) / time_in_bed, 1)

if __name__ == "__main__":
    mcp.run(transport="streamable-http")
  1. Run the MCP server:
fastmcp run mcp.py -t streamable-http --port 8888
  1. Define MCP agent in your Playbooks program:
# ToolsAgent
This agent provides various Python tools.
remote:
  type: mcp
  url: http://127.0.0.1:8888/mcp
  transport: streamable-http

# MainAgent
Your main agent description.

## Main
### Steps
- Load user info from ToolsAgent
- Calculate sleep efficiency using ToolsAgent
- Tell user their sleep efficiency

Example from insomnia.pb:

# MCP
This agent provides various python tools that the sleep coach will use.
remote:
  type: mcp
  url: http://127.0.0.1:8888/mcp
  transport: streamable-http

# Sleep Coach
You are a sleep coach helping users improve their sleep.

## Main
### Steps
- Load user info from MCP
- Get user's sleep efficiency from MCP
- Welcome user and use the info to help them

When NOT to extract:

  • ✅ 1-3 Python playbooks that are tightly coupled to agent logic
  • ✅ Python playbooks that call Markdown playbooks (requires @playbook)
  • ✅ Python playbooks with triggers (requires @playbook)

When to extract:

  • ✅ 4+ Python playbooks in single agent
  • ✅ Pure utility functions (calculations, API calls, data transformations)
  • ✅ Tools that could be reused by multiple agents
  • ✅ Complex Python logic that benefits from separate testing

5. External Playbooks - Remote Tools

Use when: Integrating MCP servers, external APIs (roadmap)

Structure:

# MCPAgent
remote:
  type: mcp
  transport: streamable-http
  url: http://localhost:8000/mcp

# LocalAgent
## Main
### Steps
- $weather = MCPAgent.get_weather(zipcode=98053)
- Tell user the $weather

Key Points: - MCP server tools automatically become playbooks - Call like any other playbook - Framework handles transport and auth


Decomposing Tasks into Playbooks

Decomposition Strategy

  1. Identify distinct concerns: What are the separable responsibilities?
  2. Look for reusability: What might be called multiple times?
  3. Consider testing: What would you want to test independently?
  4. Find boundaries: Where do soft/hard logic boundaries exist?
  5. Separate I/O from logic: User interaction vs computation

Granularity Guidelines

Too Fine-Grained

## AskName
### Steps
- Ask user for name

## ThankUser($name)
### Steps
- Thank $name

## Main
### Steps
- Ask user for name
- Thank the user

Appropriate

## Main
### Steps
- Ask user for their $name
- Thank $name for providing their name

Too Coarse

## ProcessEverything
### Steps
- Get user info
- Validate info
- Process payment
- Send confirmation
- Update inventory
- Generate report
- Send to accounting
- Archive transaction

Better

## ProcessOrder
### Steps
- Collect user info
- Process payment
- Fulfill order
- Notify stakeholders

## CollectUserInfo
### Steps
- Ask for $name, $email, $address; validate all fields; continue engaging with the user till all fields are valid or user wants to give up
- Return collected info as a dictionary

## ProcessPayment($user_info)
### Steps
- Calculate $amount
- Charge payment method
- Return payment record

## FulfillOrder($payment)
### Steps
- Update inventory
- Generate shipping label
- Send confirmation email

## NotifyStakeholders($payment)
### Steps
- Send to accounting system
- Archive transaction
- Update analytics

When to Create Separate Playbooks

Create separate playbook when:

  • ✅ Logic might be reused elsewhere
  • ✅ Has clear single responsibility
  • ✅ Could be triggered independently
  • ✅ Needs Python for deterministic logic
  • ✅ Represents distinct business process
  • ✅ Makes main flow more readable

Keep inline when:

  • ✅ Used only once
  • ✅ Tightly coupled to parent context
  • ✅ Simple, linear flow
  • ✅ Would hurt readability to separate

Natural Language vs Explicit Syntax

The Spectrum of Explicitness

Playbooks supports a spectrum from pure natural language to Python-like explicit syntax. Prefer natural language unless explicitness aids clarity or correctness.

Variable Assignment

Natural Language (Preferred):

- Ask user for their order id
- Get order details from database
- Calculate shipping cost

Semi-Explicit:

- Ask user for their $order_id
- Get $order_details from database
- Calculate $shipping_cost based on $order_details

Fully Explicit:

- $order_id = AskForOrderId()
- $order_details = GetOrderDetails($order_id)
- $shipping_cost = CalculateShipping($order_details.weight, $order_details.destination)

When to Use Each Style

Pure Natural Language:

- Greet user and ask what they need help with
- Find the most relevant article for their question
- Share the article with friendly explanation

  • ✅ Clear intent
  • ✅ One-time flow
  • ✅ No complex data passing

Variable Names (add clarity):

- Ask user for their $email and $password
- Validate $email format
- If $email is invalid
  - Tell user $email is not valid

  • ✅ Value used multiple times
  • ✅ The variable should become part of the agent's state
  • ✅ Type helps LLM understand

Explicit Calls (precision needed):

- $temp_c = ConvertToCelsius($temp_f)
- $weather = WeatherAgent.GetForecast(zipcode=$zipcode, units="metric")
- $summary = FormatWeather($weather, temperature=$temp_c)

  • ✅ Exact parameters matter
  • ✅ Cross-agent calls
  • ✅ Return value used in computation

Function Call Syntax Options

All of these are valid:

# Most natural
- Get order status

# With variable
- Get $order_status

# Explicit call, implicit args
- GetOrderStatus($order_id)

# Explicit call with named args
- $status = GetOrderStatus(order_id=$order_id)

# Cross-agent call
- $status = OrderService.GetOrderStatus(order_id=$order_id)

# Natural language with explicit args
- Get order status from order service

Guidelines:

  1. Start natural, add explicitness only when needed
  2. Use $variables when value is referenced multiple times
  3. Use explicit calls for cross-agent or when parameters matter
  4. Type annotations helpful for complex data: $results:list, $config:dict

Multi-Agent Programs

When to Use Multiple Agents

Use multiple agents when:

  • ✅ Different domains of expertise (TravelAdvisor, HotelAdvisor, FlightAdvisor)
  • ✅ Different roles in process (Host, Player)
  • ✅ Separation of concerns (Frontend, Backend, Database)
  • ✅ Specialized models needed (FastAgent with GPT-4o-mini, ResearchAgent with Claude)
  • ✅ Independent scaling or deployment
  • ✅ Separation of LLM context and knowledge

Use single agent when:

  • ✅ Shared context and state
  • ✅ Simple workflow
  • ✅ Tight coupling between components

Creating Multiple Agents

Creating agents is easy in Playbooks. Simply add a new H1 tag and describe the agent.

# PrimaryAgent
Description of primary agent

## Main
### Triggers
- At the beginning
### Steps
- Do primary work
- Call SpecializedAgent when needed

# SpecializedAgent  
Description of specialized agent

## ProcessTask($data)
public: true
### Steps
- Process $data with specialized logic
- Return result

Agent Communication Methods

1. Direct Public Playbook Calls

Most common: One agent directly calls another's public playbook

# TaxAgent
## GetTaxRate($income)
public: true
### Steps
- If $income < 100000
  - Return 15% tax rate
- Otherwise
  - Return 25% tax rate

# IncomeAgent
## ProcessIncome
### Steps
- Ask user for $income
- Ask Tax agent what the $tax_rate would be
- Tell user their $tax_rate

When to use:

  • ✅ Synchronous request-response
  • ✅ Well-defined interface
  • ✅ Return value needed immediately

2. Natural Language Messaging

Send message to agent: For asynchronous communication

# Manager
## AssignWork($task)
### Steps
- Decide which worker agent is best suited to handle $task
- Tell selected worker agent to work on $task

# WorkerAgent
## PerformTask($task)
### Steps
- Perform the task

When to use:

  • ✅ Fire-and-forget communication
  • ✅ Event notifications
  • ✅ Async workflows

3. Meetings - Multi-Party Coordination

Host creates meeting: For multi-agent coordination

# RestaurantConsultant
## MenuRedesignMeeting
meeting: true
required_attendees: [HeadChef, MarketingSpecialist]

### Steps
- Introduce myself and explain meeting purpose
- Explain the process we'll follow
- While meeting is active
  - Facilitate discussion
  - Keep discussion on track
  - Enforce max 30 turns
- If consensus reached
  - Summarize decisions
  - End meeting
  - Return menu proposal
- Otherwise
  - Return failure reason

# HeadChef
## MenuRedesignMeeting
meeting: true

### Steps
- Introduce myself
- While meeting is active
  - When asked about dish proposals
    - Propose 3-5 signature dishes with costs
  - When MarketingSpecialist suggests trends
    - Evaluate feasibility
    - Suggest alternatives if needed

# MarketingSpecialist
## MenuRedesignMeeting
meeting: true

### Steps
- Introduce myself
- Present market analysis
- While meeting is active
  - When HeadChef proposes dishes
    - Evaluate market appeal
    - Suggest pricing
  - When consensus difficult
    - Share customer feedback

To create meeting:

- Start a menu redesign meeting with HeadChef and MarketingSpecialist

Meeting mechanics:

  • Each agent needs a playbook with meeting: true
  • Same playbook name across all participating agents
  • Host agent creates meeting, invites attendees
  • Meeting provides shared communication channel
  • Messages visible to all participants
  • Meeting ends when host returns from meeting playbook

When to use meetings:

  • ✅ Multiple agents need to coordinate
  • ✅ Shared context across participants
  • ✅ Back-and-forth discussion needed
  • ✅ Consensus building

Multi-Agent Best Practices

  1. Clear interfaces: Mark playbooks public: true when designed for cross-agent calls
  2. Meaningful agent names: TaxAccountant not Agent2
  3. Document cross-agent contracts: What parameters, what returns
  4. Handle failures: What if agent doesn't respond?
  5. Avoid circular dependencies: Agent A → Agent B → Agent A can deadlock

Triggers: Event-Driven Programming

Triggers are natural language conditions that cause playbooks to execute automatically - like CPU interrupts. They enable declarative, reactive behavior in your agents.

⚠️ Use Sparingly: Triggers add "magic" behavior that can make programs harder to understand. Only use them when they significantly simplify your code.

Why Use Triggers?

Triggers eliminate repetitive coordination code by automatically invoking playbooks when conditions are met. The primary use case is input validation - you can validate user input automatically without cluttering your main flow with validation logic.

When to Use Triggers

DO use triggers for:

  • Input validation - Automatically validate when user provides data
  • State monitoring - React to threshold violations or state changes
  • Cross-cutting concerns - Behavior that applies throughout execution
  • Entry points - "At the beginning" to start your program

DON'T use triggers for:

  • Normal sequential logic (just use steps)
  • One-time checks (inline them instead)
  • Complex workflows (be explicit with function calls)
  • When the trigger condition is unclear

The Validation Pattern: Before & After

WITHOUT Triggers (explicit validation):

## Main
### Steps
- Ask user for their $email
- Validate $email and keep asking until valid
- Ask user for their $pin
- Validate $pin and keep asking until valid
- Process login with $email and $pin

## ValidateEmail($email)
### Steps
- While $email format is invalid
  - Tell user email is invalid
  - Ask for email again
- Return valid email

## ValidatePIN($pin)
### Steps
- While $pin is not 4 digits
  - Tell user PIN must be 4 digits
  - Ask for PIN again
- Return valid PIN

WITH Triggers (automatic validation):

## Main
### Steps
- Ask user for their $email
- Ask user for their $pin
- Process login with $email and $pin

## ValidateEmail
### Triggers
- When user provides email
### Steps
- If $email format is invalid
  - Tell user email is invalid
  - Ask for email again

## ValidatePIN
### Triggers
- When user provides PIN
### Steps
- If $pin is not 4 digits
  - Tell user PIN must be 4 digits
  - Ask for PIN again

Key Benefit: Main flow stays clean. Validation happens automatically whenever user provides input. No need to explicitly call validation at each input point.

Trigger Types

Temporal:

### Triggers
- At the beginning
- After 5 minutes
- At the end

State-Based:

### Triggers
- When $attempts > 3
- When $balance becomes negative
- When $order_status is "shipped"

User Interaction:

### Triggers
- When user provides email
- When user asks about refund
- When user seems frustrated

Execution Flow:

### Triggers
- After calling ProcessPayment
- Before ending program

External Events:

### Triggers
- When payment webhook is received
- When inventory drops below threshold

Agent Communication:

### Triggers
- When another agent asks about availability
- When Manager assigns new task

Common Trigger Patterns

Pattern 1: Input Validation

The most common use case - validate input automatically:

## Main
### Steps
- Ask user for their $order_id till user provides a valid order id
- Process the order

## ValidateOrderId
### Triggers
- When user provides order id
### Steps
- If $order_id is not 8 digits
  - Tell user order ID must be exactly 8 digits
  - Ask for order ID again

Pattern 2: State Guard

Monitor state and react to violations:

## Main
### Steps
- Set $attempts to 0
- While not authenticated
  - Ask user for credentials
  - Increment $attempts
  - Attempt authentication

## CheckAttemptLimit
### Triggers
- When $attempts > 5
### Steps
- Tell user they've exceeded maximum attempts
- End program

Pattern 3: Intent Detection

Respond to user intent automatically:

## Main
### Steps
- Welcome user
- Have conversation about their issue
- Resolve the issue

## ProvideHelp
### Triggers  
- When user asks for help
- When user seems confused
### Steps
- Explain available options and how to use them
- Ask what specific help they need

Pattern 4: RFC-Compliant Validation

Use triggers with detailed validation requirements:

## ValidateEmail
Validates provided email. Email address must conform to addr-spec in Section 3.4 of RFC 5322:
  addr-spec = local-part "@" domain

### Triggers
- When user provides an email
### Steps
- While $email is not valid according to RFC 5322
  - Tell user email format is invalid
  - Ask for email again
  - If user gives up
    - Apologize and end the conversation

How Triggers Simplify Programs

Example: Order Processing with Multiple Validations

WITHOUT Triggers - Repetitive validation calls:

## Main
### Steps
- Ask user for $order_id
- ValidateOrderId($order_id) and keep asking till valid
- Ask user for $email
- ValidateEmail($email) and keep asking till valid
- Ask user for $shipping_address
- ValidateShippingAddress($shipping_address) and keep asking till valid
- Process order

WITH Triggers - Clean main flow:

## Main
### Steps
- Ask user for $order_id
- Ask user for $email
- Ask user for $shipping_address
- Process order

## ValidateOrderId
### Triggers
- When user provides order id
### Steps
- If $order_id is not 8 digits
  - Tell user order ID must be 8 digits
  - Ask again

## ValidateEmail
### Triggers
- When user provides email
### Steps
- If $email format is invalid
  - Tell user email is invalid
  - Ask again

## ValidateShippingAddress
### Triggers
- When user provides shipping address
### Steps
- If $shipping_address is incomplete
  - Tell user what's missing
  - Ask for complete address

Trigger Execution Model

How triggers work:

  1. Main playbook asks for input: "Ask user for their $email"
  2. User provides input
  3. Runtime checks all triggers for matching conditions
  4. First matching trigger fires: "When user provides email"
  5. Validation playbook executes
  6. If validation fails, it asks again (loop)
  7. Control returns to main playbook when input is valid

Important: Triggers are evaluated after each step. The LLM determines when a trigger condition is met based on semantic understanding.

When NOT to Use Triggers

Don't use triggers when explicit flow is clearer:

<!-- BAD: Trigger makes flow unclear -->
## Main
### Steps
- Start processing order

## GetOrderDetails
### Triggers
- After starting to process order
### Steps
- Fetch order from database

<!-- GOOD: Explicit call is clearer -->
## Main
### Steps
- Get order details from database
- Process the order

Don't use triggers for simple one-off checks:

<!-- BAD: Unnecessary trigger -->
## Main
### Steps
- Ask user for $age

## CheckAge
### Triggers
- When user provides age
### Steps
- If $age < 18
  - Tell user must be 18 or older
  - End program

<!-- GOOD: Inline the check -->
## Main
### Steps
- Ask user for $age
- If $age < 18
  - Tell user must be 18 or older
  - End program
- Continue with adult content

Don't overuse triggers:

<!-- BAD: Too many triggers creates "magic" behavior -->
## Main
### Steps
- Start the workflow

## Step1
### Triggers
- When workflow starts
### Steps
- Do step 1

## Step2
### Triggers
- After step 1 completes
### Steps
- Do step 2

## Step3
### Triggers
- After step 2 completes
### Steps
- Do step 3

<!-- GOOD: Explicit sequential flow -->
## Main
### Steps
- Do step 1
- Do step 2
- Do step 3

Trigger Best Practices

  1. Be specific: "When user provides email" not "When email"
  2. Avoid conflicts: Don't create multiple triggers for same condition
  3. Document intent: Explain why the trigger exists in playbook description
  4. Test edge cases: What if trigger fires mid-execution?
  5. Keep it simple: If you have more than 3-4 triggers total, reconsider your design
  6. Prefer explicit: When in doubt, use explicit function calls instead of triggers

Description Placeholders

Inject dynamic content into playbook descriptions using {expression} syntax.

Basic Usage

## ProcessOrder
Processing order {$order_id} for customer {$customer_name}.
Today's date is {date.today().strftime("%Y-%m-%d")}.

When playbook executes:

Processing order 12345 for customer Alice Smith.
Today's date is 2025-10-06.

What You Can Use

Variables:

## ReviewTransaction
Transaction {$transaction_id} with amount ${$amount}

Playbook Calls:

## AnswerQuestions  
Q1 Summary: {QuarterlySummary("Q1")}
Q2 Summary: {QuarterlySummary("Q2")}

### Steps
- Answer questions based on summaries above

Python Expressions:

## AnalyzePerformance
Score: {round($score * 100, 2)}%
Status: {"Excellent" if $score > 0.9 else "Good"}
Timestamp: {timestamp.strftime("%Y-%m-%d %H:%M")}

Special Variables:

## Debug
Current agent: {agent.klass}
Playbook: {call.playbook_name}
Current time: {timestamp}

Common Patterns

Contextual Dates:

## SummarizeOrder($order)
Summarize order considering today is {date.today().strftime("%Y-%m-%d")}

### Steps
- If order is overdue
  - Apologize for delay

Dynamic Lists:

## ProcessItems
Items to process:
{chr(10).join(f"- {item}" for item in $items)}

### Steps
- Process each item

Conditional Context:

## CustomerService
{"Customer is VIP - provide premium service" if $customer.tier == "VIP" else ""}

Best Practices

  1. Evaluate once: Placeholders resolved when playbook starts, not re-evaluated
  2. Keep simple: Complex logic belongs in Python playbooks
  3. Import dependencies: from datetime import date in Python block if needed
  4. Security: No eval, exec, subprocess, __import__

Artifacts and Memory

Artifacts - Persistent Files (alpha feature)

Artifacts are named content that persists across playbook calls.

Save Artifact:

### Steps
- Create report content
- SaveArtifact("report.md", "Monthly Report", $report_content)
- Tell user "Report saved as Artifact[report.md]"

Load Artifact:

### Steps
- LoadArtifact("report.md")
- Summarize the loaded report

Python Usage:

```python
@playbook
async def GenerateReport(data: dict) -> str:
    report = format_report(data)
    await SaveArtifact("report.md", "Sales Report", report)
    return "Artifact[report.md]"

@playbook
async def AnalyzeReport() -> str:
    artifact = await LoadArtifact("report.md")
    # artifact.name, artifact.description, artifact.content
    return analysis
```

When to Use Artifacts

  • ✅ Large content (reports, documents, datasets)
  • ✅ Content referenced multiple times
  • ✅ Content persists across conversation
  • ✅ User needs to download/view separately

Common Patterns and Best Practices

Pattern: Conversational Input Collection

## Main
### Steps
- Ask user for their $email
- Engage in conversation if needed without being pushy
- Once $email is provided, continue

This pattern:

  • Asks for information
  • Handles small talk gracefully
  • Doesn't rudely demand input
  • Continues when criteria met

Pattern: Validation with Triggers

## Main
### Steps
- Ask user for their $pin
- Continue once $pin is valid

## ValidatePIN
### Triggers
- When user provides PIN
### Steps
- If $pin is not 4 digits
  - Tell user PIN must be 4 digits
  - Ask for PIN again

Pattern: Python ↔ Markdown Composition

```python
@playbook
async def FetchData(id: str) -> dict:
    """Get data from external API."""
    response = requests.get(f"https://api.example.com/data/{id}")
    return response.json()

@playbook
async def ProcessItem(item: dict) -> str:
    """Process item and generate summary."""
    # Complex processing logic
    summary = await SummarizeItem(item)  # Call Markdown playbook
    return summary
```

## SummarizeItem($item)
### Steps
- Extract key fields from $item
- Format as user-friendly summary
- Return summary

Pattern: Batch Operations

## ProcessOrders
### Steps
- Get list of $pending_orders from database
- For each $order in $pending_orders
  - ProcessSingleOrder($order)
- Tell user all orders processed

Pattern: Error Handling

## SafeOperation
### Steps
- Try to process $data
- If operation fails
  - Log error
  - Tell user operation failed with reason
  - Return error status
- Otherwise
  - Tell user success
  - Return success status

Pattern: Meeting Facilitation

## ProjectPlanningMeeting
meeting: true
required_attendees: [Developer, Designer, ProductManager]

### Steps
- Welcome everyone and state meeting goal
- Explain agenda and time limit
- Set $max_turns to 20
- Set $turn_count to 0
- While meeting active and $turn_count < $max_turns
  - Facilitate discussion
  - Increment $turn_count
  - Keep discussion on track
- If consensus reached
  - Summarize decisions
  - Assign action items
- End meeting
- Return meeting summary

Pattern: Collecting Inputs from User

When your agent needs information from the user, always ask for all inputs at once and establish a clear conversation loop until all valid data is acquired:

## Main
### Steps
- Ask user for $email and $pin, engage in a conversation till user provides valid values or gives up
- If user gives up, apologize and return
- Inform user that you were able to authenticate them

This pattern:

  • Asks for all required information upfront - more efficient for the user
  • Includes a clear termination condition - handles the case where user wants to exit
  • Validates both inputs before proceeding - ensures data quality
  • Avoids sequential asking - better user experience than asking one at a time

Why this matters: Asking for information piece by piece creates a poor user experience. Users appreciate knowing all requirements upfront.

Pattern: Mock Backend Interactions with Python Playbooks

Think if a certain action would require backend interaction (databases, APIs, authentication services). If so, use Python playbooks to encapsulate that logic. Use mock implementations as necessary to aid in development:

```python
@playbook
async def AuthenticateUser($email: str, $pin: str) -> bool:
    """Authenticate user with email and PIN.

    In production, this would call the actual authentication service.
    For now, using mock data for development.
    """
    # Mock implementation for development
    return $email == "test@test.com" and $pin == "1234"

@playbook  
async def FetchUserProfile($user_id: str) -> dict:
    """Fetch user profile from database.

    Production: Query user database
    Development: Return mock data
    """
    # Mock implementation
    return {
        "id": $user_id,
        "name": "Test User",
        "preferences": {"theme": "dark"}
    }
```

## Main
### Steps
- Ask user for $email and $pin, engage in a conversation till user provides valid values or gives up
- Authenticate user
- If user is authenticated
  - Fetch user profile
  - Welcome user by name from profile
- Otherwise
  - Tell user authentication failed

This pattern:

  • Separates backend logic - keeps the workflow clean and focused
  • Enables rapid prototyping - test workflows without backend dependencies
  • Makes transition to production easier - just swap mock with real implementation
  • Documents backend contracts - clear interface for what backend needs to provide
  • Testable - mock data allows thorough testing of workflows

When to use Python playbooks for backend:

  • Database queries or updates
  • External API calls
  • Authentication/authorization checks
  • Complex business logic calculations
  • File I/O operations
  • Any stateful operations

Best Practices Summary

DO:

  • ✅ Write playbook descriptions for humans - explain what and why
  • ✅ Use natural language unless explicitness helps
  • ✅ Add ### Notes for business rules
  • ✅ Prefer Markdown for workflows, Python for logic
  • ✅ Extract 4+ Python playbooks to MCP server
  • ✅ Make cross-agent playbooks public: true
  • ✅ Use triggers sparingly - mainly for input validation
  • ✅ Handle edge cases and errors gracefully
  • ✅ Use meaningful variable names
  • ✅ Break complex playbooks into smaller ones
  • ✅ Test with different user inputs mentally
  • ✅ Ask for all required information at once with conversation loops
  • ✅ Use mock Python playbooks for backend processes during development

DON'T:

  • ❌ Over-engineer simple flows
  • ❌ Create too many tiny playbooks
  • ❌ Use Raw mode unless truly needed
  • ❌ Ignore error cases
  • ❌ Make circular agent dependencies
  • ❌ Use explicit syntax when natural language is clear
  • ❌ Forget to document cross-agent contracts
  • ❌ Overuse triggers - prefer explicit calls when flow is clearer
  • ❌ Use triggers for sequential logic
  • ❌ Ask for inputs one at a time when you need multiple pieces of information
  • ❌ Mix backend API calls directly into workflow steps

Editing Existing Programs

Surgical Editing Principles

When modifying existing Playbooks programs:

  1. Read and understand: Review the file, understand its structure and intent
  2. Minimal changes: Change only what's needed to achieve the goal
  3. Preserve style: Match existing natural language vs explicit style
  4. Maintain consistency: Keep variable naming and structure patterns
  5. Test mentally: Think through execution flow after changes

Common Edit Patterns

Adding a new playbook:

# Existing Agent

## Existing Playbook
### Steps
- Existing logic

<!-- ADD NEW PLAYBOOK HERE -->
## NewPlaybook($param)
Description

### Steps
- New logic

Modifying steps:

  • Find the playbook to modify
  • Locate specific step
  • Update just that step
  • Ensure variable references still work

Adding a trigger:

## ExistingPlaybook
<!-- ADD THIS SECTION IF MISSING -->
### Triggers
- When condition is met

### Steps
- Existing steps

Adding agent behavior:

# ExistingAgent
<!-- MODIFY THIS DESCRIPTION -->
Existing description. NEW BEHAVIOR: Additional personality trait or capability.

Converting natural to explicit:

<!-- BEFORE -->
- Get order details and tell user

<!-- AFTER (if user requests more structure) -->
- $order = GetOrderDetails($order_id)
- Tell user about $order

Example Edit Request

Request: "Add validation to ensure order ID is 8 digits"

Process:

  1. Locate where order ID is collected
  2. Add validation playbook
  3. Add trigger to invoke validation
  4. Update main flow to handle invalid input
## Main
### Steps
- Ask user for their $order_id till valid  <!-- MODIFIED -->
- Process order

## ValidateOrderId  <!-- NEW PLAYBOOK -->
### Triggers
- When user provides order id
### Steps
- If $order_id is not 8 digits
  - Tell user order ID must be 8 digits
  - Ask for order ID again

Understanding PBAsm Compilation

Why PBAsm Matters

When you write Playbooks, the compiler transforms it to Playbooks Assembly Language (PBAsm):

  • Adds explicit types: $name$name:str
  • Adds line numbers: Hierarchical (01, 01.01, 01.01.01)
  • Adds opcodes: QUE (queue), CND (conditional), RET (return), YLD (yield)
  • Explicit yields: Shows when LLM yields control
  • Trigger labels: T1:BGN, T2:CND, etc.

Compilation Example

Source (Playbooks):

## GreetUser
### Triggers
- At the beginning
### Steps
- Ask user for their name
- If name provided
  - Thank user by name
- Otherwise
  - Ask again

Compiled (PBAsm):

## GreetUser() -> None
### Triggers
- T1:BGN At the beginning
### Steps
- 01:QUE Say(user, Ask user for their $name:str); YLD for user
- 02:CND If $name is provided
  - 02.01:QUE Say(user, Thank user by $name); YLD for call
- 03:CND Otherwise  
  - 03.01:QUE Say(user, Ask for $name again); YLD for user
- 04:RET

Key PBAsm Concepts

Opcodes (like CPU instructions):

  • QUE - Queue operation (function call, Say)
  • CND - Conditional/loop
  • RET - Return from playbook
  • YLD - Yield control to runtime
  • EXE - Execute assignment/action
  • TNK - Think deeply
  • JMP - Jump to line (loops)

Yield Reasons (why LLM pauses):

  • YLD for user - Wait for user input
  • YLD for call - Execute queued calls
  • YLD for agent - Wait for agent message
  • YLD for meeting - Wait for meeting message
  • YLD for exit - End program

Line Numbers (like assembly addresses):

  • Hierarchical: 01, 01.01, 01.02, 01.02.01
  • Enable jumps for loops
  • Track execution position

Why This Helps You

  1. Debugging: When user reports issues, think about PBAsm execution
  2. Precision: Know that fuzzy NL gets converted to structured form
  3. Optimization: Understand when LLM calls happen (at YLD points)
  4. Reasoning: Picture how LLM executes line by line

You Don't Need to Write PBAsm

  • ❌ Never write PBAsm directly
  • ✅ Write natural Playbooks language
  • ✅ Compiler handles transformation
  • ✅ Understanding PBAsm helps debugging and optimization

Quick Reference

Minimal Working Program

# MyAgent
Description of what this agent does

## Main
### Triggers
- At the beginning
### Steps
- Greet user
- Ask user what they need help with
- Help them
- End program

Cheat Sheet

Task Code
Define agent # AgentName
Define playbook ## PlaybookName($param)
Add trigger ### Triggers
- When condition
Add steps ### Steps
- Step 1
Variable $variable_name
Typed variable $count:int
Assignment $var = value
Condition If condition
- Then step
Loop While condition
- Loop step
Return Return value
End program End program
Call playbook PlaybookName($arg)
Cross-agent call OtherAgent.PlaybookName($arg)
Python playbook @playbook
async def Name(): ...
Public playbook public: true
ReAct playbook No ### Steps section
Raw playbook execution_mode: raw
Meeting playbook meeting: true
Save artifact SaveArtifact("name", "desc", $content)
Load artifact LoadArtifact("name")
Placeholder {$variable} or {expression}

Programming Principles

When writing Playbooks programs:

  1. Understand intent: What problem are you solving? What is the goal?
  2. Choose right types: Markdown for workflows, Python for logic, ReAct for research
  3. Natural first: Start with natural language, add explicitness if needed
  4. Think decomposition: Break into logical playbooks with clear responsibilities
  5. Extract to MCP: If you have 4+ Python playbooks, extract them to an MCP server
  6. Handle errors: Consider edge cases and failure modes
  7. Write idiomatically: Follow patterns and conventions from examples
  8. Document choices: Explain intent in descriptions and comments
  9. Iterate: Start simple, add complexity as needed

Remember: You're writing Software 3.0 - programs that execute on LLMs. Embrace natural language while maintaining precision. The compiler and runtime handle the complexity.


For AI Coding Assistants

When helping users write Playbooks programs:

  • Follow all the principles and patterns in this guide
  • Prefer natural language over explicit syntax unless clarity demands it
  • Always consider if 4+ Python playbooks should be extracted to an MCP server
  • Use triggers sparingly - mainly for input validation
  • Explain your architectural choices to help users learn
  • Iterate based on user feedback and requirements

Happy building! 🚀