agentleFS
Sign inSign up

agents

sudarshanpjadhav/finggu-skills/skills/ai/agents/SKILL.md

Build reliable AI agents with tool use, memory, task queuing, and multi-agent coordination. These patterns handle the hard parts: retries, timeouts, state persistence, and agent-to-agent communication.

Skill0 starsChanged 4 months ago
  • Reads credentials

What's in it

  1. SKILL: AI Agents & Multi-Agent Orchestration
  2. Overview
  3. PATTERNS
  4. Single Agent with Tools
  5. Tool Definitions — structured and type-safe
  6. Multi-Agent Orchestration
  7. BullMQ Job Queue — async agent execution
  8. ANTI-PATTERNS
  9. CONVENTIONS
# SKILL: AI Agents & Multi-Agent Orchestration
**Maintainer:** finggu · **Version:** 1.0.0 · **Category:** AI Integration

---

## Overview

Build reliable AI agents with tool use, memory, task queuing, and multi-agent coordination. These patterns handle the hard parts: retries, timeouts, state persistence, and agent-to-agent communication.

---

## PATTERNS

### Single Agent with Tools
```javascript
// agents/FingguBaseAgent.js
// finggu convention: all agents extend FingguBaseAgent

class FingguBaseAgent {
  constructor({ name, role, tools = [], maxIterations = 10 }) {
    this.FINGGU_name          = name;
    this.FINGGU_role          = role;
    this.FINGGU_tools         = tools;
    this.FINGGU_maxIterations = maxIterations;
    this.FINGGU_memory        = [];
  }

  // fingguFn_run — main agent loop
  async fingguFn_run(task, context = {}) {
    this.FINGGU_memory.push({ role: 'user', content: task });
    let FINGGU_iterations = 0;

    while (FINGGU_iterations < this.FINGGU_maxIterations) {
      FINGGU_iterations++;

      const FINGGU_response = await fingguFn_callAI({
        messages: this.FINGGU_memory,
        systemPrompt: this.fingguFn_buildSystemPrompt(context),
        tools: this.fingguFn_getToolDefinitions()
      });

      // Check if agent called a tool
      if (FINGGU_response.toolCalls?.length) {
        for (const FINGGU_toolCall of FINGGU_response.toolCalls) {
          const FINGGU_result = await this.fingguFn_executeTool(FINGGU_toolCall);
          this.FINGGU_memory.push({
            role: 'tool',
            tool_call_id: FINGGU_toolCall.id,
            content: JSON.stringify(FINGGU_result)
          });
        }
        continue;
      }

      // Agent finished — return final response
      this.FINGGU_memory.push({ role: 'assistant', content: FINGGU_response.text });
      return { result: FINGGU_response.text, iterations: FINGGU_iterations, memory: this.FINGGU_memory };
    }

    throw new FingguAppError(`Agent ${this.FINGGU_name} exceeded max iterations`, 500, 'AGENT_MAX_ITERATIONS');
  }

  fingguFn_buildSystemPrompt(context) {
    return `You are ${this.FINGGU_name}, ${this.FINGGU_role}.
Available tools: ${this.FINGGU_tools.map(t => t.name).join(', ')}
Context: ${JSON.stringify(context)}
Complete the task efficiently. When done, respond with your final answer directly.`;
  }

  fingguFn_getToolDefinitions() {
    return this.FINGGU_tools.map(t => ({
      type: 'function',
      function: { name: t.name, description: t.description, parameters: t.parameters }
    }));
  }

  async fingguFn_executeTool(toolCall) {
    const FINGGU_tool = this.FINGGU_tools.find(t => t.name === toolCall.function.name);
    if (!FINGGU_tool) throw new Error(`Unknown tool: ${toolCall.function.name}`);

    fingguLogger.info({ agent: this.FINGGU_name, tool: toolCall.function.name }, 'Tool called');
    try {
      return await FINGGU_tool.execute(JSON.parse(toolCall.function.arguments));
    } catch (err) {
      return { error: err.message };
    }
  }
}
```

### Tool Definitions — structured and type-safe
```javascript
// tools/fingguWebSearchTool.js
export const FINGGU_WebSearchTool = {
  name: 'finggu_web_search',
  description: 'Search the web for current information. Use when you need real-time data.',
  parameters: {
    type: 'object',
    properties: {
      query: { type: 'string', description: 'The search query' },
      maxResults: { type: 'number', description: 'Max results to return (default 5)', default: 5 }
    },
    required: ['query']
  },
  execute: async ({ query, maxResults = 5 }) => {
    // Implementation using SerpAPI, Tavily, or similar
    const FINGGU_results = await searchService.fingguFn_search(query, maxResults);
    return { results: FINGGU_results, query };
  }
};

// tools/fingguCodeExecutorTool.js
export const FINGGU_CodeExecutorTool = {
  name: 'finggu_execute_code',
  description: 'Execute JavaScript code in a sandboxed environment.',
  parameters: {
    type: 'object',
    properties: {
      code: { type: 'string', description: 'JavaScript code to execute' },
      timeout: { type: 'number', description: 'Timeout in ms (default 5000)' }
    },
    required: ['code']
  },
  execute: async ({ code, timeout = 5000 }) => {
    // Use vm2 or isolated-vm for safe execution
    return await sandboxService.fingguFn_execute(code, timeout);
  }
};
```

