User Feedback
UserFeedbackTools lets an agent pause and ask the user structured questions with predefined options.
UserFeedbackTools exposes a single ask_user tool. The agent presents structured questions with predefined options (single or multi-select), the run pauses, and execution resumes once the user provides their selections. This is a human-in-the-loop pattern for clarifying intent mid-run.
Prerequisites
The following example requires the openai and sqlalchemy libraries.
uv pip install -U agno openai sqlalchemyExample
from agno.agent import Agent
from agno.db.sqlite import SqliteDb
from agno.models.openai import OpenAIResponses
from agno.tools.user_feedback import UserFeedbackTools
agent = Agent(
model=OpenAIResponses(id="gpt-5.2"),
tools=[UserFeedbackTools()],
instructions=[
"You are a helpful travel assistant.",
"When the user asks you to plan a trip, use the ask_user tool to clarify their preferences.",
],
db=SqliteDb(db_file="tmp/user_feedback.db"),
markdown=True,
)
run_response = agent.run("Help me plan a vacation")Resolving the Pause
When the agent calls ask_user, the run pauses with a user_feedback_schema. Collect the user's selections and continue the run:
while run_response.is_paused:
for requirement in run_response.active_requirements:
if requirement.needs_user_feedback:
selections = {}
for question in requirement.user_feedback_schema or []:
print(f"\n{question.header}: {question.question}")
for number, option in enumerate(question.options, 1):
print(f" {number}. {option.label}: {option.description or ''}")
while True:
raw = input("Choose number(s), separated by commas: ")
parts = [part.strip() for part in raw.split(",")]
if not all(part.isdigit() for part in parts):
print("Enter the displayed option numbers.")
continue
indices = list(dict.fromkeys(int(part) - 1 for part in parts))
if not indices or any(i < 0 or i >= len(question.options) for i in indices):
print("Choose from the displayed options.")
continue
if not question.multi_select and len(indices) != 1:
print("Choose one option for this question.")
continue
selections[question.question] = [question.options[i].label for i in indices]
break
requirement.provide_user_feedback(selections)
run_response = agent.continue_run(
run_id=run_response.run_id,
requirements=run_response.requirements,
)After the loop, display run_response.content. This collector expects the model to provide the question options described below.
Question Schema
ask_user accepts a list of AskUserQuestion objects:
| Field | Type | Description |
|---|---|---|
question | str | The question. Must end with a question mark. |
header | str | Short label (max 12 chars), e.g. "Destination". |
options | List[AskUserOption] | 2-4 options to choose from. |
multi_select | bool | If True, the user can select multiple options. |
Each AskUserOption has a label (1-5 words) and an optional description. These size and wording limits guide the model; the Pydantic fields do not enforce them as validators.
Toolkit Params
| Parameter | Type | Default | Description |
|---|---|---|---|
instructions | Optional[str] | defaults | Override the default LLM instructions. |
add_instructions | bool | True | Add the instructions to the agent's system message. |