Overview

The Upsonic framework uses Tasks as its core component. Tasks can be executed by agents or direct LLM calls and can be customized with various parameters, tools, and context. The framework automatically generates the necessary steps within tasks.

The task-centric approach advantages:

  1. It removes the restriction of binding agents to single tasks, allowing them to handle multiple tasks efficiently.
  2. It improves programmability by enabling task dependencies like websites, company data, and competitor information to be defined programmatically instead of being embedded in individual agents. This approach eliminates the need for separate agents for repetitive operations like competitor analysis, creating a more scalable and maintainable system.

Creating a Task

Tasks can be imported into your project and created with custom identifiers like ‘task1’, ‘task2’ or any descriptive name you choose.

from upsonic import Task, Agent

agent = Agent(name="Historian")

task = Task("Do an in-depth analysis of US history")

agent.print_do(task)

Task Attributes

Tasks within the framework can range from basic to complex, depending on your specific requirements. The framework is designed with simplicity in mind, requiring only one mandatory parameter: the description. All other parameters are optional, providing flexibility in task configuration.

AttributeParametersTypeDescription
DescriptiondescriptionstrA clear and concise statement of what the task.
ImagesimagesList[str]Put your images to your task
Response Format (Optional)response_formatOptional[List[Union(BaseModal, ObjectResponse)]]Describe the response you expect.
Response Language (Optional)response_langOptional[str]Define the output language for your tasks.
Tools (Optional)toolsOptional[List[Union(MCP, Function)]]The tools needed to complete the task.
Context (Optional)contextOptional[List[Union(Task, KnowledgeBase, str)]]Context that helps accomplish the task

Adding Tools to a Task

Tools play a crucial role in agent functionality by bridging the gap between LLMs and real-world applications such as APIs, services, and search engines.

The framework supports two distinct types of tool implementation. The first option allows you to utilize Python functions directly as tools within Upsonic agents. The second approach leverages the Model Context Protocol (MCP), a standardized protocol for LLM tools that supports multiple platforms including Python, Node.js, and Docker. MCP tools are continuously developed and maintained by both companies and the community, with detailed information available in the “Tools” section.

Integrating a tool into your task is straightforward: simply create a list containing the desired tool’s class name and assign it to the “tools” parameter in the Task object.

1

Function Tools

Let’s define a function named is_page_available. This function will perform a simple URL validation check, returning True if the specified URL is accessible, making it useful for verifying web resources.

from upsonic import Task, Agent
from upsonic.tools import Search


# My Function Tool
import requests
def is_page_available(url: str) -> bool:
    return requests.get(url).status_code == 200


# Creating Task
task = Task(
  "Check upsonic.ai is available", 
  tools=[is_page_available] # Putting Function Tool to the task
)

# Creating Agent
agent = Agent(name="Coder")

# Running the task
agent.print_do(task)
2

MCP Tools

This example demonstrates integration with the HackerNews MCP Server, which provides several functions including get_stories, get_story_info, search_stories, and get_user_info. The MCP framework simplifies the process of connecting our agents to external services, as illustrated by this HackerNews integration.

from upsonic import Task, Agent
from upsonic.tools import Search


# My MCP Tools
class FetchMCP:
    command = "uvx"
    args = ["mcp-server-fetch"]


# Creating Task
task = Task(
  "What upsonic.ai is?", 
  tools=[FetchMCP] # Putting MCP Tool to the task
)

# Creating Agent
agent = Agent(name="Coder")

# Running the task
agent.print_do(task)
3

Together - Function + MCP Tools

Once you’ve configured your custom tools, they can be directly incorporated into the Task object. The agent will then automatically utilize these tools as needed during task execution.

from upsonic import Task, Agent
from upsonic.tools import Search


# My Function Tool
import requests
def is_page_available(url: str) -> bool:
    return requests.get(url).status_code == 200


# My MCP Tools
class FetchMCP:
    command = "uvx"
    args = ["mcp-server-fetch"]


# Creating Task
task = Task(
  "What upsonic.ai is?", 
  tools=[is_page_available, FetchMCP] # Putting multiple tools
)

# Creating Agent
agent = Agent(name="Coder")

# Running the task
agent.print_do(task)

Putting Task to Another Task as Context

The framework supports the combination of multiple tasks to handle complex operations, particularly useful in scenarios requiring deep analysis followed by report generation. While individual tasks may be complex, the true power lies in their interconnection. By creating task chains and linking them through shared context, you can build sophisticated workflows that seamlessly pass information between tasks.

from upsonic import Task, Agent

# Creating Agent
agent = Agent(name="Entrepreneur")

# Creating Task 1
task = Task("Do an in-depth analysis of the history of chips")

# Creating Task 2
task2 = Task(
  "Prepare a draft report on Europe's position", 
  context=[task] # Add Task 1 in a list as task context
)

# Running the task
agent.print_do(task)
agent.print_do(task2)

The Upsonic framework explains the context to the LLM for your purposes. You don’t have to worry about sharing multiple contexts.

