Skip to main content

What are Strategies?

Strategies are decorators that modify agent behavior to add orchestration capabilities:
  • Manual approval - Require human sign-off before agent execution
  • Pick actions - Intelligently select which actions to execute
  • Hand off - Transfer control between agents with shared memory
  • Critique - Have an LLM review and refine agent outputs
  • Conditional - Execute agents based on dynamic conditions
  • Wait - Pause until memory conditions are met
from ziet import Agent, ManualApprovalStrategy, PickActionsStrategy, HandOffStrategy, input

# Agent with manual approval
@ManualApprovalStrategy(signee=input.user_email)
@Agent(
    id="booking_agent",
    name="BookingAgent",
    instructions="Book flight after user approval",
    actions=["book_flight", "charge_payment"]
)
class BookingAgent:
    pass

# Agent that picks best actions dynamically
@PickActionsStrategy(picks=2, allow_repeats=False)
@Agent(
    id="research_agent",
    name="ResearchAgent",
    instructions="Research topic using best available sources",
    actions=["search_google", "search_news", "scrape_web"]
)
class ResearchAgent:
    pass

# Agent that hands off to another agent
@HandOffStrategy(agents=["review_agent"])
@Agent(
    id="draft_agent",
    name="DraftAgent",
    instructions="Create draft and hand off for review",
    actions=["generate_draft"]
)
class DraftAgent:
    pass

Available Strategies

Manual Approval

Require human approval before execution

Wait Strategy

Pause until memory conditions or actions complete

Critique Strategy

LLM reviews and refines output

Pick Actions

Intelligently select which actions to execute

Hand Off

Transfer control to another agent

Conditional

Execute based on conditions

How Strategies Work

Strategies are Python decorators that wrap your agent classes:
@StrategyDecorator(params)
@Agent(
    id="my_agent",
    name="MyAgent",
    instructions="...",
    actions=["action1", "action2"]
)
class MyAgent:
    pass
Execution flow:
  1. Agent is invoked
  2. Strategy intercepts execution
  3. Strategy applies its orchestration logic (approval, action selection, handoff, etc.)
  4. Agent executes with strategy-modified behavior
  5. Results flow through memory

Manual Approval Strategy

Require human approval before executing sensitive agent workflows:
from ziet import Agent, ManualApprovalStrategy, input

@ManualApprovalStrategy(
    signee=input.user_email,  # Who to ask for approval
    method="email"             # How to request: "email" | "slack" | "sms"
)
@Agent(
    id="deletion_agent",
    name="DeletionAgent",
    instructions="""
    Delete user account and all associated data.
    This is a sensitive operation requiring approval.
    """,
    actions=["delete_account", "remove_data", "send_notification"]
)
class DeletionAgent:
    pass
What happens:
  1. Agent is invoked with parameters
  2. Approval request is sent to signee via method
  3. Execution pauses until approved/rejected
  4. If approved: agent executes normally
  5. If rejected: agent execution is cancelled
Use cases:
  • Deleting user data
  • Large financial transactions
  • Production deployments
  • Bulk operations
Learn more →

Wait Strategy

Pause agent execution until memory conditions are met:
from ziet import Agent, WaitStrategy

@WaitStrategy(
    memory=["user_approval", "payment_confirmed"],
    sleep=10,       # Check every 10 seconds
    timeout=3600    # Wait max 1 hour
)
@Agent(
    id="fulfillment_agent",
    name="FulfillmentAgent",
    instructions="""
    Process and fulfill order after approval and payment are confirmed.
    Retrieve 'user_approval' and 'payment_confirmed' from memory.
    """,
    actions=["process_order", "ship_order", "send_tracking"]
)
class FulfillmentAgent:
    pass
What happens:
  1. Agent is invoked
  2. Strategy checks if memory keys exist
  3. If yes: agent executes immediately
  4. If no: execution pauses and checks every sleep seconds
  5. When conditions are met: agent resumes
  6. If timeout reached: agent fails
