Skip to content

Simple ARAL Agent Example

This is a complete, runnable example of a minimal ARAL-CORE agent. It demonstrates all five foundational layers in under 300 lines of code.

Information

Copy and run this example to see ARAL in action. Requires Node.js 18+ or Python 3.10+.


Terminal window
npm init -y
npm install @aral/sdk openai
npm install -D typescript @types/node tsx
simple-agent.ts
import { EventEmitter } from 'events';
import OpenAI from 'openai';
// ==========================================
// LAYER 1: RUNTIME
// ==========================================
class SimpleRuntime extends EventEmitter {
private isRunning = false;
async start(): Promise<void> {
this.isRunning = true;
console.log('✅ Runtime started');
this.emit('started');
}
async stop(): Promise<void> {
this.isRunning = false;
console.log('✅ Runtime stopped');
this.emit('stopped');
}
healthCheck(): { status: string; uptime: number } {
return {
status: this.isRunning ? 'healthy' : 'stopped',
uptime: process.uptime()
};
}
async executeWithRetry<T>(
fn: () => Promise<T>,
maxRetries: number = 3
): Promise<T> {
let lastError: Error;
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
return await fn();
} catch (error) {
lastError = error as Error;
if (attempt < maxRetries) {
const delay = Math.min(1000 * Math.pow(2, attempt), 10000);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
throw lastError!;
}
}
// ==========================================
// LAYER 2: MEMORY
// ==========================================
interface MemoryEntry {
role: 'user' | 'assistant';
content: string;
timestamp: number;
}
class SimpleMemory {
private messages: Map<string, MemoryEntry[]> = new Map();
private maxMessages = 20;
async remember(userId: string, role: 'user' | 'assistant', content: string): Promise<void> {
if (!this.messages.has(userId)) {
this.messages.set(userId, []);
}
const userMessages = this.messages.get(userId)!;
userMessages.push({
role,
content,
timestamp: Date.now()
});
// Prune old messages (FIFO strategy)
if (userMessages.length > this.maxMessages) {
userMessages.shift();
}
}
async recall(userId: string): Promise<MemoryEntry[]> {
return this.messages.get(userId) || [];
}
async clear(userId: string): Promise<void> {
this.messages.delete(userId);
}
}
// ==========================================
// LAYER 3: CAPABILITIES
// ==========================================
interface Action {
name: string;
description: string;
parameters: any;
handler: (params: any) => Promise<any>;
}
class SimpleCapabilities {
private actions: Map<string, Action> = new Map();
constructor() {
this.registerBuiltInActions();
}
private registerBuiltInActions(): void {
// Get current time
this.register({
name: 'get_time',
description: 'Get the current time',
parameters: { type: 'object', properties: {} },
handler: async () => {
return {
time: new Date().toLocaleTimeString(),
date: new Date().toLocaleDateString(),
timestamp: Date.now()
};
}
});
// Calculate
this.register({
name: 'calculate',
description: 'Perform a mathematical calculation',
parameters: {
type: 'object',
properties: {
expression: { type: 'string', description: 'Math expression to evaluate' }
},
required: ['expression']
},
handler: async (params) => {
try {
// Simple eval for demo (use a proper math parser in production!)
const result = eval(params.expression);
return { result, expression: params.expression };
} catch (error) {
return { error: 'Invalid expression' };
}
}
});
// Search (mock)
this.register({
name: 'web_search',
description: 'Search the web for information',
parameters: {
type: 'object',
properties: {
query: { type: 'string', description: 'Search query' }
},
required: ['query']
},
handler: async (params) => {
// Mock response
return {
query: params.query,
results: [
{ title: 'Mock Result 1', url: 'https://example.com/1' },
{ title: 'Mock Result 2', url: 'https://example.com/2' }
]
};
}
});
}
register(action: Action): void {
this.actions.set(action.name, action);
}
async execute(name: string, params: any): Promise<any> {
const action = this.actions.get(name);
if (!action) {
throw new Error(`Action "${name}" not found`);
}
// Validate parameters (simplified)
if (action.parameters.required) {
for (const required of action.parameters.required) {
if (!(required in params)) {
throw new Error(`Missing required parameter: ${required}`);
}
}
}
return await action.handler(params);
}
listActions(): string[] {
return Array.from(this.actions.keys());
}
getActionDescription(name: string): string {
const action = this.actions.get(name);
return action ? action.description : '';
}
}
// ==========================================
// LAYER 4: REASONING
// ==========================================
class SimpleReasoning {
private openai: OpenAI;
constructor(
private capabilities: SimpleCapabilities,
apiKey: string
) {
this.openai = new OpenAI({ apiKey });
}
async reason(
userMessage: string,
context: MemoryEntry[]
): Promise<{ response: string; actions: any[] }> {
// Build messages for OpenAI
const messages: any[] = [
{
role: 'system',
content: this.buildSystemPrompt()
},
...context.map(entry => ({
role: entry.role,
content: entry.content
})),
{
role: 'user',
content: userMessage
}
];
// Call OpenAI with function calling
const response = await this.openai.chat.completions.create({
model: 'gpt-4',
messages,
functions: this.getFunctionDefinitions(),
function_call: 'auto',
temperature: 0.7
});
const message = response.choices[0].message;
const actions: any[] = [];
// Execute function if called
if (message.function_call) {
const functionName = message.function_call.name;
const functionArgs = JSON.parse(message.function_call.arguments);
const result = await this.capabilities.execute(functionName, functionArgs);
actions.push({ name: functionName, params: functionArgs, result });
// Get final response with function result
const finalResponse = await this.openai.chat.completions.create({
model: 'gpt-4',
messages: [
...messages,
{
role: 'function',
name: functionName,
content: JSON.stringify(result)
}
],
temperature: 0.7
});
return {
response: finalResponse.choices[0].message.content || 'No response',
actions
};
}
return {
response: message.content || 'No response',
actions
};
}
private buildSystemPrompt(): string {
return `You are a helpful AI assistant. You have access to the following capabilities:
${this.capabilities.listActions().map(name =>
`- ${name}: ${this.capabilities.getActionDescription(name)}`
).join('\n')}
Use these capabilities when appropriate to help the user.`;
}
private getFunctionDefinitions(): any[] {
return [
{
name: 'get_time',
description: 'Get the current time and date',
parameters: { type: 'object', properties: {} }
},
{
name: 'calculate',
description: 'Perform a mathematical calculation',
parameters: {
type: 'object',
properties: {
expression: {
type: 'string',
description: 'Math expression like "2 + 2" or "Math.sqrt(16)"'
}
},
required: ['expression']
}
},
{
name: 'web_search',
description: 'Search the web for information',
parameters: {
type: 'object',
properties: {
query: { type: 'string', description: 'Search query' }
},
required: ['query']
}
}
];
}
}
// ==========================================
// LAYER 5: PERSONA
// ==========================================
interface PersonaConfig {
name: string;
role: string;
traits: {
friendliness: number;
professionalism: number;
enthusiasm: number;
};
tone: string;
}
class SimplePersona {
constructor(private config: PersonaConfig) {}
getName(): string {
return this.config.name;
}
getRole(): string {
return this.config.role;
}
apply(response: string): string {
// Apply tone adjustments based on traits
let adjusted = response;
if (this.config.traits.enthusiasm > 0.8) {
// Add enthusiasm markers
adjusted = adjusted.replace(/\./g, '!');
}
if (this.config.traits.friendliness > 0.8) {
// Ensure friendly greeting if missing
if (!adjusted.match(/^(Hi|Hello|Hey)/i)) {
adjusted = `Hi there! ${adjusted}`;
}
}
return adjusted;
}
validate(response: string): boolean {
// Check constraints (simplified)
const forbidden = ['politics', 'religion', 'violence'];
const lowerResponse = response.toLowerCase();
for (const word of forbidden) {
if (lowerResponse.includes(word)) {
return false;
}
}
return true;
}
}
// ==========================================
// MAIN AGENT
// ==========================================
class SimpleAgent {
private runtime: SimpleRuntime;
private memory: SimpleMemory;
private capabilities: SimpleCapabilities;
private reasoning: SimpleReasoning;
private persona: SimplePersona;
constructor(config: { openaiApiKey: string; persona: PersonaConfig }) {
this.runtime = new SimpleRuntime();
this.memory = new SimpleMemory();
this.capabilities = new SimpleCapabilities();
this.reasoning = new SimpleReasoning(this.capabilities, config.openaiApiKey);
this.persona = new SimplePersona(config.persona);
}
async start(): Promise<void> {
await this.runtime.start();
console.log(`🤖 ${this.persona.getName()} (${this.persona.getRole()}) is ready!`);
}
async stop(): Promise<void> {
await this.runtime.stop();
}
async chat(userId: string, message: string): Promise<string> {
try {
// 1. Remember user message
await this.memory.remember(userId, 'user', message);
// 2. Recall context
const context = await this.memory.recall(userId);
// 3. Reason about response
const result = await this.runtime.executeWithRetry(async () => {
return await this.reasoning.reason(message, context);
});
// 4. Apply persona
let response = this.persona.apply(result.response);
// 5. Validate response
if (!this.persona.validate(response)) {
response = "I'm sorry, but I can't provide that kind of information.";
}
// 6. Remember assistant response
await this.memory.remember(userId, 'assistant', response);
// 7. Log actions if any
if (result.actions.length > 0) {
console.log('🔧 Actions executed:', result.actions);
}
return response;
} catch (error) {
console.error('❌ Error:', error);
return "I'm sorry, I encountered an error. Please try again.";
}
}
healthCheck() {
return this.runtime.healthCheck();
}
}
// ==========================================
// USAGE EXAMPLE
// ==========================================
async function main() {
// Create agent
const agent = new SimpleAgent({
openaiApiKey: process.env.OPENAI_API_KEY!,
persona: {
name: 'Aria',
role: 'Helpful Assistant',
traits: {
friendliness: 0.9,
professionalism: 0.8,
enthusiasm: 0.7
},
tone: 'friendly-professional'
}
});
// Start agent
await agent.start();
const userId = 'demo-user';
// Example conversations
console.log('\n--- Conversation 1: Basic chat ---');
const response1 = await agent.chat(userId, 'Hello! How are you?');
console.log(`🤖 ${response1}\n`);
console.log('--- Conversation 2: Use capability ---');
const response2 = await agent.chat(userId, 'What time is it?');
console.log(`🤖 ${response2}\n`);
console.log('--- Conversation 3: Calculate ---');
const response3 = await agent.chat(userId, 'Can you calculate 15 * 23 for me?');
console.log(`🤖 ${response3}\n`);
console.log('--- Conversation 4: Search ---');
const response4 = await agent.chat(userId, 'Search for information about ARAL');
console.log(`🤖 ${response4}\n`);
// Health check
console.log('--- Health Check ---');
console.log(agent.healthCheck());
// Stop agent
await agent.stop();
}
// Run
main().catch(console.error);
Terminal window
export OPENAI_API_KEY="sk-..."
npx tsx simple-agent.ts
Terminal window
pip install openai asyncio
simple_agent.py
import os
import asyncio
import time
from typing import List, Dict, Any, Optional
from openai import OpenAI
# ==========================================
# LAYER 1: RUNTIME
# ==========================================
class SimpleRuntime:
def __init__(self):
self.is_running = False
async def start(self):
self.is_running = True
print('✅ Runtime started')
async def stop(self):
self.is_running = False
print('✅ Runtime stopped')
def health_check(self) -> dict:
return {
'status': 'healthy' if self.is_running else 'stopped',
'uptime': time.time()
}
async def execute_with_retry(self, fn, max_retries: int = 3):
last_error = None
for attempt in range(max_retries + 1):
try:
return await fn()
except Exception as error:
last_error = error
if attempt < max_retries:
delay = min(1.0 * (2 ** attempt), 10.0)
await asyncio.sleep(delay)
raise last_error
# ==========================================
# LAYER 2: MEMORY
# ==========================================
class SimpleMemory:
def __init__(self):
self.messages: Dict[str, List[dict]] = {}
self.max_messages = 20
async def remember(self, user_id: str, role: str, content: str):
if user_id not in self.messages:
self.messages[user_id] = []
self.messages[user_id].append({
'role': role,
'content': content,
'timestamp': time.time()
})
# Prune old messages
if len(self.messages[user_id]) > self.max_messages:
self.messages[user_id].pop(0)
async def recall(self, user_id: str) -> List[dict]:
return self.messages.get(user_id, [])
async def clear(self, user_id: str):
if user_id in self.messages:
del self.messages[user_id]
# ==========================================
# LAYER 3: CAPABILITIES
# ==========================================
class SimpleCapabilities:
def __init__(self):
self.actions = {}
self._register_built_in_actions()
def _register_built_in_actions(self):
# Get time
self.register({
'name': 'get_time',
'description': 'Get the current time',
'parameters': {'type': 'object', 'properties': {}},
'handler': self._get_time
})
# Calculate
self.register({
'name': 'calculate',
'description': 'Perform a mathematical calculation',
'parameters': {
'type': 'object',
'properties': {
'expression': {'type': 'string'}
},
'required': ['expression']
},
'handler': self._calculate
})
# Web search (mock)
self.register({
'name': 'web_search',
'description': 'Search the web for information',
'parameters': {
'type': 'object',
'properties': {
'query': {'type': 'string'}
},
'required': ['query']
},
'handler': self._web_search
})
async def _get_time(self, params):
from datetime import datetime
now = datetime.now()
return {
'time': now.strftime('%H:%M:%S'),
'date': now.strftime('%Y-%m-%d'),
'timestamp': time.time()
}
async def _calculate(self, params):
try:
result = eval(params['expression'])
return {'result': result, 'expression': params['expression']}
except Exception:
return {'error': 'Invalid expression'}
async def _web_search(self, params):
return {
'query': params['query'],
'results': [
{'title': 'Mock Result 1', 'url': 'https://example.com/1'},
{'title': 'Mock Result 2', 'url': 'https://example.com/2'}
]
}
def register(self, action: dict):
self.actions[action['name']] = action
async def execute(self, name: str, params: dict):
if name not in self.actions:
raise ValueError(f"Action '{name}' not found")
action = self.actions[name]
# Validate required parameters
if 'required' in action['parameters']:
for required in action['parameters']['required']:
if required not in params:
raise ValueError(f"Missing required parameter: {required}")
return await action['handler'](params)
def list_actions(self) -> List[str]:
return list(self.actions.keys())
def get_action_description(self, name: str) -> str:
return self.actions[name]['description'] if name in self.actions else ''
# ==========================================
# LAYER 4: REASONING
# ==========================================
class SimpleReasoning:
def __init__(self, capabilities: SimpleCapabilities, api_key: str):
self.capabilities = capabilities
self.openai = OpenAI(api_key=api_key)
async def reason(self, user_message: str, context: List[dict]) -> dict:
messages = [
{'role': 'system', 'content': self._build_system_prompt()},
*[{'role': entry['role'], 'content': entry['content']} for entry in context],
{'role': 'user', 'content': user_message}
]
# Call OpenAI
response = self.openai.chat.completions.create(
model='gpt-4',
messages=messages,
functions=self._get_function_definitions(),
function_call='auto',
temperature=0.7
)
message = response.choices[0].message
actions = []
# Execute function if called
if message.function_call:
function_name = message.function_call.name
import json
function_args = json.loads(message.function_call.arguments)
result = await self.capabilities.execute(function_name, function_args)
actions.append({'name': function_name, 'params': function_args, 'result': result})
# Get final response
final_response = self.openai.chat.completions.create(
model='gpt-4',
messages=[
*messages,
{
'role': 'function',
'name': function_name,
'content': json.dumps(result)
}
],
temperature=0.7
)
return {
'response': final_response.choices[0].message.content or 'No response',
'actions': actions
}
return {
'response': message.content or 'No response',
'actions': actions
}
def _build_system_prompt(self) -> str:
capabilities = '\n'.join(
f"- {name}: {self.capabilities.get_action_description(name)}"
for name in self.capabilities.list_actions()
)
return f"""You are a helpful AI assistant. You have access to these capabilities:
{capabilities}
Use these capabilities when appropriate to help the user."""
def _get_function_definitions(self) -> List[dict]:
return [
{
'name': 'get_time',
'description': 'Get the current time and date',
'parameters': {'type': 'object', 'properties': {}}
},
{
'name': 'calculate',
'description': 'Perform a mathematical calculation',
'parameters': {
'type': 'object',
'properties': {
'expression': {'type': 'string'}
},
'required': ['expression']
}
},
{
'name': 'web_search',
'description': 'Search the web',
'parameters': {
'type': 'object',
'properties': {
'query': {'type': 'string'}
},
'required': ['query']
}
}
]
# ==========================================
# LAYER 5: PERSONA
# ==========================================
class SimplePersona:
def __init__(self, config: dict):
self.config = config
def get_name(self) -> str:
return self.config['name']
def get_role(self) -> str:
return self.config['role']
def apply(self, response: str) -> str:
adjusted = response
if self.config['traits']['enthusiasm'] > 0.8:
adjusted = adjusted.replace('.', '!')
if self.config['traits']['friendliness'] > 0.8:
if not adjusted.startswith(('Hi', 'Hello', 'Hey')):
adjusted = f"Hi there! {adjusted}"
return adjusted
def validate(self, response: str) -> bool:
forbidden = ['politics', 'religion', 'violence']
lower_response = response.lower()
for word in forbidden:
if word in lower_response:
return False
return True
# ==========================================
# MAIN AGENT
# ==========================================
class SimpleAgent:
def __init__(self, config: dict):
self.runtime = SimpleRuntime()
self.memory = SimpleMemory()
self.capabilities = SimpleCapabilities()
self.reasoning = SimpleReasoning(self.capabilities, config['openai_api_key'])
self.persona = SimplePersona(config['persona'])
async def start(self):
await self.runtime.start()
print(f"🤖 {self.persona.get_name()} ({self.persona.get_role()}) is ready!")
async def stop(self):
await self.runtime.stop()
async def chat(self, user_id: str, message: str) -> str:
try:
# 1. Remember
await self.memory.remember(user_id, 'user', message)
# 2. Recall
context = await self.memory.recall(user_id)
# 3. Reason
async def reason_fn():
return await self.reasoning.reason(message, context)
result = await self.runtime.execute_with_retry(reason_fn)
# 4. Apply persona
response = self.persona.apply(result['response'])
# 5. Validate
if not self.persona.validate(response):
response = "I'm sorry, but I can't provide that kind of information."
# 6. Remember
await self.memory.remember(user_id, 'assistant', response)
# 7. Log actions
if result['actions']:
print('🔧 Actions executed:', result['actions'])
return response
except Exception as error:
print(f'❌ Error: {error}')
return "I'm sorry, I encountered an error. Please try again."
def health_check(self):
return self.runtime.health_check()
# ==========================================
# USAGE EXAMPLE
# ==========================================
async def main():
agent = SimpleAgent({
'openai_api_key': os.getenv('OPENAI_API_KEY'),
'persona': {
'name': 'Aria',
'role': 'Helpful Assistant',
'traits': {
'friendliness': 0.9,
'professionalism': 0.8,
'enthusiasm': 0.7
},
'tone': 'friendly-professional'
}
})
await agent.start()
user_id = 'demo-user'
print('\n--- Conversation 1 ---')
response1 = await agent.chat(user_id, 'Hello! How are you?')
print(f'🤖 {response1}\n')
print('--- Conversation 2 ---')
response2 = await agent.chat(user_id, 'What time is it?')
print(f'🤖 {response2}\n')
print('--- Conversation 3 ---')
response3 = await agent.chat(user_id, 'Calculate 15 * 23')
print(f'🤖 {response3}\n')
print('--- Health Check ---')
print(agent.health_check())
await agent.stop()
if __name__ == '__main__':
asyncio.run(main())
Terminal window
export OPENAI_API_KEY="sk-..."
python simple_agent.py

✅ Runtime started
🤖 Aria (Helpful Assistant) is ready!
--- Conversation 1: Basic chat ---
🤖 Hi there! I'm doing great! Thanks for asking! How can I help you today?
--- Conversation 2: Use capability ---
🔧 Actions executed: [{'name': 'get_time', 'params': {}, 'result': {'time': '14:30:15', 'date': '2025-01-15', 'timestamp': 1736952615}}]
🤖 Hi there! It's currently 14:30:15 on 2025-01-15!
--- Conversation 3: Calculate ---
🔧 Actions executed: [{'name': 'calculate', 'params': {'expression': '15 * 23'}, 'result': {'result': 345, 'expression': '15 * 23'}}]
🤖 Hi there! 15 multiplied by 23 equals 345!
--- Conversation 4: Search ---
🔧 Actions executed: [{'name': 'web_search', 'params': {'query': 'ARAL'}, 'result': {...}}]
🤖 Hi there! I found some information about ARAL for you!
--- Health Check ---
{'status': 'healthy', 'uptime': 12.5}
✅ Runtime stopped

layer-group Layer Separation

Each layer is independent:

  • Runtime handles execution lifecycle
  • Memory manages conversation state
  • Capabilities defines available actions
  • Reasoning makes decisions
  • Persona enforces behavior

This modularity makes testing and extending easy.

shield-halved Error Handling

The runtime provides automatic retry with exponential backoff:

await runtime.executeWithRetry(async () => {
return await reasoning.reason(message, context);
});

This makes the agent resilient to transient LLM API failures.

function Function Calling

The agent uses OpenAI function calling to:

  1. Present available capabilities to the LLM
  2. Extract structured action requests
  3. Execute capabilities automatically
  4. Return results to the LLM for final response

This creates seamless tool use.

user-check Persona Enforcement

The persona layer:

  • Adjusts response tone based on traits
  • Validates responses against constraints
  • Ensures consistent character behavior

This maintains agent identity across conversations.