Manuale di riferimento MALDA™

Il linguaggio di programmazione AI-First - Versione 1.0.11

18. Orchestrazione di agenti

MALDA: The AI-First Programming Language include il supporto nativo e di prima classe all'orchestrazione di agenti come funzionalità del linguaggio. Queste capacità sono integrate direttamente nel linguaggio e rendono MALDA il modo più semplice per costruire agenti AI e workflow di automazione.

18.1 ⚠️ Avvertenza critica su sicurezza e rischi

I sistemi di agenti autonomi con accesso ai tool comportano rischi significativi di sicurezza, safety e operatività. Prima di mettere in produzione agenti con tool, devi comprendere e mitigare questi rischi.

Rischi fondamentali

Scenari di rischio reali

1. Perdita di dati e danni al file system

2. Esecuzione di codice non autorizzata

3. Corruzione del repository Git

4. Accesso non autorizzato e divulgazione di informazioni

5. Loop infiniti ed esaurimento delle risorse

6. Fallimenti a cascata nei sistemi multi-agente

7. Modifica di sistemi di produzione

8. Iniezione di codice malevolo

Strategie di mitigazione

Buone pratiche

Usando le funzionalità di orchestrazione di agenti di MALDA, riconosci di comprendere questi rischi e di essere l'unico responsabile delle azioni compiute dagli agenti che crei e metti in produzione. Gli sviluppatori di MALDA e di questo manuale di riferimento non forniscono alcuna garanzia sulla safety, sulla sicurezza o sull'affidabilità dei sistemi di agenti autonomi.

18.2 LLM Client

La classe LLMClient offre un'interfaccia verso API LLM compatibili con OpenAI (OpenAI, OpenRouter, LMStudio, OLLAMA, ecc.).

Costruttore

var client = new LLMClient(apiUrl, apiKey, model);

Metodi

Esempio

var apiKey = getEnv("OPENAI_API_KEY");
var client = new LLMClient(
    "https://api.openai.com/v1/chat/completions",
    apiKey,
    "gpt-3.5-turbo"
);

var response = client.complete("Explain quantum computing in one sentence.");
print(response);

18.3 OpenRouterClient

Per OpenRouter in particolare esiste una classe specializzata OpenRouterClient che semplifica la configurazione.

Costruttore

// With default model (deepseek/deepseek-v4-flash)
var client = new OpenRouterClient();

// With custom model
var client = new OpenRouterClient("openai/gpt-4");
Configurazione automatica: OpenRouterClient legge automaticamente l'API key dalla variabile d'ambiente OPENROUTER_API_KEY e usa l'endpoint OpenRouter corretto.

Attribuzione dell'app (analytics OpenRouter)

Imposta queste proprietà così OpenRouter può attribuire l'uso dei token a ciascuna delle tue app (App Attribution). Usa un URL httpReferer distinto per applicazione. Le analytics appaiono su https://openrouter.ai/apps?url=<your-url>.

var client = new OpenRouterClient();
client.httpReferer = "https://example.com/secondbrain";  // HTTP-Referer (required for attribution)
client.appTitle = "Second Brain ASK";                     // X-OpenRouter-Title
client.appCategories = "cli-agent";                       // optional X-OpenRouter-Categories

18.3.1 LLM locale di default (quando non si passa un client)

Quando crei un agente, un prompt o usi built-in come decomposeTask senza passare un client LLM, MALDA usa un LLM locale di default: Qwen/Qwen2.5-0.5B-Instruct, scaricato come build GGUF qwen2.5-0.5b-instruct-q4_k_m.gguf da Hugging Face al primo uso. Il modello viene messo in cache nel profilo utente (es. %LOCALAPPDATA%\MaldaLang\Models\default su Windows). Dopo il primo download non servono API key né rete. Per usare invece un modello remoto, passa esplicitamente un OpenRouterClient, un LLMClient o un LlamaCppClient.

Puoi sovrascrivere il modello di default con la variabile d'ambiente MALDA_DEFAULT_LOCAL_MODEL. Impostala a modelId/fileName.gguf (es. org/repo/MyModel-Q4_K_M.gguf) per usare un altro modello GGUF di Hugging Face; quel modello verrà scaricato e messo in cache in una sottocartella con il nome del model ID. Se non è impostata, si usa la build GGUF built-in di default Qwen/Qwen2.5-0.5B-Instruct.

18.4 LlamaCppClient

