crewai-ts
Version:
TypeScript port of crewAI for agent-based workflows
204 lines (203 loc) • 8.24 kB
JavaScript
/**
* Agent Builder
* Concrete implementation of BaseAgentBuilder for creating optimized agents
*/
import { BaseAgentBuilder } from './BaseAgentBuilder.js';
import { CrewAgentExecutorMixin } from './CrewAgentExecutorMixin.js';
/**
* Complete agent implementation with optimized execution and memory management
*/
export class AgentBuilder extends BaseAgentBuilder {
// Agent executor for running tasks
agentExecutor;
// Stats for performance tracking
executionStats = {
tokenCount: { prompt: 0, completion: 0, total: 0 },
iterations: 0
};
/**
* Constructor for the AgentBuilder
* @param config Configuration for the agent
*/
constructor(config) {
super(config);
}
/**
* Execute a task with this agent
* Optimized with token tracking and efficient resource usage
* @param task Task to execute
* @param context Additional context for the task
* @param tools Optional tools for the task
*/
async executeTask(task, context, tools) {
// Reset stats for this execution
this.resetExecutionStats();
// Start tracking execution time
this.executionStats.startTime = Date.now();
// Lazy initialize the agent executor if needed
if (!this.agentExecutor) {
this.agentExecutor = await this.createAgentExecutor(tools, task);
}
try {
// Create executor mixin for managing memory and feedback
const executorMixin = new CrewAgentExecutorMixin(this, // Type cast is needed here
task, undefined, // No crew for now
{
maxIterations: this._maxIterations,
memory: this._memory,
humanInTheLoop: false, // Default to no human interaction
});
// Prepare the task input with context if available
const input = context ? { input: context } : {};
// Execute the task with the agent executor
const result = await this.agentExecutor.invoke(input);
// Process the result through the mixin
const processedResult = await executorMixin.processResult({
text: result.output || result
});
// Update execution stats
this.executionStats.endTime = Date.now();
this.executionStats.iterations = executorMixin.getIterations();
// Extract token usage from the result if available
if (result.tokenUsage) {
this.executionStats.tokenCount = {
prompt: result.tokenUsage.prompt_tokens || 0,
completion: result.tokenUsage.completion_tokens || 0,
total: result.tokenUsage.total_tokens || 0
};
}
return {
output: processedResult.text,
metadata: {
executionTime: this.getExecutionTime(),
iterations: this.executionStats.iterations,
totalTokens: this.executionStats.tokenCount.total,
promptTokens: this.executionStats.tokenCount.prompt,
completionTokens: this.executionStats.tokenCount.completion,
}
};
}
catch (error) {
// Handle errors gracefully
console.error(`Error executing task with agent ${this.role}:`, error);
throw error;
}
}
/**
* Get tools for delegating tasks to other agents
* Creates optimized tool representations of other agents
* @param agents Agents that can be delegated to
*/
getDelegationTools(agents) {
// If delegation is not allowed, return empty array
if (!this._allowDelegation) {
return [];
}
return agents.map(agent => {
// Don't create a delegation tool for this agent itself
if (agent.id === this.id) {
return null;
}
// Create a tool that delegates to the agent
const delegationTool = {
name: `delegate_to_${agent.role.toLowerCase().replace(/\s+/g, '_')}`,
description: `Delegate a task to ${agent.role} who has the goal: ${agent.goal}`,
verbose: false,
cacheResults: false,
execute: async ({ task }) => {
// In a real implementation, this would create a Task object and execute it
return `Task "${task}" delegated to ${agent.role}`;
},
getMetadata: () => ({
name: `delegate_to_${agent.role.toLowerCase().replace(/\s+/g, '_')}`,
description: `Delegate a task to ${agent.role} who has the goal: ${agent.goal}`
})
};
return delegationTool;
}).filter(Boolean);
}
/**
* Create a deep copy of the Agent
* Optimized to avoid unnecessary duplication of resources
*/
copy() {
// Create a new config based on current state
const config = {
role: this.originalRole,
goal: this.originalGoal,
backstory: this.originalBackstory,
llm: this._llm,
functionCallingLlm: this._functionCallingLlm,
verbose: this._verbose,
allowDelegation: this._allowDelegation,
maxIterations: this._maxIterations,
maxRpm: this._maxRpm,
memory: this._memory,
useSystemPrompt: this._useSystemPrompt,
systemPrompt: this._systemPrompt,
tools: this._tools
};
// Create a new instance with the same configuration
return new AgentBuilder(config);
}
/**
* Create an agent executor for the agent
* This is lazily initialized when needed with optimizations for reuse
* @param tools Optional tools to use
* @param task Optional task to execute
*/
async createAgentExecutor(tools, task) {
// In a real implementation, this would create a proper executor
// based on the LLM and tools configuration
// For now, we'll return a placeholder
// Create combined tools list
const allTools = [...(tools || []), ...this._tools];
// Log tools if verbose mode is enabled
if (this._verbose) {
console.log(`Creating executor for ${this.role} with ${allTools.length} tools`);
for (const tool of allTools) {
console.log(`- ${tool.name}: ${tool.description}`);
}
}
return {
invoke: async (input) => {
// Track iteration count
this.executionStats.iterations += 1;
// In a real implementation, this would execute the LLM chain
// with the proper prompt and tools
console.log(`Executing agent ${this.role} with input:`, input);
// Simulate token usage
this.executionStats.tokenCount.prompt += 100;
this.executionStats.tokenCount.completion += 50;
this.executionStats.tokenCount.total += 150;
// Return a simulated response
return {
output: `${this.role} has processed the task successfully.`,
tokenUsage: {
prompt_tokens: this.executionStats.tokenCount.prompt,
completion_tokens: this.executionStats.tokenCount.completion,
total_tokens: this.executionStats.tokenCount.total
}
};
}
};
}
/**
* Reset execution stats for a new execution
*/
resetExecutionStats() {
this.executionStats = {
tokenCount: { prompt: 0, completion: 0, total: 0 },
iterations: 0
};
}
/**
* Get the execution time in milliseconds
*/
getExecutionTime() {
if (!this.executionStats.startTime || !this.executionStats.endTime) {
return 0;
}
return this.executionStats.endTime - this.executionStats.startTime;
}
}