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 ^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: python # !pip install taskgen-ai Step 2: Import required functions and setup relevant API keys for your LLM ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: python # 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'] = '' Step 3: Define your own LLM ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Take in a ``system_prompt``, ``user_prompt``, and outputs llm response string .. code-block:: python 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_completed`` by 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 ``query`` to the user. If ``query`` is not given, then it replies based on the current task the agent is doing. If ``stateful`` is True, saves this query and reply into ``subtasks_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 ^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: python 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 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: python output = my_agent.run('Give me 5 words rhyming with cool, and make a 4-sentence poem using them') Check Agent's Status ^^^^^^^^^^^^^^^^^^^^ .. code-block:: python my_agent.status() Example Agent Reply to User - Reference the subtasks' output to answer the user's query ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: python output = my_agent.reply_user() Create Agent ------------ .. code-block:: python # 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 ------------------------- .. code-block:: python # 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 ---------------------------- .. code-block:: python # 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 docstring - After creating your agent, use ``assign_functions`` to assign a list of functions of class ``Function``, 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()`` .. code-block:: python # This is an example of an LLM-based function (see Tutorial 0) sentence_style = Function(fn_description = 'Output a sentence with words and in the style of ', 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_name`` with ``function_params``. ``stateful`` controls whether the output of this function will be saved to ``subtasks_completed`` under the key of ``subtask`` **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 ^^^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: python sentence_style = Function(fn_description = 'Output a sentence with and in the style of ', output_format = {'output': 'sentence'}, fn_name = 'sentence_with_objects_entities_emotion', llm = llm) print(sentence_style) Example External Function ^^^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: python 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 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: python 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 params - If you want specificity in the process, you can just execute the agent's functions yourself using ``use_function`` - ``use_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, set ``stateful = False`` .. code-block:: python # 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 ------------- - ``AsyncAgent`` works the same way as ``Agent``, only much faster due to parallelisation of tasks - It 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 Function - As a rule of thumb, just add the ``await`` keyword to any function that you run with the ``AsyncAgent`` Example LLM in Async Mode ^^^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: python 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 ^^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: python # This is an example of an LLM-based function (see Tutorial 0) sentence_style = AsyncFunction(fn_description = 'Output a sentence with words and in the style of ', 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') .. code-block:: python # 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 and in the style of ', 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_agent`` function, we store the entire agent's internal state, include name, description, list of functions, subtasks history and all other internal variables into a pickle file - When you use the ``load_agent`` function, 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 ^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: python my_agent.save_agent('myagent.pkl') Example Output ^^^^^^^^^^^^^^ ``Agent saved to myagent.pkl`` Example 2: Loading Agent ^^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: python new_agent = Agent().load_agent('myagent.pkl') Example Output ^^^^^^^^^^^^^^ ``Agent loaded from myagent.pkl`` .. code-block:: python # 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_USERNAME`` as your github user name - Set environment variable ``GITHUB_TOKEN`` as your github token. Steps to create github token below: - Sign-in to your github account on your browser - Navigate to token settings on github - https://github.com/settings/tokens - Generate new token (classic) - Provide token name, expiration - Select scope - repo:public_repo - Execute ``contribute_agent`` function on your agent, and provide comments on what your agent should be used for .. code-block:: python 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'] = '' os.environ['GITHUB_TOKEN'] = '' # my_agent.contribute_agent(author_comments = "This agent should be used for any addition-based calculation")