Documentación
Sistema de Agentes

Sistema de Agentes AI

Fox Framework v1.3 incorpora un sistema completo de agentes AI directamente en el core del framework. Permite construir agentes inteligentes que razonan, usan herramientas, mantienen memoria, y pueden orquestarse en sistemas multi-agente.

Arquitectura

tsfox/core/agents/
├── interfaces/       # IAgent, IModelProvider, ITool, IMemoryStore, IOrchestrator
├── errors/           # AgentError, MaxIterationsError, ToolExecutionError, ...
├── base/             # BaseAgent, InMemoryStore
├── react/            # ReActAgent
├── orchestrator/     # Orchestrator multi-agente
└── integrations/     # Event, Auth, Cache, Metrics

Instalación

El sistema de agentes está incluido en @foxframework/core. Para usar un modelo LLM necesitas un provider:

npm install @foxframework/core
npm install @foxframework/model-openai   # o model-anthropic, model-ollama

ReActAgent

El ReActAgent implementa el loop Reasoning + Acting: el modelo razona qué hacer, elige una herramienta, observa el resultado, y repite hasta tener una respuesta final.

import { ReActAgent } from '@foxframework/core/agents';
import { OpenAIProvider } from '@foxframework/model-openai';
 
const agent = new ReActAgent({
  model: new OpenAIProvider({ apiKey: process.env.OPENAI_API_KEY }),
  tools: [searchTool, calculatorTool],
  maxIterations: 10,
  systemPrompt: 'You are a helpful research assistant.',
});
 
const result = await agent.run('What is the population of Tokyo?');
console.log(result.output);

Definir una Tool

import { ITool } from '@foxframework/core/agents';
 
const searchTool: ITool = {
  name: 'search',
  description: 'Search the web for information',
  parameters: {
    type: 'object',
    properties: {
      query: { type: 'string', description: 'Search query' }
    },
    required: ['query']
  },
  execute: async ({ query }) => {
    // tu lógica de búsqueda
    return `Results for: ${query}`;
  }
};

Abortar una ejecución

const controller = new AbortController();
setTimeout(() => controller.abort(), 5000); // timeout de 5s
 
const result = await agent.run('Long task...', { signal: controller.signal });

Orchestrator

El Orchestrator coordina múltiples agentes. Usa el LLM para crear un plan de subtareas, las ejecuta en waves (respetando dependencias), y sintetiza el resultado final.

import { Orchestrator } from '@foxframework/core/agents';
import { ReActAgent } from '@foxframework/core/agents';
 
const orchestrator = new Orchestrator({
  model: new OpenAIProvider({ apiKey: process.env.OPENAI_API_KEY }),
  agents: { research: researchAgent, analysis: analysisAgent },
  maxConcurrency: 3,
});
 
const result = await orchestrator.run(
  'Research climate change and provide a summary with key statistics.'
);

Ejecución por waves

Las subtareas con dependsOn se ejecutan después de que sus dependencias terminen. Las tareas sin dependencias corren en paralelo dentro de la misma wave:

Wave 1: [research_task]          ← sin dependencias
Wave 2: [analysis_task]          ← dependsOn: research_task
Wave 3: [summary_task]           ← dependsOn: analysis_task

Memoria

Todos los agentes tienen acceso a un IMemoryStore. Por defecto usa InMemoryStore con búsqueda por palabras clave.

// Guardar en memoria
await agent.memory.set('user_preference', { theme: 'dark' });
 
// Recuperar
const pref = await agent.memory.get('user_preference');
 
// Búsqueda semántica por keyword
const results = await agent.memory.search('user preferences');

Integraciones

Event Integration

Conecta el ciclo de vida del agente al sistema de eventos de Fox:

import { AgentEventBus, AgentEventSubscriber } from '@foxframework/core/agents';
 
// Emitir eventos del agente al bus
const eventedAgent = new AgentEventBus(agent, eventEmitter);
const result = await eventedAgent.run('task');
// Emite: agent.started, agent.completed, agent.failed, agent.tool_called
 
// Ejecutar agentes en respuesta a eventos
const subscriber = new AgentEventSubscriber(eventBus, agent);
subscriber.subscribe('user.message');

Auth Integration

import { AuthenticatedAgent, AgentRateLimit } from '@foxframework/core/agents';
 
const secureAgent = new AuthenticatedAgent(agent, tokenValidator, {
  requiredRoles: ['admin'],
});
 
// Rate limiting por userId
const rateLimited = new AgentRateLimit(agent, {
  maxRequests: 10,
  windowMs: 60_000,
});
 
const result = await secureAgent.run('task', {
  authToken: 'Bearer eyJ...',
});

Cache Integration