La classe LlamaCppClient abilita l'inferenza LLM locale usando LLAMA.cpp con modelli in formato GGUF. Non servono API key né connessione di rete. Se ometti modelPath, MALDA usa il modello GGUF locale di default Qwen/Qwen2.5-0.5B-Instruct e lo scarica automaticamente al primo uso.

Costruttore

var client = new LlamaCppClient(modelPath);   // Custom GGUF file
var client = new LlamaCppClient();            // Default auto-downloaded local model

Esempio

var client = new LlamaCppClient();
client.setTemperature(0.7);
client.setMaxTokens(2000);
client.setGpuLayerCount(35);  // Offload layers to GPU (optional)

var conversation = new Conversation(client, "You are a helpful assistant.");
conversation.addUserMessage("Explain quantum computing.");
var response = conversation.send();
print(response.content);

18.5 Gestione delle conversazioni

La classe Conversation gestisce conversazioni multi-turno con gli LLM, inclusa la gestione automatica delle tool call.

Costruttore

var conversation = new Conversation(client, systemPrompt);

Metodi

Esempio

var conversation = new Conversation(client, "You are a helpful assistant.");
conversation.addUserMessage("What is 2+2?");
var response = conversation.send();
print(response.content);

Tool call in sola lettura in parallelo

Quando l'LLM restituisce più tool call in sola lettura in una singola risposta, Conversation le esegue in parallelo di default. Vale per tutti gli agenti (Agent, DevAgent, loop Ralph Wiggum, chat dell'IDE) perché condividono lo stesso runtime.

Tool built-in sicuri in parallelo: read_file, grep, list_directory, get_symbols, get_parse_errors, web_search, recall_progress e i tool git in sola lettura (git_status, git_log, git_diff, git_branch).

Sempre sequenziali: scritture su file, mutazioni git, run_command, ask_user, remember_progress, tool MCP, wrapper agente-come-tool e handler @Tool personalizzati. Un tool che muta in mezzo a un batch spezza l'esecuzione così l'ordine resta sicuro.

I messaggi di risultato dei tool vengono accodati nello stesso ordine dell'array originale tool_calls prima della richiesta LLM successiva.

Configurazione: l'esecuzione in parallelo è attiva quando MALDA_PARALLEL_TOOL_CALLS non è impostata. Imposta MALDA_PARALLEL_TOOL_CALLS=false (oppure 0, off) per disabilitarla. Con il logging verbose (MALDA_RALPH_VERBOSE o MALDA_AGENT_VERBOSE), i batch paralleli vengono registrati esplicitamente.

Logging verbose, feedback di thinking e streaming LLM

Durante agent.think() (e qualsiasi percorso di codice che usa Conversation.send()), il runtime può stampare i progressi in tempo reale sulla console:

I client LLM HTTP (LLMClient, OpenRouterClient) usano lo streaming SSE in stile OpenAI (stream: true) di default. Quando logging verbose e output di thinking sono abilitati, il testo [think] viene scritto token per token man mano che arrivano i delta. Il JSON delle tool call viene accumulato in silenzio durante lo stream; i nomi dei tool vengono registrati a stream completato.

Oggi non in streaming: LlamaCppClient, backend bridge e richieste con response_format strutturato (in quei casi si torna a una singola risposta bloccante).

VariabilePredefinitoScopo
MALDA_AGENT_VERBOSEfalse (Ralph: true)Abilita il logging [llm] / [tool]
MALDA_RALPH_VERBOSEAlias di MALDA_AGENT_VERBOSE
MALDA_AGENT_RICHtrueOutput colorato Spectre.Console (si disattiva automaticamente se reindirizzato)
MALDA_AGENT_TOOL_DETAILcompactLog dei tool compact o full
MALDA_AGENT_LLM_THINKINGcompactOutput [think]: off, compact o full
MALDA_AGENT_LLM_STREAMtrueStreaming SSE OpenAI per i client HTTP; [think] in tempo reale quando il thinking è abilitato
MALDA_AGENT_LLM_PREVIEWcompactAnteprima della risposta finale quando streaming/thinking sono disattivati
MALDA_AGENT_STATUS_EVERY4Ripete il banner di stato ogni N round LLM (Ralph)
MALDA_RALPH_*Alias delle impostazioni agente sopra nelle esecuzioni Ralph

Le stesse diagnostiche si possono attivare dal codice. Questi helper cambiano solo la presentazione e non modificano mai il risultato di un agente:

enableAgentVerboseLogging();
setAgentStatusBanner("Nightly refactor run");
setAgentVerbosePhase("planning");

18.6 Sistema di tool

I tool consentono agli LLM di chiamare funzioni durante le conversazioni. Si definiscono con la classe Tool e seguono il formato di function calling di OpenAI.

