Agents
Learn to create agents in RΞASON
Agents are an experimental feature in RΞASON. Their API is subject to change.
What is an agent?
There are some different definitions people use to define “LLM agents”. This is confusing.
So we propose the following:
An agent is just a LLM that selects which action to take from predefined pool of actions in order to accomplish a certain objective.
An example
Although people don’t think of it like it, we argue that ChatGPT is indeed an agent. (And probably the most popular in the world)
ChatGPT Plus lets users:
- chat with GPT-4;
- create images with DALL·E 3;
- get relevant answer with GPT-4 web browsing;
- plot graphs with GPT-4 running Python code and showing its output.
How does ChatGPT (the app, not the LLM) knows which action to take?
Because the LLM itself (GPT-4) decides which action to take — just respond to user, create an image using DALL·E, etc.
Its objective could be: “Your goal is to fulfill the user’s need — be that responding the message, creating a new image, web browsing, etc.”
And the actions available for the agent are:
- Send text message to user;
- Create image using DALLE-3;
- Make a web search and visit the results;
- Run a piece of python code and get its output.
The point here is not to discuss semantics but rather illustrate what RΞASON refers to as “agents”.
Why are agents useful?
LLMs have strong reasoning capabilities and, because of that they are able to decide when to call an action (and which to call) pretty precisely.
For instance, LLMs are bad at math:
LLM failing at math
But if we give access to a “Sum” action that takes two numbers and outputs the sum of them, the LLM correctly calls the action and is able to get the answer:
LLM not failing at math
Definitions
Let’s establish two definitions:
- Agent: a LLM that has an objective and a set of actions available to take in order to fulfill its goal;
- Action: a function that the LLM can call to get its output.
- Also called “Tool” in other places.
Creating your first agent
We’ll be creating a basic math agent that has access to: sum()
, subtract()
, multiply()
and divide()
actions.
To create an agent in RΞASON you’ll need to define its actions and then the agent itself.
Creating the first action
To create an action you need to:
- Create a new
.ts
file undersrc/actions
; - Export a default function;
- Add a description for the action and for its parameters for the LLM to understand them.
Let’s create the SUM
action we mentioned previously:
Two things to notice:
- All the is above the
sum
function in the form of JSDoc; - The function itself is just normal TS/JS code — you can do whatever you wish there as long as you
return
something in the end.
And that’s it! Pretty simple.
A note on JSDoc
If you never heard of JSDoc before, go here to understand it a bit better.
JSDoc may seem strange & complicated, but what we recommend is the following:
- Write the function definition;
- Go above it and type
/**
and your IDE will most-likely auto-complete for you.
JSDoc autocompletion
To verify you’ve correctly wrote JSDoc, you can hover over the function:
Function & parameters description when you hover
Creating the other actions
Awesome! We now have a sum
action, however we still need the subtract
, multiply
and divide
actions:
With all four actions created, we now need to create the agent itself.
Agent mental model
Before actually creating your first agent, we need to create the required mental model to understand how they work.
Agents mental model
Three important things to notice:
- A
step
is cycle of:- LLM receives a message (be that from your the user or from an action output it previously called);
- LLM decides which action to call (and its parameters);
- The action is ran;
- Your program decides if it want to stop the agent or go for another step.
- In a
step
the LLM can:- Call an action;
- Send a normal text message with no action call;
- Call an action and also send a normal text message.
- It is up to your program to decide when to stop your agent. Some common stop heuristics are:
- If the LLM return a message but no action call then probably it wants to end;
- Create an
end()
action that the LLM must call when it wants to end; - Use a
max_step
counter — where the LLM can run for at mostn
number of steps.
Creating the agent
To create an agent in RΞASON:
- Create a new
.ts
file undersrc/agents
; - Export a
const actions = []
containing the available actions for the agent; - Add a description for the agent;
- Call the
useAgent()
function.
Let’s take a look at the MathAgent
:
Let’s break it down:
- To define which actions the agent has you need to
export const actions = [ ]
with the actions; - To initialize an agent you call
const agent = await useAgent()
; - There are two parts to the prompt of the agent:
- The agent prompt which is define as a JSDoc above the agent function — and is used as the system intruction;
- The prompt for the initial message which is passed as the parameter to
agent.reason(initialPrompt)
.
- To run the agent
agent.reason(initialPrompt)
;agent.reason()
is an async generator that you iterate usingfor await (const step of agent.reason()) {}
- After each
step
, you decide if you want to callagent.stop()
to stop the agent or not. In this case we’re stopping the agent if:- The LLM doesn’t call an action but returns a text message;
- The LLM calls the
answer
action.
Cool! Now we need an entrypoint in order to test our agent.
Creating an entrypoint
Let’s create a POST /math
entrypoint:
Running our agent
If we call the POST /math
entrypoint we get:
Response from POST /math
Agent streaming behaviour
A important thing to notice is that there was a lot of data that was streamed even though we only explictly set to stream the final answer in the JSON’s answer
property. Why?
- Because all agents in RΞASON by default stream:
- What action the LLM choose;
- Action input/output pairs;
- The text messages the LLM return.
- This behaviour can be disabled.
But the cool thing is that if you leave the default streaming agent usage enabled, you can use RΞASON Playground’s chat mode:
RΞASON Playground's chat mode
RΞASON Playground’s chat mode
Since most agents are chat-like, we thought it’d be cool to include a chat interface in the Playground to help test your agents.
For chat mode to work:
- Your agent needs to use the default streaming behaviour of agents;
- Whenever a message is sent in the chat, the Playground will make a
POST
request with ainput
property in the body. That means your entrypoint needs to read it and pass it to your agent.
Another cool thing that chat mode does is: it handles the memory_id
automatically.
But what is memory_id
?
About memory_id
Some usecases, like creating a chat-like agent, requires persistance — i.e.: be able to restore previous messages in the chat.
RΞASON offers persistance by default:
- Every time you call
useAgent()
a newmemory_id
is created; - When you call
useAgent(memory_id)
again you can pass amemory_id
and that agent will be continue where it left off.
You can disable this.
Under the hood RΞASON uses a local SQLite database to store all the information needed. If you go to your project directory, you’ll see a database.sqlite
file.
Let’s test this out:
And in our agent:
Let’s try it on the Playground:
Using `memory_id`
In the demo above, we didn’t use chat mode just to illutrate how memory_id
works under the hood. However, as long as your entrypoint is expecting a memory_id
property inside the request’s body, chat mode will automatically send the memory_id
.
Agent options
There are some options you can set to change the default behaviour of agents in RΞASON:
Conclusion
Although there is a bit more to learn about agents in RΞASON — such as how to deal with context length of the LLM — this page served as an introduction to them.
Next, we’ll be talking about the final piece of RΞASON puzzle: observability.
Was this page helpful?