Use cases:
  • Wait for external webhooks to populate memory
  • Coordinate between multiple agents
  • Wait for human input in memory
  • Multi-stage workflows with dependencies
Learn more →

Critique Strategy

Have an LLM critique and refine agent outputs through multiple iterations:
from ziet import Agent, CritiqueStrategy

@CritiqueStrategy(
    loops=2,           # Number of critique iterations
    model="gpt-4o"     # Model for critique
)
@Agent(
    id="content_agent",
    name="ContentAgent",
    instructions="""
    Generate high-quality marketing email for the product and audience.
    The CritiqueStrategy will refine your output through multiple iterations.
    Store final result in memory with key 'final_email'.
    """,
    actions=["generate_draft", "analyze_tone"],
    model="gpt-4o-mini"
)
class ContentAgent:
    pass
What happens:
  1. Agent executes and produces initial output
  2. LLM (using model parameter) critiques the output
  3. Agent re-executes with critique feedback
  4. Repeat for loops iterations
  5. Final refined output stored in memory
Use cases:
  • Content generation (emails, articles, ads)
  • Data analysis with refinement
  • Research summaries
  • Any task requiring iterative improvement
Learn more →

Pick Actions Strategy

Intelligently select which actions to execute from a pool of available actions:
from ziet import Agent, PickActionsStrategy

@PickActionsStrategy(
    picks=2,              # Execute 2 actions
    allow_repeats=False   # Don't pick same action twice
)
@Agent(
    id="search_agent",
    name="SearchAgent",
    instructions="""
    Research the given topic by searching multiple sources.
    The PickActionsStrategy will intelligently select the best 2 actions
    from available options based on context.
    Available actions: search_google, search_news, scrape_page, etc.
    Store findings in memory for later use.
    """,
    actions=["search_google", "search_news", "scrape_page"]
)
class SearchAgent:
    pass
What happens:
  1. Agent has access to multiple actions
  2. Strategy analyzes context and picks N actions to execute
  3. Selected actions are called
  4. Results are returned
Parameters:
  • picks: Number of actions to execute (default: 1)
  • allow_repeats: Whether same action can be picked multiple times (default: False)
Use cases:
  • Dynamic action selection based on context
  • A/B testing different actions
  • Load balancing across similar actions
  • Adaptive workflows

Hand Off Strategy

Transfer control from one agent to another:
from ziet import Agent, HandOffStrategy

@HandOffStrategy(agents=["booking_agent", "refund_agent"])
@Agent(
    id="search_agent",
    name="SearchAgent",
    instructions="""
    Search for flights between origin and destination.
    Store results in durable memory with key 'flight_options'.
    Hand off to booking_agent when search is complete.
    Memory persists across the handoff.
    """,
    actions=["search_flights"]
)
class SearchAgent:
    pass


@HandOffStrategy(agents=["search_agent"])
@Agent(
    id="booking_agent",
    name="BookingAgent",
    instructions="""
    Retrieve flight options from memory (key: 'flight_options').
    Book the best flight option.
    Memory from previous agent (SearchAgent) is available.
    Store booking confirmation in memory.
    """,
    actions=["book_flight"]
)
class BookingAgent:
    pass
What happens:
  1. Agent A completes its work
  2. Agent A specifies which agent to hand off to
  3. Memory is preserved (durable across agents)
  4. Agent B starts with access to shared memory
  5. Agent B continues the workflow
Parameters:
  • agents: List of agent IDs that can be handed off to
Use cases:
  • Multi-stage workflows (search → review → book)
  • Specialized agents for different tasks
  • Human-in-the-loop workflows (agent → approval → agent)
  • Complex orchestration patterns
Learn more →

Conditional Strategy

Execute agents or actions based on dynamic conditions:
from ziet import Agent, ConditionalStrategy

@ConditionalStrategy(condition="input.user_type == 'premium'")
@Agent(
    id="premium_agent",
    name="PremiumAgent",
    instructions="Handle premium user requests with priority features",
    actions=["premium_action_1", "premium_action_2"]
)
class PremiumAgent:
    pass


