while loop over model turns and tool dispatch) instruments the same way as any app: initialize() once, wrap each run in a session, optionally mark units of work with task(). The usual gap is coverage: a missed call site produces no error, just missing data.
Inventory every LLM call site first
Before wiring anything, enumerate every place the codebase talks to a model. They are rarely all behind one shared client factory. Look for:- Official SDK clients (
openai,anthropic,@anthropic-ai/sdk), including ones pointed at another base URL (OpenRouter, vLLM, a proxy). These are auto-captured regardless of base URL: instrumentation patches the client, not the destination. - Raw HTTP calls to
chat/completions(common in agent frameworks and loops with their own HTTP layer). - Side paths that skip the shared client: a one-off titling or classification call, embeddings, provider batch-API usage, operator scripts and cron jobs.
- Background workers and queue consumers: separate processes, each with its own
initialize()requirement. See Background workers.
Check what is captured
Not every call style is captured; the table on Providers says which are. A client-SDK call site has exactly one capture path, so ifinitialize() didn’t run before that library was imported there is no HTTP-level fallback (init-order rule).
Place the session, then the tasks
Open onesession per agent run, at the loop’s entry point, with an id that is stable for the whole run (your run id or conversation id). Everything inside (every model turn, every tool span) links under it. task() is optional and marks sharper task boundaries:
initialize() runs once at process start, as in the Python quickstart. The Node shape is identical with withSession({ sessionId, userId }, fn) and task(name, fn); see SDK configuration.
Two session rules that bite custom loops specifically:
- Don’t nest sessions. Opening a
sessioninside another replaces the outer one’s context rather than merging with it. One session per run, opened once at the top. metadatakeys become span attributes, nothing more. Onlysession_idanduser_idare promoted to first-class fields; avoid metadata keys namedsession_idoruser_id, which would clobber the real ones.