Skip to content

How a team of agents becomes one system

A relay team is not four runners. It is four runners, a fixed running order, and one baton that has to pass cleanly from hand to hand. Take away the order and they collide. Take away the baton and there is nothing to hand off, just four people running alone. The runners are the easy part to picture. The order and the baton are what make them a team.

For five lessons we have met the runners: the analysts who gather, the bull and the bear who argue, the judge who rules, the trader who plans, the risk seats and the gate. Each one does its job and leaves something behind for the next. But we have not yet looked at the two things that turn those separate jobs into a single working system: the order (who runs next) and the baton (what they pass along). Those two ideas have names. The order is the orchestration. The baton is the shared state. This lesson is about both.

This track teaches how a multi-agent AI system is built and coordinated. It uses stock analysis as the worked example. It is for education only. It is not investment, financial, or trading advice, and nothing here is a recommendation to buy or sell anything.

The idea: control flow and data flow are two different things

Section titled “The idea: control flow and data flow are two different things”

When people imagine a team of AI agents, they usually picture the agents. The harder, more important design is the wiring between them, and that wiring is really two separate questions.

The first is control flow: whose turn is it? After the trader finishes, who goes next, and how is that decided? Sometimes the answer is fixed (after the trader, always the risk team). Sometimes it depends on what has happened so far (if the analyst still needs data, loop back; otherwise move on).

The second is data flow: how does one agent’s work reach the next? The bull’s argument has to reach the judge. The judge’s plan has to reach the trader. The trader’s plan has to reach the risk seats. If every agent kept its work to itself, the team would be a row of strangers.

Keep these two apart in your head, because they are solved by two different mechanisms, and almost every multi-agent system you ever build will need both.

From here we read the real system. You can follow along in your browser, with no account, no Git, and no programming knowledge needed:

github.com/TauricResearch/TradingAgents (pinned snapshot)

Everything below is drawn from that frozen snapshot (the short code 7e9e7b8 marks the exact version). The instruction lines are quoted word for word; the code is shown lightly trimmed for readability, with its logic intact.

Data flow first, because it is the simpler of the two. The whole team shares one structured workspace, defined in one file (tradingagents/agents/utils/agent_states.py). Think of it as a whiteboard that every agent can read and write. Each labelled space on the board has a name and holds one piece of the work:

tradingagents/agents/utils/agent_states.py
class AgentState(MessagesState):
company_of_interest: Annotated[str, "Company that we are interested in trading"]
trade_date: Annotated[str, "What date we are trading at"]
# each analyst leaves its report in its own space
market_report: Annotated[str, "Report from the Market Analyst"]
sentiment_report: Annotated[str, "Report from the Social Media Analyst"]
news_report: Annotated[str, "Report from the News Researcher of current world affairs"]
fundamentals_report: Annotated[str, "Report from the Fundamentals Researcher"]
# then the investment plan, the trader's plan, and the final decision
investment_plan: Annotated[str, "Plan generated by the Analyst"]
trader_investment_plan: Annotated[str, "Plan generated by the Trader"]
final_trade_decision: Annotated[str, "Final decision made by the Risk Analysts"]
# (other spaces omitted, including the debate records)

This one block quietly explains every handoff from the earlier lessons. Remember the trader reaching for the judge’s plan? It was reading the space called the investment plan (investment_plan), where the research manager, the judge from the debate lesson, wrote its ruling. (The framework labels that space loosely, but it holds the judge’s plan, not one of the four analyst reports above it.) Remember the trader leaving its proposal behind? It wrote the space called the trader’s plan (trader_investment_plan). Remember the gate issuing the last word? It wrote the final decision (final_trade_decision). Nobody passes private notes. Everything goes on the one board, in a labelled space, and the next agent reads it from there.

That is the first big idea of coordination: a single source of truth. There is one place the work lives, and every agent reads from and writes to that same place. The alternative, each agent passing a hand-picked summary to the next, is how the children’s game of telephone garbles a message. One shared board keeps everyone honest about what was actually said.

Now control flow. The team is wired together as a graph: a set of boxes (the agents) with arrows between them (who can follow whom). The system builds that graph by adding one box per agent and then drawing the arrows.

