Skip to main content

Example: Workflow Agent

This example builds an employee onboarding workflow agent. Unlike a one-shot task, this agent handles the same type of request repeatedly, so memory is central: each completed onboarding makes the next one faster and more reliable.

The Domain

An HR team needs to onboard new employees. Each onboarding involves: document collection, account provisioning, equipment assignment, training enrollment, and manager check-in. The workflow varies by department and role.

Setup

from masar import MasarClient

client = MasarClient()

# Register the domain (one-time setup)
client.domains.register(
    name="onboarding",
    entity_types=["Employee", "Document", "Account", "Equipment", "Training"],
    state_labels=["pending", "in_progress", "completed", "blocked", "skipped"],
    event_labels=["START", "SUBMIT", "APPROVE", "PROVISION", "ASSIGN", "ENROLL", "COMPLETE"]
)

First Onboarding: No Memory

def handle_onboarding(request: dict):
    # Check memory
    memories = client.memory.recall(
        context=f"Onboard {request['role']} in {request['department']}",
        domain="onboarding"
    )

    if memories.pattern:
        print(f"Following pattern: {memories.pattern.name}")
        plan = client.plan_instructions(
            current={"name": "Onboarding", "orbitals": []},
            goal=memories.pattern.schema,
            domain="onboarding"
        )
    else:
        print("No pattern found. Planning from scratch.")
        plan = client.plan_instructions(
            current={"name": "Onboarding", "orbitals": []},
            goal="full-onboarding",
            domain="onboarding"
        )

    # Execute the plan
    schema = execute_plan(plan)

    # Verify
    check = client.verify(schema=schema, domain="onboarding")

    # Store the episode
    client.memory.store(
        domain="onboarding",
        context=f"Onboard {request['role']} in {request['department']}",
        schema=schema,
        outcome="success" if check.valid else "failure",
        metadata={"department": request["department"], "role": request["role"]}
    )

    return schema

Week 1: Building Experience

# Monday
handle_onboarding({"role": "engineer", "department": "platform"})  # 12 steps, 8.2s
handle_onboarding({"role": "engineer", "department": "frontend"})  # 12 steps, 7.9s

# Wednesday
handle_onboarding({"role": "designer", "department": "product"})   # 10 steps, 6.5s

# Friday: compress the week's episodes
client.memory.compress(domain="onboarding")
# Extracted 2 patterns: "engineer-onboarding", "designer-onboarding"

Week 4: Pattern-Driven

handle_onboarding({"role": "engineer", "department": "data"})
# Output:
# Following pattern: engineer-onboarding (similarity: 0.96)
# 4 steps (skipped 8 known-good steps), 2.1s

The agent now completes engineer onboarding in 2 seconds instead of 8. It still plans (to handle department-specific variations) but uses the pattern as a starting point instead of building from zero.

Handling Variations

When a new role appears that doesn't match existing patterns:

handle_onboarding({"role": "legal-counsel", "department": "legal"})
# Output:
# No pattern found. Planning from scratch.
# 14 steps, 9.8s (includes compliance-specific states)

After a few legal onboardings, a new "legal-onboarding" pattern emerges. The agent self-organizes its expertise by role category.

Monitoring Memory Health

stats = client.memory.stats(domain="onboarding")
print(f"Episodes: {stats.total_episodes}")
print(f"Patterns: {stats.patterns}")
print(f"Storage: {stats.storage_used_mb:.1f} MB")

# Periodic cleanup
client.memory.forget(domain="onboarding", older_than_days=180, min_relevance=0.2)

Next Steps