Skip to main content

02-agent-framework

Use when building a custom agent with the OpenAI Agents SDK and MLflow ResponsesAgent. Covers Agent class, Runner, @function_tool, handoffs, streaming, ModelConfig, mlflow.models.set_model(), autolog, and manual tracing. Track A Step 2. Produces a customized, MLflow-compatible agent class ready for tools and deployment.

Aller à l'installation

Informations de source

Dépôt
databricks-solutions/vibe-coding-workshop-template
Dernière activité de la source
6 juin 2026 à 14:09
Langue détectée de SKILL.md
anglais
Étoiles
6
Forks
7

Options d'installation

Le prompt qui vérifie d'abord la source est sélectionné par défaut. Vous pouvez passer à une commande directe ou télécharger une copie locale.

Vérifiez les fichiers source

Lisez SKILL.md et les fichiers associés affichés par SkillsMP avant de décider de l'installer.

Explorateur de fichiers
10 fichiers

Affichage de SKILL.md

SKILL.md
Instructions source · Aperçu en lecture seule
name
02-agent-framework
description
Use when building a custom agent with the OpenAI Agents SDK and MLflow ResponsesAgent. Covers Agent class, Runner, @function_tool, handoffs, streaming, ModelConfig, mlflow.models.set_model(), autolog, and manual tracing. Track A Step 2. Produces a customized, MLflow-compatible agent class ready for tools and deployment.
license
Apache-2.0
clients
["ide_cli","genie_code"]
bundle_resource
none
deploy_verb
none
deploy_note
Agent code (OpenAI Agents SDK + MLflow ResponsesAgent) — no deployed resource of its own; deployed later by Track A 07. Authored identically on both clients. On Genie Code write the agent source under the cloned repo root (`{REPO_ROOT}` = `state_file_root` from `skills/vibecoding-state`); see `skills/genie-code-environment`.
coverage
full
metadata
{"last_verified":"2026-06-05","volatility":"high","upstream_sources":[],"author":"prashanth-subrahmanyam","version":"3.1.0","domain":"genai-agents","pipeline_position":"A2","consumes":"mlflow_environment, running_local_agent","produces":"agent_class, responses_agent, customized_agent, lakebase_endpoint_uri, lakebase_cold_start_retry_policy, lakebase_pool_close_policy","grounded_in":"docs.databricks.com/aws/en/generative-ai/agent-framework/author-agent, openai.github.io/openai-agents-python, mlflow.org/docs/latest/genai/serving/responses-agent, github.com/databricks/app-templates/tree/main/agent-openai-agents-sdk"}
fields_read
["agent.system_prompt","agent.capabilities","agent.model"]
# Agent Framework and ResponsesAgent Build your agent with the **OpenAI Agents SDK** and wrap it with **MLflow ResponsesAgent** for Databricks-compatible serving, tracing, and evaluation. ## When to Use - You have a running local agent from Track A Step 1 and need to customize its behavior. - You need to understand the two-layer architecture: **Agent Framework** (OpenAI Agents SDK) on top of **MLflow ResponsesAgent** (serving contract). - You want to add custom instructions, swap models, configure streaming, or set up handoffs. - You need to enable MLflow tracing for observability. **Foundation prerequisite:** This step consumes `mlflow_environment` from [F1](../../../foundation/01-mlflow-genai-foundation/SKILL.md) and uses the experiment/tracing infrastructure from [F2](../../../foundation/02-experiment-tracing-and-uc-storage/SKILL.md). Both must be complete. --- ## Two-Layer Architecture ``` ┌─────────────────────────────────────────────┐ │ OpenAI Agents SDK (Agent Framework) │ │ ├─ Agent class (instructions, model, tools)│ │ ├─ Runner.run / Runner.run_streamed │ │ ├─ @function_tool definitions │ │ └─ Handoffs for multi-agent routing │ ├─────────────────────────────────────────────┤ │ MLflow Host Runtime (Serving Contract) │ │ ├─ Option B (canonical): @invoke / @stream │ │ │ module-level async handlers, hosted │ │ │ on Databricks Apps │ │ └─ Option A (legacy): ResponsesAgent class │ │ hosted on Databricks Model Serving │ └─────────────────────────────────────────────┘ ``` The **OpenAI Agents SDK** handles conversation management, tool orchestration, and multi-agent routing. The **MLflow host runtime** wraps it for serving, tracing, and evaluation. Pick the host runtime by deployment target (next section). --- ## Deployment Target Decides the Wrapping Pattern The Track A agent runs under one of two MLflow wrapping patterns, chosen by **where you plan to deploy**. Pick the deployment target first; the authoring pattern and the downstream AppKit wiring skill follow automatically. | Option | Authoring pattern | Deployment target | AppKit wiring skill | |--------|-------------------|-------------------|---------------------| | **B (canonical)** | Module-level `@mlflow.genai.agent_server.invoke` / `@mlflow.genai.agent_server.stream` async handlers | Databricks **Apps** (as its own App, via `databricks apps deploy`) | [`06d-appkit-agent-app-proxy`](../../../../apps_lakebase/skills/06d-appkit-agent-app-proxy/SKILL.md) | | **A (legacy)** | Class-based `mlflow.pyfunc.ResponsesAgent` wrapping the Agents SDK `Runner` | Databricks **Model Serving** endpoint (via `databricks.agents.deploy()`) | [`06-appkit-serving-wiring`](../../../../apps_lakebase/skills/06-appkit-serving-wiring/SKILL.md) | ### When to pick which - **Pick Option B for any new use case.** Databricks recommends Apps as the default agent host runtime — per the 2026 [author-agent](https://docs.databricks.com/aws/en/generative-ai/agent-framework/author-agent) and [migrate-agent-to-apps](https://docs.databricks.com/aws/en/generative-ai/agent-framework/migrate-agent-to-apps) docs. This is the canonical Track A path used by the [`agent-openai-agents-sdk`](https://github.com/databricks/app-templates/tree/main/agent-openai-agents-sdk), [`agent-openai-agents-sdk-multiagent`](https://github.com/databricks/app-templates/tree/main/agent-openai-agents-sdk-multiagent), and [`agent-openai-advanced`](https://github.com/databricks/app-templates/tree/main/agent-openai-advanced) templates. The canonical SkyLoyalty walkthrough uses Option B. - **Pick Option A only when** you have a hard requirement for the legacy path — e.g. an existing Model Serving endpoint, a multi-agent Genie notebook packaged as `pyfunc`, a Knowledge Assistant wrapper that must register through UC Model Registry with `@champion`/`@production` aliases on the critical deploy path. - **Both options** still benefit from MLflow 3 tracing, `agent-evaluate`, and `mlflow.openai.autolog()`. The difference is the serving contract (`/invocations` on an App URL vs. `/serving-endpoints/:name/invocations`) and the auth model (explicit `x-forwarded-access-token` forwarding vs. the Serving plugin's `.asUser(req)`). --- ## Option B — Module-level `@invoke` / `@stream` (canonical) The Apps host runtime invokes module-level **async** functions decorated with `mlflow.genai.agent_server.invoke` and `mlflow.genai.agent_server.stream`. No class, no `mlflow.models.set_model()`, no `predict` / `predict_stream`. End-user identity flows via `http_request` → `get_user_workspace_client(http_request)`, so every tool call runs **on-behalf-of** the end user when the AppKit proxy forwards `x-forwarded-access-token`. ```python # agent.py — Option B canonical layout (mirrors agent-openai-agents-sdk template) import os import mlflow from agents import Agent, Runner, function_tool from mlflow.genai import agent_server from mlflow.models import ModelConfig from databricks_app.utils import get_user_workspace_client mlflow.openai.autolog() config = ModelConfig(development_config="config.yml") @function_tool def get_current_time() -> str: """Get the current ISO timestamp.""" from datetime import datetime return datetime.now().isoformat() def build_agent(ws) -> Agent: """Build the agent for this request, optionally per-user-scoped via ws.""" return Agent( name="loyalty-assistant", instructions="You are a helpful loyalty program assistant.", model=config.get("llm_endpoint"), tools=[get_current_time], ) @agent_server.invoke async def handle_invoke(request: dict, http_request) -> dict: ws = get_user_workspace_client(http_request) agent = build_agent(ws) result = await Runner.run(agent, request["input"]) return {"output": result.final_output} @agent_server.stream async def handle_stream(request: dict, http_request): ws = get_user_workspace_client(http_request) agent = build_agent(ws) async for event in Runner.run_streamed(agent, request["input"]): if event.type == "raw_response_event": yield event.data ``` ### Trace context: user, session, environment, request id The `@invoke` / `@stream` handler is the canonical call site for attaching trace context — user, session, environment override, and `client_request_id` for end-user feedback correlation. Set them all on the trace root **before** the `Runner.run` call so they're present even if the agent fails partway: ```python import os import mlflow @agent_server.invoke async def handle_invoke(request: dict, http_request) -> dict: ws = get_user_workspace_client(http_request) user_id = ws.current_user.me().user_name session_id = request.get("session_id") or http_request.headers.get("x-session-id", "anon") mlflow.update_current_trace( client_request_id=request.get("client_request_id"), # for /feedback correlation metadata={ "mlflow.trace.user": user_id, "mlflow.trace.session": session_id, "mlflow.source.type": os.getenv("APP_ENVIRONMENT", "development"), "agent_version": os.getenv("AGENT_VERSION", "unknown"), }, ) agent = build_agent(ws) result = await Runner.run(agent, request["input"]) return {"output": result.final_output, "trace_id": mlflow.get_current_active_span().trace_id} ``` The reserved metadata fields (`mlflow.trace.user` / `mlflow.trace.session`) are **immutable** post-log and light up the Trace UI's user / session facets. The `APP_ENVIRONMENT` env var is set in `app.yaml` per deployment. For the full pattern (auto-populated metadata, custom deployment metadata, search-by-metadata examples), see [F2c — Trace context and environments](../../../foundation/02c-trace-context-and-environments/SKILL.md). For end-user feedback correlation via `trace_id` / `client_request_id`, see [04c — End-user feedback](../../../sdlc/04c-end-user-feedback/SKILL.md). ### `app.yaml` for Option B The Apps host serves the module directly — no `start_server.py` is required. Point `command` at the MLflow agent server entrypoint and the agent module: ```yaml # app.yaml command: - mlflow - genai - agent-server - serve - --module - agent env: - name: LLM_ENDPOINT value: databricks-claude-sonnet-4-6 # F2c: app code reads APP_ENVIRONMENT and overrides mlflow.source.type via metadata. - name: APP_ENVIRONMENT value: production ``` For deployment, see [`07-deploy-and-query`](../07-deploy-and-query/SKILL.md) with `target=databricks_apps`. For wiring the AppKit frontend, see [`06d-appkit-agent-app-proxy`](../../../../apps_lakebase/skills/06d-appkit-agent-app-proxy/SKILL.md). ### Runtime model route from Tool Plan Track A agents consume `docs/agent_tool_plan.yaml.runtime_config.llm` through `ModelConfig`, never by hardcoding model names in Python. Minimum `config.yml` shape: ```yaml llm_endpoint: "databricks-claude-sonnet-4-6" llm_api_base_url: null llm_api_mode: "databricks_openai_compatible" ``` Agent construction must read: ```python config = ModelConfig(development_config="config.yml") agent = Agent( name="loyalty-assistant", instructions="...", model=config.get("llm_endpoint"), ) ``` If `llm_api_base_url` is non-null in a future Gateway route, the client factory may pass it to the OpenAI-compatible client. The core workshop does not require that path. ### Multi-agent variant For a triage / handoffs pattern, see the [`agent-openai-agents-sdk-multiagent`](https://github.com/databricks/app-templates/tree/main/agent-openai-agents-sdk-multiagent) template and the Handoffs section below — both apply identically inside `build_agent()` under Option B. ### Advanced variant (Lakebase memory + custom tools) For long-term memory backed by Lakebase, see the [`agent-openai-advanced`](https://github.com/databricks/app-templates/tree/main/agent-openai-advanced) template and [A5: Lakebase Memory](../05-lakebase-memory/SKILL.md). Memory state is loaded inside `handle_invoke` / `handle_stream` per request. ### Lakebase cold-start retry policy and lazy init The Track A agent owns the Lakebase **client policy** contract — the endpoint URI it connects to, the retry behavior on cold-start failures that Lakebase Autoscaling raises during the first request after idle, and the pool-close policy on graceful shutdown. Capture these fields in state and read them at runtime; **do not** hard-code retry counts or error classes in the agent module. Capture in state: ```yaml # state://Track_A.a2_agent_framework lakebase_endpoint_uri: "postgresql://<host>:<port>/<database>" # resolved from databricks_app.utils.get_user_workspace_client at runtime lakebase_cold_start_retry_policy: retry_on: - AdminShutdown # psycopg.errors.AdminShutdown — Lakebase autoscaler closed the prior connection - psycopg_pool.PoolClosed # the pool was closed mid-request and must be re-opened max_attempts: 3 initial_backoff_seconds: 5 # exponential: 5s, 10s, 20s lakebase_pool_close_policy: "close_on_app_shutdown" # allowed values: # - close_on_app_shutdown (default; close pool in `@app.on_event("shutdown")` or equivalent) # - close_on_request_end (only for short-lived dev runs — produces excessive cold starts in prod) # - never (only for diagnostic harnesses; leaks connections) ``` The retry-on classes (`psycopg.errors.AdminShutdown`, `psycopg_pool.PoolClosed`) are the canonical first-request-after-idle failure modes documented in the retrospective. The agent's connection helper MUST catch exactly these classes (not a bare `Exception`) and back off per `initial_backoff_seconds * 2 ** attempt` up to `max_attempts`. `preflight_check_registry.lakebase_cold_start_retry_policy_present` gates downstream prompt roles until `agent.retry_policy.lakebase_cold_start_max_retries >= 1` is recorded in state. #### Lazy init: Lakebase config MUST NOT execute at import time Connecting to Lakebase from module-level code (top of `agent.py`) is a recurring failure: imports happen during `mlflow models log` and during `databricks apps deploy` on machines that have no path to the Lakebase endpoint, blowing up the deploy. **All Lakebase-related setup runs inside `handle_invoke` / `handle_stream` (or a request-scoped factory they call), never at module import.** ```python # agent.py — Lakebase lazy init pattern (Option B) import os import asyncio import mlflow from agents import Agent, Runner from mlflow.genai import agent_server from databricks_app.utils import get_user_workspace_client mlflow.openai.autolog() # DO NOT instantiate the Lakebase pool here — that runs at import time # and breaks `mlflow models log` and `databricks apps deploy`. _LAKEBASE_POOL = None _LAKEBASE_LOCK = asyncio.Lock() async def _get_lakebase_pool(ws): """Build the pool the first time a request needs it; cache for the lifetime of the process.""" global _LAKEBASE_POOL if _LAKEBASE_POOL is not None: return _LAKEBASE_POOL async with _LAKEBASE_LOCK: if _LAKEBASE_POOL is None: from psycopg_pool import AsyncConnectionPool uri = os.environ["LAKEBASE_ENDPOINT_URI"] # from state://lakebase_endpoint_uri _LAKEBASE_POOL = AsyncConnectionPool(uri, open=False) await _LAKEBASE_POOL.open() return _LAKEBASE_POOL async def _with_cold_start_retry(coro_factory):
Voir sur GitHub
Ce SKILL.md est tres volumineux, SkillsMP affiche donc ici seulement la premiere section. Voir sur GitHub