@ConditionalStrategy(condition="memory.get('score') > 80")
@Agent(
    id="highscore_agent",
    name="HighScoreAgent",
    instructions="Process high-scoring results",
    actions=["advanced_analysis"]
)
class HighScoreAgent:
    pass


# String matching conditions
@ConditionalStrategy(condition="input.answer == 'blue'")
@Agent(
    id="blue_agent",
    name="BlueAgent",
    instructions="Handle blue answers",
    actions=["process_blue"]
)
class BlueAgent:
    pass


# Complex conditions
@ConditionalStrategy(
    condition="memory.get('payment_status') == 'confirmed' and input.amount < 1000"
)
@Agent(
    id="autoapprove_agent",
    name="AutoApproveAgent",
    instructions="Auto-approve small confirmed payments",
    actions=["approve_payment", "send_receipt"]
)
class AutoApproveAgent:
    pass
What happens:
  1. Condition is evaluated before agent executes
  2. If condition is True: agent executes normally
  3. If condition is False: agent is skipped
  4. Supports Python expressions for conditions
Condition syntax:
  • input.field - Access request input fields
  • memory.get('key') - Access memory values
  • Standard Python operators: ==, !=, >, <, >=, <=, and, or, not
  • String matching: input.answer == 'blue'
  • Numeric comparisons: input.amount > 100
  • Boolean logic: input.active and memory.get('verified')
Use cases:
  • Route to different agents based on user type
  • Skip processing if conditions aren’t met
  • A/B testing with conditional execution
  • Dynamic workflow branching
Examples:
# Example 1: User type routing
@ConditionalStrategy(condition="input.user_type == 'enterprise'")
@Agent(
    id="enterprise_agent",
    name="EnterpriseAgent",
    instructions="Handle enterprise customers with dedicated support",
    actions=["priority_support", "custom_solutions"]
)
class EnterpriseAgent:
    pass


# Example 2: Score-based processing
@ConditionalStrategy(condition="memory.get('confidence_score') > 0.9")
@Agent(
    id="highconfidence_agent",
    name="HighConfidenceAgent",
    instructions="Auto-process high confidence results without review",
    actions=["auto_approve"]
)
class HighConfidenceAgent:
    pass


# Example 3: Multi-condition logic
@ConditionalStrategy(
    condition="input.region == 'US' and memory.get('inventory') > 0"
)
@Agent(
    id="fulfillment_agent",
    name="USFulfillmentAgent",
    instructions="Fulfill orders for US region with available inventory",
    actions=["create_shipment", "notify_customer"]
)
class USFulfillmentAgent:
    pass


# Example 4: String matching
@ConditionalStrategy(condition="input.language in ['en', 'es', 'fr']")
@Agent(
    id="multilingual_agent",
    name="MultilingualAgent",
    instructions="Process supported languages",
    actions=["translate", "respond"]
)
class MultilingualAgent:
    pass


# Example 5: Negation
@ConditionalStrategy(condition="not memory.get('already_processed')")
@Agent(
    id="firsttime_agent",
    name="FirstTimeProcessor",
    instructions="Only process if not already done",
    actions=["process_data", "mark_complete"]
)
class FirstTimeProcessor:
    pass
Combining with other strategies:
@ConditionalStrategy(condition="input.amount > 10000")
@ManualApprovalStrategy(signee=input.approver_email)
@Agent(
    id="largetransaction_agent",
    name="LargeTransactionAgent",
    instructions="Handle large transactions with approval",
    actions=["process_payment", "send_notification"]
)
class LargeTransactionAgent:
    pass
# Only executes if amount > 10000, then requires manual approval
Learn more →

Combining Strategies

Stack multiple strategies on one agent for complex orchestration:
from ziet import Agent, ManualApprovalStrategy, WaitStrategy, CritiqueStrategy, input

