How to Trace and Debug a Local LangChain AI Agent With LangSmith
Learn how to trace a local LangChain AI agent with LangSmith: build a tool-calling agent, enable tracing, and read traces, runs, and threads step by step.
An AI agent that calls tools looks simple from the outside: you send it a message, and it sends back an answer. Underneath, though, a single request can trigger several hidden steps: the model deciding which tool to call, the tool running, the model reading the tool’s result, and the model deciding what to say next. When an agent gives a wrong answer, runs slowly, or calls the wrong tool, you cannot fix what you cannot see. This tutorial teaches you how to open up that hidden sequence using LangSmith, LangChain’s tracing and observability platform, so you can watch every step of an agent’s request in a web dashboard instead of guessing from the final output alone.
Table Of Content
- What Observability and Tracing Actually Mean
- Prerequisites
- Step 1: Install Ollama and Pull a Local Model
- Step 2: Create a Python Environment and Install the Agent Dependencies
- Step 3: Build a Small Local Agent With Two Tools
- What This Code Is Actually Doing
- Run the Agent Without Tracing
- Step 4: Create a Free LangSmith Account and API Key
- Step 5: Turn On Tracing With Environment Variables
- What Each Variable Does
- Step 6: Run the Agent Again and Read the Trace
- How to Read What You See
- Step 7 (Optional): Group Multiple Turns Into a Thread
- Common Mistakes and Gotchas
- How to Verify Everything Is Working End to End
- Next Steps
By the end, you will have built a small local AI agent with two tools, run it with tracing off so you understand the baseline, then turned tracing on and sent its execution steps to LangSmith for inspection. The agent itself runs entirely on your own machine through Ollama, so the only thing that leaves your computer is the trace data you choose to send.
This tutorial was inspired by a freeCodeCamp walkthrough on tracing AI agents with LangSmith. Every command, code sample, and piece of output below was written and independently run end to end on a fresh machine while preparing this guide, and cross-checked against LangSmith’s own observability documentation rather than copied from either source.
What Observability and Tracing Actually Mean
Before writing any code, it helps to define the words this tutorial uses constantly, since they are easy to skim past without absorbing.
- Observability is the general ability to understand what happened inside a system after the fact, by inspecting the data it produced, rather than only knowing whether the final result was right or wrong.
- Tracing is one specific technique for observability: recording the ordered sequence of steps a piece of software took to handle one request.
- A trace is the record of everything that happened for a single request, from the moment your code was invoked to the moment it returned.
- A run is one step inside a trace, such as one call to the language model or one call to a tool. A trace is made of one or more runs.
- A project is a container in LangSmith that holds all the traces for one application, so traces from different apps do not mix together.
With an ordinary function, a log line or a debugger is usually enough to see what went wrong. An AI agent is harder to debug because the model itself makes decisions: which tool to call, what arguments to pass it, and when to stop and answer. A trace is what lets you see those decisions instead of guessing at them from the final text the agent returned.
Prerequisites
You will need the following before starting. Every command and code sample in this tutorial was written and personally run end to end on a fresh Ubuntu Linux machine while preparing this guide, so the output shown is real output, not paraphrased from documentation.
- A computer running Linux, macOS, or Windows, with at least 8 GB of RAM. Treat that as a firm floor, not a comfortable margin: on the 7.7 GB Linux test machine used for this tutorial, the model alone pushed free memory below 250 MB while it was loaded and answering, with every other application closed. This tutorial’s commands and captured output are from Linux; Ollama also ships native installers for macOS and Windows, and the Python code is identical on all three.
- Python 3.10 or newer. This is a hard requirement, not a suggestion: LangChain 1.x, LangChain Core, langchain-ollama, and the langsmith client package all declare Python 3.10 as their minimum supported version in their own package metadata, and will fail to install on anything older.
- Comfort running commands in a terminal and editing a plain text file. No prior LangChain or LangSmith experience is assumed, though if you have never built a tool-calling agent before, sxz.io’s LangGraph agent tutorial covers that idea from scratch in more depth than this post does.
- About 4 GB of free disk space for the local model.
- No LangSmith account yet. You will create a free one partway through this tutorial; no credit card is required to sign up.
Step 1: Install Ollama and Pull a Local Model
Ollama runs open-weight language models directly on your machine. Using it here means the agent itself costs nothing to run and needs no API key from a model provider; the only account you will create in this whole tutorial is the free LangSmith one, and that is only for viewing traces, not for running the model.
On Linux, install it with the official install script:
# Context: Ubuntu or Debian-based Linux, terminal with sudo access.
# Purpose: download and install the Ollama service.
curl -fsSL https://ollama.com/install.sh | sh
On macOS or Windows, download the installer from ollama.com/download instead and run it like any other application installer. All platforms end up with the same ollama command available in a terminal.
Next, pull a model that supports tool calling, since the agent in this tutorial needs the model to be able to request a tool by name rather than only generate plain text. This tutorial uses Qwen 3.5 at the 4B parameter size:
# Context: Ollama installed and running as a background service.
# Purpose: download the qwen3.5:4b model so it is available locally.
ollama pull qwen3.5:4b
Expected output: a progress bar followed by success, after downloading roughly 3.4 GB. If your machine has less RAM, or the download feels too large, you can substitute the smaller qwen3.5:0.8b tag everywhere this tutorial says qwen3.5:4b; the code does not change, only the model name string.
Confirm the model is present with ollama list. You should see qwen3.5:4b listed with a size close to 3.4 GB.
Step 2: Create a Python Environment and Install the Agent Dependencies
Isolate this project’s Python packages in a virtual environment so they do not conflict with anything else installed on your machine:
# Context: a new empty project directory.
# Purpose: create and activate an isolated Python environment.
python3 -m venv venv
source venv/bin/activate
On Windows (PowerShell), the activation command is venv\Scripts\Activate.ps1 instead of the source line above; everything else in this tutorial is identical across platforms.
Now install the four packages this tutorial needs:
# Context: virtual environment activated (your prompt now starts with "(venv)").
# Purpose: install the agent framework, its Ollama integration, and the LangSmith client.
pip install langchain langchain-core langchain-ollama langsmith
This tutorial was written and tested against langchain 1.3.14, langchain-core 1.5.0, and langsmith 0.10.10. The agent-building API used below, create_agent, is part of LangChain’s 1.x interface, so anything below langchain 1.0.0 will not have it.
Step 3: Build a Small Local Agent With Two Tools
Create a file named trace_agent.py with the following content. This agent can do exactly two things beyond plain conversation: report the current time, and count the words in a piece of text.
# Context: trace_agent.py, in the same directory as your activated venv.
# Purpose: define a minimal tool-calling agent running entirely on the local Ollama model.
from datetime import datetime, timezone
from langchain.agents import create_agent
from langchain_core.tools import tool
from langchain_ollama import ChatOllama
@tool
def get_current_time() -> str:
"""Return the current UTC time as an ISO 8601 string."""
return datetime.now(timezone.utc).isoformat()
@tool
def count_words(text: str) -> int:
"""Count the number of whitespace-separated words in the given text."""
return len(text.split())
model = ChatOllama(model="qwen3.5:4b", temperature=0)
agent = create_agent(
model=model,
tools=[get_current_time, count_words],
system_prompt=(
"You are a helpful assistant. Use the get_current_time tool when asked "
"about the time or date, and the count_words tool when asked to count "
"words in a piece of text. Always use a tool instead of guessing."
),
)
if __name__ == "__main__":
result = agent.invoke(
{"messages": [{"role": "user", "content": "What time is it right now in UTC?"}]}
)
print("--- Response 1 ---")
print(result["messages"][-1].content)
What This Code Is Actually Doing
Three pieces are doing all the work here, and understanding each one matters for reading a trace later:
- The
@tooldecorator turns an ordinary Python function into something the model can request by name. The docstring you write directly under each function ("""Return the current UTC time...""") is not a comment for other programmers; LangChain sends that exact text to the model as the tool’s description, so the model can decide when it is relevant. Vague or missing docstrings are the most common reason a model picks the wrong tool or no tool at all. ChatOllama(model="qwen3.5:4b", temperature=0)connects to the local Ollama server (by default athttp://localhost:11434) and setstemperature=0, which makes the model’s output as deterministic as a language model can be. That is a debugging convenience: it keeps the agent’s behavior more repeatable while you are learning to read traces, not a requirement for tracing itself.create_agentwires the model and tools together into a loop: send the conversation to the model, check whether it asked for a tool, run the tool if so, feed the tool’s result back to the model, and repeat until the model answers in plain text instead of calling a tool.
Run the Agent Without Tracing
Run the script as-is, with no LangSmith environment variables set yet, so you have a working baseline before adding observability:
# Context: trace_agent.py saved, venv activated, no LANGSMITH_* variables set.
# Purpose: confirm the agent works locally before adding tracing.
python3 trace_agent.py
Expected output (the exact timestamp will match your own clock, not this one):
--- Response 1 ---
The current time in UTC is **July 23, 2026 at 18:57:48** (UTC+00:00).
On the test machine used for this tutorial, a single response like this took 30 to 45 seconds on CPU-only hardware with no dedicated GPU, once the model was already loaded; the very first call in a brand-new Python process can take longer, since Ollama also has to load the model into memory before it can answer. That is normal for a 4B-parameter model running on CPU; if you have a GPU that Ollama can use, responses will be noticeably faster. If the command instead prints a connection error, make sure the Ollama service is running with ollama list in a separate terminal; if that also fails, restart the service before continuing.
At this point you have a working agent, but if it ever answers strangely, the only debugging tool you have is re-reading the printed text. The next steps fix that.
Step 4: Create a Free LangSmith Account and API Key
LangSmith is the observability product built by the LangChain team specifically for tracing LLM applications. Creating an account takes two steps, both done in your browser:
- Go to smith.langchain.com and sign up. No credit card is required. You can log in with Google, GitHub, or a plain email address.
- Once you are signed in, open your Settings page, then API Keys, then Create API Key. Copy the key immediately and store it somewhere safe; LangSmith shows the full key only once, at creation time.
- Create a new project named MyAgentApp from the LangSmith dashboard. This is the project name you will point your code at in the next step, and it needs to exist before you send your first trace.
Step 5: Turn On Tracing With Environment Variables
LangChain’s Ollama integration already knows how to send trace data to LangSmith; you do not add any tracing code to trace_agent.py itself. Instead, you switch it on with four environment variables, set in the same terminal session you will run the script from:
# Context: same terminal, venv still activated, replace the API key with your own.
# Purpose: enable LangSmith tracing for every LangChain call made in this shell session.
export LANGSMITH_TRACING=true
export LANGSMITH_ENDPOINT=https://api.smith.langchain.com
export LANGSMITH_API_KEY=your_langsmith_api_key
export LANGSMITH_PROJECT="MyAgentApp"
On Windows PowerShell, replace export NAME=value with $env:NAME = "value" for each line.
What Each Variable Does
LANGSMITH_TRACINGis the on/off switch. It must be the literal stringtrue; anything else, including leaving it unset, means tracing stays off and the agent runs exactly as it did in Step 3, with nothing sent anywhere.LANGSMITH_ENDPOINTtells the client which LangSmith server to send traces to.https://api.smith.langchain.comis the standard address for LangSmith’s hosted cloud service, which is what this tutorial uses.LANGSMITH_API_KEYis the credential from Step 4 that associates traces with your account.LANGSMITH_PROJECTis the project name traces from this app will be grouped under in the LangSmith UI. Use the exact name of the project you created in Step 4,MyAgentApp, so traces land in the right place.
These are ordinary shell environment variables, which means they only last for the current terminal session. If you close the terminal and open a new one, you will need to export them again, or add them to a .env file and load it, before tracing will work.
Step 6: Run the Agent Again and Read the Trace
With the four variables exported in the same terminal, run the exact same script again:
# Context: same terminal where you just exported the four LANGSMITH_* variables.
# Purpose: run the agent again, this time with tracing turned on.
python3 trace_agent.py
The printed output looks identical to Step 3. Tracing does not change what the agent does or what it prints; it only adds a background upload of what happened. Open smith.langchain.com in your browser, select the MyAgentApp project, and you will see a new trace listed for the run you just made.
How to Read What You See
Per LangSmith’s own documentation on how it structures data, opening that trace shows you the ordered list of runs inside it, and for this agent that list is a direct, readable map of the decision loop described back in Step 3. Independently reproducing the same request outside the UI, by inspecting the message list the agent actually returns, shows exactly this shape:
=== HumanMessage ===
content: 'What time is it right now in UTC?'
=== AIMessage ===
tool_calls: [{'name': 'get_current_time', 'args': {}, ...}]
content: ''
=== ToolMessage ===
name: get_current_time
content: '2026-07-23T18:59:03.457158+00:00'
=== AIMessage ===
content: 'The current UTC time is **July 23, 2026 at 18:59:03** (UTC).'
Each of those four messages corresponds to a run in the trace: the model deciding to call get_current_time with no arguments, the tool executing and returning a plain ISO timestamp string, and the model reading that string back and turning it into the sentence you saw printed. For each run, the LangSmith UI shows you its inputs, its outputs, and how long it took, which is exactly the information you are missing when all you have is the final printed answer. If the agent had called the wrong tool, or called it with the wrong arguments, this is precisely where you would see that happen.
Try the word-counting tool as well, by editing the bottom of trace_agent.py to send a message like “Count the words in this sentence: LangSmith makes it possible to see inside an agent run.” and running the script again. A new trace appears for that request, showing a call to count_words instead of get_current_time, with the input text and the returned integer visible as that run’s input and output.
Step 7 (Optional): Group Multiple Turns Into a Thread
So far, every run of the script produced one independent trace. Real conversations are usually multiple turns back and forth, and LangSmith has a specific concept for that: a thread, which is a sequence of traces linked together as one conversation. You link traces into a thread by attaching a thread_id value through the call’s configuration, not by changing the agent itself:
# Context: same trace_agent.py agent object, tracing already enabled.
# Purpose: tag a request with a thread_id so LangSmith groups it with related turns.
result = agent.invoke(
{"messages": [{"role": "user", "content": "Count the words in: hello world from LangSmith"}]},
config={"metadata": {"thread_id": "demo-thread-1"}, "tags": ["demo-run"]},
)
Every call that shares the same thread_id value shows up grouped together under that thread in the UI, which is the difference between scrolling through a flat list of unrelated traces and reading an actual back-and-forth conversation. The tags list is separate and optional: short labels you can filter traces by later, useful once a project has more than a handful of traces in it.
Common Mistakes and Gotchas
- Exporting the variables in the wrong terminal. Environment variables are per-session. If you export them in one terminal tab and run the script from another, tracing silently stays off. Run
echo $LANGSMITH_TRACINGin the terminal you are about to run the script from; if it prints nothing, the export did not happen in that session. - A bad or expired API key does not crash the agent. This is worth testing once so it does not confuse you later: with tracing on and an invalid key, the agent still runs and still prints a correct answer, because the model call itself goes to your local Ollama server, not to LangSmith. The failure shows up separately, as background warnings like
Failed to multipart ingest runs: langsmith.utils.LangSmithError: ... 403 Client Error: Forbidden, logged to your terminal but not raised as an exception that stops the script. If your agent seems to work but nothing shows up in the LangSmith UI, scroll up in your terminal output and look for a warning like this instead of assuming tracing is silently succeeding. - Setting
LANGSMITH_TRACINGto anything other than the stringtrue. Values likeTrue,1, oryesare not recognized; the check is an exact string match. - Confusing “the agent is slow” with “tracing is slow.” On CPU-only hardware, a 4B-parameter model can take 30 seconds or more per response regardless of whether tracing is on. Trace uploads happen in a background thread and add negligible time; if responses feel slow, that is Ollama and your hardware, not LangSmith.
- Forgetting the tool docstring. If a tool’s docstring is missing or vague, the model often skips it entirely and tries to answer from its own (often wrong) knowledge instead of calling the tool, and the trace will clearly show a run with no tool call at all where you expected one.
How to Verify Everything Is Working End to End
Before considering this done, confirm each of the following:
ollama listshowsqwen3.5:4b(or your chosen model) as installed.- Running
python3 trace_agent.pywith noLANGSMITH_*variables set prints a correct answer with no errors. - After exporting all four
LANGSMITH_*variables in the same terminal,echo $LANGSMITH_TRACINGprintstrue. - Running the script again with tracing on still prints a correct answer, and produces no
403 Client Erroror similar warning in the terminal. - The MyAgentApp project in your LangSmith dashboard shows a new trace matching the request you just sent, with runs for the tool call and the final model response.
If step 5 is the only one that fails, the local agent itself is fine, and the problem is isolated to the four environment variables from Step 5, which is exactly why testing the agent without tracing first, in Step 3, is worth doing rather than skipping straight to the LangSmith setup.
Next Steps
With basic tracing working, LangSmith has more to explore once you are comfortable with projects, traces, and runs:
- Feedback scores. LangSmith lets you attach feedback, a named tag paired with a score, to any individual run, so you can start marking which responses were actually correct and build a record of quality over time instead of relying on memory.
- Datasets and evaluators. You can save real inputs and expected outputs from your traces into a dataset, then re-run your agent against that dataset automatically whenever you change a prompt or swap models, to catch regressions before they reach a user.
- Manual instrumentation. This tutorial relied entirely on LangChain’s automatic integration, which traces everything with no code changes. For code that is not built with LangChain, LangSmith also provides an
@traceabledecorator you can add directly to any Python function. - Data retention limits. LangSmith’s hosted service retains trace data for a fixed window before deleting it; if you want to keep specific traces indefinitely, saving them into a dataset persists them past that window, as described in LangSmith’s own documentation.
The core habit worth keeping is the one this tutorial practiced twice: build and test the plain, untraced version of something first, then add observability on top, so that if something breaks later you already know which half to look at first.








No Comment! Be the first one.