Skip to content

Lab 03 · Agent System

AI Engineering StudioLabs · ⏱ ~2–3 hours · Intermediate · Cost: $0

You'll build an agent — an LLM that decides its own next steps and calls tools to get things done — using the production-default hub-and-spoke orchestrator-worker pattern in LangGraph, with one tool reached over MCP (Model Context Protocol). Provider-agnostic; local Ollama by default (see Choosing a Model Backend).

Three-layer reading model. The steps are the main track. Context boxes add SE framing; go-deeper pointers link the detail; the close is how you'd explain it to a customer.

Use a capable model. Agents lean on the model's tool-routing. llama3.1:8b or a hosted model routes reliably; very small models (3B) are hit-or-miss.

What you build

PartFileWhat it teaches
MCP tool servermcp_server.pyExposing a system to an agent over MCP
The agentagent.pyA hub-and-spoke graph: orchestrator routes to worker tools
MCP checkmcp_check.pyVerifying MCP wiring with no LLM

Architecture

flowchart TD
  START(["user question"]) --> O["Orchestrator (LLM + tools)"]
  O -->|calls a tool| W["Workers (ToolNode)"]
  W -->|word_count| LT["local tool"]
  W -->|lookup_order| MCP["MCP server (orders)"]
  W --> O
  O -->|done| E(["final answer"])

The orchestrator decides, each turn, whether to answer or call a worker. The workers are the tools — one local Python tool and one reached over MCP. Results return to the orchestrator, which decides what to do next. That loop is the hub-and-spoke pattern.

What an SE says about this

"The orchestrator — how the request is broken into steps — is the single biggest reliability decision in an agent. When a customer asks 'how reliable is the agent?', they're really asking about the orchestrator, not the model."

Prerequisites

  • Python 3.10+ — required by the MCP adapter (langchain-mcp-adapters). macOS's built-in python3 is often 3.9; see Before you start to get a 3.10+ venv.
  • A chat backend — see Choosing a Model Backend. Prefer a capable model (llama3.1:8b local, or a hosted tier). Function calling must work (Llama 3.1+, Qwen 2.5, any Groq/OpenAI model).
  • Lab 01 (function calling) recommended first.

Quick Start

First time in the labs? Do the one-time setup — a Python virtualenv, and picking a model backend — then come back here.

bash
cd labs/03-agent-system
make setup        # install deps (langgraph, langchain-openai, langchain-mcp-adapters, mcp, dotenv)
make env          # create .env (defaults to local Ollama)
make mcp-check    # confirm the MCP server exposes its tool (no LLM needed)
make run Q="What is the status of order A100?"

Detailed Setup

Step 1 · The MCP tool

Open mcp_server.py. It's a tiny MCP server exposing one tool, lookup_order. In the real world that tool body would query your order database or API — the agent never touches that system directly, it goes through the MCP server. Confirm it works with make mcp-check (lists the tool without involving an LLM).

What an SE says about this

"MCP is how an agent safely reaches your internal systems — a uniform, auditable boundary. Your code decides what the tool exposes; the agent only gets to call it."

Step 2 · The orchestrator graph

Open agent.py. The graph has two nodes: an orchestrator (the model with tools bound to it) and a tools node (the workers). A conditional edge sends the orchestrator to the tools when it emits a tool call, then loops back. When the orchestrator answers without a tool call, the graph ends.

Go deeper This is the standard LangGraph tool-agent shape. Concepts (state, nodes, edges, hub-and-spoke) are in LangGraph in 10 Minutes; the tool loads over MCP via langchain-mcp-adapters, which launches the server and exposes its tools as LangChain tools.

Step 3 · Run it and read the trace

make run prints the full message trace: the human question, the orchestrator's tool call, the tool result, and the final grounded answer. Try a question that needs a tool ("status of order C300?") and one that doesn't ("what is 2+2?") to watch the orchestrator route differently.

Go deeper Whether a task even needs an agent (vs. RAG or a fixed workflow) is the Do We Even Need an Agent? frame — agents cost 3–10× more calls, so earn them.

Project Structure

labs/03-agent-system/
├── README.md          # this file
├── Makefile           # env, setup, run, mcp-check, clean
├── requirements.txt   # langgraph, langchain-openai, langchain-mcp-adapters, mcp, dotenv
├── .env.example       # chat backend config
├── provider.py        # provider-agnostic LangChain chat model
├── mcp_server.py      # the MCP tool server (an 'orders' system)
├── agent.py           # the hub-and-spoke LangGraph agent
└── mcp_check.py       # list MCP tools (no LLM)

Troubleshooting

SymptomLikely causeFix
connection refusedOllama not runningStart Ollama, or switch to a hosted backend in .env
Agent answers without calling the toolSmall model routing poorlyUse llama3.1:8b or a hosted model
mcp-check hangs or errorsMCP server didn't launchEnsure deps installed; run python mcp_server.py alone (it should wait on stdio)
Loops or repeats tool callsWeak model over-callingUse a stronger model; lower temperature (already 0)

Cleanup

bash
make clean   # remove Python caches

Cost

$0–negligible. All deps are pure-Python (no PyTorch); local Ollama is free; the MCP server is a local process. A hosted tier (Groq free / OpenAI pennies) is optional and recommended if your local model routes unreliably.

Explain it to a customer

"We built an assistant that doesn't just answer — it decides what to do and calls your systems to do it, through a controlled, auditable connection (MCP). A coordinator breaks the request into steps and hands each to the right tool, then assembles the answer. The reliability lives in that coordinator, which is exactly what we design and test — and we only reach for an agent when the task genuinely needs it."

Next steps