The system.json Specification
If agent.json defines a single worker, system.json is the factory floor. It orchestrates multiple agents working together in sequence, parallel, or via conditional logic.
The Docker Compose Analogy
Think of agent.json like a Dockerfile, and system.json like docker-compose.yml. Systems are just wiring; the logic lives in the agents.
Referencing Agents
Systems must reference agents that already exist. You can reference them by their deployed ID or by their source file path if deploying a monorepo.
// By ID (already deployed)
{ "agent": "id:researcher-agent-v2" }
// By Source Path
{ "agent": "path:./agents/researcher.json" }Complete System Example
{
"name": "content-pipeline",
"description": "Researches a topic and writes a blog post.",
"version": "1.0",
"config": {
"max_concurrency": 5,
"on_failure": "stop"
},
"agents": {
"researcher": { "agent": "path:./agents/researcher.json" },
"writer": { "agent": "path:./agents/writer.json" }
},
"flow": {
"type": "sequence",
"steps": [
{
"id": "step_research",
"agent": "researcher",
"input_template": "Research the following topic: \{\{ system.input }}"
},
{
"id": "step_write",
"agent": "writer",
"input_template": "Write a 500-word blog post based on these notes: \{\{ step_research.output }}"
}
]
}
}The flow Section
This is the most complex part of a system. It defines the topology of the execution graph.
Sequential Flows
Execute agents one after another, passing the output of step N as the input to step N+1.
Parallel Flows
Execute multiple agents simultaneously. Perfect for divergent tasks like researching multiple sub-topics at once.
"flow": {
"type": "parallel",
"entry": ["agent_a", "agent_b", "agent_c"],
"merge_strategy": "concat" // merge strategies: 'concat', 'summarise', 'vote'
}Conditional Routing
Route execution paths dynamically based on an agent's output. Note: A default route is mandatory.
"flow": {
"type": "router",
"agent": "classifier_agent",
"conditions": [
{
"if_output_contains": "technical",
"route_to": "senior_engineer_agent"
},
{
"if_output_contains": "billing",
"route_to": "billing_support_agent"
}
],
"default_route": "general_support_agent"
}Data Mapping
The input_template field uses standard Handlebars mustache syntax. You can reference data from:
\{\{ system.input }}- The string passed when calling the system API endpoint.\{\{ step_id.output }}- The final string output from a specific previous step.\{\{ env.VARIABLE_NAME }}- An environment variable.
Validation Rules
When you run savine deploy, the platform runs a topological sort to validate your graph. It checks for:
- Cyclic dependencies: DAGs strictly enforced, loops return
ValidationError. - Dangling references: Referencing an agent not declared in the
"agents"object. - Missing default routes: Every router must have a fallback path.
Example Systems
We maintain a library of common architectures:
- Research Pipeline (Sequence)
- Triage and Route (Router)
- Multi-perspective Analysis (Parallel with Vote merge)
- Human-in-the-loop Approval (Suspends execution pending API callback)