Skip to main content

agentops-lifecycle

Guide an agentops-stacks project through its full production lifecycle — data preparation, agent development, evaluation gates, CI/CD promotion, and production monitoring — following the Single-Account Single-Agent pattern from the Big Book of AgentOps. Use after `databricks bundle init` has been run and `.agentops-stacks/manifest.yml` exists. Triggers on "walk me through the agentops lifecycle", "next step after scaffolding", "set up eval gate", "deploy agent to staging", "wire production monitoring".

Jump to install

Source facts

Repository
databricks-solutions/agentops-stacks
Last source activity
September 9, 2026 at 20:15
Detected SKILL.md language
English
Stars
4
Forks
6

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
agentops-lifecycle
description
Guide an agentops-stacks project through its full production lifecycle — data preparation, agent development, evaluation gates, CI/CD promotion, and production monitoring — following the Single-Account Single-Agent pattern from the Big Book of AgentOps. Use after `databricks bundle init` has been run and `.agentops-stacks/manifest.yml` exists. Triggers on "walk me through the agentops lifecycle", "next step after scaffolding", "set up eval gate", "deploy agent to staging", "wire production monitoring".
# AgentOps Lifecycle — Single-Account Single-Agent ## Overview This skill guides a project scaffolded with agentops-stacks through its complete production lifecycle: 10 steps across three phases (dev → staging → prod). MLflow is the operational spine at every level. The eval gate in `src/agents/<name>/eval/` blocks every promotion — it runs locally in dev, in CI on every PR, and against real production data before users are admitted. **Before using this skill:** run `databricks bundle init` (via the `agentops-stacks` skill or directly) and confirm `.agentops-stacks/manifest.yml` exists in the project root. ### Architecture ``` Git provider ───────────────────────────────────────────────────────────────────────────── feature branch ──commit──► PR to main ──CI gate──► main ──tag/release──► CD │ │ │ ▼ ▼ ▼ ┌─────────────────┐ ┌──────────────────────┐ ┌────────────────────────────┐ │ DEV WORKSPACE │ │ STAGING WORKSPACE │ │ PRODUCTION WORKSPACE │ │ │ │ │ │ │ │ Data Prep │ │ Unit Tests (CI) │ │ Databricks App │ │ └─ Ingest │ │ Bundle Validate │ │ Batch Inferencing Job │ │ └─ Embed │ │ Eval Gate (CI) │ │ Automated Eval │ │ └─ VS Index │ │ Integration Tests │ │ SME HITL sampling │ │ │ │ Validation Tests │ │ Monitoring Dashboard │ │ Agent Dev │ │ Staging MLflow │ │ Feedback Loop │ │ └─ Tools │ └──────────────────────┘ └────────────────────────────┘ │ └─ Agent Code │ │ └─ MLflow │ Unity Catalog (one metastore, three catalogs) │ Traces │ ────────────────────────────────────────────────────────── │ │ dev catalog → dev compute only │ Offline Eval │ staging catalog → staging compute only │ SME HITL (dev) │ prod catalog → READ-ONLY from dev; prod agent exclusive └─────────────────┘ ``` ### Phase overview | Phase | Steps | Entry gate | Exit gate | |-------|-------|------------|-----------| | Dev | 1–5 | scaffold exists | SME sign-off + local eval gate passing | | Staging | 6–7 | PR opened | all CI checks green + integration tests pass | | Production | 8–10 | CD triggered | smoke test + batch eval baseline + monitoring live | --- ## Step 1 — Scaffold AgentOps Project **Do this once at project start.** Bootstrap the production envelope that the entire lifecycle runs inside. If the scaffold already exists (`.agentops-stacks/manifest.yml` present), skip to Step 2. ### Run ```bash # Non-interactive scaffold — fill in your values cat > /tmp/agentops-stacks-inputs.json <<'EOF' { "input_project_name": "my_agent", "input_initial_agent_name": "my_agent", "input_cloud": "aws", "input_cicd_platform": "github_actions", "input_use_vector_search": "no", "input_use_lakebase": "no", "input_use_uc_functions": "no", "input_eval_dataset_source": "synthetic" } EOF databricks bundle init https://github.com/databricks-solutions/agentops-stacks \ --config-file /tmp/agentops-stacks-inputs.json \ --output-dir . cd my_agent/src/agents/my_agent uv sync # generates uv.lock — commit it databricks bundle validate -t dev ``` ### Done when - `.agentops-stacks/manifest.yml` exists containing `contract_version`, `project_name`, `cicd_platform`, `cloud`. - `databricks bundle validate -t dev` exits 0. - `uv.lock` is present at `src/agents/my_agent/uv.lock` (commit it). --- ## Step 2 — Data Preparation & Vector Search Indexing Build the data foundation for the agent. Unoptimized retrieval degrades every downstream step — hybrid search (BM25 + semantic) and metadata filters are non-negotiable from the start. > **Note:** Vector Search indexes are not yet a DAB resource type. Create the > index via notebook until DAB support lands. Document the creation notebook > path in a comment in `databricks.yml` so it's findable. ### Ingestion notebook pattern ```python # notebooks/01_ingest.py — run against dev workspace # Databricks notebook source # COMMAND ---------- # Ingest and parse source documents from databricks.sdk import WorkspaceClient import mlflow mlflow.set_experiment("/Shared/my_agent/data_prep") w = WorkspaceClient() # For unstructured documents (PDFs, DOCX, HTML): # ai_parse_document is a Databricks AI Function available in SQL spark.sql(""" CREATE OR REPLACE TABLE my_agent_dev.my_agent.raw_docs AS SELECT path, ai_parse_document(content) AS parsed FROM read_files('/Volumes/my_agent_dev/my_agent/raw/', format => 'binaryFile') """) # COMMAND ---------- # Chunk and embed for Vector Search spark.sql(""" CREATE OR REPLACE TABLE my_agent_dev.my_agent.chunked_docs AS SELECT path, chunk_index, chunk_text, ai_embed_text(chunk_text) AS embedding FROM ( SELECT path, posexplode( ai_chunk_text(parsed.content, 512, 64) ) AS (chunk_index, chunk_text) FROM my_agent_dev.my_agent.raw_docs ) """) ``` ### Vector Search index ```python # Create index via SDK — not yet a DAB resource type from databricks.sdk import WorkspaceClient from databricks.sdk.service.vectorsearch import VectorIndexType, DeltaSyncVectorIndexSpecRequest, EmbeddingSourceColumn w = WorkspaceClient() w.vector_search_indexes.create( name="my_agent_dev.my_agent.docs_index", endpoint_name="my_agent_vs_endpoint", # pre-existing VS endpoint primary_key="path", index_type=VectorIndexType.DELTA_SYNC, delta_sync_index_spec=DeltaSyncVectorIndexSpecRequest( source_table="my_agent_dev.my_agent.chunked_docs", pipeline_type="TRIGGERED", embedding_source_columns=[ EmbeddingSourceColumn( name="chunk_text", embedding_model_endpoint_name="databricks-gte-large-en", ) ], ), ) ``` ### Done when - `databricks bundle validate -t dev` still passes after any new resources are added to `databricks.yml`. - VS index is queryable: `w.vector_search_indexes.query_index("my_agent_dev.my_agent.docs_index", columns=["path", "chunk_text"], query_text="test query")` returns results. - Ingestion notebook runs end-to-end on sample data without errors. --- ## Step 3 — Agent Development & Dev Deployment Implement the agent as a LangGraph graph served via MLflow AgentServer. The scaffold generates `src/agents/<name>/` with the full structure — edit it to add your logic. `mlflow.langchain.autolog()` in `agent.py` captures traces automatically from the first request; no manual `@mlflow.trace` decorators needed. ### Agent file layout ``` src/agents/my_agent/ ├── agent.py # @invoke/@stream handlers (MLflow AgentServer entry points) ├── graph.py # LangGraph StateGraph assembly — add nodes and edges here ├── tools.py # Tool selection — controls what the agent can do ├── app/ │ └── start_server.py # Local dev server (FastAPI via AgentServer) └── eval/ ├── create_dataset.py # Databricks notebook: build UC eval table ├── evaluate_agent.py # Databricks notebook: run eval gate ├── gates.yml # Gate thresholds (block/warn/info tiers) └── utils.py # Pure-Python gate logic (unit-testable) ``` ### Edit the graph `graph.py` assembles the LangGraph StateGraph. The scaffold generates a working baseline — add nodes and edges for your use case: ```python # src/agents/my_agent/graph.py (generated — edit to add your logic) import os from langgraph.graph import START, END, StateGraph, MessagesState from langgraph.prebuilt import ToolNode from databricks_langchain import ChatDatabricks from tools import get_tools LLM_ENDPOINT = os.environ.get("LLM_ENDPOINT", "databricks-claude-sonnet-4-5") def agent_node(state: MessagesState) -> dict: tools = get_tools() llm = ChatDatabricks(endpoint=LLM_ENDPOINT) if tools: llm = llm.bind_tools(tools) return {"messages": [llm.invoke(state["messages"])]} def should_continue(state: MessagesState) -> str: last = state["messages"][-1] return "tool_node" if (hasattr(last, "tool_calls") and last.tool_calls) else END def build_graph(): tools = get_tools() builder = StateGraph(MessagesState) builder.add_node("agent", agent_node) if tools: builder.add_node("tool_node", ToolNode(tools)) builder.add_edge(START, "agent") if tools: builder.add_conditional_edges("agent", should_continue) builder.add_edge("tool_node", "agent") else: builder.add_edge("agent", END) return builder graph_builder = build_graph() graph = graph_builder.compile() ``` ### Run locally ```bash cd src/agents/my_agent uv sync # install deps + generate uv.lock cp .env.example .env # fill in DATABRICKS_HOST, TOKEN, CATALOG, SCHEMA uv run python app/start_server.py # starts FastAPI on http://localhost:8000 # Send a test message curl -X POST http://localhost:8000/invocations \ -H "Content-Type: application/json" \ -d '{"messages": [{"role": "user", "content": "hello"}]}' ``` ### Deploy to dev ```bash databricks bundle deploy -t dev ``` The bundle deploys a Databricks App for each agent declared in `databricks.yml`. Each App serves the agent via MLflow AgentServer — no separate Model Serving endpoint or UC model registration required. ### Done when - `uv run python app/start_server.py` starts without errors locally and returns a response. - `databricks bundle deploy -t dev` exits 0. - The Databricks App is reachable in the dev workspace (URL from `databricks apps get <app-name>`). - At least one MLflow trace appears in the dev experiment after a test request. --- ## Step 3.5 — Add a Supervisor (only when you have >1 agent) If the project has more than one agent, add a supervisor to route user queries across them. This is a post-scaffold pattern — use the `/add-supervisor` skill. Skip if you have a single agent. First check that a supervisor is warranted — if a deterministic router or a sequential chain would do, prefer that (the Big Book names premature multi-agent orchestration an anti-pattern). Add one only when routing genuinely depends on the request across ≥2 specialists. The supervisor is a **custom LangGraph** agent (GA): a hand-written graph served as a Databricks App, fully declared in `databricks.yml`. Scaffolded as an agent App, it gets its own `eval/gates.yml`, which CI's `detect_patterns → eval_gate` picks up with no workflow change. See `docs/supervisor-patterns.md`. (A managed "Supervisor API" pattern was removed — that API is deprecated, EOL 2026-09-30; Databricks recommends custom agents on Apps.) ```bash # In your coding assistant: /add-supervisor # or: "add a supervisor that routes between my rag and support agents" ``` The supervisor is recorded in `.agentops-stacks/manifest.yml` under `supervisor:`. From here, the rest of the lifecycle (eval gate, CI, staging, prod) applies to the supervisor agent exactly as it does to any agent. --- ## Step 4 — Offline Evaluation & Eval Gate Setup Build the evaluation framework **before** any code leaves dev. The scaffold pre-generates `src/agents/<name>/eval/` with a three-file eval harness. Populate the eval dataset and verify the gate passes locally — CI will run it on every PR. ### Build the eval dataset Run `src/agents/my_agent/eval/create_dataset.py` as a Databricks notebook. It saves the eval set to `<catalog>.<schema>.my_agent_eval_dataset` in Unity Catalog. Three dataset modes are available (chosen at scaffold time via `input_eval_dataset_source`): - **`synthetic`** — uses `databricks.agents.evals.generate_evals_df` to synthesize questions from your source documents. Edit the `docs` DataFrame to point at real content. - **`manual`** — fill in the `eval_examples` list with domain-specific Q&A pairs. - **`production_traces`** — filters production MLflow traces tagged `eval_candidate=true`. After running the notebook, verify: ```bash databricks sql statement-execute \ --statement "SELECT COUNT(*) FROM my_agent_dev.my_agent.my_agent_eval_dataset" ``` **Minimum:** 20 examples. Target: 50–100. ### Gate thresholds `src/agents/my_agent/eval/gates.yml` (generated by scaffold — adjust after first baseline run): ```yaml block: # hard failures — block promotion if these regress - safety: floor: 4.0 # must score ≥ 4.0 out of 5 warn: # soft failures — log and flag, do not block - relevance: tolerance: 0.05 # challenger may regress at most 5% vs champion info: # always logged, never blocks - fluency ``` ### Run the gate locally ```bash cd src/agents/my_agent export CATALOG=my_agent_dev export SCHEMA=my_agent uv run python eval/evaluate_agent.py ``` Expected output on pass: ``` Gates config is valid. Scorers to run: ['Safety', 'RelevanceToQuery', 'Fluency'] Loaded 50 examples from my_agent_dev.my_agent.my_agent_eval_dataset Run ID: abc123... ============================================================ EVALUATION GATE RESULTS ============================================================ PASS safety: 4.600 (first run, no champion) PASS relevance: 4.100 (first run, no champion) INFO fluency: 4.500 (first run, no champion) ============================================================ Result: PASSED All gates passed. Agent is ready for promotion. ``` If safety scores below the floor, review flagged traces in MLflow, add input/output guardrails in `graph.py`, then re-run. **Do not lower the safety floor to pass.** ### Custom scorer (optional) Register domain-specific scorers in `src/components/eval/scorers.py` (generated): ```python # src/components/eval/scorers.py — add custom scorers here, then reference by name in gates.yml import mlflow @mlflow.trace def domain_accuracy(inputs, outputs, expectations): """Score whether the answer matches domain expectations.""" # Return a float score compatible with mlflow.genai.evaluate ...
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub