Step 2 · Match Prep
The pattern: a SequentialAgent runs its sub-agents in a fixed order, every time, with no model of its own. They hand work over through session state.
The opponent analyst studies Gaziantep and writes a report. The tactician turns the report into a game plan. The lineup picker turns the plan into an XI. Here's the middle one, and the line that chains them:
steps/step2_match_prep/agent.py tactician = LlmAgent(
name="tactician",
model=MODEL,
instruction="""
You are Trabzonspor's tactician. Opponent report from the analyst:
{opponent_report}
Check our own form with get_team_trends("Trabzonspor"), then write a game plan:
formation, how we attack, how we defend, and what to do in each phase of the match.
""" + HOUSE_RULES,
tools=[get_team_trends, get_match_info],
output_key="game_plan",
) return SequentialAgent(name=name, description=description, sub_agents=[opponent_analyst, tactician, lineup_picker])output_key="opponent_report" saves the analyst's final answer in session state. {opponent_report} in the tactician's instruction pastes it in before the tactician's model call. No glue code.
I asked: "Prepare Sunday's game plan."
Three agents in a row, about two minutes. The tactician, using the analyst's report:
Exploit High Conceding Windows: Gaziantep FK have conceded 67% of their goals in the second half (10 of 15 goals in their last 10 matches). Increase tempo immediately after half-time.
And the lineup picker, after checking availability:
Formation: 4-1-4-1 · André Onana; Wagner Pina, Stefan Savić, Chibuike Nwaiwu, Mustafa Eskihellaç; Fabinho; Mohamed Salah, Melih Kabasakal, Ernest Muçi, Noah Saviolo; Aral Şimşir.
In an earlier run the lineup picker tried two names from its own memory that aren't in this squad. get_player answered {"error": "No player called 'Destan' in either squad.", ...} and it moved on to real players: the error-instead-of-exception rule paying off.
Use it when the steps are always the same and each needs the previous one's output: research, plan, act.
Avoid it when the steps are independent (step 3 is faster) or the next step depends on what happened.
The gotcha: output_key and placeholders are a contract. A {placeholder} with no value in state raises a KeyError before the model is called. Keep the names identical, and make sure the writer runs before the reader.