Skip to content

Commit 8ad9626

Browse files
authored
Merge pull request #2033 from dgageot/board/tell-me-when-the-tool-run-background-age-da00ac7d
feat: make background_agents a standalone toolset
2 parents c206962 + 3fe12f2 commit 8ad9626

File tree

12 files changed

+208
-49
lines changed

12 files changed

+208
-49
lines changed

agent-schema.json

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -801,7 +801,8 @@
801801
"lsp",
802802
"user_prompt",
803803
"openapi",
804-
"model_picker"
804+
"model_picker",
805+
"background_agents"
805806
]
806807
},
807808
"instruction": {
@@ -998,7 +999,8 @@
998999
"a2a",
9991000
"lsp",
10001001
"user_prompt",
1001-
"model_picker"
1002+
"model_picker",
1003+
"background_agents"
10021004
]
10031005
}
10041006
}
@@ -1082,6 +1084,13 @@
10821084
]
10831085
}
10841086
]
1087+
},
1088+
{
1089+
"properties": {
1090+
"type": {
1091+
"const": "background_agents"
1092+
}
1093+
}
10851094
}
10861095
]
10871096
},

docs/concepts/multi-agent/index.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,50 @@ transfer_task(
4545

4646
</div>
4747

48+
## Parallel Delegation with Background Agents
49+
50+
`transfer_task` is **sequential** — the coordinator waits for the sub-agent to finish before continuing. When you need to fan out work to multiple agents at the same time, use the `background_agents` toolset instead.
51+
52+
Add it to your coordinator’s toolsets:
53+
54+
```yaml
55+
agents:
56+
root:
57+
model: anthropic/claude-sonnet-4-0
58+
description: Research coordinator
59+
sub_agents: [researcher, analyst, writer]
60+
toolsets:
61+
- type: think
62+
- type: background_agents
63+
```
64+
65+
The coordinator can then:
66+
67+
1. **Dispatch** several tasks at once with `run_background_agent` — each returns a task ID immediately
68+
2. **Monitor** progress with `list_background_agents` or `view_background_agent`
69+
3. **Collect** results once tasks complete
70+
4. **Cancel** tasks that are no longer needed with `stop_background_agent`
71+
72+
```bash
73+
# Start two tasks in parallel
74+
run_background_agent(agent="researcher", task="Find recent papers on LLM agents")
75+
run_background_agent(agent="analyst", task="Analyze our current architecture")
76+
77+
# Check on all tasks
78+
list_background_agents()
79+
80+
# Read results when ready
81+
view_background_agent(task_id="agent_task_abc123")
82+
```
83+
84+
<div class="callout callout-tip">
85+
<div class="callout-title">💡 When to use which
86+
</div>
87+
<p><strong><code>transfer_task</code></strong> — simple, sequential delegation. Best when the coordinator needs the result before deciding what to do next.</p>
88+
<p><strong><code>background_agents</code></strong> — parallel, async delegation. Best when multiple independent tasks can run simultaneously.</p>
89+
90+
</div>
91+
4892
## Example: Development Team
4993

5094
```yaml

docs/configuration/tools/index.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,31 @@ toolsets:
180180

181181
The `transfer_task` tool is automatically available when an agent has `sub_agents`. Allows delegating tasks to sub-agents. No configuration needed — it's enabled implicitly.
182182

183+
### Background Agents
184+
185+
Dispatch work to sub-agents concurrently and collect results asynchronously. Unlike `transfer_task` (which blocks until the sub-agent finishes), background agent tasks run in parallel — the orchestrator can start several tasks, do other work, and check on them later.
186+
187+
```yaml
188+
toolsets:
189+
- type: background_agents
190+
```
191+
192+
| Operation | Description |
193+
| ------------------------ | ------------------------------------------------------------------------------------------------ |
194+
| `run_background_agent` | Start a sub-agent task in the background; returns a task ID immediately |
195+
| `list_background_agents` | List all background tasks with their status and runtime |
196+
| `view_background_agent` | View live output or final result of a task by ID |
197+
| `stop_background_agent` | Cancel a running task by ID |
198+
199+
No configuration options. Requires the agent to have `sub_agents` configured so the background tasks have agents to dispatch to.
200+
201+
<div class="callout callout-tip">
202+
<div class="callout-title">💡 Tip
203+
</div>
204+
<p>Use <code>background_agents</code> when your orchestrator needs to fan out work to multiple specialists in parallel — for example, researching several topics simultaneously or running independent code analyses side by side.</p>
205+
206+
</div>
207+
183208
### LSP (Language Server Protocol)
184209

185210
Connect to language servers for code intelligence: go-to-definition, find references, diagnostics, and more.

e2e/testdata/cassettes/TestA2AServer_MultiAgent.yaml

Lines changed: 21 additions & 21 deletions
Large diffs are not rendered by default.

examples/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,4 @@ These examples are groups of agents working together. Each of them is specialize
5656
| [finance.yaml](finance.yaml) | Financial research and analysis | | | || | [duckduckgo](https://hub.docker.com/mcp/server/duckduckgo/overview) ||
5757
| [shared-todo.yaml](shared-todo.yaml) | Shared todo item manager | | || | | ||
5858
| [pr-reviewer-bedrock.yaml](pr-reviewer-bedrock.yaml) | PR review toolkit (Bedrock) ||| | | | ||
59+
| [background_agents.yaml](background_agents.yaml) | Parallel research with background agents | | | || | [duckduckgo](https://hub.docker.com/mcp/server/duckduckgo/overview) ||

examples/background_agents.yaml

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
#!/usr/bin/env docker agent run
2+
3+
# This example demonstrates the background_agents toolset, which lets a
4+
# coordinator dispatch work to multiple sub-agents in parallel rather than
5+
# waiting for each one sequentially.
6+
#
7+
# The coordinator fans out research tasks to two specialists at the same time,
8+
# monitors their progress, and synthesises a final answer once both are done.
9+
10+
agents:
11+
root:
12+
model: anthropic/claude-sonnet-4-0
13+
description: Research coordinator that dispatches work in parallel
14+
instruction: |
15+
You are a research coordinator. When the user asks a question:
16+
17+
1. Break the question into independent research tasks.
18+
2. Dispatch each task to the appropriate sub-agent using
19+
`run_background_agent` so they run in parallel.
20+
3. Use `list_background_agents` or `view_background_agent` to check
21+
progress and collect results.
22+
4. Synthesise the findings into a clear, well-structured answer.
23+
24+
Always run independent tasks concurrently — do not wait for one to
25+
finish before starting the next.
26+
sub_agents: [web_researcher, code_analyst]
27+
toolsets:
28+
- type: think
29+
- type: background_agents
30+
31+
web_researcher:
32+
model: openai/gpt-4o
33+
description: Searches the web for information and summarises findings
34+
instruction: |
35+
You are a web researcher. Use the search tools to find relevant,
36+
up-to-date information. Provide concise summaries with sources.
37+
toolsets:
38+
- type: mcp
39+
ref: docker:duckduckgo
40+
41+
code_analyst:
42+
model: anthropic/claude-sonnet-4-0
43+
description: Analyses local code and provides technical insights
44+
instruction: |
45+
You are a code analyst. Read the codebase to answer technical
46+
questions. Reference specific files and line numbers in your answers.
47+
toolsets:
48+
- type: filesystem
49+
- type: shell

pkg/config/latest/validate.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,8 @@ func (t *Toolset) validate() error {
152152
if len(t.Models) == 0 {
153153
return errors.New("model_picker toolset requires at least one model in the 'models' list")
154154
}
155+
case "background_agents":
156+
// no additional validation needed
155157
}
156158

157159
return nil

pkg/runtime/loop.go

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,27 +24,21 @@ import (
2424
"github.com/docker/docker-agent/pkg/telemetry"
2525
"github.com/docker/docker-agent/pkg/tools"
2626
"github.com/docker/docker-agent/pkg/tools/builtin"
27-
agenttool "github.com/docker/docker-agent/pkg/tools/builtin/agent"
2827
)
2928

30-
// bgAgentHandler wraps a background-agent method as a ToolHandlerFunc.
31-
func (r *LocalRuntime) bgAgentHandler(fn func(context.Context, *session.Session, tools.ToolCall) (*tools.ToolCallResult, error)) ToolHandlerFunc {
32-
return func(ctx context.Context, sess *session.Session, tc tools.ToolCall, _ chan Event) (*tools.ToolCallResult, error) {
33-
return fn(ctx, sess, tc)
34-
}
35-
}
36-
3729
// registerDefaultTools wires up the built-in tool handlers (delegation,
3830
// background agents, model switching) into the runtime's tool dispatch map.
3931
func (r *LocalRuntime) registerDefaultTools() {
4032
r.toolMap[builtin.ToolNameTransferTask] = r.handleTaskTransfer
4133
r.toolMap[builtin.ToolNameHandoff] = r.handleHandoff
4234
r.toolMap[builtin.ToolNameChangeModel] = r.handleChangeModel
4335
r.toolMap[builtin.ToolNameRevertModel] = r.handleRevertModel
44-
r.toolMap[agenttool.ToolNameRunBackgroundAgent] = r.bgAgentHandler(r.bgAgents.HandleRun)
45-
r.toolMap[agenttool.ToolNameListBackgroundAgents] = r.bgAgentHandler(r.bgAgents.HandleList)
46-
r.toolMap[agenttool.ToolNameViewBackgroundAgent] = r.bgAgentHandler(r.bgAgents.HandleView)
47-
r.toolMap[agenttool.ToolNameStopBackgroundAgent] = r.bgAgentHandler(r.bgAgents.HandleStop)
36+
37+
r.bgAgents.RegisterHandlers(func(name string, fn func(context.Context, *session.Session, tools.ToolCall) (*tools.ToolCallResult, error)) {
38+
r.toolMap[name] = func(ctx context.Context, sess *session.Session, tc tools.ToolCall, _ chan Event) (*tools.ToolCallResult, error) {
39+
return fn(ctx, sess, tc)
40+
}
41+
})
4842
}
4943

5044
// finalizeEventChannel performs cleanup at the end of a RunStream goroutine:

pkg/teamloader/registry.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"github.com/docker/docker-agent/pkg/tools"
2121
"github.com/docker/docker-agent/pkg/tools/a2a"
2222
"github.com/docker/docker-agent/pkg/tools/builtin"
23+
agenttool "github.com/docker/docker-agent/pkg/tools/builtin/agent"
2324
"github.com/docker/docker-agent/pkg/tools/mcp"
2425
)
2526

@@ -77,6 +78,7 @@ func NewDefaultToolsetRegistry() *ToolsetRegistry {
7778
r.Register("user_prompt", createUserPromptTool)
7879
r.Register("openapi", createOpenAPITool)
7980
r.Register("model_picker", createModelPickerTool)
81+
r.Register("background_agents", createBackgroundAgentsTool)
8082
return r
8183
}
8284

@@ -347,3 +349,7 @@ func createModelPickerTool(_ context.Context, toolset latest.Toolset, _ string,
347349
}
348350
return builtin.NewModelPickerTool(toolset.Models), nil
349351
}
352+
353+
func createBackgroundAgentsTool(_ context.Context, _ latest.Toolset, _ string, _ *config.RuntimeConfig, _ string) (tools.ToolSet, error) {
354+
return agenttool.NewToolSet(), nil
355+
}

pkg/teamloader/teamloader.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ import (
2727
"github.com/docker/docker-agent/pkg/team"
2828
"github.com/docker/docker-agent/pkg/tools"
2929
"github.com/docker/docker-agent/pkg/tools/builtin"
30-
agenttool "github.com/docker/docker-agent/pkg/tools/builtin/agent"
3130
"github.com/docker/docker-agent/pkg/tools/codemode"
3231
)
3332

@@ -473,7 +472,7 @@ func getToolsForAgent(ctx context.Context, a *latest.AgentConfig, parentDir stri
473472
}
474473

475474
if len(a.SubAgents) > 0 {
476-
toolSets = append(toolSets, builtin.NewTransferTaskTool(), agenttool.NewToolSet())
475+
toolSets = append(toolSets, builtin.NewTransferTaskTool())
477476
}
478477
if len(a.Handoffs) > 0 {
479478
toolSets = append(toolSets, builtin.NewHandoffTool())

0 commit comments

Comments
 (0)