Skip to main content

Budget Analyst Agent — Strands

The Budget Analyst is a Strands agent that calculates event budget breakdowns. It's one of seven specialist agents in the Event Planning Team — a multi-agent system where each agent is built with a different framework and coordinates through Dapr pub/sub.

This agent also demonstrates DaprStateSessionManager — a Strands hook that persists conversation history across invocations using Dapr state management.


Event Planning Team

AgentFrameworkPortPub/Sub Topics
Venue ScoutCrewAI8001venue.requestsvenue.results
Catering CoordinatorOpenAI Agents8002catering.requestscatering.results
Entertainment PlannerGoogle ADK8003entertainment.requestsentertainment.results
Budget AnalystStrands8004budget.requestsbudget.results
Schedule PlannerLangGraph8005schedule.requestsschedule.results
Invitations ManagerDapr Agents8006events.invitations.requests
Event CoordinatorDapr Agents8007Orchestrator
Decoration PlannerPydantic AI8008decorations.requestsdecorations.results

Prerequisites


Agent Code

The Budget Analyst uses Strands' Agent and @tool decorator with a DaprStateSessionManager for conversation history, wrapped in a DaprWorkflowAgentRunner for durable execution and pub/sub messaging.

agents/strands/main.py
import logging
import os

logging.basicConfig(level=logging.DEBUG)

from strands import Agent, tool
from diagrid.agent.strands import DaprWorkflowAgentRunner, DaprStateSessionManager
from diagrid.agent.core.state import DaprStateStore


@tool
def calculate_budget(items: str) -> str:
"""Calculate total budget from a comma-separated list of cost items."""
return (
"Budget breakdown:\n"
"- Venue: $3,500\n"
"- Catering: $2,250\n"
"- Entertainment: $1,500\n"
"- Decorations: $800\n"
"- Miscellaneous: $500\n"
"Total: $8,550\n"
"Recommended buffer (15%): $1,283\n"
"Grand Total: $9,833"
)


# State: persist conversation history across invocations
session_manager = DaprStateSessionManager(
store_name="agent-memory"
)

agent = Agent(
tools=[calculate_budget],
system_prompt="You are a budget analyst specializing in event planning. When asked to estimate costs, use the calculate_budget tool with a comma-separated list of cost items. Return the full budget breakdown with line items, totals, and recommended buffer. Always call the tool before responding.",
hooks=[session_manager],
)

runner = DaprWorkflowAgentRunner(
agent=agent,
state_store=DaprStateStore(store_name="agent-memory"),
)

# PubSub: subscribe for incoming tasks, publish results
runner.serve(
port=int(os.environ.get("APP_PORT", "8004")),
pubsub_name="agent-pubsub",
subscribe_topic="budget.requests",
publish_topic="budget.results",
)

What's happening

  1. @tool — Strands decorator that registers a Python function as a tool the agent can call
  2. DaprStateSessionManager — Strands hook that persists conversation history to Dapr state management, so the agent remembers previous interactions
  3. Agent(..., hooks=[session_manager]) — Strands agent with tools and a session hook for persistent memory
  4. DaprWorkflowAgentRunner — Wraps the agent in a durable workflow that survives crashes and restarts
  5. runner.serve() — Starts an HTTP server that subscribes to budget.requests and publishes results to budget.results via Dapr pub/sub

Dapr Components

The agent uses shared Dapr components for pub/sub messaging, state persistence, and LLM access.

Pub/Sub (agent-pubsub)

resources/agent-pubsub.yaml
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: agent-pubsub
spec:
type: pubsub.redis
version: v1
metadata:
- name: redisHost
value: localhost:6379
- name: redisPassword
value: ""

Routes messages between agents. Locally uses Redis; in production, swap for Kafka, RabbitMQ, or any Dapr pub/sub broker.

State Store (agent-memory)

resources/agent-memory.yaml
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: agent-memory
spec:
type: state.redis
version: v1
metadata:
- name: redisHost
value: localhost:6379
- name: redisPassword
value: ""
- name: actorStateStore
value: "false"

Persists agent memory and conversation state across invocations.

LLM Provider

resources/llm-provider.yaml
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: llm-provider
spec:
type: conversation.openai
version: v1
metadata:
- name: key
value: "{{OPENAI_API_KEY}}"
- name: model
value: gpt-4.1-2025-04-14

Provides LLM access through Dapr's conversation API. The API key is injected from the environment.


Run the Agent

1

Clone the quickstart

git clone https://github.com/diagridio/catalyst-quickstarts.git
cd catalyst-quickstarts/agents/strands
2

Set your API key

export OPENAI_API_KEY="your-key-here"
3

Install dependencies

pip install -r requirements.txt
4

Start the agent

diagrid dev run -f dev-python-strands.yaml
5

Test the agent

Send a budget calculation request:

curl -X POST http://localhost:8888/agent/run \
-H "Content-Type: application/json" \
-d '{"task": "Calculate the budget for a corporate event with venue, catering, and entertainment"}'

Run with the Full Team

To run all seven specialist agents together with the orchestrator, see the Event Planning Team overview.


Key Concepts

ConceptDescription
Durable WorkflowsDaprWorkflowAgentRunner checkpoints every step — agent survives crashes and restarts
Session PersistenceDaprStateSessionManager saves conversation history so the agent remembers prior interactions
Pub/Sub DecouplingAgents communicate through topics, not direct calls. Add or remove agents without code changes.
Portable InfrastructureSwap message brokers and state stores by changing YAML — agent code stays the same

Next Steps