DS DevShelfHub Projects · AI tools
Cheatsheets / LangSmith
Cheatsheet · AI frameworks

LangSmith Cheatsheet: Tracing, Datasets and Evaluation

By DevShelfHub

LangSmith is LangChain's observability and evaluation platform: every chain and agent run is traced automatically, letting you inspect each step's inputs, outputs, latency, and token usage. This cheatsheet covers tracing setup, the Client API, dataset creation, evaluators, feedback logging, the Prompt Hub, and automated testing patterns for LLM applications in production.

63 items 6 min Tracing Datasets Evals

LangSmith is LangChain's observability, evaluation, and prompt management platform for LLM applications. When you set the LANGCHAIN_TRACING_V2=true environment variable, every LangChain chain, agent, or LLM call is automatically traced — no instrumentation code required. Each trace captures the full call tree: inputs and outputs at every node, latency per step, token counts, model parameters, and any intermediate tool calls or retrieval steps. This makes debugging a misbehaving agent in production as straightforward as finding the run in the LangSmith UI and expanding the call tree.

Beyond tracing, LangSmith provides a dataset and evaluation framework: you can create datasets of input/output pairs (either manually or by saving interesting traces), then run evaluators against them — LLM-as-judge, exact-match, regex, or custom Python functions. This closes the development loop: you improve a prompt, run the evaluator suite, and compare scores across runs without writing a separate test harness. The Prompt Hub stores versioned, shareable prompt templates so your team has a single source of truth instead of scattered strings in code.

The Python Client (langsmith package) exposes the full API surface: creating and querying runs, uploading datasets, running evals, posting human feedback, and triggering automated tests in CI. For privacy-sensitive deployments, self-hosted LangSmith or open-source alternatives like Langfuse give you the same trace visibility without sending data to LangChain's cloud. This cheatsheet covers the daily LangSmith surface: SDK setup, run querying, dataset management, evaluators, feedback, and the Prompt Hub.

Start hereQuick start · 6 you’ll reach for daily

EnableLANGSMITH_TRACING=true
Trace fn@traceable
Wrap clientwrap_openai(OpenAI())
Datasetls.create_dataset("name")
Evaluateevaluate(target, data=…)
Feedbackls.create_feedback(run_id, …)

Target versions · paceVersions

Targets: langsmith ≥ 0.3 python ≥ 3.9 langchain ≥ 0.3 (optional)

LangSmith works without LangChain — @traceable and wrap_openai instrument any Python code. The env-var prefix is LANGSMITH_*; the old LANGCHAIN_* vars still work but are aliases. Names current as of May 2026. Self-hosted endpoint URL overrides via LANGSMITH_ENDPOINT.

install · env · connectSetup

bash
# SDK + tracing helpers
pip install -U langsmith
pip install -U langchain                         # optional — auto-traced when LANGSMITH_TRACING=true

# env — the only required vars
export LANGSMITH_API_KEY=ls-...
export LANGSMITH_TRACING=true                    # auto-trace LangChain / LangGraph
export LANGSMITH_PROJECT="my-app-prod"           # bucket traces by env / version
export LANGSMITH_ENDPOINT=https://api.smith.langchain.com   # self-host -> your URL

# Quick connectivity check
python - <<'PY'
from langsmith import Client
print(Client().info)                              # 200 + tenant info if wired up
PY

where things liveCommon imports

Top-level helpers in langsmith; eval utilities under langsmith.evaluation; SDK wrappers under langsmith.wrappers.

from langsmith import Client, traceable, get_current_run_treeCore API + decorator.
from langsmith.evaluation import evaluate, aevaluate, LangChainStringEvaluatorEval entry points + prebuilt evaluators.
from langsmith.wrappers import wrap_openai, wrap_anthropicAuto-trace whole SDK clients.
from langsmith.run_helpers import traceContext-manager form: with trace(name=…) as run:.
from langsmith.schemas import Example, Run, FeedbackDataclasses for read APIs.
from langsmith.async_client import AsyncClientAsync REST client.
from langsmith import RunTreeManual run construction. Use for custom integrations.

capture every callTracing

@traceableWrap any function. Nested calls become child runs automatically.
@traceable(name=…, tags=[…], metadata={…})Attach searchable metadata.
@traceable(run_type="llm")Mark as LLM call — tokens + cost get computed.
wrap_openai(OpenAI())Preferred Trace every OpenAI call without sprinkling decorators.
wrap_anthropic(Anthropic())Same idea for Anthropic.
with trace(name="step") as run:Imperative form. Set run.outputs = … before exit.
get_current_run_tree()Inside a traced call: grab the run for metadata / feedback / tags.
run.add_tags([…]) / run.add_metadata({…})Mutate during execution.
LANGSMITH_TRACING=trueMaster switch. Set to false to disable in tests.
LANGSMITH_PROJECT=my-projectBucket runs — usually one project per env (dev / staging / prod).
LANGSMITH_SAMPLE_RATE=0.1Sample traces in prod. Float in [0, 1].
python
from langsmith import traceable, get_current_run_tree
from openai import OpenAI

