Add agent-supervisor example - #903
shahryarkhalid-cmd wants to merge 1 commit into
Conversation
skrawcz
left a comment
There was a problem hiding this comment.
Thanks for this — nice, clean use of Burr (state schema, reads/writes, transitions all follow the agent_supervisor.py template and multi-agent-collaboration conventions well). I ran it through a worktree locally (mocked LLM calls to exercise the full supervisor → agent → tool_node loop) and left a few inline comments on things worth fixing before merge.
Also, examples/README.md's index doesn't include this new example yet — every other example is listed there alongside a one-line description, worth adding this one too.
Otherwise this is a solid, well-documented addition. Happy to take another look once the inline items are addressed.
| "args": tool_call.function.arguments, | ||
| } | ||
| ) | ||
| new_message = {"role": "assistant", "content": message.content or "", "name": sender} |
There was a problem hiding this comment.
Blocking: tool-call round-trip bug. This stored assistant message drops the tool_calls field from the OpenAI response:
new_message = {"role": "assistant", "content": message.content or "", "name": sender}tool_node later appends a {"role": "tool", "tool_call_id": ...} message referencing that call, but OpenAI's Chat Completions API requires the immediately-preceding assistant message to carry a matching tool_calls array — otherwise it 400s. Since this field is stripped, the example will likely fail against real api.openai.com the first time a tool is called (this may be why it worked against Groq's endpoint but wasn't verified against OpenAI itself, per the PR description). Suggested fix:
new_message = {
"role": "assistant",
"content": message.content or "",
"name": sender,
**({"tool_calls": [tc.model_dump() for tc in message.tool_calls]} if message.tool_calls else {}),
}Would appreciate a live run against real OpenAI (not just Groq) after this fix, since that's the API the example is nominally demonstrating.
| app.visualize( | ||
| output_file_path="statemachine", include_conditions=True, view=True, format="png" | ||
| ) | ||
| app.run(halt_after=["terminal"]) No newline at end of file |
There was a problem hiding this comment.
Pre-commit isn't passing on this file (confirmed locally): black --line-length=100 would reformat the app.visualize(...) call above to fit on one line, and flake8 fails with W292 no newline at end of file here. Running pre-commit run --files examples/agent-supervisor/* should surface/fix both.
| return _run_agent( | ||
| state, | ||
| system_message="You are a researcher. Use web_search to gather accurate " | ||
| "information for the coder to use. Say FINAL ANSWER when done.", |
There was a problem hiding this comment.
Nit (optional): this and the coder prompt below say "Say FINAL ANSWER when done," but nothing in the code actually checks for that phrase — the supervisor's own LLM call decides FINISH independently. Looks vestigial from the multi-agent-collaboration pattern (where it was checked via expr). Not a bug, just a bit misleading — could drop it for clarity.
| burr[start] | ||
| openai | ||
| tavily-python | ||
| python-dotenv No newline at end of file |
There was a problem hiding this comment.
python-dotenv isn't imported/used anywhere in application.py or notebook.ipynb. Either wire it up (load_dotenv()) or drop the dependency. Also missing a trailing newline (pre-commit's end-of-file-fixer will flag this).
Fleshes out the
agent_supervisor.pytemplate inexamples/templates/into afull, runnable example (
examples/agent-supervisor/). Ports LangGraph's"agent supervisor" pattern to Burr: an LLM-driven supervisor that routes
between a
researcheragent (web search) and acoderagent (pythonexecution / charting), deciding the next step at each turn rather than using
fixed transitions.
Scope was discussed beforehand with @Stefan K on Discord, who suggested this
be built as a standalone example that implements the existing template,
rather than modifying
templates/directly.Changes
examples/agent-supervisor/folder, following the standard examplestructure:
application.py--supervisor_agent,researcher,coder,tool_node,and
terminalactions, wired up withApplicationBuilder. Uses the plainOpenAI SDK for LLM calls (matching
examples/email-assistant's style)rather than LangChain or Hamilton, to keep the example dependency-light.
README.md-- explains the example and how it relates to the templateand to
examples/multi-agent-collaboration.requirements.txt,notebook.ipynb,__init__.py,statemachine.png(generated via
app.visualize()).This differs from
examples/multi-agent-collaboration(which covers the sameresearcher/coder domain) in that routing between agents is decided by the
supervisor's own LLM call at each step, rather than fixed transitions --
demonstrating the supervisor pattern specifically, as the template intends.
How I tested this
transitions wired as expected.
app.visualize()produces the expected state diagram (included asstatemachine.png).endpoint (as a stand-in, since I didn't have an OpenAI key on hand) to
validate the supervisor's routing logic, the tool-calling loop, and
termination behavior all work correctly.
-- happy to do that and confirm, or if a reviewer can sanity-check with
their own key that'd also be appreciated.
Notes
prompt in
supervisor_agentif it should be tuned further.TAVILY_API_KEYis optional -- without it,web_searchreturns aplaceholder string so the example still runs.
Checklist