Back to blog
Tutorial·7 min read·1388 words

How to Build an AI Agent with Persistent Background Agents

Learn how to build an AI agent system with persistent background agents, crash-safe event logs, and multi-model support. Full tutorial with code examples using the Qubax AI API.

How to Build an AI Agent with Persistent Background Agents — illustration

How to Build an AI Agent with Persistent Background Agents

AI agents are powerful, but most operate in a simple request-response loop: you give them a task, they work on it, and they come back when done. What if your agent could run multiple specialized sub-agents in the background simultaneously, each handling a different aspect of the task?

That is exactly what persistent background agents do. Meta's new Muse Code uses this architecture, and in this tutorial, we will show you how to build your own version using AI APIs.

What Are Persistent Background Agents?

Persistent background agents are specialized AI agents that:

  1. Stay active throughout a session (not spawned per-task)
  2. Work independently on subtasks
  3. Communicate results back to a main coordinator agent
  4. Avoid redundant work by remembering what they have already done

This architecture is particularly useful for complex tasks like codebase analysis, multi-file refactoring, or research tasks that benefit from parallel exploration.

Architecture Overview

Here is the system we will build:

code
User Task
    |
    v
[Main Coordinator Agent]
    |
    +---> [Background Agent 1: Code Explorer]
    +---> [Background Agent 2: Test Runner]
    +---> [Background Agent 3: Documentation Reader]
    |
    v
[Results Aggregated & Verified]
    |
    v
[Response to User]

Prerequisites

  • Node.js 18+ or Python 3.10+
  • An API key from Qubax AI (gives you access to GPT, Claude, and more)
  • Basic understanding of async programming

Step 1: Set Up Your Project

bash
mkdir ai-background-agents
cd ai-background-agents
npm init -y
npm install openai

Create a .env file:

env
QUBAX_API_KEY=your_api_key_here
QUBAX_BASE_URL=https://api.qubax.ai/v1

Step 2: Create the Background Agent Class

javascript
const OpenAI = require('openai');

class BackgroundAgent {
  constructor(name, systemPrompt, client) {
    this.name = name;
    this.systemPrompt = systemPrompt;
    this.client = client;
    this.memory = []; // Persistent memory across the session
    this.active = false;
    this.results = null;
  }

  async run(task) {
    this.active = true;
    const messages = [
      { role: 'system', content: this.systemPrompt },
      { role: 'user', content: task }
    ];

    // Include relevant memory
    if (this.memory.length > 0) {
      const memoryContext = 'Previous findings:\n' + 
        this.memory.map(m => '- ' + m).join('\n');
      messages.splice(1, 0, { role: 'assistant', content: memoryContext });
    }

    const response = await this.client.chat.completions.create({
      model: 'gpt-5',
      messages: messages,
      temperature: 0.2,
      max_tokens: 2000
    });

    const result = response.choices[0].message.content;
    
    // Store in memory for future use
    this.memory.push(result);
    this.results = result;
    this.active = false;
    
    return result;
  }

  hasMemory() {
    return this.memory.length > 0;
  }
}

Step 3: Create the Coordinator Agent

The coordinator agent receives the user's task, dispatches work to background agents, and synthesizes results.

javascript
class CoordinatorAgent {
  constructor(apiKey, baseUrl) {
    this.client = new OpenAI({
      apiKey: apiKey,
      baseURL: baseUrl
    });
    
    // Initialize specialized background agents
    this.agents = {
      explorer: new BackgroundAgent(
        'Code Explorer',
        'You are a code exploration agent. Analyze codebases and report structure, patterns, and potential issues. Be concise.',
        this.client
      ),
      tester: new BackgroundAgent(
        'Test Runner',
        'You are a testing agent. Design test cases and verify code correctness. Report pass/fail status and edge cases.',
        this.client
      ),
      reviewer: new BackgroundAgent(
        'Code Reviewer',
        'You are a code review agent. Check for best practices, security issues, and performance concerns. Suggest improvements.',
        this.client
      )
    };
  }

  async executeTask(userTask) {
    console.log('Coordinator: Received task:', userTask);
    
    // Phase 1: Explore (parallel)
    const exploreTask = `Analyze this task and identify what code areas are relevant: ${userTask}`;
    const exploration = await this.agents.explorer.run(exploreTask);
    console.log('Explorer:', exploration.substring(0, 100) + '...');

    // Phase 2: Generate and Review (parallel)
    const [testResults, reviewResults] = await Promise.all([
      this.agents.tester.run(`Design tests for: ${userTask}. Context: ${exploration}`),
      this.agents.reviewer.run(`Review approach for: ${userTask}. Context: ${exploration}`)
    ]);

    // Phase 3: Synthesize
    const synthesis = await this.client.chat.completions.create({
      model: 'gpt-5',
      messages: [
        {
          role: 'system',
          content: 'You are a coordinator agent. Synthesize the results from your team into a final answer.'
        },
        {
          role: 'user',
          content: `Task: ${userTask}\n\nExploration: ${exploration}\n\nTests: ${testResults}\n\nReview: ${reviewResults}`
        }
      ]
    });

    return synthesis.choices[0].message.content;
  }
}

Step 4: Add an Event Log (Crash Safety)

Inspired by Muse Code's crash-safe runtime, let's add an event log:

