Skip to content

Repository files navigation

Aria logo

A multi-agent digital marketing assistant. Give it a business objective and a brand tone; it writes the copy, plans the campaign, scores its own work against a rubric, and reworks itself until it passes.

English · Español

Python FastAPI LangGraph Next.js React TypeScript Tailwind LiteLLM Postgres SQLModel Docker pnpm uv


What is Aria?

Aria is a digital marketing assistant built as a multi-agent system. You describe a business objective and a brand tone; Aria orchestrates a team of specialised agents that:

  1. write marketing copy (posts, ads, social text),
  2. plan the campaign (publishing calendar, audience segmentation, budget),
  3. evaluate the result against a fixed quality rubric, and
  4. improve the copy automatically when the evaluation falls short.

A separate flow handles customer support: it answers questions and escalates to a human whenever it is not confident.

The whole thing is model-agnostic: the application never talks to a model provider directly. Every call goes through a gateway using a generic alias, so you can swap the model behind Aria without touching a single line of code, and no provider name ever leaks into the app.


Table of contents


Key features

  • Multi-agent orchestration with a deterministic control flow (no LLM decides the routing).
  • Self-improving campaigns: a bounded feedback loop where the analyst's weaknesses are fed back into the copywriter, so each rework is directed, not a blind retry.
  • Rubric-based scoring: campaigns are graded 0–100 against six weighted criteria; approval is derived deterministically from the score.
  • Confidence-based support: the support agent answers only when sure, and escalates to a human otherwise.
  • Model-agnostic gateway: swap models via configuration; automatic fallback if the primary provider fails or rate-limits.
  • Bilingual output (es / en), selected per run and threaded from the UI into every agent.
  • Full traceability: every agent emits start/end traces, streamed live to the UI over SSE and persisted as an audit record.
  • Privacy & compliance built in: retention purge, full erasure endpoint, security headers, rate limiting, and an ISO/IEC 27001 & 42001 control mapping.

Architecture

Four layers, each with a single responsibility. The backend never reaches a model provider directly.

┌──────────────┐     ┌───────────────────────┐     ┌──────────────┐     ┌────────┐
│   Frontend   │ ──▶ │        Backend        │ ──▶ │   Gateway    │ ──▶ │ Models │
│  (Next.js)   │     │ (FastAPI + LangGraph) │     │  (LiteLLM)   │     │        │
└──────────────┘     └───────────────────────┘     └──────────────┘     └────────┘
     :3000                    :8000                      :4000
Layer Tech Responsibility
Frontend Next.js 15, React 19, TypeScript, Tailwind Campaign form, live trace timeline, support, history, technical graph view
Backend FastAPI, LangGraph, SQLModel Agent orchestration, state, traces, persistence, API
Gateway LiteLLM proxy (config only) Resolves generic aliases to a real provider/model; automatic fallback
Models any OpenAI-compatible model Text (and image) generation behind a generic alias
Database Postgres 16 (SQLite for standalone local runs) Append-only run history as an audit record

The agents

Agent LLM? What it does
Orchestrator No (deterministic) Routes the flow, enforces the iteration limit
Content Yes Writes marketing copy from the objective and brand tone
Campaigns Yes Builds the campaign plan (calendar, audience, budget)
Analytics Yes Scores the campaign against a rubric, extracts weaknesses
Support Yes Answers customer questions; escalates when unsure

See AGENTS.md for each agent's state and guarantees, and docs/SKILLS.md for their concrete capabilities.


How it works

The campaign improvement cycle