client = OpenAI()

@traceable(name="summarise", tags=["v2"], metadata={"model": "gpt-4o-mini"})
def summarise(text: str) -> str:
    """One traced unit of work. Nested @traceable calls become child runs."""
    resp = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[
            {"role": "system", "content": "Summarise in one sentence."},
            {"role": "user",   "content": text},
        ],
    )
    # Attach extra signal to the current run for filtering in the UI
    run = get_current_run_tree()
    run.add_metadata({"input_chars": len(text)})
    return resp.choices[0].message.content

# Wrap an entire OpenAI client so EVERY call becomes a child run
from langsmith.wrappers import wrap_openai
client = wrap_openai(OpenAI())             # now untraceable calls are traced too

read & mutate runsClient API

Client()Authed from env vars.
ls.list_runs(project_name=…, run_type="llm")Generator over matching runs.
ls.list_runs(filter="eq(name, ‘summarise’)")Query DSL. AND / OR / time filters supported.
ls.read_run(run_id)Fetch one run + inputs / outputs / metadata.
ls.update_run(run_id, tags=[…])Patch a run after the fact.
ls.share_run(run_id)Get a public share URL.
ls.create_feedback(run_id, key="thumbs", score=1)Attach user / eval feedback.
ls.list_feedback(run_ids=[…])Pull feedback for analysis.
ls.infoTenant info + SDK version sanity check.

curated examplesDatasets

ls.create_dataset("name", description=…)New dataset.
ls.create_examples(inputs=[…], outputs=[…], dataset_id=…)Bulk-add examples.
ls.create_example(inputs={…}, outputs={…}, dataset_name=…)Add one example.
ls.list_examples(dataset_name=…, splits=["train","test"])Iterate examples, optionally by split.
ls.update_examples(example_ids=[…], inputs=[…])Bulk update.
ls.add_runs_to_dataset(dataset_id, runs=[…])Add real traces to a dataset (after thumbs-up).
ls.upload_csv("data.csv", input_keys=…, output_keys=…)Bulk import from CSV / JSONL.
ls.list_dataset_versions(dataset_name=…)Datasets are versioned — pin via as_of.

measure qualityEvaluation

evaluate(target, data="name", evaluators=[…])Run a target over a dataset; UI gets a comparison view.
await aevaluate(target, …)Async variant for async targets / evaluators.
evaluate(…, experiment_prefix="v2")Pin a name; future runs append the timestamp.
evaluate(…, max_concurrency=8)Parallelise. Match to your rate limits.
evaluate(…, num_repetitions=3)Run each example N times to estimate variance.
def eval_fn(run, example) -> dict:Custom evaluator. Return {key, score, comment?}.
LangChainStringEvaluator("qa")Prebuilt: QA correctness. Other names: cot_qa, criteria.
def summary_eval(runs, examples) -> dict:Aggregate-level evaluator. Compute precision / recall across all runs.
evaluate(…, summary_evaluators=[…])Attach aggregate evaluators.
evaluate(…, blocking=False)Fire-and-forget — returns immediately with experiment URL.
python
from langsmith import Client
from langsmith.evaluation import evaluate, LangChainStringEvaluator

ls = Client()

# 1 · Build / fetch a dataset
ds = ls.create_dataset("faq-golden", description="Hand-curated FAQ Q&A")
ls.create_examples(
    inputs=[{"question": "How do I reset my password?"}],
    outputs=[{"answer":   "Go to Settings > Security."}],
    dataset_id=ds.id,
)

# 2 · Define the system under test
def target(inputs: dict) -> dict:
    return {"answer": my_chain.invoke(inputs["question"])}

# 3 · Custom evaluator (any callable returning {key, score, comment?})
def exact_match(run, example) -> dict:
    return {
        "key":   "exact_match",
        "score": run.outputs["answer"].strip() == example.outputs["answer"].strip(),
    }

# 4 · Run the evaluation — UI gets a comparison view
results = evaluate(
    target,
    data="faq-golden",
    evaluators=[exact_match, LangChainStringEvaluator("qa")],
    experiment_prefix="v2-prompt",
    max_concurrency=8,
)
print(results.experiment_name)

signals from users & judgesFeedback

ls.create_feedback(run_id, key="correctness", score=1.0)Numeric score, 0-1 by convention.
ls.create_feedback(…, value="thumbs_up")Categorical value.
ls.create_feedback(…, comment="…")Free-text annotation.
ls.create_feedback_from_token(token, score=…)Use a signed token from a public share URL (browser-side feedback).
ls.create_presigned_feedback_token(run_id, key=…)Mint such a token server-side.
key naming conventionReuse the same key for human + automated feedback to stack on one chart.
Feedback aggregates by key in the UI. Decide your key taxonomy early — e.g. correctness, groundedness, user_thumbs. Renaming later splits charts in two.