javascript
const fs = require('fs');

class EventLog {
  constructor(logFile) {
    this.logFile = logFile;
    this.events = [];
    this.load();
  }

  load() {
    try {
      const data = fs.readFileSync(this.logFile, 'utf8');
      this.events = JSON.parse(data);
    } catch (e) {
      this.events = [];
    }
  }

  append(event) {
    const entry = {
      timestamp: new Date().toISOString(),
      ...event
    };
    this.events.push(entry);
    fs.writeFileSync(this.logFile, JSON.stringify(this.events, null, 2));
  }

  getLastState() {
    // Find the last completed event
    for (let i = this.events.length - 1; i >= 0; i--) {
      if (this.events[i].type === 'task_complete') {
        return this.events[i];
      }
    }
    return null;
  }

  replay() {
    console.log('Replaying event log:');
    this.events.forEach(e => {
      console.log(`[${e.timestamp}] ${e.type}: ${e.detail}`);
    });
  }
}

Step 5: Integrate Everything

javascript
require('dotenv').config();

const coordinator = new CoordinatorAgent(
  process.env.QUBAX_API_KEY,
  process.env.QUBAX_BASE_URL
);

const eventLog = new EventLog('agent_events.json');

async function main() {
  const task = process.argv[2] || 'Build a REST API endpoint for user registration';
  
  eventLog.append({ type: 'task_start', detail: task });
  
  try {
    const result = await coordinator.executeTask(task);
    eventLog.append({ type: 'task_complete', detail: 'Success', result });
    console.log('\n=== FINAL RESULT ===\n');
    console.log(result);
  } catch (error) {
    eventLog.append({ type: 'task_error', detail: error.message });
    console.error('Task failed:', error.message);
    
    // Check if we can resume
    const lastState = eventLog.getLastState();
    if (lastState) {
      console.log('Last successful state:', lastState.detail);
    }
  }
}

main();

Step 6: Run Your Agent

bash
node index.js "Add input validation to the user registration endpoint"

Output:

code
Coordinator: Received task: Add input validation to the user registration endpoint
Explorer: Analyzing task requirements...
Tester: Designing test cases for input validation...
Reviewer: Checking for security best practices...

=== FINAL RESULT ===
[Based on exploration, testing, and review, here is the validated solution...]

Advanced: Using Different Models for Different Agents

One of the advantages of using Qubax AI is access to multiple models. You can use different models for different agents:

javascript
class MultiModelCoordinator {
  constructor(apiKey, baseUrl) {
    this.client = new OpenAI({ apiKey, baseURL: baseUrl });
    
    // Use a fast, cheap model for exploration
    this.agents.explorer = new BackgroundAgent(
      'Explorer',
      'You are a code explorer...',
      this.client
    );
    this.agents.explorer.model = 'deepseek-v4-flash'; // Cheap and fast
    
    // Use a powerful model for code generation
    this.agents.coder = new BackgroundAgent(
      'Coder',
      'You are a code generation agent...',
      this.client
    );
    this.agents.coder.model = 'gpt-5'; // Best quality
  }
}

This approach lets you optimize both cost and quality — use cheaper models for simple tasks and reserve expensive models for complex reasoning.

Best Practices

  1. Keep agents focused: Each background agent should have one clear responsibility
  2. Use memory wisely: Persistent memory is powerful but can grow large — prune periodically
  3. Handle failures gracefully: One agent failing should not crash the entire system
  4. Log everything: The event log is your safety net and debugging tool
  5. Start simple: Begin with 2-3 agents and add more as needed

FAQ

What is the difference between a background agent and a regular API call?

A regular API call is stateless — each call is independent. A background agent maintains state (memory) across multiple calls within a session, allowing it to build on previous work without re-exploring.

How many background agents should I use?

Start with 2-3 specialized agents. More is not always better — each agent adds complexity and API costs. Monitor performance and scale as needed.

Can I use this architecture with any AI model?

Yes. The architecture is model-agnostic. Using Qubax AI, you can swap between GPT-5, Claude, DeepSeek, and other models without changing your code.

How do I handle agent failures?

Implement retry logic with exponential backoff, and use the event log to track which agents succeeded and which failed. If an agent fails, the coordinator can retry or skip that step.

What is the cost of running multiple background agents?

Each agent makes its own API calls, so costs scale with the number of agents. Using cheaper models for simpler tasks (like exploration) and reserving expensive models for complex tasks keeps costs manageable. See Qubax AI pricing for current rates.

How does this compare to Meta's Muse Code architecture?

Muse Code uses a similar architecture at scale — persistent background agents with an event log for crash safety. This tutorial gives you a simplified version you can customize for your own use cases.


Ready to build your own AI agents? Get started with [Qubax AI](https://qubax.ai/docs) — one API, 20+ models, and the tools you need to build production-grade AI applications.

Article tags

#tutorial#ai-agents#background-agents#nodejs#api
Share:Post on XTelegramLinkedInYHacker NewsReddit
Qubax AI

Qubax AI

AI Models at up to 99% off · Pay with crypto

Access GPT, Claude, Gemini, GLM & 340+ models through one OpenAI-compatible API. Up to 99% off. Pay with 200+ cryptocurrencies. Get $1 free credits — no credit card needed.

Related articles