Hello, world¶
The smallest complete RunAgent pipeline: one AgentEnvelope created
in-process, keyed by entity_key, processed by an agent that makes a single
model call and completes. This is the fast path — the activation runs to
completion inside one process() call, with no suspension, no side-effect
intents, and no state carried between elements.
The model is a scripted [FakeLLM], so the pipeline runs offline with no API
keys: one match_any() rule answers every request with the greeting. Swapping
make_provider for a factory returning a real provider client is the only
change a production pipeline needs.
Run it¶
It prints the single terminal output and exits:
The whole program¶
The code below is included verbatim from
examples/hello_world.py
— the same file
tests/examples/test_hello_world.py
executes in CI, asserting exactly what this page claims.
"""The smallest complete `RunAgent` pipeline: one event, one model call, one output.
One `AgentEnvelope` enters, keyed by `entity_key`; the agent awaits a single
model call (served by a scripted, offline `FakeLLM`) and completes. Everything
here is the fast path — no suspension, no side effects, no state carried
between elements.
Run it offline, with no credentials and no docker:
uv run python -m examples.hello_world
Every function a pipeline references is module-level so the DirectRunner can
pickle it by reference.
"""
from __future__ import annotations
import apache_beam as beam
from beam_agents import AgentConfig, RunAgent, RunAgentOutputs
from beam_agents._protos import AgentEnvelope
from beam_agents.core.agent import Complete
from beam_agents.core.context import ActivationContext
from beam_agents.model.client import LlmRequest
from beam_agents.model.fake import FakeLLM, match_any, respond_with
# The scripted response, and therefore the pipeline's one documented output.
GREETING = b"Hello from the beam-agents runtime!"
def make_provider() -> FakeLLM:
"""A deterministic stand-in for a real provider: every request gets `GREETING`.
Swapping this factory for one that returns a real `LLMClient` is the only
change a production pipeline needs.
"""
return FakeLLM([(match_any(), respond_with(GREETING))])
async def greeter(ctx: ActivationContext) -> Complete:
"""One model call, then complete: the whole fast path.
`ctx.call_model` is cache-first — a retried bundle replays the cached
response instead of paying for a second provider call.
"""
response = await ctx.call_model(
LlmRequest(
model_id="fake-model",
messages=[ctx.single_event.decode()],
tools_schema=None,
sampling_params=None,
)
)
return Complete(output=response.response)
def build(pipeline: beam.Pipeline) -> RunAgentOutputs:
"""Wire the minimal pipeline: one envelope, keyed upstream, into `RunAgent`."""
envelope = AgentEnvelope(entity_key=b"user-1", event_time_ms=1_000, external_event=b"hello")
keyed = (
pipeline
| "OneEvent" >> beam.Create([envelope])
# RunAgent takes a pre-keyed PCollection[KV[bytes, AgentEnvelope]]; the
# caller keys by entity_key, exactly as the documented dataflow shape does.
| "KeyByEntity"
>> beam.WithKeys(lambda env: env.entity_key).with_output_types(tuple[bytes, AgentEnvelope])
)
return keyed | RunAgent(greeter, config=AgentConfig(provider_factory=make_provider))
def main() -> None:
"""Run the example pipeline, printing each decision to stdout."""
with beam.Pipeline() as pipeline:
outputs = build(pipeline)
outputs.output | "Print" >> beam.Map(print)
if __name__ == "__main__":
main()
What the output means¶
RunAgent exposes four named outputs. Here only the main one carries anything:
.output— exactly one element: the scripted response the agent returned viaComplete(output=...)..intents— empty: the agent staged no side effects..errors— empty: the activation committed successfully..traces— the activation's trace events (oneLLM_CALLamong them); see trace delivery.
The one subtlety worth taking away: ctx.call_model is cache-first. The
response is staged in the keyed replay cache and committed atomically with the
bundle, so if the runner retries this bundle the activation replays from cache
with zero additional provider calls — the property the retry-determinism gate
pins for every pipeline, demonstrated here at its smallest.