versioned promptsPrompt hub

ls.push_prompt("org/name", object=template)Publish a ChatPromptTemplate / PromptTemplate.
ls.pull_prompt("org/name")Fetch the latest version.
ls.pull_prompt("org/name:abc123")Pin by commit hash.
ls.list_prompts(query=…, is_public=False)Search prompts.
ls.like_prompt / unlike_promptBookmark public prompts.
ls.delete_prompt("org/name")Remove. Versions are soft-deleted.

trace · feedback · evalEnd-to-end · instrumented bot

Auto-traced OpenAI client, prompt pulled from the hub, thumbs-up feedback, nightly regression eval — the pieces every prod LLM app eventually needs.

python
# Trace, attach feedback, run an eval against a versioned prompt — end to end.
import os
from langsmith import Client, traceable
from langsmith.evaluation import evaluate
from langsmith.wrappers import wrap_openai
from openai import OpenAI

os.environ.setdefault("LANGSMITH_TRACING", "true")
os.environ.setdefault("LANGSMITH_PROJECT", "support-bot")

ls = Client()
oai = wrap_openai(OpenAI())                     # every call is traced

# Pull a prompt from the hub (server-side versioned)
prompt = ls.pull_prompt("my-org/support-faq")    # ChatPromptTemplate-like

@traceable(name="answer")
def answer(question: str) -> str:
    msgs = prompt.invoke({"question": question}).to_messages()
    resp = oai.chat.completions.create(model="gpt-4o-mini", messages=msgs)
    return resp.choices[0].message.content

# Attach user feedback to the latest run (e.g. thumbs-up button)
from langsmith.run_helpers import get_current_run_tree
def thumbs_up():
    run = get_current_run_tree()
    ls.create_feedback(run.id, key="user_thumbs", score=1)

# Periodic regression eval
def target(inputs): return {"answer": answer(inputs["question"])}
def has_link(run, ex): return {"key": "has_link", "score": "http" in run.outputs["answer"]}

evaluate(target, data="faq-golden", evaluators=[has_link], experiment_prefix="nightly")

Best practiceGood to know

One project per environment. Set LANGSMITH_PROJECT=app-prod / app-staging. Mixing envs in one project drowns prod traces in test noise.
Add datasets from production traces. ls.add_runs_to_dataset turns thumbs-down runs into your next golden set. Beats hand-curating a fictional eval set.
Use experiment_prefix + git SHA for evals. f"{branch}-{sha[:7]}" makes A/B comparison in the UI trivial. Skip it and you’re hunting by timestamp.

Common trapsWatch out for

Traces include inputs + outputs by default. That means PII can leave your VPC. Use hide_inputs / hide_outputs on @traceable, or self-host.
LANGSMITH_TRACING is process-global. A library deep in your dependency tree turning it on can fire traces you didn’t plan to pay for. Pin it explicitly in entry points.
Custom evaluators must be deterministic to compare experiments. An LLM-as-judge evaluator at temperature>0 will give you a noisy delta. Pin temperature=0 or sample multiple times and average.

Go deeperSee also

LangSmith FAQ

What is LangSmith?

LangSmith is LangChain's observability and evaluation platform for LLM applications. It captures every LLM call, chain run, and agent step as a trace, lets you build and version datasets, run automated evaluations, collect human feedback, and manage prompts in a centralised hub. It works with any LLM framework, not just LangChain.

How do I enable tracing in LangSmith?

Set LANGCHAIN_TRACING_V2=true and LANGCHAIN_API_KEY in your environment. All LangChain and LangGraph runs are traced automatically with no code changes. For custom code outside LangChain, wrap functions with the @traceable decorator or use the RunTree client to log spans manually to any LangSmith project.

How do I create evaluation datasets in LangSmith?

Create a dataset with client.create_dataset(name=...) then add examples with client.create_examples(). Each example is an input/output pair (or input-only for reference-free evaluation). You can also create datasets directly from traced runs in the UI by selecting runs and clicking Add to Dataset, which makes it easy to capture real production failures.

What are LangSmith evaluators?

Evaluators are functions that score a model output against a reference or on its own. Built-in evaluators include LLM-as-judge (using a model to grade responses), exact match, and string containment. Run evaluations with client.evaluate(), which applies each evaluator to every example in a dataset and produces a summary report with pass rates and average scores.

What is the LangSmith Prompt Hub?

The Prompt Hub is a versioned registry for prompt templates. Push a prompt with client.push_prompt(name, object=prompt_template) and pull the latest version in your code with hub.pull(name). Versioning lets you compare prompt iterations, roll back to a previous version, and share prompts across team members without hardcoding them in application code.