Task и Artifact образуют жизненный цикл работы
Task - это сердце A2A. Он хранит идентификатор, context, status, history и artifacts. Терминальные состояния завершают работу; interrupted-состояния позволяют запросить ввод или authentication. А Artifact - это материализованный результат, а не просто последнее сообщение: именно он, а не текст в чате, является тем, ради чего задача существовала.
Сам жизненный цикл проще всего увидеть как цепочку состояний.
- Message
- submitted
- working
- artifact
- completed
За этой линией стоит таблица состояний - каждое говорит клиенту, что делать дальше.
State · Класс · Что делает client
submitted- Active; Сохраняет taskId, ждет updateworking- Active; Показывает progress, может cancelinput_required- Interrupted; Показывает вопрос и отправляет follow-upauth_required- Interrupted; Проходит разрешенный auth flowcompleted- Terminal; Валидирует artifactsfailed- Terminal; Классифицирует retry/escalationcanceled- Terminal; Не ожидает дальнейших updatesrejected- Terminal; Не повторяет без изменения запроса
Состояния делятся на три класса, и это деление определяет поведение клиента. Active (submitted, working) - клиент ждёт и показывает прогресс. Interrupted (input_required, auth_required) - клиент отвечает на вопрос или проходит auth. Terminal (completed, failed, canceled, rejected) - дальнейших обновлений не будет, и реакция на каждый терминал разная: валидировать artifacts, классифицировать retry, не повторять без изменения запроса.
Сам artifact в нашем примере несёт сразу два представления - Markdown для человека и JSON для кода.
try {
const review = reviewRelease(reference.service, reference.version);
const artifact: Artifact = {
artifactId: randomUUID(),
name: "release-review.md",
description: "Детерминированный отчёт о готовности релиза",
parts: [
{
content: { $case: "text", value: reviewToMarkdown(review) },
metadata: undefined,
filename: "release-review.md",
mediaType: "text/markdown"
},
{
content: { $case: "data", value: review },
metadata: undefined,
filename: "release-review.json",
mediaType: "application/json"
}
],
metadata: { decision: review.decision },
extensions: []
};
eventBus.publish(AgentEvent.artifactUpdate({
taskId,
contextId,
artifact,
append: false,
lastChunk: true,
metadata: undefined
}));Пройти по состояниям вручную и почувствовать переходы помогает симулятор.
Симулятор A2A lifecycle
Events еще не опубликованы.
Artifact может обновляться по частям. При streaming сервер публикует artifact-обновления с семантикойappend/lastChunk. Клиент собирает их поartifactIdи не считает каждый chunk новым результатом - иначе один отчёт превратится в десяток.
Мы знаем, из чего состоит работа A2A-агента. Теперь соберём самого агента - и первое архитектурное решение здесь про то, что отделить от протокола.
A2A Task в состоянии input_required. К какому классу оно относится и что делает клиент?