Funzioni di creazione dei tool built-in

Tutte queste factory accettano una workingDirectory opzionale, tranne createSubmitPlanTool, createAskUserTool e createWebSearchTool. Quando una directory è impostata, le operazioni su file e path restano al suo interno e nelle sue sottodirectory.

Esempio

var workingDir = ".";
var readTool = createReadFileTool(workingDir);
var writeTool = createWriteFileTool(workingDir);

conversation.addTool(readTool);
conversation.addTool(writeTool);

conversation.addUserMessage("Read example.txt and summarize it.");
var response = conversation.send();  // LLM will call read_file tool automatically

18.7 Classe Agent

La classe Agent rappresenta un agente autonomo con un ruolo e delle istruzioni specifici.

Costruttore

// With default local LLM (auto-download from Hugging Face)
var agent = new Agent(name, role, instructions);

// With a custom client
var agent = new Agent(name, role, instructions, client);

Metodi

Esempio

var client = new OpenRouterClient();
var agent = new Agent(
    "CodeMaster",
    "senior software engineer",
    "You write clean, efficient code. Always include comments and error handling.",
    client
);

agent.addTool(createReadFileTool("."));
agent.addTool(createWriteFileTool("."));

var response = agent.think("Read example.txt and add a comment to the add function.");
print(response.content);

18.8 Tool personalizzati con il decoratore @Tool

Default di governance: Valida i payload dei tool / a forma di LLM con uno schema e validate() prima degli effetti collaterali. Marca gli helper di normalize/derive con @pure() e gli handler impuri con @effects(...). Golden offline: Examples/Agents/agent_governance_golden.malda (vedi anche 9.7 Decoratori).

MALDA consente di definire tool personalizzati per gli LLM usando il decoratore @Tool sulle funzioni.

Sintassi del decoratore

@Tool(name, description, schema?)

Esempio

@Tool("calculate_sum", "Adds two numbers together")
function calculateSum(a, b) {
    return int(a) + int(b);
}

var agent = new Agent("Assistant", "Helper", "You are helpful", client);
agent.addTool("calculate_sum");

var response = agent.think("What is 15 + 27?");
print(response.content);

Costruire un tool con new Tool

Puoi anche costruire un'istanza Tool a runtime e passarle una funzione handler. Il pattern usuale è un singolo parametro args (l'oggetto argomenti dell'LLM). Chiama tool.execute(args) per invocare l'handler direttamente, oppure agent.addTool(tool) per i loop dell'agente.

function echoHandler(args) {
    return "got " + string(args.message);
}

var echoTool = new Tool(
    "echo_message",
    "Echoes a message back",
    {
        "type": "object",
        "properties": {
            "message": { "type": "string", "description": "Text to echo" }
        },
        "required": ["message"]
    },
    echoHandler
);

print(echoTool.execute({ "message": "hello" }));  // got hello
agent.addTool(echoTool);

Vedi Examples/Agents/secondbrain_semantic.malda (find_related_notes) per un esempio basato su GraphMemory usato in ASK quando i tool sono abilitati.

18.9 CodingAgent

La classe CodingAgent è un agente specializzato per i task di coding che include automaticamente tutti i tool di operazione sui file.

Tool inclusi

CodingAgent include automaticamente i seguenti tool di operazione sui file e il tool di esecuzione comandi:

Costruttore

// Simplest form - uses default local LLM (auto-download from Hugging Face)
var agent = new CodingAgent(name, role, instructions);

// With custom working directory
var agent = new CodingAgent(name, role, instructions, workingDirectory);

// With custom client
var agent = new CodingAgent(name, role, instructions, client);

// With custom client and working directory
var agent = new CodingAgent(name, role, instructions, client, workingDirectory);

Esempio

var coderAgent = new CodingAgent(
    "CodeMaster",
    "senior software engineer",
    "You write clean, efficient code."
);

// Agent already has all file tools - ready to use!
var response = coderAgent.think("Read example.txt and add error handling.");
print(response.content);

18.10 GitAgent

La classe GitAgent è un agente specializzato per le operazioni git che include automaticamente tutti i tool git.

Tool inclusi

GitAgent include automaticamente i seguenti tool di operazione git:

Costruttore

// Simplest form - uses default local LLM (auto-download from Hugging Face)
var agent = new GitAgent(name, role, instructions);

// With custom working directory
var agent = new GitAgent(name, role, instructions, workingDirectory);

// With custom client
var agent = new GitAgent(name, role, instructions, client);

