bento.begin(...) or @bento.interaction. Every track_ai and tool_span call made while it’s open joins that trajectory.
When to use what
Reach forbento.begin(...) when you have a multi-step agent run and want every step grouped under one row in the dashboard. When you make a single LLM call with no surrounding agent loop, bento.track_ai(...) is enough on its own. You don’t need begin for that case, because a bare track_ai is already a one-span trajectory.
Opening a trajectory
- Context manager (recommended)
- Decorator
- Imperative
How children attach
Spans emitted while a trajectory is open become its children automatically. The parent context lives in a PythonContextVar, so it follows your code through:
- Synchronous calls in the same thread
async/awaitin the same taskasyncio.create_task(...)(the new task inherits the active context at creation)
Updating mid-flight
update is additive. Existing properties are preserved unless you overwrite them by key. finish(output=, properties=) is a final update plus closing the span.
Lifecycle rules
Nested trajectories finish in reverse order. The context manager handles this for you; an out-of-orderfinish() raises RuntimeError. Calling finish() twice on the same trajectory is a no-op the second time. Both begin and track_ai detach from any outer OTel context, so customer FastAPI or Django spans won’t become parents of Bento spans by accident.
Span kinds inside a trajectory
A trajectory contains spans of any kind. The two the SDK emits directly:- LLM call (default kind):
bento.track_ai(...). Carriesgen_ai.*attributes. - Tool call:
bento.tool_span(...),interaction.tool_span(...), or@bento.tool. Carriesopeninference.span.kind="tool".
track_ai spans and the tool_span all parent to the trajectory.
What a trajectory is not
- Not a session. A session is the set of trajectories that share a
convo_id. See Sessions and users. - Not a request. One HTTP request can open zero, one, or many trajectories.
- Not opened by an integration.
bento.instrument()captures spans inside whatever turn you’ve opened withbento.begin(...), but it never opens a trajectory on its own. You decide where each turn begins and ends.