Tutorial 1: Agents#
Functionalities:#
Task-based Agents which will break down tasks into subtasks and solve them in bite-sized portions
Agents with registered functions as skills
Setup Guide#
Step 1: Install TaskGen#
# !pip install taskgen-ai
Step 2: Import required functions and setup relevant API keys for your LLM#
# Set up API key and do the necessary imports
from taskgen import *
import os
# this is only if you use OpenAI as your LLM
os.environ['OPENAI_API_KEY'] = '<YOUR API KEY HERE>'
Step 3: Define your own LLM#
Take in a
system_prompt,user_prompt, and outputs llm response string
def llm(system_prompt: str, user_prompt: str) -> str:
''' Here, we use OpenAI for illustration, you can change it to your own LLM '''
# ensure your LLM imports are all within this function
from openai import OpenAI
# define your own LLM here
client = OpenAI()
response = client.chat.completions.create(
model='gpt-4o-mini',
temperature = 0,
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_prompt}
]
)
return response.choices[0].message.content
# Verify that llm function is working
llm(system_prompt = 'You are a classifier to classify the sentiment of a sentence',
user_prompt = 'It is a hot and sunny day')
1. Agent Basics#
Create an agent by entering your agent’s name and description
Agents are task-based, so they will help generate subtasks to fulfil your main task
Agents are made to be non-verbose, so they will just focus only on task instruction (Much more efficient compared to conversational-based agentic frameworks like AutoGen)
Agent’s interactions will be stored into
subtasks_completedby default, which will serve as a memory buffer for future interactions
- Inputs for Agent:
agent_name: String. Name of agent, hinting at what the agent does
agent_description: String. Short description of what the agent does
max_subtasks: Int. Default: 5. The maximum number of subtasks the agent can have
verbose: Bool. Default: True. Whether to print out agent’s intermediate thoughts
llm: Function. The LLM to be used by the Agent
- Agent Internal Parameters:
Task: String. The task the agent has been assigned to - Defaults to “No task assigned”
Subtasks Completed: Dict. The keys are the subtask names and the values are the result of the respective subtask
Is Task Completed: Bool. Whether the current Task is completed
- Task Running
reset(): Resets the Agent Internal Parameters and Subtasks Completed. You should do this at the start of every new task assigned to the Agent to minimise potential confusion of what has been done for this task versus previous tasks
run(task: str, num_subtasks: int = max_subtasks): Performs the task. Do note that agent’s state will not be reset, so if you want to reset it, call reset() prior to running this. Runs the task for num_subtasks steps. If not specified, we will take the max_subtasks.
- Give User Output
reply_user(query: str = ‘’, stateful: bool = True): Using all information from subtasks, give a reply about the
queryto the user. Ifqueryis not given, then it replies based on the current task the agent is doing. Ifstatefulis True, saves this query and reply intosubtasks_completed
- Check status of Agent:
status(): Lists out Agent Name, Agent Description, Available Functions (default function is to use the LLM), Task, Subtasks Completed and Is Task Completed
Example Agent Creation#
my_agent = Agent('Helpful assistant', 'You are a generalist agent')
Example Agent Task Running - Split the assigned task into subtasks and execute each of them#
output = my_agent.run('Give me 5 words rhyming with cool, and make a 4-sentence poem using them')
Check Agent’s Status#
my_agent.status()
Example Agent Reply to User - Reference the subtasks’ output to answer the user’s query#
output = my_agent.reply_user()
Create Agent#
# Create your agent by specifying name and description
my_agent = Agent('Helpful assistant', 'You are a generalist agent', llm = llm)
# Show the agent status - By default agent comes equipped with default function `use_llm` which queries the llm
# end_task is to end the current task if it is completed
my_agent.status()
Automatic Running of Task#
# Do the task by subtasks. This does generation to fulfil task
my_agent.reset()
output = my_agent.run('Give me 5 words rhyming with cool, and then make a 4-sentence poem using them')
# visualise the subtask outputs of the task
print(output)
# see the updated agent status
my_agent.status()
# Generates a meaningful reply to the user about the task according to its current state. Functions like a QA bot
# The reply will go into subtasks_completed to store the conversation with the user for future context
output = my_agent.reply_user()
# You can also ask your questions to the agent, and the agent will reply according to its current state. Functions like a QA bot
# Here, we set stateful is false because we do not want this to go into subtasks_completed
output = my_agent.reply_user('Where is the pool?', stateful = False)
# see the updated agent status
my_agent.status()
my_agent.print_functions()
my_agent.reset()
my_agent.status()
# when you change the task, we will set the task completed to False so you can do it again.
# Do note that the earlier subtasks_history might affect your new task and should be reset if it is not relevant
my_agent.reset()
output = my_agent.run('Give me 5 words rhyming with task, and then make a 4-sentence poem using them')
# see the updated agent staus
my_agent.status()
Running of Task Step by Step#
# Create your agent by specifying name and description
my_agent = Agent('Number Expert', 'You are great with numbers', llm = llm)
# Runs a task for 1 step by editing num_subtasks variable to 1
output = my_agent.run('List me three random numbers from 1 to 50, then give me their sum', num_subtasks = 1)
# visualise the outputs of the task at the subtask level
print(output)
# Visualise the first subtask
my_agent.status()
# Runs the task for another step
output = my_agent.run('List me three random numbers from 1 to 50, then give me their sum', num_subtasks = 1)
# visualise the outputs of the task at the subtask level
print(output)
# Visualise the first and second subtasks
my_agent.status()
# if task already completed, then no need to do further
output = my_agent.run('List me three random numbers from 1 to 50, then give me their sum', num_subtasks = 1)
# Generates a meaningful reply to the user
output = my_agent.reply_user()
2. Power Up your Agents - Bring in Functions (aka Tools)#
First define the functions, either using class
Function(see Tutorial 0), or just any Python function with input and output types defined in the signature and with a docstringAfter creating your agent, use
assign_functionsto assign a list of functions of classFunction, or general Python functions (which will be converted to AsyncFunction)Function names will be automatically inferred if not specified
Proceed to run tasks by using
run()
# This is an example of an LLM-based function (see Tutorial 0)
sentence_style = Function(fn_description = 'Output a sentence with words <var1> and <var2> in the style of <var3>',
output_format = {'output': 'sentence'},
fn_name = 'sentence_with_objects_entities_emotion',
llm = llm)
# This is an example of an external user-defined function (see Tutorial 0)
def binary_to_decimal(binary_number: str) -> int:
'''Converts binary_number to integer of base 10'''
return int(str(binary_number), 2)
# Initialise your Agent
my_agent = Agent('Helpful assistant', 'You are a generalist agent')
# Assign the functions
my_agent.assign_functions([sentence_style, binary_to_decimal])
# Run the Agent
output = my_agent.run('First convert binary string 1001 to a number, then generate me a happy sentence with that number and a ball')
Approach 1: Automatically Run your agent using
run()- Approach 2: Manually select and use functions for your task
select_function(task: str): Based on the task, output the next function name and input parameters
use_function(function_name: str, function_params: dict, subtask: str = ‘’, stateful: bool = True): Uses the function named
function_namewithfunction_params.statefulcontrols whether the output of this function will be saved tosubtasks_completedunder the key ofsubtask
- Assign/Remove Functions:
assign_functions(function_list: list): Assigns a list of functions to the agent
remove_function(function_name: str): Removes function named function_name from the list of assigned functions
- Show Functions:
list_functions(): Returns the list of functions of the agent
print_functions(): Prints the list of functions of the agent
Example Internal Function#
sentence_style = Function(fn_description = 'Output a sentence with <obj> and <entity> in the style of <emotion>',
output_format = {'output': 'sentence'}, fn_name = 'sentence_with_objects_entities_emotion',
llm = llm)
print(sentence_style)
Example External Function#
def binary_to_decimal(binary_string: str) -> int:
'''Converts binary_string to integer of base 10'''
return int(str(binary_string), 2)
# Initialise your agent
my_agent = Agent('Helpful assistant', 'You are a generalist agent', llm = llm)
# Assign functions
my_agent.assign_functions(function_list = [sentence_style, binary_to_decimal])
# Show the functions the agent has
my_agent.print_functions()
Approach 1: Automatic Running of Task#
my_agent.reset()
output = my_agent.run('First convert binary string 1001 to a number, then generate me a happy sentence with that number and a ball')
my_agent.status()
# give a response to user
output = my_agent.reply_user()
# query according to what you need
output = my_agent.reply_user('Output only the sentence')
my_agent.status()
my_agent.subtasks_completed
my_agent.thoughts
Approach 2: Manual Selection and Running of Functions#
If you want to just see what the agent would choose for a hypothetical task, use
select_function. This will not update the internal state, and will output function name and function paramsIf you want specificity in the process, you can just execute the agent’s functions yourself using
use_functionuse_function: Uses an agent’s function using name and params. Note that by default there will be updating of subtasks_completed when performing the function. In order not to update subtasks_completed, setstateful = False
# this should call generate_sentence_with_emotion
my_agent.reset()
function_name, function_params = my_agent.select_function(
task = 'Output a sentence with bell, dog and happy')
print(f'Selected function name: {function_name}\nSelected function params: {function_params}')
my_agent.use_function(function_name, function_params, stateful = False)
# this should call binary_to_decimal
my_agent.reset()
function_name, function_params = my_agent.select_function(
task = 'What is the decimal representation of binary number 101?')
print(f'Selected function name: {function_name}\nSelected function params: {function_params}')
my_agent.use_function(function_name, function_params, stateful = False)
# this should call use_llm
my_agent.reset()
function_name, function_params = my_agent.select_function(
task = 'Research on the benefits of exercise based on LLM')
print(f'Selected function name: {function_name}\nSelected function params: {function_params}')
my_agent.use_function(function_name, function_params, stateful = False)
3. AsyncAgent#
AsyncAgentworks the same way asAgent, only much faster due to parallelisation of tasksIt can only be assigned functions of class
AsyncFunction, or general Python functions (which will be converted to AsyncFunction)If you define your own
AsyncFunction, you should define the fn_name as well if it is not an External FunctionAs a rule of thumb, just add the
awaitkeyword to any function that you run with theAsyncAgent
Example LLM in Async Mode#
async def llm_async(system_prompt: str, user_prompt: str):
''' Here, we use OpenAI for illustration, you can change it to your own LLM '''
# ensure your LLM imports are all within this function
from openai import AsyncOpenAI
# define your own LLM here
client = AsyncOpenAI()
response = await client.chat.completions.create(
model='gpt-3.5-turbo',
temperature = 0,
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_prompt}
]
)
return response.choices[0].message.content
Example Agentic Workflow#
# This is an example of an LLM-based function (see Tutorial 0)
sentence_style = AsyncFunction(fn_description = 'Output a sentence with words <var1> and <var2> in the style of <var3>',
output_format = {'output': 'sentence'},
fn_name = 'sentence_with_objects_entities_emotion', # you must define fn_name for LLM-based functions
llm = llm_async) # use an async LLM function
# This is an example of an external user-defined function (see Tutorial 0)
def binary_to_decimal(binary_number: str) -> int:
'''Converts binary_number to integer of base 10'''
return int(str(binary_number), 2)
# Initialise your Agent
my_agent = AsyncAgent('Helpful assistant', 'You are a generalist agent', llm = llm_async)
# Assign the functions
my_agent.assign_functions([sentence_style, binary_to_decimal])
# Run the Agent
output = await my_agent.run('Generate me a happy sentence with a number and a ball. The number is b1001 converted to decimal')
# Define an Async LLM function
async def llm_async(system_prompt: str, user_prompt: str):
''' Here, we use OpenAI for illustration, you can change it to your own LLM '''
# ensure your LLM imports are all within this function
from openai import AsyncOpenAI
# define your own LLM here
client = AsyncOpenAI()
response = await client.chat.completions.create(
model='gpt-4o-mini',
temperature = 0,
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_prompt}
]
)
return response.choices[0].message.content
# This is an example of an LLM-based function (see Tutorial 0)
sentence_style = AsyncFunction(fn_description = 'Output a sentence with words <var1> and <var2> in the style of <var3>',
output_format = {'output': 'sentence'},
fn_name = 'sentence_with_objects_entities_emotion', # you must define fn_name for LLM-based functions
llm = llm_async) # you must use an Async LLM function
# This is an example of an external user-defined function (see Tutorial 0)
def binary_to_decimal(binary_number: str) -> int:
'''Converts binary_number to integer of base 10'''
return int(str(binary_number), 2)
# Initialise your Agent
my_agent = AsyncAgent('Helpful assistant', 'You are a generalist agent', llm = llm_async)
# Assign the functions
my_agent.assign_functions([sentence_style, binary_to_decimal])
# Run the Agent
output = await my_agent.run('First convert binary string 1001 to a number, then generate me a happy sentence with that number and a ball')
output = await my_agent.reply_user()
my_agent.status()
# this should call generate_sentence_with_emotion
my_agent.reset()
function_name, function_params = await my_agent.select_function(
task = 'Output a sentence with bell, dog and happy')
print(f'Selected function name: {function_name}\nSelected function params: {function_params}')
await my_agent.use_function(function_name, function_params, stateful = False)
Saving and Loading Agents#
Sometimes you want to configure your agents and save them and load them elsewhere, while maintaining the current agent state
When you use the
save_agentfunction, we store the entire agent’s internal state, include name, description, list of functions, subtasks history and all other internal variables into a pickle fileWhen you use the
load_agentfunction, and we will load the entire agent saved in the pickle file into the existing agent
Key functions: - save_agent(pickle_file_name: str): Saves the agent’s internal parameters to a pickle file named pickle_file_name (include .pkl), returns the pickle file - load_agent(pickle_file_name: str): Loads the agent’s internal parameters from a pickle file named pickle_file_name (include .pkl), returns loaded agent
Example 1: Saving Agent#
my_agent.save_agent('myagent.pkl')
Example Output#
Agent saved to myagent.pkl
Example 2: Loading Agent#
new_agent = Agent().load_agent('myagent.pkl')
Example Output#
Agent loaded from myagent.pkl
# run one task to add something to subtasks_completed
my_agent.reset()
output = my_agent.run('What is the decimal representation of binary number 101?')
# see status of agent before saving
my_agent.status()
# save agent
my_agent.save_agent('myagent.pkl')
# load agent using load_agent code
new_agent = Agent().load_agent('myagent.pkl')
# see status of loaded agent (which also includes what is saved in subtasks_completed, and shared_variables)
new_agent.status()
Contributing agent to community#
Once you are done developing your agent, we encourage you to contribute your agent to taskgen community. Agent contribution works by creating a Pull Request to taskgen repo, hence you need to have a github profile. We have taken efforts to make the process simple
Follow below steps to contribute your agent:
Create your profile on github
Set environment variable
GITHUB_USERNAMEas your github user name- Set environment variable
GITHUB_TOKENas your github token. Steps to create github token below: Sign-in to your github account on your browser
Navigate to token settings on github - settings/tokens
- Generate new token (classic)
Provide token name, expiration
Select scope - repo:public_repo
- Set environment variable
Execute
contribute_agentfunction on your agent, and provide comments on what your agent should be used for
def add(x: int, y: int) -> int:
'''Takes in x and y and returns the sum'''
return x+y
my_agent = Agent('Math Expert', 'Does Math very well. Calculate only what is necessary.', llm = llm).assign_functions(add)
my_agent.run('Calculate 3 + 2 + 5')
my_agent.reply_user()
os.environ['GITHUB_USERNAME'] = '<your username here>'
os.environ['GITHUB_TOKEN'] = '<your token here>'
# my_agent.contribute_agent(author_comments = "This agent should be used for any addition-based calculation")