Skip to content
All work
2026Solo — agent design, backend, UI

ChatDPT Agent

A LangGraph agent with real tools — search the web, check a calendar, stream the thinking

Source-available · web UI and CLI

Node.js · Express 5 · LangChain · LangGraph · Groq · Tavily · Zod · React 19 · Vite · SSE streaming

Recorded session, tool calls included

LangGraph state

Recorded session — replayed

Press play to replay the recorded session

01

Context

ChatDPT Agent is a personal assistant built on LangGraph instead of a single prompt. It decides when to search the web with Tavily, when to touch a calendar tool, and when to just answer — and streams its progress into a React chat UI so you can watch the graph work.

The goal wasn't another chat wrapper. It was to build the tool-calling loop by hand, on a graph I can inspect and extend — alongside a set of small state-graph demos (including a graph that walks through cooking biryani) used to verify how state moves between nodes.

02

The hard parts

01

Tool calling as an explicit graph — and streaming both tokens, not just text

Problem
With a black-box agent executor you can't see why the model chose a tool, and a chat UI that only streams text can't show that a tool is running — the interface goes silent exactly when the agent is doing the most interesting thing.
Decision
The agent is a LangGraph StateGraph: model node, conditional edge to a tools node, tools back to the model, until a final answer. The server exposes the same run over SSE, emitting typed events — token, tool_start, tool_end — so the client renders tool activity as first-class UI, not as text the model happened to write.
Tradeoff
Graph nodes mean the agent's control flow is code I own and must maintain, including the loop guards that stop a model from calling tools forever. The alternative — a one-line executor — would have hidden precisely the part I wanted to understand.
Outcome
Every answer is explainable: the transcript shows which tools ran, with what input, in what order, and the state diagram in the UI lights up with the same sequence.

02

Typed tools, so a hallucinated argument fails in the agent — not in production

Problem
LLMs produce plausible but wrong tool arguments. An unvalidated argument reaches the tool and fails halfway through, confusing both the model and the user.
Decision
Every tool declares a Zod schema with a description the model can read; arguments are parsed at the graph boundary. A bad argument becomes a structured tool error that the model can see and correct on the next loop instead of an exception.
Tradeoff
Strict schemas occasionally reject inputs a lenient parser would have salvaged, so descriptions matter more — the schema is documentation the model depends on. That's a good forcing function, not a cost.
Outcome
Tool failures are recoverable turns in a conversation, not stack traces — and the same tool definitions are reusable from the CLI without changes.

03

What I'd change

Next: evaluating the agent instead of trusting it — a small suite of tool-choice scenarios run on every change, because 'it seemed smart in testing' is not an evaluation strategy.

04

Stack

Node.js · Express 5 · LangChain · LangGraph · Groq · Tavily · Zod · React 19 · Vite · SSE streaming