Skip to content

The person as an agent

Person is the person's side of a conversation inside a flow: it asks for input and returns what you type. Add one when the flow needs a human to answer. Person is what a flow declares the place as; HumanAgent is what fills it.

Try it

python
from hmz.flows import HumanAgent

person = HumanAgent()                      # takes only an optional name=, defaulting to "human"
person("Here is what I did. What next?")   # asks, and answers with what was typed

Saying something to it asks what to say next, and it answers with whatever you type.

In a flow

Declare a Person among the agents, and it is handed over like the rest:

python
from typing import NamedTuple

from hmz.flows import Agent, Person, flow

class Chat(NamedTuple):
    assistant: Agent
    human: Person

@flow
def run(agents: Chat, task: str) -> None:
    conversation = agents.assistant.new()
    said = task
    while said:
        answered = conversation(said, suppress=True)
        said = agents.human(answered)

That is chat, the flow the interface opens on.

A Person is not one of the agents you name with -a, because nobody is asked what the person runs. The flow above drives two agents, so you start it with one -a:

sh
hmz exec -f chat -a claude/claude-opus-5:high "Read README.md and tell me what this is."

Run from a command line, nobody is at a prompt, so it answers with nothing: the loop ends and the flow does the one thing it was given. That is what you want from chat in a script.

What it is not

A Person is not a coding agent. It runs no model and spends nothing.

Its turns are not bracketed by the begins/ends events that say whose turn it is — counting them would put the person in the graph of who handed to whom, and spin a clock at them while they thought. So the person appears in neither the handover graph of /status nor the cost readout, and the conversation with them is not one of the ones tab steps between.

It runs no moments either. A moment is a point in a turn of a model, and the person takes no such turn.

Asking them for a shape — a questionnaire

Give the person a schema, and they are asked a question per field. The model is built out of what they typed:

python
class Settled(BaseModel):
    approach: Literal["fast", "careful"] = Field(description="Which way should this be built?")
    tests: bool = Field(description="Write tests for it?")
    rounds: int = Field(default=3, description="How many rounds may it take?")

settled = agents.human("How should I do this?", schema=Settled, suppress=True)
if settled is not None and settled.tests:
    ...

A flow settles what only a person can settle in the model it is going to run on, once rather than by parsing a sentence. Each question takes the road a coding agent's own question takes, so /afk answers it the way it answers any other: nobody is there, and the questionnaire comes back as None under suppress. Which field becomes which question is a table on Answers in a shape.

The board: the half that does not wait

Saying something to the person stops the turn until they answer. That is right for a question and wrong for everything else a run wants from them, so they carry a board as well: named lines the flow and the person both write on, drawn on /status, where neither waits on the other.

python
board = agents.human.board
board.put("todo", task)                          # either of you writes this one
board.put("doing", "nothing yet", whose="flow")  # the flow's; they read it

while waiting := [one for one in board.get("todo").splitlines() if one.strip()]:
    board.put("doing", waiting[0])
    agents.builder(waiting[0], suppress=True)
    board.put("todo", "\n".join(waiting[1:]))

A line whose whose is one side's is refused to the other where it writes rather than quietly ignored. Run where nobody is at a prompt, the board is still a board — the flow writes it and reads it, and nothing changes it from outside. See The mission board.

When another flow calls yours

When a flow calls another, it may hand it one fewer agent, because nobody chooses the person. If you have your own, hand it over, so what it asks reaches whoever is at the prompt:

python
load("chat")((assistant, agents.human), task)

See also

Released under the Apache-2.0 licence.