How do you enable LangSmith tracing for a plain Python function with @traceable?
answer
- one decorator, a few environment variables
- tracing is off until you switch it on
- LANGSMITH_TRACING, API_KEY, PROJECT
- @traceable wraps any Python function
- children nest under the traced caller
basics
~10 sSet 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 sTracing 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 linesimport 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
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.
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.
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.
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