@ManualApprovalStrategy(signee=input.user_email)
@WaitStrategy(memory=["payment_confirmed"])
@CritiqueStrategy(loops=1)
@Agent(
    id="invoicing_agent",
    name="InvoicingAgent",
    instructions="""
    Generate and send invoice with multiple safeguards:
    1. Wait for 'payment_confirmed' in memory (WaitStrategy)
    2. Generate invoice with AI refinement (CritiqueStrategy)
    3. Require user approval before sending (ManualApprovalStrategy)
    4. Send invoice to customer
    """,
    actions=["generate_invoice", "send_invoice_email"],
    model="gpt-4o-mini"
)
class InvoicingAgent:
    pass
Execution order:
  • Outermost decorator executes first (ManualApproval)
  • Then middle (WaitStrategy)
  • Then innermost (CritiqueStrategy)
  • Finally the agent executes with all strategies applied

Using Input References

Reference agent input fields in strategy parameters:
from ziet import Agent, ManualApprovalStrategy, input

@ManualApprovalStrategy(signee=input.user_email)
@Agent(
    id="approval_agent",
    name="ApprovalAgent",
    instructions="""
    Execute sensitive workflow with user approval.
    The signee for approval is dynamically set from the request.
    """,
    actions=["sensitive_action", "log_activity"]
)
class ApprovalAgent:
    pass

# When invoked:
# {
#   "user_email": "manager@company.com",  # Used as signee
#   "data": {...}
# }
The input object provides references to request fields:
  • input.user_emailrequest["user_email"]
  • input.phonerequest["phone"]
  • input.slack_channelrequest["slack_channel"]
  • Any field from the incoming request

Agent Orchestration Patterns

Approval Gate

Require approval before critical agent workflows:
from ziet import Agent, ManualApprovalStrategy, input

@ManualApprovalStrategy(signee=input.approver_email)
@Agent(
    id="deployment_agent",
    name="DeploymentAgent",
    instructions="Deploy application to production environment",
    actions=["run_tests", "deploy_code", "verify_deployment"]
)
class DeploymentAgent:
    pass

Multi-Agent Coordination

Agents can hand off to each other with shared memory:
from ziet import Agent, HandOffStrategy

# Agent 1: Processes payment
@HandOffStrategy(agents=["shipping_agent"])
@Agent(
    id="payment_agent",
    name="PaymentAgent",
    instructions="""
    Process payment and store result in memory with key 'payment_confirmed'.
    Then hand off to shipping_agent.
    """,
    actions=["charge_payment", "verify_transaction"]
)
class PaymentAgent:
    pass

# Agent 2: Waits for payment, then ships
@WaitStrategy(memory=["payment_confirmed"])
@Agent(
    id="shipping_agent",
    name="ShippingAgent",
    instructions="""
    Wait for 'payment_confirmed' in memory, then ship the order.
    """,
    actions=["prepare_shipment", "create_label", "dispatch"]
)
class ShippingAgent:
    pass

Iterative Refinement

Agent improves its output through critique loops:
from ziet import Agent, CritiqueStrategy

@CritiqueStrategy(loops=3, model="gpt-4o")
@Agent(
    id="writer_agent",
    name="WriterAgent",
    instructions="""
    Write a high-quality blog post on the given topic for the target audience.
    The CritiqueStrategy will refine your output through 3 iterations.
    Store final post in memory with key 'blog_post'.
    """,
    actions=["research_topic", "generate_outline", "write_content"],
    model="gpt-4o-mini"
)
class WriterAgent:
    pass

Approval + Critique

Combine strategies for high-quality, approved agent workflows:
from ziet import Agent, ManualApprovalStrategy, CritiqueStrategy, input

@ManualApprovalStrategy(signee=input.manager_email)
@CritiqueStrategy(loops=2)
@Agent(
    id="communication_agent",
    name="CommunicationAgent",
    instructions="""
    Generate customer communication email.
    1. CritiqueStrategy refines the content (2 loops)
    2. ManualApprovalStrategy requires manager approval
    3. After approval, send the email
    """,
    actions=["generate_email", "send_email"],
    model="gpt-4o"
)
class CommunicationAgent:
    pass

