From f2bb35f0abe56f416d7212f5fd5dda191b79a81a Mon Sep 17 00:00:00 2001 From: Sathish Krishnan <10681383+SatyKrish@users.noreply.github.com> Date: Sat, 25 Apr 2026 23:48:09 -0400 Subject: [PATCH] docs: split README into operational guide + docs/design.md essay - README cut to its operational core (786 -> 384 lines): features, prereqs, getting-started, CLEARS, configuration, testing, deployment, layout, limitations. Lead now points to docs/design.md and runbook.md. - New docs/design.md holds the essay material: why this exists, the four architecture diagrams, the Spec-Kit + Claude Code three-pillars story, the chicken-egg deploy-ordering walkthrough, and "what you can learn from this repo." - Minor copy edits in specs/001-doc-intel-10k/{quickstart,research,plan} to drop vague hedges ("under 30 minutes" -> matches actual 15-25 min estimate; "improving relevance materially" -> concrete claims). - docs/_social_preview.py + regenerated docs/social-preview.png: arch line now ends "eval-gated cited agent" (was "cited agent on Mosaic AI", which redundantly repeated the DATABRICKS eyebrow). Surfaces the CLEARS gate as the agent's distinguishing trait. Co-Authored-By: Claude Opus 4.7 (1M context) --- README.md | 440 ++------------------------ docs/_social_preview.py | 27 +- docs/design.md | 355 +++++++++++++++++++++ docs/social-preview.png | Bin 38606 -> 38213 bytes specs/001-doc-intel-10k/plan.md | 2 +- specs/001-doc-intel-10k/quickstart.md | 2 +- specs/001-doc-intel-10k/research.md | 2 +- 7 files changed, 397 insertions(+), 431 deletions(-) create mode 100644 docs/design.md diff --git a/README.md b/README.md index 030ee52..3eeaa37 100644 --- a/README.md +++ b/README.md @@ -29,27 +29,21 @@ A **Databricks-native document intelligence + agent** stack: parse PDFs once wit [2], regulation [3]…" ``` +For motivation, architecture diagrams, the Spec-Kit + Claude Code build workflow, and the chicken-egg deploy-ordering story, see [**`docs/design.md`**](./docs/design.md). For day-2 ops, see [**`docs/runbook.md`**](./docs/runbook.md). + --- ## Table of contents -- [Why this exists](#why-this-exists) - [Features](#features) - [Readiness levels](#readiness-levels) - [Prerequisites](#prerequisites) - - [Software](#software) - - [Databricks workspace](#databricks-workspace) - - [Free trial signup](#free-trial-signup) - [Getting started](#getting-started) -- [Architecture](#architecture) -- [How it's built — three pillars](#how-its-built--three-pillars) -- [Deploy ordering: foundation → consumers](#deploy-ordering-foundation--consumers) - [CLEARS quality gate](#clears-quality-gate) - [Configuration](#configuration) - [Testing & validation](#testing--validation) - [Deployment](#deployment) - [Repo layout](#repo-layout) -- [What you can learn from this repo](#what-you-can-learn-from-this-repo) - [Limitations](#limitations) - [Contributing](#contributing) - [Security](#security) @@ -58,14 +52,6 @@ A **Databricks-native document intelligence + agent** stack: parse PDFs once wit --- -## Why this exists - -Databricks shipped a lot of new generative-AI surface area in 2025–2026: `ai_parse_document`, Mosaic AI Vector Search, the Agent Framework, AI Gateway, Lakebase, Databricks Apps. Tutorials show each piece in isolation; nobody shows them wired together with **eval gates, governance, and reproducible deploys** the way you'd actually ship to analysts. - -This repo is that worked example. Drop a PDF into a governed UC volume; ten minutes later, an analyst can ask cited questions in plain English with end-to-end audit. The whole stack is described declaratively as one **Databricks Asset Bundle (DAB)** plus a small bootstrap script. DAB manages catalog/schema/volume, pipeline, jobs, the Vector Search **endpoint**, the Lakebase instance, the serving endpoint, the monitor, the app, and the dashboard; the Vector Search **index** itself is created and synced by `jobs/index_refresh/sync_index.py` (DAB doesn't yet manage indexes as a resource type), and the agent model version is registered by `agent/log_and_register.py`. The bootstrap script orchestrates them in the right order. - -It also demonstrates a development workflow: **Spec-Kit** for spec-driven design, **Claude Code** with Databricks skill bundles for AI-assisted implementation, six **non-negotiable constitution principles** that gate every plan. See [How it's built](#how-its-built--three-pillars). - ## Features - **End-to-end document intelligence pipeline** — Auto Loader ingest → `ai_parse_document` → section explosion → `ai_classify` + `ai_extract` → 5-dim quality rubric → Vector Search Delta-Sync index (the endpoint is DAB-managed; the index is created/synced by `jobs/index_refresh/sync_index.py`). SQL-only pipeline (Lakeflow Spark Declarative Pipelines). @@ -181,7 +167,7 @@ DOCINTEL_WAREHOUSE_ID= \ ./scripts/bootstrap-dev.sh ``` -The script handles the chicken-egg ordering automatically — see [Deploy ordering](#deploy-ordering-foundation--consumers). +The script handles the chicken-egg ordering automatically — see [`docs/design.md` § Deploy ordering](./docs/design.md#deploy-ordering-foundation--consumers). ### 5. Run the eval gate @@ -228,323 +214,9 @@ For a guided 30-minute tour, see [`specs/001-doc-intel-10k/quickstart.md`](./spe --- -## Architecture - -### Two halves: an offline pipeline, and an online agent - -``` - ╔═══════════════════════════════════════════════════════════════════╗ - ║ pipelines/sql/ (one SQL file per tier) ║ - ╚═══════════════════════════════════════════════════════════════════╝ - - raw_filings/ ┌─────────────────┐ ┌─────────────────┐ ┌──────────────────┐ - ACME_10K.pdf ──▶ │ bronze_filings │──▶│ silver_parsed_ │──▶│ gold_filing_ │ - BETA_10K.pdf │ (raw bytes, │ │ filings (parsed │ │ sections (one │ - GAMMA_10K.pdf │ filename, │ │ VARIANT — │ │ row per parsed │ - │ ingested_at) │ │ ai_parse_ │ │ $.sections[*]; │ - │ │ │ document) │ │ fallback to │ - │ >50MB rejects: │ │ │ │ full_document │ - │ bronze_filings │ │ Status: ok / │ │ if absent) │ - │ _rejected │ │ partial / error │ │ │ - └─────────────────┘ └─────────────────┘ │ gold_filing_kpis │ - 01_bronze.sql 02_silver_parse │ (typed columns: │ - .sql │ segment_revenue │ - │ ARRAY, │ - │ top_risks │ - │ ARRAY) │ - └──────────────────┘ - 03_gold_classify - _extract.sql - │ - ▼ - ┌──────────────────┐ - │ gold_filing_ │ - │ quality │ - │ (5-dim rubric: │ - │ parse, layout, │ - │ ocr, sections, │ - │ kpi → 0-30) │ - └──────────────────┘ - 04_gold_quality.sql -``` - -**Key idea — "parse once, extract many":** PDFs are expensive to parse. Silver runs `ai_parse_document` exactly once per file and stores the structured result as a `VARIANT`. Everything downstream — classification, KPI extraction, summarization, quality scoring — reads the parsed output, never the raw bytes. This is a non-negotiable constitution principle. - -**Triggering**: prod runs the pipeline in `continuous: true` mode so Auto Loader (`read_files`) reacts to new PDFs in the volume automatically. Dev overrides to `continuous: false` to avoid a 24/7 cluster during smoke iterations. See `resources/foundation/doc_intel.pipeline.yml` and the dev override block in `databricks.yml`. - -### Vector Search bridges data and agent - -``` - gold_filing_sections ┌─────────────────────────┐ - (governed Delta table) ─────▶ │ Mosaic AI Vector │ - │ Search Index │ - Filter: embed_eligible=true │ (Delta-Sync — auto- │ - Embed column: "summary" │ refreshes when Gold │ - │ updates) │ - └─────────────────────────┘ - - Why "summary" not the raw text? - ───────────────────────────── - Embedding a 50-page 10-K verbatim is noisy. We embed an LLM-written - summary instead — tighter, more searchable. Constitution principle IV: - "Quality before retrieval." -``` - -**Ownership note**: DAB manages the Vector Search **endpoint** (`resources/consumers/filings_index.yml`) and the index-refresh **job** (`resources/consumers/index_refresh.job.yml`). The **index** itself isn't yet a DAB-managed resource type as of CLI 0.298 — `jobs/index_refresh/sync_index.py` creates the Delta-Sync index on first run and triggers a sync on subsequent runs. That's why the bootstrap script's stage-2 deploy creates the endpoint + job, and the job's first execution materializes the actual index. - -### Agent has two paths, one endpoint - -``` - User question - │ - ▼ - ┌────────────────────────────────────────────┐ - │ AnalystAgent.predict() │ - │ ───────────────────── │ - │ contains "compare" / "vs" / │ - │ "between" + ≥2 company names? │ - └────────────┬─────────────────┬─────────────┘ - │ no │ yes - ▼ ▼ - ┌──────────────────────┐ ┌──────────────────────┐ - │ Single-filing path │ │ Supervisor path │ - │ │ │ │ - │ 1. Hybrid search │ │ For each company: │ - │ (keyword + vec) │ │ ▸ run analyst path │ - │ 2. Re-rank → top 5 │ │ ▸ pull KPIs from │ - │ 3. LLM generates │ │ gold_filing_kpis │ - │ answer w/ [1] [2] │ │ Format markdown │ - │ citations │ │ table with cites. │ - └──────────────────────┘ └──────────────────────┘ - │ │ - └────────┬────────┘ - ▼ - ┌──────────────────────┐ - │ Response JSON: │ - │ answer │ - │ citations[] │ - │ grounded: bool │ - │ latency_ms │ - └──────────────────────┘ -``` - -The agent is an `mlflow.pyfunc` model registered in Unity Catalog and served behind an **AI Gateway** (rate limiting per-user, usage tracking, inference-table audit). Identity passthrough is implemented at the *App layer* when the workspace has Databricks Apps user-token passthrough enabled: the Streamlit app extracts the user's `x-forwarded-access-token` header and constructs a user-scoped `WorkspaceClient`. The served model is OBO-ready via MLflow `auth_policy` and Model Serving user credentials. If app-level passthrough is not enabled, the app falls back to service-principal auth and the repo must be treated as a reference/dev deployment, not a production row-level-security deployment. See [`SECURITY.md`](./SECURITY.md) and `app/README.md`. - -### Runtime stack - -``` - ┌──────────────────────────────────────────────────────────────────┐ - │ │ - │ Databricks App (Streamlit) ← user interacts here │ - │ app/app.py │ - │ │ - │ ┌────────────────┐ ┌──────────────────┐ │ - │ │ Chat input box │ │ Citation chips │ │ - │ │ Thumbs up/down │ │ Markdown tables │ │ - │ └────────┬───────┘ └─────┬────────────┘ │ - │ │ │ │ - └──────────────│─────────────────│─────────────────────────────────┘ - │ │ - │ query │ feedback writes - ▼ ▼ - ┌────────────────────────┐ ┌────────────────────────┐ - │ Model Serving endpoint │ │ Lakebase Postgres │ - │ "analyst-agent-dev" │ │ ───────────────── │ - │ (CPU, scales to 0) │ │ conversation_history │ - │ │ │ query_logs │ - │ + AI Gateway: │ │ feedback │ - │ rate limit │ │ │ - │ (per-user key) │ │ (Postgres for tiny │ - │ inference-table │ │ per-turn writes — │ - │ audit │ │ Delta isn't great │ - │ usage tracking │ │ at row-by-row) │ - └────────────────────────┘ └────────────────────────┘ - - OBO (user identity end-to-end, when enabled): - ────────────────────────────── - App reads `x-forwarded-access-token` from the request, builds - `WorkspaceClient(token=...)`, calls the serving endpoint with the - user's identity. The agent-side MLflow auth policy and Model Serving - OBO credentials let downstream calls run as the user. If the app-side - feature is unavailable, the bootstrap script prints an explicit warning - and the deployment remains reference/dev only. -``` - -**Why Postgres for state?** Delta tables are great for analytics but bad at "insert one tiny row per chat turn at high frequency." Lakebase is Databricks's managed Postgres — same governance, right tool for the job. - ---- - -## How it's built — three pillars - -This repo is a worked example of combining three things that, together, change how you ship Databricks projects. - -### Pillar 1 — Spec-Kit (spec-driven development) - -[Spec-Kit](https://github.com/github/spec-kit) is a workflow that forces you to write — and *clarify* — a specification before writing code. Each phase is a slash-command in Claude Code that produces a checked-in artifact: - -``` - /speckit-specify → specs//spec.md What & why (no how) - │ - ▼ - /speckit-clarify → appended Q&A in spec.md Resolve ambiguity - │ - ▼ - /speckit-plan → specs//plan.md Tech stack + structure - │ + research.md, data-model.md, - │ contracts/, quickstart.md - ▼ - /speckit-tasks → specs//tasks.md Dependency-ordered tasks - │ - ▼ - /speckit-analyze → cross-artifact consistency check - │ - ▼ - /speckit-implement → the actual code -``` - -`.specify/extensions.yml` auto-commits at each phase boundary so the trail is clean. `.specify/memory/constitution.md` defines six **non-negotiable principles** every plan must respect: - -| # | Principle | What it means | -|---|---|---| -| I | **Unity Catalog source of truth** | Every table, volume, model, index, endpoint lives under `.` — no DBFS, no workspace-local resources | -| II | **Parse once, extract many** | `ai_parse_document` runs once at Silver → VARIANT; everything downstream reads the parsed output | -| III | **Declarative over imperative** | SDP SQL pipelines, Lakeflow Jobs, DAB resources — no production notebooks | -| IV | **Quality before retrieval** | 5-dim rubric scores every section; only ≥22/30 reach the index. Embed `summary`, not raw text | -| V | **Eval-gated agents** | MLflow CLEARS scores must clear thresholds before any deploy is considered complete | -| VI | **Reproducible deploys** | `databricks bundle deploy -t ` recreates the entire stack; `dev` and `prod` parity enforced | - -When you read `specs/001-doc-intel-10k/plan.md` you'll see a "Constitution Check" gate that maps each design decision back to the principle it satisfies. When you read `specs/001-doc-intel-10k/tasks.md` you'll see how each task derives from the plan, and how user-stories (P1, P2, P3) are independently demoable. - -### Pillar 2 — Databricks Asset Bundles + the Claude Code skill suite - -[**Databricks Asset Bundles**](https://docs.databricks.com/aws/en/dev-tools/bundles/) (DABs) describe most of the workspace state as YAML. One root `databricks.yml` declares variables and targets (`dev`, `prod`); `resources/**/*.yml` declares each resource (pipeline, jobs, Vector Search endpoint, index-refresh job, serving endpoint, app, monitor, dashboard, Lakebase instance + catalog). `databricks bundle deploy -t dev` reconciles workspace state to YAML. The two non-DAB-managed pieces — the Vector Search **index** itself and the registered **model version** — are produced at runtime by `jobs/index_refresh/sync_index.py` and `agent/log_and_register.py` respectively, which the bootstrap script orchestrates. - -This repo was built with Databricks-specific Claude Code skill bundles. Those bundles are distributed by Databricks via the CLI / Claude Code plugin channel and **are not vendored in this open-source tree** — install them locally if you have access, or reference the canonical Databricks docs (mapping in [`CONTRIBUTING.md`](./CONTRIBUTING.md)). - -| Skill bundle | What it provides | Canonical docs | -|---|---|---| -| **databricks-core** | Auth, profiles, data exploration, bundle basics | [docs](https://docs.databricks.com/aws/en/dev-tools/cli/) | -| **databricks-dabs** | DAB structure, validation, deploy workflow, target separation | [docs](https://docs.databricks.com/aws/en/dev-tools/bundles/) | -| **databricks-pipelines** | Lakeflow Spark Declarative Pipelines (`ai_parse_document`, `ai_classify`, `ai_extract`, `APPLY CHANGES INTO`) | [docs](https://docs.databricks.com/aws/en/dlt/) | -| **databricks-jobs** | Lakeflow Jobs with retries, schedules, table-update / file-arrival triggers | [docs](https://docs.databricks.com/aws/en/jobs/) | -| **databricks-apps** | Databricks Apps (Streamlit), App resource bindings | [docs](https://docs.databricks.com/aws/en/dev-tools/databricks-apps/) | -| **databricks-lakebase** | Lakebase Postgres instances, branches, computes, endpoint provisioning | [docs](https://docs.databricks.com/aws/en/oltp/) | -| **databricks-model-serving** | Model Serving endpoints, AI Gateway, served entities, scaling config | [docs](https://docs.databricks.com/aws/en/machine-learning/model-serving/) | - -Skills are loaded by Claude Code on demand. When you ask Claude to "wire up Vector Search," it should read the Databricks pipeline/model-serving guidance *before* writing YAML, so the output reflects current Databricks API shapes — not stale training data. - -### Pillar 3 — Claude Code as the implementation surface - -Spec-Kit produces the specs. The Databricks skills provide platform expertise. **Claude Code orchestrates both**: every phase artifact and every code file in this repo was authored by prompting Claude Code with the spec/plan/tasks as context. - -The workflow looks like: - -1. `/speckit-specify` → Claude writes spec.md from a natural-language description, you iterate via `/speckit-clarify` until ambiguity is resolved. -2. `/speckit-plan` → Claude consults the constitution + Databricks skills, drafts plan.md with research decisions and architecture. -3. `/speckit-tasks` → Claude generates a dependency-ordered task list grouped by user story (P1, P2, P3). -4. `/speckit-implement` → Claude writes the actual SQL/Python/YAML, one task at a time, committing per task. -5. Operational loops: when the deploy hits unexpected issues (it always does), Claude reads the runbook, fixes the issue, updates the runbook, commits. - -The "AI-driven" part isn't "the AI did it for you" — it's "the AI carries the boring parts (boilerplate YAML, retry-loop scripts, dependency analysis) so you focus on the actually-hard parts (what the spec should say, what the constitution should require)." - ---- - -## Deploy ordering: foundation → consumers - -DABs deploy *everything in one shot*. But our resources have a chicken-and-egg problem on a fresh workspace: - -``` - ┌────────────────────────────────────────────────┐ - │ What "bundle deploy" tries to create: │ - │ │ - │ ▸ Pipeline ────┐ │ - │ ▸ Tables ────┼──── all need each other │ - │ ▸ Vector idx ───┤ │ - │ ▸ Model ───┤ Monitor wants the │ - │ ▸ Endpoint ────┤ KPI table to exist │ - │ ▸ App ───┤ BEFORE it can attach │ - │ ▸ Monitor ────┘ │ - │ ▸ Lakebase ──── │ - └────────────────────────────────────────────────┘ - - Endpoint needs a registered model version. - Model version needs the model logged. - Model logging needs the agent code. - Monitor needs the table populated. - Table needs the pipeline to run. - - ▶ Single `bundle deploy` → 4+ errors on a fresh workspace. -``` - -The fix is a **staged deploy** orchestrated by `scripts/bootstrap-dev.sh`. Resources are split into two directories by data dependency: - -``` - resources/ - ├── foundation/ ← no data deps — deploy first - │ ├── catalog.yml (schema + volume + grants) - │ ├── doc_intel.pipeline.yml - │ ├── retention.job.yml - │ └── lakebase_instance.yml - │ - └── consumers/ ← need foundation to be RUNNING and producing data - ├── agent.serving.yml (needs registered model version) - ├── kpi_drift.yml (needs gold_filing_kpis table) - ├── filings_index.yml (VS endpoint) - ├── index_refresh.job.yml (needs source table) - ├── analyst.app.yml (needs Lakebase + agent endpoint) - ├── usage.dashboard.yml - └── lakebase_catalog.yml (needs instance AVAILABLE) -``` - -**The bootstrap script auto-detects which mode to run** by checking whether the agent serving endpoint already has a populated config: - -``` - does analyst-agent-${target} have served entities? - │ - no ◀───────┴───────▶ yes - │ │ - ▼ ▼ - ┌──────────────────┐ ┌──────────────────┐ - │ FIRST-DEPLOY │ │ STEADY-STATE │ - │ (staged) │ │ (full deploy) │ - ├──────────────────┤ ├──────────────────┤ - │ 1. temp-rename │ │ 1. bundle deploy │ - │ consumers/* │ │ (full bundle) │ - │ .yml.skip │ │ │ - │ 2. bundle deploy │ │ 2. refresh data: │ - │ (foundation) │ │ upload, run │ - │ 3. produce data: │ │ pipeline, │ - │ upload, run, │ │ register new │ - │ register │ │ model version │ - │ model │ │ + repoint │ - │ 4. wait Lakebase │ │ serving in- │ - │ AVAILABLE │ │ place │ - │ 5. restore yamls │ │ │ - │ 6. bundle deploy │ │ │ - │ (full bundle) │ │ │ - └────────┬─────────┘ └────────┬─────────┘ - │ │ - └───────────┬───────────┘ - ▼ - ┌──────────────────────────┐ - │ Common to both: │ - │ • bundle run analyst_app│ - │ • UC grants chain │ - │ • smoke check │ - └──────────────────────────┘ -``` - -**Why two modes?** DAB tracks resource state; if you run the temp-rename trick against an *existing* deployment, DAB sees the consumer YAMLs as removed and plans to **delete** the serving endpoint, app, monitor, etc. Safe-ish on a fresh workspace; destructive in steady-state. The script detects mode and does the right thing. - -CI (`.github/workflows/deploy.yml`) assumes steady-state — the first-ever bring-up of a workspace must be done locally with `./scripts/bootstrap-dev.sh`. After that, every push to `main` runs the steady-state path: full `bundle deploy` → refresh data → repoint serving endpoint → grants → CLEARS gate. - -Full breakdown in [`docs/runbook.md`](./docs/runbook.md). - ---- - ## CLEARS quality gate -Before any deploy reaches production, an evaluation must pass. This is constitution principle V — eval-gated agents. +Before any deploy reaches production, an evaluation must pass (constitution principle V — eval-gated agents). ``` evals/dataset.jsonl (30 questions: 20 single-filing P2 + 10 cross-company P3) @@ -652,93 +324,21 @@ For day-2 ops (rolling agent versions, debugging low quality scores, inspecting ``` databricks/ ├── databricks.yml # Bundle root — variables + dev/prod targets -├── README.md # This file -├── CLAUDE.md # Runtime guidance for Claude Code sessions -├── CONTRIBUTING.md # Contribution guidelines -├── SECURITY.md # Identity modes, OBO, grants -├── PRODUCTION_READINESS.md # Reference / Pilot / Production checklists -├── VALIDATION.md # Validation procedure with expected outputs -├── REAL_10K_PILOT.md # Real EDGAR pilot guidance -├── LICENSE # MIT -│ -├── pipelines/sql/ # Lakeflow SDP — Bronze → Silver → Gold (SQL) -│ ├── 01_bronze.sql # Auto Loader BINARYFILE ingest + size filter -│ ├── 02_silver_parse.sql # ai_parse_document → VARIANT -│ ├── 03_gold_classify_extract.sql # ai_classify + ai_extract → typed KPIs -│ └── 04_gold_quality.sql # 5-dim rubric → embed_eligible filter -│ -├── agent/ # Mosaic AI Agent Framework -│ ├── analyst_agent.py # mlflow.pyfunc model + routing -│ ├── retrieval.py # Hybrid search + re-rank + OBO VS client -│ ├── supervisor.py # Cross-company fan-out -│ ├── tools.py # UC Function tool over gold_filing_kpis -│ ├── _obo.py # On-behalf-of credentials helpers -│ ├── log_and_register.py # Register + auth_policy + alias -│ └── tests/ # pytest unit tests -│ -├── app/ # Streamlit App on Databricks Apps -│ ├── app.py # Chat UI + citations + thumbs feedback + OBO -│ ├── lakebase_client.py # psycopg writes to query_logs / feedback -│ ├── app.yaml # App runtime config (port, CORS, XSRF) -│ └── README.md # App-specific runtime + local-dev notes -│ -├── evals/ # MLflow CLEARS eval gate -│ ├── dataset.jsonl # 30 hand-authored questions (P2 + P3) -│ └── clears_eval.py # mlflow.evaluate(model_type="databricks-agent") -│ -├── jobs/ # Lakeflow Jobs Python tasks -│ ├── retention/prune_volume.py # 90-day raw PDF cleanup -│ └── index_refresh/sync_index.py # Vector Search SYNC INDEX -│ -├── resources/ # DAB resources, split by data dependency -│ ├── foundation/ # Stage 1 — no data deps -│ └── consumers/ # Stage 2 — depend on foundation data -│ -├── scripts/ # Operational scripts -│ ├── bootstrap-dev.sh # Fresh-workspace bring-up (staged deploy) -│ └── wait_for_kpis.py # Poll helper used by bootstrap + CI -│ -├── samples/ # Synthetic 10-Ks for smoke tests + eval -│ ├── synthesize.py # Reproducible PDF generator -│ ├── ACME_10K_2024.pdf -│ ├── BETA_10K_2024.pdf -│ ├── GAMMA_10K_2024.pdf -│ └── garbage_10K_2024.pdf # SC-006 negative test (low quality) -│ -├── specs/ # Spec-Kit artifacts -│ └── 001-doc-intel-10k/ -│ ├── spec.md # What & why -│ ├── plan.md # Tech stack + Constitution Check -│ ├── tasks.md # Dependency-ordered implementation tasks -│ ├── research.md # Decision log -│ ├── data-model.md # Entity → table mapping -│ ├── quickstart.md # 30-min deploy walkthrough -│ └── contracts/ # JSON schemas for KPIs + agent I/O -│ -├── docs/ -│ └── runbook.md # Day-2 ops + bring-up workflow -│ -├── .specify/ # Spec-Kit machinery (constitution, hooks) -│ ├── memory/constitution.md # Six non-negotiable principles -│ └── extensions.yml # Auto-commit hooks per phase -│ -└── .github/workflows/ - └── deploy.yml # PR validate; main → steady-state deploy + CLEARS gate - # (first-ever bring-up must be done locally via bootstrap-dev.sh) +├── pipelines/sql/ # Lakeflow SDP — Bronze → Silver → Gold (SQL only) +├── agent/ # Mosaic AI Agent Framework — pyfunc, retrieval, OBO +├── app/ # Streamlit on Databricks Apps + Lakebase client +├── evals/ # MLflow CLEARS eval gate (dataset + runner) +├── jobs/ # Lakeflow Jobs (retention, index refresh) +├── resources/foundation/ # DAB resources with no data deps +├── resources/consumers/ # DAB resources that depend on foundation data +├── scripts/ # bootstrap-dev.sh + helpers +├── samples/ # Synthetic 10-K PDFs (regenerable) +├── specs/001-doc-intel-10k/ # Spec-Kit artifacts (spec, plan, tasks, etc.) +├── docs/ # design.md (this repo's "why") + runbook.md (day-2 ops) +└── .specify/ # Spec-Kit machinery (constitution, hooks) ``` ---- - -## What you can learn from this repo - -- **How to wire `ai_parse_document` into Lakeflow SDP** — pattern for streaming-tables + `STREAM(...)` views + `APPLY CHANGES INTO` keyed on filename. -- **How to score document quality before retrieval** — five 0–6 dimensions in SQL, threshold filter on the index source. -- **How to log a Mosaic AI agent to UC** — `mlflow.pyfunc` with both inputs *and* outputs in the signature (UC requirement), `AnyType` for variable-shape fields, `auth_policy` + `resources` for OBO. -- **How to ground an agent with citations** — hybrid Vector Search → re-rank → top-k → LLM with explicit "cite sources [1] [2]" prompt. -- **How to handle DAB deploy ordering** — chicken-egg dependencies between heterogeneous resources, solved with a 5-step bootstrap rather than `depends_on` (which DAB doesn't reliably honor across resource types). -- **How to gate deploys on MLflow eval** — `mlflow.evaluate(model_type="databricks-agent")` with documented metric keys, per-axis thresholds, exit-code gate in CI. -- **How to do end-to-end OBO** — `ModelServingUserCredentials` from `databricks_ai_bridge`, `CredentialStrategy.MODEL_SERVING_USER_CREDENTIALS` for Vector Search, MLflow `auth_policy` with `model-serving` + `vector-search` user scopes, App-side `user_api_scopes` declaration. -- **How Spec-Kit + Claude Code + Databricks skills compose** — every artifact in `specs/` and `pipelines/` and `agent/` was generated through that loop. +Top-level docs: [`CLAUDE.md`](./CLAUDE.md) (runtime guidance for Claude Code), [`CONTRIBUTING.md`](./CONTRIBUTING.md), [`SECURITY.md`](./SECURITY.md), [`PRODUCTION_READINESS.md`](./PRODUCTION_READINESS.md), [`VALIDATION.md`](./VALIDATION.md), [`REAL_10K_PILOT.md`](./REAL_10K_PILOT.md), [`LICENSE`](./LICENSE). --- @@ -780,7 +380,5 @@ Released under the [**MIT License**](./LICENSE) — Copyright (c) 2026 Sathish K - [**Spec-Kit**](https://github.com/github/spec-kit) — spec-driven development workflow for AI coding agents. - [**Claude Code**](https://claude.com/claude-code) — Anthropic's CLI for AI-assisted development. -- [**Anthropic Skills**](https://github.com/anthropics/skills) — general-purpose Claude Code skill bundles. -- [**Databricks Lakehouse + Mosaic AI**](https://www.databricks.com/) — Unity Catalog, Lakeflow Spark Declarative Pipelines, Mosaic AI Vector Search, Agent Framework, Model Serving, AI Gateway, Databricks Apps, Lakebase, Lakehouse Monitoring. - -The 10-K analyst pattern is inspired by Databricks's own reference architecture for governed agent applications. +- [**Agent Skills**](https://github.com/anthropics/skills) — general-purpose Claude Code skill bundles. +- [**Databricks**](https://www.databricks.com/) — Unity Catalog, Lakeflow Spark Declarative Pipelines, Mosaic AI Vector Search, Agent Framework, Model Serving, AI Gateway, Databricks Apps, Lakebase, Lakehouse Monitoring. diff --git a/docs/_social_preview.py b/docs/_social_preview.py index f9a46a9..4bc55e1 100644 --- a/docs/_social_preview.py +++ b/docs/_social_preview.py @@ -26,17 +26,30 @@ ACCENT = "#FF3621" # Databricks orange LINE = "#252D3F" # subtle separator -# Arial bundles ship on macOS, support a wide glyph set including arrows, -# and have explicit Regular/Bold/Black files (no .ttc index guessing). -FONT_REG = "/System/Library/Fonts/Supplemental/Arial.ttf" -FONT_BOLD = "/System/Library/Fonts/Supplemental/Arial Bold.ttf" -FONT_BLACK = "/System/Library/Fonts/Supplemental/Arial Black.ttf" +# Prefer macOS Arial for local generation, but fall back to Liberation Sans in +# Linux devcontainers. +FONT_CANDIDATES = { + "regular": [ + "/System/Library/Fonts/Supplemental/Arial.ttf", + "/usr/share/fonts/truetype/liberation2/LiberationSans-Regular.ttf", + ], + "bold": [ + "/System/Library/Fonts/Supplemental/Arial Bold.ttf", + "/usr/share/fonts/truetype/liberation2/LiberationSans-Bold.ttf", + ], + "black": [ + "/System/Library/Fonts/Supplemental/Arial Black.ttf", + "/usr/share/fonts/truetype/liberation2/LiberationSans-Bold.ttf", + ], +} OUT = Path(__file__).parent / "social-preview.png" def font(size: int, weight: str = "regular") -> ImageFont.FreeTypeFont: - path = {"regular": FONT_REG, "bold": FONT_BOLD, "black": FONT_BLACK}[weight] + path = next((p for p in FONT_CANDIDATES[weight] if Path(p).exists()), None) + if path is None: + raise FileNotFoundError(f"No usable font found for weight={weight!r}") return ImageFont.truetype(path, size) @@ -70,7 +83,7 @@ def main() -> None: # One-line architecture summary, near bottom. ASCII arrows guarantee # glyph coverage across any future font swap. arch_f = font(22, "bold") - arch_text = "ai_parse_document -> typed KPIs -> Vector Search -> cited agent on Mosaic AI" + arch_text = "ai_parse_document -> typed KPIs -> Vector Search -> eval-gated cited agent" d.text((margin, H - margin - 80), arch_text, font=arch_f, fill=FG) # Separator + footer. diff --git a/docs/design.md b/docs/design.md new file mode 100644 index 0000000..f405159 --- /dev/null +++ b/docs/design.md @@ -0,0 +1,355 @@ +# Design — Databricks Document Intelligence Agent + +This document covers the *why*, the architecture, and the build workflow behind the repo. For setup and day-to-day use, see [`README.md`](../README.md). For day-2 ops, see [`runbook.md`](./runbook.md). + +## Table of contents + +- [Why this exists](#why-this-exists) +- [Architecture](#architecture) + - [Two halves: an offline pipeline, and an online agent](#two-halves-an-offline-pipeline-and-an-online-agent) + - [Vector Search bridges data and agent](#vector-search-bridges-data-and-agent) + - [Agent has two paths, one endpoint](#agent-has-two-paths-one-endpoint) + - [Runtime stack](#runtime-stack) +- [How it's built — three pillars](#how-its-built--three-pillars) + - [Pillar 1 — Spec-Kit](#pillar-1--spec-kit-spec-driven-development) + - [Pillar 2 — Databricks Asset Bundles + the Claude Code skill suite](#pillar-2--databricks-asset-bundles--the-claude-code-skill-suite) + - [Pillar 3 — Claude Code as the implementation surface](#pillar-3--claude-code-as-the-implementation-surface) +- [Deploy ordering: foundation → consumers](#deploy-ordering-foundation--consumers) +- [What you can learn from this repo](#what-you-can-learn-from-this-repo) + +--- + +## Why this exists + +Databricks shipped a lot of new generative-AI surface area in 2025–2026: `ai_parse_document`, Mosaic AI Vector Search, the Agent Framework, AI Gateway, Lakebase, Databricks Apps. Tutorials show each piece in isolation; nobody shows them wired together with **eval gates, governance, and reproducible deploys** the way you'd actually ship to analysts. + +This repo is that worked example. Drop a PDF into a governed UC volume; ten minutes later, an analyst can ask cited questions in plain English with end-to-end audit. The whole stack is described declaratively as one **Databricks Asset Bundle (DAB)** plus a small bootstrap script. DAB manages catalog/schema/volume, pipeline, jobs, the Vector Search **endpoint**, the Lakebase instance, the serving endpoint, the monitor, the app, and the dashboard; the Vector Search **index** itself is created and synced by `jobs/index_refresh/sync_index.py` (DAB doesn't yet manage indexes as a resource type), and the agent model version is registered by `agent/log_and_register.py`. The bootstrap script orchestrates them in the right order. + +It also demonstrates a development workflow: **Spec-Kit** for spec-driven design, **Claude Code** with Databricks skill bundles for AI-assisted implementation, six **non-negotiable constitution principles** that gate every plan. See [How it's built](#how-its-built--three-pillars). + +--- + +## Architecture + +### Two halves: an offline pipeline, and an online agent + +``` + ╔═══════════════════════════════════════════════════════════════════╗ + ║ pipelines/sql/ (one SQL file per tier) ║ + ╚═══════════════════════════════════════════════════════════════════╝ + + raw_filings/ ┌─────────────────┐ ┌─────────────────┐ ┌──────────────────┐ + ACME_10K.pdf ──▶ │ bronze_filings │──▶│ silver_parsed_ │──▶│ gold_filing_ │ + BETA_10K.pdf │ (raw bytes, │ │ filings (parsed │ │ sections (one │ + GAMMA_10K.pdf │ filename, │ │ VARIANT — │ │ row per parsed │ + │ ingested_at) │ │ ai_parse_ │ │ $.sections[*]; │ + │ │ │ document) │ │ fallback to │ + │ >50MB rejects: │ │ │ │ full_document │ + │ bronze_filings │ │ Status: ok / │ │ if absent) │ + │ _rejected │ │ partial / error │ │ │ + └─────────────────┘ └─────────────────┘ │ gold_filing_kpis │ + 01_bronze.sql 02_silver_parse │ (typed columns: │ + .sql │ segment_revenue │ + │ ARRAY, │ + │ top_risks │ + │ ARRAY) │ + └──────────────────┘ + 03_gold_classify + _extract.sql + │ + ▼ + ┌──────────────────┐ + │ gold_filing_ │ + │ quality │ + │ (5-dim rubric: │ + │ parse, layout, │ + │ ocr, sections, │ + │ kpi → 0-30) │ + └──────────────────┘ + 04_gold_quality.sql +``` + +**Key idea — "parse once, extract many":** PDFs are expensive to parse. Silver runs `ai_parse_document` exactly once per file and stores the structured result as a `VARIANT`. Everything downstream — classification, KPI extraction, summarization, quality scoring — reads the parsed output, never the raw bytes. This is a non-negotiable constitution principle. + +**Triggering**: prod runs the pipeline in `continuous: true` mode so Auto Loader (`read_files`) reacts to new PDFs in the volume automatically. Dev overrides to `continuous: false` to avoid a 24/7 cluster during smoke iterations. See `resources/foundation/doc_intel.pipeline.yml` and the dev override block in `databricks.yml`. + +### Vector Search bridges data and agent + +``` + gold_filing_sections ┌─────────────────────────┐ + (governed Delta table) ─────▶ │ Mosaic AI Vector │ + │ Search Index │ + Filter: embed_eligible=true │ (Delta-Sync — auto- │ + Embed column: "summary" │ refreshes when Gold │ + │ updates) │ + └─────────────────────────┘ + + Why "summary" not the raw text? + ───────────────────────────── + Embedding a 50-page 10-K verbatim is noisy. We embed an LLM-written + summary instead — tighter, more searchable. Constitution principle IV: + "Quality before retrieval." +``` + +**Ownership note**: DAB manages the Vector Search **endpoint** (`resources/consumers/filings_index.yml`) and the index-refresh **job** (`resources/consumers/index_refresh.job.yml`). The **index** itself isn't yet a DAB-managed resource type as of CLI 0.298 — `jobs/index_refresh/sync_index.py` creates the Delta-Sync index on first run and triggers a sync on subsequent runs. That's why the bootstrap script's stage-2 deploy creates the endpoint + job, and the job's first execution materializes the actual index. + +### Agent has two paths, one endpoint + +``` + User question + │ + ▼ + ┌────────────────────────────────────────────┐ + │ AnalystAgent.predict() │ + │ ───────────────────── │ + │ contains "compare" / "vs" / │ + │ "between" + ≥2 company names? │ + └────────────┬─────────────────┬─────────────┘ + │ no │ yes + ▼ ▼ + ┌──────────────────────┐ ┌──────────────────────┐ + │ Single-filing path │ │ Supervisor path │ + │ │ │ │ + │ 1. Hybrid search │ │ For each company: │ + │ (keyword + vec) │ │ ▸ run analyst path │ + │ 2. Re-rank → top 5 │ │ ▸ pull KPIs from │ + │ 3. LLM generates │ │ gold_filing_kpis │ + │ answer w/ [1] [2] │ │ Format markdown │ + │ citations │ │ table with cites. │ + └──────────────────────┘ └──────────────────────┘ + │ │ + └────────┬────────┘ + ▼ + ┌──────────────────────┐ + │ Response JSON: │ + │ answer │ + │ citations[] │ + │ grounded: bool │ + │ latency_ms │ + └──────────────────────┘ +``` + +The agent is an `mlflow.pyfunc` model registered in Unity Catalog and served behind an **AI Gateway** (rate limiting per-user, usage tracking, inference-table audit). Identity passthrough is implemented at the *App layer* when the workspace has Databricks Apps user-token passthrough enabled: the Streamlit app extracts the user's `x-forwarded-access-token` header and constructs a user-scoped `WorkspaceClient`. The served model is OBO-ready via MLflow `auth_policy` and Model Serving user credentials. If app-level passthrough is not enabled, the app falls back to service-principal auth and the repo must be treated as a reference/dev deployment, not a production row-level-security deployment. See [`../SECURITY.md`](../SECURITY.md) and [`../app/README.md`](../app/README.md). + +### Runtime stack + +``` + ┌──────────────────────────────────────────────────────────────────┐ + │ │ + │ Databricks App (Streamlit) ← user interacts here │ + │ app/app.py │ + │ │ + │ ┌────────────────┐ ┌──────────────────┐ │ + │ │ Chat input box │ │ Citation chips │ │ + │ │ Thumbs up/down │ │ Markdown tables │ │ + │ └────────┬───────┘ └─────┬────────────┘ │ + │ │ │ │ + └──────────────│─────────────────│─────────────────────────────────┘ + │ │ + │ query │ feedback writes + ▼ ▼ + ┌────────────────────────┐ ┌────────────────────────┐ + │ Model Serving endpoint │ │ Lakebase Postgres │ + │ "analyst-agent-dev" │ │ ───────────────── │ + │ (CPU, scales to 0) │ │ conversation_history │ + │ │ │ query_logs │ + │ + AI Gateway: │ │ feedback │ + │ rate limit │ │ │ + │ (per-user key) │ │ (Postgres for tiny │ + │ inference-table │ │ per-turn writes — │ + │ audit │ │ Delta isn't great │ + │ usage tracking │ │ at row-by-row) │ + └────────────────────────┘ └────────────────────────┘ + + OBO (user identity end-to-end, when enabled): + ────────────────────────────── + App reads `x-forwarded-access-token` from the request, builds + `WorkspaceClient(token=...)`, calls the serving endpoint with the + user's identity. The agent-side MLflow auth policy and Model Serving + OBO credentials let downstream calls run as the user. If the app-side + feature is unavailable, the bootstrap script prints an explicit warning + and the deployment remains reference/dev only. +``` + +**Why Postgres for state?** Delta tables are great for analytics but bad at "insert one tiny row per chat turn at high frequency." Lakebase is Databricks's managed Postgres — same governance, right tool for the job. + +--- + +## How it's built — three pillars + +This repo combines three things: Spec-Kit for spec-driven design, Databricks Asset Bundles + Claude Code skill bundles for declarative platform work, and Claude Code as the implementation surface. + +### Pillar 1 — Spec-Kit (spec-driven development) + +[Spec-Kit](https://github.com/github/spec-kit) is a workflow that forces you to write — and *clarify* — a specification before writing code. Each phase is a slash-command in Claude Code that produces a checked-in artifact: + +``` + /speckit-specify → specs//spec.md What & why (no how) + │ + ▼ + /speckit-clarify → appended Q&A in spec.md Resolve ambiguity + │ + ▼ + /speckit-plan → specs//plan.md Tech stack + structure + │ + research.md, data-model.md, + │ contracts/, quickstart.md + ▼ + /speckit-tasks → specs//tasks.md Dependency-ordered tasks + │ + ▼ + /speckit-analyze → cross-artifact consistency check + │ + ▼ + /speckit-implement → the actual code +``` + +`.specify/extensions.yml` auto-commits at each phase boundary so the trail is clean. `.specify/memory/constitution.md` defines six **non-negotiable principles** every plan must respect: + +| # | Principle | What it means | +|---|---|---| +| I | **Unity Catalog source of truth** | Every table, volume, model, index, endpoint lives under `.` — no DBFS, no workspace-local resources | +| II | **Parse once, extract many** | `ai_parse_document` runs once at Silver → VARIANT; everything downstream reads the parsed output | +| III | **Declarative over imperative** | SDP SQL pipelines, Lakeflow Jobs, DAB resources — no production notebooks | +| IV | **Quality before retrieval** | 5-dim rubric scores every section; only ≥22/30 reach the index. Embed `summary`, not raw text | +| V | **Eval-gated agents** | MLflow CLEARS scores must clear thresholds before any deploy is considered complete | +| VI | **Reproducible deploys** | `databricks bundle deploy -t ` recreates the entire stack; `dev` and `prod` parity enforced | + +When you read `specs/001-doc-intel-10k/plan.md` you'll see a "Constitution Check" gate that maps each design decision back to the principle it satisfies. When you read `specs/001-doc-intel-10k/tasks.md` you'll see how each task derives from the plan, and how user-stories (P1, P2, P3) are independently demoable. + +### Pillar 2 — Databricks Asset Bundles + the Claude Code skill suite + +[**Databricks Asset Bundles**](https://docs.databricks.com/aws/en/dev-tools/bundles/) (DABs) describe most of the workspace state as YAML. One root `databricks.yml` declares variables and targets (`dev`, `prod`); `resources/**/*.yml` declares each resource (pipeline, jobs, Vector Search endpoint, index-refresh job, serving endpoint, app, monitor, dashboard, Lakebase instance + catalog). `databricks bundle deploy -t dev` reconciles workspace state to YAML. The two non-DAB-managed pieces — the Vector Search **index** itself and the registered **model version** — are produced at runtime by `jobs/index_refresh/sync_index.py` and `agent/log_and_register.py` respectively, which the bootstrap script orchestrates. + +This repo was built with Databricks-specific Claude Code skill bundles. Those bundles are distributed by Databricks via the CLI / Claude Code plugin channel and **are not vendored in this open-source tree** — install them locally if you have access, or reference the canonical Databricks docs (mapping in [`../CONTRIBUTING.md`](../CONTRIBUTING.md)). + +| Skill bundle | What it provides | Canonical docs | +|---|---|---| +| **databricks-core** | Auth, profiles, data exploration, bundle basics | [docs](https://docs.databricks.com/aws/en/dev-tools/cli/) | +| **databricks-dabs** | DAB structure, validation, deploy workflow, target separation | [docs](https://docs.databricks.com/aws/en/dev-tools/bundles/) | +| **databricks-pipelines** | Lakeflow Spark Declarative Pipelines (`ai_parse_document`, `ai_classify`, `ai_extract`, `APPLY CHANGES INTO`) | [docs](https://docs.databricks.com/aws/en/dlt/) | +| **databricks-jobs** | Lakeflow Jobs with retries, schedules, table-update / file-arrival triggers | [docs](https://docs.databricks.com/aws/en/jobs/) | +| **databricks-apps** | Databricks Apps (Streamlit), App resource bindings | [docs](https://docs.databricks.com/aws/en/dev-tools/databricks-apps/) | +| **databricks-lakebase** | Lakebase Postgres instances, branches, computes, endpoint provisioning | [docs](https://docs.databricks.com/aws/en/oltp/) | +| **databricks-model-serving** | Model Serving endpoints, AI Gateway, served entities, scaling config | [docs](https://docs.databricks.com/aws/en/machine-learning/model-serving/) | + +Skills are loaded by Claude Code on demand. When you ask Claude to "wire up Vector Search," it should read the Databricks pipeline/model-serving guidance *before* writing YAML, so the output reflects current Databricks API shapes — not stale training data. + +### Pillar 3 — Claude Code as the implementation surface + +Spec-Kit produces the specs. The Databricks skills provide platform expertise. **Claude Code orchestrates both**: every phase artifact and every code file in this repo was authored by prompting Claude Code with the spec/plan/tasks as context. + +The workflow looks like: + +1. `/speckit-specify` → Claude writes spec.md from a natural-language description, you iterate via `/speckit-clarify` until ambiguity is resolved. +2. `/speckit-plan` → Claude consults the constitution + Databricks skills, drafts plan.md with research decisions and architecture. +3. `/speckit-tasks` → Claude generates a dependency-ordered task list grouped by user story (P1, P2, P3). +4. `/speckit-implement` → Claude writes the actual SQL/Python/YAML, one task at a time, committing per task. +5. Operational loops: when the deploy hits unexpected issues (it always does), Claude reads the runbook, fixes the issue, updates the runbook, commits. + +AI-driven here means Claude carries the boring parts (boilerplate YAML, retry-loop scripts, dependency analysis) so you spend time on what the spec should say and what the constitution should require. + +--- + +## Deploy ordering: foundation → consumers + +DABs deploy *everything in one shot*. But our resources have a chicken-and-egg problem on a fresh workspace: + +``` + ┌────────────────────────────────────────────────┐ + │ What "bundle deploy" tries to create: │ + │ │ + │ ▸ Pipeline ────┐ │ + │ ▸ Tables ────┼──── all need each other │ + │ ▸ Vector idx ───┤ │ + │ ▸ Model ───┤ Monitor wants the │ + │ ▸ Endpoint ────┤ KPI table to exist │ + │ ▸ App ───┤ BEFORE it can attach │ + │ ▸ Monitor ────┘ │ + │ ▸ Lakebase ──── │ + └────────────────────────────────────────────────┘ + + Endpoint needs a registered model version. + Model version needs the model logged. + Model logging needs the agent code. + Monitor needs the table populated. + Table needs the pipeline to run. + + ▶ Single `bundle deploy` → 4+ errors on a fresh workspace. +``` + +The fix is a **staged deploy** orchestrated by `scripts/bootstrap-dev.sh`. Resources are split into two directories by data dependency: + +``` + resources/ + ├── foundation/ ← no data deps — deploy first + │ ├── catalog.yml (schema + volume + grants) + │ ├── doc_intel.pipeline.yml + │ ├── retention.job.yml + │ └── lakebase_instance.yml + │ + └── consumers/ ← need foundation to be RUNNING and producing data + ├── agent.serving.yml (needs registered model version) + ├── kpi_drift.yml (needs gold_filing_kpis table) + ├── filings_index.yml (VS endpoint) + ├── index_refresh.job.yml (needs source table) + ├── analyst.app.yml (needs Lakebase + agent endpoint) + ├── usage.dashboard.yml + └── lakebase_catalog.yml (needs instance AVAILABLE) +``` + +**The bootstrap script auto-detects which mode to run** by checking whether the agent serving endpoint already has a populated config: + +``` + does analyst-agent-${target} have served entities? + │ + no ◀───────┴───────▶ yes + │ │ + ▼ ▼ + ┌──────────────────┐ ┌──────────────────┐ + │ FIRST-DEPLOY │ │ STEADY-STATE │ + │ (staged) │ │ (full deploy) │ + ├──────────────────┤ ├──────────────────┤ + │ 1. temp-rename │ │ 1. bundle deploy │ + │ consumers/* │ │ (full bundle) │ + │ .yml.skip │ │ │ + │ 2. bundle deploy │ │ 2. refresh data: │ + │ (foundation) │ │ upload, run │ + │ 3. produce data: │ │ pipeline, │ + │ upload, run, │ │ register new │ + │ register │ │ model version │ + │ model │ │ + repoint │ + │ 4. wait Lakebase │ │ serving in- │ + │ AVAILABLE │ │ place │ + │ 5. restore yamls │ │ │ + │ 6. bundle deploy │ │ │ + │ (full bundle) │ │ │ + └────────┬─────────┘ └────────┬─────────┘ + │ │ + └───────────┬───────────┘ + ▼ + ┌──────────────────────────┐ + │ Common to both: │ + │ • bundle run analyst_app│ + │ • UC grants chain │ + │ • smoke check │ + └──────────────────────────┘ +``` + +**Why two modes?** DAB tracks resource state; if you run the temp-rename trick against an *existing* deployment, DAB sees the consumer YAMLs as removed and plans to **delete** the serving endpoint, app, monitor, etc. Safe-ish on a fresh workspace; destructive in steady-state. The script detects mode and does the right thing. + +CI (`.github/workflows/deploy.yml`) assumes steady-state — the first-ever bring-up of a workspace must be done locally with `./scripts/bootstrap-dev.sh`. After that, every push to `main` runs the steady-state path: full `bundle deploy` → refresh data → repoint serving endpoint → grants → CLEARS gate. + +For the per-step procedure and known failure modes, see [`runbook.md` § Known deploy ordering gaps](./runbook.md#known-deploy-ordering-gaps-discovered-in-the-2026-04-24-smoke-test). + +--- + +## What you can learn from this repo + +- **Wiring `ai_parse_document` into Lakeflow SDP** — pattern for streaming-tables + `STREAM(...)` views + `APPLY CHANGES INTO` keyed on filename. +- **Scoring document quality before retrieval** — five 0–6 dimensions in SQL, threshold filter on the index source. +- **Logging a Mosaic AI agent to UC** — `mlflow.pyfunc` with both inputs *and* outputs in the signature (UC requirement), `AnyType` for variable-shape fields, `auth_policy` + `resources` for OBO. +- **Grounding an agent with citations** — hybrid Vector Search → re-rank → top-k → LLM with explicit "cite sources [1] [2]" prompt. +- **Handling DAB deploy ordering** — chicken-egg dependencies between heterogeneous resources, solved with a 5-step bootstrap rather than `depends_on` (which DAB doesn't reliably honor across resource types). +- **Gating deploys on MLflow eval** — `mlflow.evaluate(model_type="databricks-agent")` with documented metric keys, per-axis thresholds, exit-code gate in CI. +- **End-to-end OBO** — `ModelServingUserCredentials` from `databricks_ai_bridge`, `CredentialStrategy.MODEL_SERVING_USER_CREDENTIALS` for Vector Search, MLflow `auth_policy` with `model-serving` + `vector-search` user scopes, App-side `user_api_scopes` declaration. +- **Spec-Kit + Claude Code + Databricks skills composing** — every artifact in `specs/` and `pipelines/` and `agent/` was generated through that loop. diff --git a/docs/social-preview.png b/docs/social-preview.png index 735c2e7903bdea40fa1e42826a0597086247af7a..d02501bc858a4e69b2606165526b0def3157f64f 100644 GIT binary patch literal 38213 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 literal 38606 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