agno: who decides what
How to read this page
A seat is a judgment the agent makes over and over: which model a name means, what the model sees, may this action run, is the work done. Each card records the question, how it is answered, and who or what holds it, with the code that implements or exposes that decision. Seat ids (A1, B12 …) are links.
Each answer describes what agno does by default. Exceptions named are the main ones, not a complete list.
The chip on each card has two words. The first is about the question: a RULE has one right answer given its inputs, so a run that answers differently is a bug; a RULING is one that competent people could answer differently. Enforcing a limit someone set is a RULE; choosing what to keep, what to trust or when the work is done is a RULING, even when code holds it.
The second word is who holds it: llm = a language-model call; model = another learned model; code = code in their repository; human = a person; external = code outside the repository that the agent hands the decision to.
Paths are relative to the repository root. A bare :120-140 or (323) means lines in the file named just before it.
Counts
118 seats across 9 stages: A 11, B 17, C 15, D 24, E 13, F 13, G 6, H 9, I 10.
| Kind of question · holder | Seats |
|---|---|
| RULING · code | 39 |
| RULE · code | 32 |
| RULING · llm | 28 |
| RULING · external | 13 |
| RULING · human | 4 |
| RULING · model | 1 |
| RULE · external | 1 |
What this edition does not have
There is no list of gaps. No coverage figure: this edition does not say how many of agno's model calls and decision points the seats leave out. Each card shows the question, the answer and its holder; for most seats we also recorded what checks the judgment and how it has failed, but those notes are not shown here.
What is left out of this edition
3 of the 118 cards are left out of this edition. The counts above include them.
A. Intake and routing
A1 When an agent or team is built without a model, which model answers its requests? RULING · code
- Answered how
An agent or team built without a model is answered by OpenAI's
gpt-5.4through the Responses API; any model the developer passes, as an object or a"provider:id"string, wins.- Held by
libs/agno/agno/agent/_init.py:89-101set_default_model(OpenAIResponses(id="gpt-5.4")), called first ininitialize_agent(libs/agno/agno/agent/_init.py:476-477), which each run dispatch calls (libs/agno/agno/agent/_run.py:1388,:2771,:3368); team twinlibs/agno/agno/team/_init.py:580.
A2 When a run arrives without a session id or user id, which conversation and which user does it count as? RULE · code
- Answered how
A run with no session id joins the agent's own
session_id(the developer's, or else one made on the first such run and kept on the object; an id passed on a call is not kept); a run with no user id (or an empty one) takes the agent'suser_id, which defaults to None.- Held by
libs/agno/agno/agent/_session.py:46-67initialize_session: no session id -> the agent'ssession_id, else a new uuid that is then STORED on the agent instance (:57-59, "sticky"); no or empty user id -> the agent'suser_id(:64-65). Called fromlibs/agno/agno/agent/_run.py:1385(run),:2768(arun),:3362(continue),:4256.
A3 When the developer declared an input schema, does this input match it? RULE · code
- Answered how
No input schema is set by default, so input passes as given; with a schema, input that is not that model, or does not parse into it, is refused with a
ValueErrorbefore the model is called.- Held by
libs/agno/agno/agent/_run.py:1374(run;:2757inarun) callsvalidate_input(libs/agno/agno/utils/agent.py:1349-1400: a Message gives its content; a JSON string becomes a dict, then the pydantic model or TypedDict).
A4 How is the caller's input (text, a list, a dict, a Message, a pydantic object, a list of messages) turned into what the model receives? RULE · code
- Answered how
Text, or a list of strings (joined by newlines), becomes one user message (no input and no media adds none); a Message, or a list whose first item is a Message or role-tagged dict, is sent as written, roles included; a single dict is read as a Message and a pydantic object is sent as JSON text. A message failing validation is dropped with a warning.
- Held by
libs/agno/agno/agent/_messages.py:2000-2079inget_run_messages(str/None/list-of-non-messages ->get_user_message; Message used as is; dict validated as a Message unless the input schema is a TypedDict; pydantic -> JSON; a list whose first item is a Message or role-dict -> each appended as its own message), withget_user_message:1525-1692.
A5 When the call, the stored session and the agent disagree on streaming, history, dependencies, knowledge filters or metadata, which one wins? RULE · code
- Answered how
A value passed on the call beats the agent's setting; metadata merges agent, then stored session, then call (later wins), dependencies merge with the call's keys winning, and a call-level knowledge filter on the same key replaces the agent's.
- Held by
libs/agno/agno/agent/_run_options.py:79-186resolve_run_options(call > agent for flags; dependencies merged call-over-agent:147-154; metadata agent < session < call:165-170; filters vialibs/agno/agno/agent/_utils.py:23-58get_effective_filters, run filters override agent filters); applied byapply_to_contextlibs/agno/agno/agent/_run_options.py:38-76("explicit args > existing run_context > resolved defaults", exceptuser_id, where an owner already on the run_context wins,:50-54), called atlibs/agno/agno/agent/_run.py:1444-1451.
A6 Does this run stream events, run detached in the background, or return a single result? RULE · code
- Answered how
A run returns one finished result unless streaming is asked for on the call or set on the agent (
streamdefaults to None, read as off); background execution exists only forarun(background=True)and requires a database.- Held by
libs/agno/agno/agent/_run_options.py:107-126(stream: call >agent.stream> False; stream_events forced off when not streaming); background:libs/agno/agno/agent/_run.py:2868-2904(arun(background=True)only; requires a db,:2869-2872; streams through a queue when stream is also on,:2873-2890).
A8 Should the developer's pre-run hook change the input (or the session) before the model sees it? RULING · external
- Answered how
No pre-hook is installed by default, so the input reaches the model unchanged; a developer's pre-hook is called with the run input and may rewrite it before the model sees it.
- Held by
libs/agno/agno/agent/_run.py:466-484->execute_pre_hooks(libs/agno/agno/agent/_hooks.py:43-151; hooks run in order withrun_input,run_context,session,user_id,:62-72; the possibly changed input is stored back on the run,:151); hooks normalised atlibs/agno/agno/agent/_run.py:1376-1382.
A9 When a paused, finished or failed run is continued, which run is it, from which message does it restart, and is it continued in place or copied? RULE · code
- Answered how
A continued run is found by object or by id and resumes from the end of its stored transcript by default; continuing a run that already completed makes a forked copy rather than changing the original, and a cancelled run is refused.
- Held by
libs/agno/agno/agent/_run.py:3285-3500continue_run_dispatch: run by object or by id (:3340-3344,:3467-3480); boundary_resolve_continue_from:3106-3138("end" default, "last_user", index, or regenerate's own checkpoint); a completed run is always forked (:3456-3457); owner falls back to the paused run's user (:3379-3381,_resolve_continue_owner:3161-3172); reserved metadata restored from the stored row, never from the caller (:3386-3399).
A10 Does the team leader coordinate members, route the request to one member, broadcast it to all, or work from a task list? RULE · code
- Answered how
A team with no mode set runs in coordinate mode; an explicit
modewins, and otherwiserespond_directlyselects route anddelegate_to_all_membersselects broadcast (both set: broadcast, with a warning). Tasks mode is reached only by naming it.- Held by
libs/agno/agno/team/_init.py:210-231(explicitmodewins and resets the two flags to match; elserespond_directly-> route,delegate_to_all_members-> broadcast, else coordinate); conflict resolved atlibs/agno/agno/team/_init.py:868-877(both flags -> broadcast, with a warning).
A11 When a workflow step runs an agent or a team, what does that step receive as its input? RULING · code
- Answered how
From the second step on, an agent or team step receives only the previous step's output, not the user's original request; the original reaches it only when the previous step produced no content. Workflow history is off by default.
- Held by
libs/agno/agno/workflow/step.py:2722-2737_prepare_message: when earlier step outputs exist and the executor is an agent, team or workflow, the step gets ONLY the last step's deepest content and the workflow's original input is dropped; then optional workflow history is prepended as text (libs/agno/agno/workflow/step.py:1285-1297), and HITL extras appended (:1299-1317). The docstring says "combining message and previous step outputs" (:2727); the code replaces instead.
A12 When the calling front end sends its own tools with the run (AG-UI frontend tools), are they added to what the model may call? RULE · code
- Answered how
A bare agent receives no client tools; when a run context carries them (in agno only AgentOS's AG-UI interface sets them), they are offered after the developer's tools, and a client tool reusing a developer tool's name is skipped.
- Held by
libs/agno/agno/agent/_tools.py:226-228(run_context.client_toolsappended to the resolved tools).
B. Context: what the model sees
B1 Does the model get the developer's fixed system message, a framework-built one, or none at all? RULE · code
- Answered how
With no system message given, agno builds one from the agent's settings; a developer-supplied system message replaces that whole build, including memories, knowledge instructions and JSON-output instructions.
build_context=Falsesends none.- Held by
libs/agno/agno/agent/_messages.py:941-968(asystem_messagereplaces the whole build; it may be a callable,:949-954;build_context=False-> no system message,:966-968).
B2 Which of the agent's settings and which framework texts are written into the default system message? RULE · code
- Answered how
A default agent's system message holds only what the developer set (description, role, instructions, expected output, additional context) plus the model provider's own lines and any toolkit instructions; every other block is switched on by its own setting or feature.
- Held by
libs/agno/agno/agent/_messages.py:973-1210(order: description,<your_role>,<instructions>(developer then model),<additional_information>(markdown, datetime, location, name, offload), tool instructions; state-variable substitution:1076-1081; then expected output, additional context, skills, memories, session summary, learnings, knowledge instructions, the model's own system text, JSON-output prompt, session state). Team twinlibs/agno/agno/team/_messages.py(JSON block:411-421).
B3 Should {name} (and $name) placeholders in the system message and in the user's own message be replaced with values from the session state, dependencies, metadata and user id? RULE · code
- Answered how
Yes, by default: a
{key}or$keyin the instructions or in the user's own text message is replaced by the matching session-state, dependency, metadata or user-id value. A default agent's session state already holds the run's session and run ids (and its user id, when there is one).- Held by
libs/agno/agno/agent/_messages.py:838-881format_message_with_state_variables(ChainMap, first match wins: session_state, dependencies, metadata, user_id;{var}rewritten to${var}, thenstring.Template.safe_substitute); applied to the system message (:956-961,:1076-1081, only the part built before:1076) and to a plain-text USER message (:1653-1658) whenresolve_in_contextis True (default,libs/agno/agno/agent/agent.py:287); session state seeded withcurrent_user_id,current_session_id,current_run_id(libs/agno/agno/agent/_init.py:409-427, called atlibs/agno/agno/agent/_run.py:451). Team twinlibs/agno/agno/team/_messages.py:1609.
B4 Which earlier messages of this conversation are replayed to the model? RULING · code
- Answered how
None by default; with history switched on (and a database) the model is replayed the last 3 runs of this session, or the count the developer sets, skipping team-member runs, runs in skip statuses and stored system messages.
- Held by
libs/agno/agno/agent/_messages.py:1964-1998(only whenadd_history_to_context, default False,libs/agno/agno/agent/agent.py:163) ->_history_for_runlibs/agno/agno/agent/_messages.py:245-291->libs/agno/agno/session/agent.py:141-262get_messages(lastnum_history_runsruns, which the constructor sets to 3 when neither limit is given,libs/agno/agno/agent/agent.py:601-602; skips member runs:195, runs in skip statuses:183-198, messages already tagged as history:171-172, and the system-role messages whensystem_message_roleis not user/assistant/tool,libs/agno/agno/agent/_messages.py:1969-1971); tool calls trimmed bymax_tool_calls_from_history(:1979-1980).
B5 Which remembered facts about this user are written into the system message? RULING · code
- Answered how
Memory is off by default, so nothing is added. Once on, every memory stored for the run's user goes into every system message, with no cap or relevance filter; runs with no user id all read the one shared
"default"user's memories.- Held by
libs/agno/agno/agent/_messages.py:1095-1133(whenadd_memories_to_context, whichset_memory_managerturns on whenever memory is on,libs/agno/agno/agent/_init.py:116-119, called whenupdate_memory_on_run,enable_agentic_memoryor amemory_manageris set,:484-485): every memoryget_user_memoriesreturns,libs/agno/agno/memory/manager.py:174-186; a run with no user id reads user"default"(libs/agno/agno/agent/_messages.py:1097-1098,libs/agno/agno/memory/manager.py:177-178).
B6 Is the stored summary of earlier turns added to the system message? RULE · code
- Answered how
Off by default. With session summaries on, the session's stored summary, when one exists, is added to every system message; with compaction also on, the model reads two summaries of the same history, which agno warns about.
- Held by
libs/agno/agno/agent/_messages.py:1135-1144(whenadd_session_summary_to_context, auto-on withenable_session_summariesor asession_summary_manager,libs/agno/agno/agent/_init.py:180-183, and the session has a summary).
B7 Which saved learnings (user profile, user observations, learned knowledge) are recalled into the system message for this message? RULING · code
- Answered how
Learning is off by default. With
learning=Trueand a database, the user-profile and user-memory stores are recalled for the run's user and written into the system message with their usage guidance; without a database learning stays off, with a warning.- Held by
libs/agno/agno/agent/_messages.py:1146-1164(when a LearningMachine was set up,libs/agno/agno/agent/_init.py:122-169, andadd_learnings_to_context, default True) ->libs/agno/agno/learn/machine.py:493-534build_context->recall:901-945(each enabled store'srecall, given user, session, agent and the message text from_learning_message_textlibs/agno/agno/agent/_messages.py:889-914).
B8 When the agent has a knowledge base, what does the system message tell the model about searching it? RULE · code
- Answered how
Only when a knowledge base is attached: by default the system message tells the model to search it with
search_knowledge_basebefore answering; with agentic filters on, it also lists the filter keys found in stored content for this user.- Held by
libs/agno/agno/agent/_messages.py:1166-1178->libs/agno/agno/knowledge/knowledge.py:5321-5347build_context(search instructions; plus the valid filter keys, read from stored content scoped to the user, when agentic filters are on).
B9 Which documents does the knowledge base return, and how many, for the query the model (or the user's message) supplies? RULING · model
- Answered how
Up to 10 documents from the knowledge base's search (after any configured reranking), narrowed by any filters and scoped to the run's user id when there is one; with no user id, every owner's documents are searched. A failing search looks like an empty one (a
ValueErroris raised instead).- Held by
libs/agno/agno/agent/_messages.py:2523-2643get_relevant_docs_from_knowledge->Knowledge.retrieve(libs/agno/agno/knowledge/knowledge.py:5771-5802) ->Knowledge.search:1187-1258(vector db search with the embedder,max_resultsdefault 10:93; optional query transformer:1236-1240and reranker:1248; owner scopeuser_id, "None searches everything":1199).
B10 Should the framework retrieve documents up front and append them to the user's message (classic RAG)? RULE · code
- Answered how
Off by default; the model searches through a tool instead (B8, B9). When switched on, documents are retrieved for the user's raw text, before placeholders are filled, and appended to that message under
<references>.- Held by
libs/agno/agno/agent/_messages.py:1619-1651(retrieve on the raw user text) and:1663-1673(<references>appended), whenadd_knowledge_to_context(default False,agent.py:178).
B11 When agentic filters are on, which metadata filters does the model put on its knowledge search? RULING · llm
- Answered how
Off by default (
enable_agentic_knowledge_filters=False). When on, the model may pass filters tosearch_knowledge_base; they are used only when the run carries no filters of its own (agent or call), and unknown keys are dropped with a warning.- Held by
libs/agno/agno/agent/_default_tools.py:128-196search_knowledge_base(query, filters), filled by the agent's model (A1); filters merged bylibs/agno/agno/utils/knowledge.py:7-36(the run's own filters win outright when both exist,:26-28; a list-type run filter with model filters raises,:29-33), validated against known keys withvalidate_filters=True(libs/agno/agno/agent/_messages.py:2559-2581, invalid keys dropped with a warning).
B12 When the developer supplies their own retrieval function, what does it return for this query? RULING · external
- Answered how
No custom retriever by default. When one is set it is called first and fully replaces agno's own search, and the model is given the search tool even if no knowledge base is attached.
- Held by
libs/agno/agno/agent/_messages.py:2583-2609(agent.knowledge_retrievercalled first, with agent, filters, run_context, query, num_documents, user id).
B13 Which skills (name, description, scripts, references) does the system message list? RULE · code
- Answered how
No skills by default. When given, every loaded skill's name, description, script names and reference names are listed in every system message, with no selection; of two skills sharing a name, the last loaded wins.
- Held by
libs/agno/agno/agent/_messages.py:1089-1093->libs/agno/agno/skills/agent_skills.py:90-148get_system_prompt_snippet(every loaded skill); loadinglibs/agno/agno/skills/agent_skills.py:34-52, run once whenSkillsis constructed (:29-32), where a later skill with the same name replaces the earlier one (:44-46).
B14 Which tools (developer tools, framework tools, client tools) are offered to the model on this run, and which copy wins when two share a name? RULE · code
- Answered how
A default agent offers exactly the developer's tools; framework tools join only when their feature is on, except knowledge search, which joins once a knowledge base or retriever is attached. On a name clash the first registered (the developer's) wins, with a warning.
- Held by
libs/agno/agno/agent/_tools.py:202-323get_tools(developer tools first, then client tools, then framework tools by setting: compaction search, filesystem, chat history, tool-call history, past sessions, agentic memory, offload read-back, learning, session-state update, knowledge search:304-314, add-to-knowledge, skills) ->parse_tools:473-649(first registration of a name wins; later duplicates skipped with a warning:541-546,:574-578,:611-615; a callable that fails to parse is dropped with a warning:646-647).
B15 When the model calls a context provider's query_<id> tool (web, Slack, Gmail, Drive, wiki, database, MCP, workspace), what answer comes back? RULING · llm
- Answered how
Absent by default. Only when the developer adds a provider's tools does the model get a
query_<id>tool, answered by that provider's sub-agent (run with the caller's user, session, metadata and dependencies) or, without one, by the provider's own query code.- Held by
libs/agno/agno/context/provider.py:300-338_query_tool/_query_chunks-> the provider's sub-agent (_aget_query_agent,:214-216, overridden per provider; run with the caller's user_id, session_id, metadata and dependencies,:222-239,:246-257), or the provider's ownaquerywhen it has no sub-agent (:321-327). Wired only when the developer passesprovider.get_tools()(:203-208) as tools; intoolsmode that returns the provider's underlying tools instead of a query tool (:206-207).
B16 What does the team leader's system message say about each member and about how to delegate? RULE · code
- Answered how
The leader sees each member's name, role and description (with an id for direct members only) plus mode-specific delegation rules; in the default coordinate mode it is told members see only the text it writes for them. Member tools are listed only with
add_member_tools_to_context, off by default.- Held by
libs/agno/agno/team/_messages.py:287-319_build_team_context-> roster:99-146(id for top-level members only, name, role, description; tool names only withadd_member_tools_to_context, default False,libs/agno/agno/team/team.py:187) and_get_mode_instructionslibs/agno/agno/team/_messages.py:171-284(per mode; what members see,:250-268).
B17 When the leader delegates, does the member get only the leader's task text, the user's raw message, the team's history, or other members' exchanges? RULE · code
- Answered how
By default a member receives only the task text the leader writes for it, not the conversation. Separate settings send the user's raw message instead, the team's history, or other members' exchanges.
- Held by
libs/agno/agno/team/_default_tools.py:529-566in_setup_delegate_task_to_member: task = the leader's text unlessdetermine_input_for_membersis False, then the raw input (:546-550); team history added whenadd_team_history_to_members(:537-544); other members' exchanges and their media whenshare_member_interactions(libs/agno/agno/team/_tools.py:549-568); the member's own history when the member hasadd_history_to_context(libs/agno/agno/team/_default_tools.py:559-565). Defaultslibs/agno/agno/team/team.py:122(True),:145,:149(False); the leader is told the same atlibs/agno/agno/team/_messages.py:250-268.
C. Choosing the next action
C1 Given the conversation and the tools it can see, does the agent answer now, or call which tools with which arguments? RULING · llm
- Answered how
At every turn the agent's model decides whether to answer or call tools; agno sends every registered tool and lets the model choose unless the integrator sets
tool_choice.- Held by
the agent's
model(integrator's choice), called per turn atlibs/agno/agno/models/base.py:718(_process_model_response, which calls_invoke_with_retryatlibs/agno/agno/models/base.py:1125, then the providerinvoke); tool calls read back atlibs/agno/agno/models/base.py:742-749.
C2 When the model's tool-call arguments arrive as text, what object do they mean (including malformed or string-typed values)? RULING · code
- Answered how
agno decodes the arguments as JSON (falling back to Python-literal syntax), returns an error to the model for anything that is not an object (an empty list means no arguments), and turns top-level string values 'none', 'null', 'true' and 'false' into null and booleans.
- Held by
libs/agno/agno/models/base.py:2054-2090get_function_calls_to_run(called by_prepare_function_calls,:3119): each tool call goes throughget_function_call_for_tool_call(libs/agno/agno/utils/tools.py:8-24, missing arguments default to"{}") andget_function_call(libs/agno/agno/utils/functions.py:10-75:json.loads, thenast.literal_eval; an empty list means no arguments; any other non-object is refused; top-levelnone/null/true/falsestrings converted); a decode error is sent back as the tool's result and the call is skipped (libs/agno/agno/models/base.py:2079-2088).
C3 When a tool call streams in pieces, which pieces belong to which call? RULE · code
- Answered how
For streams from the
OpenAIChatmodel class (not the defaultOpenAIResponsesmodel), agno stitches tool-call fragments together by their index; a fragment with no index is put on call 0, so parallel calls from a provider that omits it merge into one (#11148).- Held by
libs/agno/agno/models/openai/chat.py:750-787(OpenAIChat.parse_tool_calls): pieces grouped byindex, a missing index read as 0 (:762), argument fragments concatenated (:781-782). Each provider adapter has its own assembler (e.g.libs/agno/agno/models/cerebras/cerebras.py:587filters incomplete calls); only OpenAIChat was read.
C4 Do the model's arguments fit the tool's declared parameter types, and if not exactly, may they be coerced? RULING · code
- Answered how
agno checks the arguments against the tool function's type hints with pydantic in lax mode, so near-misses such as the text '5' for an integer are coerced; a call that still does not fit is not run and the model gets the error. A tool with a parameter named, or typed as,
agentorteamis not checked at all.- Held by
pydantic
validate_callaround the tool's callable (libs/agno/agno/tools/function.py:1746-1822, lax mode,arbitrary_types_allowed), applied via_wrap_callable(libs/agno/agno/tools/function.py:1723) fromprocess_entrypoint(libs/agno/agno/tools/function.py:1718) and_build_from_callable(libs/agno/agno/tools/function.py:1588). Skipped for coroutines on pydantic < 2.10 (libs/agno/agno/tools/function.py:1774-1779), for any callable with a parameter NAMEDagentorteam(:1785-1791) and for any parameter typed to reach Agent/Team (:1793-1818); each of these skips leaves the WHOLE callable unvalidated. A callable already wrapped keeps its existing validation wrapper (:1781-1783).
C5 Should the provider be made to enforce each tool's schema strictly at sampling time? RULING · code
- Answered how
Off by default: tool schemas become strict only when the run has an
output_schema, noparser_model, and a model with native structured outputs, or when a tool author setsstricton that tool.- Held by
libs/agno/agno/agent/_tools.py:517-525(strict whenoutput_schemais set, noparser_model,structured_outputstrue oruse_json_modefalse, and the model supports native structured outputs); a Toolkit function's or Function's ownstrictwins (libs/agno/agno/agent/_tools.py:552-556,libs/agno/agno/agent/_tools.py:582-590), a plain callable takes the run-wide value (libs/agno/agno/agent/_tools.py:620,:640-641); schema rewrite atlibs/agno/agno/tools/function.py:1824-1880.
C6 After this turn, is the run over, or does the agent take another turn? RULING · llm
- Answered how
The model decides: agno keeps calling it while it asks for tools and stops when a reply has none, with no turn limit by default.
tool_call_limitrefuses calls past the limit but never ends the loop itself.- Held by
the model, by emitting tool calls or not: the loop continues after every batch of tool results (
libs/agno/agno/models/base.py:870-871) and ends when a reply has no tool calls (libs/agno/agno/models/base.py:873-874). Code overrides end it early: a result flagged stop-after (libs/agno/agno/models/base.py:838-840, C7/C8), a tool needing confirmation, external execution or user input (libs/agno/agno/models/base.py:851-861), an unresolved requirement on the run (libs/agno/agno/models/base.py:866-868). Same structure inaresponse(libs/agno/agno/models/base.py:884, stop check:1062),response_stream(libs/agno/agno/models/base.py:1367,:1570),aresponse_stream(libs/agno/agno/models/base.py:1648,:1851).
C7 Should the run end right after this tool's result, with no further model turn (and is the tool's output the answer)? RULING · code
- Answered how
No tool ends the run by default. A tool author can set
stop_after_tool_callso the loop ends after the batch containing that tool, andshow_resultso the tool's output becomes part of the answer text.- Held by
the tool's
stop_after_tool_callflag (default False,libs/agno/agno/tools/function.py:1259), copied onto the result (libs/agno/agno/models/base.py:2199) and read by the loop (:838-840); the tool'sshow_resultflag (default False,libs/agno/agno/tools/function.py:1257) makes the tool's own output part of the answer text (libs/agno/agno/models/base.py:2357-2358, accumulated at:809-814).
C8 May a tool (or a tool hook) redirect the run mid-flight: stop it, or inject new messages and continue? RULING · external
- Answered how
Only when the integrator's own tool or hook raises
StopAgentRunorRetryAgentRun; agno then adds that exception's messages after the tool results and either ends the loop (Stop) or lets the model continue (Retry). A tool flagged stop-after also ends the run, without raising (C7).- Held by
libs/agno/agno/models/base.py:2256-2273(run_function_callcatches the integrator'sStopAgentRun/RetryAgentRun,libs/agno/agno/exceptions.py:64-93, re-raised out of the call atlibs/agno/agno/tools/function.py:2706-2723), turns itsuser_message/agent_message/messagesinto extra messages (libs/agno/agno/models/base.py:98-131) placed after the tool results (:2577-2579), and withstop_executionends the loop (:2389).
C9 Before the main model answers, what does a separate reasoning model think about the task? RULING · llm
- Answered how
Off by default. When
reasoning_modelis set to a model agno recognises as a native reasoning model, that model reads the run's input first and its thinking is appended to what the main model sees.- Held by
ReasoningManager.reason(libs/agno/agno/reasoning/manager.py:859-904), fromhandle_reasoning(libs/agno/agno/agent/_response.py:75-88) at run step 8 (libs/agno/agno/agent/_run.py:546); the reasoning model runs as a fresh innerAgent(libs/agno/agno/reasoning/helpers.py:7-24) unlessreasoning_agentis given (libs/agno/agno/reasoning/manager.py:178-181); per-provider calls e.g.libs/agno/agno/reasoning/openai.py:43-79.
C10 Is the configured reasoning model a "native reasoning model" agno will use? RULE · code
- Answered how
agno decides from the model's class name and substrings of its id (e.g.
o3,gpt-5,deepseek-r1); a model it does not recognise gets a warning and no reasoning step. Only reached whenreasoning_modelis set.- Held by
libs/agno/agno/reasoning/manager.py:143-176dispatches to per-provider checkers, e.g.libs/agno/agno/reasoning/openai.py:14-40: class name in a fixed list AND the id CONTAINSo4,o3,o1,gpt-5orgpt-6(substring), or an OpenAI-compatible id containingdeepseek-r1,minimax-m2, etc.
C11 In what format is the model asked to return a structured answer? RULING · code
- Answered how
Absent by default. With an
output_schema, a model class marked for native structured outputs gets the schema itself; other models get a JSON-schema wrapper or a plain 'JSON object' request, and the text is parsed afterwards (C12).- Held by
libs/agno/agno/agent/_response.py:857-896(get_response_format): the schema itself for models with native structured outputs unlessuse_json_mode(then{"type":"json_object"}); for models markedsupports_json_schema_outputs, ajson_schemawrapper unlessstructured_outputsis truthy anduse_json_modefalse, which sends NO format (libs/agno/agno/agent/_response.py:891-892); otherwise{"type":"json_object"}. No response format when aparser_modelis set (libs/agno/agno/agent/_run.py:1454).
C12 Does the model's final text become the structured object the caller asked for, and how hard does agno try? RULING · code
- Answered how
Absent by default. With an
output_schema, agno parses the final text into it, repairing and even merging JSON fragments (a dict schema is only JSON-decoded, never checked against the schema); if every attempt fails it keeps the raw text and the run still reports completed.- Held by
libs/agno/agno/agent/_response.py:904-938(convert_response_to_structured_format, run step 12 atlibs/agno/agno/agent/_run.py:621) with the salvage parserlibs/agno/agno/utils/string.py:195-249: (1) strict JSON, (2) after_clean_json_content(:117-152: strips code fences, unwraps markdown around keys, deletes control characters, re-escapes quotes in values), (3) the single extracted JSON object, (4) a MERGE of every JSON object found, field by field, lists concatenated and the last scalar winning (:155-192). A dict schema is only JSON-parsed, never checked against the schema (libs/agno/agno/agent/_response.py:913-921).
C13 Given the main model's free-text answer, what structured object does a second "parser" model say it means? RULING · llm
- Answered how
Absent by default. When an integrator sets both
parser_modelandoutput_schema, a second model turns the main answer into the structured object and its reply replaces the main answer.- Held by
agent.parser_model, called atlibs/agno/agno/agent/_response.py:349-395after the main loop (libs/agno/agno/agent/_run.py:585-587).
C14 Should a different model rewrite the final answer, and what does it say? RULING · llm
- Answered how
Absent by default. When
output_modelis set, a second model writes the final answer from the same conversation, and its text replaces the main model's answer unchecked.- Held by
agent.output_model, called atlibs/agno/agno/agent/_response.py:582-605(libs/agno/agno/agent/_run.py:582).
C15 What follow-up prompts should be suggested to the user after the answer? RULING · llm
- Answered how
Off by default (
followups=False). When enabled and the answer has content, a model (the followups model, else the agent's own) suggests follow-up prompts; a failure leaves them empty and never changes the answer.- Held by
generate_followups(libs/agno/agno/agent/_response.py:1885-1892), run step 12b (libs/agno/agno/agent/_run.py:624); model chosen atlibs/agno/agno/agent/followup.py:146-147(theFollowupConfigmodel, elsefollowup_model, else the agent's own model); messages_build_followup_messages(libs/agno/agno/agent/_response.py:1737-1796), parse_parse_followups_response(libs/agno/agno/agent/_response.py:1799-1824).
D. Gatekeeping: may this action run?
D1 May a tool call the model issued run, without a person being asked? RULING · code
- Answered how
Code runs every tool call the model makes without asking anyone; it pauses for a person only on tools the integrator marked
requires_confirmation,requires_user_inputorexternal_execution, or on the ask-the-person tools when the integrator adds them (D7).- Held by
libs/agno/agno/models/base.py:2431-2575(syncrun_function_calls: a call pauses only when itsFunctionhasrequires_confirmation:2450,requires_user_input:2463,external_execution:2550, or is theget_user_input/ask_usertool :2484, :2521; otherwiserun_function_call:2570) andlibs/agno/agno/models/base.py:2659-2823(async, same flags :2662, :2674, :2694, :2736, :2770; unflagged calls run in parallel viaasyncio.gather:2821).
D2 Approve or reject this paused tool call? RULING · human
- Answered how
Absent by default. When a tool is marked for confirmation the run pauses and returns; the integrator's code collects the person's yes or no and calls
continue_run, and a missing answer counts as a no.- Held by
libs/agno/agno/run/requirement.py:109-123(RunRequirement.confirm()/.reject(), set by the person behind the integrator's code that callscontinue_run). Applied atlibs/agno/agno/agent/_tools.py:1084-1095(sync; streamlibs/agno/agno/agent/_tools.py:1136-1148; async from :1178): confirmed and no result yet →run_tool; anything else →reject_tool_call(libs/agno/agno/agent/_tools.py:961-971, error text "Function call was rejected by the user"). Team: a member's pause is copied to the team run (libs/agno/agno/team/_tools.py:645-672) and decided atlibs/agno/agno/team/_run.py:5412,5472,5545,5626(same branch, copied).
D3 If nobody answers a paused run, does anything proceed without a person? RULE · code
- Answered how
Nobody: a paused agent run is stored and returned, then waits until some caller continues it; nothing in the library answers for the person or expires the pause.
- Held by
libs/agno/agno/agent/_run.py:236-257(handle_agent_run_paused: statuspaused, stored, returned);libs/agno/agno/run/requirement.py:13-187(aRunRequirementhas no deadline field).
D4 When one tool call in a turn needs a person, do the other calls in the same turn still run? RULING · code
- Answered how
Yes. In the turn where one flagged call pauses, the model's other unflagged calls still run before the person answers: one after another in a sync run, in parallel in an async run.
- Held by
libs/agno/agno/models/base.py:2562-2575(sync: a paused callcontinues and the loop runs the next call) andlibs/agno/agno/models/base.py:2802-2823(async: every unflagged call in the batch runs in parallel).
D5 Has this approval-required tool's approval record been resolved before the run continues? RULE · code
- Answered how
Absent by default (needs
@approvaland a database). An agent continue by id that passes no answers is refused until every approval record is resolved; one that passes answers skips this check. A team continue refuses a pending approval only when answers are passed.- Held by
libs/agno/agno/run/approval.py:805-835(check_and_apply_approval_resolution; async :838-861), called fromlibs/agno/agno/agent/_run.py:3538-3552(sync; asynclibs/agno/agno/agent/_run.py:4866-4884,:5385-5403) andlibs/agno/agno/team/_run.py:7643-7666(asynclibs/agno/agno/team/_run.py:9460-9477,:9943-9960). Record created at pause:libs/agno/agno/run/approval.py:219-279, called fromlibs/agno/agno/agent/_run.py:248andlibs/agno/agno/team/_hooks.py:100-217. Each tool paired with its own record:libs/agno/agno/run/approval.py:570-631.
D6 What values does the person give a user-input tool, and does the tool then run with them? RULING · human
- Answered how
Absent by default. When a tool is marked
requires_user_input, the run pauses for the listed fields; on continue the person's values (through the integrator's code) are copied into the tool's arguments and the tool runs.- Held by
libs/agno/agno/run/requirement.py:125-144(RunRequirement.provide_user_input, called by the person behind thecontinue_runcaller). Applied atlibs/agno/agno/agent/_tools.py:1114-1121(copies each field's value into the tool arguments viahandle_user_input_update,libs/agno/agno/agent/_tools.py:767-771, then runs the tool).
D7 Should the agent stop and ask the person? RULING · llm
- Answered how
Absent by default (needs
UserControlFlowToolsorUserFeedbackTools). With either toolkit on, the model decides when to stop and ask, and the run pauses for the person.- Held by
libs/agno/agno/models/base.py:2484-2518(the agent's model,Agent.model, callsget_user_input,libs/agno/agno/tools/user_control_flow.py:35, orask_user,libs/agno/agno/tools/user_feedback.py:47; intercepted BY TOOL NAME here and atlibs/agno/agno/models/base.py:2521-2547; asynclibs/agno/agno/models/base.py:2694-2768).
D8 What does the person answer to the agent's questions? RULING · human
- Answered how
Absent by default. When the model asks, the person's answers (through the integrator's code) go back to the model as the tool's result and the run continues; unanswered fields arrive as empty values.
- Held by
libs/agno/agno/run/requirement.py:125-171(RunRequirement.provide_user_input/provide_user_feedback, called by the person behind thecontinue_runcaller). Applied atlibs/agno/agno/agent/_tools.py:1102-1112(answers sent to the model as the tool result,libs/agno/agno/agent/_tools.py:774-822).
D9 Who runs this tool, and what result comes back to the model? RULING · external
- Answered how
Absent by default. A tool marked
external_executionnever runs inside agno: the run pauses, the integrator runs it, and whatever result they pass back is what the model sees.- Held by
libs/agno/agno/models/base.py:2550-2560(pause for aFunctionwithexternal_execution=True; the integrator's code outside agno runs it); result accepted atlibs/agno/agno/agent/_tools.py:732-764(handle_external_execution_update).
D10 Does this input look like a prompt-injection attempt? RULING · code
- Answered how
Absent by default (add it to
pre_hooks). When on, it refuses a run whose user input contains any of 18 fixed phrases such as "ignore previous instructions"; tool results, retrieved documents and history are not checked.- Held by
libs/agno/agno/guardrails/prompt_injection.py:16-52(PromptInjectionGuardrail.check: a lowercased substring match against 18 phrases, :17-36), run as a pre-hook atlibs/agno/agno/agent/_hooks.py:98-151(normalized to a boundcheckatlibs/agno/agno/utils/hooks.py:90-94).
D11 Does this input contain personal data, and is it masked or refused? RULING · code
- Answered how
Absent by default. When on, it refuses user input matching US SSN, card, email or phone patterns, or with
mask_piistars the matches out; only the user's input text is checked.- Held by
libs/agno/agno/guardrails/pii.py:23-78(PIIDetectionGuardrail: four regexes :35-42 plus custom :44-54;mask_piirewrites the input :64-72, else raises :74-78), run as a pre-hook (libs/agno/agno/agent/_hooks.py:98-151).
D12 Does this input violate the moderation policy? RULING · external
- Answered how
Absent by default. When on, OpenAI's moderation service decides whether the user's input (text, plus images) is refused; if the call fails for any reason, the input goes through unchecked.
- Held by
libs/agno/agno/guardrails/openai.py:54-98(OpenAIModerationGuardrail.checksends the input to OpenAI's moderation endpoint, modelomni-moderation-latestby default :28; call :73, async :119; decision mapped :77-98).
D13 May this run's input proceed, per the integrator's own pre-hook or guardrail? RULING · external
- Answered how
Absent by default. An integrator's pre-hook can refuse a run's input by raising agno's input-check or output-check error; any other failure inside the hook is logged and the run goes on.
- Held by
libs/agno/agno/agent/_hooks.py:43-151(runs the integrator callables inAgent.pre_hooks, anyBaseGuardrailor function; asynclibs/agno/agno/agent/_hooks.py:154-259), called atlibs/agno/agno/agent/_run.py:466-484before tools and messages are built (:486-519). Team: normalized atlibs/agno/agno/team/_run.py:1969and run by its own hook runner.
D14 May this run's output be returned, per a post-hook check? RULING · external
- Answered how
Absent by default. A post-hook can raise agno's output-check error, which marks the run as an error; the generated text is replaced by the error only when it was empty, and a streamed run has already sent it.
- Held by
libs/agno/agno/agent/_hooks.py:262-360(runs the integrator callables inAgent.post_hooks/Team.post_hooks; asynclibs/agno/agno/agent/_hooks.py:363-461), called atlibs/agno/agno/agent/_run.py:628-639(stream :1103-1114).
D16 May this tool call proceed, and with what arguments, per the tool hooks? RULING · external
- Answered how
Absent by default. Tool hooks wrap every tool call and can refuse it, change its arguments or replace its result (an Agent's
tool_hooksreplace a tool's own, #10754); under synchronousrun(), hooks writtenasyncare skipped with a warning.- Held by
libs/agno/agno/tools/function.py:2508-2537(sync chain over the integrator middleware inAgent.tool_hooks,libs/agno/agno/agent/agent.py:208, orFunction.tool_hooks; async chainlibs/agno/agno/tools/function.py:2833-2865); a hook refuses by not callingnext_func.
D17 May this permission-gated operation run for this caller? RULE · external
- Answered how
Absent by default. Without a trusted
RunAuthorizationset by the application (AgentOS sets one), there is no authority to check against, and permission-gated tools run unchecked (a user id on the run is not authority).- Held by
libs/agno/agno/run/authorization.py:46-50(RunAuthorization.requireasks anAuthorizationProviderfromagno.os.authz.provider, TYPE_CHECKING import at :19-20), applied byrequires_permission(libs/agno/agno/run/authorization.py:117-151) and activated per tool by_tool_authorization(libs/agno/agno/run/authorization.py:154-167) fromlibs/agno/agno/tools/function.py:2598-2609.
D18 May the model set an argument the framework injects (identity, run context, media)? RULE · code
- Answered how
No, for parameters agno fills itself (agent, team, run context, media, identity): the model's value is dropped before any hook or the tool sees it, unless the tool's own schema declares that name; identity names are dropped regardless.
- Held by
libs/agno/agno/tools/function.py:2620-2623(call of_drop_injected_overrides, before any hook runs; bodylibs/agno/agno/tools/function.py:2384-2436).
D19 May this workflow step run (person's confirmation)? RULING · human
- Answered how
Absent by default. A step with
HumanReview(requires_confirmation=True)pauses until a person answers; a no skips that step and the workflow continues unlesson_rejectsays otherwise (cancel, else-branch or retry).- Held by
libs/agno/agno/workflow/utils/hitl.py:73-126(step_pause_statusdecides the pause fromHumanReview,libs/agno/agno/workflow/types.py:64-106); the person behindWorkflow.continue_runanswers (libs/agno/agno/workflow/workflow.py:6670-6672), and a rejected step follows itson_reject(libs/agno/agno/workflow/workflow.py:6719-6731).
D21 Does this workflow step's output need a person's review? RULING · external
- Answered how
Absent by default. An integrator can make output review conditional with a function of the step's output; if that function raises, the output passes without review (#11284).
- Held by
libs/agno/agno/workflow/utils/hitl.py:299-317(evaluates the integrator predicateHumanReview.requires_output_reviewwhen it is callable,libs/agno/agno/workflow/types.py:94).
D22 May the agent read, write or run in this path of the workspace? RULING · code
- Answered how
Absent by default (the
Workspacetoolkit is opt-in). When used, it refuses paths outside its root (default: the process's working directory) and paths matching a default credential-and-noise list, and asks before any write, edit, move, delete or shell command. An approved shell command starts in the root but is not confined to it.- Held by
libs/agno/agno/tools/workspace.py:597-620(Workspace._resolve: root-escape check :608-611, exclude patterns :612-619); default partitions (reads auto, writes and shell confirm) at :313-319, :416-446.
D23 May PythonTools touch this file path? RULE · code
- Answered how
Absent by default. PythonTools keeps its file helpers inside a base folder (default: the working directory), but the code it runs can do anything the process can, and it asks nobody unless the integrator adds confirmation.
- Held by
libs/agno/agno/tools/python.py:103-105(_check_pathagainstbase_dirwhenrestrict_to_base_dir=True, defaultlibs/agno/agno/tools/python.py:50; same check atlibs/agno/agno/tools/python.py:139-141andlibs/agno/agno/tools/python.py:164-167).
D24 Is this file-system path one the agent may address? RULE · code
- Answered how
Absent by default. agno's file-system store refuses paths with
.or..segments, control characters, backslashes or a trailing slash (a leading slash is stripped), so a path cannot climb out of its namespace.- Held by
libs/agno/agno/fs/_paths.py:39-57(control characters, backslashes, trailing slashes,./..segments refused) and the local backend root atlibs/agno/agno/fs/local.py:28.
E. Execution and observation
E1 Do the tool calls in one model reply run one at a time, or all at once? RULING · code
- Answered how
With
run()agno runs one reply's tool calls one after another; witharun()it runs them all at once, sync tools on worker threads. No setting chooses this; the entry point does.- Held by
chosen by the entry point, not by a setting: sync
run()executes them in order (libs/agno/agno/models/base.py:2431-2575,run_function_calls); asyncarun()runs every non-paused call concurrently withasyncio.gather(..., return_exceptions=True)(libs/agno/agno/models/base.py:2802-2823,arun_function_calls), sync callables on worker threads (libs/agno/agno/models/base.py:2608-2610).
E2 When a tool fails, what does the model see, and does the run go on? RULING · code
- Answered how
The error text goes back to the model as the tool's result and the model decides what next; the run does not fail. Toolkits that return 'Error: ...' strings instead of raising are recorded as successful calls.
- Held by
FunctionCall._execute(libs/agno/agno/tools/function.py:2713-2717): any exception becomes a failed result carryingstr(e); the loop sends it to the model as a tool message flaggedtool_call_error=True(libs/agno/agno/models/base.py:2192-2199); a generator tool that fails mid-iteration is marked failed and the model gets the error text, not the partial output (libs/agno/agno/models/base.py:2322-2327, asynclibs/agno/agno/models/base.py:2950-2959,:3013-3018; content chosen atlibs/agno/agno/models/base.py:2194). Control exceptions are C8; cancellation re-raises (libs/agno/agno/tools/function.py:2711-2712).
E3 When the model provider returns an error, should the same call be retried? RULING · code
- Answered how
agno does not retry a failed model call by default (
retries=0); apart from one retry with guidance for a malformed Gemini reply (E6), any retry comes from the provider SDK's own client default where the adapter leavesmax_retriesunset (the defaultOpenAIResponsesmodel andOpenAIChatamong them).- Held by
Model._invoke_with_retryand its three siblings (libs/agno/agno/models/base.py:226-272,:274-321,:323-373,:375-427) with_is_retryable_error(:199-224: never 400/401/403/404/413/422, never a context-window error by class or message pattern; everything else retryable).retriesdefault 0 (:178), fixed 1s delay, optional doubling (:180-197). Beneath it, the vendor SDK's own client retry applies wherever the agno adapter leavesmax_retriesunset (e.g.libs/agno/agno/models/openai/chat.py:83, dropped when None at:121-127).
E4 When a whole run fails, should agno start the run over from the beginning? RULING · code
- Answered how
No: by default a failed run is not restarted (
retries=0). Withretriesset, any exception other than cancellation or a guardrail check restarts the whole run, including tool calls that already succeeded.- Held by
libs/agno/agno/agent/_run.py:426-437and:727-737(Agent.retries, default 0,libs/agno/agno/agent/agent.py:308): any non-cancel, non-guardrail exception restarts the run at step 1 after a delay; same pattern atlibs/agno/agno/agent/_run.py:834,:1571,:2179and the continue paths:3697,:3915,:4724,:5245.
E5 When the main model fails, should a different model take over (or should the request first be shrunk)? RULING · code
- Answered how
No fallback model is configured by default, so a model error that survives the model's own retries (E3, E6) fails the run. One shrink-and-retry of the same model after a "context too long" error happens only with
compaction=True(or aCompactionset toon_context_overflow=True, F7).- Held by
call_model_with_fallback(libs/agno/agno/models/fallback.py:203-242, async and stream siblings fromlibs/agno/agno/models/fallback.py:245) andget_fallback_models(libs/agno/agno/models/fallback.py:121-156): rate-limit errors go toon_rate_limit, context-window errors toon_context_overflow, other errors toon_errorexcept 4xx client errors other than 429/529 (libs/agno/agno/models/fallback.py:143-155). Before any fallback, a context-window error triggers one 'shrink and retry the same model' attempt (libs/agno/agno/models/fallback.py:219-227) through_recompact_after_overflow(libs/agno/agno/agent/_messages.py:608-644, passed atlibs/agno/agno/agent/_run.py:557-559).
E6 When Gemini emits a malformed function call, should agno ask it again with a corrective hint? RULING · code
- Answered how
Only for Gemini models: a reply ending with finish reason MALFORMED_FUNCTION_CALL is retried once with a fixed corrective user message (
retry_with_guidancedefault True), then the call fails with an error.- Held by
libs/agno/agno/models/google/gemini.py:1242-1249raisesRetryableModelProviderErroron finish reasonMALFORMED_FUNCTION_CALLwhenretry_with_guidance(default True,libs/agno/agno/models/base.py:185); the stream parser does the same (libs/agno/agno/models/google/gemini.py:1410);libs/agno/agno/models/base.py:255-269appendsMALFORMED_FUNCTION_CALL_GUIDANCE(libs/agno/agno/models/google/utils.py:132) as a temporary user message and retries, at mostretry_with_guidance_limit= 1 time (libs/agno/agno/models/base.py:187).
E7 In what form does a tool's result reach the model? RULING · code
- Answered how
agno turns whatever a tool returns into text with
str()(a dict reaches the model as its Python repr) and sends all of it, with no size limit by default; media a tool produces goes to the model in a follow-up user message.- Held by
libs/agno/agno/models/base.py:2281-2358(sync) and:2977-3058(async): a plain return value is converted withstr()(:2355,:3055), so a dict or list reaches the model as its Python repr, not JSON; aToolResultgives itscontentplus media (:2340-2353); a generator's items are concatenated, with nested agent/team/workflow run events contributing only their content (:2281-2336). Media is moved out of the tool message into a follow-up USER message "The tool call above generated the attached media." whensend_media_to_model(default True,libs/agno/agno/agent/agent.py:236) (libs/agno/agno/models/base.py:3152-3197).
E9 May a stored earlier result answer this call instead of running it (tool cache, model-reply cache)? RULING · code
- Answered how
Off by default for both: tool results are cached only for tools with
cache_results=True, model replies only withcache_response=True. A cached tool's key includes user and session only when the tool takes a run context.- Held by
tool cache:
libs/agno/agno/tools/function.py:2628-2694(key from tool name, arguments and the run context's identity,:1915-1942; files undergettempdir()/agno_cacheby default,:1944-1956; TTL 3600s,:1304); bypassed for authorization-sensitive tools (:2631-2636) and attached media (:2637-2639). Model-reply cache:libs/agno/agno/models/base.py:682-689,:878-880(cache_response, default False,:172).
E10 Is the model's reply complete, or was it cut off or refused? RULE · code
- Answered how
agno does not stop or flag a reply cut off by the token limit or refused: whatever text came back is the answer, and the run reports completed (the default Responses model only logs a warning on an "incomplete" reply).
- Held by
no reader: the shared loop ends on 'no tool calls' alone (
libs/agno/agno/models/base.py:873-874) and never looks at a finish or stop reason (Grepfinish_reason|stop_reason|truncatoverlibs/agno/agno/models/base.py-> 0; positive controltool_callsin the same file -> 22). The OpenAI chat parser readsfinish_reasononly for metrics (libs/agno/agno/models/openai/chat.py:802); Claude readsstop_reasononly for"tool_use"(libs/agno/agno/models/anthropic/claude.py:1058); the OpenAI Responses non-streamed path logs a warning onincompleteand continues, in every call, not only in background mode (libs/agno/agno/models/openai/responses.py:925-929). The run is then marked completed (libs/agno/agno/agent/_run.py:665).
E11 Which part of the model's text is hidden reasoning and which is the answer? RULE · code
- Answered how
The default
OpenAIResponsesmodel keeps the provider's reasoning summary apart from the answer. For OpenAI-chat replies (non-streamed) and before structured parsing, the text inside think-tags (or before a lone</think>) becomes reasoning and only what follows</think>stays the answer; Ollama replies are split this way only when they contain<think>.- Held by
extract_thinking_contentapplied to a non-streamed OpenAI-chat reply before any parsing (libs/agno/agno/models/openai/chat.py:831-836), to an Ollama chat reply only when it contains<think>(libs/agno/agno/models/ollama/chat.py:366-374), and before structured parsing (libs/agno/agno/utils/string.py:202-205,libs/agno/agno/utils/string.py:257-260); the rule itself atlibs/agno/agno/utils/reasoning.py:12-30: everything up to the first</think>(from<think>if present, else from the start) is reasoning, text before<think>is dropped, the rest is the answer.
E12 When a run fails, does the caller get an exception or a normal-looking result? RULING · code
- Answered how
Agent.run()does not raise when a run fails: it returns a result whose status is 'error' and, if the run produced no content, whose content is the error message.- Held by
libs/agno/agno/agent/_run.py:727-758(and the guardrail branch:692-711): the run is markedstatus=error, the error text is put intocontentif empty, the run is stored, andRunOutputis RETURNED, not raised; same pattern in the async and continue paths.
E13 How much of a stored file may one FileSystem read return, and what does the agent see when a store is full? RULING · code
- Answered how
Absent by default (
filesystem=None). With a FileSystem attached, a whole-file read over 100,000 characters is refused with a hint to read a range (ranged reads are not capped), and a listing shows at most 200 folders and 200 files.- Held by
libs/agno/agno/fs/toolkit.py:47(_MAX_READ_CHARS100,000: a whole-file read above it is refused with a hint to read a range,:274-280);:45(_MAX_DIR_ENTRIES200, listing truncated with an "...and N more" row,:365-388); quota messages (:226-237) instruct the model "Never overwrite or discard records you might still need to make room; if nothing is safely disposable, stop and report that storage is full."
E14 How much of a shell command's output does the coding toolkit return, and how long may a command run? RULING · code
- Answered how
Only when an integrator adds
CodingToolsand enablesrun_shell(off by default): output past 2,000 lines or 50 KB is cut (the full output is saved to a temporary file whose path is returned), and a command stops after the model'stimeoutargument, else 120 seconds.- Held by
libs/agno/agno/tools/coding.py:234-263(_truncate_output: 2,000 lines / 50,000 bytes,libs/agno/agno/tools/coding.py:144-145; on truncation the full output is written to a temp file whose path is appended,libs/agno/agno/tools/coding.py:647-658);run_shellis off by default (enable_run_shell=False,libs/agno/agno/tools/coding.py:150) and takes a model-suppliedtimeout, falling back toshell_timeout120s (libs/agno/agno/tools/coding.py:588,:612,:146).
F. Limits and memory
F1 When an agent keeps asking for tools, when are further tool calls refused? RULING · code
- Answered how
No limit by default: code runs every tool call the model asks for. With
tool_call_limitset, calls past it are not run; the model is told so and asked again, and the run goes on.- Held by
libs/agno/agno/models/base.py:2431-2445(run_function_calls: counts calls, past the limit appendscreate_tool_call_limit_error_result,libs/agno/agno/models/base.py:2222-2230); limit read fromAgent.tool_call_limit(libs/agno/agno/agent/agent.py:198, defaultNone) and passed atlibs/agno/agno/agent/_run.py:563.
F3 In task mode, when does the team leader's planning loop stop if the goal is never marked complete? RULING · code
- Answered how
Only in task mode (
mode=TeamMode.tasks; default is coordinate): after 10 leader turns by default. A run that hits the cap is still marked completed, with only a log warning that tasks were left unfinished.- Held by
libs/agno/agno/team/_run.py:371(for iteration in range(team.max_iterations)), exhaustion at:452-456; defaultmax_iterations: int = 10(libs/agno/agno/team/team.py:124).
F4 Has this session's history grown enough that older turns should be folded into a summary? RULING · code
- Answered how
Off by default.
compaction=Truenever folds on size, only after a too-long rejection (F7). ACompaction(...)object folds once replayed history reaches 150,000 tokens, counted locally unless told otherwise.- Held by
libs/agno/agno/compaction/compaction.py:258-277(should_compact: token count>= compact_at_tokens, default 150,000:56,122), applied at run start inlibs/agno/agno/agent/_messages.py:700-716; the fold-worth guard_worth_compactinglibs/agno/agno/compaction/compaction.py:825-861(off unlessenforce_min_fold_ratio,:152).
F5 When history is folded, which recent turns are kept word for word? RULING · code
- Answered how
Off by default. When a fold happens, the last five runs stay word for word (fewer when they exceed the token tail limit or after a too-long rejection); a turn is never split and the newest question is never folded.
- Held by
libs/agno/agno/compaction/compaction.py:397-454(boundary_for/_run_tail_boundary: keep lastuncompacted_runs=5 runs by default:65,135, capped by a token tail limit:364-376, never inside the newest exchange:415-426), pair-safe cut inlibs/agno/agno/compaction/_cut.py(choose_boundary).
F6 What should the summary of the folded conversation say? RULING · llm
- Answered how
Off by default. When compaction folds, the agent's own model (unless
Compaction.modelis set) writes the summary from a fixed prompt; it replaces the older turns in what the model sees, while the stored session keeps the originals.- Held by
libs/agno/agno/compaction/compaction.py:628-645(_summarize, callingCompaction.model); the model defaults to the agent's (libs/agno/agno/agent/_init.py:243-244).
F7 After the provider rejects a request as too long, should agno fold and retry? RULING · code
- Answered how
Only with
compaction=True(where it is the only trigger) or aCompactionset toon_context_overflow=True: agno folds the oldest turns of the rejected request and retries it once. Off by default; with no safe cut the original error stands.- Held by
libs/agno/agno/agent/_messages.py:608-644(_recompact_after_overflow), planner:497-554(shrinks the kept tail one run at a time), shrink check:557-585; wired atlibs/agno/agno/agent/_run.py:557-559(recover_from_overflow).
F8 When a compaction summary runs over its length budget, what is cut? RULING · code
- Answered how
Only when a
compacted_token_budgetis set (none by default): the summary's end is cut at line breaks, keeping whole leading lines and words, and a note says it was cut. Nothing checks that the model put the important parts first.- Held by
libs/agno/agno/compaction/compaction.py:566-626(_fit_to_budget: keeps leading whole lines, drops trailing ones, appendsSUMMARY_CUT_NOTE).
F9 Have enough raw tool results piled up that they should be compressed? RULING · code
- Answered how
Off by default. With
compress_tool_results=True, before each model call code compresses every uncompressed tool result once three of them have piled up (or a token limit, if set, is reached).- Held by
libs/agno/agno/compression/manager.py:69-103(should_compress: 3 uncompressed tool results by default:62-64, or a token limit); called at the top of every model turnlibs/agno/agno/models/base.py:705-712.
F10 What part of a tool result is worth keeping when it is compressed? RULING · llm
- Answered how
Off by default. With compression on, the agent's model (unless the manager names another) rewrites each tool result to its facts, dates, names and ids, and later model calls see the rewrite instead of the original.
- Held by
libs/agno/agno/compression/manager.py:105-140(_compress_tool_result, callingCompressionManager.model); the model defaults to the agent's (libs/agno/agno/agent/_init.py:186-197).
F11 Is this tool result too long to keep in the conversation, so it is stored and replaced by a pointer? RULING · code
- Answered how
Off by default. With
offload_tool_results=Trueand a SQLite or Postgres database, a tool result over 16,000 characters is stored aside and the model gets a short preview plus an id it can read back with a tool.- Held by
libs/agno/agno/offload/store.py:653-660(should_offload: length overthreshold_chars, defaultDEFAULT_THRESHOLD_CHARS=READ_MAX_CHARS= 16,000,libs/agno/agno/offload/store.py:40-42; never for read-back toolsNEVER_OFFLOADED_TOOLS); envelope atlibs/agno/agno/offload/store.py:667-707.
F12 What should the running summary of this session say, for later runs? RULING · llm
- Answered how
Off by default. With
enable_session_summaries=True(and a database or a cached session, for later runs to see it), after each run the agent's model re-reads the session's user and assistant turns (not tool calls) and writes a fresh summary that later runs can see.- Held by
libs/agno/agno/session/summary.py:242-276(create_session_summary, callingSessionSummaryManager.model, which defaults to the agent's model,libs/agno/agno/agent/_init.py:172-183); fired after the run atlibs/agno/agno/agent/_run.py:655-663(requires both a manager andenable_session_summaries) and in a team atlibs/agno/agno/team/_run.py:491-496(requires only a manager).
F13 What of a finished run is kept in the stored session? RULE · code
- Answered how
By default each stored run keeps its own messages, tool results and media, but not the history messages that were replayed into it.
- Held by
libs/agno/agno/agent/_run.py:5872-5882(scrub_run_output_for_storage, applied to a storage copy atlibs/agno/agno/agent/_run.py:6003; for members atlibs/agno/agno/team/_default_tools.py:616); settingsstore_media=True,store_tool_messages=True,store_history_messages=False(libs/agno/agno/agent/agent.py:238-248).
F14 In a workflow loop, when does the loop stop if its end condition never says so? RULING · code
- Answered how
After three passes by default (
max_iterations=3), unless a step asks to stop first. Exists only in a workflow that contains aLoopstep.- Held by
libs/agno/agno/workflow/loop.py:462(while iteration < self.max_iterations), defaultmax_iterations: int = 3(:76).
G. Judging the result
G1 Before an agent's answer is returned, does anything check that it is right or complete? RULING · code
- Answered how
Nothing, by default. The run ends when the model replies without asking for a tool, and that reply is returned as completed; a check runs only if the integrator adds post-hooks (guardrails or evals).
- Held by
libs/agno/agno/agent/_run.py:620-665: after the model loop the run converts to the output schema if one is set (:621), runspost_hooksonly if any were given (:627-639; defaultNone,libs/agno/agno/agent/agent.py:214), then setsRunStatus.completed(:665). The answer's "done" is the model stopping its tool calls (C6).
G2 In task mode, has the team's overall goal been met? RULING · llm
- Answered how
Only in task mode (
mode=TeamMode.tasks; absent by default): the leader's model decides by callingmark_all_complete, which code accepts without checking that any task completed.- Held by
libs/agno/agno/team/_task_tools.py:304-315(mark_all_complete, called by the team leader's model; setsgoal_complete), read by the loop atlibs/agno/agno/team/_run.py:425-428.
G3 In task mode, does a finished task list (or a turn with nothing to do) end the run? RULE · code
- Answered how
Task mode only: a list whose tasks all completed ends the loop; if any failed, the leader gets another turn; with no tasks, two turns without a tool call end it. All bounded by F3's 10 turns.
- Held by
libs/agno/agno/team/_run.py:430-451: all tasks terminal with no failure ends the loop (TaskList.all_terminal,libs/agno/agno/team/task.py:144-148); with failures the loop continues; with no tasks, two turns without a tool call end it (libs/agno/agno/team/_run.py:439-449).
G4 When a member finishes a task, did the task succeed? RULING · code
- Answered how
Task mode only. Code decides from the member's run: an exception or error status marks the task failed; anything else, including an empty reply, marks it completed. Whether the content does the task is left to the leader (G2).
- Held by
libs/agno/agno/team/_task_tools.py:605-650(execute_task): exception →failed(:605-611); memberRunStatus.error→failed(:629-635); any content →completed(:636-643); no content →completedwith "No content returned" (:644-650). Parallel copy:888-1027, async:655-835,:1092-1217. Dependent tasks fail automatically when a dependency failed (libs/agno/agno/team/task.py:206-222).
G5 Did this workflow step succeed, so the workflow may go on? RULE · code
- Answered how
Workflow only. A step fails when its agent or team run ended in error or was cancelled, but by default the workflow goes on: every step gets review settings whose failure action is "skip". It stops only for a step set to
on_error="fail"that does not skip failures. Its content is not judged.- Held by
libs/agno/agno/workflow/step.py:2779-2821(_process_step_output:success = status not in (cancelled, error),:2805); the workflow stops on a failed step whoseon_errorisfail(libs/agno/agno/workflow/workflow.py:261-275, called at:3166).
G6 Has a workflow loop done enough to stop? RULING · external
- Answered how
Workflow
Looponly: whatever end condition the author wrote (a function or CEL expression), checked after each pass; with none, or if it errors, the loop runs to its maximum (3 by default, F14).- Held by
libs/agno/agno/workflow/loop.py:290-316(_evaluate_end_condition: the integrator'send_conditioncallable, or a CEL expression evaluated in code), checked after each pass atlibs/agno/agno/workflow/loop.py:536-537; optional per-iteration person review at:545(human_review.requires_iteration_review).
H. Delegation
H1 Which team member should take this piece of the work? RULING · llm
- Answered how
In the default coordinate mode the team leader's model picks the member for each sub-task from the roster's roles and descriptions, and may hand out several in one turn. A wrong id just returns the roster to it.
- Held by
libs/agno/agno/team/_default_tools.py:706-723(delegate_task_to_member: the team leader's model choosesmember_id), at the default modecoordinate(libs/agno/agno/team/_init.py:225-231).
H2 What exactly does a member receive as its task? RULING · llm
- Answered how
By default the leader writes the member's task in its own words and the member sees only that (plus team history or member interactions if those are turned on). With
determine_input_for_members=Falsethe user's original message is passed instead.- Held by
libs/agno/agno/team/_default_tools.py:546-550(the leader'staskargument, or the user's input when the flag is off), withdetermine_input_for_members=Trueby default (libs/agno/agno/team/team.py:122).
H3 What does the leader get back from a member, and can it tell a failure from an answer? RULE · code
- Answered how
The member's reply text comes back as a tool result. A member that errored returns its error message as if it were the reply, so the leader must recognise failure from the words; its prompt tells it to say so plainly.
- Held by
libs/agno/agno/team/_default_tools.py:844-870(non-streaming path): content string, or concatenated tool results when the content is empty (:855-861), or "No response from the member agent." (:846-849), or JSON of a structured reply; a paused member yields "requires human input" (:832-842).
H4 When a member changes shared session state, whose change survives? RULE · code
- Answered how
Only matters when session state is used. Each member runs on a copy of the team's state, merged back after it returns, so on a key two members both changed, the member that finishes last wins.
- Held by
libs/agno/agno/team/_default_tools.py:728(each member runs on a shallowcopy(...)) and the copy is merged back after the member returns (merge_dictionaries,libs/agno/agno/team/_default_tools.py:626-627); in task mode adeepcopy(libs/agno/agno/team/_task_tools.py:511) merged atlibs/agno/agno/team/_task_tools.py:404-405.
H5 In task mode, which member runs each task, and which tasks run together? RULING · llm
- Answered how
Task mode only (absent by default): the leader's model creates tasks, names who does each, and chooses which independent ones run together; code only enforces dependencies and statuses.
- Held by
libs/agno/agno/team/_task_tools.py:181-217(create_task(assignee)), withexecute_task(task_id, member_id)(libs/agno/agno/team/_task_tools.py:476-500) andexecute_tasks_parallel(task_ids)(libs/agno/agno/team/_task_tools.py:845), all called by the leader's model.
H6 Should this team's leader rewrite the member's reply, or pass it straight to the user? RULING · llm
- Answered how
By default (coordinate mode) the leader writes its own answer from what members returned. In route mode the chosen member's reply goes to the user as written and the leader gets no turn to review it.
- Held by
libs/agno/agno/team/_messages.py:233-248(coordinate mode, the default: the leader's model writes the final answer from member outputs, "Write one answer ... Never concatenate member outputs"); route mode instead marks the delegate toolstop_after_tool_call(libs/agno/agno/team/_default_tools.py:1523-1524).
H7 Which steps does a workflow router send this input down? RULING · external
- Answered how
Workflow
Routeronly: the author's selector function or CEL expression decides; if it names an unknown step, the router runs nothing and only logs a warning. A person picks instead when the router is set to ask.- Held by
libs/agno/agno/workflow/router.py:528-566(_route_steps: the integrator'sselectorcallable:551-564, or a CEL expression evaluated in code:536-548); a person picks instead whenhuman_review.requires_user_input=True(libs/agno/agno/workflow/router.py:138-191,:418-421).
H8 Should a workflow's conditional steps run for this input? RULING · external
- Answered how
Workflow
Conditiononly: the author's condition (function, bool or CEL) decides; the default evaluator is True. If a CEL condition fails to evaluate, or a function returns a non-bool, agno treats it as false.- Held by
libs/agno/agno/workflow/condition.py:371-412(_evaluate_condition: the integrator'sevaluatorcallable, a bool, or CEL); default evaluatorTrue(libs/agno/agno/workflow/condition.py:101-106); a person confirms instead whenrequires_confirmation(comment:100).
H9 Should this message re-run the workflow, or be answered from earlier runs? RULING · llm
- Answered how
Only when a workflow is given a
WorkflowAgent(absent by default): its model decides whether a message needs the whole workflow run again, with its own rewritten query, or can be answered from earlier runs.- Held by
libs/agno/agno/workflow/agent.py:15-78(WorkflowAgent), calling therun_workflowtool (libs/agno/agno/workflow/agent.py:109-192) or answering itself.
I. Learning and feedback
I1 After a run, what should be remembered about this user, and what changed or should be dropped? RULING · llm
- Answered how
Off by default. With
update_memory_on_run=Trueand a database, a background model call (the agent's model by default) reads the user's message and existing memories and adds, rewrites or deletes memories itself. Without auser_idall users share the bucket "default".- Held by
libs/agno/agno/memory/manager.py:1044-1100(create_or_update_memories, callingMemoryManager.model, which defaults to the agent's model,libs/agno/agno/agent/_init.py:104-119), reached viacreate_user_memories(libs/agno/agno/memory/manager.py:377-430) from a background future started before the model call (libs/agno/agno/agent/_managers.py:180-215, called atlibs/agno/agno/agent/_run.py:526-531); team copylibs/agno/agno/team/_managers.py:41-44,106-107(consolidated, same function).
I2 During a run, should the agent change what it remembers about the user? RULING · llm
- Answered how
Off by default. With
enable_agentic_memory=Trueand a database, the agent's own model decides mid-run when something about the user is worth saving and describes the change in words for the memory model (I3); the end-of-run pass (I1) is then skipped.- Held by
libs/agno/agno/agent/_default_tools.py:37-74(update_user_memory, called by the agent's main model) whenenable_agentic_memory=True(libs/agno/agno/agent/agent.py:137; tool added atlibs/agno/agno/agent/_tools.py:273).
I3 Given the agent's memory request, which memory records change? RULING · llm
- Answered how
Off by default (needs I2). A second call to the memory model turns the request into adds and edits on this user's memories; delete and clear exist only if the integrator turns them on, and clear empties the whole table.
- Held by
libs/agno/agno/memory/manager.py:1197-1251(run_memory_task) viaupdate_memory_task(libs/agno/agno/memory/manager.py:490-526).
I4 After a run, what should the learning machine record about the user's profile and memories? RULING · llm
- Answered how
Off by default. With
learning=Trueand a database, background model calls (the agent's model by default), started alongside the main model call, read the conversation so far and update this user's profile and memory notes; with nouser_idnothing is saved.- Held by
libs/agno/agno/learn/stores/user_memory.py:728-799(UserMemoryStore.extract_and_save) andlibs/agno/agno/learn/stores/user_profile.py:819(UserProfileStore.extract_and_save), both inALWAYSmode, built bylearning=Truewithuser_profile=True, user_memory=True(libs/agno/agno/agent/_init.py:149-151; needs a db,:142-145), dispatched byLearningMachine.process(libs/agno/agno/learn/machine.py:827-866) from a background future started before the model call (libs/agno/agno/agent/_run.py:534-541,libs/agno/agno/agent/_managers.py:265-298).
I6 Which notes about this session's context should be kept? RULING · llm
- Answered how
Off by default, and
learning=Truedoes not turn it on. When aLearningMachineenables session context, a background model call, started alongside the main model call, reads the session's messages so far and updates its notes; people, things and decisions are I9.- Held by
libs/agno/agno/learn/stores/session_context.py:128-162(SessionContextStore.process: ALWAYS mode only,:147-150; a background extraction throughextract_and_saveafter the run,:155-162, withtool_call_limit=max_updates_per_run,:567).
I7 Should this exchange be added to the agent's knowledge base? RULING · llm
- Answered how
Off by default. With
update_knowledge=Trueand a knowledge base, the agent's model may save any question-and-answer pair into the knowledge base that later retrieval reads; with nouser_idit goes into the shared bucket.- Held by
libs/agno/agno/agent/_default_tools.py:397-418(add_to_knowledge, called by the agent's model) whenupdate_knowledge=True(libs/agno/agno/agent/agent.py:232) and a knowledge base is set (libs/agno/agno/agent/_tools.py:316); write atlibs/agno/agno/agent/_default_tools.py:361-394.
I8 Should the agent change its own saved session state? RULING · llm
- Answered how
Off by default. With
enable_agentic_state=Truethe agent's model can overwrite top-level keys of the session state, which is saved with the session and read by later runs when there is a database or a cached session.- Held by
libs/agno/agno/agent/_default_tools.py:325-358(update_session_state_tool, called by the agent's model) whenenable_agentic_state=True(libs/agno/agno/agent/agent.py:106).
I9 During a run, which people, things and decisions should the agent record in its learning stores? RULING · llm
- Answered how
Off by default, and
learning=Truedoes not turn it on. When aLearningMachineenables entity memory or decision logs, the agent's own model records people, things and decisions through tools during the run; entity notes default to the shared "global" namespace.- Held by
the agent's main model, calling the entity-memory tools (
libs/agno/agno/learn/stores/entity_memory.py:825-853,get_tools; the store refuses any mode but AGENTIC,:402-410) and the decision-log tools (libs/agno/agno/learn/stores/decision_log.py:207-234,log_decisionat:378; AGENTIC only,:67-71). Both stores'processis a no-op (decision_log.py:139-141), so nothing is extracted after the run. Neither is turned on bylearning=True, which builds user profile and user memory only (libs/agno/agno/agent/_init.py:149-151).
I10 When the agent records a new fact about an entity, which existing facts does it replace? RULING · llm
- Answered how
Only with entity memory enabled (off by default): one model call judges which of the entity's latest 50 live facts a new fact replaces; only judgments at confidence 0.8 or above retire a fact, and a failed judgment keeps every fact.
- Held by
libs/agno/agno/learn/stores/entity_memory.py:1328-1374(_judge_superseded: one structured call to the store's model with a single strictmark_supersededtool andtool_call_limit=1, over the entity's last 50 live facts); applied at:1480-1484(a judgment belowsupersession_threshold, default 0.8,libs/agno/agno/learn/config.py:369-372, is ignored).
How this map was made
Four readers each took a part of the agent and wrote seat cards from the source. The map's author then re-read every seat against the cited lines and corrected what the source did not support, and rewrote each answer in a short form that describes the default. Reviews by a model from another family (read-only, at the same commit) checked the answers against the code, and each of their findings was checked against the code before anything changed. A checker confirms that each seat's holder line exists at the pinned commit and carries its anchor text. Nothing on this page was produced by running their code.