This article is published in English.
Anatomy of an AI Business Team: Orchestrating Agents with LangGraph and FastAPI
A walkthrough of an open-source multi-agent platform: how Cofounder, Manager and specialist agents are orchestrated, share memory, pause for approval and report.
Most AI automation still follows a single-agent shape: one model receives a prompt and returns an answer. That is fine for question answering, but real business work is distributed across roles, some steps run in parallel, and some must wait for a person to sign off. This walkthrough dissects an open-source platform, Multi Agent for Business Automation, that models a small startup team as coordinated agents. By the end you should understand how its orchestration, memory, approval gates and API surface fit together, and which design choices you can reuse in your own agent systems.
From a chatbot to a virtual office
Consider how a new venture actually gets planned. A founder articulates the vision. A manager turns it into concrete tasks. Finance checks revenue and cost assumptions, marketing works out positioning, legal looks for risk, and sales prepares outreach. Some of that happens in sequence, some concurrently, and some of it is blocked until a human approves the next move.
According to its README, the project is a platform for automating business work and supporting decisions, in which agents with distinct roles, personalities and tools are coordinated through LangGraph workflows, shared memory and human-in-the-loop (HITL) approval gates, with report generation and external integrations on top. The ambition is explicitly not another chatbot but a virtual office where agents collaborate the way a startup team would.
The five architectural layers
The system splits cleanly into five layers:
- Frontend: a React dashboard for chat-based onboarding, workflow visualization, approvals and analytics.
- Backend API: FastAPI REST endpoints, WebSocket streams, authentication, reports and memory APIs.
- Agent layer: the Cofounder, Manager, Finance, Marketing, Legal, Money and Sales agents.
- Orchestration layer: LangGraph workflows alongside a custom orchestrator, task delegation and checkpointing.
- Memory and infrastructure: Neo4j, Qdrant, Redis or Upstash, Supabase, and report storage.
According to the README, the stack is React 18 with Vite, FastAPI, LangGraph plus LangChain and CrewAI for agent workflows, Neo4j, Qdrant and Redis for memory and queues, and Supabase as an optional layer for authentication and persistence.
The overall lifecycle flows top to bottom, from the user's vision through the leadership agents to the specialists, and finally into shared memory, approvals and outputs:
User Vision
↓
Cofounder Agent
↓
Manager Agent
↓
Specialist Agents
├── Finance
├── Marketing
├── Legal
├── Money
└── Sales
↓
Shared Memory + Approval Gates
↓
Reports + Dashboard + Integrations
The point to take from this diagram is that the design is a workflow system, not a prompt chain. State, memory, approvals, retries, observability and persisted outputs are all first-class concerns.
The five-stage workflow
Stage 1: turning a vague idea into structured context
Everything begins when the user explains what they want: a product concept, a business idea, or some operational objective. The Cofounder Agent receives this input, which may be as loose as a single sentence:
Input:
"I want to build an AI tool for small businesses."
Its job is to convert that into structured context the rest of the team can work from:
Cofounder Output:
- Vision statement
- Target users
- Problem definition
- Market opportunity
- Strategic assumptions
- Initial business direction
The README labels this step "Vision intake". On the backend it can be triggered through several entry points, depending on whether the client wants a one-shot project, a conversation, or an enhanced session:
POST /api/start-project
POST /api/conversation/start
POST /api/enhanced/start-session
Whichever route is used, the backend creates a project or session, records the initial state, and dispatches the message to the right agent.
Stage 2: planning and delegation by the Manager
With the vision structured, the Manager Agent takes over and breaks the strategy down into work that can actually be executed:
Manager Responsibilities:
- Convert vision into roadmap
- Break work into functional domains
- Create agent assignments
- Define task dependencies
- Decide which agents should execute in parallel
In the custom orchestrator this is implemented in AgentOrchestrator.start_project(), found in backend/flows/orchestrator.py. It generates a project ID, runs the Cofounder first, feeds the Cofounder's result to the Manager, and then launches specialists according to the Manager's assignments. In pseudocode:
cofounder_result = await Cofounder.execute(vision_task)
manager_result = await Manager.execute(manager_task)
specialist_results = await execute_specialists(manager_result)
generate_outputs(all_results)
The Manager is the control layer. Without it, each specialist would work from the raw idea in isolation and return outputs that do not reference one another. Centralizing decomposition in one agent is what makes the specialists' results add up to a coherent plan.
Stage 3: running specialists in parallel
Once assignments exist, domain agents take over. The default roster and their responsibilities are:
- Cofounder: vision, strategy and market opportunity.
- Manager: roadmap, task decomposition and delegation.
- Finance: revenue model, cost model, ROI and projections.
- Marketing: positioning, campaigns and content strategy.
- Legal: compliance, risk, contracts and policy checks.
- Money: pricing, monetization and revenue operations.
- Sales: pipeline, outreach and sales strategy.
Each agent is defined in backend/agents/personalities.py, and every definition carries traits, a communication style, areas of expertise and role-specific tools. That matters: the agents are modeled as role-specific executors with their own behavior, confidence thresholds and task context, not merely as different system prompts.
Execution is handled by _execute_specialists(), which prepares one task per specialist (Finance, Marketing, Legal and Money, plus Sales when it is enabled). Each agent call is wrapped in a 60-second timeout:
parallel_executions = [
asyncio.wait_for(agent.execute(task), timeout=60.0)
for agent_name, task in specialist_tasks
]
All of those wrapped calls are then awaited together:
specialist_results = await asyncio.gather(
*parallel_executions,
return_exceptions=True
)
Because asyncio.gather() receives return_exceptions=True, a timeout or crash in one agent is returned as an exception object in the results list instead of cancelling the whole batch. The trade-off is that calling code must inspect each result and decide whether a failed specialist should be retried, skipped or escalated. The broader design decision is sound: when the Finance analysis never needs the Marketing output, or vice versa, there is no reason to run them one after the other.
Stage 4: shared memory and context propagation
A multi-agent system degrades quickly when every agent sees only its own context. The platform addresses this with a unified memory layer in which each store has a distinct job:
Neo4j → graph memory and relationships
Qdrant → vector memory and semantic retrieval
Redis → task queue, cache, runtime coordination
Local data → fallback storage and exported outputs
The README summarizes this as Neo4j graph memory, Qdrant vector search, Redis or Upstash queues, and local caching with graceful fallback. Each store serves a different need.
Graph memory records relationships between agents, tasks, projects and outputs:
Agent → executed → Task
Task → belongs_to → Project
Agent → produced → Output
Output → depends_on → PriorContext
That structure makes it possible to visualize collaboration and to trace exactly which agent produced which result, and from what prior context.
Vector memory in Qdrant supports semantic recall, so an agent can ask for earlier work by meaning rather than by ID:
"Find previous market assumptions."
"Retrieve prior financial analysis."
"Use the earlier legal risk summary."
Redis-backed queue memory covers runtime coordination:
- Event streaming
- Task queues
- Cache
- Agent activity updates
- WebSocket event propagation
The project also degrades gracefully when services are missing. In local development Redis can be replaced by an in-memory adapter, and LLM calls can be routed to a mock provider. That lowers the barrier to running the whole system on a laptop without provisioning every production dependency, though it also means local runs will not surface issues that only appear with real services.
Stage 5: human-in-the-loop approval
High-impact actions should not execute blindly, and here human approval is part of the core workflow rather than a bolt-on. Gates apply to decisions that can affect:
- Customer-facing campaigns
- Pricing recommendations
- Legal-sensitive outputs
- CRM updates
- Social media publishing
- Financial projections
- Business-critical recommendations
The README highlights Slack notification hooks and configurable timeouts for these approval flows. On the API side, approvals are exposed through endpoints such as:
GET /api/approvals/pending
POST /api/approvals/{approval_id}/respond
GET /api/approvals/advanced/pending
GET /api/approvals/stats
The basic lifecycle of an approval looks like this:
Agent generates action
↓
System evaluates confidence/risk
↓
Approval request is created
↓
Frontend or Slack notifies human
↓
Human approves/rejects/provides feedback
↓
Workflow continues or stops
The result is a tiered autonomy model: low-risk work proceeds automatically, while anything customer-facing, financial or legally sensitive waits for a human decision. If you are designing similar gates, a related walkthrough of routing, fan-out and approval patterns in LangGraph covers the graph-level mechanics in more depth.
Inside the FastAPI backend
The backend's main entry point, backend/main.py, instantiates a set of global services when the application starts:
AgentOrchestrator
Enhanced Orchestrator
ReportGenerator
SimplePredictor
AgentCollaborator
AdvancedApprovalManager
AgentService
ReportService
LangGraphOrchestrator
Queue Manager
Per the README, every route is mounted by backend/main.py; backend/api/main.py is a leaner alternative entry point that mounts only a subset of routes. The code is organized by responsibility:
backend/
├── main.py # Primary FastAPI app
├── api/ # Route controllers
├── agents/ # Agent implementations and personalities
├── workflows/ # LangGraph and HITL orchestrators
├── flows/ # PRD DAG orchestrator
├── memory/ # Graph, vector, cache managers
├── task_queue/ # Redis/Upstash queue with fallback
├── integrations/ # HubSpot, Slack, Instagram clients
├── approvals/ # Approval managers
├── collaboration/ # Cross-agent communication
├── outputs/ # Report generation
├── analytics/ # Predictions and metrics
├── auth/ # Supabase auth
├── services/ # LLM, agent, report services
└── tools/ # Search and tool registry
The separation between workflows/ (LangGraph and HITL orchestrators) and flows/ (the PRD-driven DAG orchestrator) is worth noticing: the project carries two orchestration approaches side by side, which is flexible but means you should check which path a given endpoint uses.
The API surface, group by group
Authentication
POST /api/auth/signup
POST /api/auth/signin
GET /api/auth/user
In production-style mode these endpoints rely on Supabase authentication. In local or demo mode the app can skip Supabase entirely and fall back to lightweight development logic. Configuration options named in the README include DEMO_MODE, the Supabase variables and the LLM provider variables. The corresponding handlers:
signup() → creates user account
signin() → authenticates user
get_current_user()→ verifies token and returns user profile
Project lifecycle
GET /api/projects
POST /api/projects
POST /api/start-project
POST /api/auto-execute
GET /api/auto-execute/status
These map to the following functions:
get_user_projects() → fetch projects for authenticated user
create_project() → create new project record
start_project() → execute Cofounder → Manager → Specialists workflow
auto_execute_project() → start automated coordination from raw vision
get_auto_execution_status() → return execution progress and logs
start_project() is the heart of this group. Its internal sequence is:
1. Generate project_id
2. Execute Cofounder Agent
3. Log Cofounder result
4. Execute Manager Agent with Cofounder context
5. Log Manager result
6. Execute specialist agents in parallel
7. Export and persist final outputs
8. Return project status and timeline
That sequence corresponds directly to AgentOrchestrator.start_project() and _execute_specialists() in the orchestrator module.
Enhanced sessions
POST /api/enhanced/start-session
POST /api/enhanced/continue-session
POST /api/enhanced/approve-and-execute
GET /api/enhanced/session-status
GET /api/enhanced/live-logs
GET /api/enhanced/session-results
GET /api/enhanced/system-metrics
POST /api/enhanced/cancel-session
POST /api/enhanced/cleanup-sessions
Each endpoint has a narrow purpose:
start-session → create enhanced automation session
continue-session → continue existing multi-turn planning session
approve-and-execute → approve captured vision and run workflow
session-status → inspect session state
live-logs → stream logs for UI monitoring
session-results → return final session output
system-metrics → expose high-level runtime metrics
cancel-session → stop active session
cleanup-sessions → remove old sessions
This is the more production-oriented path, because it splits conversation, approval, execution, monitoring and result retrieval into separate calls. A client can poll status or stream logs without holding a long request open, and a session can be cancelled or cleaned up explicitly.
Agent management
GET /api/agents/status
GET /api/agents/list
GET /api/agents/personalities
GET /api/agents/configs
POST /api/agents/configs
POST /api/agents/execute
GET /api/agents/logs/live
What each route does:
agents/status → current status of all agents
agents/list → available agent inventory
agents/personalities→ UI-visible personality metadata
agents/configs → current agent runtime settings
update configs → update temperature, approval mode, priority, enabled flag
agents/execute → execute a single agent manually
logs/live → recent agent execution logs
When you call /api/agents/list, the roster comes back sorted into four buckets (strategic, business, operations, specialized), and the runtime configuration endpoints let an operator change temperature, approval mode, priority and whether an agent is enabled.
Conversations
POST /api/conversation/start
POST /api/conversation/{conversation_id}/message
POST /api/conversation/{conversation_id}/approve
The handlers behind them:
start_conversation() → starts a Cofounder-led discovery conversation
continue_conversation() → continues the existing conversation with stored context
approve_conversation() → approves the captured vision and starts task distribution
This flow exists so the system can gather enough detail before spending effort on specialist agents. Internally it proceeds like this:
1. User sends initial idea
2. Cofounder Agent asks clarifying questions or structures the vision
3. Conversation state is persisted
4. System detects whether vision is ready for approval
5. User approves
6. System starts agent coordination
In the orchestrator, start_conversation() assigns a new conversation ID, saves what the user wrote, calls the Cofounder Agent, persists the reply, and returns a ready_for_approval flag that tells the client whether the vision is complete enough to approve.
LangGraph workflows
POST /api/workflow/execute
POST /api/workflow/resume/{thread_id}
The corresponding functions:
execute_langgraph_workflow() → runs a LangGraph-based workflow
resume_workflow() → resumes workflow from checkpoint
These endpoints expose the LangGraph layer directly. The README characterizes it as LangGraph state machines that checkpoint their progress, can correct themselves, and include interrupt nodes where a human steps in. Resumability is the key property: when a long-running workflow pauses for approval or fails midway, it should continue from its last checkpoint rather than start over and pay for every LLM call again. Our look at how LangGraph's InMemorySaver stores checkpoints explains what is actually persisted at each step.
Reports
GET /api/reports/comprehensive
GET /api/reports/{report_type}
POST /api/reports/generate-pdf
GET /api/reports/download/{filename}
GET /api/reports/domains
GET /api/reports/domains/{domain}
What each report route produces:
comprehensive report → generate full business report
specific report → generate executive, marketing, financial, etc.
generate PDF → convert report data into downloadable PDF
download report → serve generated PDF/HTML
domain reports → return modular reports by business domain
Report generation is a headline feature: executive, marketing, financial and comprehensive reports in JSON, with PDF output rendered through WeasyPrint. This is what separates the system from a chatbot, since the end result is a business artifact rather than a chat message.
Memory
GET /api/memory/graph
GET /api/memory/stats
POST /api/memory/export
DELETE /api/memory/clear
And what they return:
memory/graph → return graph nodes and edges for visualization
memory/stats → return vector/graph memory statistics
memory/export → export stored memory and outputs
memory/clear → clear memory stores
The README groups these under /api/memory/* for graph export and statistics. With them you can audit the agents' accumulated knowledge, trace their outputs, and watch how much storage each memory backend uses. Note that the clear endpoint is destructive and deserves the same access controls as any other admin action.
Analytics
GET /api/analytics/predictions
GET /api/enhanced/system-metrics
GET /api/communication/stats
Their purpose:
predictions → analyze generated outputs and estimate success/revenue/market timing
system metrics → return enhanced orchestrator metrics
communication stats → return inter-agent communication analytics
Behind them sit simple helper functions:
_analyze_project_success()
_analyze_revenue_trend()
_analyze_market_timing()
These inspect agent outputs and derive business-facing signals such as a success probability, the direction of revenue growth and recommended timing. They are heuristics over generated text, so treat their output as indicative rather than as forecasts.
Integrations
/api/integrations/*
The repository lists HubSpot, Slack and the Instagram Business API, each with a specific role:
HubSpot → CRM workflows
Slack → HITL approval notifications
Instagram → marketing automation with compliance checks
The underlying idea is that agents should push results into real business systems, not just produce content, while approval gates stand in front of anything sensitive.
WebSockets and streaming
WS /ws/agent-updates
WS /ws/agent-events
GET /api/stream/logs
What each stream carries:
/ws/agent-updates → periodically sends agent status updates
/ws/agent-events → streams queue/Redis events to frontend
/api/stream/logs → streams recent logs via server-sent response
For a multi-agent UI, this visibility is essential. Operators need to see which agent is running, which task is active, whether an approval is waiting, and where a workflow broke.
The React frontend
The client is built on this stack:
React 18
Vite
React Router
Tailwind CSS
React Flow
Recharts
Per the README, the dashboard combines a chat-driven onboarding flow, live agent status, a visual map of task flow, and views for checking PRD compliance. Its responsibilities are:
1. Capture the user’s project vision
2. Display agent execution status
3. Visualize task flow
4. Show pending approval requests
5. Display reports and analytics
6. Show memory/monitoring data
7. Connect to WebSocket streams
8. Communicate with FastAPI through api.js
The source layout is compact:
frontend/
└── src/
├── App.jsx
├── pages/
├── components/
└── services/api.js
App.jsx serves as the authenticated shell, pages/ holds the workflow, analytics and monitoring views, and components/ supplies the building blocks: panels for the dashboard, approval screens, PRD compliance views and integration settings. Every HTTP request goes through services/api.js, which adds retries and caching in one place and keeps network concerns out of the components.
The end-to-end flow
Put together, the main path through the system runs as follows:
1. User submits business vision from React UI
2. Frontend sends request to FastAPI
3. FastAPI creates project/session
4. Cofounder Agent structures vision
5. Manager Agent converts vision into roadmap and assignments
6. Specialist agents execute assigned tasks
7. Results are stored in memory
8. HITL approval is requested where required
9. Reports are generated
10. Frontend displays logs, status, graph, reports, and outputs
Mapped onto actual modules and methods, the same flow looks like this:
React UI
↓ HTTP/WebSocket
FastAPI main.py
↓
AgentOrchestrator / EnhancedOrchestrator
↓
CofounderAgent.execute()
↓
ManagerAgent.execute()
↓
_execute_specialists()
↓
FinanceAgent / MarketingAgent / LegalAgent / MoneyAgent / SalesAgent
↓
MemoryManager + GraphMemory + VectorMemory
↓
ReportGenerator
↓
Dashboard + PDF/HTML/JSON Output
Each layer has one job: the UI handles interaction, the orchestrators coordinate, agents execute, the memory managers persist context, and the report generator shapes the output.
Why a graph orchestrator instead of a chain
Multi-agent workflows need more than a series of function calls. They need:
- State transitions
- Conditional routing
- Checkpointing
- Retry behavior
- Human approval interruptions
- Recovery after failure
- Resume from previous state
The README names LangGraph state machines as the orchestration layer precisely for these capabilities. A plain prompt chain is linear:
A → B → C → Done
A business workflow branches on intermediate results:
A → B
├── if finance confidence low → retry Finance
├── if legal risk high → ask human approval
├── if marketing ready → generate campaign
└── if all complete → generate report
Low finance confidence triggers a retry, high legal risk triggers a human review, and the final report waits until every branch is complete. Encoding that as hard-coded sequential calls quickly becomes a tangle of conditionals; a graph with explicit nodes, edges and checkpoints keeps it inspectable and resumable.
What the design gets right
- Clear agent boundaries: each agent owns one business domain, which keeps outputs consistent and makes new roles easy to add.
- Workflow-first orchestration: execution is modeled as a workflow, not a stream of prompt responses.
- Parallel specialists: Finance, Marketing, Legal, Money and Sales run concurrently where dependencies allow, shortening end-to-end time.
- Shared memory: Neo4j, Qdrant, Redis and local storage together cover relationships, semantic recall, queues, caching and fallback.
- Human-in-the-loop safety: approval gates make the system usable for decisions with real consequences.
- Real-time observability: WebSockets, logs, timelines, dashboards and analytics expose what agents are doing.
- Structured reports: the output is a document you can hand to a stakeholder, not a chat transcript.
- Integration readiness: HubSpot, Slack and Instagram support show the system is meant to touch real business tools.
Hardening it for production
The foundations are solid: a modular backend, dedicated agents, a memory abstraction, an approval flow and an observable frontend. The obvious next steps are about reliability, security, monitoring, evaluation and cost control:
1. Add distributed tracing with OpenTelemetry
2. Add LangSmith or custom LLM evaluation dashboards
3. Add cost tracking per agent and per workflow
4. Add stronger RBAC for users, agents, and approvals
5. Add persistent workflow checkpoints in Postgres or Redis
6. Add retry policies per agent type
7. Add queue-based background execution with Celery/RQ/Arq
8. Add structured output validation with Pydantic schemas
9. Add agent-level unit tests and golden-output evaluation
10. Add Docker production profiles and deployment manifests
Two of these deserve emphasis. Cost tracking per agent and per workflow matters because parallel specialists multiply LLM calls, and costs are invisible until they are measured. Structured output validation with Pydantic schemas matters because every downstream step, from memory writes to reports and CRM updates, is only as reliable as the shape of the data an agent returns.
Key takeaways
- Treat the orchestration layer as the product; the LLM is one component inside it.
- Put a manager-style agent in charge of decomposition so specialist outputs stay coherent.
- Run independent agents concurrently with per-agent timeouts, and handle partial failures explicitly.
- Give each memory store one job: graph for provenance, vectors for recall, queues for coordination.
- Make approval gates, checkpoints and live observability part of the core design, not later additions.