Skip to content

Latest commit

 

History

3 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Multi Agent Research & Decision Assistant

An application for supervisor based multi agent workflows with LangGraph, MCP tool boundaries, human approval, and a React monitoring console.

Screenshot

Run the project

  1. Start the backend stack from the repository root: docker compose up --build.
  2. Start the frontend in a second terminal: Set-Location frontend; npm install; npm run dev.
  3. Open http://localhost:5173.

The API runs at http://localhost:8000; interactive documentation is at http://localhost:8000/docs.

For full instructions, see:

  • Backend README: Docker, FastAPI, PostgreSQL, Redis, LangGraph, MCP, API calls, approval flow, and tests.
  • Frontend README: React setup, environment variables, polling, approval UI, and frontend builds.
  • MIT License

Option A: Run the backend in Docker

This is the simplest full-stack development path. Docker starts PostgreSQL, Redis, and the FastAPI container. The frontend still runs from its own folder.

Prerequisites

  • Docker Desktop with Compose
  • Node.js 18 or newer
  • npm

Start the backend stack

From the repository root:

Copy-Item .env.example .env
docker compose up --build

The first build installs the Python dependencies and may take a few minutes. The services are then available at:

Service URL or address
FastAPI http://localhost:8000
FastAPI docs http://localhost:8000/docs
PostgreSQL localhost:5432
Redis localhost:6379

The backend waits for the PostgreSQL and Redis Docker health checks before starting. To run it in the background instead:

docker compose up --build -d
docker compose ps
docker compose logs -f backend

Stop the stack with:

docker compose down

Add -v only when you intentionally want to delete the PostgreSQL and Redis volumes:

docker compose down -v

Start the frontend

Open a second terminal:

Set-Location frontend
Copy-Item .env.example .env
npm install
npm run dev

Open http://localhost:5173. The frontend defaults to http://localhost:8000/api/v1, so no extra configuration is needed for Option A.


Option B: Run infrastructure in Docker and the applications locally

This mode is useful while learning or debugging Python and React code.

Terminal 1: start PostgreSQL and Redis

From the repository root, start only the infrastructure services:

docker compose up postgres redis

Leave this terminal running. The backend should use localhost for these services when it runs outside Docker. The root .env.example is for Compose and uses Docker service names, so create backend/.env with localhost values for local FastAPI development:

POSTGRES_HOST=localhost
POSTGRES_PORT=5432
POSTGRES_DB=multiagent
POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres
REDIS_HOST=localhost
REDIS_PORT=6379

Terminal 2: start FastAPI

Create and activate a virtual environment, then install the backend dependencies:

Set-Location backend
python -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install -r requirements.txt
uvicorn app.main:app --reload --port 8000

If PowerShell blocks activation, run this once in the current user scope or use the Python executable directly:

Set-ExecutionPolicy -Scope CurrentUser RemoteSigned

Terminal 3: start React

Set-Location frontend
npm install
npm run dev

Then open http://localhost:5173.

To point the frontend at another backend, create frontend/.env:

VITE_API_BASE_URL=http://localhost:8000/api/v1

Restart Vite after changing a VITE_ environment variable.

When using Option B, the root .env is only read by Docker Compose and backend/.env is read by the locally started FastAPI process. Do not confuse the two files.

Use the application

  1. Open http://localhost:5173.
  2. Enter a question in the research brief and select Start research.
  3. The frontend stores the returned run_id and polls the run status and event endpoints every 1.2 seconds.
  4. Watch the workflow timeline move through the input guardrail, supervisor, parallel research, analyst, critic, and synthesizer nodes.
  5. Wait for awaiting_approval. Saving a report is a medium-risk action, so it does not happen automatically.
  6. Edit the filename if needed, then choose Approve, Edit & approve, or Reject.
  7. After approval, the backend resumes from the synthesized run state, calls the workspace MCP tool, and exposes the result in the frontend.

