This guide is for developers and technical founders who want to build a production-grade AI agent in Australia — not a toy prototype. We'll cover the exact stack we use, the decisions that matter, and the compliance patterns that keep you operating legally in regulated Australian industries.
We've shipped three live agents (Finley, Archie and Perry). Everything here is drawn from those builds, not from theory.
The architecture decision: what an AI agent actually is in code
At its simplest, an AI agent is a loop. In production it looks like this:
- User sends a message
- Your server calls the language model API with the message + a list of available tools
- The model decides which tool to call (or responds directly if no tool is needed)
- Your server executes the tool and returns the result to the model
- The model synthesises the result into a response
- Repeat until the goal is complete
This is called the ReAct pattern (Reasoning + Acting). Every major AI agent framework — LangChain, LlamaIndex, Anthropic's own tooling — is an implementation of some variation of this loop.
For production agents, we build this loop ourselves rather than using a framework. Frameworks add abstraction layers that make debugging hard and compliance auditing nearly impossible. When your agent makes a decision that affects someone's financial or insurance situation, you need to know exactly why it made that decision.
The stack
Here's what we use across all three of our agents:
| Layer | Technology | Why |
|---|---|---|
| Language model | Anthropic Claude (claude-sonnet-4-5) | Best tool-calling reliability in production. Constitutional AI principles align with our compliance requirements. Enterprise API with Australian data agreements. |
| Runtime | Node.js 22+ | Async-first, excellent streaming support, large ecosystem for API integrations. |
| Framework | Express.js | Lightweight, predictable, easy to audit. No magic. |
| Database | PostgreSQL on RDS ap-southeast-2 | Australian region. Reliable. ACID compliant for financial data. |
| Hosting | AWS ap-southeast-2 (Sydney) | Australian data sovereignty. Required for Privacy Act compliance when handling personal financial data. |
| Caching | Redis (ElastiCache) | Rate data and static lookups change daily, not per-request. Cache aggressively. |
| Streaming | Server-Sent Events (SSE) | Claude's responses stream token-by-token. SSE is simpler than WebSockets for one-way streaming. |
Setting up the Anthropic Claude API
Get your API key from console.anthropic.com. For production Australian deployments, you should use an enterprise API agreement — contact Anthropic's sales team. Enterprise agreements cover data processing agreements, which you need to satisfy Privacy Act obligations.
import Anthropic from '@anthropic-ai/sdk'; const client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY, // For Australian enterprise: add your data residency config here }); const MODEL = 'claude-sonnet-4-5'; // Use a specific version in production const MAX_TOKENS = 4096;
Defining tools
Tools are what separate an agent from a chatbot. Each tool is a JSON definition that tells the model what function it can call and what parameters that function expects.
const tools = [ { name: 'calculate_borrowing_power', description: 'Calculate maximum borrowing power based on income, expenses and current APRA serviceability buffer. Returns borrowing capacity in AUD.', input_schema: { type: 'object', properties: { gross_annual_income: { type: 'number', description: 'Combined gross annual income in AUD' }, monthly_expenses: { type: 'number', description: 'Total monthly living expenses in AUD' }, existing_debts: { type: 'number', description: 'Total existing monthly debt repayments in AUD' }, deposit_amount: { type: 'number', description: 'Available deposit amount in AUD' } }, required: ['gross_annual_income', 'monthly_expenses', 'deposit_amount'] } }, { name: 'get_lender_rates', description: 'Retrieve current home loan interest rates from Australian lenders. Returns rates for the specified loan type and LVR band.', input_schema: { type: 'object', properties: { loan_amount: { type: 'number', description: 'Loan amount in AUD' }, loan_type: { type: 'string', enum: ['owner_occupier', 'investment'] }, repayment_type: { type: 'string', enum: ['principal_and_interest', 'interest_only'] } }, required: ['loan_amount', 'loan_type'] } } ];
The agent loop
This is the core of the agent — the loop that runs until the model produces a final response with no tool calls:
async function runAgent(userMessage, conversationHistory = []) { const messages = [ ...conversationHistory, { role: 'user', content: userMessage } ]; let response = await client.messages.create({ model: MODEL, max_tokens: MAX_TOKENS, system: SYSTEM_PROMPT, // See compliance section below tools: tools, messages: messages }); // Loop while the model wants to call tools while (response.stop_reason === 'tool_use') { const toolUseBlocks = response.content.filter(b => b.type === 'tool_use'); const toolResults = []; for (const toolUse of toolUseBlocks) { const result = await executeTool(toolUse.name, toolUse.input); toolResults.push({ type: 'tool_result', tool_use_id: toolUse.id, content: JSON.stringify(result) }); } // Add assistant message and tool results to history messages.push({ role: 'assistant', content: response.content }); messages.push({ role: 'user', content: toolResults }); // Continue the loop response = await client.messages.create({ model: MODEL, max_tokens: MAX_TOKENS, system: SYSTEM_PROMPT, tools: tools, messages: messages }); } return response.content.find(b => b.type === 'text')?.text ?? ''; }
The compliance system prompt
This is the part most tutorials skip — and it's the most important part for any Australian agent operating in a regulated space. The system prompt is where you define the agent's identity, capabilities, limitations, and legal guardrails.
For a finance agent, our system prompt includes these key sections:
const SYSTEM_PROMPT = `You are Finley, an AI finance agent for AI Agent Business Australia. IDENTITY: You provide general information about home loans and borrowing in Australia. You are not a licensed financial adviser and do not provide personal financial advice. CAPABILITIES: - Calculate borrowing power using current APRA serviceability requirements - Retrieve current interest rates from the 22 lenders in our comparison network - Explain home loan types, features and terminology in plain English - Help users understand how different scenarios affect their borrowing capacity HARD LIMITS — NEVER: - Recommend a specific product to a specific person - State that one lender is "better" than another for a user's situation - Provide advice on whether someone should buy a property - Reference products from lenders not in our ASIC-registered comparison network COMPLIANCE FOOTER: End every response that includes rate or borrowing information with: "This is general information only, not personal financial advice. Rates shown are indicative and may change. Consult a licensed mortgage broker or financial adviser before making borrowing decisions."`;
Australian hosting requirements
If your agent handles any personal information (names, income figures, addresses, health details), you have obligations under the Privacy Act 1988 and Australian Privacy Principles. The key requirements for hosting:
- Australian or equivalent data residency: Personal data must not be stored in countries without "substantially similar" privacy protections without explicit consent. AWS ap-southeast-2 (Sydney) satisfies this.
- Encryption in transit and at rest: TLS 1.3 minimum for all API calls. AES-256 for stored data.
- Conversation data retention policy: Define and document how long you retain conversation logs. We retain for 90 days for debugging, then delete unless the user has an account.
- Anthropic API data processing: Under enterprise API terms, Anthropic does not use your conversations to train models. Confirm this in your API agreement before deploying.
Streaming responses to the browser
Users expect AI responses to stream in real time. Here's how to pipe Claude's streaming response through to the browser via SSE:
// Express route — streams response to browser app.post('/api/chat', async (req, res) => { res.setHeader('Content-Type', 'text/event-stream'); res.setHeader('Cache-Control', 'no-cache'); res.setHeader('Connection', 'keep-alive'); const stream = client.messages.stream({ model: MODEL, max_tokens: MAX_TOKENS, system: SYSTEM_PROMPT, tools: tools, messages: req.body.messages }); stream.on('text', (text) => { res.write(`data: ${JSON.stringify({ type: 'text', content: text })}\n\n`); }); stream.on('finalMessage', (message) => { res.write(`data: ${JSON.stringify({ type: 'done' })}\n\n`); res.end(); }); stream.on('error', (error) => { res.write(`data: ${JSON.stringify({ type: 'error', message: error.message })}\n\n`); res.end(); }); });
Rate limiting and error handling
Claude's API has rate limits and will occasionally return errors. In production, you need to handle both:
- Exponential backoff on 529 (overloaded) errors — retry with 1s, 2s, 4s delays
- User-facing rate limiting — set a per-user daily token budget to prevent runaway API costs from a single session
- Context window management — Claude Sonnet has a 200,000 token context window, but long conversations cost more. Summarise old turns or trim history beyond N turns.
- Graceful degradation — if the API is unavailable, show the user a clear message and offer to notify them when it's back
Testing your agent before launch
We test agents against three categories of inputs before launch:
Happy path: Normal user inputs that the agent should handle correctly. Run 100 representative queries and check both the accuracy of tool calls and the quality of the response.
Edge cases: Unusual inputs — very large or small numbers, ambiguous queries, queries in different Australian state contexts (e.g. different stamp duty rates), and queries with incomplete information.
Adversarial inputs: Inputs designed to get the agent to break its compliance rules — asking for specific product recommendations, asking it to pretend it's a human adviser, attempting prompt injection via user input. Log anything that slips through and tighten the system prompt.
The full project checklist
- Anthropic enterprise API agreement signed
- Hosting on AWS ap-southeast-2 or equivalent Australian region
- System prompt reviewed by a legal professional
- Tool definitions tested against 100+ real user queries
- Compliance disclaimer present in every regulated output
- Privacy policy published covering conversation data
- Rate limiting and daily token budgets configured
- Error handling and graceful degradation tested
- Streaming tested on slow connections (3G simulation)
- Escalation path to human adviser documented and accessible
Ready to build your own AI agent?
We build compliance-ready AI agents for Australian businesses — from $5,000.
Talk to us about your build →