716 lines
27 KiB
Markdown
716 lines
27 KiB
Markdown
# Running omnigent on Databricks
|
|
|
|
A production deployment guide for running omnigent agents on Databricks
|
|
infrastructure. Covers the four canonical integration points:
|
|
|
|
1. **Databricks Apps** as the managed runtime for the omnigent server
|
|
2. **Mosaic AI Foundation Model APIs** as the LLM provider
|
|
3. **Mosaic AI Gateway** as the governance and audit layer over LLM calls
|
|
4. **MLflow Tracing in Unity Catalog** as the long-term trace store
|
|
|
|
omnigent's fine standalone with any OTLP backend and any LLM
|
|
provider. This guide's for the production deployment story where
|
|
governance, audit, cost tracking, and managed scale matter.
|
|
|
|
> **Databricks customer? Start with the managed offering.**
|
|
> [Omnigent on Databricks](https://docs.databricks.com/aws/en/omnigent/)
|
|
> (Beta) is a fully managed service: Databricks operates the omnigent
|
|
> server for you, already wired to workspace identity, Foundation
|
|
> Models, AI Gateway, and MLflow Tracing. You enable the **Omnigent**
|
|
> preview in your workspace settings and follow the quickstart there.
|
|
> No deploy tooling, no Lakebase bootstrap, no bundle to maintain. That
|
|
> is the recommended path for most Databricks users.
|
|
>
|
|
> This guide covers the **self-managed** path: deploying and operating
|
|
> the omnigent server yourself on Databricks Apps. Reach for it when the
|
|
> managed service is not available in your region, or when you need
|
|
> something it does not expose today (custom YAML policies, bring-your-own
|
|
> provider API keys, custom egress controls).
|
|
|
|
## Who this is for
|
|
|
|
This guide assumes you're new to both omnigent and Databricks. Each
|
|
section starts with a short context paragraph, then the concrete
|
|
commands. If you already use both, skim the quick start at the top of
|
|
each section.
|
|
|
|
## What you get
|
|
|
|
When omnigent runs on Databricks with the four integration points
|
|
wired:
|
|
|
|
- **Single trace per agent turn.** Every span the agent emits lands
|
|
in MLflow Tracing in Unity Catalog with the standard OpenTelemetry
|
|
GenAI semantic-convention attributes (`gen_ai.operation.name`,
|
|
`gen_ai.agent.name`, `gen_ai.provider.name`, `gen_ai.request.model`,
|
|
`tool.name`). Searchable, filterable, retained per UC governance.
|
|
- **Per-key LLM cost and audit.** Every LLM call (whether to
|
|
Mosaic AI Foundation Models, OpenAI, Anthropic, or a custom
|
|
endpoint) flows through Mosaic AI Gateway. Per-key cost tracking,
|
|
rate limits, PII guardrails, audit logs, all enforced at the
|
|
gateway. Switching providers or rotating keys is a Gateway config
|
|
change, not an agent change.
|
|
- **Managed runtime with workspace identity.** The omnigent server
|
|
runs on Databricks Apps with the workspace SSO as the user
|
|
identity. No separate auth to set up.
|
|
- **Lakehouse-native state.** Conversation state, agent bundles, and
|
|
executor snapshots persist in Lakebase Postgres and Unity Catalog
|
|
Volumes. Lifecycle managed by the workspace.
|
|
|
|
> **A note on auth tier.** The audit + cost-tracking + guardrails
|
|
> story above assumes API-key tier with the provider. Consumer
|
|
> subscriptions like Anthropic Claude Max, OpenAI ChatGPT Plus, and
|
|
> Cursor Pro use a per-user OAuth flow that the Gateway can't proxy.
|
|
> See [Auth tier compatibility](#auth-tier-compatibility-api-key-vs-subscription--oauth)
|
|
> in the Gateway section for the details and the org-deployment
|
|
> guidance.
|
|
|
|
---
|
|
|
|
## Architecture
|
|
|
|
The integration is layered. The omnigent server runs on Databricks
|
|
Apps. It exports OpenTelemetry traces (via the work landed in [PR
|
|
#1050](https://github.com/omnigent-ai/omnigent/pull/1050)) to MLflow
|
|
Tracing's OTLP receiver. Agent runs make LLM calls through Mosaic AI
|
|
Gateway, which proxies to either Mosaic AI Foundation Models or an
|
|
external provider (OpenAI, Anthropic) configured as an External Model.
|
|
|
|

|
|
|
|
The boundary stays sharp. omnigent itself remains a standalone Apache
|
|
2.0 Python package. Each integration point is an env var or a config
|
|
file, not a fork.
|
|
|
|
---
|
|
|
|
## Prerequisites
|
|
|
|
1. **A Databricks workspace** with the following enabled:
|
|
- Databricks Apps
|
|
- Unity Catalog
|
|
- Mosaic AI Model Serving
|
|
- MLflow Tracing in Unity Catalog (Public Preview as of 2026 H1)
|
|
2. **The [Databricks CLI](https://docs.databricks.com/aws/en/dev-tools/cli/install)**
|
|
installed and authenticated against your workspace. Either a CLI
|
|
profile (`DATABRICKS_CONFIG_PROFILE=<profile>`) or env-based auth
|
|
(`DATABRICKS_HOST` + `DATABRICKS_CLIENT_ID` + `DATABRICKS_CLIENT_SECRET`).
|
|
3. **Python 3.11+** locally with `uv` installed (the omnigent project
|
|
standard).
|
|
4. **Workspace permissions** to:
|
|
- Create or use a Unity Catalog catalog and schema for trace
|
|
storage
|
|
- Create a Databricks App (or use an existing one)
|
|
- Query at least one Mosaic AI Foundation Model serving endpoint
|
|
|
|
Verify your CLI auth before continuing:
|
|
|
|
```bash
|
|
databricks current-user me -p <your-profile>
|
|
```
|
|
|
|
This should print your user object as JSON. If it errors with
|
|
"Invalid access token", run `databricks auth login --profile <your-profile>`
|
|
and try again.
|
|
|
|
---
|
|
|
|
## Quick start (5 minutes)
|
|
|
|
If you want to see the integration working before reading the full
|
|
guide, this is the fastest path. It runs omnigent locally (not on
|
|
Apps) but wires up Foundation Models + Gateway + tracing to the
|
|
workspace.
|
|
|
|
```bash
|
|
pip install 'omnigent[tracing]' openai
|
|
|
|
export DATABRICKS_HOST=https://<your-workspace>.cloud.databricks.com
|
|
export DATABRICKS_TOKEN=<personal-access-token>
|
|
|
|
# Point omnigent at Mosaic AI Foundation Models as the LLM provider
|
|
export OPENAI_BASE_URL=$DATABRICKS_HOST/serving-endpoints
|
|
export OPENAI_API_KEY=$DATABRICKS_TOKEN
|
|
|
|
# Point omnigent's OTel exporter at the MLflow OTLP receiver in UC
|
|
export MLFLOW_TRACKING_URI=databricks
|
|
export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
|
|
export OTEL_EXPORTER_OTLP_ENDPOINT=$DATABRICKS_HOST
|
|
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer $DATABRICKS_TOKEN"
|
|
|
|
# Sanity check the model endpoint works
|
|
python -c "
|
|
from openai import OpenAI
|
|
import os
|
|
c = OpenAI(base_url=os.environ['OPENAI_BASE_URL'], api_key=os.environ['OPENAI_API_KEY'])
|
|
r = c.chat.completions.create(
|
|
model='databricks-claude-sonnet-4',
|
|
messages=[{'role':'user','content':'Reply with exactly: HELLO'}],
|
|
max_tokens=5,
|
|
)
|
|
print(r.choices[0].message.content)
|
|
print(f'input={r.usage.prompt_tokens} output={r.usage.completion_tokens}')
|
|
"
|
|
|
|
# Start the local omnigent server with tracing
|
|
omni server
|
|
```
|
|
|
|
Verified output (run against e2-dogfood workspace):
|
|
|
|
```
|
|
HELLO
|
|
input=15 output=2
|
|
```
|
|
|
|
Open the MLflow Traces UI in your workspace and you should see one
|
|
trace per agent turn with the GenAI semconv attributes set.
|
|
|
|
The rest of this guide walks each piece in depth.
|
|
|
|
---
|
|
|
|
## 1. Deploy on Databricks Apps
|
|
|
|
### Context
|
|
|
|
Databricks Apps is a managed runtime for HTTP applications inside the
|
|
Databricks workspace. omnigent's server is an HTTP app, so it fits
|
|
natively. Apps gives you workspace SSO (the agent's user identity is
|
|
the workspace identity), Lakebase Postgres for state, Unity Catalog
|
|
Volumes for files, and access to all workspace data through the same
|
|
identity.
|
|
|
|
### Quick deploy
|
|
|
|
The `deploy/databricks/` directory in the omnigent repository contains
|
|
a complete Databricks Asset Bundle for deploying the omnigent server
|
|
to Apps backed by Lakebase. Use it as-is for a first deploy:
|
|
|
|
```bash
|
|
git clone https://github.com/omnigent-ai/omnigent
|
|
cd omnigent
|
|
uv sync --extra databricks
|
|
|
|
# Set targets.prod.workspace.host in deploy/databricks/databricks.yml, then run
|
|
# the deploy orchestrator — it builds the wheels and runs `databricks bundle
|
|
# deploy` + `bundle run` for you:
|
|
# uv run python deploy/databricks/deploy.py --app-name omnigent --profile <profile> ...
|
|
# See deploy/databricks/README.md for the full command and required flags.
|
|
```
|
|
|
|
The full deploy walkthrough (one-time Lakebase bootstrap, UC volume
|
|
creation, service principal permissions) lives in
|
|
[`deploy/databricks/README.md`](../deploy/databricks/README.md). Treat
|
|
that as canonical for the deploy details. The rest of this page covers
|
|
the integration knobs you set on top of that deploy.
|
|
|
|
### What you get on Databricks vs DIY
|
|
|
|
Self-hosting the omnigent server (e.g., on a VM or in a container)
|
|
works fine. The Apps path adds:
|
|
|
|
| Capability | Self-hosted | Databricks Apps |
|
|
|---|---|---|
|
|
| Identity | You configure | Workspace SSO automatic |
|
|
| State store | You manage Postgres | Lakebase, managed |
|
|
| File / artifact store | You manage S3/GCS | UC Volumes, governed |
|
|
| Scaling | You manage | App compute, billed per usage |
|
|
| Audit logs | You wire up | UC audit, automatic |
|
|
| Secret management | You manage | Workspace secrets |
|
|
|
|
---
|
|
|
|
## 2. Mosaic AI Foundation Models as LLM provider
|
|
|
|
### Context
|
|
|
|
Mosaic AI Foundation Model APIs expose curated LLMs (Anthropic Claude,
|
|
Meta Llama, OpenAI GPT-OSS, Mistral, and others) behind a single,
|
|
pay-per-token endpoint. The endpoints speak the OpenAI Chat
|
|
Completions API, so any client that targets OpenAI also targets
|
|
Databricks Foundation Models with two env vars.
|
|
|
|
omnigent's harnesses (`claude_sdk`, `openai_agents_sdk`, `pi`, and
|
|
others) talk to LLMs via OpenAI-compatible HTTP. Pointing those
|
|
harnesses at Foundation Models is the same two env vars.
|
|
|
|
### Setup
|
|
|
|
```bash
|
|
export OPENAI_BASE_URL=$DATABRICKS_HOST/serving-endpoints
|
|
export OPENAI_API_KEY=$DATABRICKS_TOKEN
|
|
```
|
|
|
|
Then any omnigent agent spec that points at a `databricks-` model
|
|
(for example `databricks-claude-sonnet-4`, `databricks-meta-llama-3-1-70b-instruct`,
|
|
or `databricks-gpt-oss-20b`) resolves to a Foundation Model call.
|
|
|
|
### Verified call
|
|
|
|
```bash
|
|
python -c "
|
|
from openai import OpenAI
|
|
import os
|
|
c = OpenAI(base_url=os.environ['OPENAI_BASE_URL'], api_key=os.environ['OPENAI_API_KEY'])
|
|
r = c.chat.completions.create(
|
|
model='databricks-claude-sonnet-4',
|
|
messages=[{'role':'user','content':'Reply with exactly two words: GATEWAY OK'}],
|
|
max_tokens=10,
|
|
)
|
|
print(f'Response: {r.choices[0].message.content!r}')
|
|
print(f'Usage: input={r.usage.prompt_tokens} output={r.usage.completion_tokens} total={r.usage.total_tokens}')
|
|
print(f'Model: {r.model}')
|
|
"
|
|
```
|
|
|
|
Output (verified against e2-dogfood):
|
|
|
|
```
|
|
Response: 'GATEWAY OK'
|
|
Usage: input=16 output=6 total=22
|
|
Model: global.anthropic.claude-sonnet-4-20250514-v1:0
|
|
```
|
|
|
|
Note that `model` in the response is the backing model identifier
|
|
(here, Anthropic Claude Sonnet 4 served through Bedrock). Your
|
|
endpoint name (`databricks-claude-sonnet-4`) is the stable handle
|
|
your agent spec uses.
|
|
|
|
### What you get on Databricks vs BYO API key
|
|
|
|
| Capability | BYO provider key | Foundation Models |
|
|
|---|---|---|
|
|
| Billing | Provider bills you directly | Bills as Databricks compute |
|
|
| Per-key cost tracking | Provider dashboard | Workspace cost dashboard |
|
|
| Audit logs | Provider-specific | UC audit, automatic |
|
|
| Network isolation | Provider edge | Workspace network |
|
|
| Model availability | Whatever you provision | Curated list, swap-in via endpoint config |
|
|
|
|
---
|
|
|
|
## 3. Mosaic AI Gateway as the LLM governance layer
|
|
|
|

|
|
|
|
### Context
|
|
|
|
This is the integration that matters most for production. Mosaic AI
|
|
Gateway (configured via the **External Models** feature on Model
|
|
Serving) lets you front any LLM provider (OpenAI, Anthropic,
|
|
Foundation Models, custom endpoints) with a Databricks-managed
|
|
endpoint that adds:
|
|
|
|
- Per-key cost tracking and rate limits
|
|
- PII detection and other guardrails
|
|
- Audit logs for every request and response
|
|
- A stable endpoint URL even when you change providers behind it
|
|
- Optional fallback to a backup provider on failure
|
|
|
|
omnigent doesn't need to know it's calling a Gateway. The Gateway
|
|
endpoint speaks the OpenAI API, so the same `OPENAI_BASE_URL` knob
|
|
that points omnigent at Foundation Models points it at the Gateway.
|
|
|
|
### Create an External Model endpoint
|
|
|
|
This is a one-time setup per provider key. Using the Databricks CLI:
|
|
|
|
```bash
|
|
databricks serving-endpoints create --json '{
|
|
"name": "production-openai-gateway",
|
|
"config": {
|
|
"served_entities": [{
|
|
"name": "openai-production",
|
|
"external_model": {
|
|
"name": "gpt-4o-mini",
|
|
"provider": "openai",
|
|
"task": "llm/v1/chat",
|
|
"openai_config": {
|
|
"openai_api_key": "{{secrets/<scope>/openai-prod-key}}"
|
|
}
|
|
}
|
|
}],
|
|
"traffic_config": {
|
|
"routes": [{"served_entity_name": "openai-production", "traffic_percentage": 100}]
|
|
}
|
|
},
|
|
"ai_gateway": {
|
|
"usage_tracking_config": {"enabled": true},
|
|
"rate_limits": [{"calls": 1000, "key": "user", "renewal_period": "minute"}],
|
|
"guardrails": {"input": {"pii": {"behavior": "BLOCK"}}, "output": {"pii": {"behavior": "BLOCK"}}}
|
|
}
|
|
}' -p <your-profile>
|
|
```
|
|
|
|
The `openai_api_key` value uses a [Databricks workspace
|
|
secret](https://docs.databricks.com/aws/en/security/secrets/) so the
|
|
raw key never lives in the endpoint config.
|
|
|
|
After creation, the endpoint URL is:
|
|
|
|
```
|
|
https://<your-workspace>.cloud.databricks.com/serving-endpoints/production-openai-gateway/invocations
|
|
```
|
|
|
|
The OpenAI-style chat completions URL (what omnigent uses) is:
|
|
|
|
```
|
|
https://<your-workspace>.cloud.databricks.com/serving-endpoints
|
|
```
|
|
|
|
with the endpoint name passed as the `model` parameter on the request.
|
|
|
|
### Point omnigent at the Gateway endpoint
|
|
|
|
For each harness that calls LLMs (claude-sdk, openai-agents-sdk, pi,
|
|
etc.), set:
|
|
|
|
```bash
|
|
export OPENAI_BASE_URL=$DATABRICKS_HOST/serving-endpoints
|
|
export OPENAI_API_KEY=$DATABRICKS_TOKEN
|
|
```
|
|
|
|
Then in the omnigent agent spec, use the Gateway endpoint name as the
|
|
model:
|
|
|
|
```yaml
|
|
# agent.yaml
|
|
name: my-agent
|
|
executor:
|
|
type: openai-agents-sdk
|
|
model: production-openai-gateway # the Gateway endpoint, not "gpt-4o-mini"
|
|
```
|
|
|
|
When the agent runs, every LLM call hits the Gateway. Cost tracking,
|
|
guardrails, audit, and rate limits enforce automatically. Rotating the
|
|
underlying OpenAI key is a Gateway config update with zero agent
|
|
restart.
|
|
|
|
### Verified end-to-end
|
|
|
|
Created a real External Model endpoint on e2-dogfood that proxies
|
|
through `databricks-model-serving` to `databricks-claude-sonnet-4`,
|
|
then called it via the same OpenAI SDK pattern omnigent uses. The
|
|
Gateway config included `usage_tracking_config.enabled=true` and a
|
|
`60 calls/minute/user` rate limit.
|
|
|
|
```python
|
|
from openai import OpenAI
|
|
client = OpenAI(base_url=f'{DATABRICKS_HOST}/serving-endpoints', api_key=DATABRICKS_TOKEN)
|
|
resp = client.chat.completions.create(
|
|
model='omnigent-docs-test-gateway', # the Gateway endpoint, not the backing model
|
|
messages=[{'role':'user','content':'Reply with exactly two words: PROXY OK'}],
|
|
max_tokens=10,
|
|
)
|
|
```
|
|
|
|
Output:
|
|
|
|
```
|
|
Response: 'PROXY OK'
|
|
Usage: input=16 output=6 total=22
|
|
Model: global.anthropic.claude-sonnet-4-20250514-v1:0
|
|
```
|
|
|
|
The `model` in the response is the backing model the Gateway forwarded
|
|
to (Anthropic Claude Sonnet 4 via Bedrock). The Gateway endpoint name
|
|
(`omnigent-docs-test-gateway`) is what omnigent calls. Swapping the
|
|
backing model is a Gateway config update, zero change in the agent.
|
|
|
|
### Auth tier compatibility (API key vs subscription / OAuth)
|
|
|
|
Most coding-agent SDKs in this space support two auth flows. The
|
|
distinction matters for what Gateway can actually intercept.
|
|
|
|
**API key tier.** Per-token billing, a header on every request, a key
|
|
that lives in a workspace secret. The Gateway pattern proxies cleanly:
|
|
the proxied call uses the standard provider endpoint, the workspace
|
|
secret holds the key, every prompt and response flows through the
|
|
workspace.
|
|
|
|
**Subscription / OAuth tier.** Flat-rate consumer subscriptions
|
|
(Anthropic Claude Max, OpenAI ChatGPT Plus, Cursor Pro). The SDK
|
|
authenticates via a browser OAuth flow against the consumer
|
|
infrastructure. The resulting token is per-user, per-device, and
|
|
short-lived. Gateway can't proxy these calls because the workspace
|
|
secret can't hold a per-user OAuth token, and Gateway's outbound auth
|
|
expects an API key, not a refreshable OAuth bearer. Even if the proxy
|
|
were technically feasible, consumer subscriptions are positioned as
|
|
individual products; orgs evaluating them for team use should verify
|
|
the terms of service allow that pattern before relying on it.
|
|
|
|
| Auth tier | Works with Gateway proxy? | Workspace audit? | Central cost tracking? |
|
|
|---|---|---|---|
|
|
| API key (Anthropic API, OpenAI API, etc.) | Yes | Yes — every prompt / response in UC | Yes — Gateway `usage_tracking_config.enabled` |
|
|
| Claude Max / ChatGPT Plus / Cursor Pro (OAuth subscription) | No | omnigent session metadata only; LLM calls invisible | No — billed to the individual's consumer account |
|
|
|
|
**Practical guidance for an org deployment.** Most enterprise
|
|
Anthropic / OpenAI deals are API-tier with a volume agreement and an
|
|
enterprise SLA. The Gateway story assumes that tier. If individual
|
|
developers want to keep their consumer subscriptions for personal
|
|
usage, that's fine, but those sessions sit outside the workspace
|
|
audit and cost tracking.
|
|
|
|
If centralized governance is a hard requirement, the omnigent host
|
|
can enforce API-key-only by setting the provider's API-key env var
|
|
(`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, etc.) before invoking the SDK
|
|
subprocess and refusing to launch the SDK in OAuth mode. With
|
|
`ANTHROPIC_API_KEY` set, the Claude Agent SDK uses the API endpoint
|
|
and the workspace key, not the OAuth flow. Same pattern for the
|
|
OpenAI Agents SDK.
|
|
|
|
If mixed usage is acceptable, document the boundary explicitly:
|
|
production / customer-facing work goes through the workspace
|
|
(API tier + Gateway), individual experimentation can use consumer
|
|
subscriptions but is out of band for compliance.
|
|
|
|
### What you get on Databricks vs raw provider calls
|
|
|
|
| Capability | Raw provider call from omnigent | Through AI Gateway |
|
|
|---|---|---|
|
|
| Cost tracking per agent / user | Build it yourself | Automatic, queryable in UC |
|
|
| Rate limits per key | Provider-level, coarse | Per-key, configurable per minute / hour / day |
|
|
| PII guardrails | Build a separate filter | Built-in, BLOCK or LOG behavior |
|
|
| Audit logs | Provider-specific | UC audit, automatic, full request and response |
|
|
| Provider swap | Code change | Gateway config update |
|
|
| Fallback on provider failure | Build it yourself | Gateway route config |
|
|
| Audit and cost across many keys | Manual reconciliation | One workspace dashboard |
|
|
|
|
---
|
|
|
|
## 4. MLflow Tracing in Unity Catalog
|
|
|
|

|
|
|
|
### Context
|
|
|
|
omnigent emits OpenTelemetry spans for every agent turn, LLM call, and
|
|
tool invocation. The spans follow the OpenTelemetry GenAI semantic
|
|
conventions (`gen_ai.operation.name`, `gen_ai.agent.name`,
|
|
`gen_ai.provider.name`, `gen_ai.request.model`, `tool.name`) shipped
|
|
in [PR #1050](https://github.com/omnigent-ai/omnigent/pull/1050). Any
|
|
OTLP-compatible backend (Jaeger, Tempo, Datadog) can receive them.
|
|
|
|
MLflow Tracing exposes an OTLP/HTTP receiver at the MLflow tracking
|
|
server. On Databricks, that receiver writes traces into a Unity
|
|
Catalog table, governed per workspace UC policies. Operators get a
|
|
search UI, retention policies, lineage, and the standard UC RBAC for
|
|
free.
|
|
|
|
### Setup
|
|
|
|
Three env vars on the omnigent server:
|
|
|
|
```bash
|
|
export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
|
|
export OTEL_EXPORTER_OTLP_ENDPOINT=$DATABRICKS_HOST
|
|
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer $DATABRICKS_TOKEN"
|
|
```
|
|
|
|
omnigent's `telemetry.init()` auto-detects the OTLP endpoint and wires
|
|
up the MLflow OTel exporter. No code changes in the agent.
|
|
|
|
### What the trace looks like
|
|
|
|
For a single agent turn that calls one tool and one LLM:
|
|
|
|
```
|
|
[agent:debby] (root span)
|
|
gen_ai.operation.name = invoke_agent
|
|
gen_ai.agent.name = debby
|
|
gen_ai.provider.name = databricks
|
|
gen_ai.request.model = databricks-claude-sonnet-4
|
|
session.id = conv_e4f5a6b7c8d9e0f1
|
|
task.id = resp_d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3
|
|
|
|
[llm_call] (child span)
|
|
gen_ai.operation.name = chat
|
|
gen_ai.provider.name = databricks
|
|
gen_ai.request.model = databricks-claude-sonnet-4
|
|
gen_ai.usage.input_tokens = 1523
|
|
gen_ai.usage.output_tokens = 847
|
|
|
|
[tool:calculator] (child span)
|
|
gen_ai.operation.name = execute_tool
|
|
tool.name = calculator
|
|
tool.call_id = call_abc123
|
|
```
|
|
|
|
The trace ID is the hex suffix of the response ID, so you can search
|
|
for a specific request in MLflow by stripping the `resp_` prefix.
|
|
|
|
### Verified end-to-end
|
|
|
|
Emitted a synthetic trace from a local Python script (using the same
|
|
`mlflow.start_span` API omnigent's `TracingContext` wraps) against the
|
|
e2-dogfood Databricks workspace's MLflow OTLP receiver. Verified the
|
|
trace landed in UC and the spans carry the expected attributes:
|
|
|
|
```
|
|
Tracking URI: databricks
|
|
Experiment: id=3163592711242134 path=/Users/.../omnigent-databricks-docs-verification
|
|
Trace ID: tr-f13c03f61e44a0442c8865ab2c79e5a4
|
|
Total traces found: 1
|
|
trace_id: tr-f13c03f61e44a0442c8865ab2c79e5a4
|
|
spans: 3
|
|
'agent:debby' attrs: ['gen_ai.agent.name', 'gen_ai.operation.name',
|
|
'gen_ai.provider.name', 'gen_ai.request.model']
|
|
'llm_call' attrs: ['gen_ai.operation.name', 'gen_ai.provider.name',
|
|
'gen_ai.request.model']
|
|
'tool:calculator' attrs: ['gen_ai.operation.name', 'tool.name']
|
|
```
|
|
|
|
The trace is queryable via `mlflow.search_traces()` and shows up in
|
|
the workspace MLflow Traces UI at
|
|
`/ml/experiments/<experiment_id>/traces/<trace_id>` per UC RBAC.
|
|
|
|
Workspace MLflow Traces UI (e2-dogfood) showing the verification trace
|
|
in the experiment table:
|
|
|
|

|
|
|
|
Trace detail view with the `llm_call` and `tool:calculator` child spans
|
|
expanded:
|
|
|
|

|
|
|
|
### Content capture and privacy
|
|
|
|
omnigent does not capture message bodies into traces by default. Set:
|
|
|
|
```bash
|
|
export OMNIGENT_OTEL_CAPTURE_CONTENT=true
|
|
```
|
|
|
|
to include user messages and tool arguments in `mlflow.spanInputs` /
|
|
`mlflow.spanOutputs`. Leave unset for production unless you have
|
|
explicit consent and PII handling in place.
|
|
|
|
### What you get on Databricks vs DIY OTel collector
|
|
|
|
| Capability | DIY OTLP backend | MLflow Tracing in UC |
|
|
|---|---|---|
|
|
| Persistent storage | You provision | Managed, UC-governed |
|
|
| Search UI | You install Jaeger / Tempo / Grafana | MLflow Traces UI in workspace |
|
|
| Retention policy | You configure | Per UC table policy |
|
|
| RBAC | Backend-specific | UC, same as your tables |
|
|
| Cross-trace correlation | Backend-specific | Built into MLflow eval |
|
|
| Cost attribution | Build separately | Aligns with workspace cost reporting |
|
|
|
|
---
|
|
|
|
## Reference: env var summary
|
|
|
|
| Variable | Purpose | Where you set it |
|
|
|---|---|---|
|
|
| `DATABRICKS_HOST` | Workspace URL | Apps env or local shell |
|
|
| `DATABRICKS_TOKEN` | Personal access token or service principal token | Apps env (Workspace Secret) or local shell |
|
|
| `OPENAI_BASE_URL` | LLM provider endpoint, points at Foundation Models or AI Gateway | Apps env or harness spawn-env |
|
|
| `OPENAI_API_KEY` | Auth for the above endpoint | Apps env or harness spawn-env |
|
|
| `MLFLOW_TRACKING_URI` | Set to `databricks` for workspace-hosted MLflow | Apps env |
|
|
| `OTEL_EXPORTER_OTLP_PROTOCOL` | Set to `http/protobuf` for the MLflow OTLP receiver | Apps env |
|
|
| `OTEL_EXPORTER_OTLP_ENDPOINT` | Workspace URL (same as `DATABRICKS_HOST`) | Apps env |
|
|
| `OTEL_EXPORTER_OTLP_HEADERS` | `Authorization=Bearer $DATABRICKS_TOKEN` | Apps env |
|
|
| `OMNIGENT_OTEL_CAPTURE_CONTENT` | `true` to include message bodies in traces | Apps env (default off) |
|
|
|
|
---
|
|
|
|
## Roadmap
|
|
|
|
The four sections above cover the V1 integration. The omnigent +
|
|
Databricks story extends further. Planned follow-ups, each as a
|
|
separate PR:
|
|
|
|
- **Unity Catalog functions as agent tools.** A `databricks-tools`
|
|
optional extra in omnigent that exposes UC functions as
|
|
first-class agent tools with full UC governance and audit.
|
|
- **Mosaic AI Vector Search as agent tool.** A vector search tool
|
|
for RAG agents that uses UC-governed vector indexes.
|
|
- **Mosaic AI Agent Evaluation integration.** Sample production
|
|
traces from omnigent for the managed Mosaic AI Agent Evaluation
|
|
pipeline.
|
|
- **Inference Tables.** Auto-logged Mosaic AI Model Serving requests
|
|
as a second observability path alongside OTel traces.
|
|
- **Lakehouse Monitoring for agent drift.** Long-term drift
|
|
detection on agent trace tables.
|
|
|
|
Track these on the [omnigent issues
|
|
list](https://github.com/omnigent-ai/omnigent/issues) under the
|
|
`databricks` label.
|
|
|
|
---
|
|
|
|
## Troubleshooting
|
|
|
|
### "Credential was not sent" on Foundation Models call
|
|
|
|
The OpenAI SDK needs an explicit `api_key` argument, not just the
|
|
`OPENAI_API_KEY` env var, when used with a custom `base_url`. Pass it
|
|
explicitly:
|
|
|
|
```python
|
|
client = OpenAI(base_url=os.environ['OPENAI_BASE_URL'], api_key=os.environ['DATABRICKS_TOKEN'])
|
|
```
|
|
|
|
### Traces not appearing in MLflow UI
|
|
|
|
1. Confirm `OTEL_EXPORTER_OTLP_ENDPOINT` matches your workspace URL
|
|
exactly (no trailing slash).
|
|
2. Confirm `OTEL_EXPORTER_OTLP_HEADERS` includes the Bearer token.
|
|
3. Check `mlflow tracking get-uri` returns `databricks`.
|
|
4. Run a small synthetic trace and watch for OTLP export errors in
|
|
the omnigent server logs.
|
|
|
|
### Apps deployment fails with "permission denied for table agents"
|
|
|
|
This typically means a shared Lakebase project. Per
|
|
[`deploy/databricks/README.md`](../deploy/databricks/README.md), use a
|
|
fresh Lakebase project per omnigent app rather than sharing one.
|
|
|
|
### Gateway endpoint returns 403 "Invalid access token"
|
|
|
|
The External Model's underlying provider key (stored in workspace
|
|
secrets) has expired or rotated. Update the secret value, then update
|
|
the endpoint config via `databricks serving-endpoints update`.
|
|
|
|
---
|
|
|
|
## Provenance
|
|
|
|
This guide was authored by [Debu Sinha](https://github.com/debu-sinha)
|
|
(Lead Applied AI/ML Engineer, Databricks Solutions Architecture).
|
|
The MLflow Tracing integration section depends on the OTel
|
|
observability series shipped in PRs [#1050](https://github.com/omnigent-ai/omnigent/pull/1050),
|
|
[#1068](https://github.com/omnigent-ai/omnigent/pull/1068),
|
|
[#1070](https://github.com/omnigent-ai/omnigent/pull/1070),
|
|
[#1071](https://github.com/omnigent-ai/omnigent/pull/1071),
|
|
[#1072](https://github.com/omnigent-ai/omnigent/pull/1072), and
|
|
[#1083](https://github.com/omnigent-ai/omnigent/pull/1083).
|
|
|
|
Verified end-to-end against the e2-dogfood Databricks workspace
|
|
(2026-06-24):
|
|
|
|
- Foundation Model call output (`databricks-claude-sonnet-4`,
|
|
18 tokens in / 5 tokens out via CLI and 16 / 6 via OpenAI SDK)
|
|
- OpenAI SDK pattern against `/serving-endpoints` with PAT auth
|
|
- External Model (Gateway) endpoint created, called, and torn down:
|
|
`omnigent-docs-test-gateway` proxied to `databricks-claude-sonnet-4`
|
|
via `provider=databricks-model-serving`, with
|
|
`ai_gateway.usage_tracking_config.enabled=true` and a
|
|
`60 calls/minute/user` rate limit. CLI call returned `PROXY OK`,
|
|
20 tokens. OpenAI SDK call returned `PROXY OK`, 22 tokens, model
|
|
resolved to `global.anthropic.claude-sonnet-4-20250514-v1:0`.
|
|
Endpoint and the supporting workspace secret were deleted after
|
|
verification.
|
|
- MLflow OTLP receiver pattern via a real synthetic-trace round-trip:
|
|
experiment id `3163592711242134`, trace id
|
|
`tr-f13c03f61e44a0442c8865ab2c79e5a4`, 3 spans with the expected
|
|
`gen_ai.*` and `tool.*` attributes, fetched back via
|
|
`mlflow.search_traces()`
|
|
|
|
The Apps deployment section links to `deploy/databricks/README.md`
|
|
which is the canonical, already-merged recipe.
|
|
|
|
Maintenance and updates: open an issue with the `databricks` label, or
|
|
ping @debu-sinha on the omnigent Slack channel.
|