## Summary Follow-up suggestions can re-offer a request the main answer declined. This PR makes the default suggestions respect the answer's boundaries and makes follow-ups configurable through one argument. - **Boundary-aware default prompt.** The follow-up prompt asks the model to stay within what the answer covered, not to re-offer a declined request, not to invent missing information, and to treat the conversation as data. It applies to every Agent and Team with follow-ups enabled. This is prompt guidance, not enforcement: structure and count are validated, semantics are not. - **`followups: bool | FollowupConfig`.** `False` disables, `True` enables the defaults, and a `FollowupConfig` enables and configures: count range, separate model, custom instructions. The instructions go only to the follow-up call, never the main model call. - **Count on the config.** Either `num_followups` (exactly N, the same meaning as the released top-level argument) or a `min_followups`/`max_followups` range. `max_followups` alone lets the model return fewer or none when a declined request leaves nothing useful in scope. The maximum is enforced by clipping; the minimum is requested in the prompt. - **Not a breaking change.** Existing code keeps getting exactly N suggestions; only the prompt wording changes. - **Stale suggestions are cleared** before every regeneration, including when generation is skipped. `None` means follow-ups were absent, failed, malformed or cancelled; a follow-up failure never replaces the main answer. This refreshes and extends #7248 by @fenil210-cactus, retaining the contributor's commits, on an Agno-owned branch. It is the proposed replacement for #7248. ## API ```python from agno.agent import Agent, FollowupConfig Agent(followups=False) # disabled Agent(followups=True) # exactly 3, main model Agent(followups=True, num_followups=5) # exactly 5, as today Agent( followups=FollowupConfig( max_followups=3, # up to 3, possibly none model="openai:gpt-5.5", instructions="Suggest only documentation questions.", ) ) ``` `Team` accepts the same forms. | You write | You get | |---|---| | `followups=True` | exactly 3 | | `followups=True, num_followups=5` | exactly 5 | | `FollowupConfig()` | exactly 3, the same as `True` | | `FollowupConfig(num_followups=5)` | exactly 5 | | `FollowupConfig(max_followups=3)` | up to 3, possibly none | | `FollowupConfig(min_followups=1, max_followups=3)` | 1 to 3 | | `FollowupConfig(min_followups=1)` | 1 to 3 (the maximum defaults to 3) | | `FollowupConfig(num_followups=5, max_followups=3)` | `ValueError`: `num_followups` or `min_followups`/`max_followups`, not both | - The prompt's count line follows the range: "exactly N", "at most N" or "between min and max". The "return fewer, including an empty list" sentence is added only when the minimum is 0. - The maximum is enforced by clipping. The minimum is only requested; if the model returns fewer, they are passed through as-is. - `FollowupConfig` validates its count when built: `num_followups >= 1`, `max_followups >= 1`, `0 <= min_followups <= max_followups`, and `num_followups` is not combined with a range. - **Schema-level enforcement was considered and left out.** OpenAI strict JSON schema does enforce `minItems`/`maxItems`, but in a live check with schema and prompt in agreement it changed nothing: the prompt alone hit the requested counts in 72 of 72 calls. Support for these keywords varies by provider, and when the schema and the prompt disagreed, calls timed out or failed. The maximum is already guaranteed for every provider by clipping. - `agent.followups` reads back as what was passed: the bool, or the config. A string model on the config is resolved at construction on a copy, so a config shared between components is never mutated. - `agent.num_followups` is the effective maximum. - **Not a breaking change.** The top-level arguments keep working exactly as before: `followups=True` with `num_followups=` and `followup_model=`. - **One form or the other.** Passing a `FollowupConfig` together with `num_followups` or `followup_model` raises `ValueError`, even when the values agree. There are no precedence rules between the two forms. With a config, unset values use the defaults: 3 suggestions and the component model. - `deep_copy()` keeps the config. `deep_copy(update={"followups": FollowupConfig(...)})` replaces it; `deep_copy(update={"num_followups": n})` on a component with a config raises, like the constructor. - `to_dict` / `from_dict` store whichever form was used: `followups` as `True`, `False` or the config's fields, plus top-level `num_followups` and `followup_model` only when there is no config. Models are stored by identity only (`id`, `name`, `provider`), never with request headers or credentials; register the live model in a `Registry` to keep custom endpoints. ## Implementation | Area | Change | |---|---| | `agno/agent/followup.py` | `FollowupConfig` with `num_followups` or `min_followups`/`max_followups`, and `count_range()`; `resolve_followup_settings` (rejects a config combined with the top-level arguments, returns the effective count); `prepare_followup_call` (clears stale suggestions, picks model, count and instructions, resolves a string model assigned after construction); `drop_derived_followup_fields` for `deep_copy` | | `agno/agent/_response.py` | Boundary-aware prompt, count line per range, clipping to the maximum; `_run_followups_model` / `_arun_followups_model` shared by all eight Agent and Team generate functions | | `agno/team/_response.py` | The four Team generate functions call the shared helpers | | `agent.py`, `team.py`, `team/_init.py`, `*/_utils.py`, `*/_storage.py` | Union-typed `followups`, `num_followups: Optional[int] = None`, the one-form check at construction, `deep_copy` handling, storage round trip | | `agno/environments/runner.py` | Rollout isolation gives the config's model a cache-off copy per attempt | | Cookbook | `02_agents/14_advanced/followup_instructions.py`, README section, TEST_LOG entry | ## Validation On the latest commit (`6f526f350d`), Python 3.12: - `tests/unit/{agent,team,environments,run,utils}`: 2869 passed, 3 skipped, 1 failed. The failure, `tests/unit/team/test_team_skills.py::test_system_message_contains_skills_snippet`, fails identically on `main`. - New tests fail on the previous head and pass now. They cover each count mode through the public paths for Agent and Team (sync, async, streaming): prompt wording, clipping, and when the "return fewer" sentence appears. They also cover config validation, fewer-than-minimum results, and a round trip of the range. Earlier tests still cover the one-form rule, a string config model assigned after construction, and stale suggestions. - `./scripts/format.sh` clean. `./scripts/validate.sh`: ruff passes; mypy reports errors only in files this PR does not touch (optional SDK stubs such as `utils/models/_genai_compat.py`). - Live prompt check: four refusal scenarios plus a normal answer, 5 runs each on `gpt-5.6-luna` and `gpt-5.5`, counting suggestions that point back at the declined topic. | Prompt | luna | gpt-5.5 | |---|---|---| | previous prompt, exactly 3 | 51/60 | 26/60 | | this PR, exactly 3 (default) | 7/60 | 2/60 | | this PR, up to 3 (`max_followups=3`) | 2/59 | 4/60 | | this PR, 1 to 3 | 6/59 | 5/60 | With this PR, none re-offered the declined request itself. The remaining hits point elsewhere ("Provide a reliable weather source for Paris") or ask about availability ("Ask whether a Rust SDK is planned"). Differences between the three modes are within noise at this sample size. The normal answer got 3 suggestions in every run. - Cookbook (`max_followups=3`): three live runs with `gpt-5.5`; the agent declined the poem each time and every follow-up stayed within Python documentation. ## Remaining considerations - Team continuation does not regenerate follow-ups today (inherited, unchanged here). A separate PR should cover it across sync/async/streaming and success/failure/cancel, and weigh the extra model call. - #9507 proposes a differently shaped `FollowupConfig`. It needs a decision before this merges so we don't ship two shapes. - Semantic boundaries are best-effort; an out-of-scope suggestion can still be returned. Public docs are deferred; this PR updates the cookbook only. ## Type of change - [x] Bug fix - [x] New feature - [ ] Breaking change - [x] Improvement - [ ] Model update - [ ] Other: The follow-up prompt wording changes for every component with follow-ups enabled; suggestion counts do not change unless a `FollowupConfig` asks for a range. --- ## Checklist - [x] Code complies with style guidelines - [x] Ran format/validation scripts (`./scripts/format.sh` and `./scripts/validate.sh`) - format clean; validate fails only on pre-existing mypy errors in untouched files (see Validation) - [x] Self-review completed - [x] Documentation updated (comments, docstrings) - [x] Examples and guides: Relevant cookbook examples have been included or updated (if applicable) - [ ] Tested in clean environment (left to hosted CI) - [x] Tests added/updated (if applicable) ### Duplicate and AI-Generated PR Check - [ ] I have searched existing [open pull requests](https://github.com/agno-agi/agno/pulls) and confirmed that no other PR already addresses this issue (related: #7248, #9507) - [x] If a similar PR exists, I have explained below why this PR is a better approach - [x] Check if this PR was entirely AI-generated (by Copilot, Claude Code, Cursor, etc.) --- ## Additional Notes Related PRs: #7248 (the contributor's original; this PR retains those commits and replaces it) and #9507 (competing config shape; see Remaining considerations). 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Yash Pratap Solanky <[email protected]> Co-authored-by: ysolanky <[email protected]>
Build, run, and manage agent platforms.
Introduction
Agno is a framework and runtime for agent platforms. Build agents, run them as a service, manage your platform using a web UI.
- Build your agent platform using the Agno SDK.
- Run your agent platform using the AgentOS runtime.
- Manage everything using the AgentOS UI.
Agno allows you to own your agent stack. Maintain control of your data, memory, and security posture (JWT-based RBAC), and turn your agent platform into a learning loop with simulations and usage data.
Get started
Hand this prompt to your coding agent (Claude Code, Cursor, Codex):
Help me set up my agent platform.
Clone https://github.com/agno-agi/agentos-railway into a folder called
agent-platform, cd in, read the README, and follow the get started guide.
Your coding agent will set up your agent platform and run it locally using Docker, giving you a REST API for serving your agents, a Postgres database for storing your data and traces, an MCP server, and a control plane.
Deploying somewhere else? Use the same prompt but point it to a different repo. The starter templates are identical except for the deploy scripts: swap agentos-railway for agentos-docker, agentos-aws, agentos-gcp, agentos-azure, agentos-fly, agentos-render, agentos-modal, or agentos-helm.
Prefer to code by hand?
Features
- Production API. 50+ endpoints with SSE and websockets to build a product on top.
- Storage. Store sessions, memory, knowledge, and traces in your own database.
- 100+ integrations. Connect to GitHub, Slack, Postgres, and more using pre-built toolkits.
- Context Providers. Access live data from Slack, Drive, wikis, MCP, and custom sources.
- Human approval. Pause runs for user confirmation. Block tools that require admin approval.
- Observability. Monitor with OpenTelemetry tracing, run history, and audit logs.
- Security. Get JWT-based RBAC and multi-user, multi-tenant isolation out of the box.
- Interfaces. Expose your agents via Slack, Telegram, WhatsApp, Discord, AG-UI, A2A.
- Scheduling. Cron-based scheduling and background jobs with no external infrastructure.
- Deploy anywhere. Run on any cloud platform that runs containers. Docker, Railway, AWS, GCP.
Use Agno with your coding agent
Two options:
- Recommended: Add Agno docs as an MCP server. Add docs.agno.com/mcp to your favourite coding agent.
- Add Agno docs as an indexed source. In Cursor: Settings → Indexing & Docs → Add
https://docs.agno.com/llms-full.txt. Also works in VSCode, Windsurf, and similar tools.
Read the full guide here.
Community
- (https://x.com/AgnoAgi): follow for releases and demos
- Newsletter: monthly updates on what's shipping
Contributing
See the contributing guide.
License
Agno is distributed under the Apache-2.0 license.
Telemetry
Agno sends a telemetry event per agent run so we know which model providers to prioritize. Prompts, messages, and outputs are never sent. Disable by setting AGNO_TELEMETRY=false.