Dashboard Management

View and manage pending approvals in the dashboard:
1

View Pending Approvals

Navigate to DashboardApprovals to see all pending approval requests.
2

Review Details

Click on an approval to see:
  • Action name and description
  • Input parameters
  • Requester information
  • Timestamp
3

Approve or Reject

Click Approve or Reject with optional comment.The action will resume or be cancelled accordingly.

Best Practices

Always require approval for agent workflows that can’t be undone
@ManualApprovalStrategy(signee=input.admin_email)
@Agent(
    id="deletion_agent",
    name="DeletionAgent",
    instructions="Delete database and all associated resources",
    actions=["backup_data", "delete_database", "cleanup_resources"]
)
class DeletionAgent:
    pass
Don’t wait forever - set realistic timeouts for WaitStrategy
@WaitStrategy(
    memory=["approval"],
    timeout=7200  # 2 hours max
)
@Agent(
    id="processing_agent",
    name="ProcessingAgent",
    instructions="Wait for approval in memory, then process order",
    actions=["validate_order", "process_payment", "ship_order"]
)
class ProcessingAgent:
    pass
More loops = higher cost and latency
# Good: 1-3 loops
@CritiqueStrategy(loops=2)
@Agent(
    id="content_agent",
    name="ContentAgent",
    instructions="Generate content with 2 refinement iterations",
    actions=["generate_content"],
    model="gpt-4o-mini"
)
class ContentAgent:
    pass

# Avoid: Too many loops
@CritiqueStrategy(loops=10)  # Expensive!
@Agent(
    id="content_agent",
    name="ContentAgent",
    instructions="...",
    actions=["generate_content"]
)
class ContentAgent:
    pass
Strategies orchestrate entire agent workflows, not individual actions
# ✅ Good: Strategy on agent
@ManualApprovalStrategy(signee=input.user_email)
@Agent(
    id="booking_agent",
    name="BookingAgent",
    instructions="Complete booking workflow after approval",
    actions=["book_flight", "charge_payment", "send_confirmation"]
)
class BookingAgent:
    pass

# ❌ Bad: Don't put strategies on actions
# Actions are simple units of work
# Strategies belong on agents for orchestration

Limitations

Current limitations:
  • Max timeout: 24 hours for WaitStrategy
  • Critique cost: Each loop uses LLM tokens
  • Approval methods: Email, Slack, SMS (more coming)
  • No custom strategies yet: Only built-in strategies available

Complete Example: Multi-Agent Flight Booking

This example demonstrates a complete multi-agent workflow with handoffs, strategies, and durable memory:
from ziet import Action, Agent, memory, input
from ziet import PickActionsStrategy, CritiqueStrategy, HandOffStrategy, ManualApprovalStrategy
from ziet.integrations import google, apify, openai, stripe, sendgrid

# ============================================
# ACTIONS
# ============================================

@Action(
    id="search_google_flights",
    name="Search Google for Flights",
    description="Search Google for flights",
    timeout=30
)
def search_google_flights(origin: str, dest: str, date: str) -> None:
    query = f"flights from {origin} to {dest} on {date}"
    results = google.search(query, num_results=10)
    memory.add(key="google_flight_results", value=results)

@Action(
    id="search_flight_sites",
    name="Search Flight Sites",
    description="Search flight comparison sites",
    timeout=30
)
def search_flight_sites(origin: str, dest: str) -> None:
    # Search Kayak, Expedia, etc.
    results = google.search(f"{origin} to {dest} kayak expedia")
    memory.add(key="comparison_site_results", value=results)

@Action(
    id="scrape_airline",
    name="Scrape Airline",
    description="Scrape airline website",
    timeout=60
)
def scrape_airline(url: str) -> None:
    data = apify.scrape(url)
    scraped_data = {
        "url": url,
        "content": data.get("text", "")[:1000],
        "prices": data.get("extracted", {})
    }
    memory.add(key=f"scraped_{url}", value=scraped_data)

