crewai-ts
Version:
TypeScript port of crewAI for agent-based workflows
217 lines (211 loc) • 7.64 kB
JavaScript
/**
* Agent implementation
* Optimized for efficient execution and memory management
*/
import { v4 as uuidv4 } from 'uuid';
/**
* Core Agent class that implements the BaseAgent interface
* Optimized with:
* - Lazy initialization of heavy components
* - Memoization of expensive operations
* - Token usage tracking and optimization
* - Efficient memory management
*/
export class Agent {
id;
role;
goal;
backstory;
llm;
functionCallingLlm;
verbose;
allowDelegation;
maxExecutionTime;
maxIterations;
maxRpm;
memory;
useSystemPrompt;
systemPrompt;
tools;
// Cached state for optimization
agentExecutor; // Will be properly typed with AgentExecutor
timesExecuted = 0;
tokenUsage = {
prompt: 0,
completion: 0,
total: 0
};
constructor(config) {
// Generate a unique ID for the agent
this.id = uuidv4();
// Required properties
this.role = config.role;
this.goal = config.goal;
// Optional properties with defaults
this.backstory = config.backstory;
this.llm = config.llm;
this.functionCallingLlm = config.functionCallingLlm;
this.verbose = config.verbose ?? false;
this.allowDelegation = config.allowDelegation ?? true;
this.maxExecutionTime = config.maxExecutionTime;
this.maxIterations = config.maxIterations ?? 15;
this.maxRpm = config.maxRpm;
this.memory = config.memory ?? true;
this.useSystemPrompt = config.useSystemPrompt ?? true;
this.systemPrompt = config.systemPrompt;
this.tools = config.tools ?? [];
}
/**
* Execute a task with this agent
* Optimized with token tracking and efficient resource usage
*/
async executeTask(task, context, tools) {
// Track execution count
this.timesExecuted += 1;
// Start tracking execution time
const startTime = Date.now();
// Lazy initialize the agent executor if needed
if (!this.agentExecutor) {
this.agentExecutor = await this.createAgentExecutor(tools, task);
}
try {
// 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);
// Calculate execution time
const executionTime = Date.now() - startTime;
return {
output: result.output || result,
metadata: {
executionTime,
iterations: this.timesExecuted,
// Token tracking would be populated from actual LLM usage
}
};
}
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
*/
getDelegationTools(agents) {
if (!this.allowDelegation) {
return [];
}
// Create delegation tools from other agents
// This would be implemented with the actual Agent Tools functionality
return [];
}
/**
* Set knowledge for the agent
* Optimized for efficient knowledge embedding and retrieval
*/
async setKnowledge(knowledge) {
// Implementation for setting agent knowledge would go here
// This would connect to a vector store or other knowledge base
}
/**
* Create an agent executor for the agent
* This is lazily initialized when needed
*/
async createAgentExecutor(tools, task) {
// Get the LLM instance to use for this agent
const llm = await this.getLLM();
if (!llm) {
throw new Error(`No LLM configured for agent ${this.role}. Please provide an LLM.`);
}
// Create a proper agent executor that uses the LLM
return {
invoke: async (input) => {
if (this.verbose) {
console.log(`Agent ${this.role} executing task with input: ${JSON.stringify(input)}`);
}
try {
// Construct the prompt for the LLM
const messages = [
{ role: 'system', content: this.getSystemPrompt(task) },
{ role: 'user', content: this.getUserPrompt(input, task) }
];
// Call the LLM with the constructed prompt
const result = await llm.complete(messages, {
temperature: 0.7,
maxTokens: 2000
});
if (this.verbose) {
console.log(`Agent ${this.role} received response: ${result.content}`);
}
// Update token usage tracking for the agent
this.tokenUsage.prompt += result.promptTokens || 0;
this.tokenUsage.completion += result.completionTokens || 0;
this.tokenUsage.total += result.totalTokens || 0;
return {
output: result.content,
metadata: {
promptTokens: result.promptTokens || 0,
completionTokens: result.completionTokens || 0,
totalTokens: result.totalTokens || 0,
iterations: this.timesExecuted
}
};
}
catch (error) {
console.error(`Error in agent ${this.role} execution:`, error);
throw error;
}
}
};
}
/**
* Get the system prompt for the agent
* This defines the agent's role, goal, and behavior
*/
getSystemPrompt(task) {
return `You are ${this.role}.
Your goal is: ${this.goal}
${this.backstory ? `Backstory: ${this.backstory}
` : ''}${task ? `You are working on the task: ${task.description}
` : ''}Please provide a detailed and thoughtful response.`;
}
/**
* Get the user prompt for the agent
* This includes the specific task or query for the agent
*/
getUserPrompt(input, task) {
if (typeof input === 'string') {
return input;
}
else if (task) {
return `Please complete the following task: ${task.description}
Provide a detailed response that fulfills the task requirements.`;
}
else {
return 'Please provide your expert analysis and response.';
}
}
/**
* Get the LLM instance to use for this agent
* Resolves string LLM references to actual LLM instances
*/
async getLLM() {
if (!this.llm) {
return undefined;
}
// If the LLM is already a BaseLLM instance, return it
if (typeof this.llm !== 'string') {
return this.llm;
}
// If the LLM is a string, it should be resolved to an actual LLM instance
// This would typically be handled by a LLM registry or factory
// For now, we'll just throw an error
throw new Error(`String LLM references are not yet supported: ${this.llm}`);
}
toString() {
return `Agent(role=${this.role}, goal=${this.goal})`;
}
}