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.

Ir para a instalação

Informações da origem

Repositório
databricks-solutions/vibe-coding-workshop-template
Última atividade na origem
6 de junho de 2026 às 14:09
Idioma detectado do SKILL.md
inglês
Estrelas
6
Forks
7

Opções de instalação

Por padrão, está selecionado o prompt que primeiro revisa a origem. Você pode mudar para um comando direto ou baixar uma cópia local.

Revise os arquivos de origem

Leia o SKILL.md e os arquivos complementares exibidos pelo SkillsMP antes de decidir se vai instalar.

Explorador de arquivos
10 arquivos

Exibindo SKILL.md

SKILL.md
Instruções da origem · Visualização somente leitura
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):
Ver no GitHub
Este SKILL.md e muito grande, entao o SkillsMP mostra aqui apenas a primeira secao. Ver no GitHub