Creating a Simple Agent
This guide will walk you through creating a simple agent and using it to process tasks. We'll build a weather agent that can provide weather information for different cities.
Basic Setup
First, let's create a simple agent that can use a weather tool:
import { JorEl } from 'jorel';
import { z } from 'zod';
const jorEl = new JorEl({ openAI: true });
// 1. Register the tool at team level
jorEl.team.addTools([{
name: "get_weather",
description: "Get the current weather for a city",
executor: async ({ city }) => ({
temperature: 22,
conditions: "sunny"
}),
params: z.object({
city: z.string(),
}),
}]);
// 2. Create the agent
const weatherAgent = jorEl.team.addAgent({
name: "weather_agent",
description: "Specialist in weather information",
systemMessageTemplate:
"You are a weather specialist. " +
"Provide detailed weather information when asked.",
allowedTools: ["get_weather"],
});
Processing Tasks
A task represents a conversation with an agent. It tracks:
- Messages between user and agent
- Tool usage
- Processing state
- Events and statistics
Creating a Task
// Create a task with an initial user message
const task = await jorEl.team.createTask(
"What's the weather like in Sydney?"
);
Executing a Task
The simplest way to process a task is to execute it to completion:
const result = await jorEl.team.executeTask(task, {
limits: {
maxIterations: 10, // Prevent infinite loops
maxGenerations: 6, // Limit LLM calls
maxDelegations: 2, // Limit agent delegations
},
});
console.log(result.result);
// The weather in Sydney is sunny with a temperature of 22°C.
Understanding Task Events
Tasks generate events that help you understand what happened during processing:
const { events, stats, tokens } = result.eventsWithStatistics;
// Log the sequence of events
for (const event of events) {
console.log(`${event.eventType}: ${event.action}`);
}
// Example output:
// generation: Agent weather_agent generated assistant_with_tools message
// toolUse: Agent weather_agent used tool get_weather
// generation: Agent weather_agent generated assistant message
Adding Context with Documents
You can provide contextual information to your agent using documents:
const weatherAgent = jorEl.team.addAgent({
name: "weather_agent",
description: "Specialist in weather information",
systemMessageTemplate:
"You are a weather specialist. Here is some context: {{documents}}",
allowedTools: ["get_weather"],
documents: [{
title: "Temperature Guidelines",
content: "Always provide temperatures in both Celsius and Fahrenheit."
}],
});
Customizing Response Format
You can specify that your agent should return JSON responses:
const weatherAgent = jorEl.team.addAgent({
name: "weather_agent",
description: "Specialist in weather information",
systemMessageTemplate:
"You are a weather specialist. Return all responses as JSON with fields: " +
"city, temperature_c, temperature_f, conditions",
allowedTools: ["get_weather"],
responseType: "json",
});
const task = await jorEl.team.createTask(
"What's the weather in Sydney?"
);
const result = await jorEl.team.executeTask(task);
console.log(result.result);
// {
// "city": "Sydney",
// "temperature_c": 22,
// "temperature_f": 71.6,
// "conditions": "sunny"
// }
Step-by-Step Processing
Instead of executing a task to completion, you can process it step by step:
// Create the task
const task = await jorEl.team.createTask(
"What's the weather in Sydney?"
);
// Process one step at a time
let currentTask = task;
while (currentTask.status !== "completed" && currentTask.status !== "halted") {
// Resume the task for one step
currentTask = await jorEl.team.resumeTask(currentTask, {
limits: {
maxIterations: 10,
maxGenerations: 6,
},
});
// Check what happened in this step
const latestEvent = currentTask.events[currentTask.events.length - 1];
console.log(`Step completed: ${latestEvent.eventType}`);
if (currentTask.status === "halted") {
console.log(`Task halted: ${currentTask.haltReason}`);
}
}
Best Practices
-
Clear System Messages: Make your agent's role and capabilities clear in the system message
-
Appropriate Limits: Always set reasonable limits when executing tasks to prevent:
- Infinite loops (
maxIterations
) - Excessive costs (
maxGenerations
) - Deep delegation chains (
maxDelegations
)
- Infinite loops (
-
Error Handling: Tasks can halt for various reasons:
if (result.status === "halted") {
switch (result.haltReason) {
case "maxIterations":
console.log("Task took too many steps");
break;
case "maxGenerations":
console.log("Task exceeded LLM call limit");
break;
case "approvalRequired":
console.log("Task needs user approval to continue");
break;
// ... handle other cases
}
}
- Resource Monitoring: Keep track of token usage with
eventsWithStatistics
const { tokens } = result.eventsWithStatistics;
console.log("Token usage:", tokens);
// {
// 'gpt-4': {
// input: 245,
// output: 62
// }
// }
Next Steps
- Learn about Task Processing in detail
- Explore Delegation between agents
- Learn about Transfer of control