Skip to content

Flow Control and Errors

David-Andrew Samson edited this page Aug 26, 2024 · 3 revisions

Error Handling

The ReAct loop has 2 separate error mechanisms, mainly for errors that the LLM sees/deals with vs errors that the programmer needs to deal with.

Throwing an Exception in a Tool

This is the main method for communicating errors to the LLM. When an exception is thrown, the ReAct loop catches it, and presents it to the LLM. The LLM can then try something else in order to recover from the error.

For example this simple calculator tool only supports one operation at a time, and throws an exception if the inputs are not valid:

@tool
def calculator(expression:str) -> float:
    """
    A simple calculator tool. Can perform basic arithmetic
    ...
    """

    #ensure that only one operation is present
    ops = [c for c in expression if c in '+-*/^%']
    if len(ops) > 1:
        raise ValueError(f"Invalid expression, too many operators. Expected exactly one of '+ - * / ^ %', found {', '.join(ops)}")
    if len(ops) == 0:
        raise ValueError(f"Invalid expression, no operation found. Expected one of '+ - * / ^ %'")
    
    op = ops[0]

    _a, _b = expression.split(op)
    a = float(_a)
    b = float(_b)

    if op == '+':
        return a+b
    elif op == '-':
        return a-b
    ...

The tool explicitly has code for throwing exceptions if multiple operators are encountered or if no known operators are found. Additionally, the call to float() can throw an exception if the value is not a valid number, which also would be propagated back up to the agent.

In this way, the agent can recognize if it does something incorrect, e.g. using the tool with the wrong inputs, and then it has the opportunity to try again and correct the mistake.

Loop Controller Fatal Error

(todo)

  • using this dependency injected flow control which exits the whole loop by raising failed task

Flow Control

Loop Controller Success

(todo)

  • dependency injection for breaking out of the loop (on tool success)