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¶
- Core Concepts
- Language Syntax and Formatting
- The Five Playbook Types
- Decomposing Tasks into Playbooks
- Natural Language vs Explicit Syntax
- Multi-Agent Programs
- Triggers: Event-Driven Programming
- Description Placeholders
- Artifacts and Memory
- Common Patterns and Best Practices
- Editing Existing Programs
- 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
H2 Tags (##
) - Playbook Definitions
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
Variables - Always prefixed with $
Type Annotations - Optional but helpful for clarity
Comments
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:
- 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")
- Run the MCP server:
- 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¶
- Identify distinct concerns: What are the separable responsibilities?
- Look for reusability: What might be called multiple times?
- Consider testing: What would you want to test independently?
- Find boundaries: Where do soft/hard logic boundaries exist?
- 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 ✅
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):
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:
- Start natural, add explicitness only when needed
- Use
$variables
when value is referenced multiple times - Use explicit calls for cross-agent or when parameters matter
- 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:
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¶
- Clear interfaces: Mark playbooks
public: true
when designed for cross-agent calls - Meaningful agent names:
TaxAccountant
notAgent2
- Document cross-agent contracts: What parameters, what returns
- Handle failures: What if agent doesn't respond?
- 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:
State-Based:
### Triggers
- When $attempts > 3
- When $balance becomes negative
- When $order_status is "shipped"
User Interaction:
Execution Flow:
External Events:
Agent Communication:
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:
- Main playbook asks for input: "Ask user for their $email"
- User provides input
- Runtime checks all triggers for matching conditions
- First matching trigger fires: "When user provides email"
- Validation playbook executes
- If validation fails, it asks again (loop)
- 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¶
- Be specific: "When user provides email" not "When email"
- Avoid conflicts: Don't create multiple triggers for same condition
- Document intent: Explain why the trigger exists in playbook description
- Test edge cases: What if trigger fires mid-execution?
- Keep it simple: If you have more than 3-4 triggers total, reconsider your design
- 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:
What You Can Use¶
Variables:
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:
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:
Best Practices¶
- Evaluate once: Placeholders resolved when playbook starts, not re-evaluated
- Keep simple: Complex logic belongs in Python playbooks
- Import dependencies:
from datetime import date
in Python block if needed - 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:
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:
- Read and understand: Review the file, understand its structure and intent
- Minimal changes: Change only what's needed to achieve the goal
- Preserve style: Match existing natural language vs explicit style
- Maintain consistency: Keep variable naming and structure patterns
- 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:
- Locate where order ID is collected
- Add validation playbook
- Add trigger to invoke validation
- 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/loopRET
- Return from playbookYLD
- Yield control to runtimeEXE
- Execute assignment/actionTNK
- Think deeplyJMP
- Jump to line (loops)
Yield Reasons (why LLM pauses):
YLD for user
- Wait for user inputYLD for call
- Execute queued callsYLD for agent
- Wait for agent messageYLD for meeting
- Wait for meeting messageYLD 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¶
- Debugging: When user reports issues, think about PBAsm execution
- Precision: Know that fuzzy NL gets converted to structured form
- Optimization: Understand when LLM calls happen (at YLD points)
- 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:
- Understand intent: What problem are you solving? What is the goal?
- Choose right types: Markdown for workflows, Python for logic, ReAct for research
- Natural first: Start with natural language, add explicitness if needed
- Think decomposition: Break into logical playbooks with clear responsibilities
- Extract to MCP: If you have 4+ Python playbooks, extract them to an MCP server
- Handle errors: Consider edge cases and failure modes
- Write idiomatically: Follow patterns and conventions from examples
- Document choices: Explain intent in descriptions and comments
- 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! 🚀