Skip to main content
User input flows allow you to gather specific information from users during execution. This is useful for:
  • Collecting required parameters
  • Getting user preferences
  • Gathering missing information

How It Works

When you mark a tool with requires_user_input=True, your agent will:
  1. Pause execution before calling the tool
  2. Set is_paused to True on the run response
  3. Populate user_input_schema with the fields that need to be filled
  4. Wait for you to provide the requested values
  5. Continue execution once you call continue_run() with the filled values
The key difference from user confirmation is that here you’re actually providing data to fill in the tool’s parameters, not just approving or rejecting the tool call.

Collecting Specific Fields

You can control which fields require user input using the user_input_fields parameter. Fields not in this list will be filled by the agent automatically based on the conversation context. In the example below, the agent pauses to collect the to_address parameter from the user for the send_email tool:
from typing import List

from agno.agent import Agent
from agno.models.openai import OpenAIChat
from agno.tools import tool
from agno.tools.function import UserInputField
from agno.utils import pprint


# You can either specify the user_input_fields or leave empty for all fields to be provided by the user
@tool(requires_user_input=True, user_input_fields=["to_address"])
def send_email(subject: str, body: str, to_address: str) -> str:
    """
    Send an email.

    Args:
        subject (str): The subject of the email.
        body (str): The body of the email.
        to_address (str): The address to send the email to.
    """
    return f"Sent email to {to_address} with subject {subject} and body {body}"


agent = Agent(
    model=OpenAIChat(id="gpt-4o-mini"),
    tools=[send_email],
    markdown=True,
)

run_response = agent.run(
    "Send an email with the subject 'Hello' and the body 'Hello, world!'"
)
if run_response.is_paused:
    for tool in run_response.tools_requiring_user_input:  # type: ignore
        input_schema: List[UserInputField] = tool.user_input_schema  # type: ignore

        for field in input_schema:
            # Get user input for each field in the schema
            field_type = field.field_type
            field_description = field.description

            # Display field information to the user
            print(f"\nField: {field.name}")
            print(f"Description: {field_description}")
            print(f"Type: {field_type}")

            # Get user input
            if field.value is None:
                user_value = input(f"Please enter a value for {field.name}: ")
            else:
                print(f"Value: {field.value}")
                user_value = field.value

            # Update the field value
            field.value = user_value

    run_response = agent.continue_run(run_id=run_response.run_id, updated_tools=run_response.tools)
    pprint.pprint_run_response(run_response)
In this example, the agent will fill in subject and body based on the user’s request (“Hello” and “Hello, world!”), but will pause and ask the user for the to_address since it’s in the user_input_fields list.

Understanding UserInputField

The RunOutput object has a list of tools. When a tool requires user input, it will have a user_input_schema populated with UserInputField objects:
class UserInputField:
    name: str  # The name of the field
    field_type: Type  # The required type of the field
    description: Optional[str] = None  # The description of the field
    value: Optional[Any] = None  # The value of the field. Populated by the agent or the user.
If field.value is already set (not None), it means the agent has pre-filled it from the conversation context. You can either use that value or override it with user input.The same UserInputField structure is used in Dynamic User Input, where the agent dynamically creates these fields when it needs information.

Collecting All Fields

If you want the user to provide all fields instead of letting the agent fill some automatically, simply omit the user_input_fields parameter or pass an empty list:
@tool(requires_user_input=True)  # No user_input_fields means all fields need user input
def send_email(subject: str, body: str, to_address: str) -> str:
    """Send an email."""
    return f"Sent email to {to_address} with subject {subject} and body {body}"
This is useful when you want complete control over the data being passed to sensitive operations, or when you don’t trust the LLM to extract the right values from context.

Handling Pre-Filled Values

When you specify user_input_fields, you’re telling the agent which parameters the user should provide. The agent will automatically fill in the other parameters based on the conversation context. For example, with user_input_fields=["to_address"] on a send_email(subject, body, to_address) function:
  • subject and body (not in the list) → Agent fills these from context, value="Hello" etc.
  • to_address (in the list) → User must provide this, value=None
The user_input_schema will include all parameters, but you only need to collect values for fields where value=None:
# You can either specify the user_input_fields or leave empty for all fields to be provided by the user
@tool(requires_user_input=True, user_input_fields=["to_address"])
def send_email(subject: str, body: str, to_address: str) -> str:
    """
    Send an email.

    Args:
        subject (str): The subject of the email.
        body (str): The body of the email.
        to_address (str): The address to send the email to.
    """
    return f"Sent email to {to_address} with subject {subject} and body {body}"

agent = Agent(
    model=OpenAIChat(id="gpt-5-mini"),
    tools=[send_email],
)

run_response = agent.run("Send an email with the subject 'Hello' and the body 'Hello, world!'")
if run_response.is_paused:
    for tool in run_response.tools_requiring_user_input:
        input_schema: List[UserInputField] = tool.user_input_schema

        for field in input_schema:
            # Display field information to the user
            print(f"\nField: {field.name} ({field.field_type.__name__}) -> {field.description}")

            # Get user input (if the value is not set, it means the user needs to provide the value)
            if field.value is None:
                user_value = input(f"Please enter a value for {field.name}: ")
                field.value = user_value
            else:
                print(f"Value provided by the agent: {field.value}")

    run_response = (
        agent.continue_run(run_id=run_response.run_id, updated_tools=run_response.tools)
    )

Async Support

User input works seamlessly with async agents. Just use arun() and acontinue_run():
run_response = await agent.arun("Send an email with the subject 'Hello'")

if run_response.is_paused:
    for tool in run_response.tools_requiring_user_input:
        for field in tool.user_input_schema:
            if field.value is None:
                field.value = input(f"Please enter {field.name}: ")
    
    response = await agent.acontinue_run(run_id=run_response.run_id, updated_tools=run_response.tools)
Dynamic User Input also supports async patterns with the same methods.

Streaming Support

User input also works with streaming. The agent will emit events until it needs user input, then pause:
for run_event in agent.run("Send an email", stream=True):
    if run_event.is_paused:
        for tool in run_event.tools_requiring_user_input:
            for field in tool.user_input_schema:
                if field.value is None:
                    field.value = input(f"Please enter {field.name}: ")
        
        # Continue streaming
        response = agent.continue_run(
            run_id=run_event.run_id,
            updated_tools=run_event.tools,
            stream=True
        )

Usage Examples

Developer Resources