Tasks are the fundamental building blocks of AI workflows in Marvin. They represent discrete, well-defined objectives that need to be accomplished by an AI agent.

import marvin

task = marvin.Task("Write the Marvin docs")

What are tasks?

LLMs excel when given clear, specific instructions that allow them to focus their knowledge and capabilities on a well-defined goal. A Task in Marvin is a structured way to define these instructions and guide the AI’s behavior. Each task represents a “checkpoint” that requires the AI to meet an observable, verifiable goal. In this way, tasks serve as a bridge between the structured world of traditional software and the more fluid, adaptive world of AI.

This task-centric approach allows you to leverage the full power of AI while maintaining precise oversight. Each task becomes a checkpoint where you can validate outputs, ensuring that the AI’s work aligns with your application’s requirements and constraints.

Creating tasks

A task in Marvin typically consists of:

  • Instructions: what needs to be accomplished
  • Expected output: what form the result should have, including any constraints or validation
  • Agent: the AI entity responsible for executing the task
  • Tools: any additional capabilities needed to complete the task
  • Memories: any relevant context or history that should be available to the agent

In practice, you will often use the marvin.run function to create and run a task in a single step. This is a common operation and accepts all the same arguments as creating a Task directly.

Using the Task class

The most straightforward way to create a task is by using the Task class:

import marvin

task = marvin.Task(
    instructions="Write a poem about the provided topic",
    result_type=str,
    context={"topic": "AI"}
)

result = task.run()
print(result)

Agents

The agents parameter specifies which AI agents are assigned to complete the task. If not provided, Marvin will use the default agent. You can provide either a single agent or a team of agents that will work together as a unit.

import marvin

task = marvin.Task(
    instructions="Write a poem about AI",
    agents=[marvin.Agent(name="Marvin", model="openai:gpt-4o-mini")]
)

Task properties

When creating a task, you can configure various properties to define its behavior and requirements. Here are the key configuration options:

Instructions

The instructions of a task tell the agent what needs to be accomplished. This is the only required task configuration, as it indicates what the agent should do.

import marvin

poem = marvin.run(instructions="Write a poem about AI")

print(poem)

Instructions can be “meta”, especially if you have an agent that is working on your workflow itself (e.g. monitoring progress, creating new tasks, etc.). Be creative!

import marvin

marvin.run("Write a poem... then fail this task.", allow_fail=True)

Result type

A task’s result type indicates the type of value that the task will return. This is used to validate the task’s result and to help agents understand the task’s output.

A variety of different result types are supported, including:

  • Builtin types: str, int, bool, list, dict, etc.
  • None: sometimes a task requires agents to take actions but not return any specific value
  • Builtin collections: Tuple[int, str], List[str], Dict[str, int], etc.
  • Annotated types: Annotated[str, "a 5 digit zip code"]
  • Pydantic models
  • Lists of literal values: provide a list of values to require the agent to choose one of them as its result. For example,["book", "movie", "album"] would require the agent to choose one of the three values.

The default result type is str.

Pydantic model example:

import marvin
from pydantic import BaseModel

class Name(BaseModel):
    first: str
    last: str

name = marvin.run("The input is 'John Doe'", result_type=Name)

print(repr(name))

Classification example:

import marvin

media = marvin.run(
    "Star Wars: Return of the Jedi",
    result_type=["book", "movie", "album"]
)

print(media)

Result validator

You can specify a custom validation function for the task’s result using the task’s result_validator parameter. This function will be called with the raw result and should return the validated result or raise an exception if the result is not valid.

import marvin

def validate_even(value: int) -> int:
    if value % 2 != 0:
        raise ValueError("Value must be even")
    return value

number = marvin.run("Choose a number", result_validator=validate_even)

print(number)

Context

The context of a task is a dictionary that provides additional information about the task. This information is visible to agents during execution, helping them understand the task they are working on.

import marvin

is_spam = marvin.run(
    "Is this email spam?",
    result_type=bool,
    context=dict(email='You just won a million dollars!'),
)

print(is_spam)

Tools

The tools of a task are a list of tools that the task requires. These tools will be made available to the agent during execution.

import marvin
import random


def roll_dice(n_dice: int):
    return [random.randint(1, 6) for _ in range(n_dice)]