import { CachedAgent, InMemoryAgentCache } from '@foxframework/core/agents';
 
const cachedAgent = new CachedAgent(agent, new InMemoryAgentCache(), {
  ttl: 5 * 60 * 1000, // 5 minutos
});
// Respuestas idénticas se sirven del cache (key = SHA-256 del input)

Metrics Integration

import { AgentMetrics, AgentMetricsRegistry } from '@foxframework/core/agents';
 
const tracked = new AgentMetrics(agent);
await tracked.run('task');
 
console.log(tracked.metrics);
// {
//   totalRuns: 1, successRuns: 1, failedRuns: 0,
//   totalTokens: 342, avgLatency: 1240, p95Latency: 1240,
//   lastRunAt: Date
// }
 
// Registro global de todos los agentes
const registry = new AgentMetricsRegistry();
registry.register('myAgent', tracked);
const report = registry.report();

Interfaces

IAgent

interface IAgent {
  readonly id: string;
  readonly status: AgentStatus; // 'idle' | 'running' | 'completed' | 'failed' | 'aborted'
  run(input: string, options?: RunOptions): Promise<AgentResult>;
  abort(): void;
}

IModelProvider

interface IModelProvider {
  complete(messages: Message[], options?: CompletionOptions): Promise<CompletionResult>;
  stream?(messages: Message[], options?: CompletionOptions): AsyncGenerator<string>;
}

ITool

interface ITool {
  definition: {
    name: string;
    description: string;
    parameters: JSONSchema;
  };
  execute(params: Record<string, unknown>, context: AgentContext): Promise<string>;
}

Built-in Tools (v1.4)

Fox Framework ships six ready-to-use tools in @foxframework/core/agents/tools. Import them individually:

import {
  CalculatorTool,
  HttpTool,
  FilesystemTool,
  createFilesystemTool,
  JsonPathTool,
  createSqlQueryTool,
  createVectorSearchTool,
} from '@foxframework/core/agents/tools';

CalculatorTool

Evaluates mathematical expressions safely (no eval). Supports arithmetic, **, %, and functions: sqrt, abs, ceil, floor, round, log, sin, cos, tan. Constants: PI, E.

const agent = new ReActAgent({
  model,
  tools: [CalculatorTool],
});
// Agent input: "What is sqrt(144) + 2**8?"
// Tool call: { expression: "sqrt(144) + 2**8" }
// Result: "sqrt(144) + 2**8 = 268"

HttpTool

Makes HTTP requests (GET, POST, PUT, PATCH, DELETE) using native fetch. Returns formatted JSON or plain text.

const agent = new ReActAgent({
  model,
  tools: [HttpTool],
});
// Agent input: "Get the latest rates from https://api.exchangerate.host/latest"

FilesystemTool

Reads, writes, appends, lists, and deletes files/directories. Use createFilesystemTool to restrict access to a safe directory:

// Unrestricted (default)
import { FilesystemTool } from '@foxframework/core/agents/tools';
 
// Restricted to a specific directory
const safeTool = createFilesystemTool({ allowedBase: '/tmp/agent-workspace' });
 
const agent = new ReActAgent({ model, tools: [safeTool] });

Operations: read, write, append, list, exists, delete, mkdir.

JsonPathTool

Queries and extracts data from JSON using path expressions:

const agent = new ReActAgent({ model, tools: [JsonPathTool] });
// Tool call: { json: '{"users":[{"name":"Alice"}]}', path: ".users[0].name", operation: "get" }
// Result: '"Alice"'

Operations: get, keys, count, flatten, pretty.

SqlQueryTool (factory)

Wraps any IQueryExecutor to let the agent run SQL. Read-only by default; set allowMutations: true to allow writes:

import { createSqlQueryTool } from '@foxframework/core/agents/tools';
import { PostgresProvider } from '@foxframework/db-postgres';
 
const db = new PostgresProvider({ connectionString: process.env.DATABASE_URL });
 
const sqlTool = createSqlQueryTool(db, {
  allowMutations: false, // only SELECT (default)
  maxRows: 50,           // cap results (default: 100)
  label: 'database',    // tool name shown to the model
});
 
const agent = new ReActAgent({ model, tools: [sqlTool] });

VectorSearchTool (factory)

Wraps any IVectorSearchProvider (Pinecone, Weaviate, Chroma, or custom) for semantic retrieval:

import { createVectorSearchTool } from '@foxframework/core/agents/tools';
import { PineconeProvider } from '@foxframework/vector-pinecone';
 
const vectorProvider = new PineconeProvider({
  apiKey: process.env.PINECONE_API_KEY!,
  indexHost: process.env.PINECONE_INDEX_HOST!,
  embed: async (text) => myEmbeddingFn(text),
});
 