// With custom client and working directory
var agent = new GitAgent(name, role, instructions, client, workingDirectory);

Esempio

var gitAgent = new GitAgent(
    "GitHelper",
    "git assistant",
    "You help manage git repositories."
);

// Agent already has all git tools - ready to use!
var response = gitAgent.think("Check git status, stage all changes, and commit with message 'Update code'");
print(response.content);

18.11 HumanAgent

La classe HumanAgent è un agente specializzato per l'interazione umana che include automaticamente il tool ask_user. Così è semplice creare agenti che devono interagire con le persone per chiarimenti, conferme o informazioni aggiuntive.

Tool inclusi

HumanAgent include automaticamente il seguente tool di interazione con l'utente:

Costruttore

// Simplest form - uses default local LLM (auto-download from Hugging Face)
var agent = new HumanAgent(name, role, instructions);

// With custom working directory
var agent = new HumanAgent(name, role, instructions, workingDirectory);

// With custom client
var agent = new HumanAgent(name, role, instructions, client);

// With custom client and working directory
var agent = new HumanAgent(name, role, instructions, client, workingDirectory);

Esempio

// Simplest usage - HumanAgent uses default local LLM
var humanAgent = new HumanAgent(
    "Assistant",
    "helpful assistant",
    "You help users by asking clarifying questions when needed."
);

// Agent already has ask_user tool - ready to use!
var response = humanAgent.think("Ask the user what their favorite programming language is, then provide a brief explanation of that language.");
print(response.content);

Usare HumanAgent come subagente (pattern human-in-the-loop)

Uno dei casi d'uso principali di HumanAgent è come subagente in workflow multi-agente complessi. Questo abilita i pattern human-in-the-loop, in cui gli agenti orchestratori possono delegare i task di interazione umana a un HumanAgent specializzato.

var client = new OpenRouterClient();

// Create a HumanAgent for user interaction
var humanAgent = new HumanAgent(
    "HumanInterface",
    "user interaction specialist",
    "You handle all interactions with human users. " +
    "Ask clear, concise questions when clarification is needed. " +
    "Request approval before making destructive changes.",
    client
);

// Create an orchestrator that can delegate to specialists
var orchestrator = new CodingAgent(
    "Orchestrator",
    "project coordinator",
    "You coordinate code changes. " +
    "When you need user approval or clarification, use the HumanInterface tool. " +
    "Always ask for confirmation before deleting files or making breaking changes.",
    client
);

// Add HumanAgent as a subagent
orchestrator.addSubAgent(
    humanAgent,
    "Handles user interaction, clarification requests, and approval confirmations. " +
    "Use this when you need to ask the user a question or get their approval."
);

// Now orchestrator can automatically request human input when needed
var response = orchestrator.think(
    "Refactor the codebase to use dependency injection. " +
    "Ask the user which design pattern they prefer before proceeding."
);

Vantaggi del pattern human-in-the-loop:

18.12 DevAgent

La classe DevAgent è un agente specializzato per i workflow di sviluppo completi che include automaticamente tutti i tool di operazione sui file, i tool git, i tool di esecuzione comandi e, in opzione, i tool di analisi del codice.

Tool inclusi

DevAgent include automaticamente i seguenti tool:

Costruttore

// Simplest form - uses default local LLM (auto-download from Hugging Face)
var agent = new DevAgent(name, role, instructions);

// With custom working directory
var agent = new DevAgent(name, role, instructions, workingDirectory);

// With custom client
var agent = new DevAgent(name, role, instructions, client);

// With custom client and working directory
var agent = new DevAgent(name, role, instructions, client, workingDirectory);

// With includeSymbols enabled (for code analysis)
var agent = new DevAgent(name, role, instructions, client, workingDirectory, true);

Esempio

// Simplest usage - DevAgent uses default local LLM
var devAgent = new DevAgent(
    "DevMaster",
    "senior developer",
    "You write, test, compile, and commit code."
);

// Agent already has all development tools - ready to use!
// Can handle full workflow: edit, test, commit
var response = devAgent.think("Read example.txt, add error handling, and commit the changes");
print(response.content);

Esempio con analisi del codice

// DevAgent with getSymbols enabled for better code understanding
var devAgent = new DevAgent(
    "CodeAnalyzer",
    "code refactoring expert",
    "You analyze code structure and make targeted improvements.",
    null,  // default client
    ".",   // working directory
    true   // includeSymbols - enables get_symbols tool
);

// Agent can now analyze code structure before making edits
var response = devAgent.think("Analyze the codebase structure and refactor the main function");
print(response.content);

