Quick Start
Get the whole platform — six agents, Postgres, Redis, and the web UI — running locally. Docker is the only hard requirement. You do not need Python, .NET, Node, or a paid API key installed to run it.
Pick the section for your machine. macOS and Linux can use the helper script; Windows should use the Docker Compose commands directly, since scripts/dev.sh is a bash script.
1. Get the code and configure it
Identical everywhere:
git clone https://github.com/nitin27may/e-commerce-agents.git
cd e-commerce-agents
cp .env.example .env
On Windows PowerShell, the last line is copy .env.example .env.
Then open .env and set a model provider. Any one of these works — see Run without a paid API key if you would rather not use a paid one:
LLM_PROVIDER=openai
OPENAI_API_KEY=sk-...
2. Run it
macOS and Linux
./scripts/dev.sh
The script builds the images, waits for Postgres to become healthy, runs the seeder as a one-shot job, then starts the agents and the frontend. It also prints a summary of every URL at the end.
Windows
scripts/dev.sh is a bash script and will not run in PowerShell or cmd. You have two paths, and they are not equal — pick based on whether you want WSL2 on your machine.
Recommended: WSL2
WSL2 gives the best experience, and if you already run Docker Desktop you are most likely using its WSL2 backend anyway, so this adds no new moving parts. Everything works exactly as documented for macOS and Linux — the helper script, the --clean/--seed-only flags, all of it:
wsl # drop into your Linux distro
git clone https://github.com/nitin27may/e-commerce-agents.git
cd e-commerce-agents
cp .env.example .env
./scripts/dev.sh
Two things to get right:
- Clone inside the Linux filesystem (
~/e-commerce-agents), not under/mnt/c/. Bind-mounting across the Windows filesystem boundary is dramatically slower and is the usual answer to “why is my container so slow on WSL2”. - Enable WSL integration in Docker Desktop — Settings → Resources → WSL Integration — for the distro you are using, or
dockerwill not be found inside WSL.
Git Bash (bundled with Git for Windows) also runs the script, but only WSL2 gives you a real Linux filesystem, so container startup is much faster there.
Not using WSL2? PowerShell works fine
There is a PowerShell script that does everything dev.sh does — same profiles, same ordering, same health checks, same flags:
git clone https://github.com/nitin27may/e-commerce-agents.git
cd e-commerce-agents
Copy-Item .env.example .env
notepad .env # set OPENAI_API_KEY, then save and close
./scripts/dev.ps1
If PowerShell refuses to run it (running scripts is disabled on this system), that is the execution policy, not the script. Either allow local scripts once — Set-ExecutionPolicy -Scope CurrentUser RemoteSigned — or bypass it for this run alone with powershell -ExecutionPolicy Bypass -File .\scripts\dev.ps1.
Or skip the script entirely. Nothing here needs one; docker compose is the same command on every platform, and these three lines are what the script does, minus the health polling and the closing summary:
docker compose up -d db redis aspire
docker compose --profile seed run --rm seeder
docker compose --profile agents --profile frontend up -d --build
Then open http://localhost:3000.
You do not need to wait between those commands: the seeder declares depends_on: db: {condition: service_healthy}, so Compose blocks it until Postgres passes its health check. The first run builds images, so expect a few minutes before anything responds.
PowerShell differences worth knowing:
| Instead of | Use |
|---|---|
cp .env.example .env | Copy-Item .env.example .env |
\ at end of line (continuation) | a backtick `, or put it all on one line |
./scripts/dev.sh --clean | ./scripts/dev.ps1 -Clean — or docker compose down -v, then re-run |
./scripts/dev.sh --seed-only | ./scripts/dev.ps1 -SeedOnly — or docker compose --profile seed run --rm seeder |
./scripts/dev.sh --infra-only | ./scripts/dev.ps1 -InfraOnly — or docker compose up -d db redis aspire |
./scripts/dev.sh --dotnet | ./scripts/dev.ps1 -Dotnet |
lsof -i :3000 (port conflicts) | netstat -ano \| findstr :3000 |
docker compose logs -f orchestrator | identical — Compose commands don’t change |
dev.ps1 needs PowerShell 7+ on macOS and Linux (install docs); on Windows the built-in Windows PowerShell 5.1 is enough. On macOS and Linux dev.sh remains the more idiomatic choice — the two are interchangeable.
To stop everything: docker compose down. To stop and wipe the database as well: docker compose down -v.
If you use Git Bash rather than PowerShell and
./scripts/dev.shfails withbad interpreter: /bin/bash^M, that is Git’score.autocrlfrewriting the script to CRLF on checkout, not a broken script. The repo ships a.gitattributespinning*.shto LF, so a fresh clone is fine; an older clone needsgit rm --cached -r . && git reset --hardto pick it up.
One-liner, any platform
If you would rather not run the steps separately, this starts everything including the seeder:
docker compose --profile seed --profile agents --profile frontend up --build
3. Open it
| What | URL |
|---|---|
| Web app | http://localhost:3000 |
| Orchestrator API | http://localhost:8080 |
| Aspire dashboard (traces) | http://localhost:18888 |
Sign in with any seeded account — alice.johnson@gmail.com / customer123 is a customer with order history, which makes the demo scenarios more interesting than a fresh account. The full list is in the README’s Test Users table.
You can also browse the catalog and use the shopping assistant at /shop without signing in — product discovery is served anonymously.
Run the .NET backend instead
Same database, same prompts, same frontend — a different compose file. Only one stack can run at a time, because both bind the same ports.
# macOS / Linux
./scripts/dev.sh --dotnet
# Any platform, Compose directly
docker compose -f docker-compose.dotnet.yml \
--profile seed --profile agents --profile mcp --profile frontend up --build
On Windows PowerShell, put the whole command on one line, or use a backtick (`) for line continuation instead of the backslash.
Run without a paid API key
Nothing above requires an OpenAI subscription. Any OpenAI-compatible endpoint works through the same code path — set LLM_BASE_URL and leave LLM_PROVIDER=openai:
# Ollama — fully local, no account, no key, no rate limit
LLM_PROVIDER=openai
LLM_BASE_URL=http://localhost:11434/v1
OPENAI_API_KEY=ollama # any non-empty string — Ollama doesn't check it
LLM_MODEL=qwen2.5:14b # must be a tool-calling-capable model — see below
Start the model first, and raise the context window — this is the single most common cause of a local run behaving worse than a hosted one:
ollama pull qwen2.5:14b
OLLAMA_CONTEXT_LENGTH=64000 ollama serve
Ollama defaults to a 4K context on machines with under 24 GiB of VRAM. An agent loop accumulating tool results passes 4K within a few turns, and Ollama then silently discards the oldest messages — starting with the system prompt — with no error and nothing in the response to tell you. The symptom is a confident, well-formed, wrong answer. Ollama’s own documentation recommends at least 64000 tokens for agent workloads.
Check that your local model can actually call tools. Every specialist here depends on tool-calling, and the failure is quiet: a model with unreliable function-calling stops calling tools and starts inventing product names and prices instead of erroring.
Measured 2026-08-21 on a 2-tool, multi-turn loop (check stock, compute the shortfall, restock to the reorder point):
qwen2.5:14b,gemma4:12bandqwen3.5:9ball passed — correct tool sequence, correct arithmetic, clean termination. That is one scenario, not a benchmark: treat it as evidence that the 9B-and-up class is viable here, not as a ranking.If answers look plausible but the agent timeline shows no tool calls, that is the symptom of a model that cannot hold up.
The second silent failure: reasoning models can answer with nothing at all.
qwen2.5:14bis the recommended default because it emits no thinking trace. Reasoning models interleave a long internal monologue before the answer, and it is billed against the same output budget — so a smallmax_tokensgets spent on thinking and the reply comes back empty, withfinish_reason: "length"rather than an error.Measured on the same prompt and the same 1,024-token cap:
Model Thinking trace finish_reason Latency Answer qwen2.5:14bnone stop~10 s present gemma4:12b~1,000 chars stop~39 s present qwen3.5:9b~3,957 chars length~65 s empty Note that smaller is not faster here —
qwen3.5:9bis the smallest of the three and 6.5x slower than the largest, because it spends the time thinking. If a model returns blank content, checkfinish_reasonbefore concluding it cannot do tool calls; raisemax_tokens(4096 is a safe floor) or pick a non-reasoning model.
On Ollama, the endpoint must be reachable from inside the container: use http://host.docker.internal:11434/v1 on Docker Desktop (macOS/Windows) rather than localhost.
Other commands
./scripts/dev.sh --clean # nuke volumes and rebuild from scratch
./scripts/dev.sh --infra-only # just db, redis, aspire
./scripts/dev.sh --seed-only # re-run the seeder against an existing DB
./scripts/dev.sh --dotnet # the .NET stack instead of Python
The Compose equivalents are docker compose down -v, docker compose up -d db redis aspire, and docker compose --profile seed run --rm seeder.
If something breaks
Start with Troubleshooting — it covers every first-run failure we know about, including port conflicts, the seeder racing the database, and missing embeddings.
The two most common:
- Port 3000 or 8080 already in use. Another service owns it.
docker compose downdoes not help if the conflict is outside Compose — check withlsof -i :3000(macOS/Linux) ornetstat -ano | findstr :3000(Windows). - The chat answers but never calls a tool. Almost always the model, not the code — see the warning above.
Where to go next
- Concepts — what an agent is, why more than one, what a graph means here
- Tutorials — 34 chapters, Python and .NET, each runnable without an API key
- Architecture — how the whole system fits together
- Deployment — configuration reference, profiles, environment variables
Source: docs/quick-start.md — this page is generated from the repository.