The Select AI Agent API exposes Java wrappers for Oracle Database
DBMS_CLOUD_AI_AGENT operations.
Select AI for Java 1.1.0 was validated for the agent API on Oracle
Autonomous AI Database 26ai and Oracle Autonomous AI Database 19c. These are
the supported database/service environments for this release. Other Oracle AI
Database or Oracle Database releases are not certified for Java 1.1.0, even
when the DBMS_CLOUD_AI_AGENT package is present.
The Oracle Select AI capability matrix defines support for individual agent subprograms, Select AI profile attributes, providers, and built-in tools by database release. The Java SDK does not override those database capabilities.
SelectAIAgent.create(DbConnectionConfig) opens the configured JDBC
connection, while SelectAIAgent.create(DataSource) defers connection access
until an operation runs. Neither factory probes the complete
DBMS_CLOUD_AI_AGENT capability set. Missing package or view objects,
privileges, or an unsupported operation are therefore reported when the
affected operation is invoked, as SelectAIException wrapping the JDBC or
Oracle Database error.
Agent discovery and existing-resource loading also require the documented
USER_AI_AGENTS, USER_AI_AGENT_TASKS, USER_AI_AGENT_TOOLS, and
USER_AI_AGENT_TEAMS views and their corresponding attribute views. The SDK
uses these views internally; applications should use the public Java
interfaces rather than query them directly. The internal package owner is an
implementation detail and is not a customer prerequisite.
SelectAIAgent supports the same connection modes as SelectAI:
SelectAIAgent agentClient = SelectAIAgent.create(dbConnectionConfig);
SelectAIAgent pooledAgentClient = SelectAIAgent.create(dataSource);DbConnectionConfig mode creates one SDK-owned JDBC connection and releases it
when SelectAIAgent.close() is called. DataSource mode borrows a JDBC
connection for each operation and closes it after the operation completes.
Use try-with-resources for deterministic cleanup:
try (SelectAIAgent agentClient = SelectAIAgent.create(dbConnectionConfig)) {
List<Team> teams = agentClient.listTeams();
}Agent framework resources follow the same configured-versus-created pattern as other SDK resources.
Agent agent = agentClient.agent(
"SUPPORT_AGENT",
AgentAttributes.builder()
.profileName("GENAI_PROFILE")
.role("Answer support questions")
.build(),
"Support agent",
AgentStatus.ENABLED);
agent.create();Objects created with only a name represent existing database resources:
Team team = agentClient.team("SUPPORT_TEAM");
TeamRunResult result = team.run(RunTeamRequest.builder()
.userPrompt("Summarize open support issues.")
.build());
System.out.println(result.getRawValue());The name-only team(...) factory and listTeams() load team metadata from
the current user's team view and return mutable, owner-scoped Team handles.
Teams granted by another owner are not returned by listTeams(). To run a
shared team, use the client-level method instead of trying to load a Team
handle:
TeamRunResult result = agentClient.runTeam("TEAM_OWNER.SHARED_SUPPORT_TEAM",
RunTeamRequest.builder()
.userPrompt("Summarize open support issues.")
.build());
System.out.println(result.getRawValue());The owner must grant the consumer run access with
DBMS_CLOUD_AI_AGENT.GRANT_TEAM_ACCESS using the owner's unqualified team
name. The database verifies the consumer's access when runTeam(...) executes.
The grant permits running the team as a unit, not managing it or independently
running its agents, tasks, or tools. The consumer may also need separate
privileges for resources used by the workflow. See the
Oracle team-sharing prerequisites.
For discovery, listAvailableTeams() delegates to
DBMS_CLOUD_AI_AGENT.LIST_TEAMS() and returns read-only AvailableTeam
snapshots. This view can include shared teams available to the caller and is
separate from the owner-scoped lifecycle handles returned by listTeams().
AgentWorkflowSample
is a standalone sample that demonstrates the complete resource flow:
- Create a SQL
Tool. - Create a
Taskthat uses the tool. - Create an
Agentthat uses the tool and the configured Select AI profile. - Create a
Teamcontaining the agent and task. - Create a database conversation and run the team with its conversation ID.
- Continue the conversation by sending another
RunTeamRequestwith the same ID.
The sample removes the team, agent, task, tool, and conversation that it creates.
It attempts every registered cleanup even if another cleanup fails. Execution
and cleanup failures produce a nonzero process exit status. When both fail,
the execution failure remains primary and cleanup failures are retained as
suppressed exceptions.
The profile supplied through SELECTAI_PROFILE_NAME must already exist. See the
class-level JavaDoc on AgentWorkflowSample for the required environment
variables and compile and run commands.
PooledTeamResumeSample
demonstrates an actual WAITING_FOR_HUMAN transition rather than ordinary
conversation continuation. It uses an existing human-input team and Oracle
UCP, included only in the sample and test dependencies:
- Run
startto create a conversation, print its ID, and invoke the team. - The sample verifies that the database reports
WAITING_FOR_HUMAN, then closes its pool and exits. A different state fails the sample. - Preserve the team name and conversation ID. Run
resumein a new process with the same database identity and the human response. - A new pool and client check the waiting state and submit the response with
the original ID. The sample reports
SUCCEEDEDor anotherWAITING_FOR_HUMANpause; other states fail explicitly.
The two processes cannot share a JDBC session. Within each process, each SDK
operation borrows and returns a connection, and no connection is retained
between human interactions. The sample does not retry failed runs or force a
state transition. Keep the conversation for inspection or further input, then
remove it with DropConversationSample once the workflow is finished and no
longer needed. See the sample setup and commands.
The team owner can manage consumer access from Java through the owner-scoped
SelectAIAgent client:
import com.oracle.database.selectai.agent.SelectAIAgent;
try (SelectAIAgent agentClient = SelectAIAgent.create(ownerConfig)) {
agentClient.grantTeamAccess("SHARED_SUPPORT_TEAM", "TEAM_CONSUMER");
// Later, when access is no longer required:
agentClient.revokeTeamAccess("SHARED_SUPPORT_TEAM", "TEAM_CONSUMER");
}grantTeamAccess(...) and revokeTeamAccess(...) pass the owner's unqualified
team name to DBMS_CLOUD_AI_AGENT. The connected user must own the team; a
database administrator is not inherently required. The grantee runs the shared team
with SelectAIAgent.runTeam("OWNER.SHARED_SUPPORT_TEAM", request). Access is
limited to running the team as a unit; it does not grant permission to modify
the team or independently run its agents, tasks, or tools. Revocation blocks
new runs and resumes but does not terminate a process already in progress.
When the owner already has a database-backed Team handle, the equivalent
operations are Team#grantAccess(String) and Team#revokeAccess(String).
DatabaseAdmin remains reserved for setup and administrative operations.
For a new run without established session context, create a database conversation and pass its returned ID. An arbitrary client-generated UUID is not a substitute. To resume a team waiting for human input, retain the ID and supply it with both the initial prompt and the later response. This also works when the calls borrow different pooled connections.
The conversation lifecycle is managed by the core SelectAI API, while team
execution is managed by SelectAIAgent. The Java workflow is:
- Create or load a database conversation through
SelectAIand retain its database-generated ID. - Pass that ID to
RunTeamRequest.conversationId(...)and callTeam#run(RunTeamRequest)orSelectAIAgent#runTeam(String, RunTeamRequest). - Reuse the same ID for a later human response,
getTeamStateByConversationId(...), orsetTeamExecutionState(...)call. - Drop the conversation through the core
ConversationAPI when it is no longer needed.
runTeam(...) returns a TeamRunResult containing the database result; it does
not create or return a conversation ID. For a shared team, the state operations use the
same owner-qualified team name passed to runTeam(...).
Team execution is synchronous. A long-running workflow can retain the
operation connection until the database returns a result. Applications should
size connection pools for expected concurrent workflow count, configure an
appropriate SelectAIOptions query timeout, and retry only when the workflow
is safe to repeat. A JDBC statement timeout does not guarantee cancellation of
work that is already running in the database or at an external model provider.
Assuming selectAI is an open SelectAI client and team is an existing
Team handle:
Conversation conversation = selectAI.conversation(
ConversationAttributes.builder().title("Support request").build());
String conversationId = conversation.create();
team.run(RunTeamRequest.builder()
.userPrompt("I want to return a chair")
.conversationId(conversationId)
.build());
TeamRunResult result = team.run(RunTeamRequest.builder()
.userPrompt("My order number is 1234")
.conversationId(conversationId)
.build());
System.out.println(result.getRawValue());Call conversation.drop(false) when it is no longer needed. If neither a conversation
ID nor session context is present, the database can reject the run with
ORA-20053.
RunTeamRequest exposes the typed RunTeamParams model for the documented
RUN_TEAM runtime parameters. It includes conversationId,
instructionVariables, attributeVariables, and stateVariables. The
convenience methods on RunTeamRequest.Builder, such as
conversationId(...) and stateVariables(...), remain available for the
common case. Do not combine those individual methods with params(...) in the
same request. The prompt is optional, as in
DBMS_CLOUD_AI_AGENT.RUN_TEAM. The same RunTeamRequest can be used with
SelectAIAgent#runTeam(String, RunTeamRequest) across pooled connections.
Builders can be reused for subsequent prompts without changing earlier
requests. Runtime-variable maps are immutable snapshots, including nested
maps and lists; BigDecimal values retain their decimal precision. See
RunTeamParamsSample
for an offline example.
For an owner-scoped team handle, prefer
Team#getStateByConversationId(String) and
Team#setExecutionState(ExecutionStateRequest) with the same
conversation_id. Use ExecutionStateParams.forConversationId(String) when
constructing typed SET_EXECUTION_STATE parameters. Use
ExecutionStateRequest.Builder#params(ExecutionStateParams) when the request
should expose those parameters explicitly. The convenience
ExecutionStateRequest.Builder#conversationId(String) method is equivalent
for the common case. The typed execution-state model currently supports the
required conversation_id parameter explicitly.
For a team granted by another owner, use
SelectAIAgent#getTeamStateByConversationId(String, String) and
SelectAIAgent#setTeamExecutionState(String, ExecutionStateRequest) with the
owner-qualified team name. These top-level methods do not
load owner-only team metadata and are intended for shared-team state
inspection and management.
SelectAIAgent exposes DBMS_CLOUD_AI_AGENT.GET_VARIABLE and
SET_VARIABLE for namespaced runtime variables. The default scope is
ephemeral; passing null to setVariable(...) clears the variable.
try (SelectAIAgent agentClient = SelectAIAgent.create(connectionConfig)) {
agentClient.setVariable("state_variables", "request_id", "REQ-123");
String requestId = agentClient.getVariable("state_variables", "request_id");
System.out.println(requestId);
agentClient.setVariable("state_variables", "request_id", null);
}Overloads accepting an explicit scope are also available. A missing variable
is returned as null.
Runtime variables are associated with the current database session. In
DataSource mode, separate SDK operations may use different pooled
connections, so applications must not depend on a variable remaining visible
to a later operation unless they control session affinity. The database
validates supported namespaces, names, scopes, and values. See
GetVariableSample
and SetVariableSample
for complete runnable examples.
Operations that require an existing database resource reject configured objects
that have not completed create().
For an agent, task, tool, or team pending creation, getStatus() returns its
configured status (or null if none was supplied) without a database query.
For a database-backed resource, each call fetches only current status from the
corresponding USER_AI_AGENTS, USER_AI_AGENT_TASKS, USER_AI_AGENT_TOOLS, or
USER_AI_AGENT_TEAMS view. The description is supplied or loaded when the
handle is initialized and does not change on status refresh. If the resource no
longer exists or the lookup fails, it throws SelectAIException.
The name-only agent(...), task(...), tool(...), and team(...) factories
load existing resources and their attributes before returning. Factories that
take attributes configure a resource in memory; call create() to persist it.
| Resource | Common operations | Additional operations |
|---|---|---|
Agent |
create(), enable(), disable(), drop(boolean), setAttribute(...), setAttributes(AgentAttributes), getAgentName(), getDescription(), getStatus(), getAgentAttributes() |
None |
Task |
create(), enable(), disable(), drop(boolean), setAttribute(...), setAttributes(TaskAttributes), getTaskName(), getDescription(), getStatus(), getTaskAttributes() |
None |
Tool |
create(), enable(), disable(), drop(boolean), setAttribute(...), setAttributes(ToolAttributes), getToolName(), getDescription(), getStatus(), getToolAttributes() |
describe(), run(String) returns ToolRunResult |
Team |
create(), enable(), disable(), drop(boolean), setAttribute(...), setAttributes(TeamAttributes), getTeamName(), getDescription(), getStatus(), getTeamAttributes() |
run(RunTeamRequest) returns TeamRunResult, describe() returns TeamDescription, getStateByConversationId(String), setExecutionState(ExecutionStateRequest), exportDefinition(...) |
TeamDescription exposes the database-reported team name, description, and
optional version through typed accessors. TeamDescription#getSkills() returns
the A2A-compatible skills representation as JSON, preserving fields introduced
by newer database releases. Team.describe() returns null if the database
returns no value. A non-null response must be a single JSON object; malformed
or non-object responses result in SelectAIException rather than empty
metadata. Unknown fields are tolerated, and absent optional fields remain null.
Team state methods return the TeamExecutionState
enum directly. Known states are available as constants; database state values
introduced by a newer release map to TeamExecutionState.UNKNOWN.
SelectAIAgent#getTeamStateByConversationId(String, String) and
SelectAIAgent#setTeamExecutionState(String, ExecutionStateRequest) provide
the corresponding state operations for owner-qualified shared teams that
cannot be opened through SelectAIAgent#team(String).
ToolType is a value object rather than a closed enum. The SDK provides
constants for tool types known by this release, and ToolType.of(String) also
accepts values introduced by newer database releases. When a tool is loaded,
the database value is preserved in ToolAttributes#getToolType() and can be
read directly with getToolTypeValue(). Unknown values remain available for
inspection and JSON round trips instead of preventing the tool from loading.
For a resource pending creation, get*Attributes() returns its configured
attributes without querying the database. For a database-backed resource, it
fetches current attributes on each call. If the attribute view returns no rows,
the getter checks whether the resource still exists; a missing resource raises
SelectAIException, while an existing resource may have empty attributes.
getDescription() returns the value
supplied or loaded when the handle was initialized; getStatus() does not
change it. See the linked interface Javadocs for arguments and failure
behavior. Runnable examples are listed in the
samples guide.
ToolAttributes.Builder.toolParams(ToolParams) and
ToolAttributes.getToolParams() use immutable parameter models instead of
untyped Object values. These configure a built-in tool, not the input supplied
to Tool.run(String).
| Model | Tool type | Typed builder fields |
|---|---|---|
SqlToolParams |
SQL |
profileName |
RagToolParams |
RAG |
profileName |
WebSearchToolParams |
WEBSEARCH |
credentialName |
SlackNotificationToolParams |
NOTIFICATION |
credentialName, channel; sets notification_type to slack |
EmailNotificationToolParams |
NOTIFICATION |
credentialName, recipient, sender, smtpHost, subject; sets notification_type to email |
GenericToolParams |
Any database-defined type | JSON-compatible parameters through parameter(name, value) or fromMap(...) |
ToolAttributes attributes = ToolAttributes.builder()
.toolType(ToolType.SQL)
.toolParams(SqlToolParams.builder()
.profileName("SALES_PROFILE")
.build())
.build();The SDK serializes this as
{"tool_type":"SQL","tool_params":{"profile_name":"SALES_PROFILE"}}.
Pass the attributes to SelectAIAgent.tool(...) before create(), or to an
existing tool's setAttributes(...) method. Required fields and tool-specific
rules remain database-validated; builders permit partial configurations.
Tool-specific builders support additionalParameter(name, value) for fields
introduced by newer database releases. Use the typed setters for known fields.
For existing map-based code, replace toolParams(map) with
toolParams(GenericToolParams.fromMap(map)) or use
ToolParams.fromMap(toolType, map) to select a typed model.
Loading through tool(name), listTools(), or getToolAttributes() selects a
model using tool_type and, for notifications, notification_type. Unknown
fields are retained. Unknown types or incompatible field shapes use
GenericToolParams instead of preventing the tool from loading. Every model
exposes the complete parameter object through toMap(), with immutable nested
maps and lists. Caller-owned values are defensively copied.
See ToolParamsSample
for all parameter models, getters, map conversion, and additional parameters.
It runs without a database connection. For database-backed creation, use
CreateSqlToolSample, CreateRagToolSample, CreateWebSearchToolSample,
CreateSlackNotificationToolSample, or
CreateEmailNotificationToolSample. SetSqlToolParamsSample demonstrates a
typed update, and LoadToolSample reads typed or generic parameters. See the
typed tool sample instructions
for required variables, commands, and cleanup.
ToolAttributes.Builder.toolInputs(List<ToolInput>) and
ToolAttributes.getToolInputs() use immutable input metadata instead of
untyped maps. Each ToolInput has a name and optional description:
ToolInput input = ToolInput.builder()
.name("data_guard")
.description("Only supported values are \"Enabled\" and \"Disabled\".")
.build();
ToolAttributes attributes = ToolAttributes.builder()
.toolInputs(List.of(input))
.build();The serialized attribute is:
{"tool_inputs":[{"name":"data_guard","description":"Only supported values are \"Enabled\" and \"Disabled\"."}]}The description guides the model; it does not create Java-side allowed-value
validation. Input metadata is distinct from the actual values sent to
Tool.run(...). The database validates the input names and their compatibility
with the configured tool. No mandatory, default_value, or allowed-values
fields are modeled here.
The builder copies the list when it is supplied, and the getter returns an
immutable list. null omits the attribute, while an empty list sends an empty
JSON array. Null list entries are rejected locally. Loading a tool through
tool(name), listTools(), or getToolAttributes() maps the database array
into the same typed entries.
See ToolInputSample for an offline example, CreateToolWithInputsSample for custom-tool creation, SetToolInputsSample for a persistent update, and LoadToolSample for reading input metadata. See the sample instructions for required variables, Bash/PowerShell lifecycle commands, cleanup, and the integration runner's optional typed-input workflow. The custom function must already exist; the samples do not create, execute, or drop it.
An existing database-backed Agent, Task, Tool, or Team can update one
attribute with a typed setAttribute(...) overload or update multiple
attributes with its corresponding setAttributes(...) method. These methods
invoke DBMS_CLOUD_AI_AGENT.SET_ATTRIBUTE and SET_ATTRIBUTES using JDBC
bind variables and the appropriate object type. The database validates
supported attribute names, values, and database-version-specific restrictions.
Attribute updates require a created or loaded resource. A
creation-configured resource must be created first; its initial attributes are
supplied through the corresponding SelectAIAgent creation factory. After a
successful update, the corresponding get*Attributes() method reads the
current values from the database. Database errors are reported as
SelectAIException.
Runnable examples:
SetAgentAttributeSampledemonstratesAgent#setAttribute(String, String).SetAgentAttributesSampledemonstratesAgent#setAttributes(AgentAttributes).
The same typed update operations are available on Task, Tool, and Team
through their corresponding setAttribute(...) overloads and
setAttributes(TaskAttributes), setAttributes(ToolAttributes), or
setAttributes(TeamAttributes). Each operation supplies the resource name
and object type to DBMS_CLOUD_AI_AGENT, uses JDBC bind variables, and reloads
the typed attributes from the database after success. Runnable examples are
available as SetTaskAttributeSample, SetTaskAttributesSample,
SetToolAttributeSample, SetToolAttributesSample, SetTeamAttributeSample,
and SetTeamAttributesSample.
ImportTeamRequest accepts native JSON-compatible Java maps for team
specifications. ExportTeamRequest uses the typed ExportTeamParams model;
the documented export option is overwrite:
import com.oracle.database.selectai.model.agent.ExportTeamRequest;
import com.oracle.database.selectai.model.agent.ExportTeamParams;
import com.oracle.database.selectai.model.agent.ImportTeamRequest;
import java.util.List;
import java.util.Map;
ImportTeamRequest importRequest = ImportTeamRequest.builder(
"GENAI_PROFILE", "SUPPORT_TEAM")
.specification(Map.of("agents", List.of()))
.force(true)
.build();
ExportTeamRequest exportRequest = ExportTeamRequest.builder(
"https://objectstorage.example/team.json")
.params(ExportTeamParams.builder().overwrite(true).build())
.build();To receive the exported team definition directly as a JSON string, use
Team.exportDefinition() for database defaults or
Team.exportDefinition(ExportTeamParams) for the typed overwrite option.
The ExportTeamRequest overload writes the definition to Object Storage or a
database directory instead of returning it to the application.
The database validates the team definition, destination, referenced resources, and caller privileges.
TeamAttributes follows the CREATE_TEAM attributes documented by
DBMS_CLOUD_AI_AGENT:
| Java property | JSON attribute | Meaning |
|---|---|---|
agents |
agents |
Required agent-to-task mappings. |
process |
process |
Team task execution process, such as sequential. |
longTermMemoryLength |
long_term_memory_length |
Maximum number of prior task summaries retained as memory. A null value uses the database default of 30. |
supervisorAgent |
supervisor_agent |
Name of an agent created with AgentAttributes.Builder.supervisor(true) and assigned to supervise this team. |
Each agent mapping contains an agent name and its task name. There are no
top-level tasks or version attributes in the CREATE_TEAM payload.
TeamAttributes attributes = TeamAttributes.builder()
.agents(List.of(TeamAttributes.TeamMember.builder("SUPPORT_AGENT")
.task("SUPPORT_TASK")
.build()))
.process("sequential")
.build();The agent and team settings work together: AgentAttributes.Builder.supervisor(true)
designates an agent as a supervisor at creation time, while
TeamAttributes.Builder.supervisorAgent(name) assigns that agent to a team.
The supervisor flag cannot be changed after agent creation. The team's agents
list contains worker agent-task pairs only; do not include the supervisor there.
Use process("sequential") so the supervisor delegates one worker subtask at a
time. Keep worker-specific instructions on the worker tasks.
See SupervisorTeamSample for a complete example that creates a SQL tool, revenue and margin workers, their tasks, a supervisor, a team, and a conversation. It runs a synthetic analysis request and cleans up the resources it creates. An existing profile and a database supporting supervisor-led teams are required. See the sample run instructions.
The Agent API follows the SDK logging policy:
DEBUGlogs operation starts and diagnostics.INFOlogs successful state-changing operations.ERRORlogs failed database operations with resource identifiers.
Agent operation messages do not explicitly include prompt text, tool input
JSON, generated output, full attribute payloads, credentials, passwords,
private keys, or connection secret material. However, current ERROR logs
include the original JDBC exception. Its message, causes, and suppressed
exceptions can contain customer data or secrets echoed by the database or
driver; these logs are not automatically redacted. The original cause is also
preserved in SelectAIException, so application-level exception logging needs
the same review. Sample programs may print result payloads to standard output for
test evidence; production applications should apply their own redaction,
retention, and access-control policy.
