I use Azure Service Bus to pass work between agents - not to give them unchecked control. For example, an agent can request an order check, but a refund still needs human approval.
My approach covers 3 message types: queues assign work, topics publish results, and subscriptions route those results to the right consumers.
Before going live, I check:
- Message design: Choose Standard or Premium to match the features needed, use versioned contracts, and keep large files outside messages.
- Processing: Use sessions to keep related messages in order. Plan for at-least-once delivery, retries and lock loss without repeating business actions.
- Security: Use managed identities, limit permissions and require approval for refunds.
- Release checks: Deploy settings through existing pipelines, test failures, trace requests and review dead-letter messages.
- Costs: Track Service Bus and agent-processing spend separately in £, with a named budget owner.
The aim is simple: each request reaches the right worker, failures have a recovery path, and agents stay within agreed limits.
::: @figure
{Azure Service Bus AI Agent Message Flow}
:::
Plan Queues, Topics and Message Contracts
Choose Queues, Topics and Subscriptions
With the workflow mapped, choose a queue, topic or subscription for each message path. Before deployment, check that your Service Bus tier supports topics, sessions and duplicate detection.
| Entity | Delivery pattern | Processing model | Suitable workflow |
|---|---|---|---|
| Queue | Sends each message to one consumer | Workers compete for messages | Assigning a background task to an available worker |
| Topic | Distributes messages to subscriptions | Producers publish; workers receive through subscriptions | Sharing an event across several capabilities |
| Subscription | Receives messages that match its filter | Consumers process messages from that subscription | Delivering relevant events to one capability |
Set subscription filters on application properties such as messageType, schemaVersion and recipientType, rather than inspecting the payload. Explicitly configure expiry, maximum delivery count, dead-lettering and duplicate detection.
Once routing is set, standardise the message envelope.
Define a Versioned Message Contract
Keep the envelope stable and put business data in payload. Include messageType, correlationId, conversationId, messageId, schemaVersion, replyTo and expiry. These fields let agents route, correlate and expire messages consistently.
Use messageType, correlationId and conversationId to trace commands and events across asynchronous hops.
| Contract field | Service Bus property | Purpose |
|---|---|---|
messageId |
MessageId |
Identifies each message for deduplication |
correlationId |
CorrelationId |
Links related messages across asynchronous hops |
messageType |
Subject |
Supports routing and filtering |
conversationId |
SessionId |
Ensures ordered processing of related messages |
replyTo |
ReplyTo |
Specifies the queue or topic for responses |
schemaVersion |
Application property | Tracks contract changes and compatibility |
expiry |
TimeToLive |
Sets automatic message expiry |
Validate the envelope before sending and before processing. Check required fields, the schema version and payload size.
Store large documents outside the message and send a secure, authorised reference instead. Check the message size limit for your Service Bus tier, including envelope overhead. Version the schema without breaking existing producers or consumers.
Build Session-Based Agent Workers
Keep Related Messages in Order
With SessionId defined in the contract, workers can process each workflow in order. Enable sessions when creating the queue or subscription, then set SessionId from the message conversationId. Keep it stable throughout the workflow.
When a worker accepts a session, it takes exclusive ownership and processes that session’s messages sequentially. Other workers can process different sessions at the same time. Use session state only for workflow checkpoints.
Handle Retries, Locks and Duplicates
With ordering in place, protect the worker against retries and lock loss. Use the Python azure-servicebus SDK in Peek-Lock mode and settle messages explicitly.
Treat delivery as at-least-once. Make handlers idempotent with a stable business identifier or idempotency token. Don’t assume duplicate detection provides exactly-once processing.
Renew the session lock for long-running tasks. If the lock is lost, stop processing and settling messages. Use exponential back-off with jitter for transient faults, and immediately dead-letter invalid contracts and other non-retryable failures. Set a sensible maximum delivery count before a message is dead-lettered.
These worker rules feed into the identity, deployment and recovery controls in the next section.
Secure, Deploy and Test Agent Integration
Use Managed Identity and Least-Privilege Access
Once message handling is in place, secure access to the bus. Use managed identities for Azure Service Bus to keep agent access separate from application credentials. Assign the Azure Service Bus Data Sender and Data Receiver roles at queue or topic scope to follow least-privilege access.
Deploy Through Existing Release Pipelines
Keep access controls and routing rules consistent across environments. Store namespace, entity and identity settings, along with subscription filters, outside application code. Deploy them through your current application release pipeline.
Test Recovery and Monitor Messages
Before release, test the full agent message flow under failure conditions. Check routing, session ordering, duplicate delivery, lock expiry and dead-letter recovery. After release, monitor failures and dead-letter messages.
Build Async Messaging in .NET with Azure Service Bus (Step-by-Step)
Conclusion: Check Production Readiness
Once routing, security and recovery are in place, make readiness a release gate. Check that the queue, topic and subscription map still matches the business workflow and that contract versions work with existing systems. Under load, session IDs must still send related messages to the same worker, while idempotent processing must prevent repeated business actions.
A production-ready agent flow keeps routing, ordering, security and recovery aligned.
Before go-live, check:
- Contract compatibility: Current consumers can handle the versioned contract, and deployment rollback remains safe.
- Recovery: Failure tests have recorded results.
- Observability: Each business request can be traced across agents and dead-letter paths.
- Access controls: Each managed identity has only the permissions its agent needs.
- Cost ownership: One team owns the Service Bus and agent runtime budget.
If gaps remain, Hokstad Consulting can help with AI strategy and custom agent implementation.
FAQs
How do I handle failed messages without breaking session order?
Use bounded retries with exponential back-off and jitter for transient errors, without blocking other sessions. Once the retry limit is reached - typically three to five attempts - move the failed message to a Dead-Letter Queue (DLQ) for manual review or secondary processing. This lets the session sequence continue.
Make handlers idempotent so duplicate deliveries don’t repeat the same action. Plan exception handling and recovery logic before implementation.
How can I require human approval before an agent issues a refund?
Use an approval-gated workflow that pauses before the agent issues a refund. Save its internal state so it can resume after human approval [1][2].
Show the reviewer the proposed action, then record their identity, decision and the active policy version. Once approval is given, resume the workflow from the saved state [1].
How do I update message contracts without disrupting existing agents?
Prioritise schema discipline and monitoring to prevent silent failures [1]. Define clear schemas with explicit status and metadata fields so routing stays accurate and services don’t overreact to changes [2]. Track schema changes to spot risks when producers alter event structures without telling consumers [1].
Hokstad Consulting recommends strict schema versioning and auditing to stop contract changes from creating inconsistencies that disrupt event-driven orchestration [2][1].