Goal: Build a reference architecture/accelerator for agentic commerce where reusable agents (Order, Product, Customer, etc.) can plug into different enterprise systems through connectors, without changing the agents themselves.
Why this matters: most enterprises have order, product, and customer data fragmented across separate systems — a commerce platform, an ERP, a CRM, inventory — with inconsistent IDs and formats between them. An AI agent built directly against that mess either gives confidently wrong answers or can't safely take action at all. Being "AI-ready" starts with data being ready: a clean, governed, tenant-scoped layer the agent can trust, decoupled from wherever that data actually lives. That decoupling — agents talk to a stable schema through tools, connectors do the work of keeping it populated — is the actual point of this repo, demonstrated with two working agents rather than argued in the abstract.
- Order Agent — customers ask about an order or ask to cancel/modify it, and the agent can actually do it, with a confirm step before anything irreversible happens.
- Product Agent — semantic search over the product catalog ("something warm for winter hiking") with live, accurate stock levels, not guesses.
Both currently run against seed data reachable through the same tenant-scoped
schema a real connector (Shopify, an ERP export, a custom sync) would write
into instead — see docs/ARCHITECTURE.md §1 ("Connector principle") and §8
for adding another agent on the same pattern. Full diagrams, data model, and
the tradeoffs behind each architectural choice are in docs/ARCHITECTURE.md.
apps/agent— Python, FastAPI + LangGraph. Two separate single-agent tool-use loops:graph.py(Order Agent:get_order_status,propose_cancel_order,propose_modify_order) andproduct_graph.py(Product Agent:search_products,get_product_details) — talking to InsForge Postgres over its REST API (no raw Postgres connection string is exposed, seeapp/db.py).apps/web— Next.js + Tailwind.ChatWidget.tsxis the embeddable component (reused for both agents via props);/demosimulates a client site with both installed.- InsForge — hosted Postgres + pgvector (multi-tenant schema: tenants, customers, products [+description/category/stock/embedding], orders, order_items, conversation_messages) + free tier.
- OpenRouter — dev-tier chat LLM (config-driven; swap to Claude in prod
via
LLM_PROVIDER=anthropic, seeapps/agent/app/llm.py). - Local
sentence-transformers— product embeddings, deliberately not another OpenRouter call (seeapp/embed.pyand Known rough edges below).
Agent service:
cd apps/agent
python3 -m venv .venv && .venv/bin/pip install -r requirements.txt
cp .env.example .env # fill in INSFORGE_URL, INSFORGE_API_KEY, OPENROUTER_API_KEY
.venv/bin/python -m app.seed # seed the demo tenant with sample data
.venv/bin/uvicorn app.main:app --port 8000 --reloadWeb app:
cd apps/web
npm install
npm run dev # http://localhost:3000/demoBoth need to be running for the demo page to work — it shows both agents side
by side. The demo tenant's API key is demo-local-dev-key (set in both
.env files via DEMO_TENANT_API_KEY / NEXT_PUBLIC_DEMO_TENANT_API_KEY).
The seed script's product catalog is hand-written (not Faker), specifically
so semantic search has coherent text to actually search over.
InsForge doesn't expose a raw Postgres connection string (REST-only access),
so this doesn't use LangGraph's official Postgres checkpointer. Instead,
apps/agent/app/memory.py loads/saves plain conversation turns against the
conversation_messages table, scoped by (tenant_id, session_id) — same
persistence guarantee, implemented at the app layer. Verified live: a brand
new Python process recalls prior turns in the same session correctly.
Two things will bite you if this sits idle for a few weeks and you come back
to a broken demo — both are free-tier quirks, not bugs, and both are fully
diagnosed with fixes in docs/ARCHITECTURE.md §9:
- OpenRouter's free models rotate out from under you (rate-limited,
silently deprecated). Check
OPENROUTER_MODELin.envagainstcurl https://openrouter.ai/api/v1/modelsfiltered for:free. - InsForge pauses inactive free-tier projects.
npx -y @insforge/cli projects getto check status,projects restoreifpaused.
Embeddings deliberately don't go through OpenRouter at all — app/embed.py
uses a local sentence-transformers model instead (free, no rate limits,
~13s one-time load per process), specifically to avoid a second dependency
on that same shared free-tier pool. Full reasoning in docs/ARCHITECTURE.md
§2 and §4.