Skip to main content

What is an agent?

An agent in AI systems is an autonomous entity that can perceive its environment, make decisions, and take actions to achieve specific goals. In the context of Agenite, an agent combines the reasoning capabilities of Large Language Models (LLMs) with the ability to use tools and interact with its environment. Think of an agent as an AI assistant that can not only understand and generate text but also perform actions in the real world through its available tools – like searching the web, querying databases, making calculations, or even calling other specialized agents.
Key aspects of an agent
  • Autonomous decision-making: Agents use LLMs to reason about tasks and decide what actions to take
  • Tool usage: Agents can extend their capabilities by using tools to interact with external systems
  • Memory & state: Agents maintain context through state management
  • Sequential processing: Agents follow well-defined execution steps
  • Composability: Agents can work together in hierarchical structures

Example: A research assistant agent

To make this concrete, imagine a research assistant agent:
  1. A user asks, “What were the major tech announcements from Google I/O 2023?”
  2. The agent (powered by an LLM) understands this is a factual query that requires up-to-date information
  3. It decides to use its web search tool to find recent articles
  4. The agent receives search results and analyzes them
  5. It might use additional tools to gather more details or verify information
  6. Finally, it synthesizes a comprehensive answer from all collected data
This illustrates the agent’s ability to:
  • Understand natural language requests
  • Choose appropriate tools based on the task
  • Process and integrate information from multiple sources
  • Deliver a coherent response that meets the user’s need

Agenite agents

In Agenite, an Agent is the central orchestrator that manages interactions between language models (LLMs), tools, and possibly other agents. It implements a step-based execution model with state management and extensibility through middlewares.

Basic structure

An agent consists of: Core components:
  • An LLM provider (like OpenAI, Anthropic, etc.) that handles model interactions
  • A collection of tools the agent can use to perform tasks
  • A system instructions string that guides the agent’s behavior
  • A state management system using reducers
Execution components:
  • A sequence of steps that determine the execution flow
  • Optional middlewares that can intercept and modify the execution flow
  • Methods for running the agent: iterate() and execute()

Creating an agent

Important agent configuration properties

The Agent constructor accepts a configuration object with these key properties:

Running an agent

Agenite provides two methods to run agents:

1. execute() - Simple run

The simplest way to run an agent to completion:

2. iterate() - Step-by-step control

For fine-grained control over execution, use iterate() to access the underlying generator:
The while loop pattern is crucial when using iterate() because:
  1. It properly handles the generator protocol, advancing through steps with next()
  2. It allows you to send data back into the generator (like tool results)
  3. It cleanly handles the termination condition when done becomes true
Common use cases for iterate() include:
  • Building interactive UIs that show real-time LLM responses
  • Custom tool execution logic that might involve user input
  • Advanced logging and monitoring
  • Human-in-the-loop workflows where users can intervene

Steps: The agent execution flow

Agents in Agenite operate through a sequence of well-defined Steps. Each step is a discrete piece of functionality that yields control at key points.

Core steps

Agenite provides these built-in steps:
  1. LLM call (agenite.llm-call)
    • Sends messages to the LLM provider
    • Handles streaming responses
    • Determines whether to proceed to tool calling or end
  2. Tool call (agenite.tool-call)
    • Processes tool use requests from the LLM
    • Yields control to let the application execute tools
    • Routes to either Tool Result or Agent Call based on tool type
  3. Tool result (agenite.tool-result)
    • Executes standard tools (like calculators, APIs)
    • Processes tool results
    • Prepares results to send back to the LLM
  4. Agent call (agenite.agent-call)
    • Handles delegation to sub-agents
    • Manages nested execution
    • Returns sub-agent results back to the main agent
  5. End (agenite.end)
    • Terminates the execution flow
    • Returns the final state

Step flow visualization

Step implementation

Each step in Agenite has a standard structure:

Customizing the step flow

Agenite provides the basic agent execution flow that covers perception (understanding the environment through LLM calls) and action (executing tools). However, sometimes you might want a different execution flow for specific use cases. You can customize this by defining your own steps or modifying existing ones. To customize steps:

When to customize steps

Consider customizing steps when you need to:
  1. Add specialized processing: For example, adding validation, preprocessing, or analysis before/after LLM calls
  2. Create complex workflows: Building multi-stage agent interactions with specific logic between steps
  3. Implement custom state transitions: Controlling exactly when and how an agent moves between different execution phases
  4. Add monitoring or debugging: Injecting custom logging or instrumentation at specific points in execution

Step execution flow

When an agent runs, it follows these general steps:
  1. Start at the configured startStep (default: ‘agenite.llm-call’)
  2. For each step:
    • Run beforeExecute() to prepare parameters
    • Run execute() which can yield values back to the caller
    • Run afterExecute() to process the result
  3. Use the next property from the step’s return value to determine the next step
  4. Continue until reaching ‘agenite.end’ or an error occurs
This execution model gives you fine-grained control over the agent’s behavior while maintaining a clean, predictable flow.

Example: creating a content validation step

Here’s a practical example of creating a validation step that checks user input before sending it to the LLM:
This example shows how to:
  • Create a custom validation step before the LLM call
  • Check user input against content policy rules
  • Either block the request or allow it to proceed to the LLM
  • Handle custom events in the iterate() loop

Visualizing a custom step flow

Here’s how the validation step changes the default flow: By adding custom steps, you can create more sophisticated agent behavior that better fits your specific use case.

State management

Agents maintain state throughout their execution. The primary state is the message history, but you can add custom state fields with reducers:
The reducer pattern allows you to control how new state is merged with existing state during execution.

State flow visualization

As an agent executes, each step produces state changes that are processed through reducers:
  1. Step execution: Each step (LLM Call, Tool Call, etc.) runs and returns an object:
  2. State processing: For each field in the returned state object:
    • Find the matching reducer function
    • Apply the reducer to merge new values with existing state
    • Create an updated state that includes these changes
  3. Execution flow: Move to the next step specified in the next field, using the updated state
This cycle repeats for each step in the agent’s execution, creating a chain where each step builds on the state from previous steps.

Example

When a step returns:
And the current state is:
The updated state becomes:
Then, execution continues with the agenite.tool-call step.

The state as agent output

An important aspect of Agenite’s state management is that the final state becomes the agent’s output. When an agent completes execution (reaching the agenite.end step), the accumulated state is returned as the result:
This means that by defining custom state reducers, you’re not just controlling how state evolves during execution—you’re also shaping the exact structure of the agent’s output. You can:
  • Track custom metrics throughout execution
  • Accumulate data across multiple steps
  • Transform or filter certain types of information
  • Define exactly what data your application receives when the agent finishes
For example, if you want your agent to track a conversation summary:

Extending with middlewares

Middlewares are powerful wrappers around the agent’s execution generator that enable you to intercept, modify, or enhance the execution flow. They work by forming a chain that surrounds the main generator.

How middlewares work

Each middleware is an async generator function that:
  1. Receives the inner generator and context as parameters
  2. Creates a loop to manage the generator’s execution
  3. Can intercept values as they are yielded out
  4. Can modify values being sent back via next()
  5. Returns the final value when execution completes

Middleware examples

1. Logger middleware

A simple logger that records all events and responses:

2. Human-in-the-loop (HITL) middleware

Allows a human to review and approve agent actions before execution:
This simple middleware integrates at the tool call level, allowing a human to approve or reject each tool call the agent tries to make. When a tool call is rejected, the agent receives an error response that it must handle appropriately.

Combining multiple middlewares

Middlewares are applied in the order provided, with the first middleware in the array being the outermost wrapper:
In this example:
  1. The logger middleware sees all events first and logs them
  2. Then the human approval middleware handles review requests
  3. Finally, the agent’s core generator processes them
The execution flow travels inward through all middlewares, then outward again as values are yielded back up the chain.

Custom events and return values

Middlewares can also yield custom events and modify the final return value, enabling powerful integration patterns:
This example demonstrates two key capabilities:
  1. Custom Event Types: The middleware yields events with custom type values like middleware.token and includes arbitrary data properties.
  2. Enhanced Return Values: When the generator is done, the middleware enhances the final state with additional properties that the caller can access.
These capabilities allow middlewares to:
  • Implement custom monitoring, logging, or metrics
  • Add side channels for communication with the caller
  • Embed metadata in the final result
  • Create specialized UI events for front-end applications
  • Build detailed analytics or debugging systems
By combining custom events and return values with the core middleware functionality, you can extend Agenite agents in virtually unlimited ways.

Visualizing middleware flow with custom events

This diagram illustrates how Agenite’s middleware system works, based on the actual implementation:
  1. Middleware Application: When agent.iterate() is called, the step generator is created first, then middlewares are applied in right-to-left order using reduceRight(). This means the first middleware in the array wraps all others.
  2. Event Flow: When steps execute inside the generator, events flow outward through the middleware chain:
    • Step Generator yields an event
    • Each middleware processes the event in sequence
    • The outermost middleware passes events to the iterate loop
  3. Custom Events: Middlewares can intercept standard events and create custom events (like middleware.token) which the iterate loop handles.
  4. Return Enhancement: When the step generator completes, the final state flows back through the middleware chain, allowing each middleware to enhance it before returning to the caller.
The bidirectional nature of this architecture enables powerful patterns:
  • Middlewares can monitor and transform events
  • Middlewares can add custom properties to the final state
  • The iterate loop gets visibility into both standard and custom events
  • Each middleware sees both the events flowing out and the results flowing back
By combining custom events and return values with this chained middleware pattern, you can build sophisticated monitoring, human-in-the-loop systems, and complex agent behaviors.

Custom middleware tips

When creating custom middlewares:
  1. Error handling: Always include try/catch blocks to prevent errors from breaking the agent
  2. Types: Consider what event types you need to handle - most middlewares only need to process specific events
  3. Performance: Be mindful of performance, especially for events that occur frequently like streaming chunks
  4. Chaining: Test your middleware with others to ensure they work well together
  5. State: Avoid shared state between middleware instances unless explicitly designed for it

Multi-agent architecture

Agents can be composed into hierarchies, with coordinator agents delegating to specialist agents:
When the coordinator receives a question, it can choose to delegate to the appropriate specialist using the Agent Call step.

Multi-agent flow

This architecture enables complex problem-solving through specialized agents working together.