# Human agents (https://www.agentenvframework.com/docs/agents/human-agents)

> A person is an A2A agent too

1. To the framework, a person is an A2A agent too: `dana` serves the same A2A endpoint as `claudius`.
2. A multi-turn `prompt_agent` sends the task’s prompt to `claudius` with `message/send`.
3. It forwards the reply to `dana` the same way, as the next message in the conversation.
4. A person reads it and types an answer, and `dana`’s `run()` returns it as the reply.
5. The person’s answer is the next prompt to `claudius`.
6. The person replies with an empty line, so `dana`’s task fails and the conversation ends. Every turn is in the conversation store.

Parts of the scene:

- **claudius, an LLM agent**: `ClaudiusRLMAgent`: its `run()` answers each message with Claude.
- **dana, a human agent**: `TerminalHuman`: its `run()` shows each message to a person and returns what they type. It is served and reached like any other A2A agent.
- **A multi-turn prompt_agent**: A `prompt_agent` step with `max_conversation_turns: 3` and `user_a2a_url` set to `dana`’s A2A URL. Between turns, it sends the agent’s reply to the user and the user’s answer back.
- **The same A2A endpoint**: Both agents serve `/a2a` and take `message/send`. `prompt_agent` polls each task until it finishes, so a person has up to `user_agent_timeout_seconds`, 600 by default, to answer.
- **What the person sees**: Each of the agent’s replies, and a prompt to answer. An empty line ends the conversation.
- **The conversation**: The conversation store keeps every turn, the agent’s and the person’s, under the id the run records in `a2a_conversations`.
- **How it ends**: An empty reply returns `TaskResult.failure`, and `prompt_agent` ends a conversation when the user’s task does not complete. Otherwise it ends after `max_conversation_turns` turns.

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:

```python title="human.py"
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:

```json
{
  "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.