LLMWithBackend pairs a conversational frontend LLM with a backend LLM that handles work needing tools or careful reasoning: it goes where the LLM goes in your pipeline, runs the backend as an agent of its own, and passes the messages between the two, so there is no bus or job code to write. The rest of this page shows how to build agents of your own.
LLMWorker overview
Multi-agent systems often run several LLM-backed agents — a greeter, a support agent, a researcher — each with its own instructions, tools, and (optionally) its own conversation context.LLMWorker is the building block for each one. It extends PipelineWorker with everything you need to run an LLM-powered agent:
- A pipeline with your LLM service, automatically built
- Tool registration via the
@tooldecorator - Activation handling that injects messages and runs the LLM
LLMWorker so you can host @tool methods, then instantiate it with its own LLM service. Pass bridged=() so the agent receives frames from the bus:
bus= argument to the constructor — the worker gets its bus when you register it with runner.add_workers(...). The default pipeline is Pipeline([llm]), with tools from build_tools() automatically registered. When bridged=() is set, the framework wraps this pipeline with edge processors that connect it to the bus.
The @tool decorator
The@tool decorator marks a method as an LLM-callable tool. The framework automatically collects all @tool-decorated methods and registers them with the LLM service.
Args section in the docstring.
Tool options
The@tool decorator accepts options:
Tool parameters
Every tool method receivesself and params: FunctionCallParams as the first two arguments. Additional arguments are the tool’s parameters that the LLM fills in.
The params object gives you access to:
params.result_callback(result)— return the result to the LLMparams.llm— the LLM service instance, useful for queuing frames
Returning results
Always callparams.result_callback() to return the tool result to the LLM:
Activation with messages
When anLLMWorker is activated, you can inject messages into its context. Pass an LLMWorkerActivationArgs via the args parameter:
on_activated() implementation:
- Sets the tools from
build_tools() - Injects the provided messages into the LLM context
- Runs the LLM if
run_llmisTrue(the default whenmessagesis set)
Managing context with LLMContextWorker
A plainLLMWorker runs an LLM but doesn’t manage conversation context on its own — it relies on context coming from elsewhere (for example, the main agent’s aggregators bridged in). When an agent needs to keep its own history, use LLMContextWorker. It extends LLMWorker with a built-in LLMContext and the user/assistant aggregator pair, building the pipeline as [user_aggregator, llm, assistant_aggregator] for you.
LLMContextWorker gets its own context by default, so agents don’t see each other’s history. To give several agents a shared conversation, pass the same context= to each:
self.user_aggregator and self.assistant_aggregator.
Custom pipelines
If you need more control, you can pass a custompipeline= to the LLMWorker constructor. For example, to add TTS to the agent’s own pipeline:
What’s next
Now that your agents can run LLMs and call tools, here’s a powerful one: an agent that sees and drives the user’s screen.Controlling the UI
A UIWorker that reads the screen and acts on it over a two-way RTVI interface