## 1. Project Overview & Quickstart (agno-agi/dash) ## File: README.md # Dash A **self-learning data agent** built with systems engineering principles. It grounds answers in 6 layers of context and improves with every query. Chat with Dash via Slack, the terminal, or the [AgentOS](https://os.agno.com?utm_source=github&utm_medium=example-repo&utm_campaign=agent-example&utm_content=dash&utm_term=agentos) web UI. ## Quick Start ```sh # Clone the repo git clone https://github.com/agno-agi/dash.git && cd dash cp example.env .env # Edit .env and add your OPENAI_API_KEY # Start the system docker compose up -d --build # Generate sample data and load knowledge docker exec -it dash-api python scripts/generate_data.py docker exec -it dash-api python scripts/load_knowledge.py ``` Confirm Dash is running at [http://localhost:8000/docs](http://localhost:8000/docs). ### Connect to the Web UI 1. Open [os.agno.com](https://os.agno.com?utm_source=github&utm_medium=example-repo&utm_campaign=agent-example&utm_content=dash&utm_term=agentos) and login 2. Add OS → Local → `http://localhost:8000` 3. Click "Connect" **Try it** (SaaS metrics dataset): - What's our current MRR? - Which plan has the highest churn rate? - Show me revenue trends by plan over the last 6 months - Which customers are at risk of churning? ## Deploy to Railway Railway deployment uses `.env.production` to keep production credentials separate from local dev. ```sh cp example.env .env.production # Edit .env.production — set OPENAI_API_KEY ``` ### Step 1: Deploy infrastructure This creates the Railway project, database, and app service. The app will crash-loop until the JWT key is added in the next step — that's expected. ```sh railway login ./scripts/railway_up.sh ``` ### Step 2: Get your JWT key Production requires a `JWT_VERIFICATION_KEY` from [AgentOS](https://os.agno.com?utm_source=github&utm_medium=example-repo&utm_campaign=agent-example&utm_content=dash&utm_term=agentos). You need the Railway domain from step 1 to set this up. 1. Copy your Railway domain from the output of step 1 (e.g. `dash-production-xxxx.up.railway.app`) 2. Open [os.agno.com](https://os.agno.com?utm_source=github&utm_medium=example-repo&utm_campaign=agent-example&utm_content=dash&utm_term=agentos) and login 3. Add OS → Live → paste your Railway URL 4. Go to **Settings** and generate a key pair 5. Add the public key to `.env.production` (wrap in single quotes): ```bash JWT_VERIFICATION_KEY='-----BEGIN PUBLIC KEY----- MIIBIjANBgkq... -----END PUBLIC KEY-----' ``` ### Step 3: Push environment and redeploy ```sh ./scripts/railway_env.sh ./scripts/railway_redeploy.sh ``` `railway_env.sh` reads `.env.production` and sets each variable on the Railway service. Safe to run repeatedly. Handles multiline values (PEM keys) correctly. ### Production operations Database scripts must run inside Railway's network (the internal hostname `pgvector.railway.internal` isn't reachable from your local machine). Use SSH to connect to the running container: ```sh railway ssh --service dash # Inside the container: python scripts/generate_data.py python scripts/load_knowledge.py ``` Other operations run locally: ```sh railway logs --service dash railway open ``` ## Why Dash Exists Ask a question in English, get a correct, meaningful answer. That's the goal. But raw LLMs writing SQL hit a wall fast: schemas lack meaning, types are misleading, tribal knowledge is missing, there's no way to learn from mistakes, and results lack interpretation. The root cause is missing context and missing memory. Dash solves this with **six layers of grounded context**, a **self-learning loop** that improves with every query, and a focus on delivering insights you can act on. ## Architecture: Five Layers, One System Agentic software is just software with the business logic replaced by agents. Everything else is systems engineering. Dash is built across five layers that reinforce each other. ``` Agent Engineering → dash/team.py + dash/agents/ Data Engineering → knowledge/ + Agno Learning Machine + PostgreSQL Security Engineering → AgentOS auth + RBAC + read-only SQL enforcement Interface Engineering → app/main.py (FastAPI) + Slack + AgentOS Infrastructure → Dockerfile + compose.yaml + scripts/ ``` ### 1. Agent Engineering The agent team and execution flow. Model, instructions, tools, knowledge, and the self-learning loop. ``` AgentOS (app/main.py) [scheduler=True, tracing=True] ├── FastAPI / Uvicorn ├── Slack Interface (optional) └── Dash Team (dash/team.py, coordinate mode) ├─ Analyst (dash/agents/analyst.py) reads public + dash │ ├─ SQLTools (read-only) → public schema (company data) │ ├─ introspect_schema → both schemas │ ├─ save_validated_query → knowledge base │ └─ ReasoningTools ├─ Engineer (dash/agents/engineer.py) reads public, writes dash │ ├─ SQLTools (full) → dash schema (agent-managed) │ ├─ introspect_schema → both schemas │ ├─ update_knowledge → knowledge base (schema changes) │ └─ ReasoningTools │ Leader tools: SlackTools (optional) Knowledge: dash_knowledge (table schemas, queries, business rules, dash views) Learnings: dash_learnings (error patterns, type gotchas, fixes) ``` ### 2. Data Engineering Context is data. Memory is data. Knowledge is data. All managed with data engineering principles: well-designed schemas, structured querying, databases for fast read/writes. **Six layers of grounded context:** | Layer | Purpose | Source | |------|--------|--------| | **Table Usage** | Schema, columns, relationships | `knowledge/tables/*.json` | | **Human Annotations** | Metrics, definitions, business rules | `knowledge/business/*.json` | | **Query Patterns** | SQL that is known to work | `knowledge/queries/*.sql` | | **Institutional Knowledge** | Docs, wikis, external references | MCP (optional) | | **Learnings** | Error patterns and discovered fixes | Agno `Learning Machine` | | **Runtime Context** | Live schema changes | `introspect_schema` tool | **The self-learning loop:** ``` User Question ↓ Retrieve Knowledge + Learnings ↓ Reason about intent ↓ Generate grounded SQL ↓ Execute and interpret ↓ ┌────┴────┐ ↓ ↓ Success Error ↓ ↓ ↓ Diagnose → Fix → Save Learning ↓ (never repeated) ↓ Return insight ↓ Optionally save as Knowledge ``` Two complementary systems: | System | Stores | How It Evolves | |------|--------|----------------| | **Knowledge** | Validated queries and business context | Curated by you + Dash | | **Learnings** | Error patterns and fixes | Managed by `Learning Machine` automatically | **Dual schema enforcement:** A structural boundary between company data and agent-managed data. | Schema | Owner | Access | |--------|-------|--------| | `public` | Company (loaded externally) | Read-only — never modified by agents | | `dash` | Engineer agent | Views, summary tables, computed data | The Engineer builds reusable data assets (`dash.monthly_mrr`, `dash.customer_health_score`, `dash.churn_risk`) and records them to knowledge. The Analyst discovers and prefers these views over raw table queries. ### 3. Security Engineering Auth uses RBAC with JWT verification in production. Every query is scoped to `user_id`. Read-only access is a tool configuration, not a prompt instruction. The Analyst agent's SQL tools are scoped to read-only at the system level. See [Security](#security) for setup details. ### 4. Interface Engineering One agent definition, multiple surfaces. Dash is reachable via REST API (FastAPI), Slack threads, and the AgentOS web UI. Each surface has its own identity system: a Slack user ID maps to sessions via thread timestamps, the API uses JWT-backed auth. ### 5. Infrastructure Engineering Dockerfile, Docker Compose, one-command deployment. Scheduled tasks for proactive behavior. The infrastructure layer is boring on purpose. 95% of running an agent is identical to running any other service. ## Slack Dash can receive Slack DMs, @mentions, and thread replies, and can also post to channels proactively. Quick setup: 1. Run Dash and give it a public URL (ngrok locally, or your Railway domain). 2. Follow [docs/SLACK_CONNECT.md](docs/SLACK_CONNECT.md) to create and install the Slack app from the manifest. 3. Set `SLACK_TOKEN` and `SLACK_SIGNING_SECRET`, then restart Dash. 4. In Slack, confirm Event Subscriptions is verified and send a DM or `@mention` to test it. Each Slack thread maps to one Dash session. For the manifest, ngrok commands, Railway deployment, permissions, and troubleshooting, see [docs/SLACK_CONNECT.md](docs/SLACK_CONNECT.md). ## Data Model (SaaS Metrics) Synthetic B2B SaaS dataset (~900 customers, 2 years of data): | Table | Description | |-------|-------------| | `customers` | Company info, industry, size, acquisition source, status | | `subscriptions` | Plan, MRR, seats, billing cycle, lifecycle status | | `plan_changes` | Upgrades, downgrades, cancellations with MRR impact | | `invoices` | Billing records, payment status, billing periods | | `usage_metrics` | Daily API calls, active users, storage, reports | | `support_tickets` | Priority, category, resolution time, satisfaction | ## Adding Knowledge Dash works best when it understands how your organization talks about data. ``` knowledge/ ├── tables/ # Table meaning and caveats ├── queries/ # Proven SQL patterns └── business/ # Metrics and language ``` ### Table Metadata ```json { "table_name": "customers", "table_description": "B2B SaaS customer accounts with company info and lifecycle status", "use_cases": ["Churn analysis", "Cohort segmentation", "Acquisition reporting"], "data_quality_notes": [ "signup_date is DATE (not TIMESTAMP) — no time component", "status values: active, churned, trial", "company_size is self-reported" ] } ``` ### Query Patterns ```sql -- -- Monthly MRR from active subscriptions -- SELECT DATE_TRUNC('month', started_at) AS month, SUM(mrr) AS total_mrr FROM subscriptions WHERE ended_at IS NULL GROUP BY 1 ORDER BY 1 DESC -- ``` ### Business Rules ```json { "metrics": [ { "name": "MRR", "definition": "Sum of active subscriptions excluding trials" } ], "common_gotchas": [ { "issue": "Active subscription detection", "solution": "Filter on ended_at IS NULL, not status column" } ] } ``` ### Load Knowledge ```sh python scripts/load_knowledge.py # Upsert changes python scripts/load_knowledge.py --recreate # Fresh start ``` ## Evaluations Five eval categories using Agno's eval framework: | Category | Eval Type | What It Tests | |----------|-----------|---------------| | accuracy | AccuracyEval (1-10) | Correct data and meaningful insights | | routing | ReliabilityEval | Team routes to correct agent/tools | | security | AgentAsJudgeEval (binary) | No credential or secret leaks | | governance | AgentAsJudgeEval (binary) | Refuses destructive SQL operations | | boundaries | AgentAsJudgeEval (binary) | Schema access boundaries respected | ```sh python -m evals # Run all evals python -m evals --category accuracy # Run specific category python -m evals --verbose # Show response details ``` ## Local Development ```sh ./scripts/venv_setup.sh && source .venv/bin/activate docker compose up -d dash-db python scripts/generate_data.py python scripts/load_knowledge.py python -m dash # CLI mode python -m app.main # AgentOS mode (web UI at os.agno.com) ``` ## Environment Variables | Variable | Required | Default | Purpose | |----------|----------|---------|---------| | `OPENAI_API_KEY` | Yes | — | OpenAI API key | | `SLACK_TOKEN` | No | `""` | Slack bot token (interface + tools) | | `SLACK_SIGNING_SECRET` | No | `""` | Slack signing secret (interface only) | | `DB_HOST` | No | `localhost` | PostgreSQL host | | `DB_PORT` | No | `5432` | PostgreSQL port | | `DB_USER` | No | `ai` | PostgreSQL user | | `DB_PASS` | No | `ai` | PostgreSQL password | | `DB_DATABASE` | No | `ai` | PostgreSQL database | | `PORT` | No | `8000` | API port | | `RUNTIME_ENV` | No | `prd` | `dev` enables hot reload | | `AGENTOS_URL` | No | `http://127.0.0.1:8000` | Scheduler callback URL (production) | | `JWT_VERIFICATION_KEY` | Production | — | RBAC public key from [os.agno.com](https://os.agno.com?utm_source=github&utm_medium=example-repo&utm_campaign=agent-example&utm_content=dash&utm_term=agentos) | ## Security Production deployments require authentication via [Agno AgentOS](https://docs.agno.com/agent-os/security/overview?utm_source=github&utm_medium=example-repo&utm_campaign=agent-example&utm_content=dash&utm_term=security). Dash enables [RBAC authorization](https://docs.agno.com/agent-os/security/rbac?utm_source=github&utm_medium=example-repo&utm_campaign=agent-example&utm_content=dash&utm_term=rbac) when `RUNTIME_ENV=prd` (the default). Without a valid `JWT_VERIFICATION_KEY`, production endpoints will reject all requests. Local development (`RUNTIME_ENV=dev`, set by Docker Compose) runs without auth so you can iterate freely. ### Auth Setup See [Deploy to Railway](#deploy-to-railway) for the full setup flow, including how to get your `JWT_VERIFICATION_KEY` from AgentOS. The Agno control plane handles JWT issuance, session management, traces, metrics, and the web UI. See the [AgentOS Security docs](https://docs.agno.com/agent-os/security/overview?utm_source=github&utm_medium=example-repo&utm_campaign=agent-example&utm_content=dash&utm_term=security) for details. ### Schema-Level Enforcement Beyond API-level auth, Dash enforces data access at the database level: - **Analyst** connects with `default_transaction_read_only=on` — PostgreSQL rejects any write attempt - **Engineer** writes are scoped to the `dash` schema — a SQLAlchemy event listener blocks any DDL/DML targeting `public` - **Leader** has no direct database access These are infrastructure guardrails, not prompt instructions. They hold regardless of what the model generates. ## Learn More - [OpenAI's In-House Data Agent](https://openai.com/index/inside-our-in-house-data-agent/) — the inspiration - [Self-Improving SQL Agent](https://www.ashpreetbedi.com/articles/sql-agent) — deep dive on an earlier architecture - [Agno Docs](https://docs.agno.com?utm_source=github&utm_medium=example-repo&utm_campaign=agent-example&utm_content=dash&utm_term=docs) --- ## File: docs/IMPROVE_DASH.md # Improve Dash Run this prompt in Claude Code to start a self-improvement loop. Claude will run smoke tests, analyze what's broken, fix instructions/knowledge, and verify — repeating until tests pass. --- ## Prompt ``` You are improving Dash, a self-learning data agent. Your job is to run smoke tests, analyze failures, fix the root causes, and verify your fixes. Repeat until all tests pass or you've done 5 rounds. ## How Dash works Read CLAUDE.md for the full picture. The key files you can edit: - `dash/instructions.py` — system prompts for Leader, Analyst, Engineer (primary lever) - `knowledge/business/metrics.json` — business rules and data gotchas - `knowledge/queries/common_queries.sql` — validated SQL patterns Do NOT edit: team.py, agent definitions, tools, database schema, or the smoke tests. ## The loop For each round: 1. **Run smoke tests:** ``` source .venv/bin/activate && python -m evals smoke --verbose ``` /* Detailed source-code truncated for AI context efficiency. */ ``` --- ## File: docs/SLACK_CONNECT.md # Connecting Dash to Slack Slack gives Dash two capabilities: 1. **Receiving messages** — users interact with Dash via DMs, @mentions, and thread replies. 2. **Sending messages** — Dash posts to channels proactively (scheduled task results) or on request. Each Slack thread maps to a session ID, so every thread gets its own conversation context. ## Prerequisites - Dash running locally (`docker compose up -d --build`) or deployed to a public URL - A Slack workspace where you can install apps ## Step 1: Get a Public URL Slack needs a public URL to send events to Dash. **Production** — use your deployed URL (e.g., `https://dash-production-xxxx.up.railway.app`). **Local development** — use [ngrok](https://ngrok.com/download/mac-os): ```sh # Docker Compose (Quick Start) ngrok http 8000 # Bare AgentOS (python -m app.main) ngrok http 7777 ``` Copy the `https://` URL (e.g., `https://abc123.ngrok-free.app`). This is your base URL. ## Step 2: Create a Slack App from Manifest 1. Go to [api.slack.com/apps](https://api.slack.com/apps) 2. Click **Create New App → From a manifest** 3. Select your workspace 4. Switch to **JSON** and paste the manifest below 5. Replace `YOUR_URL_HERE` with your base URL from Step 1 6. Click **Create** ```json { "display_information": { "name": "Dash", "description": "Self-learning data agent that delivers insights, not just SQL results", "background_color": "#1a1a2e" }, "features": { "app_home": { "home_tab_enabled": false, "messages_tab_enabled": true, "messages_tab_read_only_enabled": false }, "bot_user": { "display_name": "Dash", "always_online": true } }, "oauth_config": { "scopes": { "bot": [ "app_mentions:read", "assistant:write", "channels:history", "channels:read", "chat:write", "chat:write.customize", "chat:write.public", "files:read", "files:write", "groups:history", "im:history", "im:read", "im:write", "search:read.public", "search:read.files", "search:read.users", "users:read", "users:read.email" ] } }, "settings": { "event_subscriptions": { "request_url": "YOUR_URL_HERE/slack/events", "bot_events": [ "app_mention", "message.channels", "message.groups", "message.im" ] }, "org_deploy_enabled": false, "socket_mode_enabled": false, "token_rotation_enabled": false } } ``` ## Step 3: Install to Workspace 1. Go to **Install App** in the sidebar 2. Click **Install to Workspace** 3. Authorize the requested permissions 4. Copy the **Bot User OAuth Token** (`xoxb-...`) ## Step 4: Add Credentials and Restart 1. Copy the bot token from Step 3 → `SLACK_TOKEN` 2. Go to **Basic Information** in the sidebar, under **App Credentials**, copy **Signing Secret** → `SLACK_SIGNING_SECRET` **Local development:** ```env SLACK_TOKEN="xoxb-your-bot-token" SLACK_SIGNING_SECRET="your-signing-secret" ``` ```sh docker compose up -d --build ``` **Railway:** Add both variables to `.env.production`, then sync and redeploy: ```sh ./scripts/railway_env.sh ./scripts/railway_redeploy.sh ``` ## Step 5: Verify Event Subscriptions Slack verifies your endpoint with a `challenge` request when the app is created. If Dash wasn't running at that time, the verification fails silently and events won't be delivered. 1. Go to your Dash app settings → **Event Subscriptions** 2. If the Request URL shows "Your URL didn't respond", click **Retry** 3. Confirm it shows **Verified** with a green checkmark 4. Click **Save Changes** ## Verify - **DM**: Open a direct message to the Dash bot and send a message. - **Channel**: @mention Dash in any channel (e.g., `@Dash what's our MRR?`). - **Thread**: Reply in a thread — Dash continues the conversation with full context. ## Updating Permissions After changing the manifest or scopes, go to **Install App** and click **Reinstall to Workspace** to apply the new permissions. ## Bot Scopes Reference | Scope | Purpose | |-------|---------| | `app_mentions:read` | Respond when @mentioned | | `assistant:write` | Slack AI assistant features | | `channels:history` | Read channel message history for context | | `channels:read` | List and discover public channels | | `chat:write` | Post messages | | `chat:write.customize` | Custom message formatting (username, icon) | | `chat:write.public` | Post to public channels without joining | | `files:read` | Read files shared in channels | | `files:write` | Upload files (reports, exports) | | `groups:history` | Read private channel history | | `im:history` | Read DM history | | `im:read` | View DMs | | `im:write` | Send DMs | | `search:read.public` | Search public messages | | `search:read.files` | Search files | | `search:read.users` | Search users | | `users:read` | View user profiles | | `users:read.email` | View user email addresses | ## How It Works Dash uses [Agno's Slack interface](https://docs.agno.com) which handles: - **Event verification**: Validates the signing secret on every incoming event. - **Message routing**: Bot mentions, DMs, channel messages, and group messages all route to the Dash team leader. - **Thread sessions**: Each Slack thread timestamp becomes a session ID. Thread replies reuse the same session context without needing to @mention again. - **Streaming**: Responses stream to Slack in real time. - **User identity**: Dash knows who is asking via `users:read` scope. The Slack interface is registered conditionally in `app/main.py` — only when both `SLACK_TOKEN` and `SLACK_SIGNING_SECRET` are set. ### SlackTools vs Slack Interface Two separate things: - **Slack Interface** (`app/main.py`): Receives incoming events from Slack. Requires both `SLACK_TOKEN` and `SLACK_SIGNING_SECRET`. - **SlackTools** (`dash/team.py`): Lets the team leader send messages to channels, search messages, and get user info. Requires only `SLACK_TOKEN`. Enabled tools: `send_message`, `list_channels`, `send_message_thread`, `get_channel_info`, `get_thread`, `get_user_info`, `search_messages`. ## 2. Official Technical Reference & Guides (agno-agi/docs) # Agno Docs Agno documentation site built with Mintlify. ## Quickstart 1. Install the Mintlify CLI: `npm i mint` 2. From the repo root (the folder with `docs.json`), run `mint dev` 3. Open the local site at `http://localhost:3000` ## Contributing We welcome contributions to improve the Agno documentation! Please read our [CONTRIBUTING.md](CONTRIBUTING.md) for detailed guidelines on: - How to set up your development environment - Pull request and branch naming conventions - Documentation structure and writing guidelines - Testing and validation procedures ## Development Install the [Mintlify CLI](https://www.npmjs.com/package/mintlify) to run the documentation site locally: ``` npm i mint ``` Run the following command at the root of your documentation (where `docs.json` is) ``` mint dev ``` ## Publishing Changes Publish changes by pushing to the main branch via a PR. ``` git add . git commit -m "update message" git push ``` ## How to generate a new API reference 1. In your local `agno` repo, run the `AgentOS` cookbook containing all supported interfaces, using the latest version of Agno. ```bash python cookbook/05_agent_os/interfaces/all_interfaces.py ``` 2. Download the latest API reference files: ```bash curl -o reference-api/openapi.json http://localhost:7777/openapi.json ``` Using swagger-cli to create openapi.yaml counterpart: ```bash swagger-cli bundle reference-api/openapi.json --outfile reference-api/openapi.yaml --type yaml ``` 3. Delete all files in the `reference-api/schema/` folder (the auto-generated files) 4. Run `npx @mintlify/scraping@latest openapi-file reference-api/openapi.json -o reference-api/schema` to generate the new API reference 5. Update the `docs.json` file to include any new pages. 6. Run `mint dev` to see the changes ## Troubleshooting - Mintlify dev isn't running - Run `mint update` it'll update dependencies. - Page loads as a 404 - Make sure you are running in a folder with `docs.json` ## AI Powered Development First, symlink the `agno` repo (gitignored): ``` ln -s ~/code/agno agno ``` For agno engineers, also symlink the `specs` repo: ``` ln -s ~/code/specs specs ```