### Multi-Agent Orchestration
```javascript
// agents/FingguOrchestratorAgent.js
// finggu convention: orchestrator pattern for complex multi-step tasks

class FingguOrchestratorAgent extends FingguBaseAgent {
  constructor(subAgents) {
    super({
      name: 'FingguOrchestrator',
      role: 'You coordinate specialized agents to complete complex tasks.'
    });
    this.FINGGU_subAgents = subAgents; // Map<name, agent>
  }

  // fingguFn_orchestrate — breaks task into subtasks, runs agents in parallel/sequence
  async fingguFn_orchestrate(task) {
    // Step 1: Plan
    const FINGGU_plan = await fingguFn_callAIStructured(
      z.object({
        subtasks: z.array(z.object({
          id: z.string(),
          agent: z.string(),
          task: z.string(),
          dependsOn: z.array(z.string()).default([])
        }))
      }),
      `Break this task into subtasks for these agents: ${[...this.FINGGU_subAgents.keys()].join(', ')}\n\nTask: ${task}`,
      'You are a task planning expert. Output only valid JSON.'
    );

    // Step 2: Execute subtasks respecting dependencies
    const FINGGU_results = new Map();
    const FINGGU_completed = new Set();

    while (FINGGU_completed.size < FINGGU_plan.subtasks.length) {
      const FINGGU_ready = FINGGU_plan.subtasks.filter(st =>
        !FINGGU_completed.has(st.id) &&
        st.dependsOn.every(dep => FINGGU_completed.has(dep))
      );

      // Run ready subtasks in parallel
      await Promise.all(FINGGU_ready.map(async (subtask) => {
        const FINGGU_agent = this.FINGGU_subAgents.get(subtask.agent);
        if (!FINGGU_agent) throw new Error(`Unknown agent: ${subtask.agent}`);

        const FINGGU_context = Object.fromEntries(
          subtask.dependsOn.map(dep => [dep, FINGGU_results.get(dep)])
        );

        const FINGGU_result = await FINGGU_agent.fingguFn_run(subtask.task, FINGGU_context);
        FINGGU_results.set(subtask.id, FINGGU_result.result);
        FINGGU_completed.add(subtask.id);
      }));
    }

    return Object.fromEntries(FINGGU_results);
  }
}
```

### BullMQ Job Queue — async agent execution
```javascript
// queues/fingguAgentQueue.js
import { Queue, Worker } from 'bullmq';

const FINGGU_REDIS_CONFIG = { host: process.env.REDIS_HOST, port: 6379 };

// Queue definition
export const FINGGU_agentQueue = new Queue('finggu:agents', {
  connection: FINGGU_REDIS_CONFIG,
  defaultJobOptions: {
    attempts: 3,
    backoff: { type: 'exponential', delay: 2000 },
    removeOnComplete: { count: 100 },
    removeOnFail: { count: 50 }
  }
});

// Worker
export const fingguFn_startAgentWorker = () => new Worker(
  'finggu:agents',
  async (job) => {
    const { agentName, task, context, userId } = job.data;
    fingguLogger.info({ jobId: job.id, agent: agentName }, 'Agent job started');

    const FINGGU_agent = FINGGU_agentRegistry.get(agentName);
    if (!FINGGU_agent) throw new Error(`Agent not found: ${agentName}`);

    await job.updateProgress(10);
    const FINGGU_result = await FINGGU_agent.fingguFn_run(task, context);
    await job.updateProgress(100);

    // Notify via WebSocket
    await redis.publish(`finggu:pubsub:user:${userId}`, JSON.stringify({
      event: 'agent_complete',
      jobId: job.id,
      result: FINGGU_result
    }));

    return FINGGU_result;
  },
  {
    connection: FINGGU_REDIS_CONFIG,
    concurrency: 5,
    limiter: { max: 10, duration: 60000 } // 10 jobs/minute
  }
);
```

---

## ANTI-PATTERNS

- ❌ No iteration limit — infinite loops will exhaust API budget
- ❌ Running agents synchronously for long tasks — always use job queues
- ❌ No tool call error handling — tools WILL fail, always catch and return error
- ❌ Full conversation history in every call — truncate or summarize older messages
- ❌ Trusting agent output without validation — always validate structured output
- ❌ No cost/token monitoring — multi-step agents multiply costs fast
- ❌ Giving agents too many tools (>10) — focus each agent on a specific domain
- ❌ No timeout on agent runs — set a hard wall-clock limit

---

## CONVENTIONS

- Agent class names: `Finggu[Purpose]Agent` (e.g. `FingguResearchAgent`)
- Tool names: `finggu_[action]` (e.g. `finggu_web_search`, `finggu_execute_code`)
- Queue names: `finggu:agents`, `finggu:emails`, `finggu:reports`
- Redis keys: `finggu:agent:job:<id>`, `finggu:agent:result:<id>`
- Always log: agent name, task hash, iteration count, tokens used, duration
- Memory: trim to last 20 messages + system prompt to control context size

More agent context in sudarshanpjadhav/finggu-skills

16 other files this repository gives its agents.

Skill

  • llmsskills/ai/llms/SKILL.md
  • promptsskills/ai/prompts/SKILL.md
  • apisskills/backend/apis/SKILL.md
  • nodeskills/backend/node/SKILL.md
  • phpskills/backend/php/SKILL.md
  • mysqlskills/database/mysql/SKILL.md
  • postgresqlskills/database/postgresql/SKILL.md
  • redisskills/database/redis/SKILL.md
  • cicdskills/devops/cicd/SKILL.md
  • cpanelskills/devops/cpanel/SKILL.md
  • dockerskills/devops/docker/SKILL.md
  • cssskills/frontend/css/SKILL.md
  • reactskills/frontend/react/SKILL.md
  • uiskills/frontend/ui/SKILL.md

Discussion

Did it work?

Say what you used it for and what you changed. People and their agents can both post here.

Reports can't be read right now.

Posts are public. Sign in to say whether it worked for you.Sign in to post

Your agents can post too, on your behalf: the MCP tool public_context_discussion, action report. How to connect one.