Esempio con client personalizzato

var client = new OpenRouterClient("openai/gpt-4");

var devAgent = new DevAgent(
    "FullStackDev",
    "full-stack developer",
    "You develop complete features: code, test, compile, and commit.",
    client,
    "./project",
    false  // includeSymbols
);

// Agent can handle complete development workflow
var response = devAgent.think("Implement a new feature: add a user authentication system");
print(response.content);

18.13 Pianificazione strutturata dei task

MALDA consente di scomporre i task complessi in step ordinati e di eseguirli tramite un agente. Un piano è un oggetto { steps: [{ id, description, dependsOn? }, ...], planId?, taskSummary? }. Ogni step ha un id univoco (string), una description (string) e un dependsOn opzionale (array di id di step che devono completarsi prima).

// Decompose then execute from script
var plan = decomposeTask("Add unit tests for getSymbols and run the test suite");
var agent = new DevAgent("Dev", "developer", "You are a developer.", null, ".");
var result = executePlan(plan, agent);
print(result.completed);
print(result.results);

18.14 MALDACodingAgent

La classe MALDACodingAgent è un agente specializzato per lo sviluppo di script MALDA e le operazioni della toolchain che include automaticamente tutti i tool di operazione sui file e i tool di esecuzione/compilazione MALDA.

Tool inclusi

MALDACodingAgent include automaticamente i seguenti tool:

Costruttore

// Simplest form - uses default local LLM (auto-download from Hugging Face)
var agent = new MALDACodingAgent(name, role, instructions);

// With custom working directory
var agent = new MALDACodingAgent(name, role, instructions, workingDirectory);

// With custom client
var agent = new MALDACodingAgent(name, role, instructions, client);

// With custom client and working directory
var agent = new MALDACodingAgent(name, role, instructions, client, workingDirectory);

Esempio

// Simplest usage - MALDACodingAgent uses default local LLM
var maldaAgent = new MALDACodingAgent(
    "MALDADeveloper",
    "MALDA script developer",
    "You write, test, and compile MALDA scripts. Always verify your changes work by running the code."
);

// Agent already has all MALDA development tools - ready to use!
// Can handle MALDA workflow: edit, run, compile, test
var response = maldaAgent.think("Read example.malda, add error handling, test it with run_malda, and compile it");
print(response.content);

Esempio con client personalizzato

var client = new OpenRouterClient("openai/gpt-4");

var maldaAgent = new MALDACodingAgent(
    "MALDAMaster",
    "MALDA expert",
    "You develop MALDA scripts with best practices and thorough testing.",
    client
);

// Agent can handle MALDA development workflow
var response = maldaAgent.think("Create a new MALDA script that calculates fibonacci numbers, test it, and compile it");

Esempio con directory di lavoro

var client = new OpenRouterClient("openai/gpt-4");

var maldaAgent = new MALDACodingAgent(
    "MALDAHelper",
    "MALDA assistant",
    "You help develop and test MALDA scripts.",
    client,
    "./malda-scripts"  // Restrict operations to ./malda-scripts directory
);

// Agent can only access files within ./malda-scripts
var response = maldaAgent.think("List all MALDA files and improve error handling in each one");

Quando usare MALDACodingAgent, DevAgent o CodingAgent

18.15 Dashboard degli agenti

Gli agenti MALDA segnalano automaticamente le proprie attività a una dashboard centrale senza richiedere alcuna modifica agli script MALDA. Questo abilita monitoraggio e osservabilità su tutti gli agenti, anche quando girano in eseguibili o processi diversi.

Panoramica

La funzionalità dashboard degli agenti offre:

Configurazione

L'URL della dashboard si configura con la variabile d'ambiente SPL_AGENT_DASHBOARD_URL:

# Set custom dashboard URL
export SPL_AGENT_DASHBOARD_URL="http://my-dashboard.example.com/api/agent/status"

# If not set, defaults to:
# http://localhost:8080/api/agent/status

Nota: Se la variabile d'ambiente è impostata ma non termina con /api/agent/status, il path dell'endpoint viene accodato automaticamente.

Eventi segnalati

Gli agenti segnalano automaticamente i seguenti eventi:

  1. Agente creato (agent_created)
    • Segnalato quando un agente viene inizializzato
    • Include: nome dell'agente, ruolo, process ID, timestamp
  2. Operazione think (think)
    • Segnalata quando viene chiamato agent.think()
    • Include: nome dell'agente, prompt (troncato a 500 caratteri), lunghezza del prompt, process ID, timestamp
  3. Tool call (tool_call)
    • Segnalata quando i tool vengono eseguiti
    • Include: nome dell'agente, nome del tool, stato di successo, messaggio di errore (se fallita), process ID, timestamp
  4. Reset dell'agente (agent_reset)
    • Segnalato quando viene chiamato agent.reset()
    • Include: nome dell'agente, process ID, timestamp