orchestrator → content → campaigns → analytics
                                         │
                          approved ──────────────────→ END
                          rejected & iterations < 2 ─→ adjust ─→ content
                          rejected & iterations ≥ 2 ─→ END (hard limit)
  1. The orchestrator receives the objective and routes to content.
  2. Content writes the copy; campaigns turns it into a plan.
  3. Analytics scores the plan 0–100 against a fixed rubric (objective alignment, clarity, persuasion & CTA, brand-tone fit, platform fit, feasibility) and extracts the concrete weaknesses.
  4. If the score is ≥ 70 the campaign is approved and the flow ends.
  5. If not, the orchestrator loops back to content, injecting the weaknesses so the next attempt fixes them specifically. This repeats at most twice (a hard limit that guarantees the loop always terminates).

Approval is never parsed from free LLM text: it is derived deterministically from score ≥ MIN_SCORE, so every routing decision is auditable.

The support flow

An independent flow with its own state. The support agent answers FAQs directly when confident; otherwise it returns an escalation marker and flags the case for a human. It never touches campaign state.


Tech stack

Free tier friendly: every piece can run on a free/local model and open-source tooling.

  • Backend: Python 3.12, FastAPI, LangGraph, SQLModel, slowapi (rate limiting), uv (package manager).
  • Frontend: Next.js 15, React 19, TypeScript 5, Tailwind CSS, framer-motion, mermaid, pnpm (package manager).
  • Gateway: LiteLLM proxy (configuration only, no custom code).
  • Data: Postgres 16 (SQLite for standalone local runs).
  • Local model: Ollama (optional, demonstrates model-agnosticism).
  • Infra: Docker Compose, GNU Make for one-command orchestration.
  • Observability: structured JSON logging; optional LangSmith tracing.

Quick start (one command)

Prerequisites: Docker (Desktop on macOS/Windows), pnpm, and uv.

# 1. Clone
git clone <your-repo-url> aria
cd aria

# 2. Configure secrets (see "Configuration" below for what each var means)
cp .env.example .env
cp backend/.env.example backend/.env
# then edit .env and backend/.env with your gateway key and provider key

# 3. Bring up EVERYTHING: backend, Postgres, gateway, local model runner,
#    and the Next.js frontend dev server.
make dev

That's it. make dev builds and starts the Docker stack and then runs the frontend in the foreground.

Service URL
Frontend http://localhost:3000
Backend API http://localhost:8000
API docs (Swagger) http://localhost:8000/docs
Gateway http://localhost:4000

Everyday Make targets

make dev        # bring up the whole stack + the frontend (default)
make up         # start only the backend/db/gateway stack (Docker), in the background
make front      # run only the Next.js frontend in the foreground
make down        # stop and remove the Docker stack (keeps the db volume/data)
make restart     # recreate the Docker stack
make logs        # follow the Docker stack logs
make ps          # show the Docker stack status
make clean       # stop the stack and DELETE the db volume (WARNING: wipes all data)
make studio      # run LangGraph Studio for the campaign graph (outside Docker)
make help        # list all targets

Manual setup (run each layer yourself)

Prefer to run things one by one? Here is each layer, copy-paste ready.

Backend

cd backend
uv sync --extra dev                        # install deps into a local .venv
.venv/bin/python -m pytest -q              # run the test suite
uv run uvicorn app.main:app --reload       # serve on http://localhost:8000

