Human agents
A person is an A2A agent too
The framework models a human agent no differently from an LLM agent: both serve an A2A endpoint,
take messages and reply. Only run() differs, because a person writes the reply instead of a
model. So a task can put a person wherever an agent goes, such as the user in a multi-turn
conversation with claudius.
Write a human agent
A human agent is an SDK agent whose run() waits for a person. This one shows each message in a
terminal and returns what the person types:
import asyncio
from agentenv_protocol.a2a_agent import (
AgentEnvAgent, AgentIdentity, TaskRequest, TaskResult, TextPart, a2a_agent,
)
@a2a_agent(identity=AgentIdentity(
name="dana", description="Dana, answering from a terminal.", version="1.0.0",
))
class TerminalHuman(AgentEnvAgent):
async def run(self, request: TaskRequest) -> TaskResult:
message = "\n".join(part.text for part in request.parts if isinstance(part, TextPart))
reply = await asyncio.to_thread(input, f"\n{message}\n> ")
if not reply.strip():
return TaskResult.failure("ended", "The person ended the conversation.")
return TaskResult.builder().succeeded().add_text(reply).build()
if __name__ == "__main__":
TerminalHuman().serve()serve() gives it the same A2A endpoint and agent card as any agent, and it needs no extensions.
Anything a person can answer from works the same way: a web form, a chat app or a review queue.
Talk to it from a task
Run python human.py and point a prompt_agent step at it. max_conversation_turns above 1 makes
the step a conversation, and user_a2a_url names who plays the user:
{
"id": "reply",
"type": "prompt_agent",
"agent_name": "claudius",
"prompt_id": "reply",
"prompt": "Send the Q3 numbers to Sam Lee.",
"max_conversation_turns": 3,
"user_a2a_url": "http://localhost:<port>"
}Each of claudius's replies goes to the person, and their answer is its next prompt. The
conversation ends after max_conversation_turns turns, or earlier when the person's task does not
complete, as with the empty line above. A person has user_agent_timeout_seconds, 600 by default,
to answer each turn.
Without user_a2a_url, the step uses the endpoint in [conversations] default_human_a2a_url or
AGENT_ENV_HUMAN_A2A_URL. The framework ships no human endpoint, so you run one like the agent above.
The conversation record
Every turn, the agent's and the person's, lands in the conversation store. The run records its id
under a2a_conversations in the context's metadata, keyed by the step's id, and agent-env up
shows it in the run's Conversation tab.
Last updated on