APDT is a local, single-user framework for running structured multi-agent analysis on product and business questions. You submit a question or idea — in the terminal or a browser chat window — and a coordinated team of specialist AI agents (product strategy, monetisation, regulatory, build planning, UX / service design) analyses it. A senior Head / Product Director agent synthesises the specialist outputs into a direct, conversational response.
This is a framework and example project, not a polished product. It is intended as a starting point for builders who want to explore multi-agent LangGraph architectures. Expect rough edges.
Not actively maintained. This repository is published as a reference. Issues and pull requests are not monitored.
- What it can do today
- What it does not yet do
- Tech stack
- Assumptions
- Quickstart
- Before first run
- How to run
- Configuring OpenAI
- Configuring the agents
- Limitations
- Accept a free-text product or business question via the CLI or a Streamlit browser chat UI.
- Route the question to the relevant subset of specialist agents using LLM-driven selection.
- Run each selected specialist in sequence; each one can see the outputs of prior agents.
- Synthesise all specialist outputs into a single prose response via the Head / Product Director agent.
- Persist conversation history, agent outputs, and LLM usage logs to a local SQLite database.
- Retrieve relevant past decisions, risks, and artefacts from a local FAISS vector index and inject them into each new run as context.
- Write structured markdown artefacts (product briefs, decisions, regulatory notes, build packages) to
docs/. - Track per-agent token usage and cost in a Cost Monitor tab in the Streamlit UI (stacked bar chart, one bar per run).
- Support multi-turn conversations: each chat thread persists in SQLite and can be resumed.
- Upload PDF or DOCX files as conversation context, with optional persistence to project memory.
- Pause for human approval when guardrails fire (high risk count, cost threshold, token limit), and resume after an approve / reject decision in the CLI or UI.
- Per-agent model and temperature configuration — run cheap agents on
gpt-4o-miniand higher-stakes agents on a stronger model, all from YAML.
- Agent tools are not enabled. The framework includes a
src/tools/directory with stubs for web search and GitHub handoff, but no tool is wired into any agent's reasoning loop. Agents reason only from their prompts and injected context — they cannot query the web, read live data, or call external APIs mid-run. - No streaming. Responses are returned in full after all agents complete; there is no token-by-token stream to the UI.
- No authentication or multi-user access. The system is local-first and single-user by design. There is no login, session isolation, or access control.
- No production hardening. Error handling is best-effort. The system is not designed for internet-facing deployment.
- No memory across unrelated projects by default. The FAISS index and SQLite records are namespaced by
project_name, but there is no cross-project retrieval or shared knowledge base. - Agent profiles are pre-configured for product/business analysis. The built-in YAML profiles describe a product team. To use APDT for a different domain you will need to rewrite the agent profiles in
config/agent_profiles/.
| Layer | Technology |
|---|---|
| Agent orchestration | LangGraph |
| LLM provider | OpenAI API (gpt-4o-mini default; configurable per agent) |
| Embeddings | OpenAI text-embedding-3-small |
| Vector index | FAISS (faiss-cpu) |
| Long-term memory | SQLite (data/app.db) via the standard library sqlite3 |
| Browser UI | Streamlit |
| CLI | Python (cli.py) |
| Config | YAML (config/) |
| Data validation | Pydantic v2 |
| Language | Python 3.11+ |
- You have a valid OpenAI API key with access to the models referenced in agent profiles (defaults:
gpt-4o-mini,gpt-4.1-mini,text-embedding-3-small). - You are running locally on a single machine. No containerisation, cloud deployment, or reverse proxy is assumed.
- Python 3.11 or later. The project was developed on 3.13; earlier versions in the 3.11–3.12 range should work but are untested.
- A virtual environment is strongly recommended to avoid dependency conflicts.
- The
data/directory is writable. SQLite, FAISS, checkpoints, and logs all land here. - The OpenAI embeddings API is reachable. Semantic retrieval will fail gracefully if it is not, but the first run with an empty FAISS index will skip retrieval entirely.
# 1. Clone and create a virtual environment
python -m venv venv
.\venv\Scripts\Activate.ps1 # Windows
# source venv/bin/activate # macOS / Linux
# 2. Install dependencies
pip install -r requirements.txt
# 3. Configure your OpenAI API key (see below)
copy .env.example .env
# Edit .env and set OPENAI_API_KEY=sk-...
# 4. Initialise the database
python scripts/init_db.py
# 5. Run a product analysis from the terminal
python cli.py --prompt "Analyse my B2B SaaS idea"
# 6. Or open the browser UI
streamlit run src/ui/streamlit_app.pyComplete these steps before submitting your first query. Skipping them will result in database errors or missing context.
copy .env.example .envOpen .env and set:
OPENAI_API_KEY=sk-...
Nothing else in .env is required for a basic run.
The database schema must be created before any workflow runs:
python scripts/init_db.pyThis creates data/app.db with all required tables. It is safe to run multiple times (idempotent).
If the repository you cloned contains data from a previous user's sessions, clear the following before your first run:
| What | Where | How |
|---|---|---|
| SQLite database | data/app.db and data/app.db.bak |
Delete the files; init_db.py will recreate app.db |
| FAISS vector index | data/vectors/index.faiss and data/vectors/metadata.json |
Delete both files |
| LangGraph checkpoints | data/checkpoints/ |
Delete all files inside the folder |
| Run logs | data/logs/ |
Delete all files inside the folder |
| Generated artefacts | docs/product_briefs/, docs/decisions/, docs/monetisation/, docs/regulatory/, docs/build_packages/ |
Delete any .md files inside these folders |
You can keep the folder structure itself — only the files inside need to go.
If you want semantic retrieval to surface past decisions and risks, populate the FAISS index from whatever is already in SQLite:
python scripts/seed_project.py --project-name MyProjectOn a fresh install with an empty database this does nothing and can be skipped. The workflow runs fine without a populated index — retrieval is non-blocking.
# Basic analysis
python cli.py --prompt "What are the key risks of launching a marketplace?"
# Continue an existing conversation thread
python cli.py --prompt "What about regulatory risk?" --thread-id <thread-id>
# Verbose output (per-agent tokens, full risk list)
python cli.py --prompt "..." --verbose
# Save results to SQLite and name the project
python cli.py --prompt "..." --save --project-name "MyProject"
# Upload a document as context
python cli.py --prompt "Analyse this brief" --file brief.pdf
# Persist a document to project memory + embed for semantic search
python cli.py --prompt "..." --file brief.pdf --persist-doc --auto-embedstreamlit run src/ui/streamlit_app.py
# Opens at http://localhost:8501The UI has two tabs:
- Chat — conversational interface; each message triggers a full multi-agent run. Past conversations are listed in the sidebar by LLM-generated name and can be resumed.
- Cost Monitor — stacked bar chart of per-agent LLM costs, one bar per completed run, ordered chronologically. Refreshes each time you open the tab.
# Full test suite
python -m pytest tests/ -q
# Specific areas
python -m pytest tests/test_agents.py -v
python -m pytest tests/test_graph.py -v
python -m pytest tests/test_memory.py -vAll LLM and embeddings calls go through src/services/llm.py and src/services/embeddings.py. Both read from config/settings.yaml and the OPENAI_API_KEY environment variable.
llm:
default_model: "gpt-4o-mini" # fallback model for any agent without its own setting
temperature: 0.7 # fallback temperature
max_tokens: 2000
embeddings:
model: "text-embedding-3-small"Each agent profile can declare its own model and temperature:
model: "gpt-4o"
temperature: 0.2If these fields are absent, the global defaults apply. The actual model used per agent is recorded in AgentResult.metadata["model"] and in the usage_logs SQLite table, so mixed-model runs are fully traceable.
Default model assignments (as shipped):
| Agent | Model | Temperature |
|---|---|---|
| Orchestrator (routing only) | gpt-4o-mini | 0.3 |
| Head / Product Director (synthesis) | gpt-4.1-mini | 0.4 |
| Product Strategist | gpt-4o-mini | 0.7 |
| Monetisation | gpt-4o-mini | 0.4 |
| Regulatory | gpt-4o-mini | 0.2 |
| Build Planner | gpt-4o-mini | 0.4 |
| UX / Service Design | gpt-4o-mini | 0.7 |
Agent behaviour is driven entirely by YAML profiles in config/agent_profiles/. Each file defines:
name,role,purpose— how the agent describes itself in promptssystem_style— the persona the agent adoptsgoals,responsibilities,constraints— shape the reasoningdecision_rules— explicit decision logic (e.g. when to escalate, when to flag risk)model,temperature— per-agent LLM settings (optional; falls back to global defaults)
To retarget the framework for a different domain (e.g. software engineering decisions, marketing strategy, legal analysis), rewrite the agent profiles. The pipeline, memory, and UI require no code changes for a domain swap.
To add a new specialist agent, see docs/architecture/adding-a-specialist.md. The pattern is:
- Create a YAML profile in
config/agent_profiles/. - Create a Python class in
src/agents/that inherits fromBaseAgent. - Register the agent name in
src/graph/nodes.py. - Add to the routing prompt in
src/graph/nodes.pyso the LLM knows it exists.
APDT runs on your local machine and assumes a single concurrent user. There is no authentication, no session isolation, no rate limiting, and no protection against concurrent writes to the SQLite database. Do not expose the Streamlit UI on a public network.
The src/tools/ directory contains stubs for web search (web_search.py) and GitHub Copilot handoff (github_handoff.py), but these are not wired into any agent's reasoning loop. Agents cannot search the web, call APIs, or read live data mid-run. All reasoning is based on the prompt, the injected product context, and whatever has been previously stored in the vector index.
APDT is a demonstration of a multi-agent LangGraph pattern. It is not hardened for production:
- No input sanitisation beyond basic string checks.
- No output validation beyond Pydantic schema enforcement.
- No retry logic for transient API failures beyond what the OpenAI client provides.
- The FAISS index is not optimised for large document collections.
- All agent outputs are written to disk as plain markdown without further processing.
Every query runs multiple LLM calls (one per selected specialist, plus orchestration and Head synthesis). A typical full run with all five specialists currently costs approximately $0.002–$0.015 USD at standard gpt-4o-mini / gpt-4.1-mini rates. Costs are logged per-agent in usage_logs and visible in the Cost Monitor tab, but APDT does not enforce spending limits by default.
The UI and CLI both wait for all agents to complete before showing the response. On a slow connection or with many specialists selected, this can mean a wait of 15–30 seconds before any output appears.
LangGraph checkpoints (used for thread continuity within a session) are stored in an InMemorySaver and are lost when the Python process exits. Conversation message history and usage logs persist to SQLite and survive restarts, but the LangGraph execution state does not. A thread interrupted mid-run cannot be resumed after a process restart.
This repository is published as a reference implementation. There is no ongoing maintenance, no issue tracker response, and no guarantee of compatibility with future versions of LangGraph, Streamlit, or the OpenAI API.
APDT/
├── cli.py ← Terminal entry point
├── config/
│ ├── settings.yaml ← Global model, memory, guardrail settings
│ ├── agent_profiles/ ← One YAML file per agent (behaviour + model)
│ └── products/ ← Product context template
├── src/
│ ├── agents/ ← Agent classes (BaseAgent + specialists)
│ ├── graph/ ← LangGraph pipeline (state, nodes, routing, workflow)
│ ├── memory/ ← SQLite store, FAISS vector index, checkpointing
│ ├── services/ ← LLM and embeddings wrappers
│ ├── tools/ ← Artefact writer, usage logger, tool stubs
│ └── ui/ ← Streamlit browser UI
├── scripts/ ← Init, seed, and smoke-test scripts
├── tests/ ← pytest suite (750+ tests)
├── data/
│ ├── app.db ← SQLite database (created by init_db.py)
│ ├── vectors/ ← FAISS index files
│ ├── checkpoints/ ← LangGraph in-memory checkpoints (transient)
│ └── logs/ ← Run logs
└── docs/
├── architecture/ ← Architecture documentation
└── [artefact folders]/ ← Generated markdown outputs
The pipeline is a linear LangGraph workflow with conditional branching:
ingest → retrieve_context → select_agents → [specialists*] → head_product_director → approval_check → finalize
- ingest — validates and normalises the user input.
- retrieve_context — embeds the query and searches the FAISS index for relevant past context; always non-blocking.
- select_agents — calls
gpt-4o-mini(JSON mode) to decide which specialist agents are needed for this query. - specialists — the selected agents run in sequence; each sees prior outputs as context.
- head_product_director — reads all specialist outputs and writes a single conversational prose response.
- approval_check — fires guardrails (risk count, cost, token limit); pauses for human decision if triggered.
- finalize — writes artefacts to
docs/and marks the run complete.
The shared GraphState object flows through every node, accumulating outputs, token usage, errors, and approval state.
| Document | Contents |
|---|---|
docs/architecture/overview.md |
Full module map, data flow, state structure |
docs/architecture/memory-layers.md |
Short-term checkpoint vs long-term SQLite vs FAISS retrieval |
docs/architecture/adding-a-specialist.md |
Step-by-step guide for adding a new specialist agent |