The backend expects a gateway at GATEWAY_URL. For standalone local runs it defaults to a SQLite database (sqlite:///./app.db), so you do not need Postgres.

Frontend

cd frontend
pnpm install
pnpm dev                                    # serve on http://localhost:3000
pnpm typecheck                              # type-check without emitting

NEXT_PUBLIC_API_BASE points the client at the backend (defaults to http://localhost:8000).

Gateway

cd gateway
uv run litellm --config config.yaml --port 4000

The real provider/model behind each generic alias lives only in gateway/config.yaml.

Full stack with Docker (no Make)

docker compose up -d --build                # start backend, db, gateway, ollama
docker compose logs -f                      # follow logs
docker compose down                         # stop (keeps data)
docker compose down -v                      # stop and wipe the db volume

Configuration

Secrets are environment-only and git-ignored. Copy the examples and fill in real values; a startup guard rejects placeholder values.

Root .env (used by Docker Compose for backend, db, gateway, local model):

Variable Meaning
GATEWAY_KEY Auth key the backend uses to call the gateway
MODEL_ALIAS Generic model alias (default primary-model)
MODEL_LOCAL_ALIAS Alias for the local model (local-model)
PROVIDER_API_KEY Real provider key, consumed only by the gateway
PROVIDER_FALLBACK_API_KEY Key for the automatic fallback provider
PROVIDER_IMAGE_* Image-generation provider endpoints and key (gateway only)
POSTGRES_USER / _PASSWORD / _DB Postgres credentials
DATABASE_URL Connection string (Postgres in Docker; point at Supabase in deploy)
TEMPERATURE / MAX_TOKENS Generation defaults
DATA_RETENTION_DAYS 0 keeps runs forever; a positive value purges older runs on startup
DEMO_MODE Force a failing first iteration so the improvement cycle is demoable

backend/.env mirrors the model/gateway vars and adds RATE_LIMIT (per-client rate limit on the POST endpoints, e.g. 10/minute) and GATEWAY_TIMEOUT.

No provider names anywhere in the app. Only the gateway config knows which real model sits behind primary-model, fallback-model and local-model.


API reference

Method Path Purpose
GET / Service info
GET /health Service status + gateway connectivity
POST /campaign Run the campaign graph; stream traces over SSE, ending with a done event carrying the final result
POST /support Run the support flow; return the final answer + escalation flag
GET /history Persisted runs, newest first, with totals for pagination
DELETE /history/campaign/{id} Delete one campaign run
DELETE /history/support/{id} Delete one support run
GET /graph Compiled graph structure, agents, state schema, parameters
GET /docs Swagger UI

Example, start a campaign:

curl -N -X POST http://localhost:8000/campaign \
  -H "Content-Type: application/json" \
  -d '{
    "objective": "Launch a new specialty coffee subscription",
    "brand_tone": "warm, artisanal, a little playful",
    "language": "en"
  }'

Project layout

.
├── backend/          FastAPI + LangGraph service
│   └── app/
│       ├── main.py           app, middleware, router wiring
│       ├── api/              routers: campaign, support, history, graph, image
│       ├── graph/            LangGraph graph, routing, metrics, nodes, state, traces
│       ├── llm/              call_agent(): the only door to the gateway (Contract 2)
│       └── persistence/      SQLModel storage (audit record)
├── frontend/         Next.js app (campaign, support, history, technical views)
├── gateway/          LiteLLM proxy config (generic model aliases)
├── docs/             spec, skills, handoff + deployment + ISO/GDPR compliance docs
├── docker-compose.yml
├── Makefile          one-command dev orchestration
└── AGENTS.md         the agents

Documentation

Document What it covers
docs/SPEC.md Specification: scope, architecture, contracts, API, constraints
AGENTS.md Every agent: role, state, invocation, guarantees
docs/SKILLS.md The concrete capabilities (skills) of each agent
docs/HANDOFF.md Implementation status and detailed run instructions
docs/compliance/ ISO/IEC 27001 & 42001 control mapping, GDPR record of processing
docs/deploy-railway.md Deployment guide

Conventions

  • Work on a feature branch; merge to main via PR.
  • Commits: type:short description, in English, no parentheses.
  • Code, comments and identifiers are English-only. Agent output is user-facing and bilingual (es / en).
  • No provider names anywhere in the application or docs; generic aliases only.

License

Released under the MIT License.


Authors

  • Andrés Torres Mamani
  • Michelle Zulay Gelves Contreras
  • Naizabeth De Los Angeles Bermudez Davila
  • Roberto Antonio Molero Losada

About

Aria. A multi-agent digital marketing assistant. Give it a business objective and a brand tone; a team of agents writes the copy, plans the campaign, scores itself against a rubric, and reworks until it passes. Model-agnostic via a gateway.

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages