title: why-agent
emoji: π
colorFrom: indigo
colorTo: purple
sdk: docker
app_port: 7860
pinned: false
license: mit
why-agent β internal working doc
Owners: Mapo, Isa
This is our working doc. It's where we record what we agreed on, why we agreed on it, and who owns what. Update it when decisions change.
1. What we're building
An autonomous root-cause agent for data. User asks "why did metric X move?" β agent investigates and returns a structured report with an evidence chain. Works against any user-provided DuckDB/Parquet dataset. Built on AMD MI300X, deployed to Streamlit Community Cloud.
Working name: Why Agent. Repo name: why-agent.
2. Why this, not something else
We considered three other directions and rejected them. The reasoning matters β it's our defense if we're tempted to scope-creep later.
| Considered | Why we rejected it |
|---|---|
| Multi-agent research over arXiv | Crowded space (Undermind, Elicit, Consensus). No clear users beyond PhD niche. |
| Conflict-aware research agent | Intellectually interesting, no real buyer. "Neat" β "needed." |
| Generic NL-to-SQL agent | Saturated. Every cloud and BI vendor ships one. We can't out-product Snowflake. |
| Multi-agent diagnostic system | One capable agent teaches us more about agent fundamentals than orchestrating several shallow ones. |
What makes why-agent the right pick:
- Real users: any data team at any company > a few hundred people.
- Real gap: only Tellius does autonomous RCA, and it's closed enterprise. No OSS equivalent.
- Plays to us: Isa lives this problem at PayPal; Mapo has shipped RAG + agentic systems.
- Beyond simple RAG: required by the hackathon brief, naturally true here.
3. Business insight (the thing this fixes)
Every company runs on metrics. When a metric moves unexpectedly, an analyst spends 30β90 minutes doing mechanical work:
Confirm the drop is real Decompose by every dimension they can think of Find the anomalous slice Drill in further Cross-check related signals Write up the conclusion
This is expert-level but repetitive. It's exactly the shape of work agents do well. But today's tools answer "what is X?", not "why did X change?".
The market split:
- Descriptive layer (NL-to-SQL): saturated. Snowflake Cortex, Databricks Genie, Hex, Julius, Looker+Gemini, etc.
- Diagnostic layer (autonomous RCA): one closed-source vendor (Tellius). Nothing in OSS. This is where we play.
Our wedge in one sentence:
What Looker shows you, why-agent investigates for you.
4. Architecture overview
Judge / user Cost
β ββββ
βΌ
ββββββββββββββββββββββββββββββββ $0
β Streamlit Cloud β
β https://why-agent... β
β β
β UI + agent + tools + data β
β all in one Python process β
ββββββββββββββββ¬ββββββββββββββββ
β HTTPS, OpenAI-compat
βΌ
ββββββββββββββββββββββββββββββββ $1.99/hr
β AMD MI300X droplet β (when ON)
β vLLM + Llama-3.3-70B BF16 β
ββββββββββββββββββββββββββββββββ
Three logical pieces:
- The model β vLLM serving Llama 70B on MI300X. Heavy, expensive.
- The agent β Python code (LangGraph). Light, runs anywhere.
- The data β DuckDB on Parquet + a YAML semantic layer. Tiny.
Everything except the model lives inside one Streamlit app on Streamlit Cloud. The model is reached via HTTPS to the AMD droplet.
Why this split
- The model needs a GPU. Nothing else does.
- Streamlit Cloud is free; GPU droplets are $1.99/hr.
- The agent can use Anthropic API as a fallback when the GPU is off.
- Iteration speed: code changes don't redeploy the GPU.
Three model backends, env-switchable
MODEL_BACKEND=minimax β MiniMax API (MiniMax-M2.7). Use Days 1-2 (no GPU). MODEL_BACKEND=vllm β MI300X. Use Day 3+ for integration & demo. MODEL_BACKEND=replay β Pre-recorded traces. For demo when GPU off.
This is critical infrastructure β implement on Day 1.
5. The agent
Loop
plan β decompose β drill β cross-check β critique β report
β β
βββββββ if evidence weak, loop back ββββββββ
A LangGraph state machine. Each step is an explicit node. State persists across the whole investigation.
When critique returns VERDICT: weak, its justification text is stored
in state.critique_feedback and injected into the next phase's system
prompt as a targeted directive β so the agent retries the specific gap
identified, rather than re-exploring from scratch and burning retry budget.
The four tools
We deliberately have only four. Fewer integrations = fewer demo failure modes. Hypothesis tracking lives in agent prompt + memory, not in a tool.
| Tool | Purpose |
|---|---|
inspect_schema(table) |
Returns columns, types, sample values, business descriptions from semantic layer |
run_sql(query) |
Executes a read-only DuckDB query, returns rows |
decompose_metric(metric, dims) |
Slices metric by each dim, ranks slices by anomaly magnitude |
compare_periods(metric, before, after, segment) |
Quantifies how a slice changed between two windows |
Tools were cut from six. Resist re-adding them. The agent gets smarter by reasoning better, not by having more tools.
What "beyond simple RAG" means here
| Simple RAG | why-agent |
|---|---|
| 1 retrieval | Many SQL queries, planned |
| 1 generation | Multi-step loop |
| Static | Hypothesis-driven branching |
| No self-eval | Self-critique before reporting |
| Document-bound | Operates on live structured data |
6. Data + semantic layer
Dataset
why-agent works against any user-provided data β drop Parquet files
into data/parquet/ and point the semantic layer at them. There is no
fixed dataset baked into the project.
The current demo dataset is a marketing/CRM extract:
| File | Contents |
|---|---|
campaigns.parquet |
Campaign metadata and performance metrics |
client_first_purchase_date.parquet |
Customer acquisition timeline |
holidays.parquet |
Holiday calendar for seasonality context |
messages.parquet |
Message-level send/open/click events |
DuckDB view registration: All *.parquet files in PARQUET_DIR are registered as independent DuckDB views named after their file stem. There are no implicit JOINs between views β cross-table queries require explicit INNER JOIN / LEFT JOIN clauses. The agent learns which tables need joining from the joins section returned by inspect_schema, and writes SQL with explicit joins. This keeps the system generic: any set of parquet files is registered as-is, and the semantic layer YAML defines the relationships.
The semantic layer
A single data/semantic_layer.yml file. Defines:
- Tables: column names, types, business meaning, and primary keys (supports composite PKs via a list)
- Metrics: named measures the agent can compute (
open_rate,messages_sent, etc.) - Dimensions: slicing axes for decomposition (
channel,topic,eventually_converted, etc.) - Relationships: join keys between tables
- Filters: globally applied rules (e.g., exclude test campaigns)
- Value labels: what enum values mean (e.g.,
bulkvstriggervstransactional) - Gotchas: dataset-specific analysis pitfalls surfaced to the agent at plan time
This artifact is the contract between Isa and Mapo. Isa produces it; Mapo consumes it. Once it stabilizes, both of us build forward without blocking each other.
7. Tech stack (locked)
| Layer | Choice |
|---|---|
| Hosting | Streamlit Community Cloud (free, public URL) |
| Frontend | Streamlit |
| Backend | LangGraph agent + tools (pure Python, in-process) |
| Orchestration | LangGraph |
| Model | Llama-3.3-70B-Instruct, BF16 |
| Inference | vLLM on ROCm |
| Hardware | AMD MI300X (1 GPU) |
| Data engine | DuckDB |
| Data format | Parquet |
| Semantic layer | YAML |
| Tracing | LangSmith (free tier) |
| Validation | Pydantic |
| Deps | uv |
| Lint | Ruff |
| Containers | Docker |
8. Demo strategy
Two modes the public URL supports
- π’ Live MI300X mode β full live agent against the GPU. Available during scheduled windows we post.
- πΌ Replay mode β pre-recorded canonical investigations, played back from saved JSON traces. Always available.
- π¬ Anthropic fallback β for ad-hoc judge questions when GPU is off.
Demo scenarios (rehearsed, deterministic)
- Why did message open rate drop in the most recent campaign? Expected: specific campaign/segment underperformance, tied to send-time or audience slice.
- Why did new customer acquisition spike in a particular month? Expected: campaign concentration or holiday effect in the acquisition data.
- Why is weekend engagement consistently lower than weekday? Expected: structural pattern, agent should distinguish from anomaly.
Scenario 1 is our hero demo.
One thing to verify early
Run scenario 1 end-to-end on Day 3. If the agent can't reach the conclusion from the data alone, we need a different question. Don't wait until Day 5 to find out.
9. Cost & GPU budget
We have $100 in AMD Developer Cloud credits = ~50 GPU-hours.
| Phase | GPU hrs | $ |
|---|---|---|
| Day 0 β validate vLLM + Llama 70B serves | 3 | $6 |
| Days 1β2 β local build (no GPU) | 0 | $0 |
| Day 3 β first integration | 4 | $8 |
| Day 4 β iteration & prompt tuning | 6 | $12 |
| Day 5 β record replays + polish | 8 | $16 |
| Day 6 β demo day | 12 | $24 |
| Reserve | 17 | $34 |
| Total | 50 | $100 |
Rules
- DESTROY the droplet, don't stop it. Stopped droplets still bill.
- Credits expire 30 days after activation. Activate close to use.
- One GPU only β never the 8x ($15.92/hr will burn through credits in 6 hours).
- ~$5 of our own money for a 200GB block volume to cache model weights across droplet recreations is a sensible add-on. Worth confirming.
10. Team split & ownership
The split mirrors the architecture: Isa owns data + meaning,
Mapo owns agent + interface. The handoff is semantic_layer.yml.
Isa owns
- Demo dataset β Parquet files in
data/parquet/ - Semantic layer YAML
- Ground-truth validation: "is the agent's answer actually right?"
- Build-in-public post about the data layer
Mapo owns
- LangGraph state machine + agent loop
- The four tools (Pydantic schemas, implementations)
- LLM client (multi-backend switching)
- Streamlit UI (chat, reasoning trace, replay picker)
- vLLM-on-MI300X setup scripts
- Streamlit Cloud deployment
- Build-in-public post about the agent design
Shared
- Demo script & live narration
- README, submission video, final pitch
- Day-end sync (15 min) to surface blockers
11. Day-by-day plan (rough)
| Day | Mapo | Isa | Joint |
|---|---|---|---|
| 0 (pre) | Validate vLLM on MI300X, write start_vllm.sh, destroy droplet |
Prepare demo Parquet files, drop into data/parquet/ |
Repo scaffolded, README locked |
| 1 | Agent loop + tools against Anthropic API | Draft semantic_layer.yml v1 |
Agree on tool I/O signatures |
| 2 | First end-to-end agent investigation working | Refine semantic layer, prep demo scenarios | Smoke-test with provided dataset |
| 3 | Switch to vLLM backend, run scenario 1 live | Validate scenario 1 ground truth | First real demo run; identify gaps |
| 4 | Prompt tuning, self-critique node | Scenario 2 + 3 ground truth | Build-in-public posts |
| 5 | Streamlit polish, record replays, deploy to Cloud | Final dataset cleanup, write evaluation notes | Rehearse demo |
| 6 | Final fixes, submission prep | Final fixes, submission prep | Submit + pitch |
What's allowed to slip
- Scenario 3 (the structural-pattern one)
- Streamlit polish beyond functional
- Build-in-public posts after the first two
What is NOT allowed to slip
- Scenario 1 working end-to-end by Day 3
- Public URL up and demoable by Day 5
- Replay mode working by Day 5
12. Risks & open questions
Risks we can name today
- vLLM-on-MI300X setup stalls Day 1. Mitigation: validate on Day 0, keep Anthropic API fallback always wired.
- The "right answer" for our chosen scenario isn't reachable from the data alone. Mitigation: pick scenario on Day 0; smoke-test end-to-end by Day 3.
- Streamlit Cloud RAM limit exceeded by data + agent + LangGraph process overhead. Mitigation: keep Parquet files under 300 MB total; profile on Day 2.
- Live demo timing variance β agent takes 90s instead of 60s. Mitigation: don't claim "60 seconds"; frame as "minutes vs hours."
- One of us gets sick. Mitigation: README + repo are clear enough the other can demo solo.
Open questions to resolve early
- Which specific question is our hero demo?
- Is the semantic layer accurate enough for the demo dataset?
- Do we pay $5 for the model-weights block volume?
- Do we want a custom domain, or is
*.streamlit.appfine? (default: streamlit.app fine)
13. Working agreements
- Decisions go in this doc. If we changed our minds, edit it. No re-litigating in chat.
- Day-end sync, 15 min. Just blockers and tomorrow's priority.
- Push to main freely until Day 4. From Day 5: PRs only.
- No new tools, libraries, or scope past Day 3 without explicit agreement from both of us.
- The semantic layer is a contract. Once stable, breaking changes require a heads-up.
14. Implementation status
| Component | Status |
|---|---|
LLM client β 3 backends (minimax, vllm, replay) |
β done |
Pydantic state model (InvestigationState) |
β done |
| LangGraph state machine (6-phase loop) | β done |
inspect_schema tool |
β done β derived dimensions surface SQL expression |
run_sql tool |
β done |
compare_periods tool |
β done |
decompose_metric tool |
β done |
| System + critique prompts | β done |
| REPL for local testing | β done |
| Streamlit UI | β done |
Demo dataset in data/parquet/ |
β done |
| Replay recording script | β¬ pending |
| vLLM Docker + MI300X scripts | β¬ pending |
15. Getting started (dev setup)
# Prerequisites: Python 3.12+, uv installed
git clone https://github.com/Isa-Mapo-Hackathon/why-agent
cd why-agent
uv sync
# Copy and fill in .env
cp .env.example .env
# Set MINIMAX_API_KEY (get from MiniMax dashboard)
# PARQUET_DIR defaults to data/parquet β point at data/dev for the toy dataset
# Run the test suite (120 tests, no network required)
uv run pytest
# Interactive REPL against the real MiniMax API
uv run python scripts/repl_graph.py
# > Q: Why did PR activity drop on Oct 21 2018?
# Lint + format (must be clean before any commit)
uv run ruff check --fix && uv run ruff format
# Run the app β FastAPI backend + Next.js frontend (default)
uv run uvicorn client.backend.main:app --reload --port 8000 # Terminal 1
cd client/frontend && npm run dev # Terminal 2
# Alternative: Streamlit (single terminal)
uv run streamlit run streamlit_app.py
Environment variables
| Variable | Required | Description |
|---|---|---|
MODEL_BACKEND |
Yes | minimax / vllm / replay |
MINIMAX_API_KEY |
When MODEL_BACKEND=minimax |
MiniMax API key |
VLLM_ENDPOINT |
When MODEL_BACKEND=vllm |
e.g. http://host:8000/v1 |
REPLAY_SCENARIO_ID |
When MODEL_BACKEND=replay |
Scenario JSON filename (without .json) |
PARQUET_DIR |
No | Path to Parquet files (default: data/parquet) |
SEMANTIC_LAYER_PATH |
No | Default: data/semantic_layer.yml |
16. Running Locally (Full Stack)
Default: FastAPI + Next.js
Terminal 1 β FastAPI backend:
uv run uvicorn client.backend.main:app --reload --port 8000
Backend runs at http://localhost:8000. Check health at http://localhost:8000/api/health.
Terminal 2 β Next.js frontend:
cd client/frontend
npm install # first time only
npm run dev
Frontend runs at http://localhost:3000.
Alternative: Streamlit UI
Single terminal, no frontend setup needed. Use this for quick iteration on the agent loop.
uv run streamlit run streamlit_app.py
Opens at http://localhost:8501.
Common development commands
| Task | Command |
|---|---|
| Install/sync deps | uv sync |
| Add dependency | uv add <package> (runtime) or uv add --dev <package> (dev) |
| Run all tests | uv run pytest -v |
| Run one test file | uv run pytest tests/test_agent_smoke.py -v |
| Lint & auto-fix | uv run ruff check --fix |
| Format code | uv run ruff format |
| Type check (optional) | uv run pyright |
| Run FastAPI backend | uv run uvicorn client.backend.main:app --reload --port 8000 |
| Run Next.js frontend | cd client/frontend && npm run dev |
| Run Streamlit (alt) | uv run streamlit run streamlit_app.py |
| Build Next.js | cd client/frontend && npm run build |
| Build Docker image | docker build -t why-agent:latest . |
17. Development & Testing
Running tests
# All tests
uv run pytest
# Single file
uv run pytest tests/test_tools.py -v
# Single test
uv run pytest tests/test_tools.py::test_inspect_schema -v
# With print output
uv run pytest -s
Tests are smoke tests β we verify that tools run without crashing and return the expected JSON shape. Mocking is minimal.
Code quality gates (required before commit)
uv run ruff check --fix # Fix lint issues
uv run ruff format # Format code
Both must pass before committing. Set up a pre-commit hook to automate:
cat > .git/hooks/pre-commit << 'EOF'
#!/bin/bash
uv run ruff check --fix && uv run ruff format || exit 1
EOF
chmod +x .git/hooks/pre-commit
Using the REPL (interactive testing)
# Against MiniMax API (requires MINIMAX_API_KEY)
export MODEL_BACKEND=minimax
uv run python scripts/repl_graph.py
# > Q: Why did message open rate drop?
# > Q: Why does weekend engagement differ?
# Against replay (no API key needed)
export MODEL_BACKEND=replay
export REPLAY_SCENARIO_ID=scenario_1
uv run python scripts/repl_graph.py
18. Deployment to HF Spaces
why-agent is designed to deploy to Hugging Face Spaces via Docker. The included Dockerfile is multi-stage and includes everything: Python agent, FastAPI backend, Next.js frontend, and nginx reverse proxy.
Quick deploy (3 steps)
1. Push code to HF Spaces:
# Set up remote once (replace with your Spaces URL)
git remote add space https://huggingface.co/spaces/YOUR_USERNAME/why-agent.git
# Then push (HF Spaces auto-detects Dockerfile and builds)
git push space main
2. Set environment variables in HF Spaces Settings:
Go to Space Settings > Variables and add:
| Variable | Value |
|---|---|
MODEL_BACKEND |
replay (recommended) or vllm if you have a GPU endpoint |
MINIMAX_API_KEY |
Only if using MODEL_BACKEND=minimax |
VLLM_ENDPOINT |
Only if using MODEL_BACKEND=vllm (e.g. http://vllm-api.example.com:8000/v1) |
HF_DATASET_ID |
Optional: e.g. username/why-agent-data (auto-downloads at boot) |
3. Verify:
curl https://YOUR_SPACE_URL/api/health
Should return: {"ok": true}
Model backends explained
| Backend | Use case | Cost | Setup |
|---|---|---|---|
| replay | Demo when GPU offline, pre-recorded scenarios | Free | Set REPLAY_SCENARIO_ID=scenario_1 |
| minimax | Fallback LLM for ad-hoc questions | ~$0.01/query | Set MINIMAX_API_KEY |
| vllm | High-quality, fast inference on GPU | $1.99/hr (AMD MI300X) | Set VLLM_ENDPOINT |
Recording demo scenarios for offline playback
When a scenario works end-to-end, record it:
export MODEL_BACKEND=minimax
export MINIMAX_API_KEY=your-key
uv run python scripts/record_replay.py --scenario scenario_1
This saves replays/scenario_1.json. Commit it and deploy with MODEL_BACKEND=replay.
Docker: build and run locally
# Build
docker build -t why-agent:latest .
# Run
docker run -p 7860:7860 -e MODEL_BACKEND=replay why-agent:latest
Then visit http://localhost:7860.
Environment variables reference (complete)
| Variable | Default | Description |
|---|---|---|
MODEL_BACKEND |
β | LLM backend: minimax, vllm, or replay |
MINIMAX_API_KEY |
β | MiniMax API key (if using minimax backend) |
VLLM_ENDPOINT |
β | vLLM server URL (if using vllm backend; include /v1) |
REPLAY_SCENARIO_ID |
β | Scenario ID for replay mode (filename without .json) |
PARQUET_DIR |
/app/data/parquet |
Path to Parquet dataset directory |
SEMANTIC_LAYER_PATH |
/app/data/semantic_layer.yml |
Path to semantic layer YAML |
HF_DATASET_ID |
β | HF Dataset ID to auto-download at boot (optional) |
LANGSMITH_API_KEY |
β | LangSmith API key for tracing (optional) |
Health check endpoints
# Health check
curl http://localhost:7860/api/health
# Returns: {"ok": true}
# Demo questions
curl http://localhost:7860/api/demo-questions
# Returns: {"questions": [...]}
# Investigate (POST)
curl -X POST http://localhost:7860/api/investigate \
-H "Content-Type: application/json" \
-d '{"question": "Why did open rate drop?"}'
# Streams Server-Sent Events (SSE)
Troubleshooting deployment
| Issue | Solution |
|---|---|
| Build fails with npm error | Ensure Node 20+ installed; run npm install --legacy-peer-deps locally |
| API returns 500 | Check HF Spaces logs; verify PARQUET_DIR and SEMANTIC_LAYER_PATH exist |
| vLLM endpoint unreachable | Verify VLLM_ENDPOINT includes /v1; check GPU server is running |
| Data not loading | Set HF_DATASET_ID to auto-download, or manually COPY Parquet files into Dockerfile |
| Replay scenario not found | Verify REPLAY_SCENARIO_ID matches filename in replays/ (without .json) |
19. Architecture & Project Structure
why-agent/
βββ agent/ # Core agent logic
β βββ graph.py # LangGraph state machine (6-phase loop)
β βββ state.py # Pydantic InvestigationState model
β βββ client.py # Multi-backend LLM client
β βββ constants.py # Named constants (backends, tools, demo questions)
β βββ tools/ # The four tools
β β βββ inspect_schema.py # Returns table metadata + business context
β β βββ run_sql.py # Execute read-only DuckDB queries
β β βββ compare_periods.py # Quantify metric change between windows
β β βββ decompose_metric.py # Slice metric by dimensions, rank anomalies
β βββ prompts/ # System + critique prompts (markdown)
β βββ system.md
β βββ critique.md
β
βββ client/
β βββ backend/ # FastAPI server
β β βββ main.py # GET /health, POST /api/investigate
β β βββ deps.py # Dependency injection
β β βββ sse.py # Server-Sent Events formatting
β β βββ tests/
β βββ frontend/ # Next.js app (React + TypeScript)
β βββ src/app/page.tsx # Main UI page
β βββ src/app/api/investigate # Next.js API route (optional)
β βββ package.json
β
βββ data/
β βββ parquet/ # Dataset files (user-provided; gitignored)
β βββ semantic_layer.yml # Business metadata, metrics, dimensions, joins
β βββ root_cause/ # Ground-truth documentation
β
βββ replays/ # Pre-recorded investigation traces (JSON)
β βββ scenario_1.json
β
βββ tests/ # Python smoke tests
β βββ test_tools.py # Tool execution and output shape
β βββ test_client_backends.py # Verify 3 backends (minimax, vllm, replay)
β βββ test_agent_smoke.py # End-to-end agent smoke test
β
βββ docker/ # Container config
β βββ Dockerfile # Multi-stage: Next.js + Python + nginx
β βββ entrypoint.sh # Boot script (handles HF Dataset download)
β βββ nginx.conf # Reverse proxy (routes / to Next.js, /api/* to FastAPI)
β βββ supervisord.conf # Process management (nginx, FastAPI, Next.js)
β
βββ scripts/ # Utilities
β βββ repl_graph.py # Interactive REPL for testing the agent
β βββ record_replay.py # Save a scenario as replay JSON
β
βββ streamlit_app.py # Streamlit UI (standalone, no backend needed)
βββ pyproject.toml # Dependencies + test config
βββ Dockerfile # Deployment image
βββ .env.example # Environment template
βββ CLAUDE.md # Implementation decisions & constraints
βββ README.md # This file (project overview + business context)
βββ docs/
βββ why-agent-architecture.png # Diagram
20. Coding conventions
Per CLAUDE.md, follow these when writing code:
- Sync by default β DuckDB and Streamlit are sync. Use
async defonly at the LLM boundary. - Pydantic v2 β All structured data (tool inputs/outputs, state, semantic layer).
- Type annotations β Required on public functions (args + return type).
- No print() β Use
logger = logging.getLogger(__name__)in agent code. - No magic strings β Backend names, tool names, scenario IDs go in
agent/constants.py. - Tool docstrings for the LLM β Write them as if the model will read them (be descriptive about what it does and when to use it).
Example tool implementation
from pydantic import BaseModel, Field
import logging
logger = logging.getLogger(__name__)
class MyToolInput(BaseModel):
query: str = Field(description="A human-readable query or metric name.")
class MyToolOutput(BaseModel):
result: dict
error: str | None = None
def my_tool(args: MyToolInput) -> dict:
"""Use this tool to analyze X. Returns a dict with 'result' (the data) and optional 'error'."""
try:
result = ...
return {"result": result}
except Exception as exc:
logger.exception("Tool failed for query: %s", args.query)
return {"error": str(exc), "hint": "Try phrasing the query differently"}
21. Locked decisions (do not change without explicit approval)
These decisions are locked per CLAUDE.md. Changing any requires discussion:
| Decision | Value | Why |
|---|---|---|
| Architecture | Single agent (not multi-agent) | Simpler to debug, easier to understand agentic fundamentals |
| Tool count | 4 tools (fixed) | Fewer integrations = fewer demo failure modes |
| Orchestration | LangGraph (not CrewAI, AutoGen, etc.) | Explicit state machine, good tracing, community support |
| Model (prod) | Llama-3.3-70B (vLLM) | Open-source, fast on MI300X, no licensing |
| Model (dev) | MiniMax-M2.7 (API fallback) | No GPU required, quick iteration |
| Data engine | DuckDB on Parquet | Embedded, column-oriented, single query engine |
| Semantic layer | Single YAML file (hand-written) | Simple, no tooling overhead, easy to version |
| UI | Streamlit (primary) + Next.js (secondary) | Free hosting, rapid iteration, good for demos |
| Hosting | HF Spaces (primary) + Streamlit Cloud (backup) | Free, simple, community-friendly |
| License | MIT | Open-source, permissive |
If a task seems to require changing one of these, pause and ask before proceeding.
22. Risks & known limitations
- Parquet size β Keep total Parquet data under 500 MB to fit in HF Spaces' memory limit. Profile on Day 2.
- Investigation latency β Agent might take 60β120 seconds on a fallback model. Frame demos as "minutes vs hours," not "60 seconds."
- GPU availability β The MI300X droplet costs $1.99/hr. Use
MODEL_BACKEND=replaywhen the GPU is off. - Concurrent requests β HF Spaces free tier queues additional requests (no parallelism). For production, use a dedicated server.
- Replay maintenance β Scenarios must be re-recorded if the agent loop changes significantly.
23. Resources & links
- AMD Developer Hackathon: https://lablab.ai/ai-hackathons/amd-developer
- AMD Developer Cloud docs: https://www.amd.com/en/developer/resources/cloud-access/amd-developer-cloud.html
- LangGraph docs: https://langchain-ai.github.io/langgraph/
- vLLM on ROCm: https://docs.vllm.ai/en/latest/getting_started/amd-installation.html
- Streamlit Cloud: https://share.streamlit.io
- MiniMax API: https://platform.minimaxi.chat/
- Hugging Face Spaces: https://huggingface.co/spaces
