Genie Agent Mode learning path
Create, grant, invoke, govern, test, and clean up a Databricks Genie Agent used through the Beta Agent Mode API.
Harness supports the complete application-side journey around Genie Agent Mode, but two native Databricks concepts must stay distinct:
- A Genie Agent is the Databricks resource. Harness can create, inspect, query, update,
permission, export/import, and trash that resource through
DatabricksGenieAdmin. - Agent Mode is a Beta response-streaming API for an existing Genie Agent. Harness does not create a second “Agent Mode agent” resource.
The ordinary Genie Conversation API and the Agent Mode SSE API are separate clients. Harness never silently substitutes one for the other.
Choose the path
| Situation | Use |
|---|---|
| A Genie Agent already exists | Configure genie.agentMode.agentId, acknowledge Beta, and invoke it. |
| Harness should manage the resource | Configure genie.manage, create through genieAdmin, grant the runtime principal, then bind the returned agentId to Agent Mode. |
| A Harness model should delegate analytics to Genie | Set modelTool, register dbx.tools, and retain normal Harness policy and budgets around the call. |
| The workload runs as a Databricks App | Declare databricks.app.genie; the build emits a native genie_space resource and injects the ID through valueFrom. |
Prerequisites
You need:
@fabric-harness/databricks@^7.0.2,@fabric-harness/sdk@^6.1.0, and Node.js 22 or newer;- a Databricks workspace enrolled by the account team for the Agent Mode preview;
- Genie Agents Agent Mode API enabled under workspace Previews;
- a pro or serverless SQL Warehouse;
SELECTon every Unity Catalog table used by the Genie Agent;- an authoring principal with workspace-folder and Genie management permission when Harness manages the resource; and
- a runtime principal with
CAN_RUNon the Genie Agent plus access to its warehouse and data.
Use separate authoring and runtime principals in production. The same service principal is acceptable only for a controlled development proof where that broader authority is intentional.
Path 1: invoke an existing Genie Agent
import {
type DatabricksGenieAgentModeResponse,
databricks,
} from '@fabric-harness/databricks';
import { defineAgent, schema } from '@fabric-harness/sdk';
const host = process.env.DATABRICKS_HOST!;
const revenueIntelligence = databricks({
host,
principal: {
kind: 'service-principal',
host,
clientId: process.env.DATABRICKS_RUNNER_CLIENT_ID!,
clientSecret: process.env.DATABRICKS_RUNNER_CLIENT_SECRET!,
},
genie: {
agentMode: {
agentId: process.env.DATABRICKS_REVENUE_GENIE_AGENT_ID!,
acknowledgeBeta: true,
timeoutMs: 5 * 60_000,
idleTimeoutMs: 2 * 60_000,
onEvent: (event) => console.log(event.sequenceNumber, event.type),
},
},
});
export default defineAgent({
name: 'revenue-review',
input: schema.object({
question: schema.string(),
conversationId: schema.string().optional(),
}),
output: schema.string(),
run: async ({ input }) => {
const revenueReview = await revenueIntelligence.genieAgentMode!.complete(input.question, {
...(input.conversationId ? { conversationId: input.conversationId } : {}),
});
return JSON.stringify(revenueReview satisfies DatabricksGenieAgentModeResponse);
},
});Run it:
export DATABRICKS_HOST=https://<workspace-host>
export DATABRICKS_RUNNER_CLIENT_ID=...
export DATABRICKS_RUNNER_CLIENT_SECRET=...
export DATABRICKS_REVENUE_GENIE_AGENT_ID=0123456789abcdef0123456789abcdef
fh run revenue-review \
--payload '{"question":"Summarize revenue by region."}'Expected output includes increasing SSE sequence numbers, a response.completed event, and one
bounded terminal response containing a conversationId. Pass that ID on the next invocation to
continue the same native conversation. Agent Mode allows one in-flight response per conversation.
What developers see
The application can present the native conversation while preserving the verified caller, ordered SSE sequence, generated SQL step, result evidence, terminal event, and bounded timeout. This representative view is sanitized and does not embed a workspace or user identifier.

