Assigning and coordinating work

Once a system has a manager agent and multiple worker agents, the next challenge is getting useful work done. For real programs, this means breaking a larger goal into pieces, assigning those pieces clearly, and keeping track of how they fit together. This lesson exists to orient us around how work is represented, assigned, and coordinated in a manager–worker architecture.

Representing work as discrete tasks

In a multi-agent system, work is easiest to manage when it is broken into discrete tasks. A task is a clearly defined unit of work with a known purpose and expected outcome. Instead of asking a worker to “help with the site,” the manager assigns something concrete, such as generating a single HTML page.

Tasks are usually represented as simple data structures. They capture what needs to be done, along with any inputs required to do it.

task = {
    "id": "generate-mars-page",
    "type": "generate_page",
    "body": "Mars",
}

This kind of representation makes tasks easy to pass around, store, and reason about.

Assigning tasks from a manager to workers

The manager agent is responsible for assigning tasks to workers. This assignment is an explicit act: the manager chooses a task and decides which worker should handle it. The worker does not decide what to work on next unless the manager asks it to.

Assignment can be as simple as selecting a worker and handing over the task object.

worker = workers["html_generator"]
worker.assign(task)

At this point, responsibility for the task is clear. The manager knows who is doing the work, and the worker knows exactly what it has been asked to do.

Tracking task ownership

Once tasks are assigned, the system needs to remember who is responsible for what. Without this, coordination quickly becomes ambiguous. Task ownership is usually tracked in manager-owned state.

This might be as simple as a dictionary that maps task IDs to worker names.

task_assignments = {
    "generate-mars-page": "html_generator"
}

With this structure in place, the manager can answer basic questions like which tasks are in progress and which worker should report results.

Coordinating execution across multiple workers

When multiple workers are active, coordination becomes more than just assignment. The manager needs to decide when tasks are started, whether they can run at the same time, and how progress is monitored.

Coordination often follows a simple pattern: assign tasks, wait for results, then decide what to do next. The manager stays in control of the overall flow while workers focus only on execution.

for task in pending_tasks:
    assign_task(task)

collect_results()

This keeps the system predictable, even when several workers are operating independently.

Managing dependencies between tasks

Some tasks cannot begin until others are finished. These dependencies must be made explicit so the manager can enforce the correct order of execution. A task might depend on data, files, or decisions produced by another task.

Dependencies are often represented by listing prerequisite task IDs.

task = {
    "id": "build-index",
    "depends_on": ["generate-mars-page", "generate-moon-page"]
}

With dependencies defined, the manager can delay assignment until the required tasks are complete. This avoids race conditions and ensures that workers always have the inputs they need.

Conclusion

By representing work as discrete tasks, assigning them deliberately, tracking ownership, coordinating execution, and respecting dependencies, a manager agent can keep multiple workers aligned toward a single goal. At this point, we are oriented to how work flows through a manager–worker system and how coordination replaces ad-hoc cooperation with structured control.