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.
- 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.
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

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.
Curious about RΞASON design philoshopy?
Curious about RΞASON design philoshopy?
Our beliefs are:
- We believe LLMs are a new primitive that programmers can use and by being a new primitive, there needs to be some new syntax to use them.
- They are not a new way to program though. Its still up to the programmer to do the right thing, create the right abstractions, etc.
- Frameworks/libraries should only interfere in areas that are non-differentiable for your app.
- The use of JSDoc as prompts and of Typescript type information (interfaces and types) to inform the LLM the types of parameters;
- We try as hard as possible to use native JS features that naturally interop with other code;
- RΞASON intentionally stays out of prompting and retrieval — as those areas are key to the sucess of your LLM app and therefore its up to the developer to handcraft them.
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.
SUM
action we mentioned previously:
src/actions/sum.ts
- 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.
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

Function & parameters description when you hover
Creating the other actions
Awesome! We now have asum
action, however we still need the subtract
, multiply
and divide
actions:
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
- 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.
MathAgent
:
src/agents/math-agent.ts
- 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.
Creating an entrypoint
Let’s create aPOST /math
entrypoint:
src/entrypoints/math.ts
Running our agent
If we call thePOST /math
entrypoint we get:
Response from POST /math
JSON response
JSON response
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’sanswer
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.
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.
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.
database.sqlite
file.
Let’s test this out:
src/entrypoints/math.ts
src/agents/math-agent.ts
Using `memory_id`
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:src/agents/math-agent.ts