Giving Knowledge Base as Context

The framework incorporates a robust KnowledgeBase feature designed to extend LLM models beyond their inherent knowledge limitations. While LLM models are constrained by their training data boundaries, real-world applications frequently demand access to external information sources such as PDFs, documents, and spreadsheets. Through seamless integration with the Context system, the KnowledgeBase feature enables you to efficiently incorporate these external data sources into your tasks, enhancing the model’s capability to process and utilize supplementary information.

You can see the supported files and options of the Knowledge Base System from here.

1

Define a KnowledgeBase

In this example, we’ll use a PDF and a web link to make a knowledge base.

from upsonic import Task, Agent
from upsonic import KnowledgeBase

# Creating Knowledge Base
resume_list = KnowledgeBase(
  sources=["resume1.pdf", "resume2.pdf"]
)

# Creating Task
task = Task(
  "Summerize the Resumes", 
  context=[resume_list] # Adding Knowledge Base to the task
)

# Creating Agent
agent = Agent(name="Head of Development")

# Running the task
agent.print_do(task)

Giving Images to Task

LLMs are particularly successful in reading images. Therefore, within the task you assign to the agent, you can enable it to read text from images, understand yellow highlights, interpret graphs, and perform any analysis by including images. For this, the image path needs to be specified using the images parameter.

from upsonic import Task, Agent

# Creating Task
task = Task(
  "Extract the names in the paper",
  images=["paper.png"] # Add your image paths
)

# Creating Agent
agent = Agent(name="OCR Agent")

# Running the task
agent.print_do(task)

Giving string as Context

Tasks represent specific objectives that need to be accomplished within the system. These tasks often contain variable elements such as URLs, names, individuals, or topics. Traditionally, all these components would need to be incorporated directly into the prompt, resulting in a non-programmatic approach. This conventional method relies heavily on f-strings or format operations, which significantly limits prompt management capabilities and constrains it within the main execution flow.

In the context system we also support str as context. This provide an capability to put your variables to outside of the prompt.

Use this when you need to separate your prompt and variable, like in a city list. You don’t need to use it if your prompt doesn’t have variables.

from upsonic import Task, Agent

# String as Context
city = "New Yorg"


# Creating Task
task = Task(
  "Find resources in the city",
  context=[city] # Adding city string as context
)

# Creating Agent
agent = Agent(name="City Guide")

# Running the task
agent.print_do(task)

Response Format

The Upsonic framework uses Pydantic BaseModel compatibility for defining expected results, enabling programmatic handling of agent responses. By specifying a response format, the system returns structured objects rather than raw text, allowing for seamless integration and logic implementation within your application.

For instance, when requesting a list of cities, the system returns a structured list of strings containing city names. This approach emphasizes value-oriented development by abstracting away the implementation details, allowing developers to focus on utilizing the data rather than managing its format and parsing.

from upsonic import Task, Agent
from pydantic import BaseModel


# Response format for Task
class TravelResponse(BaseModel):
  cities: list[str]


# Creating Task
task = Task(
  "Create a plan to visit cities in Canada", 
  response_format=TravelResponse # Specify the response format
)


# Creating Agent
agent = Agent(name="City Guide")

# Running the task
agent.print_do(task)

Response Language

Tasks internally handle their reasoning and logic in English. Therefore, tasks inherently perform better in English. However, if a different language is required for the output, you can directly utilize the response_lang parameter within the Upsonic framework.

This parameter integrates a one-to-one translation system that converts the output into your desired language.

from upsonic import Task

task = Task(
  "Create a plan to visit cities in Canada", 
  response_lang="Deutsch" # Specifying language of response
)

Accessing to Task Results

Tasks can be executed through two distinct runners: direct LLM calls or agents. These runners serve as execution mechanisms for task processing and result generation, offering flexibility in implementation. The system supports both parallel processing and multi-client operations, enhancing scalability and performance.

To maintain a controlled and organized infrastructure, all results are stored within the Task object itself. This architectural decision provides a centralized approach to result management, enabling better monitoring, access, and control over task outcomes. Such design creates a robust and manageable infrastructure that can be effectively tailored to your specific requirements.

When you run the task, the results are stored in the Task.response. You can get it directly.

from upsonic import Task, Agent

# Creating Task
task = Task("Make a deep analyze to history of chips")

# Creating Agent
agent = Agent(name="Product Manager")


# Running and Accessing to result - Exactly Same
 
result1 = agent.do(task) # 1.Way

result2 = task.response # 2.Way

Hey, just a heads-up: if you set a response_format, the task response will be an object of your class.

from upsonic import Task, Agent
from pydantic import BaseModel

# Response format for Task
class TravelResponse(BaseModel):
  cities: list[str]


# Creating Task
task = Task(
  "Create a plan to visit cities in Canada", 
  response_format=TravelResponse # Specify the response format
)


# Creating Agent
agent = Agent(name="City Guide")


# Running the task
agent.print_do(task)


# Geting the results as object
result = task.response

print("Cities")
for i in result.cities:
  print(i)