The current demo uses deterministic local data. No Gemini key is required to run the default workflow.


API walkthrough

Start a run:

$run = Invoke-RestMethod -Method Post `
    http://localhost:8000/api/v1/research `
    -ContentType 'application/json' `
    -Body '{"query":"Compare PostgreSQL and Pinecone for a RAG application"}'
$run

Poll status, events, and approval:

$id = $run.run_id
Invoke-RestMethod http://localhost:8000/api/v1/runs/$id
Invoke-RestMethod http://localhost:8000/api/v1/runs/$id/events
Invoke-RestMethod http://localhost:8000/api/v1/runs/$id/approval

Approve the generated filename:

Invoke-RestMethod -Method Post `
    http://localhost:8000/api/v1/runs/$id/approval `
    -ContentType 'application/json' `
    -Body '{"decision":"approve"}'

Edit and approve instead:

Invoke-RestMethod -Method Post `
    http://localhost:8000/api/v1/runs/$id/approval `
    -ContentType 'application/json' `
    -Body '{"decision":"edit","arguments":{"filename":"database-comparison.md"}}'

Reject the action:

Invoke-RestMethod -Method Post `
    http://localhost:8000/api/v1/runs/$id/approval `
    -ContentType 'application/json' `
    -Body '{"decision":"reject","reason":"Do not save this report."}'

Read the completed result:

Invoke-RestMethod http://localhost:8000/api/v1/runs/$id/result

Health checks:

Invoke-RestMethod http://localhost:8000/api/v1/health
Invoke-RestMethod http://localhost:8000/api/v1/health/dependencies

How the workflow works

flowchart TD
        Client[React polling console] --> API[FastAPI]
        API --> Guard[Input guardrail]
        Guard --> Supervisor[Supervisor / planner]
        Supervisor -->|LangGraph Send| ResearchA[Research task A]
        Supervisor -->|LangGraph Send| ResearchB[Research task B]
        ResearchA --> MCP[MCP client boundary]
        ResearchB --> MCP
        MCP --> Analyst[Analyst]
        Analyst --> Critic[Critic]
        Critic --> Synth[Synthesizer]
        Synth --> Approval{Medium-risk save_report}
        Approval -->|approve or edit| Workspace[Workspace MCP]
        Approval -->|reject| Rejected[Controlled rejection]
        Workspace --> Output[Output guardrail and result]
Loading
  • backend/app/graph.py contains the explicit StateGraph and Send fan-out example. The supervisor creates two tasks and Send routes each task to the researcher node concurrently.
  • backend/app/workflow.py coordinates the run lifecycle and keeps the run state readable.
  • The current analyst and critic are deterministic teaching implementations. The Gemini provider boundary is in backend/app/llm.py and reads GEMINI_API_KEY and GEMINI_MODEL from the environment when enabled.
  • GET /runs/{run_id} provides current node, progress, status, and tasks. GET /runs/{run_id}/events provides a small pollable activity feed.
  • The frontend does not calculate workflow progress. It renders the backend's progress, current_node, task statuses, and events.

MCP servers and tools

The first version keeps MCP deterministic and local so it can be learned and tested without external accounts. The three server modules are:

Server Tools Current behavior
Search search, fetch_url Returns deterministic local evidence
Documents list_documents, read_document Reads Markdown files from backend/data/documents/
Workspace create_report, save_report Writes reports to backend/data/reports/

Agents call MCPClient.call(server, tool, arguments) in backend/app/mcp.py; they do not import these server modules directly. The current client is an in-process adapter, not three independently running services. This is intentional for Project 1. The client boundary can later be replaced with MCP stdio or HTTP transport without changing the agent workflow.

To try the document server, place a Markdown file in backend/data/documents/, then call the client from a Python shell or add a document task to the workflow. Approved reports appear in backend/data/reports/. The Docker backend mounts ./backend/data into the container so those files remain visible on the host.


Configuration

Copy .env.example to .env at the repository root. Important values include:

GEMINI_API_KEY=
GEMINI_MODEL=gemini-2.0-flash
POSTGRES_HOST=postgres
POSTGRES_PORT=5432
POSTGRES_DB=multiagent
POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres
REDIS_HOST=redis
REDIS_PORT=6379
MAX_RESEARCH_RETRIES=2

Never commit .env or place secrets in frontend code. VITE_ variables are bundled into browser JavaScript, so a Gemini API key must never be put in frontend/.env.


Persistence and caching status

PostgreSQL models and an async database adapter are present in backend/app/persistence_models.py and backend/app/database.py. The learning workflow currently uses an in-memory run repository so it works immediately in local tests. The current /health/dependencies response reports infrastructure as not configured until real repository, Redis, and LangGraph PostgreSQL checkpointer wiring is enabled.

Likewise, CacheService is currently an in-process replaceable adapter. Its interface is get, set, and delete, and only completed repeatable research results are cached. Approval state and mutable workflow state are not cached.


Tests and development checks

From backend/:

python -m pytest tests -q
python -m compileall app mcp_servers

From frontend/:

npm install
npx tsc -b
npx vite build

The tests use deterministic MCP behavior and do not require a real Gemini API key, PostgreSQL connection, Redis connection, or external network service.


Project layout

backend/
    app/
        main.py              FastAPI routes
        workflow.py          asynchronous research and approval lifecycle
        graph.py             LangGraph StateGraph and Send example
        mcp.py               MCP client boundary
        guardrails.py        input and output checks
        policy.py            action risk classification
        persistence_models.py PostgreSQL model definitions
    mcp_servers/           deterministic local MCP server implementations
    tests/
frontend/
    src/api/               centralized API calls
    src/types/             API types
    src/App.tsx            research console and workflow views
    src/styles.css         responsive visual system

Architecture

flowchart TD
    API[FastAPI] --> G[Input guardrail]
    G --> S[Supervisor LangGraph]
    S -->|Send fan-out| R1[Research task]
    S -->|Send fan-out| R2[Research task]
    R1 --> M[MCP client]
    R2 --> M
    M --> A[Analyst and critic]
    A --> Y[Synthesizer]
    Y --> H{Save report approval}
    H -->|approve/edit| W[Workspace MCP]
    H -->|reject| X[Controlled rejection]
Loading

backend/app/graph.py is the small executable LangGraph teaching example. The workflow uses it to plan two concurrent research tasks, then calls the MCP client boundary. backend/app/database.py and persistence_models.py define the PostgreSQL persistence boundary for runs, tasks, approvals, and artifacts; the in-memory repository is used by default for deterministic learning and tests. Replace it with the SQL repository and a LangGraph PostgreSQL checkpointer when wiring a deployed environment.

Medium and high risk actions are represented by an Approval and pause at awaiting_approval. POST an approval with approve, edit plus arguments, or reject plus a reason. No report write occurs before approval. Redis caching is limited to completed, repeatable research results; the included cache is an in-process adapter so the API runs without infrastructure.


Example

$run = Invoke-RestMethod -Method Post http://localhost:8000/api/v1/research -ContentType 'application/json' -Body '{"query":"compare solar and wind energy"}'
Invoke-RestMethod http://localhost:8000/api/v1/runs/$($run.run_id)
Invoke-RestMethod -Method Post http://localhost:8000/api/v1/runs/$($run.run_id)/approval -ContentType 'application/json' -Body '{"decision":"approve"}'
Invoke-RestMethod http://localhost:8000/api/v1/runs/$($run.run_id)/result

Gemini configuration is reserved for the provider abstraction and comes from GEMINI_API_KEY and GEMINI_MODEL; no key is required for the deterministic local workflow.

About

Multi agent orchestration engine built with FastAPI, LangGraph, Google Gemini, and MCP. Features parallel research fan out, human-in-the-loop state checkpoints, and async persistence.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages