Agents Deep Dive: BaseAgent
The BaseAgent is the default, general-purpose agent implementation provided by AgentB. It orchestrates the core interaction loop: receiving input, communicating with an LLM, processing the LLM's response, handling tool calls, and managing conversation state.
Most simple to moderately complex agent use cases can be handled by BaseAgent when configured with the appropriate IAgentContext (which includes the LLM, tools, and run configuration).
Key Features of BaseAgent
BaseAgentStandard Execution Loop: Implements the lifecycle described in Core Concepts: Agent Lifecycle & Events.
LLM Interaction: Prepares messages for the LLM, including system prompts and conversation history managed by the
ContextManager.Tool Handling:
Formats available tool definitions for the LLM.
Parses tool call requests from the LLM's response using
LLMResponseProcessor.Executes tools using
ToolExecutor.Feeds tool results back to the LLM for further processing.
Streaming Support: Natively handles streaming LLM responses, emitting
thread.message.deltaevents.Event Emission: Generates a comprehensive stream of
AgentEvents throughout its execution.Context Management Integration: Relies on the
ContextManager(provided inIAgentContext) to prepare messages and manage the LLM's context window.Configurable: Its behavior is heavily influenced by the
AgentRunConfigand the components (likeIToolProvider) injected viaIAgentContext.Continuations: Can automatically continue interacting with the LLM after tool executions, up to a configurable
maxToolCallContinuationslimit.Cancellation: Supports cooperative cancellation of its current run via the
cancelRun()method.
How BaseAgent Works
BaseAgent WorksWhen baseAgent.run(agentContext, initialTurnMessages) is called:
Initialization:
Sets an internal cancellation flag to
false.Retrieves dependencies (
llmClient,toolProvider,messageStorage, etc.) fromagentContext.Emits
agent.run.createdoragent.run.status.changed(if continuing a run).
Main Loop: Enters a loop that represents turns of interaction.
Safety Check: Increments an iteration guard to prevent infinite loops (max iterations =
maxToolCallContinuations+ safety buffer).Cancellation Check: Checks the
isCancelledThisRunflag. Iftrue, emits cancellation events and exits.Step Creation: Emits
agent.run.step.createdfor the current turn.Persist Inputs: Saves
initialTurnMessages(user prompt or tool results) tomessageStorage, emittingthread.message.created.Prepare LLM Input:
Uses
contextManager.prepareMessagesForLLM()to get the full list of messages for the LLM, including system prompt and history.Fetches available tools from
toolProviderand formats them usingllmClient.formatToolsForProvider().
Call LLM:
Emits
agent.run.status.changed(e.g., "LLM call for turn...").Calls
llmClient.generateResponse()with messages, tools, and run configuration (model, temperature, stream=true, etc.).
Process LLM Stream:
Creates an assistant message shell (
thread.message.created).Iterates through the LLM response stream using
responseProcessor.processStream():For
text_chunk: Emitsthread.message.delta.For
tool_call_detected: Emitsthread.run.step.tool_call.created,thread.run.step.tool_call.completed_by_llm, andthread.message.delta(withtoolCallsChunk). Stores detected tool calls.For
stream_end: Notes thefinishReason.For
error: Emitsthread.run.failedand throws an error to stop the run.
Checks for cancellation during stream processing.
Finalize Assistant Message: Saves the complete assistant message (text + tool calls) to
messageStorage, emittingthread.message.completed.Decision Based on
finishReason:If
tool_calls:Checks
maxToolCallContinuations. If exceeded, emitsthread.run.requires_action(with tool calls for external handling) orthread.run.failed, then exits.Emits
thread.run.requires_action(for observability).Calls
toolExecutor.executeToolCalls()with the detected tool calls andagentContext.For each tool execution, emits
agent.tool.execution.startedandagent.tool.execution.completed.If a tool was
DelegateToSpecialistTool, appropriateagent.sub_agent.invocation.completedevent is also emitted based on the tool's result metadata.
Formats tool results as
LLMMessages and sets them asinitialTurnMessagesfor the next loop iteration.If all tool executions failed, emits
thread.run.failedand exits.
If
stop(or null/undefined):Emits
thread.run.completedwith the final assistant message.Exits the loop and the
run()method.
If other reasons (e.g.,
length,content_filter):Emits
thread.run.failed.Exits the loop and the
run()method.
Error Handling: A top-level try/catch within
run()handles unexpected errors, emitsthread.run.failed, and ensures the generator concludes.finallyBlock: Logs that the run processing loop has concluded.
submitToolOutputs() Method
submitToolOutputs() MethodIf a BaseAgent run ends in a requires_action state (e.g., because maxToolCallContinuations was reached, or if an agent was designed to pause for external tool execution), the submitToolOutputs(agentContext, toolCallOutputs) method can be used to resume the run.
It takes the
agentContextand an array of tool outputs.Formats these outputs into
LLMMessages withrole: 'tool'.Calls
this.run(agentContext, toolResultLLMMessages), effectively re-entering the main loop with the tool results as the initial input for that turn.
cancelRun() Method
cancelRun() MethodSets an internal flag
this.isCancelledThisRun = true.The main
run()loop checks this flag at various points and, if set, will:Emit
agent.run.status.changed(to 'cancelling', then 'cancelled').Cleanly exit the generator.
When to Use BaseAgent
BaseAgentDefault Agent: It's the standard agent used by
AgentBfacade andApiInteractionManagerif no otheragentImplementationis specified and the mode doesn't inherently require a specialized agent likePlanningAgent.General Conversational AI: For chatbots that might need to use tools.
API Interaction Agents: When connecting to APIs via
OpenAPIConnectoringenericOpenApimode.Worker Agents:
BaseAgentis often the default implementation for "worker" or "specialist" agents that are invoked by aPlanningAgentvia theDelegateToSpecialistTool. These worker agents are given a focused set of tools for a specific sub-task.
Customization
While you can extend BaseAgent by creating a subclass, much of its behavior is customized by:
AgentRunConfig: Provided inIAgentContext, this controls:model: The LLM model to use.systemPrompt: The primary instructions for the LLM.temperature,maxTokens: LLM generation parameters.toolChoice: How the LLM should select tools.maxToolCallContinuations: How many times the agent can loop after tool calls.And more (see
AgentRunConfigdetails).
IToolProvider: The tools made available toBaseAgentviaIAgentContext.toolProviderdefine its capabilities.Other
IAgentContextComponents: The specific implementations ofILLMClient,IMessageStorage, etc., also affect its operation.
If you need to fundamentally alter the execution loop or decision-making logic beyond what configuration allows, you would then create your own class implementing the IAgent interface (potentially still drawing inspiration from BaseAgent's structure).
Last updated