skip to content

Tracing & Runs

How a run tree gets built and shipped: one decorator on your functions, or automatic capture when you use LangChain. Knowing the run types and how to attach metadata is what makes traces searchable later.

on this pageshow

questions

6

How do you enable LangSmith tracing for a plain Python function with @traceable?

level: juniorimportance: must knowfreq 78%

answer

  1. one decorator, a few environment variables
  2. tracing is off until you switch it on
  3. LANGSMITH_TRACING, API_KEY, PROJECT
  4. @traceable wraps any Python function
  5. children nest under the traced caller

basics

~10 s

Set LANGSMITH_TRACING=true and LANGSMITH_API_KEY in the environment, then decorate the function with @traceable from the langsmith package. Each call is logged as a run in the LANGSMITH_PROJECT project, nested under any traced caller.

solid answer

~40 s

Tracing is off unless you switch it on, so the first step is environment: `LANGSMITH_TRACING="true"` is the switch, `LANGSMITH_API_KEY` authenticates, and `LANGSMITH_PROJECT` picks the project runs land in (otherwise `default`). Then `from langsmith import traceable` and put `@traceable` on any function — it does not have to be LangChain code, it can be a plain function, an async function or a generator. Each invocation becomes a **run** recording inputs, outputs, latency and errors; if a traced function calls another traced function, the child nests under the parent and the whole thing shows up as one trace tree. You can pass `name=`, `run_type=`, `tags=` and `metadata=` to the decorator. With the switch off, `@traceable` is a near no-op: the function runs normally and nothing is sent.

code

python · 20 lines
python
import os
from langsmith import traceable

os.environ["LANGSMITH_TRACING"] = "true"
os.environ["LANGSMITH_API_KEY"] = "lsv2_pt_example"
os.environ["LANGSMITH_PROJECT"] = "checkout-assistant"


@traceable(run_type="tool", name="lookup_order")
def lookup_order(order_id: str) -> dict:
    return {"order_id": order_id, "status": "shipped"}


@traceable(name="answer_question")
def answer_question(question: str) -> str:
    order = lookup_order("A-1001")
    return f"Your order is {order['status']}."


print(answer_question("where is my order?"))

go deeper

for a junior

Be able to name the switch (LANGSMITH_TRACING=true), the key, the project variable, and show the one-line @traceable decorator on an ordinary Python function.

for a middle

Explain how nesting happens without you wiring it — the active run lives in a context variable, so a traced callee attaches to the traced caller — and what the decorator captures as inputs and outputs.

for a senior

Show that you know delivery is batched on a background thread, and that short-lived or serverless processes need an explicit flush. Be ready to walk a missing-trace diagnosis from env vars to egress.

for a principal

Own the decision of where the tracing boundary sits: which services are instrumented, which project each writes to, and how tracing config is delivered and rotated across environments without leaking API keys.