Path 2: manage, grant, invoke, and clean up
The runnable with-databricks-genie-authoring workspace contains this lifecycle. The important
handoff is salesAnalyst.agentId: the exact resource returned by management is the resource Agent
Mode invokes.
import {
LakebaseDatabricksManagedResourceStore,
databricks,
databricksSdk,
lakebaseClient,
} from '@fabric-harness/databricks';
const host = process.env.DATABRICKS_HOST!;
const authorClientId = process.env.DATABRICKS_AUTHOR_CLIENT_ID!;
const genieAuthoringPrincipal = {
kind: 'service-principal' as const,
host,
clientId: authorClientId,
clientSecret: process.env.DATABRICKS_AUTHOR_CLIENT_SECRET!,
};
const genieRunnerPrincipal = {
kind: 'service-principal' as const,
host,
clientId: process.env.DATABRICKS_RUNNER_CLIENT_ID!,
clientSecret: process.env.DATABRICKS_RUNNER_CLIENT_SECRET!,
};
const genieAuthoringSdk = databricksSdk({ host, principal: genieAuthoringPrincipal });
const genieOwnershipStore = new LakebaseDatabricksManagedResourceStore({
client: lakebaseClient({
host: process.env.DATABRICKS_LAKEBASE_HOST!,
database: process.env.DATABRICKS_LAKEBASE_DATABASE!,
user: process.env.DATABRICKS_LAKEBASE_USER!,
endpoint: process.env.DATABRICKS_LAKEBASE_ENDPOINT!,
credentialClient: genieAuthoringSdk.postgres,
}),
});
const genieManagement = databricks({
host,
principal: genieAuthoringPrincipal,
genie: {
manage: {
resourceStore: genieOwnershipStore,
delete: 'managed-only',
},
},
});
const salesAnalyst = await genieManagement.genieAdmin!.create(
{
version: 2,
title: 'Revenue intelligence analyst',
parentPath: '/Shared/agents',
warehouseId: process.env.DATABRICKS_WAREHOUSE_ID!,
dataSources: [{ table: 'main.sales.orders' }],
instructions: ['Answer revenue questions using governed data sources.'],
},
{
ownership: {
principal: process.env.DATABRICKS_AUTHOR_CLIENT_ID!,
toolCallId: 'revenue-intelligence-bootstrap',
},
},
);
try {
await genieManagement.genieAdmin!.updatePermissions(salesAnalyst.agentId, [
{
servicePrincipalName: process.env.DATABRICKS_RUNNER_CLIENT_ID!,
permissionLevel: 'CAN_RUN',
},
]);
const revenueIntelligence = databricks({
host,
principal: genieRunnerPrincipal,
genie: {
agentMode: {
agentId: salesAnalyst.agentId,
acknowledgeBeta: true,
timeoutMs: 5 * 60_000,
idleTimeoutMs: 2 * 60_000,
},
},
});
const revenueReview = await revenueIntelligence.genieAgentMode!.complete(
'Summarize revenue by region.',
);
console.log(revenueReview.output);
} finally {
await genieManagement.genieAdmin!.delete(salesAnalyst.agentId);
}Production resource management should use a durable DatabricksManagedResourceStore; the provided
Lakebase implementation survives process replacement. Model-facing management additionally
requires a steward audience, catalog allowlist, exact-operation approval, and a server-side
sqlPolicy before SQL-bearing configuration becomes available.
Path 3: expose Agent Mode as a governed model tool
modelTool constructs databricks_genie_agent_mode, but the Harness definition must register
revenueIntelligence.tools:
import { databricks } from '@fabric-harness/databricks';
import { defineAgent, schema } from '@fabric-harness/sdk';
const host = process.env.DATABRICKS_HOST!;
const genieRunnerPrincipal = {
kind: 'service-principal' as const,
host,
clientId: process.env.DATABRICKS_RUNNER_CLIENT_ID!,
clientSecret: process.env.DATABRICKS_RUNNER_CLIENT_SECRET!,
};
const revenueIntelligence = databricks({
host,
principal: genieRunnerPrincipal,
model: 'system.ai.gpt-oss-20b',
genie: {
agentMode: {
agentId: process.env.DATABRICKS_REVENUE_GENIE_AGENT_ID!,
acknowledgeBeta: true,
modelTool: { maxOutputBytes: 512 * 1024 },
},
},
});
export default defineAgent({
name: 'revenue-briefing',
input: schema.object({ question: schema.string() }),
output: schema.string(),
init: {
modelProvider: revenueIntelligence.modelProvider,
tools: revenueIntelligence.tools,
policy: revenueIntelligence.policy,
},
run: ({ input, prompt }) =>
prompt(`Use the governed Genie tool to answer this revenue question: ${input.question}`),
});Progress events go only to the server-side onEvent callback. The model receives one bounded
terminal response, not the entire SSE stream.
Bind the resource to a Databricks App
export default {
target: 'databricks-app',
databricks: {
app: {
genie: {
agentId: process.env.DATABRICKS_REVENUE_GENIE_AGENT_ID,
permission: 'CAN_RUN',
},
},
},
};The build writes a native genie_space resource and injects DATABRICKS_GENIE_AGENT_ID through
valueFrom; the ID is not embedded in the model prompt. CAN_EDIT and CAN_MANAGE require the App
binding to opt into authoring.
Failure behavior
| Failure | Harness behavior | Next action |
|---|---|---|
404 FEATURE_DISABLED | Fails explicitly; ordinary Genie is not substituted. | Ask the account team to enroll the workspace and enable the preview. |
403 | Surfaces native permission denial without exposing credentials. | Grant the runtime principal and verify warehouse/data access. |
409 | Reports the one-in-flight-response conflict. | Wait for or cancel the active response; do not retry concurrently. |
| Idle or overall deadline | Aborts the request and raises DatabricksGenieAgentModeTimeoutError. | Inspect progress evidence and choose a bounded workload-specific deadline. |
| Caller cancellation | Propagates the abort signal to the response stream. | Record the cancelled terminal state; do not convert it to a retry. |
| Malformed or out-of-order SSE | Raises DatabricksGenieAgentModeProtocolError. | Retain redacted evidence and check the Beta API rollout/version. |
response.failed | Yields the terminal event, then raises DatabricksGenieAgentModeResponseError. | Inspect the bounded native error and correct the question, data, or agent configuration. |
| Stream ends without a terminal event | Fails the protocol contract. | Treat the outcome as unknown; do not claim completion. |
Response-creating POSTs are never automatically retried because an ambiguous response could duplicate work. Event size, event count, model-tool output, idle time, and total time are bounded.
Cleanup
- A managed test lifecycle must trash the exact Genie Agent in a
finallyblock. - Harness waits until exact-ID reads show the resource is no longer active before removing its durable ownership record.
- Repeating deletion after the resource moved to trash is an idempotent no-op.
- Do not delete a pre-existing production Genie Agent merely because one Agent Mode conversation finished; retain or delete it according to its owner and lifecycle policy.
Test and certification boundary
Run the local protocol and composition tests without workspace credentials:
pnpm --filter @fabric-harness/databricks test -- genie-agent-mode
pnpm --filter @fabric-harness/example-with-databricks-genie-authoring test
pnpm --filter @fabric-harness/example-with-databricks-genie-authoring buildThe current 7.0.2 protected record establishes stable Genie Agent create/query/update/trash
authoring and separately records a passing optional Tier O Agent Mode streaming probe in Azure
eastus2. It does not promote the Beta API to stable, establish Agent Mode authoring as a separate
resource lifecycle, prove the 14-day rolling claim, or certify AWS/GCP workspaces. Read the exact
certification record before making a customer claim.
Databricks resource management
Author governed Genie Agents, Jobs, Lakeflow pipelines, AI Search, Model Serving, Unity Catalog, workspace, and secret resources from an agent, with approvals, compute policy, and managed-only deletion.
Authoring certification and release evidence
The release gate, destructive protected-workspace lifecycle checks, sweeper, and retained evidence behind the stability claims for Databricks resource management.