const vectorTool = createVectorSearchTool(vectorProvider, {
  label: 'knowledge_search',
  defaultTopK: 5,
});
 
const agent = new ReActAgent({ model, tools: [vectorTool] });

Streaming (v1.4)

Stream agent steps to the browser over Server-Sent Events (SSE). Three layers of abstraction:

SseStream — low-level

Wraps any ServerResponse-compatible object and writes formatted SSE frames:

import { SseStream } from '@foxframework/core/agents/streaming';
 
app.get('/sse', (req, res) => {
  const sse = new SseStream(res); // sets SSE headers automatically
  sse.send({ event: 'message', data: { text: 'Hello!' } });
  sse.close();
});

AgentSseEmitter — mid-level

Runs an agent and streams every step as SSE events:

import { AgentSseEmitter } from '@foxframework/core/agents/streaming';
 
app.get('/agent/stream', async (req, res) => {
  const emitter = new AgentSseEmitter(myAgent, res, {
    heartbeatIntervalMs: 15_000, // keeps proxy connections alive (default: 15s)
  });
  await emitter.run(req.query.input as string);
});

SSE events emitted:

EventPayloadWhen
step{ stepNumber, type, content, toolCall?, toolResult? }After each agent step
done{ runId, answer, status, usage, stepCount }Run completed
error{ message }Run threw an exception
heartbeat{}Every heartbeatIntervalMs

createAgentSseHandler — route factory

Creates a ready-to-use Express/Fox request handler:

import { createAgentSseHandler } from '@foxframework/core/agents/streaming';
 
// Default: reads input from req.query.input, req.body.input, or req.body.message
app.get('/stream', createAgentSseHandler(myAgent));
 
// Custom input extraction
app.post('/stream', createAgentSseHandler(myAgent, {
  getInput: (req) => (req.body as any).question,
  heartbeatIntervalMs: 10_000,
}));

Client-side consumption

const es = new EventSource('/stream?input=What+is+the+capital+of+France%3F');
 
es.addEventListener('step', (e) => {
  const step = JSON.parse(e.data);
  console.log(`[${step.type}] ${step.content}`);
});
 
es.addEventListener('done', (e) => {
  const result = JSON.parse(e.data);
  console.log('Answer:', result.answer);
  es.close();
});
 
es.addEventListener('error', (e) => {
  console.error('Agent error:', JSON.parse(e.data).message);
  es.close();
});

Telemetry / Tracing (v1.4)

AgentTracer

AgentTracer is a proxy that wraps any IAgent and emits spans for every run and tool call. It is vendor-neutral — plug in any ITracer backend.

import { AgentTracer } from '@foxframework/core/agents/telemetry';
 
// With a no-op tracer (default — zero overhead)
const tracedAgent = new AgentTracer(myAgent);
 
// With a custom tracer
const tracedAgent = new AgentTracer(myAgent, { tracer: myTracer });

Spans emitted:

SpanAttributes
agent.runagent.id, agent.name, agent.input, run.id, run.status, run.stepCount, run.answer, llm.*_tokens
agent.tool_calltool.name, tool.call_id, tool.arguments, agent.id, step.number

ITracer interface

Implement ITracer for any tracing backend:

import type { ITracer, ISpan } from '@foxframework/core/agents/telemetry';
 
class ConsoleTracer implements ITracer {
  startSpan(name: string): ISpan {
    const start = Date.now();
    console.log(`[span start] ${name}`);
    return {
      setAttribute: (k, v) => { console.log(`  ${k}=${v}`); return this as any; },
      recordException: (e) => { console.error('  exception:', e); return this as any; },
      setStatus: (s, m) => { console.log(`  status=${s}`, m ?? ''); return this as any; },
      end: () => console.log(`[span end] ${name} (${Date.now() - start}ms)`),
    };
  }
}
 
const tracedAgent = new AgentTracer(myAgent, { tracer: new ConsoleTracer() });

OpenTelemetry via @foxframework/otel-agents

For production OTel tracing, install the adapter package:

npm install @foxframework/otel-agents @opentelemetry/api @opentelemetry/sdk-node
import { trace } from '@opentelemetry/api';
import { OtelAgentTracer } from '@foxframework/otel-agents';
import { AgentTracer } from '@foxframework/core/agents/telemetry';
 
// Assumes OTel SDK is already initialised (NodeSDK.start())
const tracer = new OtelAgentTracer(trace.getTracer('fox-agents', '1.0.0'));
const tracedAgent = new AgentTracer(myAgent, { tracer });
 
// All runs now emit OTel spans compatible with Jaeger, Zipkin, OTLP, etc.
const result = await tracedAgent.run('Summarise the quarterly report.');