## What a run and a trace are LangSmith's storage unit is a **run**: one recorded execution of one unit of work, carrying a name, a run type, inputs, outputs, start/end timestamps, an error field, tags and metadata. Runs form a tree — a run started while another run is active becomes its child. The whole tree, identified by the root run, is what the UI calls a **trace**. A trace lives inside a **project**, which is just a named bucket you filter and monitor at. ## The environment switches The current spelling of the variables is the `LANGSMITH_*` family: - `LANGSMITH_TRACING` — set to `"true"` to turn tracing on. Nothing is sent without it. - `LANGSMITH_API_KEY` — the key that authenticates to the platform. - `LANGSMITH_PROJECT` — the project runs are written to; absent, runs go to `default`. - `LANGSMITH_ENDPOINT` — the API base URL, which you change for the EU region or for a self-hosted install. An older generation of names (`LANGCHAIN_TRACING_V2`, `LANGCHAIN_API_KEY`, `LANGCHAIN_PROJECT`, `LANGCHAIN_ENDPOINT`) is still honoured for compatibility, but it is the superseded spelling — write the `LANGSMITH_*` names in new code and treat the old ones as something you migrate away from when you find them in an existing repo. ## The decorator ``` from langsmith import traceable @traceable def answer(question: str) -> str: ... ``` That is the whole integration for hand-written code. The decorator captures the call's keyword/positional arguments as the run's inputs and the return value as its outputs, serialising both to JSON. It supports sync functions, `async def` functions, generators and async generators; for a streaming generator you can pass `reduce_fn` so the yielded chunks are folded into a single output value rather than stored as a list of fragments. Useful decorator arguments: - `name=` — the display name, defaulting to the function name. - `run_type=` — the classification (`"chain"` by default; also `"llm"`, `"tool"`, `"retriever"`, `"prompt"`, `"parser"`, `"embedding"`). - `tags=` / `metadata=` — labels and key/value data used later for search. - `project_name=` — send this function's runs to a project other than `LANGSMITH_PROJECT`. - `process_inputs=` / `process_outputs=` — callables that transform the payload before it is recorded. Per call, you can also pass `langsmith_extra={"metadata": {...}, "tags": [...]}` as a keyword argument to the decorated function; the decorator strips it out before your function sees it. ## Nesting and manual trees Nesting is implicit: the decorator keeps the active run in a context variable, so a traced function called from inside another traced function attaches as its child automatically. `get_current_run_tree()` returns the active `RunTree` when you need the run's id — for example to attach feedback to it later. If you need full manual control, `RunTree` from `langsmith.run_trees` lets you construct runs yourself: create the root, `create_child(...)`, `end(outputs=...)`, and `post()`/`patch()` to ship them. Almost nobody needs this in application code; the decorator covers the ordinary cases. ## Errors If the decorated function raises, LangSmith records the error on the run, marks it as errored, and re-raises. This is deliberate: the failing calls are exactly the ones you want in the trace store, and a tracing decorator that swallowed exceptions would be a much worse bug than a missing trace. ## Delivery is asynchronous The SDK does not block your request on an HTTP call to LangSmith — runs are batched and shipped from a background thread. Two consequences bite people. First, in a short-lived process (a serverless handler, a CLI script) the process can exit before the batch is flushed and the runs simply never arrive; call `Client().flush()` — or make sure the same client instance stays alive and is flushed — before returning. Second, a run appears in the UI a second or two after it happened, which is normal and not a symptom of misconfiguration. ## Common mistakes Forgetting `LANGSMITH_TRACING` entirely and concluding the decorator is broken; setting the variables in a local shell but not in the deployed environment; assuming `@traceable` only works for LangChain objects (LangChain and LangGraph are traced automatically once the switch is on, but the decorator is for arbitrary Python); and expecting inputs that are not JSON-serialisable to survive — pass `process_inputs` to convert them, or you get a best-effort string representation.

  • What happens to the run if the decorated function raises an exception?
    The decorator records the exception on the run, marks the run as errored so it is filterable in the project view, ends it with the duration up to the failure, and re-raises so your own error handling is unaffected. Errored runs are usually the most valuable ones in the store, which is why tracing never swallows the exception.
  • Your Lambda handler returns successfully but the run never appears in LangSmith. What do you check?
    First that the environment variables are actually set in the deployed function, not just locally. Then the flush: the SDK batches runs on a background thread, and a serverless container can freeze or exit before the batch ships. Call `Client().flush()` before returning, reusing the same client the traced code uses. Finally, confirm egress to the configured `LANGSMITH_ENDPOINT` is permitted.
  • How do you send one function's runs to a different project from the rest of the service?
    Pass `project_name=` to `@traceable` for a permanently separate destination, or wrap the call in `tracing_context(project_name="...")` when the routing is per request — for example sending internal canary traffic to its own project so it does not mix with production runs. Both override `LANGSMITH_PROJECT` for the runs they cover.

saying these in an interview costs you the question

  • Thinks importing langsmith is enough to start tracing
  • Believes @traceable only works on LangChain objects
  • Quotes LANGCHAIN_TRACING_V2 as the current variable name
  • Assumes a function that raises produces no run
  • Expects runs from a process that exits immediately to arrive

context

open as a page

What does LangSmith's wrap_openai add that a plain @traceable does not?

level: middleimportance: must knowfreq 66%

basics

~20 s

wrap_openai patches an OpenAI client so every model call becomes an llm run carrying the exact messages, model parameters and token usage the API returned — which is what LangSmith needs to show token counts and cost. A plain @traceable records only your function's inputs and outputs.

open as a page

In LangSmith, what does the run_type on @traceable actually change?

level: middleimportance: should knowfreq 52%

basics

~20 s

run_type classifies a run — chain (the default), llm, tool, retriever, prompt, parser or embedding. It changes how LangSmith renders the run, whether tokens and cost are attributed to it, and it becomes a filter dimension in the project view and in list_runs.

open as a page

How do tags and metadata make LangSmith runs findable, and where do you set them?

level: middleimportance: should knowfreq 58%

basics

~20 s

Tags are short labels for coarse slicing (environment, variant); metadata is arbitrary key/value data for identifiers (user id, prompt version, session). Set either on @traceable, per call via langsmith_extra, or for a whole request with tracing_context. Both are searchable in the project view and via Client.list_runs filters.

open as a page

Prompts traced to LangSmith contain PII — how do you keep content out of runs?

level: seniorimportance: should knowfreq 45%

basics

~20 s

Strip payloads before they leave the process: construct langsmith.Client(hide_inputs=True, hide_outputs=True) — or set LANGSMITH_HIDE_INPUTS/LANGSMITH_HIDE_OUTPUTS — to drop them wholesale, or pass process_inputs/process_outputs to @traceable to redact selected fields. Structure, latency, errors and token counts still ship.

open as a page

At high request volume, how do you decide which LangSmith runs to trace at all?

level: principalimportance: should knowfreq 34%

basics

~20 s

Sample at the root: LANGSMITH_TRACING_SAMPLING_RATE keeps a fraction of traces whole, and tracing_context(enabled=...) lets code decide per request from cheap signals. Trace internal, canary and high-value traffic fully, sample the bulk, and route classes of traffic to separate projects.

open as a page