@Action(
    id="book_flight",
    name="Book Flight",
    description="Book flight",
    timeout=60
)
def book_flight() -> None:
    # Get recommended flight from memory
    flight = memory.get("recommended_flight")
    
    # Book the flight
    booking = {
        "booking_id": f"BK{flight['id']}",
        "flight": flight,
        "status": "confirmed"
    }
    memory.add(key="final_booking", value=booking)

@Action(
    id="charge_payment",
    name="Charge Payment",
    description="Charge payment",
    timeout=30
)
def charge_payment(amount: int, email: str) -> None:
    payment = stripe.create_payment_intent(
        amount=amount * 100,
        currency="usd",
        metadata={"customer": email}
    )
    memory.add(key="payment_result", value=payment)

@Action(
    id="send_confirmation",
    name="Send Confirmation",
    description="Send confirmation email",
    timeout=20
)
def send_confirmation(email: str) -> None:
    # Retrieve booking from memory
    booking = memory.get("final_booking")
    
    sendgrid.send(
        to=email,
        subject=f"Flight Booked - {booking['booking_id']}",
        body=f"<h1>Booking Confirmed</h1><p>Booking ID: {booking['booking_id']}</p>",
        html=True
    )
    memory.add(key="confirmation_sent", value={"email": email, "sent": True})


# ============================================
# AGENT 1: FLIGHT SEARCHER
# ============================================

@PickActionsStrategy(picks=2, allow_repeats=False)
@HandOffStrategy(agents=["reviewer_agent"])
@Agent(
    id="searcher_agent",
    name="FlightSearcher",
    description="Search for flight options using multiple strategies",
    instructions="""
    You are a flight search agent. Your goal is to:
    1. Search multiple sources for flights (Google, comparison sites)
    2. Use the PickActionsStrategy to select the 2 best search methods
    3. Gather at least 10-20 flight options
    4. Store initial request in memory with key 'initial_request'
    5. Combine all results and store in memory with key 'all_flight_options'
    6. Hand off to reviewer_agent when search is complete
    
    Prioritize variety in search sources for comprehensive results.
    The PickActionsStrategy will intelligently select from available actions:
    - search_google_flights: stores results in 'google_flight_results'
    - search_flight_sites: stores results in 'comparison_site_results'
    - scrape_airline: stores results in 'scraped_{url}'
    
    After actions complete, retrieve from memory and combine into 'all_flight_options'.
    """,
    actions=["search_google_flights", "search_flight_sites", "scrape_airline"],
    model="gpt-4o-mini"
)
class FlightSearcherAgent:
    pass


# ============================================
# AGENT 2: FLIGHT REVIEWER
# ============================================

@CritiqueStrategy(loops=2, model="gpt-4o")
@HandOffStrategy(agents=["booker_agent"])
@Agent(
    id="reviewer_agent",
    name="FlightReviewer",
    description="Review and analyze flight options",
    instructions="""
    You are a flight analysis expert. Your task:
    1. Retrieve all flight options from memory using key 'all_flight_options'
    2. Retrieve initial request from memory using key 'initial_request'
    3. Analyze each option considering price, duration, stops, airline quality
    4. Recommend the top 3 options with detailed reasoning
    5. The CritiqueStrategy will refine your analysis through 2 loops
    6. Store the final analysis in memory with key 'flight_analysis'
    7. Store the top recommendation in memory with key 'recommended_flight'
    8. Hand off to booker_agent when analysis is complete
    
    Consider overall value, not just price. Factor in convenience, reliability, 
    and travel experience. The CritiqueStrategy will help you refine your analysis.
    """,
    actions=[],
    model="gpt-4o"
)
class FlightReviewerAgent:
    pass


# ============================================
# AGENT 3: FLIGHT BOOKER
# ============================================

