Core Loop

The Core Loop block implements the core “ReAct-style” loop, where the LLM plans, calls tools, and observes.

It is best suited for open-ended queries where the path to the solution isn’t fixed and needs to choose between several tools.

A Core Loop block is primarily defined by its LLM, its instructions, and its tools.

A Simple Visual Agent is conceptually the same thing as a Structured Visual Agent with a single Core Loop block.

Exit Conditions

Most block types have a single “next block” field that specifies which block the control flow moves to when the block has finished executing. This connects multiple blocks into a graph.

The Core Loop block has more complex “exit conditions”.

The Core Loop block normally terminates when the underlying LLM decides that it does not need to call tools anymore. It will then usually output some text, which it its “answer”. If there is a “Default next block” defined on the Core Loop block, the control flow moves to this block. Else, control flow remains on the Core Loop block and it will usualy resume at the next turn of the conversation.

It is also possible to make the Core Loop block terminate early and move to a different block with Exit conditions.

If exit conditions are defined, these are checked in order at the end of each react loop (after tools have been called).

If any exit condition evaluates to true, the control flow passes to the next block defined on that exit condition.

Four different types of exit conditions can be defined:

  • State has keys - True if the state contains all keys in a given list

  • Scratchpad has keys - True if the scratchpad contains all keys in a given list

  • Tools called - True if all the tools in a given list have been called

  • Expression - True if a given CEL expression is true

Example use case

A key use case for Exit Conditions is the following: imagine an agent that is used by a Business Development Representative to prepare emails to send to prospects.

A first need is to identify precisely the prospect. For that, the Agent has a tool to query a prospect database. However, there are several similarly-named prospect. Thus, several iterations may be required to uniquely identify the prospect.

You would typically implement this with two Core Loop blocks and an exit condition:

  • The goal of the first Core Loop block is to uniquely identify the prospect, possibly over several turns

  • The first Core Loop block has no “default next block”, so it remains active over turns

  • Once ambiguities have been resolved, the first Core Loop block uses the “Set state” virtual tool to set the prospect_id state key

  • The first Core Loop block has a single exit condition: State has key: prospect_id

  • Thus, the first Core Loop block passes control to the second Core Loop block, which is tasked with actually working on the properly identified customer

State and scratchpad virtual tools

In addition to the explicitly defined tools, you can enable virtual tools that make the LLM able to read and write the state and/or scratchpad.

The use case documented above uses this capability

Forcing tool call arguments

Normally, all arguments of a tool call are defiend by the LLM. However, there can be cases where you want to force the value of a given argument, in order to ensure that the LLM does not perform unwanted tool calls.

In the definition of each tool, you can use CEL expressions to explicitly force the values of tool call arguments

Using Agents as tools

In addition to the explicitly defined tools, you can also use any Agent, whether visual or code-based, as a tool, taking a question and returning a response. Simply select “Use a Sub-Agent” as the tool.

History passing

Typically, the Core Loop block receives and uses the whole history of the conversation, so that it has context.

When the Core Loop block is used as a “helper” block, for a specific task, or within a Parallel or For Each block, it can make sense to disable history, and only prompt through :doc:`CEL templating </agents/structured-visual-agents/expressions>. In that case, you can disable history passing.