rolls = marvin.run(
    "Roll 3 dice",
    result_type=list[int],
    tools=[roll_dice],
)

print(rolls)

Memories

Tasks can be configured with a list of memories that provide additional context to the agent. These memories can include relevant history, knowledge bases, or other information that helps the agent complete the task.

import marvin
from marvin.memory import Memory

memory = Memory(key="documentation")
memory.add("Marvin is a powerful AI framework...")

task = marvin.Task(
    instructions="Explain what Marvin is",
    memories=[memory]
)

Parent

Tasks can be configured with a parent task. Creating hierarchies of tasks can help agents understand the relationships between different tasks and to better understand the task they are working on.

Task Planning

When a task is created with plan=True, the agent is given the ability to break down complex tasks into smaller, more manageable subtasks. This is particularly useful for handling large or complex objectives that benefit from being tackled incrementally.

The agent can create subtasks at any point during task execution, and these subtasks become part of the task hierarchy. Each subtask is treated as a dependency of the parent task, ensuring that all subtasks are completed before the parent task is considered finished.

import marvin

task = marvin.Task(
    instructions="Write a comprehensive report about AI",
    plan=True  # Enable task planning
)

When planning is enabled, the agent can:

  • Create one or more subtasks at any time
  • Define clear, focused objectives for each subtask
  • Establish dependencies between subtasks
  • Monitor progress through the task hierarchy

This feature is especially valuable for:

  • Breaking down complex tasks into manageable pieces
  • Creating checkpoints in long-running processes
  • Enabling parallel work on independent subtasks
  • Maintaining clear progress tracking

Depends on

Tasks can be configured with a list of tasks that they depend on. This information helps Marvin prioritize work and avoid running a task before its upstream dependencies are complete.

Additional Options

Marvin tasks support several additional options to control their behavior:

  • allow_fail: Whether the task is allowed to be marked as failed (default: False)
  • allow_skip: Whether the task is allowed to be skipped (default: False)
  • cli: Whether to enable CLI interaction tools for the agent (default: False)
  • plan: Whether to enable task planning capabilities, allowing the agent to create subtasks to help complete the parent task (default: False)

Runtime properties

The following properties of a Task are set during task execution, and can be examined as part of your workflow’s logic.

Status

The status of a task reflects whether an agent has started working on it, and what the ultimate outcome of that work was. Tasks are always created with a PENDING status, progress to a RUNNING status when the agent begins to work on it, and finally moves to one of a few completed statuses when the task is finished.

Agents use tools to mark tasks as SUCCESSFUL or FAILED. Successful tasks will also have a result property, which contains the task’s final output. This is a value that satisfies the task’s instructions and result type configuration. Failed tasks will have an error message as their result.

import marvin

task = marvin.Task("Write a poem about AI")
task.run()

print(task.status)

In addition to checking the status explicitly, you can call a number of helper methods on the task:

MethodDescription
is_pending()Returns True if the task is pending.
is_running()Returns True if the task is running.
is_successful()Returns True if the task is successful.
is_failed()Returns True if the task is failed.
is_skipped()Returns True if the task is skipped.
is_complete()Returns True if the task is complete (either successful, failed, or skipped)
is_incomplete()Returns True if the task is incomplete (either pending or running)
is_ready()Returns True if the task is ready to be worked on (i.e. all dependencies are complete but the task is incomplete)

Result

When a task is completed successfully, its result property will contain the task’s final output. This is a value that satisfies the task’s instructions and result type configuration.

If a task fails, its result property will contain an error message describing the failure.

import marvin

task = marvin.Task("Write a poem about AI")
task.run()

print(task.result)

Tools and Context

Tasks in Marvin can be enhanced with tools and context:

  • Tools are Python functions that the task can use to perform actions (like API calls or data retrieval)
  • Context is additional information provided to help the task succeed

For example:

import marvin
import datetime

def get_current_time() -> str:
    """Get the current date and time."""
    return datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")

task = marvin.Task(
    instructions="Tell me the current time and create a greeting appropriate for that time of day",
    tools=[get_current_time],
    context={"user_name": "Alex"}
)

result = task.run()
print(result)

For a comprehensive guide on working with tools and context, see the Tools and Context documentation.