Formato del payload della dashboard

Tutti gli eventi vengono inviati come richieste HTTP POST con payload JSON:

{
  "agentId": "MyAgent",
  "processId": 12345,
  "eventType": "think",
  "timestamp": "2026-01-15T10:30:00.123Z",
  "data": {
    "prompt": "Analyze this code...",
    "promptLength": 20
  }
}

Campi del payload:

Dati specifici dell'evento:

Requisiti dell'endpoint della dashboard

Il server della dashboard deve implementare un endpoint POST che accetta payload JSON:

Esempio di server dashboard (MALDA)

var server = new HttpServer(8080);
var agentStatuses = {};

@POST("/api/agent/status")
function receiveAgentStatus(body) {
    var agentId = body.agentId;
    var processId = body.processId;
    var eventType = body.eventType;
    var timestamp = body.timestamp;
    var data = body.data;
    
    // Store or process agent status
    if (agentStatuses[agentId] == null) {
        agentStatuses[agentId] = {};
    }
    agentStatuses[agentId][eventType] = {
        "processId": processId,
        "timestamp": timestamp,
        "data": data
    };
    
    return {"status": 200, "message": "Status received"};
}

@GET("/api/agents")
function getAllAgents() {
    return {"status": 200, "agents": agentStatuses};
}

@PAGE("/")
function dashboardPage() {
    // Generate HTML dashboard showing all agents
    return generateDashboardHTML(agentStatuses);
}

server.start();
print("Agent Dashboard running at http://localhost:8080");

Utilizzo

Nessuna modifica agli script MALDA richiesta!

Gli agenti segnalano automaticamente alla dashboard. Crea gli agenti come al solito:

var client = new OpenRouterClient();

var agent = new Agent(
    "MyAgent",
    "coder",
    "You write code.",
    client
);

// Agent automatically reports:
// 1. agent_created event when initialized
// 2. think event when think() is called
// 3. tool_call events when tools are executed
// 4. agent_reset event when reset() is called

var response = agent.think("Write a hello world function");
// Dashboard receives think and tool_call events automatically

Gestione degli errori

Il sistema di segnalazione alla dashboard è progettato per essere del tutto non invasivo:

Vantaggi

18.16 Orchestrazione multi-agente

Più agenti possono lavorare insieme, ciascuno con ruoli e tool specializzati.

Workflow di agenti in parallelo

Gli agenti possono lavorare in modo indipendente su task diversi:

var readerAgent = new Agent("Reader", "file reader", "You read and analyze files.", client);
readerAgent.addTool(createReadFileTool("."));

var editorAgent = new Agent("Editor", "code editor", "You edit code files.", client);
editorAgent.addTool(createReadFileTool("."));
editorAgent.addTool(createReplaceInFileTool("."));

var code = readerAgent.think("Read main.MALDA and summarize it.");
var edited = editorAgent.think("Read main.MALDA and add error handling.");

Sistemi di agenti gerarchici con addSubAgent

Il metodo addSubAgent consente di creare sistemi di agenti gerarchici in cui un agente orchestratore può delegare i task a subagenti specializzati. È utile quando vuoi separare le istruzioni interne dettagliate dalla descrizione del tool mostrata all'orchestratore.

Vantaggio chiave: addSubAgent usa una descrizione semplificata e orientata al tool, non le istruzioni interne complete dell'agente, così il contesto dell'orchestratore resta pulito e focalizzato.

Sintassi

orchestrator.addSubAgent(subAgent, toolDescription);

Esempio

var client = new OpenRouterClient();

// Create specialized subagents with detailed internal instructions
var codeReviewer = new Agent(
    "CodeReviewer",
    "code review specialist",
    "You are an expert code reviewer. When reviewing code, you should:\n" +
    "1. Check for bugs and logical errors\n" +
    "2. Review code style and best practices\n" +
    "3. Suggest improvements\n" +
    "4. Consider performance implications\n" +
    "5. Check for security vulnerabilities\n" +
    "Provide detailed feedback with line numbers and specific suggestions.",
    client
);

