A trajectory is one run of your agent: from the user’s prompt to your final response, with every model call, tool call, retry, and planning step in between grouped together. In the dashboard, a trajectory is one row. Click into it and you see the full step-by-step. You open one withDocumentation Index
Fetch the complete documentation index at: https://docs.bentolabs.ai/llms.txt
Use this file to discover all available pages before exploring further.
bento.begin(...) or @bento.interaction. Every track_ai and tool_span call made while it is open joins that trajectory.
When to use what
| Use this | When |
|---|---|
bento.track_ai(...) | One LLM call. No surrounding agent loop. |
bento.begin(...) | A multi-step agent run. You want every step grouped under one row in the dashboard. |
If you only make one model call per request, you don’t need
begin. 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
LIFO finish order
LIFO finish order
Nested trajectories must be finished in reverse order. The context manager handles this for you. Out-of-order
finish() raises RuntimeError.Idempotent finish
Idempotent finish
Calling
finish() twice on the same trajectory is a no-op the second time.Detached parent
Detached parent
Both
begin and track_ai detach from any outer OTel context. Customer FastAPI or Django spans will not become parents of BentoLabs spans by accident.Span kinds inside a trajectory
A trajectory contains spans of any kind. The two emitted directly by the SDK:- 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 auto-instrumented. The SDK does not patch OpenAI, Anthropic, or LangChain. You decide what becomes a span.