mirror of
https://github.com/aljazceru/Tutorial-Codebase-Knowledge.git
synced 2025-12-19 15:34:23 +01:00
215 lines
11 KiB
Markdown
215 lines
11 KiB
Markdown
---
|
||
layout: default
|
||
title: "Task"
|
||
parent: "Google A2A"
|
||
nav_order: 2
|
||
---
|
||
|
||
# Chapter 2: Task - The AI's Work Order
|
||
|
||
In the [previous chapter](01_agent_card.md), we learned how to find an AI agent and read its "business card" – the **Agent Card** – to understand what it can do and how to contact it. Think of it like finding a translator's contact information.
|
||
|
||
But just knowing the translator exists isn't enough. You need to actually *give them something to translate*! How do you formally request work from an A2A agent?
|
||
|
||
That's where the **Task** comes in. It solves the problem of **requesting and tracking work**.
|
||
|
||
## What is a Task?
|
||
|
||
Imagine you run a busy workshop. When a customer comes in wanting something built or fixed, you don't just rely on a verbal request. You create a **work order** or a **job ticket**. This ticket contains:
|
||
|
||
1. **What needs to be done?** (The customer's request - e.g., "Build a small bookshelf")
|
||
2. **Who requested it?** (Customer details)
|
||
3. **A unique ID** to track this specific job.
|
||
4. **The current status** (e.g., "Not Started", "In Progress", "Awaiting Materials", "Completed").
|
||
5. **The final result** (e.g., the finished bookshelf, or notes about why it couldn't be done).
|
||
|
||
In the A2A world, a **Task** is exactly like that work order. It's the main way agents exchange work:
|
||
|
||
1. **Instructions:** It starts with the initial request message from the client (e.g., "Translate 'hello world' to French").
|
||
2. **Tracking ID:** Each task gets a unique ID so both the client and the agent know which job they're talking about.
|
||
3. **Status:** It has a state that changes as the agent works on it (e.g., `submitted`, `working`, `completed`, `failed`).
|
||
4. **Results:** When finished, it holds the output, called **Artifacts** (e.g., the translated text "Bonjour le monde").
|
||
|
||
So, if our "Translator Agent" receives a Task asking for a translation, that Task object will contain the text to translate, track whether the agent is currently translating it, and eventually hold the French translation once it's done.
|
||
|
||
## Creating and Sending a Task
|
||
|
||
How does a client (like your application, or another agent) actually create and send a Task to an agent server? It uses a specific command defined by the A2A protocol, usually called `tasks/send`.
|
||
|
||
Let's say our client found the "Translator Agent" from Chapter 1 and knows its `url` is `http://translator-agent.com/a2a`. The client wants to translate "hello".
|
||
|
||
Here's a simplified Python example of how the client might send this request:
|
||
|
||
```python
|
||
# File: samples/python/hosts/cli/cli_host.py (Conceptual Snippet)
|
||
import requests
|
||
import json
|
||
import uuid # To generate unique IDs
|
||
from common.types import TaskSendParams, Message, TextPart, Task
|
||
|
||
# Agent's communication endpoint (from Agent Card)
|
||
agent_a2a_url = "http://translator-agent.com/a2a"
|
||
|
||
# 1. Prepare the Task request details
|
||
task_id = str(uuid.uuid4()) # Generate a unique ID for this job
|
||
user_message = Message(
|
||
role="user",
|
||
parts=[TextPart(text="Translate 'hello' to French")]
|
||
)
|
||
task_params = TaskSendParams(id=task_id, message=user_message)
|
||
|
||
# 2. Create the JSON-RPC request structure
|
||
request_payload = {
|
||
"jsonrpc": "2.0",
|
||
"method": "tasks/send", # The command to send a task
|
||
"params": task_params.model_dump(exclude_none=True), # Our task details
|
||
"id": "req-1" # An ID for *this specific web request*
|
||
}
|
||
|
||
# 3. Send the request to the agent's URL
|
||
print(f"Sending task {task_id} to {agent_a2a_url}")
|
||
response = requests.post(agent_a2a_url, json=request_payload)
|
||
response.raise_for_status() # Check for HTTP errors
|
||
|
||
# 4. Process the response
|
||
response_data = response.json()
|
||
if response_data.get("result"):
|
||
# Agent accepted the task! It returns the initial Task object.
|
||
initial_task = Task(**response_data["result"])
|
||
print(f"Task created! ID: {initial_task.id}, State: {initial_task.status.state}")
|
||
elif response_data.get("error"):
|
||
print(f"Error creating task: {response_data['error']}")
|
||
|
||
```
|
||
|
||
**Explanation:**
|
||
|
||
1. **Prepare Details:** We generate a unique `task_id` and create the `Message` containing the text we want translated. These become the `params` for our request.
|
||
2. **Build Request:** We wrap our `params` in a standard structure specifying the `method` (`tasks/send`) we want the agent to execute. (This structure is part of JSON-RPC, which is used by A2A - more on this in the [next chapter](03_a2a_protocol___core_types.md)).
|
||
3. **Send:** We use the `requests` library to send this structure as JSON data via an HTTP POST request to the agent's A2A `url`.
|
||
4. **Process Response:** The agent sends back a response. If successful, the `result` contains the newly created `Task` object, likely in the `submitted` state. We print its ID and initial state. If something went wrong, the `error` field will contain details.
|
||
|
||
**Example Output:**
|
||
|
||
```
|
||
Sending task a1b2c3d4-e5f6-7890-abcd-ef1234567890 to http://translator-agent.com/a2a
|
||
Task created! ID: a1b2c3d4-e5f6-7890-abcd-ef1234567890, State: submitted
|
||
```
|
||
|
||
Now the client knows the task was received and has its unique ID (`a1b2c3d4-...`). It can use this ID later to check the status or get the final result.
|
||
|
||
## Task Lifecycle: States
|
||
|
||
A task doesn't just get created and instantly completed. It goes through different stages, represented by its `state` field. Here are the main states:
|
||
|
||
* `submitted`: The agent has received the task request but hasn't started working on it yet.
|
||
* `working`: The agent is actively processing the request (e.g., performing the translation).
|
||
* `input-required`: (Optional) The agent needs more information from the client to continue. The client would then send another message using the same Task ID.
|
||
* `completed`: The agent finished successfully. The results are available in the Task's `artifacts`.
|
||
* `failed`: The agent encountered an error and could not complete the task.
|
||
* `canceled`: The client (or agent) explicitly canceled the task before completion.
|
||
* `unknown`: The state couldn't be determined.
|
||
|
||
These states allow the client to understand the progress of their request. For long-running tasks, the agent might even send updates as the state changes (we'll cover this in [Chapter 7: Streaming Communication (SSE)](07_streaming_communication__sse_.md)).
|
||
|
||
## Under the Hood: How a Task is Handled
|
||
|
||
Let's trace what happens when the client sends that `tasks/send` request:
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant C as Client App
|
||
participant A as Agent Server (A2A Endpoint)
|
||
participant TS as Task Store (e.g., Memory, DB)
|
||
participant TL as Task Logic (e.g., Translator)
|
||
|
||
C->>A: POST /a2a (JSON-RPC: method="tasks/send", params={id="T1", msg="Translate..."})
|
||
Note right of A: Receives HTTP request, parses JSON-RPC
|
||
|
||
A->>TS: Create/Find Task Record (ID: "T1")
|
||
Note right of TS: Creates a new Task object in 'submitted' state
|
||
TS-->>A: New Task Object (ID: "T1", state: "submitted")
|
||
|
||
A-->>C: 200 OK (JSON-RPC: result={Task Object with state 'submitted'})
|
||
Note left of C: Client receives confirmation Task is created
|
||
|
||
Note over A,TL: Agent asynchronously starts processing...
|
||
A->>TL: Start processing Task "T1" (Input: "Translate...")
|
||
A->>TS: Update Task "T1" status to 'working'
|
||
Note right of TS: Updates Task record state
|
||
|
||
TL->>A: Processing finished (Output: "Bonjour")
|
||
Note over A,TS: Agent updates Task with result and 'completed' state
|
||
A->>TS: Update Task "T1" (state: 'completed', artifacts: ["Bonjour"])
|
||
|
||
```
|
||
|
||
**Steps:**
|
||
|
||
1. **Client Sends Request:** The client sends the `tasks/send` JSON-RPC request via HTTP POST to the agent's A2A URL.
|
||
2. **Server Receives:** The agent server receives the request and understands it wants to start a task.
|
||
3. **Server Stores Task:** The server creates a new `Task` record (using something like the `InMemoryTaskStore` or `FileStore` shown in `samples/js/src/server/store.ts` or conceptually managed by `samples/python/common/server/task_manager.py`). It assigns the initial `submitted` state and stores the user's message.
|
||
4. **Server Responds:** The server immediately sends a response back to the client confirming the task was created, including the initial `Task` object.
|
||
5. **Server Processes (Async):** The server (likely in the background) triggers the actual work (e.g., calls its internal translation logic). It updates the task's state in the store to `working`.
|
||
6. **Server Completes:** Once the translation is done, the server updates the task's state to `completed` and adds the result ("Bonjour") as an `Artifact` in the task record within the store.
|
||
|
||
The client can later use the Task ID (`T1`) to fetch the updated Task object (using a different command like `tasks/get`) and retrieve the final translation from the `artifacts`.
|
||
|
||
### Key Data Structures
|
||
|
||
The definition of these structures can be found in the protocol specification and helper libraries:
|
||
|
||
* **Task:** (`samples/python/common/types.py:Task`, `samples/js/src/schema.ts:Task`) Holds the ID, status, artifacts, history, etc.
|
||
* **Message:** (`samples/python/common/types.py:Message`, `samples/js/src/schema.ts:Message`) Represents a communication turn (user or agent) containing Parts.
|
||
* **Part:** (`samples/python/common/types.py:Part`, `samples/js/src/schema.ts:Part`) The actual content (text, file, or structured data).
|
||
* **Artifact:** (`samples/python/common/types.py:Artifact`, `samples/js/src/schema.ts:Artifact`) Output generated by the agent, also composed of Parts.
|
||
* **TaskStatus:** (`samples/python/common/types.py:TaskStatus`, `samples/js/src/schema.ts:TaskStatus`) Contains the `TaskState` and timestamp.
|
||
|
||
```typescript
|
||
// File: samples/js/src/schema.ts (Simplified Task Structure)
|
||
|
||
export interface Task {
|
||
// Unique identifier for the task.
|
||
id: string;
|
||
// The current status of the task.
|
||
status: TaskStatus;
|
||
// Optional list of artifacts (outputs).
|
||
artifacts?: Artifact[] | null;
|
||
// (Optional) History of messages for this task
|
||
// history?: Message[] | null;
|
||
// ... other fields like sessionId, metadata
|
||
}
|
||
|
||
export interface TaskStatus {
|
||
// The current state (e.g., "submitted", "working", "completed").
|
||
state: TaskState;
|
||
// Optional message associated with this status.
|
||
message?: Message | null;
|
||
// Timestamp of this status update.
|
||
timestamp?: string;
|
||
}
|
||
|
||
// Example Artifact containing translated text
|
||
// artifact = { parts: [ { type: "text", text: "Bonjour le monde" } ] }
|
||
```
|
||
|
||
This structure acts as the digital "work order" that travels between the client and the agent, carrying the request, tracking progress, and holding the final result.
|
||
|
||
## Conclusion
|
||
|
||
The **Task** is the fundamental unit of work in the A2A protocol. It's how one agent asks another to do something. Think of it as a formal job request or work order that:
|
||
|
||
* Contains the initial instructions (as a `Message`).
|
||
* Has a unique ID for tracking.
|
||
* Goes through different states (`submitted`, `working`, `completed`, etc.) to show progress.
|
||
* Holds the final results (`Artifacts`).
|
||
|
||
By sending a `tasks/send` request, a client initiates a Task, and by checking the Task's status and artifacts later, the client gets the results.
|
||
|
||
Now that we understand the basic concepts of finding an agent ([Agent Card](01_agent_card.md)) and giving it work ([Task](02_task.md)), let's look closer at the communication rules and the specific data types used in the A2A protocol.
|
||
|
||
**Next:** [Chapter 3: A2A Protocol & Core Types](03_a2a_protocol___core_types.md)
|
||
|
||
---
|
||
|
||
Generated by [AI Codebase Knowledge Builder](https://github.com/The-Pocket/Tutorial-Codebase-Knowledge) |