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
- Memory Lifecycle Guide - Deep dive into memory management
- Custom Domains Guide - Register your own domain
- Helpdesk Example - Another domain example