Supervisor agent: one-shot workflows#363
Conversation
|
Some requests!
|
|
At this stage, we've plugged in the workflow agent and can get a similar answer via the supervisor and the original standalone workflow_chat service. For a simple task that only requires one pass through the workflow agent, the token consumption increase in the agentic version is 2x input tokens and 3x output tokens (maybe an underestimate, given the basic prompt of the supervisor). This is our baseline cost without any optimisations. |
Add lightweight router to global agent
|
TODOs:
The weaknesses of this implementation right now are:
Basically planning works well for the cases we've tested, but we don't know about other cases, and because of changes to the input, non planning steps won't well. Ways forward:
|
|
Here's the next steps:
|
|
Structured outputs are deprecated now, so I reverted back to Sonnet 4.5 as some changes are needed across services. |
Short Description
Adds a global agent — a supervisor-style orchestration layer that sits in front of
workflow_chatandjob_chat. It accepts a single unified payload from the frontend and intelligently routes requests to the right subagent(s), or escalates to a multi-step planner when the task requires coordination across both.Current testing focus: One-shot workflow generation from scratch (when the user doesn't have an existing workflow). Other scenarios (editing existing workflows, job-level chat, multi-turn conversations) have not been tested end-to-end and may not work correctly.
To test use
pytest global_agent/tests/test_planner_multistep.py -v -sFixes #333 (done without this code being merged)
Fixes #398
Fixes #404
Implementation Details
Architecture
The global agent uses a two-tier dispatch model:
Router (
router.py) — A fast, cheap Claude Haiku call classifies the user request into one of three destinations:workflow_agent,job_code_agent, orplanner. Uses a constrained JSON generation trick (pre-filled assistant turn'{"destination": "') to force deterministic structured output. On any routing failure, defaults toplanner.Planner (
planner.py) — A Claude Sonnet tool-calling loop (up to 20 iterations) that can orchestrate multiple subagent calls in sequence. The planner sees a redacted version of the workflow YAML (job bodies replaced with# [use inspect_job_code to view]) to keep context small, and maintains a livecurrent_yamlstate that gets stitched after each subagent call.Direct routes — For simple requests (e.g., "edit this job's code"), the router bypasses the planner entirely and calls
workflow_chat.main()orjob_chat.main()directly as in-process Python function calls.Key Design Decisions
workflow_yamlstring is the single state carrier between turns and between agents. The planner mutates a local copy during its loop, stitching code in after each subagent call, and returns the final state as an attachment.call_workflow_agentandcall_job_agentalways passhistory: []. The planner encodes all necessary context in themessagefield of each tool call, treating subagents as stateless specialists.New Files
global_agent/global_agent.pyglobal_agent/router.pyglobal_agent/planner.pyglobal_agent/subagent_caller.pyglobal_agent/config.yamlglobal_agent/prompts.yamlglobal_agent/tools/tool_definitions.pyglobal_agent/yaml_utils.pyglobal_agent/PAYLOAD_SPEC.mdsearch_documentation/search_documentation.pyChanges to Existing Services
streaming_util.py: Simplified streaming utilitiesutil.py: Addedsum_usage()for token aggregation across agents,search_documentation_tool()helperload_adaptor_docs: Minor adjustmentsTests
Tests in
global_agent/tests/covering:Changes needed in Lightning
This is a new service with a new API. Changes are needed in Lightning if we want to experiment with this. See: OpenFn/lightning#4532
AI Usage
Please disclose how you've used AI in this work (it's cool, we just want to know!):
You can read more details in our Responsible AI Policy