Architecture
E-Commerce Agents is a multi-agent e-commerce platform built on Microsoft Agent Framework (MAF). Six specialized agents collaborate via A2A protocol, orchestrated by a central Customer Support agent that classifies user intent and routes requests to the right specialist.
1. System Overview
The platform comprises three tiers: a Next.js frontend, a FastAPI orchestrator gateway backed by five specialist agents, and shared infrastructure (PostgreSQL + pgvector, Redis, .NET Aspire Dashboard).
graph TB
accTitle: 1. System Overview
subgraph Client["Client Tier"]
FE["Next.js 16 Frontend<br/>(React 19 + Tailwind)"]
end
subgraph Gateway["Gateway Tier"]
ORCH["Orchestrator<br/>FastAPI :8080<br/>JWT Auth + Intent Routing"]
end
subgraph Agents["Specialist Agent Tier · A2A Protocol"]
PD["Product Discovery<br/>:8081"]
OM["Order Management<br/>:8082"]
PP["Pricing & Promotions<br/>:8083"]
RS["Review & Sentiment<br/>:8084"]
IF["Inventory & Fulfillment<br/>:8085"]
end
subgraph Infra["Infrastructure"]
PG[("PostgreSQL 16<br/>+ pgvector")]
RD[("Redis 7")]
ASPIRE[".NET Aspire Dashboard<br/>:18888"]
end
subgraph External["External Services"]
LLM["OpenAI / Azure OpenAI<br/>GPT-4.1 + Embeddings"]
end
FE -->|"REST + SSE"| ORCH
ORCH -->|"A2A /message:send"| PD
ORCH -->|"A2A /message:send"| OM
ORCH -->|"A2A /message:send"| PP
ORCH -->|"A2A /message:send"| RS
ORCH -->|"A2A /message:send"| IF
PD --> PG
OM --> PG
PP --> PG
RS --> PG
IF --> PG
ORCH --> PG
ORCH --> RD
PD -->|"Embeddings API"| LLM
ORCH -->|"ChatClient"| LLM
PD -->|"ChatClient"| LLM
OM -->|"ChatClient"| LLM
PP -->|"ChatClient"| LLM
RS -->|"ChatClient"| LLM
IF -->|"ChatClient"| LLM
ORCH -.->|"OTLP"| ASPIRE
PD -.->|"OTLP"| ASPIRE
OM -.->|"OTLP"| ASPIRE
PP -.->|"OTLP"| ASPIRE
RS -.->|"OTLP"| ASPIRE
IF -.->|"OTLP"| ASPIRE
style Client fill:#6366f1,stroke:#4f46e5,stroke-width:2px,color:#fff
style FE fill:#818cf8,stroke:#6366f1,color:#fff
style Gateway fill:#0891b2,stroke:#0e7490,stroke-width:2px,color:#fff
style ORCH fill:#22d3ee,stroke:#06b6d4,color:#0c4a6e
style Agents fill:#0d9488,stroke:#0f766e,stroke-width:2px,color:#fff
style PD fill:#2dd4bf,stroke:#14b8a6,color:#134e4a
style OM fill:#2dd4bf,stroke:#14b8a6,color:#134e4a
style PP fill:#2dd4bf,stroke:#14b8a6,color:#134e4a
style RS fill:#2dd4bf,stroke:#14b8a6,color:#134e4a
style IF fill:#2dd4bf,stroke:#14b8a6,color:#134e4a
style Infra fill:#475569,stroke:#334155,stroke-width:2px,color:#fff
style PG fill:#94a3b8,stroke:#64748b,color:#1e293b
style RD fill:#94a3b8,stroke:#64748b,color:#1e293b
style ASPIRE fill:#94a3b8,stroke:#64748b,color:#1e293b
style External fill:#d97706,stroke:#b45309,stroke-width:2px,color:#fff
style LLM fill:#fbbf24,stroke:#f59e0b,color:#78350f
2. Agent Communication Pattern
All user requests enter through the Orchestrator. The Orchestrator classifies intent, calls one or more specialist agents via A2A, and synthesizes a unified response.
sequenceDiagram
accTitle: 2. Agent Communication Pattern
actor User
participant FE as Next.js Frontend
participant ORCH as Orchestrator<br/>(FastAPI :8080)
participant LLM as OpenAI / Azure OpenAI
participant AGENT as Specialist Agent<br/>(A2AAgentHost)
participant DB as PostgreSQL + pgvector
User->>FE: Sends chat message
FE->>ORCH: POST /api/chat<br/>Authorization: Bearer {JWT}
Note over ORCH: JWT validation<br/>Set ContextVars (email, role)
ORCH->>LLM: ChatAgent.run() with system prompt<br/>+ ECommerceContextProvider
Note over LLM: Intent classification<br/>Tool selection
LLM-->>ORCH: Tool call: call_specialist_agent<br/>(agent_name, message)
ORCH->>AGENT: POST /message:send<br/>X-Agent-Secret + X-User-Email
Note over AGENT: Auth middleware validates<br/>shared secret, sets ContextVars
AGENT->>LLM: ChatAgent.run() with<br/>specialist system prompt
Note over LLM: Selects domain tools<br/>(search, check_stock, etc.)
LLM-->>AGENT: Tool calls
AGENT->>DB: Execute tool queries<br/>(asyncpg parameterized SQL)
DB-->>AGENT: Query results
AGENT-->>ORCH: A2A response (JSON)
Note over ORCH: Orchestrator LLM synthesizes<br/>specialist response into<br/>natural language
ORCH->>DB: Persist conversation + usage log
ORCH-->>FE: ChatResponse (JSON)
FE-->>User: Rendered response
3. Agent Architecture
Every specialist agent follows a consistent four-file structure. The Orchestrator is the only agent that uses FastAPI directly – all specialists use A2AAgentHost from the MAF A2A library.
graph TB
accTitle: 3. Agent Architecture
subgraph AgentHost["Specialist Agent (e.g., product_discovery/)"]
MAIN["main.py<br/>A2AAgentHost entry point<br/>Lifespan: telemetry + DB pool"]
AGENTPY["agent.py<br/>create_*_agent() -> ChatAgent<br/>Registers tools + context providers"]
TOOLS["tools.py<br/>@tool decorated functions<br/>Domain-specific DB queries"]
PROMPTS["prompts.py<br/>SYSTEM_PROMPT constant<br/>Agent persona + instructions"]
end
subgraph SharedLib["shared/ (Cross-Agent Library)"]
CONFIG["config.py<br/>Pydantic Settings"]
DBMOD["db.py<br/>asyncpg pool management"]
AUTH["auth.py<br/>AgentAuthMiddleware"]
CTX["context.py<br/>ContextVars (email, role)"]
CTXPROV["context_providers.py<br/>ECommerceContextProvider"]
FACTORY["agent_factory.py<br/>ChatClient factory (OpenAI/Azure)"]
TELEM["telemetry.py<br/>OTel setup + instrumentation"]
SHARED_TOOLS["tools/<br/>Shared tools (inventory,<br/>pricing, user, return, loyalty)"]
end
MAIN --> AGENTPY
MAIN --> DBMOD
MAIN --> AUTH
MAIN --> TELEM
AGENTPY --> TOOLS
AGENTPY --> PROMPTS
AGENTPY --> FACTORY
AGENTPY --> CTXPROV
AGENTPY --> SHARED_TOOLS
TOOLS --> DBMOD
TOOLS --> CTX
CTXPROV --> CTX
CTXPROV --> DBMOD
AUTH --> CTX
AUTH --> CONFIG
FACTORY --> CONFIG
style MAIN fill:#0ea5e9,stroke:#0284c7,color:#fff
style AGENTPY fill:#0ea5e9,stroke:#0284c7,color:#fff
style TOOLS fill:#0ea5e9,stroke:#0284c7,color:#fff
style PROMPTS fill:#0ea5e9,stroke:#0284c7,color:#fff
style CONFIG fill:#64748b,stroke:#475569,color:#fff
style DBMOD fill:#0d9488,stroke:#115e59,color:#fff
style AUTH fill:#ef4444,stroke:#dc2626,color:#fff
style CTX fill:#64748b,stroke:#475569,color:#fff
style CTXPROV fill:#64748b,stroke:#475569,color:#fff
style FACTORY fill:#f59e0b,stroke:#d97706,color:#fff
style TELEM fill:#64748b,stroke:#475569,color:#fff
style SHARED_TOOLS fill:#0d9488,stroke:#0f766e,color:#fff
Agent Inventory
| Agent | Port | Module | Key Tools |
|---|---|---|---|
| Orchestrator | 8080 | orchestrator/ | call_specialist_agent (A2A router) |
| Product Discovery | 8081 | product_discovery/ | search_products, semantic_search, compare_products, find_similar_products, get_trending_products |
| Order Management | 8082 | order_management/ | get_user_orders, get_order_details, get_order_tracking, cancel_order, modify_order, check_return_eligibility, initiate_return, process_refund |
| Pricing & Promotions | 8083 | pricing_promotions/ | validate_coupon, optimize_cart, get_active_deals, check_bundle_eligibility, get_loyalty_tier, calculate_loyalty_discount |
| Review & Sentiment | 8084 | review_sentiment/ | get_product_reviews, analyze_sentiment, get_sentiment_by_topic, get_sentiment_trend, detect_fake_reviews, compare_product_reviews |
| Inventory & Fulfillment | 8085 | inventory_fulfillment/ | check_stock, get_warehouse_availability, get_restock_schedule, estimate_shipping, compare_carriers, calculate_fulfillment_plan, place_backorder |
4. Orchestrator Pattern
The Orchestrator is the single entry point for all user traffic. It handles authentication, intent classification via LLM, agent routing via A2A, and conversation persistence.
flowchart TD
accTitle: 4. Orchestrator Pattern
REQ["Incoming Request<br/>POST /api/chat"]
JWT{"JWT Valid?"}
REJECT["401 Unauthorized"]
SETCTX["Set ContextVars<br/>(email, role, session_id)"]
LOAD["Load Conversation History<br/>+ ECommerceContextProvider"]
CLASSIFY["LLM Intent Classification<br/>via ChatAgent.run()"]
SINGLE{"Single or<br/>Multi-Intent?"}
ROUTE_ONE["call_specialist_agent<br/>(agent_name, message)"]
ROUTE_MULTI["Sequential A2A Calls<br/>to Multiple Specialists"]
A2A_CALL["POST /message:send<br/>X-Agent-Secret + X-User-Email<br/>to Specialist Agent"]
TIMEOUT{"Response<br/>OK?"}
ERROR["Error Handling<br/>Retry or Fallback Message"]
RESPONSE["Specialist Response"]
SYNTH["LLM Synthesizes<br/>Specialist Responses<br/>into Natural Language"]
PERSIST["Persist to DB<br/>conversations + messages +<br/>usage_logs + execution_steps"]
RETURN["Return ChatResponse<br/>(response, conversation_id,<br/>agents_involved)"]
REQ --> JWT
JWT -->|No| REJECT
JWT -->|Yes| SETCTX
SETCTX --> LOAD
LOAD --> CLASSIFY
CLASSIFY --> SINGLE
SINGLE -->|Single| ROUTE_ONE
SINGLE -->|Multi| ROUTE_MULTI
ROUTE_ONE --> A2A_CALL
ROUTE_MULTI --> A2A_CALL
A2A_CALL --> TIMEOUT
TIMEOUT -->|Error / Timeout| ERROR
TIMEOUT -->|OK| RESPONSE
ERROR --> SYNTH
RESPONSE --> SYNTH
SYNTH --> PERSIST
PERSIST --> RETURN
style REQ fill:#0ea5e9,stroke:#0284c7,color:#fff
style JWT fill:#ef4444,stroke:#dc2626,color:#fff
style REJECT fill:#ef4444,stroke:#dc2626,color:#fff
style SETCTX fill:#64748b,stroke:#475569,color:#fff
style LOAD fill:#0ea5e9,stroke:#0284c7,color:#fff
style CLASSIFY fill:#f59e0b,stroke:#d97706,color:#fff
style SINGLE fill:#f59e0b,stroke:#d97706,color:#fff
style ROUTE_ONE fill:#0ea5e9,stroke:#0284c7,color:#fff
style ROUTE_MULTI fill:#0ea5e9,stroke:#0284c7,color:#fff
style A2A_CALL fill:#0ea5e9,stroke:#0284c7,color:#fff
style TIMEOUT fill:#ef4444,stroke:#dc2626,color:#fff
style ERROR fill:#ef4444,stroke:#dc2626,color:#fff
style RESPONSE fill:#10b981,stroke:#059669,color:#fff
style SYNTH fill:#f59e0b,stroke:#d97706,color:#fff
style PERSIST fill:#0d9488,stroke:#115e59,color:#fff
style RETURN fill:#10b981,stroke:#059669,color:#fff
5. Auth Flow
E-Commerce Agents uses self-contained JWT authentication (PyJWT + bcrypt). There is no external identity provider. Inter-agent calls use a shared secret instead of JWT.
User Authentication
sequenceDiagram
accTitle: User Authentication
actor User
participant FE as Next.js Frontend
participant ORCH as Orchestrator API
participant DB as PostgreSQL
Note over User,DB: Signup Flow
User->>FE: Enter email, password, name
FE->>ORCH: POST /api/auth/signup
ORCH->>ORCH: Hash password (bcrypt)
ORCH->>DB: INSERT INTO users (email, password_hash, name, role='customer')
DB-->>ORCH: User created
ORCH->>ORCH: Create access token (JWT HS256, 60 min)<br/>Create refresh token (JWT HS256, 7 days)
ORCH-->>FE: { access_token, refresh_token, user }
FE->>FE: Store tokens in localStorage
Note over User,DB: Login Flow
User->>FE: Enter email, password
FE->>ORCH: POST /api/auth/login
ORCH->>DB: SELECT password_hash FROM users WHERE email = $1
DB-->>ORCH: password_hash
ORCH->>ORCH: bcrypt.checkpw(password, hash)
alt Password Invalid
ORCH-->>FE: 401 Invalid credentials
else Password Valid
ORCH->>ORCH: Create access + refresh tokens
ORCH-->>FE: { access_token, refresh_token, user }
end
Note over User,DB: Authenticated Request
FE->>ORCH: POST /api/chat<br/>Authorization: Bearer {access_token}
ORCH->>ORCH: jwt.decode(token, JWT_SECRET, HS256)
ORCH->>ORCH: Validate type='access', not expired
ORCH->>ORCH: Set ContextVars:<br/>current_user_email = sub<br/>current_user_role = role
ORCH-->>FE: Proceed to handler
Note over User,DB: Token Refresh
FE->>ORCH: POST /api/auth/refresh<br/>{ refresh_token }
ORCH->>ORCH: jwt.decode(refresh_token)<br/>Validate type='refresh'
ORCH->>DB: SELECT * FROM users WHERE email = sub
ORCH->>ORCH: Create new access + refresh tokens
ORCH-->>FE: { access_token, refresh_token, user }
Inter-Agent Authentication
sequenceDiagram
accTitle: Inter-Agent Authentication
participant ORCH as Orchestrator
participant AGENT as Specialist Agent
participant MW as AgentAuthMiddleware
ORCH->>AGENT: POST /message:send<br/>X-Agent-Secret: {AGENT_SHARED_SECRET}<br/>X-User-Email: alice.johnson@gmail.com<br/>X-User-Role: customer
AGENT->>MW: Request intercepted
alt Secret matches AGENT_SHARED_SECRET
MW->>MW: Set ContextVars:<br/>email = X-User-Email<br/>role = X-User-Role
MW-->>AGENT: Proceed to handler
Note over AGENT: Tools read user identity<br/>from ContextVars
else Secret invalid
MW-->>ORCH: 401 Invalid agent secret
end
RBAC Roles
| Role | Access Level | Description |
|---|---|---|
customer | Default | Standard shopping, orders, reviews |
power_user | Extended | Access to advanced agent features via marketplace |
seller | Seller tools | Draft review responses, view sentiment reports |
admin | Full | Approve access requests, manage agent catalog, all operations |
For the full security architecture — threat model, guardrails middleware stack, identity-spoof detection, SQL ownership filters, and production hardening checklist — see docs/security-guide.md.
6. Data Flow
End-to-end data flow showing how a user request traverses the system, from initial HTTP request through agent processing to database persistence.
flowchart LR
accTitle: 6. Data Flow
subgraph Input["Request"]
USER_MSG["User Message<br/>'Find me wireless headphones<br/>under $200 with good reviews'"]
end
subgraph Auth["Authentication"]
JWT_CHECK["JWT Decode<br/>HS256 Validation"]
CTX_SET["ContextVars Set<br/>email + role"]
end
subgraph Orchestration["Orchestration Layer"]
CTX_LOAD["Context Loaded<br/>User profile + recent orders<br/>(ECommerceContextProvider)"]
LLM_ROUTE["LLM Classifies Intent<br/>-> product-discovery<br/>-> review-sentiment"]
end
subgraph A2A_1["Product Discovery Agent"]
PD_LLM["ChatAgent selects tools"]
PD_SEARCH["semantic_search()<br/>Embed query -> pgvector"]
PD_FILTER["search_products()<br/>category + price filter"]
PD_STOCK["check_stock()<br/>Cross-check inventory"]
end
subgraph A2A_2["Review & Sentiment Agent"]
RS_LLM["ChatAgent selects tools"]
RS_REVIEWS["get_product_reviews()"]
RS_SENT["analyze_sentiment()<br/>Rating breakdown + themes"]
end
subgraph Synthesis["Response Synthesis"]
COMBINE["Orchestrator LLM combines:<br/>- Product results<br/>- Stock status<br/>- Review summaries<br/>into natural language"]
end
subgraph Persist["Persistence"]
CONV["conversations table"]
MSG["messages table<br/>(user + assistant)"]
USAGE["usage_logs table<br/>+ execution_steps"]
end
subgraph Output["Response"]
RESP["ChatResponse<br/>Products + reviews + stock<br/>agents_involved: [product-discovery,<br/>review-sentiment]"]
end
USER_MSG --> JWT_CHECK
JWT_CHECK --> CTX_SET
CTX_SET --> CTX_LOAD
CTX_LOAD --> LLM_ROUTE
LLM_ROUTE -->|"A2A call 1"| PD_LLM
PD_LLM --> PD_SEARCH
PD_LLM --> PD_FILTER
PD_LLM --> PD_STOCK
LLM_ROUTE -->|"A2A call 2"| RS_LLM
RS_LLM --> RS_REVIEWS
RS_LLM --> RS_SENT
PD_SEARCH --> COMBINE
PD_FILTER --> COMBINE
PD_STOCK --> COMBINE
RS_REVIEWS --> COMBINE
RS_SENT --> COMBINE
COMBINE --> CONV
COMBINE --> MSG
COMBINE --> USAGE
COMBINE --> RESP
style USER_MSG fill:#0ea5e9,stroke:#0284c7,color:#fff
style JWT_CHECK fill:#ef4444,stroke:#dc2626,color:#fff
style CTX_SET fill:#64748b,stroke:#475569,color:#fff
style CTX_LOAD fill:#0ea5e9,stroke:#0284c7,color:#fff
style LLM_ROUTE fill:#f59e0b,stroke:#d97706,color:#fff
style PD_LLM fill:#f59e0b,stroke:#d97706,color:#fff
style PD_SEARCH fill:#0ea5e9,stroke:#0284c7,color:#fff
style PD_FILTER fill:#0ea5e9,stroke:#0284c7,color:#fff
style PD_STOCK fill:#0ea5e9,stroke:#0284c7,color:#fff
style RS_LLM fill:#f59e0b,stroke:#d97706,color:#fff
style RS_REVIEWS fill:#0ea5e9,stroke:#0284c7,color:#fff
style RS_SENT fill:#0ea5e9,stroke:#0284c7,color:#fff
style COMBINE fill:#f59e0b,stroke:#d97706,color:#fff
style CONV fill:#0d9488,stroke:#115e59,color:#fff
style MSG fill:#0d9488,stroke:#115e59,color:#fff
style USAGE fill:#0d9488,stroke:#115e59,color:#fff
style RESP fill:#10b981,stroke:#059669,color:#fff
7. Technology Decisions
| Decision | Choice | Rationale |
|---|---|---|
| Agent Framework | Microsoft Agent Framework (MAF) Python SDK | First-class ChatAgent abstraction with @tool decorators, ContextProvider, and built-in A2A support. Avoids hand-rolling function-calling loops. |
| Inter-Agent Protocol | A2A via agent-framework-a2a | Standard protocol for agent-to-agent communication. Each specialist exposes /message:send. Decoupled from transport – could swap HTTP for gRPC later. |
| LLM Provider | OpenAI / Azure OpenAI (configurable) | Single ChatClient interface via MAF. Swap with LLM_PROVIDER env var. Azure for production (managed identity, RBAC); OpenAI for local dev — or any OpenAI-compatible endpoint (Ollama, LM Studio, vLLM, OpenRouter) via LLM_BASE_URL for a fully local, zero-cost dev loop. |
| Database | PostgreSQL 16 + pgvector | Single database for relational data and vector embeddings. text-embedding-3-small (1536 dims) for semantic product search. IVFFlat index for fast cosine similarity. |
| Web Framework | FastAPI (orchestrator) + Starlette (specialists) | FastAPI for the orchestrator because it needs REST endpoints (auth, chat, marketplace, admin). Specialists use the lighter A2AAgentHost which wraps Starlette. |
| Auth | Self-contained JWT (HS256) + bcrypt | No external IdP dependency for the demo. Access tokens (60 min) + refresh tokens (7 days). Inter-agent auth via shared secret header. |
| User Context | Python ContextVars | Request-scoped state (email, role) set by auth middleware, read by any @tool function. No need to pass user info through function parameters. |
| DB Access | asyncpg (raw SQL) | Maximum control over queries. No ORM overhead. Parameterized $1, $2 syntax prevents SQL injection. Connection pool (5-20) per agent. |
| Telemetry | OpenTelemetry -> .NET Aspire Dashboard | Auto-instrumented: httpx (LLM + A2A calls), asyncpg (DB queries), FastAPI/Starlette (HTTP). Custom spans for A2A calls and tool execution. All correlate via trace_id. |
| Cache | Redis 7 | Session data and conversation state caching. Alpine image for minimal footprint. |
| Frontend | Next.js 16 + React 19 + Tailwind + shadcn/ui | Server Components by default, pnpm for package management. Minimal client-side JS. |
| Containerization | Docker Compose + multi-target Dockerfile | All 6 agents share one Dockerfile with ARG AGENT_NAME. Each agent is a separate service with its own port. Single docker compose up --build to start everything. |
| Package Management | uv (Python) + pnpm (Node) | uv for fast dependency resolution and virtual environment management. pnpm for disk-efficient node_modules. |
8. A2A Protocol
All inter-agent communication uses the A2A (Agent-to-Agent) protocol. The orchestrator calls each specialist via an HTTP POST to /message:send. There is no message broker or event bus — calls are synchronous HTTP within the Docker network.
Endpoint
POST http://{agent-host}:{port}/message:send
Each specialist registers this endpoint automatically via A2AAgentHost from agent-framework-a2a. The host, port, and service name come from the AGENT_REGISTRY env var, which the orchestrator reads at startup.
Request format
{
"message": {
"parts": [
{ "type": "text", "text": "Find wireless headphones under $200" }
]
},
"history": [
{ "role": "user", "content": "What headphones do you have?" },
{ "role": "assistant", "content": "We have several options..." }
]
}
The orchestrator forwards the last 10 conversation turns (each truncated to 500 characters) in history. This gives specialists enough context for follow-up questions without unbounded payload growth.
Authentication headers
| Header | Value | Purpose |
|---|---|---|
X-Agent-Secret | AGENT_SHARED_SECRET env var | Validates the caller is a trusted orchestrator; rejected with 401 if missing or wrong |
X-User-Email | e.g. alice@example.com | Propagates user identity for per-user data scoping in tool queries |
X-User-Role | customer, seller, admin | Propagates RBAC role for permission checks inside @tool functions |
AgentAuthMiddleware in shared/auth.py validates the secret and sets ContextVars (current_user_email, current_user_role) that every @tool function reads directly — no need to thread user identity through function parameters.
Response
The specialist returns its full agent response as JSON. The orchestrator passes this to its own LLM for synthesis into the final natural-language reply.
Why synchronous HTTP?
The platform uses synchronous A2A calls (one specialist at a time) rather than parallel fan-out for two reasons: each agent turn is fast enough that sequential calls stay well within acceptable latency, and later specialists often need context from earlier results (Pricing needs to know which products were recommended before it can optimize the cart).
For the full sequence diagram showing JWT validation, context loading, and A2A call flow, see §2. Agent Communication Pattern.
Related
docs/agent-flows.md— five multi-agent collaboration sequence diagramsdocs/security-guide.md— threat model, guardrails, full auth hardening checklistdocs/maf-best-practices.md— MAF @tool, middleware, and orchestration patternsdocs/adding-an-agent.md— step-by-step guide to adding a specialist agent- Project README
Source: docs/architecture.md — this page is generated from the repository.