tradingagents/graph/setup.py
workflow = StateGraph(AgentState)
workflow.add_node("Bull Researcher", bull_researcher_node)
workflow.add_node("Trader", trader_node)
workflow.add_node("Portfolio Manager", portfolio_manager_node)
# (one box added per agent)

Notice what the graph is built around: the shared whiteboard from a moment ago (AgentState). The orchestration and the shared state are not two unrelated systems. The graph moves control from box to box, and the board is what each box reads and writes as it runs. Control flow and data flow, wired into one thing.

The arrows come in two kinds, and the difference is the heart of this lesson. Some arrows are fixed: after this box, always go to that one.

# after the judge plans, always hand to the trader; after the gate decides, stop
workflow.add_edge("Research Manager", "Trader")
workflow.add_edge("Portfolio Manager", END)

A fixed arrow is a promise: this handoff never changes. The judge is always followed by the trader. The gate is always the end of the line. There is no decision to make here, so the system does not make one.

Other arrows are conditional: where to go next depends on what is currently written on the board.

# after the bull speaks, a small function decides: keep debating, or go to the judge?
workflow.add_conditional_edges(
"Bull Researcher",
self.conditional_logic.should_continue_debate,
{
"Bear Researcher": "Bear Researcher",
"Research Manager": "Research Manager",
},
)

Read that middle line, because you have met it before. The function that decides whether to keep debating (should_continue_debate) is the one from the lesson on the bull and the bear. Here is where it actually lives: it is the decision attached to a conditional arrow. When the bull finishes, the system runs that little function, looks at how many rounds have happened, and picks the next box: back to the bear for another round, or on to the judge.

And this is the quiet reveal of the whole track. Every “should we continue” decision you have already seen is a conditional arrow on this one graph. The check on whether the analyst needs another tool call, from the data-fetching lesson. The check on whether the debate goes another round, from the bull and the bear. The check that rotates the three risk seats and then routes to the gate, from the risk lesson. They are not scattered tricks. They are the conditional arrows of a single map, each deciding one fork in the road.

You may never wire up a graph by hand. But the moment you ask AI to do more than one step, you are making these two decisions whether you notice or not.

  • Decide how your steps share information. The robust choice is a single source of truth: one place the work accumulates, that each step reads from and writes to. The fragile choice is a chain of hand-offs where each step only sees a summary of the step before. Summaries lose things, and the loss compounds. If you have ever watched an AI workflow slowly drift away from what you originally asked, a leaky chain of summaries is often why.
  • Decide which handoffs are fixed and which are conditional. A fixed step always runs in the same place; reach for it when the order genuinely never changes. A conditional step routes based on what has happened so far (loop again, escalate, or finish); reach for it when the work needs to adapt. Naming which is which, out loud, is most of good workflow design.
  • See the wiring, not just the agents. When an AI system misbehaves, the bug is usually not in one agent. It is in the wiring: a step that could not see what an earlier step produced, or a fork that routed the wrong way. If you know to look at the control flow and the data flow, you know where to look.

The agents are the part everyone talks about. The wiring is the part that decides whether they add up to a system or just a crowd.

  • No shared source of truth. Each step is fed only the previous step’s output, so context quietly leaks away and late steps work from a thinned-out picture. One accumulating workspace fixes it.
  • Making everything conditional. If every handoff is a decision, the system is hard to follow and easy to send down the wrong path. Use a fixed handoff wherever the order truly never changes, and save the decisions for the real forks.
  • Confusing control flow with data flow. “Who runs next” and “what they can see” are different questions. A step can be next in line and still be missing the information it needs, because the order was wired but the workspace was not.
  • A team of agents is held together by two things: orchestration and shared state. Orchestration is the control flow (who runs next). Shared state is the data flow (what they pass along). Different problems, different mechanisms, both required.
  • Shared state is one source of truth. Every agent reads from and writes to a single structured workspace, so the handoffs you saw in earlier lessons are just reads and writes of named spaces on one board.
  • Arrows are fixed or conditional. A fixed arrow is a handoff that never changes; a conditional arrow runs a small function that reads the current state and chooses the next step. The “should we continue” checks from earlier lessons are exactly those conditional arrows.

The graph now runs start to finish: gather, argue, judge, plan, stress-test, decide. But a thoughtful team does one more thing. It remembers how its past calls turned out, and lets that shape the next one. How the system stores and learns from its own history is the next lesson, and a hands-on capstone closes the track after that.