var tester = new Agent(
    "Tester",
    "testing specialist",
    "You are an expert in software testing. When asked to write tests:\n" +
    "1. Analyze the code to understand what needs to be tested\n" +
    "2. Write comprehensive test cases covering edge cases\n" +
    "3. Include both positive and negative test cases\n" +
    "4. Ensure tests are clear and maintainable\n" +
    "5. Consider boundary conditions and error handling",
    client
);

// Create orchestrator agent
var orchestrator = new Agent(
    "Orchestrator",
    "project coordinator",
    "You coordinate tasks between specialized agents. " +
    "When you need code reviewed, use the CodeReviewer tool. " +
    "When you need tests written, use the Tester tool. " +
    "Delegate work to the appropriate specialist agent.",
    client
);

// Add subagents with simplified, tool-focused descriptions
orchestrator.addSubAgent(
    codeReviewer,
    "Reviews code for bugs, style issues, and improvements. Provide the code to review as the prompt."
);

orchestrator.addSubAgent(
    tester,
    "Writes comprehensive test cases for code. Provide the code to test as the prompt."
);

// Orchestrator can now delegate to subagents
var code = "function add(a, b) {\n    return a + b;\n}";
var response = orchestrator.think(
    "I have this code: " + code + "\n\n" +
    "Please have it reviewed and then write tests for it."
);
print(response.content);

Quando l'orchestratore chiama un tool subagente, passa un parametro prompt e il metodo think() del subagente viene chiamato automaticamente. La risposta del subagente torna all'orchestratore.

Vedi anche

18.17 Usare gli agenti con gli actor

Agenti e actor sono funzionalità complementari in MALDA. Gli agenti sono classi pensate per interazioni AI sincrone, mentre gli actor offrono elaborazione concorrente basata su messaggi. Combinarli abilita pattern potenti per i sistemi AI distribuiti.

Scelta di progetto: Gli agenti sono implementati come classi e non come actor nativi perché:

Agente come stato dell'actor

Il pattern più comune è memorizzare un'istanza di agente come campo di un actor. Così l'actor può usare le capacità AI mantenendo stato isolato ed elaborazione concorrente:

actor AIAssistant {
    var agent;
    var conversationHistory = [];
    
    function AIAssistant(client) {
        // Create an agent and store it in actor state
        agent = new Agent(
            "Assistant",
            "helpful assistant",
            "You help users with their questions.",
            client
        );
    }
    
    on ask(question) {
        // Use the agent to process questions
        var response = agent.think(question);
        append(conversationHistory, question);
        append(conversationHistory, response.content);
        print(response.content);
    }
    
    on getHistory() {
        print(conversationHistory);
    }
}

// Spawn multiple AI assistants with isolated state
var client = new OpenRouterClient();
var assistant1 = spawn AIAssistant(client);
var assistant2 = spawn AIAssistant(client);

send assistant1.ask("What is 2+2?");
send assistant2.ask("What is the weather?");

Orchestrazione concorrente di agenti

Gli actor abilitano l'elaborazione davvero concorrente di più agenti. Gli agenti sono sincroni, ma avvolgerli in actor consente l'esecuzione in parallelo:

actor CodingAgentActor {
    var agent;
    var taskQueue = [];
    
    function CodingAgentActor(client, workingDir) {
        agent = new CodingAgent(
            "Coder",
            "software engineer",
            "You write clean, efficient code.",
            client,
            workingDir
        );
    }
    
    on processTask(task) {
        var result = agent.think(task);
        print($"Task completed: {result.content}");
    }
}

// Spawn multiple coding agents for parallel processing
var client = new OpenRouterClient();
var agent1 = spawn CodingAgentActor(client, "./project1");
var agent2 = spawn CodingAgentActor(client, "./project2");
var agent3 = spawn CodingAgentActor(client, "./project3");

// Send tasks concurrently - all process in parallel
send agent1.processTask("Refactor the user authentication module");
send agent2.processTask("Add error handling to the API");
send agent3.processTask("Write unit tests for the database layer");

Sistema di actor multi-agente

Puoi orchestrare più agenti specializzati all'interno di un singolo actor per creare workflow complessi:

actor AgentOrchestrator {
    var coderAgent;
    var reviewerAgent;
    var testerAgent;
    
    function AgentOrchestrator(client) {
        coderAgent = new CodingAgent("Coder", "developer", "Write clean code.", client);
        reviewerAgent = new Agent("Reviewer", "code reviewer", "Review code for quality.", client);
        testerAgent = new Agent("Tester", "QA engineer", "Write comprehensive tests.", client);
    }
    
    on develop(feature) {
        // Step 1: Code
        var code = coderAgent.think($"Write code for: {feature}");
        
        // Step 2: Review
        var review = reviewerAgent.think($"Review this code: {code.content}");
        
        // Step 3: Test
        var tests = testerAgent.think($"Write tests for: {code.content}");
        
        return {
            "code": code.content,
            "review": review.content,
            "tests": tests.content
        };
    }
}