@ManualApprovalStrategy(signee=input.user_email, method="email")
@Agent(
    id="booker_agent",
    name="FlightBooker",
    description="Book flight after manual approval",
    instructions="""
    You are responsible for the final booking. Your workflow:
    1. Retrieve recommended flight from memory using key 'recommended_flight'
    2. Retrieve flight analysis from memory using key 'flight_analysis'
    3. Retrieve initial request from memory using key 'initial_request'
    4. The ManualApprovalStrategy will send approval request to user via email
    5. WAIT for user approval (execution pauses automatically)
    6. After approval: charge payment using charge_payment action
    7. Book the flight using book_flight action (retrieves 'recommended_flight' from memory)
    8. Send confirmation email using send_confirmation action (retrieves 'final_booking' from memory)
    9. Store final result in memory with key 'final_result'
    
    Only proceed with payment after explicit approval. The ManualApprovalStrategy
    handles the approval workflow automatically. If the user rejects, execution stops.
    All actions store their results in memory for traceability.
    """,
    actions=["book_flight", "charge_payment", "send_confirmation"],
    model="gpt-4o-mini"
)
class FlightBookerAgent:
    pass


# ============================================
# EXECUTION
# ============================================

# Start the workflow by calling searcher_agent
# Memory persists across all agent handoffs
# Each agent is stateless but shares durable memory

"""
Execution Flow:

1. searcher_agent (PickActionsStrategy + HandOffStrategy)
   - Intelligently picks 2 search actions from available pool
   - Searches multiple sources (Google, flight sites, airline scraping)
   - Stores results in memory: 'all_flight_options', 'initial_request'
   - Hands off to reviewer_agent
   - Memory persists across handoff

2. reviewer_agent (CritiqueStrategy + HandOffStrategy)
   - Reads flight options from memory
   - Analyzes with GPT-4
   - CritiqueStrategy refines analysis (2 loops)
   - Stores refined analysis in memory: 'flight_analysis', 'recommended_flight'
   - Hands off to booker_agent
   - Memory persists across handoff

3. booker_agent (ManualApprovalStrategy)
   - Reads recommended flight from memory
   - ManualApprovalStrategy sends approval request via email
   - Execution pauses waiting for approval
   - After approval: charges payment, books flight, sends confirmation
   - Stores final result in memory: 'final_result'

Memory is durable throughout:
- If any step fails, memory persists
- Agents can retry from last successful step
- No data loss across handoffs
- Each agent is stateless but accesses shared memory
"""

Running the Multi-Agent Workflow

# Deploy all agents
ziet deploy

# Start the workflow (calls searcher_agent first)
ziet run searcher_agent \
  --origin "SFO" \
  --dest "NYC" \
  --date "2024-12-25" \
  --user_email "user@example.com" \
  --max_price 600
Execution Flow:
  1. searcher_agent runs
    • PickActionsStrategy selects 2 best search actions dynamically
    • Searches Google and comparison sites
    • Stores 20+ flight options in memory
    • Hands off to reviewer_agent
  2. reviewer_agent runs
    • Reads flight options from memory
    • Analyzes with GPT-4
    • CritiqueStrategy refines analysis (2 iterations)
    • Stores top recommendation in memory
    • Hands off to booker_agent
  3. booker_agent pauses
    • ManualApprovalStrategy sends approval email to user
    • Execution pauses, waiting for approval…
  4. User approves via email
  5. booker_agent resumes
    • Charges payment via Stripe
    • Books flight
    • Sends confirmation email
    • Returns final result

Key Features Demonstrated

Stateless agents - Each agent step is independent
Durable memory - Persists across all handoffs and failures
PickActionsStrategy - Intelligently selects search methods
CritiqueStrategy - Refines analysis through iteration
HandOffStrategy - Seamless agent-to-agent transitions
ManualApprovalStrategy - Human-in-the-loop approval
Multi-tenant - Memory scoped to run_id
Fault-tolerant - Can retry from any point

Coming Soon

We’re adding more strategies:
  • RateLimitStrategy - Throttle action execution
  • CacheStrategy - Auto-cache action results
  • FallbackStrategy - Automatic fallback on failure
  • ParallelStrategy - Execute actions in parallel
  • CustomStrategy - Define your own strategies

Next Steps