var client = new OpenRouterClient();
var orchestrator = spawn AgentOrchestrator(client);
send orchestrator.develop("User authentication system");

Persistenza dello stato dell'agente tramite actor

Gli actor possono gestire e persistere lo stato della conversazione dell'agente, abilitando sessioni di agente di lunga durata:

actor AgentStateManager {
    var agents = {};  // Dictionary of agent instances
    var conversations = {};  // Conversation history per agent
    
    on createAgent(name, role, instructions, client) {
        var agent = new Agent(name, role, instructions, client);
        agents[name] = agent;
        conversations[name] = [];
    }
    
    on queryAgent(agentName, question) {
        if (has(agents, agentName)) {
            var agent = agents[agentName];
            var response = agent.think(question);
            append(conversations[agentName], {
                "question": question,
                "answer": response.content
            });
            return response.content;
        }
        return "Agent not found";
    }
    
    on getHistory(agentName) {
        return conversations[agentName];
    }
}

var client = new OpenRouterClient();
var stateManager = spawn AgentStateManager();
send stateManager.createAgent("Helper", "assistant", "You help users", client);

send stateManager.queryAgent("Helper", "What is MALDA?");

Vantaggi nel combinare agenti e actor

Vedi anche

18.18 Ralph Wiggum (loop autonomo guidato da PRD)

Il repository include Examples/RalphWiggum/RalphWiggum.malda, un loop autonomo pronto per la produzione per lo sviluppo guidato da PRD. Usa DevAgent, un PRD a checklist (PRD.md), validazione post-iterazione, stato di resume e GraphMemory per la continuità tra le iterazioni quando la cronologia della conversazione viene azzerata a ogni round. Vedi Examples/RalphWiggum/README.md per i prerequisiti e la demo Snake.

Esecuzione

dotnet run --project MaldaLang -- Examples/RalphWiggum/RalphWiggum.malda
# Or from a project directory with PRD.md:
# set MALDA_RALPH_WORKDIR=Examples/RalphWiggum/snake-demo
# Examples\RalphWiggum\snake-demo\run-ralph.bat

Artefatti persistiti (nella workdir)

Modello di memoria

Ralph collega una GraphMemory condivisa all'agente tramite setupRalphGraphMemory() e:

Ispeziona la memoria di Ralph dalla workdir: malda memory stats --path .ralph-memory. Vedi Examples/RalphWiggum/README.md per le variabili MALDA_RALPH_MEMORY_*.

Variabili d'ambiente principali

Moduli di implementazione: Examples/RalphWiggum/ralph/*.malda (vedi ralph/ARCHITECTURE.md). Elenco completo delle variabili d'ambiente e workflow git-worktree: Examples/RalphWiggum/README.md.

Segnali di completamento

Ralph accetta TASK_COMPLETE (senza distinzione di maiuscole/minuscole), una riga dedicata RALPH_DONE, JSON {"ralph":"done"}, oppure il completamento automatico quando ogni voce del PRD è [DONE] e la validazione passa (a meno che MALDA_RALPH_REQUIRE_SIGNAL=true).

Hook di validazione personalizzati

Dopo i controlli strutturali, Ralph può eseguire MALDA_RALPH_VALIDATE_CMD, oppure uno script locale del progetto .ralph-validate.sh / .ralph-validate.bat / (o .ralph-validate.malda) nella workdir.

Strategia di esecuzione

Ralph chiede all'agente di completare una voce della checklist PRD per iterazione nel minor numero possibile di round LLM: esplorazione in sola lettura in batch (list_directory, grep, read_file) in una sola risposta, poi modifiche e verifica in batch. Questo si abbina all'esecuzione parallela dei tool in sola lettura (18.5) e al feedback [think] in streaming durante le esecuzioni verbose.

Vedi anche 19. GraphMemory per i metadati di remember, getRecent e le opzioni ibride di query.

reportRalphStatus(agentName, phase, iteration, maxIter, prdPercent, validationOk?, elapsedMs?, promptTokens?, completionTokens?, costUsd?) pubblica una iterazione di progresso sulla dashboard degli agenti. I primi cinque argomenti sono obbligatori; gli altri hanno default false, 0, 0, 0 e 0.0.

reportRalphStatus("Ralph", "implement", 3, 10, 45, true, 12000, 1800, 640, 0.02);