<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Oracle Developers</title>
    <description>The latest articles on DEV Community by Oracle Developers (@oracledevs).</description>
    <link>https://dev.to/oracledevs</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F11587%2F7c934ee0-6aa6-42f9-b43f-91e6fa82ef41.png</url>
      <title>DEV Community: Oracle Developers</title>
      <link>https://dev.to/oracledevs</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/oracledevs"/>
    <language>en</language>
    <item>
      <title>Agent‑safe change delivery on Oracle: discovery, online mechanics, idempotent migrations, and provable rollbacks</title>
      <dc:creator>Mark Nelson</dc:creator>
      <pubDate>Thu, 28 May 2026 15:04:13 +0000</pubDate>
      <link>https://dev.to/oracledevs/agent-safe-change-delivery-on-oracle-discovery-online-mechanics-idempotent-migrations-and-4hof</link>
      <guid>https://dev.to/oracledevs/agent-safe-change-delivery-on-oracle-discovery-online-mechanics-idempotent-migrations-and-4hof</guid>
      <description>&lt;p&gt;This is article 7 of 8 in my Oracle AI Database Skills series.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The safety net is the process, not the model. Discovery before proposal, preview before apply, session-scoped verification before broad exposure, and a prepared rollback path — these steps protect against risk regardless of how confident the assistant sounds.&lt;/li&gt;
&lt;li&gt;Invisible indexes let you test the impact of a change in one session before committing it to all queries. A change that can be reversed with a single statement is the safest kind to ship.&lt;/li&gt;
&lt;li&gt;Liquibase updateSQL and Flyway dry-run output play the same role as SHOWSQL for migrations: they show exactly what SQL would execute before anything changes, giving a human an explicit approval moment.&lt;/li&gt;
&lt;li&gt;DBMS_REDEFINITION and Edition-Based Redefinition reduce blast radius for structural and code changes by keeping the previous version available as a rollback window through the entire cutover period.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
   
&lt;/h2&gt;

&lt;p&gt; &lt;/p&gt;

&lt;p&gt;Change is where ambiguity turns into outages. If you let an assistant propose schema or index changes on Oracle Database, slow the work down just enough to make it safe. The pattern across this series is route → act → trust: route proposals through Oracle Database Skills so the assistant performs discovery and establishes intent; act only through a bounded, inspectable MCP surface (SQLcl MCP, where MCP is Model Context Protocol—locally, or a managed MCP in Autonomous AI Database when available); and deliver changes with Oracle‑native online and editioning mechanics, inside a migration workflow that’s idempotent on rerun and leaves a trail you can prove later.&lt;/p&gt;

&lt;p&gt;This piece shows that posture in motion. We’ll take one additive change an assistant proposes, preview it, apply it safely, verify it helped, and capture the evidence—with a rollback ready if it didn’t.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fydk8kl0tn0l66cinmmpj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fydk8kl0tn0l66cinmmpj.png" alt="Agent-Safe Control Path" width="800" height="221"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Version scope and expectations
&lt;/h2&gt;

&lt;p&gt;Most examples target Oracle Database 19c and later. We rely on invisible indexes and session testing via the &lt;code&gt;OPTIMIZER_USE_INVISIBLE_INDEXES&lt;/code&gt; parameter (settable at the session or system level) as documented in the 19c Database Reference. For index maintenance, we use online index rebuilds where the 19c SQL Language Reference documents them for your index type and release (&lt;code&gt;ALTER INDEX ... REBUILD ONLINE&lt;/code&gt;). For structural table changes under DML load, we point to &lt;code&gt;DBMS_REDEFINITION&lt;/code&gt; (19c PL/SQL Reference). For view/package evolution, we use Edition‑Based Redefinition (EBR). Migration previews use Liquibase or Flyway state tables and dry‑run modes. Evidence and rollback rely on session tagging, SQLcl MCP logging, and (optionally) Unified Auditing.&lt;/p&gt;

&lt;p&gt;If you’re on Oracle Database 26ai or Autonomous AI Database, you can also evolve vector indexes with a build‑new → validate → drop‑old pattern and measure recall/latency with &lt;code&gt;DBMS_VECTOR&lt;/code&gt;. Always confirm exact syntax and capabilities for your Release Update (RU) and service tier.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ALTER INDEX (ONLINE for REBUILD): 19c SQL Language Reference&lt;/li&gt;
&lt;li&gt;OPTIMIZER_USE_INVISIBLE_INDEXES: 19c Database Reference&lt;/li&gt;
&lt;li&gt;Managing invisible indexes: 19c Admin Guide&lt;/li&gt;
&lt;li&gt;DBMS_REDEFINITION: 19c PL/SQL Reference&lt;/li&gt;
&lt;li&gt;CREATE VECTOR INDEX, VECTOR_DISTANCE, DBMS_VECTOR: 26ai docs&lt;/li&gt;
&lt;li&gt;SQLcl MCP restrict levels, tools, monitoring: 25.x SQLcl docs&lt;/li&gt;
&lt;li&gt;Liquibase and Flyway: vendor docs&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why agent‑safe change matters most
&lt;/h2&gt;

&lt;p&gt;Assistants are good at pattern matching; they are bad at implicit context. Database change has a lot of implicit context: constraints you can’t see from one table, write paths you forgot exist, and optimizer behavior that swings when you add the “perfect” index. The way to make an assistant useful here is to force it to surface what it thinks it knows before it acts. That’s what Oracle Database Skills are for: schema discovery, intent disambiguation, destructive‑operation guards, and idempotency patterns live right next to delivery mechanics—migrations, online operations, EBR, and testing—so the work becomes a sequence you can reason about, not a free‑form prompt.&lt;/p&gt;

&lt;p&gt;The assistant can still draft the change. Your job becomes review and control: you see the SQL (Article 5’s &lt;code&gt;SHOWSQL&lt;/code&gt; discipline), you preview the migration (dry run), you apply through a constrained channel (MCP), and you verify impact before broad exposure (invisible index testing, or EBR for code). When that loop is the norm, teams stop arguing about whether to “let the model write DDL” and start shipping low‑risk improvements steadily.&lt;/p&gt;

&lt;h2&gt;
  
  
  Safety before execution: discovery, intent, and rerunnable SQL
&lt;/h2&gt;

&lt;p&gt;Change work starts with a map. If the assistant suggests an index for &lt;code&gt;DOCUMENTS&lt;/code&gt;, its first step isn’t to create it; it is to list what already exists and establish the blast radius.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc65jdwm053g8t7p4jaor.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc65jdwm053g8t7p4jaor.png" alt="Safety Gates Before Execution" width="800" height="624"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Inventory what’s there (use &lt;code&gt;USER_*&lt;/code&gt; views in‑schema; &lt;code&gt;ALL_*&lt;/code&gt;/&lt;code&gt;DBA_*&lt;/code&gt; if querying across schemas):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;index_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;visibility&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="n"&gt;user_indexes&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;  &lt;span class="k"&gt;table_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'DOCUMENTS'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also list indexed columns so you don’t reinvent an existing index:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;index_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;column_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;column_position&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="n"&gt;user_ind_columns&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;  &lt;span class="k"&gt;table_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'DOCUMENTS'&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt;  &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;index_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;column_position&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the assistant claims “add an index to speed source and published date filters,” make it specify the target predicates and expected plan effect. If it can’t state the &lt;code&gt;WHERE&lt;/code&gt; clause and the rows accessed in plain terms, it doesn’t understand the workload yet. Article_06 showed why this matters: you don’t expose a new index broadly until you’ve seen it influence a plan the way you expect.&lt;/p&gt;

&lt;p&gt;For DML, destructive‑operation guards are non‑negotiable. Updates and deletes happen only with a &lt;code&gt;WHERE&lt;/code&gt; clause you can count. Preview the scope:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="n"&gt;documents&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;  &lt;span class="k"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'kb'&lt;/span&gt;
&lt;span class="k"&gt;AND&lt;/span&gt;    &lt;span class="n"&gt;published&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nb"&gt;DATE&lt;/span&gt; &lt;span class="s1"&gt;'2025-01-01'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Oracle doesn’t enforce &lt;code&gt;WHERE&lt;/code&gt; clauses on UPDATE/DELETE; enforce guardrails in tooling (preview gates) and code review.&lt;/p&gt;

&lt;p&gt;Reruns should be safe by default. On the data side, &lt;code&gt;MERGE&lt;/code&gt; turns brittle “insert then update” sequences into idempotent steps:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;MERGE&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;
&lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="s1"&gt;'max_connections'&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="k"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'100'&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;dual&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;MATCHED&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;
&lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="n"&gt;MATCHED&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the DDL side, migration tools carry the idempotency for you with state tables and checksums. Liquibase tracks applied changeSets in &lt;code&gt;DATABASECHANGELOG&lt;/code&gt; and coordinates with &lt;code&gt;DATABASECHANGELOGLOCK&lt;/code&gt;, and Flyway uses &lt;code&gt;flyway_schema_history&lt;/code&gt;. Crucially, both can emit exactly what they would execute. Liquibase’s &lt;code&gt;updateSQL&lt;/code&gt; and Flyway’s &lt;code&gt;dryRunOutput&lt;/code&gt; are the human gate that pairs well with Article 5’s &lt;code&gt;SHOWSQL&lt;/code&gt;: you inspect the proposed SQL first, then approve the apply.&lt;/p&gt;

&lt;p&gt;Preview‑gate example (Liquibase):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Print the SQL that would run, without changing the database&lt;/span&gt;
liquibase &lt;span class="nt"&gt;--changelog-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;changelog.xml updateSQL

&lt;span class="c"&gt;# Example output (truncated)&lt;/span&gt;
&lt;span class="nt"&gt;--&lt;/span&gt; Changeset 20250108-idx-docs:1
CREATE INDEX docs_src_pub_idx ON documents&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;source&lt;/span&gt;, published&lt;span class="o"&gt;)&lt;/span&gt; INVISIBLE&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nt"&gt;--&lt;/span&gt; Changeset 20250108-config-merge:2
MERGE INTO config ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Liquibase prints the SQL it would execute against the current database state, based on &lt;code&gt;DATABASECHANGELOG&lt;/code&gt; and checksums.&lt;/p&gt;

&lt;p&gt;Alternatively, with Flyway (Teams/Enterprise for dry‑run variants):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Generate a dry-run script of pending migrations&lt;/span&gt;
flyway &lt;span class="nt"&gt;-locations&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;filesystem:sql &lt;span class="nt"&gt;-dryRunOutput&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;preview.sql migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;dryRunOutput&lt;/code&gt; is not available in Community; use Teams/Enterprise for dry‑run script generation.&lt;/p&gt;

&lt;h2&gt;
  
  
  From preview to apply: bounded MCP, not ad‑hoc credentials
&lt;/h2&gt;

&lt;p&gt;Once you’ve reviewed a change, you still need a controlled way to act. That’s the MCP layer: the SQLcl MCP Server locally, or a managed MCP experience in Autonomous AI Database if your tier and RU expose one.&lt;/p&gt;

&lt;p&gt;SQLcl MCP exposes a deliberately small tool surface—connect, disconnect, list saved connections, run SQL—over a &lt;code&gt;stdio&lt;/code&gt; server. In the 25.3 docs, Level 4 is the most restrictive. Check your installed version’s “Restrict levels” page for the default, and keep the highest level practical in shared environments. The recommended posture is to use pre‑saved least‑privilege connections; restrict levels can be configured to disallow ad‑hoc credentials and keep the tool surface small. The server tags sessions (&lt;code&gt;V$SESSION.MODULE&lt;/code&gt; and &lt;code&gt;.ACTION&lt;/code&gt;) and writes activity to &lt;code&gt;DBTOOLS$MCP_LOG&lt;/code&gt; once used, so DBAs can see who did what and when. This is what “bounded action” looks like: the assistant can only do the few things the server allows, with least privilege and a record behind it. See “Using the SQLcl MCP Server,” “Restrict levels,” and “Monitoring” in the SQLcl docs for the precise tools, defaults, and logs for your version.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fee09xwc5ygmd400zf6f5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fee09xwc5ygmd400zf6f5.png" alt="Bounded MCP action surfaces for governed Oracle Database access and auditing" width="800" height="405"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If your Autonomous AI Database service and RU expose a managed MCP endpoint (see your service’s Help Center “Use MCP Server” page), you can integrate assistant actions with database identity, network controls, and auditing. Capabilities and APIs vary by service tier and RU; verify availability and procedures in your tenancy documentation. If a managed MCP endpoint isn’t available for your tier/RU, use the SQLcl MCP Server approach above.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reduce blast radius with Oracle’s online mechanics
&lt;/h2&gt;

&lt;p&gt;Not every change needs a window. Oracle has first‑party features for evolving structures while the system stays available.&lt;/p&gt;

&lt;p&gt;Structural changes under write load use &lt;code&gt;DBMS_REDEFINITION&lt;/code&gt;. The flow is predictable—eligibility check, start, copy dependent objects and constraints, sync the interim table, finish, and clean up—and the documentation spells out object and LOB rules per release (19c PL/SQL Reference). &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fddtpnkzk891z6dn92e3o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fddtpnkzk891z6dn92e3o.png" alt="DBMS_REDEFINTION Skeleton" width="800" height="188"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A compact, typical plan:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Eligibility (choose key strategy; use &lt;code&gt;CONS_USE_PK&lt;/code&gt; when a primary key exists, or &lt;code&gt;CONS_USE_ROWID&lt;/code&gt; otherwise—confirm constraints and object types supported in your version):
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;
  &lt;span class="n"&gt;DBMS_REDEFINITION&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CAN_REDEF_TABLE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;uname&lt;/span&gt;        &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'APP_OWNER'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tname&lt;/span&gt;        &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'ORDERS'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;options_flag&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;DBMS_REDEFINITION&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CONS_USE_PK&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Create interim table with desired structure (required unless supplying a column mapping to &lt;code&gt;START_REDEF_TABLE&lt;/code&gt;):
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Example: like-for-like copy you will later adjust as needed&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;app_owner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;orders_int&lt;/span&gt;
&lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;app_owner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Start:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;
  &lt;span class="n"&gt;DBMS_REDEFINITION&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;START_REDEF_TABLE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;uname&lt;/span&gt;     &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'APP_OWNER'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tname&lt;/span&gt;     &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'ORDERS'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;int_table&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'ORDERS_INT'&lt;/span&gt;
    &lt;span class="c1"&gt;-- , col_mapping =&amp;gt; '...'  -- use when structures differ&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Copy dependents (constraints, triggers, indexes, grants) and track errors:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;DECLARE&lt;/span&gt;
  &lt;span class="n"&gt;v_errs&lt;/span&gt; &lt;span class="n"&gt;PLS_INTEGER&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;BEGIN&lt;/span&gt;
  &lt;span class="n"&gt;DBMS_REDEFINITION&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;COPY_TABLE_DEPENDENTS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;uname&lt;/span&gt;            &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'APP_OWNER'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tname&lt;/span&gt;            &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'ORDERS'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;int_table&lt;/span&gt;        &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'ORDERS_INT'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;num_errors&lt;/span&gt;       &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;v_errs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;copy_triggers&lt;/span&gt;    &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;TRUE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;copy_constraints&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;TRUE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;copy_indexes&lt;/span&gt;     &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;TRUE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;copy_statistics&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;TRUE&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Sync:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;
  &lt;span class="n"&gt;DBMS_REDEFINITION&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SYNC_INTERIM_TABLE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'APP_OWNER'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'ORDERS'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Finish and swap:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;
  &lt;span class="n"&gt;DBMS_REDEFINITION&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FINISH_REDEF_TABLE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'APP_OWNER'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'ORDERS'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Cleanup: reconcile stats/grants, then drop interim artifacts as appropriate.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;LOBs, materialized view logs, row movement, and editioning interactions have version‑specific rules—confirm them in your release documentation before use.&lt;/p&gt;

&lt;p&gt;When your change is in editioned code rather than the table itself, Edition‑Based Redefinition lets you run two versions at once and cut over on your terms. Tables are not editioned, so the safe pattern is to shield them behind editioning views and synonyms, evolve the views or packages in a child edition, test in that edition, then switch references.&lt;/p&gt;

&lt;p&gt;Prerequisites (typically applied by a DBA):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;System privileges: &lt;code&gt;CREATE ANY EDITION&lt;/code&gt; (to create editions), &lt;code&gt;DROP ANY EDITION&lt;/code&gt; (to retire), and the ability to &lt;code&gt;ALTER USER ... ENABLE EDITIONS&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Object privileges in the target schema to create editioning views and editioned synonyms.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Build‑plan: EBR cutover (compact)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7wjsx025hx58vviwzpu2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7wjsx025hx58vviwzpu2.png" alt="Edition-Based Redefinition Rollback Path" width="800" height="158"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enable editions for your user:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;USER&lt;/span&gt; &lt;span class="n"&gt;app_owner&lt;/span&gt; &lt;span class="n"&gt;ENABLE&lt;/span&gt; &lt;span class="n"&gt;EDITIONS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;In ORA$BASE (or your current default edition), create an editioning view to shield the underlying table. The view name is editioned; the table is not:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;EDITIONING&lt;/span&gt; &lt;span class="k"&gt;VIEW&lt;/span&gt; &lt;span class="n"&gt;ev_orders&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order_ts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Create a child edition:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;EDITION&lt;/span&gt; &lt;span class="n"&gt;app_v2&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;CHILD&lt;/span&gt; &lt;span class="k"&gt;OF&lt;/span&gt; &lt;span class="n"&gt;ORA&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;BASE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Test in the child edition and evolve the API surface:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;SESSION&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;EDITION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;app_v2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Evolve API in the child edition (example: add a computed projection)&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="k"&gt;REPLACE&lt;/span&gt; &lt;span class="n"&gt;EDITIONING&lt;/span&gt; &lt;span class="k"&gt;VIEW&lt;/span&gt; &lt;span class="n"&gt;ev_orders&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;order_ts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="k"&gt;CASE&lt;/span&gt; &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'CANCELLED'&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;is_cancelled&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Cutover: change which edition clients use (via connection‑level edition, services, or app routing). Keep ORA$BASE as a rollback window.&lt;/li&gt;
&lt;li&gt;Rollback: switch sessions back to the previous edition while you investigate; retire the old edition once stable.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Indexes get two useful mechanics. First, invisible indexes let you test plan influence without affecting other sessions. You can create a new index INVISIBLE or set an existing index invisible, then in your session enable it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;SESSION&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;optimizer_use_invisible_indexes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;TRUE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If plans improve (verified via &lt;code&gt;EXPLAIN PLAN&lt;/code&gt; and &lt;code&gt;DBMS_XPLAN&lt;/code&gt;, and ideally timed execution), you can make the index visible for everyone. Second, &lt;code&gt;ALTER INDEX ... REBUILD ONLINE&lt;/code&gt; is documented in 19c for supported index types, letting DML continue during maintenance with some restrictions. Note the careful wording: in 19c the ONLINE clause is documented for REBUILD; do not assume &lt;code&gt;CREATE INDEX ONLINE&lt;/code&gt; for B‑tree indexes unless your exact release and index type say so.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmnhdotxo5mbxrdfhaz4o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmnhdotxo5mbxrdfhaz4o.png" alt="Index Evolution Patterns" width="800" height="374"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The same mindset for vector and relational indexes
&lt;/h2&gt;

&lt;p&gt;Oracle Database 26ai adds native vector indexes—HNSW (Hierarchical Navigable Small World) and IVF (Inverted File)—and vector distance operators. The safe evolution pattern is the same as for relational indexes: build a tuned index in parallel, validate it with representative queries, then drop the old one after a soak period.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Check your exact RU/service tier for required/optional clauses and defaults.&lt;/span&gt;
&lt;span class="c1"&gt;-- HNSW example (common defaults shown; tune per workload)&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;VECTOR&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;docs_hnsw_new&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;documents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;ORGANIZATION&lt;/span&gt; &lt;span class="n"&gt;HNSW&lt;/span&gt;
&lt;span class="n"&gt;DISTANCE&lt;/span&gt; &lt;span class="n"&gt;COSINE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- IVF example (parameter defaults vary by RU)&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;VECTOR&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;docs_ivf_new&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;documents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;ORGANIZATION&lt;/span&gt; &lt;span class="n"&gt;IVF&lt;/span&gt;
&lt;span class="n"&gt;DISTANCE&lt;/span&gt; &lt;span class="n"&gt;COSINE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your RU requires additional parameters (e.g., neighborhood or partition knobs), supply them as documented; prefer a build‑new → validate → drop‑old evolution unless your docs explicitly state online/rebuild semantics. Validate using workload queries or &lt;code&gt;DBMS_VECTOR&lt;/code&gt; advisers/accuracy checks, watching both recall and latency.&lt;/p&gt;

&lt;h2&gt;
  
  
  Proving what happened—and preparing to undo it
&lt;/h2&gt;

&lt;p&gt;If an assistant participates in change, you need clean answers to who did what and how to reverse it. Start by tagging sessions at the application level:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;
  &lt;span class="n"&gt;DBMS_APPLICATION_INFO&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SET_MODULE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'mcp-agent'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'index-additive-change'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;DBAs can then look in &lt;code&gt;V$SESSION&lt;/code&gt; for that module/action pair, and SQLcl MCP adds its own evidence in &lt;code&gt;DBTOOLS$MCP_LOG&lt;/code&gt; with each tool‑mediated request. With Unified Auditing enabled, you can go further: define policies for specific DDL or DML and query &lt;code&gt;UNIFIED_AUDIT_TRAIL&lt;/code&gt; to show who executed which statement and when. Access to dynamic performance views or audit trails depends on privileges; if your role is narrower, MCP logs still give you a reliable record. If you lack access to &lt;code&gt;V$SESSION&lt;/code&gt;, ask a DBA to expose a filtered view you can query.&lt;/p&gt;

&lt;p&gt;Rollback should be proportional to the change. For an index, the fastest backout is to mark it invisible; the hard backout is to drop it. For redefinition or EBR, the rollback is to keep the original structure or edition until final cutover and switch back if something misbehaves. For migrations, Liquibase can roll back by tag/changeSet/date when you’ve authored rollbacks (for supported change types); Flyway supports undo with authored scripts (Teams/Enterprise editions). The operative word is “authored”: write the backout path when you write the forward path.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fybp0y92bjwom802cpoy5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fybp0y92bjwom802cpoy5.png" alt="Evidence and Rollback Map" width="800" height="497"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A quick Unified Auditing check (if enabled and you have privileges):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;event_timestamp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dbusername&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;object_schema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;object_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sql_text&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="n"&gt;unified_audit_trail&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;  &lt;span class="n"&gt;action_name&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'CREATE INDEX'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'ALTER INDEX'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'DROP INDEX'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;AND&lt;/span&gt;    &lt;span class="n"&gt;object_schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'APP_OWNER'&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt;  &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;event_timestamp&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;span class="k"&gt;FETCH&lt;/span&gt; &lt;span class="k"&gt;FIRST&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="k"&gt;ROWS&lt;/span&gt; &lt;span class="k"&gt;ONLY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Mini‑demo: propose, review, apply, verify, prove
&lt;/h2&gt;

&lt;p&gt;Let’s run one small, additive change that an assistant proposes. We’ll add a composite index to speed a common filter on &lt;code&gt;DOCUMENTS&lt;/code&gt;. The mechanics are deliberately conservative: INVISIBLE first, session‑level test, then visible or back out.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F588afslapeys0r7h09yi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F588afslapeys0r7h09yi.png" alt="Mini-Demo Route" width="800" height="188"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Prerequisites for this demo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Oracle Database 19c or later.&lt;/li&gt;
&lt;li&gt;Privileges: &lt;code&gt;CREATE INDEX&lt;/code&gt; on &lt;code&gt;DOCUMENTS&lt;/code&gt; owner; &lt;code&gt;ALTER SESSION&lt;/code&gt;; &lt;code&gt;EXPLAIN PLAN&lt;/code&gt;; &lt;code&gt;EXECUTE&lt;/code&gt; on &lt;code&gt;DBMS_XPLAN&lt;/code&gt; and &lt;code&gt;DBMS_APPLICATION_INFO&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Access to &lt;code&gt;V$SESSION&lt;/code&gt; requires &lt;code&gt;SELECT_CATALOG_ROLE&lt;/code&gt; or equivalent; if not available, rely on &lt;code&gt;DBTOOLS$MCP_LOG&lt;/code&gt; for evidence.&lt;/li&gt;
&lt;li&gt;SQLcl MCP Server running with a pre‑saved least‑privilege connection to the target schema.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;1) Preflight discovery. If an assistant suggests an index, ask it to verify there isn’t already one that covers the same columns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;index_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;visibility&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="n"&gt;user_indexes&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;  &lt;span class="k"&gt;table_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'DOCUMENTS'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;index_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;column_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;column_position&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="n"&gt;user_ind_columns&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;  &lt;span class="k"&gt;table_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'DOCUMENTS'&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt;  &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;index_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;column_position&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Outside the owner schema, use &lt;code&gt;ALL_INDEXES&lt;/code&gt;/&lt;code&gt;ALL_IND_COLUMNS&lt;/code&gt;.)&lt;/p&gt;

&lt;p&gt;2) Proposal. The assistant drafts an index for &lt;code&gt;source&lt;/code&gt; and &lt;code&gt;published&lt;/code&gt;. Keep it INVISIBLE so only a session that opts in can use it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;docs_src_pub_idx&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;documents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;published&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;INVISIBLE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3) Apply through a bounded surface. Approve the statement and run it via SQLcl MCP’s SQL tool using a least‑privilege, pre‑saved connection. Configure restrict levels to keep the tool surface small and auditable; MCP logs the call and tags the session automatically in &lt;code&gt;V$SESSION&lt;/code&gt; (subject to your privileges).&lt;/p&gt;

&lt;p&gt;4) Test before broad exposure. Enable invisible indexes only in your session and check plan shape and timing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;SESSION&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;optimizer_use_invisible_indexes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;TRUE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;EXPLAIN&lt;/span&gt; &lt;span class="n"&gt;PLAN&lt;/span&gt; &lt;span class="k"&gt;FOR&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="n"&gt;documents&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;  &lt;span class="k"&gt;source&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'kb'&lt;/span&gt;
&lt;span class="k"&gt;AND&lt;/span&gt;    &lt;span class="n"&gt;published&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nb"&gt;DATE&lt;/span&gt; &lt;span class="s1"&gt;'2025-01-01'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;plan_table_output&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DBMS_XPLAN&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DISPLAY&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For final validation, prefer executed plans via &lt;code&gt;DBMS_XPLAN.DISPLAY_CURSOR&lt;/code&gt; with &lt;code&gt;ALLSTATS LAST&lt;/code&gt; after running the query, as EXPLAIN PLAN can differ from runtime under binds or adaptive features. If &lt;code&gt;PLAN_TABLE&lt;/code&gt; isn’t present in your schema, either create it (DBA task) or use &lt;code&gt;DBMS_XPLAN.DISPLAY_CURSOR&lt;/code&gt;, which doesn’t rely on &lt;code&gt;PLAN_TABLE&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Also capture actual timings on representative data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Show the last executed statement's plan with runtime stats (if available)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DBMS_XPLAN&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DISPLAY_CURSOR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'ALLSTATS LAST'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the plan and timings improve, make the index visible:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;docs_src_pub_idx&lt;/span&gt; &lt;span class="n"&gt;VISIBLE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;5) Evidence. Tag the module/action explicitly and pull recent MCP entries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;
  &lt;span class="n"&gt;DBMS_APPLICATION_INFO&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SET_MODULE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'mcp-agent'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'index-additive-change'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;/&lt;/span&gt;

&lt;span class="c1"&gt;-- Evidence in V$SESSION (requires privileges)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="k"&gt;session&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;  &lt;span class="n"&gt;module&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'mcp-agent'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Evidence in MCP logs (column names can vary by version)&lt;/span&gt;
&lt;span class="c1"&gt;-- DESCRIBE DBTOOLS$MCP_LOG;&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="n"&gt;DBTOOLS&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;MCP_LOG&lt;/span&gt;
&lt;span class="k"&gt;FETCH&lt;/span&gt; &lt;span class="k"&gt;FIRST&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="k"&gt;ROWS&lt;/span&gt; &lt;span class="k"&gt;ONLY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- If you need ordering, first identify a timestamp or sequence column via DESCRIBE/USER_TAB_COLUMNS,&lt;/span&gt;
&lt;span class="c1"&gt;-- then apply ORDER BY accordingly.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If logs appear empty, run a simple MCP &lt;code&gt;run-sql&lt;/code&gt; request and retry—the log table populates after the first tool interactions. Querying &lt;code&gt;V$SESSION&lt;/code&gt; requires &lt;code&gt;SELECT_CATALOG_ROLE&lt;/code&gt; or equivalent; if you don’t have it, rely on MCP logs and application‑level evidence or ask a DBA to expose a filtered view.&lt;/p&gt;

&lt;p&gt;6) Rollback if needed. If the index hurts a minority of queries or adds unacceptable write overhead, back it out quickly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;docs_src_pub_idx&lt;/span&gt; &lt;span class="n"&gt;INVISIBLE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;-- tactical backout&lt;/span&gt;
&lt;span class="c1"&gt;-- or&lt;/span&gt;
&lt;span class="k"&gt;DROP&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;docs_src_pub_idx&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;            &lt;span class="c1"&gt;-- hard revert&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s the loop: propose through discovery, preview the SQL, act through MCP, verify locally via INVISIBLE, and record the proof.&lt;/p&gt;

&lt;h2&gt;
  
  
  Delivery mechanics that hold up in real pipelines
&lt;/h2&gt;

&lt;p&gt;The same loop translates cleanly to CI/CD and change boards. Migration tools handle state and retries; MCP provides execution discipline; Oracle features reduce blast radius.&lt;/p&gt;

&lt;p&gt;In practice, you keep DDL/DML changes in version control and insist on previews. Liquibase’s &lt;code&gt;updateSQL&lt;/code&gt; prints exactly what would run for a given changelog; Flyway’s &lt;code&gt;dryRunOutput&lt;/code&gt; does the same for pending migrations. Checksums and state tables prevent accidental reruns. Avoid hand‑run DDL outside the tool, which can desynchronize history tables from reality. For table changes that must happen while writes continue, &lt;code&gt;DBMS_REDEFINITION&lt;/code&gt; is the predictable route; for code and view evolution, EBR gives you reversible cutovers. For online index maintenance, REBUILD ONLINE helps keep writes flowing, while INVISIBLE indexing keeps tests local until you’re ready.&lt;/p&gt;

&lt;p&gt;Testing belongs inside this posture, not as an afterthought. For PL/SQL and schema‑anchored behavior, &lt;code&gt;utPLSQL&lt;/code&gt; lets you write fast, repeatable tests and run them in CI. When you’re changing query paths, pair &lt;code&gt;DBMS_XPLAN&lt;/code&gt; plan displays with simple timing harnesses and capture them in artifacts the same way you capture dry‑run SQL. This is how agents and humans occupy the same workflow: the agent proposes, the pipeline proves.&lt;/p&gt;

&lt;h2&gt;
  
  
  A short view for migration leads
&lt;/h2&gt;

&lt;p&gt;If you’re modernizing a system or switching database platforms, treat “agent‑safe change” as the daily rhythm and migrations as a route through it. The sequence has four stages:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1 — Assess.&lt;/strong&gt; Inventory every object by type and complexity before writing a single migration script. Oracle’s skills repo includes a &lt;code&gt;migration-assessment.md&lt;/code&gt; skill that routes this step. In practice, complexity falls into three tiers: simple objects (tables with standard types, views, sequences) that translate with minimal manual effort; moderate objects (stored procedures, triggers, basic PL/SQL packages) that need construct-by-construct review; and complex objects (advanced types, Java stored procedures, database links, Oracle-specific partitioning strategies, or heavy use of &lt;code&gt;DBMS_*&lt;/code&gt; packages) that require hands-on expert review. An assistant can draft the inventory query and score the tiers—your job is to sanity-check the classification before committing to a timeline.&lt;/p&gt;

&lt;p&gt;A useful starting inventory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;object_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;cnt&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="n"&gt;all_objects&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;  &lt;span class="k"&gt;owner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;UPPER&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;‘&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;SOURCE_SCHEMA&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;’&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt;  &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;object_type&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt;  &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;cnt&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2 — Translate with source-specific guidance.&lt;/strong&gt; Each source platform has predictable problem areas. For PostgreSQL migrations, the most common friction points documented in &lt;code&gt;migrate-postgres-to-oracle.md&lt;/code&gt; are: sequence syntax (&lt;code&gt;SERIAL&lt;/code&gt; → &lt;code&gt;GENERATED AS IDENTITY&lt;/code&gt;), &lt;code&gt;BOOLEAN&lt;/code&gt; columns (Oracle has no native BOOLEAN in SQL prior to 23ai; the standard convention is &lt;code&gt;NUMBER(1,0)&lt;/code&gt; with a &lt;code&gt;CHECK (col IN (0,1))&lt;/code&gt; constraint), &lt;code&gt;RETURNING&lt;/code&gt; clauses (Oracle restricts &lt;code&gt;RETURNING INTO&lt;/code&gt; to PL/SQL blocks, requiring application refactoring), &lt;code&gt;ILIKE&lt;/code&gt; (rewrite as &lt;code&gt;UPPER(column) LIKE UPPER(pattern)&lt;/code&gt; with a supporting function-based index), and &lt;code&gt;DATE&lt;/code&gt; semantics (Oracle's &lt;code&gt;DATE&lt;/code&gt; stores time-of-day, so equality filters like &lt;code&gt;WHERE created = DATE '2024-01-15'&lt;/code&gt; silently miss rows that have a non-midnight time component). An assistant loaded with &lt;code&gt;migrate-postgres-to-oracle.md&lt;/code&gt; from the skills repo can flag these automatically when reviewing source DDL—but always verify the translation with a human before running it in staging.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3 — Pilot additive changes in staging.&lt;/strong&gt; Before cutover, run a representative subset of translated objects through the full discovery → preview → bounded apply → verify loop described above. This is where invisible indexes, preview-gate SQL, and &lt;code&gt;DBMS_REDEFINITION&lt;/code&gt; earn their keep. Focus on the moderate and complex tiers first; the simple tier can usually be scripted and batch-applied. Confirm that application queries return the same results on Oracle as they did on the source—row counts, join shapes, and date arithmetic are the usual suspects for silent semantic differences.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4 — Cutover and verify.&lt;/strong&gt; Keep ORA$BASE or a pre-cutover schema snapshot available as a rollback window. Use Liquibase or Flyway changelogs so the migration state is recorded and reruns are safe. After cutover, run your standard smoke tests plus any Oracle-specific validation queries you assembled during assessment. Tag the migration session with &lt;code&gt;DBMS_APPLICATION_INFO&lt;/code&gt; so post-cutover audit queries can isolate migration activity from normal application traffic.&lt;/p&gt;

&lt;p&gt;By the time you’re scheduling the production cutover, the mechanics should be boring. The assistant can draft scripts and surface incompatibilities; your tooling and Oracle’s online features make the process predictable and reversible.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to remember—and what’s next
&lt;/h2&gt;

&lt;p&gt;Agent‑safe change is not a bet on model intelligence; it’s a bet on process. Force discovery before action. Require a previewed SQL artifact. Act only through a bounded server with least privilege. Use Oracle’s online and editioning features to keep availability high. Tag sessions, log activity, and keep a rollback path that matches the change. When you operate this way, you can accept more assistant help without accepting more risk.&lt;/p&gt;

&lt;p&gt;In Article 8 we’ll close the loop with governance: identity and least privilege, policy, VPD, data redaction, and Unified Auditing as a procedural trust layer around the same route → act → trust loop.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources and further reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;SQLcl MCP Server: tools, restrict levels, monitoring and logs&lt;br&gt;
&lt;a href="https://docs.oracle.com/en/database/oracle/sql-developer-command-line/25.2/sqcug/using-oracle-sqlcl-mcp-server.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/sql-developer-command-line/25.2/sqcug/using-oracle-sqlcl-mcp-server.html&lt;/a&gt;&lt;br&gt;
&lt;a href="https://docs.oracle.com/en/database/oracle/sql-developer-command-line/25.3/sqcug/sqlcl-mcp-server-tools.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/sql-developer-command-line/25.3/sqcug/sqlcl-mcp-server-tools.html&lt;/a&gt;&lt;br&gt;
&lt;a href="https://docs.oracle.com/en/database/oracle/sql-developer-command-line/25.3/sqcug/configuring-restrict-levels-sqlcl-mcp-server.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/sql-developer-command-line/25.3/sqcug/configuring-restrict-levels-sqlcl-mcp-server.html&lt;/a&gt;&lt;br&gt;
&lt;a href="https://docs.oracle.com/en/database/oracle/sql-developer-command-line/25.4/sqcug/monitoring-sqlcl-mcp-server.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/sql-developer-command-line/25.4/sqcug/monitoring-sqlcl-mcp-server.html&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Online operations and index behavior (19c)&lt;br&gt;
ALTER INDEX (ONLINE for REBUILD): &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/ALTER-INDEX.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/ALTER-INDEX.html&lt;/a&gt;&lt;br&gt;
OPTIMIZER_USE_INVISIBLE_INDEXES: &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/19/refrn/OPTIMIZER_USE_INVISIBLE_INDEXES.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/19/refrn/OPTIMIZER_USE_INVISIBLE_INDEXES.html&lt;/a&gt;&lt;br&gt;
Managing invisible indexes: &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/19/admin/managing-indexes.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/19/admin/managing-indexes.html&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;DBMS_REDEFINITION (19c)&lt;br&gt;
&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/19/arpls/DBMS_REDEFINITION.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/19/arpls/DBMS_REDEFINITION.html&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;EBR (editions and editioning views)&lt;br&gt;
Start with the SQL Language Reference (CREATE EDITION; ALTER SESSION SET EDITION) and the Edition‑Based Redefinition Guide for your release (19c/23ai/26ai).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Vector indexing and functions (26ai)&lt;br&gt;
CREATE VECTOR INDEX: &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/create-vector-index.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/create-vector-index.html&lt;/a&gt;&lt;br&gt;
VECTOR_DISTANCE: &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/vector_distance.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/vector_distance.html&lt;/a&gt;&lt;br&gt;
DBMS_VECTOR: &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/arpls/dbms_vector1.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/arpls/dbms_vector1.html&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Migrations tooling&lt;br&gt;
Liquibase: &lt;a href="https://www.liquibase.org/" rel="noopener noreferrer"&gt;https://www.liquibase.org/&lt;/a&gt;&lt;br&gt;
Flyway: &lt;a href="https://documentation.red-gate.com/fd" rel="noopener noreferrer"&gt;https://documentation.red-gate.com/fd&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Evidence and auditing&lt;br&gt;
DBMS_APPLICATION_INFO: PL/SQL Packages and Types Reference (match your version)&lt;br&gt;
Unified Auditing and UNIFIED_AUDIT_TRAIL: Oracle Database Security Guide (match your version)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Series continuity&lt;br&gt;
Article 5 (Select AI, AI Profiles, SHOWSQL) and Article 6 (index and vector evaluation) provide the inspection and evaluation habits used here.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Notes on differences across releases and editions
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Index ONLINE semantics: In Oracle 19c, &lt;code&gt;ONLINE&lt;/code&gt; is documented for &lt;code&gt;ALTER INDEX ... REBUILD&lt;/code&gt; for supported index types with restrictions; check your exact RU and index type. Do not assume &lt;code&gt;CREATE INDEX ONLINE&lt;/code&gt; for B‑tree indexes in 19c.&lt;/li&gt;
&lt;li&gt;Invisible indexes: Controlled by &lt;code&gt;OPTIMIZER_USE_INVISIBLE_INDEXES&lt;/code&gt; at the session/system level. Validate on your target RU.&lt;/li&gt;
&lt;li&gt;Vector indexes: 26ai introduces HNSW and IVF; confirm syntax and capabilities for your RU/tier. Prefer build‑new → validate → drop‑old unless your docs state otherwise.&lt;/li&gt;
&lt;li&gt;SQLcl MCP: Tools and restrict‑level defaults are versioned. Verify the tool list and defaults for your installed SQLcl version.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>agents</category>
      <category>database</category>
      <category>devops</category>
      <category>sql</category>
    </item>
    <item>
      <title>How to Enforce Role-Based Data Access in AI Applications with Oracle Deep Data Security</title>
      <dc:creator>Mark Nelson</dc:creator>
      <pubDate>Thu, 28 May 2026 13:54:19 +0000</pubDate>
      <link>https://dev.to/oracledevs/how-to-enforce-role-based-data-access-in-ai-applications-with-oracle-deep-data-security-43om</link>
      <guid>https://dev.to/oracledevs/how-to-enforce-role-based-data-access-in-ai-applications-with-oracle-deep-data-security-43om</guid>
      <description>&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;AI assistants should not rely on prompts or application code to enforce sensitive data access rules.&lt;/li&gt;
&lt;li&gt;Oracle Deep Data Security keeps row, column, and document authorization in the database before data reaches the LLM.&lt;/li&gt;
&lt;li&gt;DEAL uses the same SQL and vector retrieval for Linda and Wendy, while Oracle Database returns different authorized results.&lt;/li&gt;
&lt;li&gt;The companion GitHub repository contains the runnable Python demo, including ADB-S setup scripts and an optional OCI GenAI chatbot loop.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
   
&lt;/h2&gt;

&lt;p&gt; &lt;/p&gt;

&lt;p&gt;AI-powered applications often use two kinds of retrieval at the same time: SQL for structured business records and vector search for contextual documents. A loan assistant might use SQL to list applications under review, then use vector search to retrieve policy guidance about income verification, debt-to-income exceptions, or credit risk. Both paths can reach sensitive data.&lt;/p&gt;

&lt;p&gt;That retrieval power creates a security problem. If an assistant can call broad SQL tools or broad RAG retrievers, it might fetch data the current user should not see. Prompt injection, missing metadata filters, a malformed tool call, or a forgotten Python redaction step can turn a useful chatbot into a data exposure path.&lt;/p&gt;

&lt;p&gt;The safest place to enforce access rules is the database, where the protected data already lives. Oracle Database security controls have been trusted for decades to enforce application access policies close to the rows and columns being protected. An LLM should not become the system of record for authorization decisions. It is probabilistic, prompt-influenced, and easy to steer into requesting more data than the user should receive.&lt;/p&gt;

&lt;p&gt;That distinction matters because data exfiltration is expensive. A single overbroad answer can expose regulated customer data, trigger incident response and disclosure work, create audit findings, violate compliance requirements, and erode trust in the AI application. Database security policy is also more provable and auditable than LLM behavior: you can inspect grants, test queries under known identities, review database audit records, and show that enforcement happens before data leaves the database. The goal is not to make the prompt better at saying no; the goal is to make sure the database only returns data the active end user is allowed to see.&lt;/p&gt;

&lt;p&gt;In this tutorial, we build &lt;strong&gt;DEAL — Deep Data Security Enabled Assistant for Lending&lt;/strong&gt;. DEAL is a terminal-based Python loan assistant that connects to &lt;strong&gt;Oracle AI Database 26ai on Autonomous Database Serverless&lt;/strong&gt; with a wallet. The demo uses direct logon as local Deep Sec end users: &lt;code&gt;linda&lt;/code&gt;, a loan officer, and &lt;code&gt;wendy&lt;/code&gt;, an underwriter.&lt;/p&gt;

&lt;p&gt;The proof point is simple: DEAL runs the same broad SQL and the same vector retrieval pattern for both users. Oracle Database enforces the configured data access policies for the demo's SQL and vector retrieval paths before data reaches Python, the tool layer, or the LLM. The Python code does not filter rows by user, redact restricted fields, or filter RAG results by audience.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is Oracle Deep Data Security?
&lt;/h2&gt;

&lt;p&gt;Oracle Deep Data Security is a database-enforced data authorization framework in &lt;strong&gt;Oracle AI Database 26ai&lt;/strong&gt;. It lets applications pass end-user security context to the database so that the database can enforce fine-grained access to protected data.&lt;/p&gt;

&lt;p&gt;In DEAL, there are four important pieces:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Database owner user&lt;/strong&gt;: the database account Python uses for setup scripts, such as &lt;code&gt;DEEPSEC&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Application end user&lt;/strong&gt;: the human using the app. DEAL uses &lt;code&gt;linda&lt;/code&gt; and &lt;code&gt;wendy&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deep Sec end-user context&lt;/strong&gt;: the database identity that is active while a user-scoped operation runs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data roles and data grants&lt;/strong&gt;: the Deep Data Security objects that define which rows, columns, and operations are available.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The teaching pattern throughout the demo is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;same query shape
same SQL
different Deep Sec end user
different authorized result
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The no-token path uses direct logon as local Deep Sec end users. Deep Sec also supports service-user context patterns where the application connects as a service user and sets end-user context before each database operation. Some service-user patterns use external identity tokens, and some use prepared local Deep Sec end-user identities.&lt;/p&gt;

&lt;p&gt;Use this table to choose the right identity path:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Path&lt;/th&gt;
&lt;th&gt;How the database knows the end user&lt;/th&gt;
&lt;th&gt;External IAM or Entra token required?&lt;/th&gt;
&lt;th&gt;When to use it&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Local Deep Sec direct logon&lt;/td&gt;
&lt;td&gt;Python connects directly as a local Deep Sec end user such as &lt;code&gt;linda&lt;/code&gt; or &lt;code&gt;wendy&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Best for a compact tutorial, lab, or proof where each demo user can have a local Deep Sec password&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Service user with local Deep Sec context&lt;/td&gt;
&lt;td&gt;Python connects as the application/service schema, then sets context for a prepared local Deep Sec end user&lt;/td&gt;
&lt;td&gt;No external IAM token, but requires local end-user setup and context-key handling&lt;/td&gt;
&lt;td&gt;Useful when the app should keep a stable service connection while still avoiding an external identity provider for a demo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Service user with OCI IAM or Microsoft Entra&lt;/td&gt;
&lt;td&gt;Python connects as the application/service schema, then sets context using a database access token issued by the identity provider&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Best for production-style apps that already authenticate users through OCI IAM, Entra, or a federated identity flow&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The rest of the tutorial uses the first path: local Deep Sec direct logon. That keeps the security proof focused on database-enforced row, column, and vector retrieval behavior instead of identity-provider setup.&lt;/p&gt;

&lt;p&gt;Deep Data Security requires Oracle Database 26ai and is supported only in &lt;code&gt;python-oracledb&lt;/code&gt; Thin mode. Do not call &lt;code&gt;oracledb.init_oracle_client()&lt;/code&gt; in this tutorial.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxex6ktv9gk5v6cxpdiep.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxex6ktv9gk5v6cxpdiep.png" alt="Architecture diagram showing Linda or Wendy using the Python DEAL assistant, which connects to Oracle AI Database 26ai with python-oracledb Thin mode as local Deep Sec end users and receives database-enforced loan rows, restricted columns, and policy document results." width="800" height="541"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;DEAL architecture: the Python assistant connects as local Deep Sec end users, and Oracle Database enforces the configured data access policies for the demo’s SQL and vector retrieval paths.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Features of Oracle Deep Data Security
&lt;/h2&gt;

&lt;p&gt;DEAL uses a small lending scenario to demonstrate the Deep Data Security capabilities that matter most to AI application developers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Data roles and end-user identity
&lt;/h3&gt;

&lt;p&gt;Deep Sec data roles group data access permissions. DEAL uses two data roles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;LOAN_OFFICER_ROLE&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UNDERWRITER_ROLE&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The prepared Deep Sec end users are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;linda&lt;/code&gt;, assigned to &lt;code&gt;LOAN_OFFICER_ROLE&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;wendy&lt;/code&gt;, assigned to &lt;code&gt;UNDERWRITER_ROLE&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the no-token path, Linda and Wendy are local Deep Sec end users who can log on directly after an administrator grants a standard &lt;code&gt;CREATE SESSION&lt;/code&gt; role through the Deep Sec data roles. That keeps the tutorial free of external IAM or Entra token setup.&lt;/p&gt;

&lt;h3&gt;
  
  
  Row-level security
&lt;/h3&gt;

&lt;p&gt;Deep Sec data grants can include row predicates. DEAL uses row rules like these:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Linda sees loan applications where &lt;code&gt;assigned_officer&lt;/code&gt; matches the active Deep Sec username.&lt;/li&gt;
&lt;li&gt;Wendy sees loan applications where &lt;code&gt;in_underwriting_queue = 'Y'&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The application SQL does not add &lt;code&gt;WHERE assigned_officer = :end_user&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Column-level security
&lt;/h3&gt;

&lt;p&gt;Data grants can restrict column access. DEAL restricts sensitive financial and underwriting fields for Linda, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;customer_ssn&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;customer_income&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;customer_credit_score&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;underwriting_decision&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;risk_score&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;underwriting_notes&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the demo output, restricted values are displayed as &lt;code&gt;NULL&lt;/code&gt;. The Python code does not replace values with &lt;code&gt;None&lt;/code&gt; or hide fields.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deep Sec-scoped vector retrieval
&lt;/h3&gt;

&lt;p&gt;Role-scoped vector retrieval matters because RAG systems often search across policy, procedure, and knowledge-base documents that are not equally appropriate for every user. A loan officer might need intake and customer-document guidance, while an underwriter might need credit-risk escalation procedures. If the retriever ranks every nearby document first and asks the LLM to ignore unauthorized context later, sensitive guidance has already left the database. DEAL makes the authorization check happen before vector ranking returns results to the application.&lt;/p&gt;

&lt;p&gt;DEAL stores policy documents in Oracle Database with a &lt;code&gt;VECTOR(3, FLOAT32)&lt;/code&gt; column. The core demo uses deterministic 3-dimensional demo vectors so the tutorial stays self-contained.&lt;/p&gt;

&lt;p&gt;The vector retrieval query runs under Linda’s or Wendy’s Deep Sec context. The application does not add a security filter such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;audience&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="k"&gt;role&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead, Deep Sec data grants govern which &lt;code&gt;loan_policies&lt;/code&gt; rows are visible to the query; the vector query ranks the rows visible under the active end-user context.&lt;/p&gt;

&lt;p&gt;We use &lt;code&gt;VECTOR_DISTANCE()&lt;/code&gt; in the SQL because it keeps the distance expression and metric explicit. Oracle AI Vector Search also provides shorthand vector distance operators, but this tutorial intentionally stays with &lt;code&gt;VECTOR_DISTANCE()&lt;/code&gt; so the Deep Sec proof is easier to read. The demo uses a small data set and does not create a vector index. A vector index is not useful for proving performance or plans on eight rows; add indexing later when you move to a larger corpus and can verify index metadata and execution behavior for your workload.&lt;/p&gt;

&lt;h3&gt;
  
  
  Application trust boundary
&lt;/h3&gt;

&lt;p&gt;The assistant is not the security boundary. DEAL’s Python code is responsible for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Connecting to Oracle AI Database 26ai.&lt;/li&gt;
&lt;li&gt;Binding values safely.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The database is responsible for enforcing the configured row, column, and policy-document access rules.&lt;/p&gt;

&lt;p&gt;The demo avoids these application-side security patterns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;assigned_officer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;audience&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'general'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="k"&gt;role&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;end_user&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;linda&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;customer_ssn&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those patterns can work in one carefully reviewed code path, but they are fragile as the application grows. Every new query, retriever, tool function, report, or chatbot action becomes another place where a developer has to remember the right row predicate, audience filter, and redaction rule. If one path forgets a filter, the database may return too much data before the mistake is noticed. Keeping the rules in Deep Sec data grants makes enforcement centralized, testable, and consistent across the application paths that reach the protected tables.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Get Started with Oracle Deep Data Security
&lt;/h2&gt;

&lt;p&gt;This tutorial assumes an administrator-prepared &lt;strong&gt;Oracle AI Database 26ai on Autonomous Database Serverless&lt;/strong&gt; environment where Oracle Deep Data Security is enabled and the required privileges have been granted. The demo targets ADB-S 26ai; it does not use Oracle Database Free or a local Oracle container.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;p&gt;You need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Oracle AI Database 26ai on Autonomous Database Serverless.&lt;/li&gt;
&lt;li&gt;A downloaded and unzipped Autonomous Database wallet.&lt;/li&gt;
&lt;li&gt;A tutorial owner schema user, such as &lt;code&gt;DEEPSEC&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Database password for the tutorial owner schema.&lt;/li&gt;
&lt;li&gt;A TNS alias from the wallet, or a connect descriptor using host, port, and service name values from your Oracle AI Database 26ai environment.&lt;/li&gt;
&lt;li&gt;Wallet password.&lt;/li&gt;
&lt;li&gt;Prepared Deep Sec end users for &lt;code&gt;linda&lt;/code&gt; and &lt;code&gt;wendy&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Passwords for the local Deep Sec end users &lt;code&gt;linda&lt;/code&gt; and &lt;code&gt;wendy&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Privileges to create Deep Sec data roles and data grants for the tutorial objects, or an administrator who has prepared those objects.&lt;/li&gt;
&lt;li&gt;Optional: OCI Generative AI access for the chatbot extension.&lt;/li&gt;
&lt;li&gt;Python 3.10 or later.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Prepared Deep Sec identity contract
&lt;/h3&gt;

&lt;p&gt;Before you run the demo, an administrator must create or provide the Deep Sec identity setup. This tutorial uses local Deep Sec users and direct logon, so the setup includes end users such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="k"&gt;USER&lt;/span&gt; &lt;span class="n"&gt;linda&lt;/span&gt; &lt;span class="n"&gt;IDENTIFIED&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="k"&gt;USER&lt;/span&gt; &lt;span class="n"&gt;wendy&lt;/span&gt; &lt;span class="n"&gt;IDENTIFIED&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those end users are not ordinary database schema users. Because the SQL does not quote &lt;code&gt;linda&lt;/code&gt; or &lt;code&gt;wendy&lt;/code&gt;, Oracle stores the end-user names using the normal uppercase identifier form: &lt;code&gt;LINDA&lt;/code&gt; and &lt;code&gt;WENDY&lt;/code&gt;. The setup scripts connect as the tutorial owner schema, such as &lt;code&gt;DEEPSEC&lt;/code&gt;, while the user-facing read and RAG scripts connect directly as &lt;code&gt;LINDA&lt;/code&gt; or &lt;code&gt;WENDY&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The tutorial owner needs the Deep Sec privileges required to create data roles, grant data roles, create data grants, create end users, and enable mandatory use of data grants for the demo tables. Ask an administrator to grant:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;DATA&lt;/span&gt; &lt;span class="k"&gt;ROLE&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;deepsec&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;DATA&lt;/span&gt; &lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;deepsec&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;ANY&lt;/span&gt; &lt;span class="k"&gt;DATA&lt;/span&gt; &lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;deepsec&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="n"&gt;ADMINISTER&lt;/span&gt; &lt;span class="k"&gt;ANY&lt;/span&gt; &lt;span class="k"&gt;DATA&lt;/span&gt; &lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;deepsec&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;ANY&lt;/span&gt; &lt;span class="k"&gt;DATA&lt;/span&gt; &lt;span class="k"&gt;ROLE&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;deepsec&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="k"&gt;USER&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;deepsec&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="k"&gt;USER&lt;/span&gt; &lt;span class="k"&gt;SECURITY&lt;/span&gt; &lt;span class="n"&gt;CONTEXT&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;deepsec&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;USE&lt;/span&gt; &lt;span class="k"&gt;DATA&lt;/span&gt; &lt;span class="n"&gt;GRANTS&lt;/span&gt; &lt;span class="k"&gt;ONLY&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;deepsec&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For direct logon without tokens, an administrator also creates a normal database role with &lt;code&gt;CREATE SESSION&lt;/code&gt;, then grants that role to the Deep Sec data roles used by the tutorial:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;ROLE&lt;/span&gt; &lt;span class="n"&gt;deal_direct_logon_role&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;SESSION&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;deal_direct_logon_role&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="n"&gt;deal_direct_logon_role&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;loan_officer_role&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="n"&gt;deal_direct_logon_role&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;underwriter_role&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If &lt;code&gt;loan_officer_role&lt;/code&gt; and &lt;code&gt;underwriter_role&lt;/code&gt; do not exist yet, create &lt;code&gt;deal_direct_logon_role&lt;/code&gt; first, run Step 5 to create the Deep Sec data roles, and then run the two &lt;code&gt;GRANT deal_direct_logon_role ...&lt;/code&gt; statements.&lt;/p&gt;

&lt;p&gt;After the tutorial tables exist, the owner enables mandatory data-grant enforcement on the protected tables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;USE&lt;/span&gt; &lt;span class="k"&gt;DATA&lt;/span&gt; &lt;span class="n"&gt;GRANTS&lt;/span&gt; &lt;span class="k"&gt;ONLY&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;loan_applications&lt;/span&gt; &lt;span class="n"&gt;ENABLED&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;USE&lt;/span&gt; &lt;span class="k"&gt;DATA&lt;/span&gt; &lt;span class="n"&gt;GRANTS&lt;/span&gt; &lt;span class="k"&gt;ONLY&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;loan_policies&lt;/span&gt; &lt;span class="n"&gt;ENABLED&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a service-user context setup that uses an external identity provider, the &lt;code&gt;DEEPSEC_DATABASE_ACCESS_TOKEN&lt;/code&gt; value in &lt;code&gt;.env&lt;/code&gt; is not a database password and not a convenient static tutorial secret. It is a database access token issued by the configured identity provider, such as OCI IAM or Microsoft Entra ID, and it can expire. Your administrator or identity setup must tell you how to obtain and refresh it.&lt;/p&gt;

&lt;p&gt;For the no-token local-user setup, use the direct-logon pattern: Python connects directly as local Deep Sec end users such as &lt;code&gt;LINDA&lt;/code&gt; and &lt;code&gt;WENDY&lt;/code&gt;. In that mode, the database does not need &lt;code&gt;DEEPSEC_DATABASE_ACCESS_TOKEN&lt;/code&gt;, but an administrator must grant a standard &lt;code&gt;CREATE SESSION&lt;/code&gt; role to the Deep Sec data roles.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;end_user_identity&lt;/code&gt; value passed to &lt;code&gt;oracledb.create_end_user_security_context()&lt;/code&gt; depends on the identity model:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For IAM or Entra users, it is typically an identity token string.&lt;/li&gt;
&lt;li&gt;For local Deep Sec users in a service-user context pattern, it can be a tuple such as &lt;code&gt;("LINDA", key)&lt;/code&gt; where the key comes from the prepared local-user setup.&lt;/li&gt;
&lt;li&gt;For direct logon, the helper does not call &lt;code&gt;create_end_user_security_context()&lt;/code&gt;; it opens the connection as the local Deep Sec end user.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The demo helper supports &lt;code&gt;DEEPSEC_CONTEXT_MODE=direct_logon&lt;/code&gt;, &lt;code&gt;external_token&lt;/code&gt;, or &lt;code&gt;local_tuple&lt;/code&gt;. Use &lt;code&gt;direct_logon&lt;/code&gt; for this tutorial.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create the project
&lt;/h3&gt;

&lt;p&gt;You can copy the files from this tutorial, or clone the companion GitHub repository that contains the same runnable demo project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/markxnelson/deep-data-security.git deal-deepsec
&lt;span class="nb"&gt;cd &lt;/span&gt;deal-deepsec
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you are copying the code from the article instead of cloning the repository, create a project directory first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;deal-deepsec
&lt;span class="nb"&gt;cd &lt;/span&gt;deal-deepsec

python &lt;span class="nt"&gt;-m&lt;/span&gt; venv .venv
&lt;span class="nb"&gt;source&lt;/span&gt; .venv/bin/activate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On Windows PowerShell:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;python&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-m&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;venv&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;venv&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;venv&lt;/span&gt;&lt;span class="nx"&gt;\Scripts\Activate.ps1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you cloned the companion repository, create and activate the virtual environment inside that cloned directory before installing dependencies.&lt;/p&gt;

&lt;p&gt;The companion repository already includes &lt;code&gt;requirements.txt&lt;/code&gt;, &lt;code&gt;.env.example&lt;/code&gt;, &lt;code&gt;.gitignore&lt;/code&gt;, and the Python scripts. If you cloned it, use the file-creation sections below as reference and skip ahead to installing dependencies after your virtual environment is active.&lt;/p&gt;

&lt;p&gt;Create &lt;code&gt;requirements.txt&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;oracledb
python-dotenv
oci
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install the dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Successfully installed oracledb ...
Successfully installed python-dotenv ...
Successfully installed oci ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Add local environment variables
&lt;/h3&gt;

&lt;p&gt;Create &lt;code&gt;.env.example&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Oracle AI Database 26ai on ADB-S connection.&lt;/span&gt;
&lt;span class="nv"&gt;ADB_USERNAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;DEEPSEC
&lt;span class="nv"&gt;ADB_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your-database-password

&lt;span class="c"&gt;# Use an ADB wallet TNS alias such as yourdb_high,&lt;/span&gt;
&lt;span class="c"&gt;# or a TCPS connect descriptor with host, port, and service_name values.&lt;/span&gt;
&lt;span class="nv"&gt;ADB_DSN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your-adb-tns-alias-or-connect-descriptor

&lt;span class="c"&gt;# Directory containing the unzipped wallet files, including tnsnames.ora and ewallet.pem.&lt;/span&gt;
&lt;span class="nv"&gt;ADB_WALLET_LOCATION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/path/to/unzipped/adb-wallet

&lt;span class="c"&gt;# Wallet password created when the wallet was downloaded.&lt;/span&gt;
&lt;span class="c"&gt;# This is not the database user password.&lt;/span&gt;
&lt;span class="nv"&gt;ADB_WALLET_PASSPHRASE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your-wallet-password

&lt;span class="c"&gt;# Deep Sec context mode must match your administrator-prepared identity setup.&lt;/span&gt;
&lt;span class="c"&gt;# Use direct_logon for local Deep Sec users without IAM/Entra tokens.&lt;/span&gt;
&lt;span class="c"&gt;# Use external_token for IAM/Entra-style identity strings.&lt;/span&gt;
&lt;span class="c"&gt;# Use local_tuple when a service connection sets context for local end-user tuples.&lt;/span&gt;
&lt;span class="nv"&gt;DEEPSEC_CONTEXT_MODE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;direct_logon

&lt;span class="c"&gt;# Required only for service-user context modes.&lt;/span&gt;
&lt;span class="c"&gt;# direct_logon does not use this value.&lt;/span&gt;
&lt;span class="nv"&gt;DEEPSEC_DATABASE_ACCESS_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;

&lt;span class="c"&gt;# Direct-logon passwords for the local Deep Sec end users.&lt;/span&gt;
&lt;span class="nv"&gt;DEEPSEC_LINDA_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your-linda-end-user-password
&lt;span class="nv"&gt;DEEPSEC_WENDY_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your-wendy-end-user-password

&lt;span class="c"&gt;# Protected object owner used when direct-logon users query the demo tables.&lt;/span&gt;
&lt;span class="nv"&gt;DEAL_OBJECT_OWNER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;DEEPSEC

&lt;span class="c"&gt;# OCI Generative AI settings for the final chatbot step.&lt;/span&gt;
&lt;span class="nv"&gt;OCI_CONFIG_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;~/.oci/config
&lt;span class="nv"&gt;OCI_PROFILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;DEFAULT
&lt;span class="nv"&gt;OCI_GENAI_ENDPOINT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://inference.generativeai.us-chicago-1.oci.oraclecloud.com
&lt;span class="nv"&gt;OCI_GENAI_COMPARTMENT_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your-compartment-ocid
&lt;span class="nv"&gt;OCI_GENAI_MODEL_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your-generative-ai-model-id
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy it to &lt;code&gt;.env&lt;/code&gt; and edit the values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cp&lt;/span&gt; .env.example .env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Never commit &lt;code&gt;.env&lt;/code&gt; to source control. Add &lt;code&gt;.gitignore&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.env
.venv/
__pycache__/
*.pyc
.pytest_cache/
.DS_Store
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  DEAL Demo Project
&lt;/h2&gt;

&lt;p&gt;The demo uses small scripts that build on each other. Run them in order:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python 01_verify_deepsec.py
python 02_create_schema.py
python 03_load_data.py
python 04_configure_deepsec.py
python 05_context_read_demo.py
python 07_vector_basics.py
python 08_secure_rag_retrieval.py
python 08b_keyword_vs_vector_check.py  &lt;span class="c"&gt;# optional&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After Step 8, the Deep Sec security proof is complete. The remaining scripts wrap the proven operations as DEAL tool functions, print a final terminal session, and optionally connect those tools to OCI Generative AI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python 09_deal_tools_demo.py
python 10_run_deal_sessions.py
python 11_oci_genai_chatbot.py  &lt;span class="c"&gt;# optional, requires OCI GenAI configuration&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 1: Verify the ADB-S connection and Deep Sec metadata visibility
&lt;/h3&gt;

&lt;p&gt;Start with the smallest useful checkpoint: connect to Oracle AI Database 26ai using the wallet and confirm that &lt;code&gt;python-oracledb&lt;/code&gt; is running in Thin mode.&lt;/p&gt;

&lt;p&gt;Create &lt;code&gt;01_verify_deepsec.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pathlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;oracledb&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dotenv&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_dotenv&lt;/span&gt;


&lt;span class="nf"&gt;load_dotenv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;required_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
        &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;replace-&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;your-&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/path/to/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Set &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; in .env before running this script.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;first_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;replace-&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;your-&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Set &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; or &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; in .env before running this script.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;optional_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;replace-&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;your-&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;wallet_dsn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wallet_dir&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;tnsnames&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wallet_dir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tnsnames.ora&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;tnsnames&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Set ADB_DSN or provide a wallet with tnsnames.ora.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;tnsnames&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;splitlines&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="n"&gt;stripped&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;stripped&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;stripped&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;#&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;stripped&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;No TNS aliases found in wallet tnsnames.ora.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;wallet_dir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;first_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ADB_WALLET_LOCATION&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DB_WALLET_DIR&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;dsn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;optional_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ADB_DSN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DB_DSN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="nf"&gt;wallet_dsn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wallet_dir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;oracledb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;first_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ADB_USERNAME&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DB_USER&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;first_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ADB_PASSWORD&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DB_PASSWORD&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;dsn&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;dsn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;config_dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;wallet_dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;wallet_location&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;wallet_dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;wallet_password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;optional_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ADB_WALLET_PASSPHRASE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ADB_WALLET_PASSWORD&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DB_WALLET_PASSWORD&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DEAL environment check&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;cur&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;select sys_context(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;USERENV&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;CURRENT_SCHEMA&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;) from dual&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Connected as: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchone&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;select banner_full from v$version where rownum = 1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;version_text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchone&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;oracledb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DatabaseError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
            select version_full
            from product_component_version
            where product like &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Oracle Database%&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;
            fetch first 1 row only
            &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;version_text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchone&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Database version: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;version_text&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Thin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;oracledb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_thin_mode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Thick&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;python-oracledb mode: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Thin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Deep Data Security requires python-oracledb Thin mode for this demo.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
        select view_name
        from all_views
        where view_name in (
            &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;DBA_DATA_ROLES&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;,
            &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;DBA_DATA_ROLE_GRANTS&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;,
            &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;DBA_DATA_GRANTS&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;,
            &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ALL_DATA_GRANTS&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;,
            &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;USER_DATA_GRANTS&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;
        )
        order by view_name
        &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;visible_views&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchall&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;visible_views&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Deep Sec metadata visible to this schema: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;visible_views&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Deep Sec metadata views are not visible to this schema.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Continue only with an administrator-prepared Deep Sec environment.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python 01_verify_deepsec.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DEAL environment check
Connected as: DEEPSEC
Database version: Oracle AI Database 26ai Enterprise Edition Release 23.26.2.1.0
python-oracledb mode: Thin
Deep Sec metadata visible to this schema: ALL_DATA_GRANTS, USER_DATA_GRANTS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your schema cannot see Deep Sec metadata views, you may see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Deep Sec metadata views are not visible to this schema.
Continue only with an administrator-prepared Deep Sec environment.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If this fails:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Confirm &lt;code&gt;ADB_DSN&lt;/code&gt;, or let the helper pick an alias from wallet &lt;code&gt;tnsnames.ora&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Confirm &lt;code&gt;ADB_WALLET_LOCATION&lt;/code&gt; contains the unzipped wallet files.&lt;/li&gt;
&lt;li&gt;Confirm &lt;code&gt;ADB_WALLET_PASSPHRASE&lt;/code&gt; is the wallet password, not the database password.&lt;/li&gt;
&lt;li&gt;Confirm the database is Oracle AI Database 26ai.&lt;/li&gt;
&lt;li&gt;Do not switch to Thick mode.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 2: Add a small connection helper
&lt;/h3&gt;

&lt;p&gt;Now that we have seen the full connection code, move the connection boilerplate into a small helper. Do not add authorization logic yet.&lt;/p&gt;

&lt;p&gt;Create &lt;code&gt;deal_db.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pathlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;oracledb&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dotenv&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_dotenv&lt;/span&gt;


&lt;span class="nf"&gt;load_dotenv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;required_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
        &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;replace-&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;your-&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/path/to/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Set &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; in .env before running this script.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;first_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;replace-&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;your-&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Set &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; or &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; in .env before running this script.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;optional_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;replace-&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;your-&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;wallet_dsn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wallet_dir&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;tnsnames&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wallet_dir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tnsnames.ora&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;tnsnames&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Set ADB_DSN or provide a wallet with tnsnames.ora.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;tnsnames&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;splitlines&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="n"&gt;stripped&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;stripped&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;stripped&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;#&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;stripped&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;No TNS aliases found in wallet tnsnames.ora.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;wallet_dir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;first_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ADB_WALLET_LOCATION&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DB_WALLET_DIR&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;dsn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;optional_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ADB_DSN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DB_DSN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="nf"&gt;wallet_dsn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wallet_dir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;oracledb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;first_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ADB_USERNAME&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DB_USER&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;first_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ADB_PASSWORD&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DB_PASSWORD&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;dsn&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;dsn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;config_dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;wallet_dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;wallet_location&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;wallet_dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;wallet_password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;optional_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ADB_WALLET_PASSPHRASE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ADB_WALLET_PASSWORD&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DB_WALLET_PASSWORD&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is no command to run for this helper. Later scripts import &lt;code&gt;connect()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Before continuing, you should understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;deal_db.py&lt;/code&gt; centralizes only connection setup.&lt;/li&gt;
&lt;li&gt;It does not filter data, redact columns, or set user context.&lt;/li&gt;
&lt;li&gt;It does not call &lt;code&gt;oracledb.init_oracle_client()&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 3: Create loan and policy tables
&lt;/h3&gt;

&lt;p&gt;Create two tables:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;loan_applications&lt;/code&gt; for structured loan data.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;loan_policies&lt;/code&gt; for policy documents and demo vectors.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Create &lt;code&gt;02_create_schema.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;oracledb&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;deal_db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;connect&lt;/span&gt;


&lt;span class="n"&gt;DDL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    create table loan_applications (
        id number primary key,
        customer_name varchar2(100) not null,
        loan_amount number(12,2) not null,
        purpose varchar2(100) not null,
        status varchar2(40) not null,
        officer_notes varchar2(4000),
        customer_ssn varchar2(20),
        customer_income number(12,2),
        customer_credit_score number(4),
        underwriting_decision varchar2(40),
        risk_score number(5,2),
        underwriting_notes varchar2(4000),
        assigned_officer varchar2(40) not null,
        in_underwriting_queue char(1) check (in_underwriting_queue in (&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Y&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;N&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;))
    )
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    create table loan_policies (
        id number primary key,
        title varchar2(200) not null,
        body varchar2(4000) not null,
        audience varchar2(40) not null,
        embedding vector(3, float32)
    )
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;drop_if_exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;table_name&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;drop table &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;table_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; purge&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;oracledb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DatabaseError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;942&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt;


&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;cur&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="nf"&gt;drop_if_exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;loan_policies&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;drop_if_exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;loan_applications&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Dropped existing DEAL demo tables if they existed.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;statement&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;DDL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;statement&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Created loan_applications.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Created loan_policies with VECTOR(3, FLOAT32) embedding column.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Schema setup complete.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python 02_create_schema.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Dropped existing DEAL demo tables if they existed.
Created loan_applications.
Created loan_policies with VECTOR(3, FLOAT32) embedding column.
Schema setup complete.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before continuing, you should understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The policy corpus is stored in Oracle Database, not in a separate vector store.&lt;/li&gt;
&lt;li&gt;The vector column is intentionally small: &lt;code&gt;VECTOR(3, FLOAT32)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Security will be added with Deep Sec data grants, not views or Python filters.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 4: Load synthetic data and deterministic demo vectors
&lt;/h3&gt;

&lt;p&gt;Load enough data to make security differences visible.&lt;/p&gt;

&lt;p&gt;The row design is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Linda is assigned applications &lt;code&gt;101&lt;/code&gt;, &lt;code&gt;102&lt;/code&gt;, and &lt;code&gt;105&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Wendy can see underwriting queue applications &lt;code&gt;102&lt;/code&gt;, &lt;code&gt;103&lt;/code&gt;, &lt;code&gt;105&lt;/code&gt;, and &lt;code&gt;106&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Policy documents are tagged as &lt;code&gt;general&lt;/code&gt;, &lt;code&gt;loan_officer&lt;/code&gt;, or &lt;code&gt;underwriter&lt;/code&gt;, but the application will not filter on that column.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The three-audience policy model is intentional. A simpler &lt;code&gt;all&lt;/code&gt;/&lt;code&gt;underwriter&lt;/code&gt; model can work, but &lt;code&gt;general&lt;/code&gt;, &lt;code&gt;loan_officer&lt;/code&gt;, and &lt;code&gt;underwriter&lt;/code&gt; makes the policy proof easier to inspect: both roles can receive general guidance, and each role can also receive role-specific documents without any Python-side audience filtering.&lt;/p&gt;

&lt;p&gt;Create &lt;code&gt;03_load_data.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;array&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;array&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;deal_db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;connect&lt;/span&gt;


&lt;span class="n"&gt;loans&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Avery Stone&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;320000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Home purchase&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;RECEIVED&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Missing final pay stub.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;111-22-3333&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;128000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;720&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PENDING_REVIEW&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;41&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Not reviewed yet.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;LINDA&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;N&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;102&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Noah Rivers&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;485000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Home purchase&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;UNDER_REVIEW&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Customer uploaded tax documents.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;222-33-4444&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;151000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;695&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PENDING_REVIEW&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;72&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Watch revolving debt.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;LINDA&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Y&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;103&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Maya Chen&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;210000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Refinance&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;UNDER_REVIEW&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Transferred from branch intake.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;333-44-5555&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;98000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;681&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PENDING_REVIEW&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;68&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Check income variability.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;raj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Y&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;104&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Grace Hill&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;150000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Home equity&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;NEEDS_DOCS&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Awaiting insurance statement.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;444-55-6666&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;87000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;735&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;NOT_STARTED&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;35&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Not in queue.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;amir&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;N&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;105&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Owen Park&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;640000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Jumbo mortgage&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;UNDER_REVIEW&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Customer requested expedited review.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;555-66-7777&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;220000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;705&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PENDING_REVIEW&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;77&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Large loan amount.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;LINDA&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Y&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;106&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Sofia Reyes&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;390000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Investment property&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ESCALATED&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Escalated by branch manager.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;666-77-8888&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;132000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;660&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PENDING_REVIEW&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;83&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Exception review needed.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;raj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Y&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;policies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;General lending eligibility&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Baseline eligibility requirements for consumer lending applications.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;general&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0.75&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;])),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Income verification basics&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Required documents for income verification and employment review.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;general&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0.65&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;])),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Document retention requirements&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Retention periods for lending documents and customer communications.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;general&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0.30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.50&lt;/span&gt;&lt;span class="p"&gt;])),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Loan officer workflow checklist&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Loan officer workflow for intake, status updates, and customer follow-up.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;loan_officer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0.85&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;])),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Officer notes standards&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Standards for writing clear customer-facing officer notes.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;loan_officer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0.80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.15&lt;/span&gt;&lt;span class="p"&gt;])),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Credit risk escalation policy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;When credit risk signals require underwriting escalation.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;underwriter&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.90&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;])),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Debt-to-income exception review&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Underwriting guidance for debt-to-income ratio exceptions.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;underwriter&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0.10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.85&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;])),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Collateral review guidance&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Collateral review requirements for underwriting decisions.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;underwriter&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.75&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.20&lt;/span&gt;&lt;span class="p"&gt;])),&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;


&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;cur&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;executemany&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
        insert into loan_applications (
            id, customer_name, loan_amount, purpose, status, officer_notes,
            customer_ssn, customer_income, customer_credit_score,
            underwriting_decision, risk_score, underwriting_notes,
            assigned_officer, in_underwriting_queue
        ) values (:1, :2, :3, :4, :5, :6, :7, :8, :9, :10, :11, :12, :13, :14)
        &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;loans&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;executemany&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
        insert into loan_policies (id, title, body, audience, embedding)
        values (:1, :2, :3, :4, :5)
        &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;policies&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Inserted &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;loans&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; loan applications.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Inserted &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;policies&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; loan policy documents.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Synthetic data load complete.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python 03_load_data.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Inserted 6 loan applications.
Inserted 8 loan policy documents.
Synthetic data load complete.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before continuing, you should understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All data is synthetic.&lt;/li&gt;
&lt;li&gt;Sensitive columns are populated so column restrictions are visible.&lt;/li&gt;
&lt;li&gt;The vectors are deterministic demo vectors, not model-generated embeddings.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;audience&lt;/code&gt; column is a database policy anchor, not an application-side filter.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 5: Configure Oracle Deep Data Security data roles and data grants
&lt;/h3&gt;

&lt;p&gt;Now create the Deep Sec authorization model.&lt;/p&gt;

&lt;p&gt;This step assumes &lt;code&gt;linda&lt;/code&gt; and &lt;code&gt;wendy&lt;/code&gt; are prepared Deep Sec end users in your environment. The script creates data roles, grants those roles to the end users, and creates data grants on the two tutorial tables.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpzdl6wu8xtijktz7vqfc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpzdl6wu8xtijktz7vqfc.png" alt="Role-based access rules diagram showing Linda with the LOAN_OFFICER_ROLE and Wendy with the UNDERWRITER_ROLE, including each role's loan application row rule, restricted column behavior, and policy document access." width="800" height="325"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;DEAL access rules: Deep Sec data roles and data grants define which loan rows, restricted columns, and policy documents Linda and Wendy can access.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Create &lt;code&gt;04_configure_deepsec.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;deal_db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;connect&lt;/span&gt;


&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;cur&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Using prepared Deep Sec end users: LINDA, WENDY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;create data role loan_officer_role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Created data role: LOAN_OFFICER_ROLE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;create data role underwriter_role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Created data role: UNDERWRITER_ROLE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;grant data role loan_officer_role to linda&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Granted LOAN_OFFICER_ROLE to LINDA.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;grant data role underwriter_role to wendy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Granted UNDERWRITER_ROLE to WENDY.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;grants&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
        create or replace data grant deal_loan_officer_read as
        select (
            all columns except customer_ssn, customer_income,
            customer_credit_score, underwriting_decision,
            risk_score, underwriting_notes
        )
        on loan_applications
        where assigned_officer = ORA_END_USER_CONTEXT.username
        to loan_officer_role
        &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
        create or replace data grant deal_underwriter_read as
        select on loan_applications
        where in_underwriting_queue = &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Y&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;
        to underwriter_role
        &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
        create or replace data grant deal_policy_general_to_officer as
        select on loan_policies
        where audience = &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;general&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;
        to loan_officer_role
        &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
        create or replace data grant deal_policy_officer as
        select on loan_policies
        where audience = &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;loan_officer&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;
        to loan_officer_role
        &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
        create or replace data grant deal_policy_general_to_underwriter as
        select on loan_policies
        where audience = &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;general&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;
        to underwriter_role
        &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
        create or replace data grant deal_policy_underwriter as
        select on loan_policies
        where audience = &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;underwriter&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;
        to underwriter_role
        &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;grant&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;grants&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;grant&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Created loan application read grants.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Created loan policy read grants.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Configured Deep Data Security for DEAL.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it once against a fresh demo schema:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python 04_configure_deepsec.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Using prepared Deep Sec end users: LINDA, WENDY
Created data role: LOAN_OFFICER_ROLE
Created data role: UNDERWRITER_ROLE
Granted LOAN_OFFICER_ROLE to LINDA.
Granted UNDERWRITER_ROLE to WENDY.
Created loan application read grants.
Created loan policy read grants.
Configured Deep Data Security for DEAL.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the direct-logon role grants were waiting on the Deep Sec data roles, have the administrator run them now. Then enable mandatory data-grant enforcement on the two demo tables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="n"&gt;deal_direct_logon_role&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;loan_officer_role&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="n"&gt;deal_direct_logon_role&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;underwriter_role&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;USE&lt;/span&gt; &lt;span class="k"&gt;DATA&lt;/span&gt; &lt;span class="n"&gt;GRANTS&lt;/span&gt; &lt;span class="k"&gt;ONLY&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;loan_applications&lt;/span&gt; &lt;span class="n"&gt;ENABLED&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;USE&lt;/span&gt; &lt;span class="k"&gt;DATA&lt;/span&gt; &lt;span class="n"&gt;GRANTS&lt;/span&gt; &lt;span class="k"&gt;ONLY&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;loan_policies&lt;/span&gt; &lt;span class="n"&gt;ENABLED&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If this fails:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Confirm the tutorial schema has the required Deep Sec privileges.&lt;/li&gt;
&lt;li&gt;Confirm &lt;code&gt;linda&lt;/code&gt; and &lt;code&gt;wendy&lt;/code&gt; exist as Deep Sec end users in your prepared environment.&lt;/li&gt;
&lt;li&gt;Confirm your schema owns the demo tables or your administrator has prepared the object-owner model.&lt;/li&gt;
&lt;li&gt;Do not replace Deep Data Security with views, triggers, VPD, or Python filters for this tutorial.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Before continuing, you should understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Data roles are part of the Deep Data Security authorization model.&lt;/li&gt;
&lt;li&gt;Data grants define row and column access in the database.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;loan_policies.audience&lt;/code&gt; column is used only inside database policy grants.&lt;/li&gt;
&lt;li&gt;Application SQL still remains broad.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 6: Set end-user context and prove read and column enforcement
&lt;/h3&gt;

&lt;p&gt;Now add Deep Sec context support to the helper and prove that the same SQL returns different authorized results. In &lt;code&gt;direct_logon&lt;/code&gt; mode, the helper connects directly as &lt;code&gt;LINDA&lt;/code&gt; or &lt;code&gt;WENDY&lt;/code&gt; and qualifies protected objects through the owner schema.&lt;/p&gt;

&lt;p&gt;Add this function to the bottom of &lt;code&gt;deal_db.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;direct_logon_connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;passwords&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;linda&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;required_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DEEPSEC_LINDA_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wendy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;required_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DEEPSEC_WENDY_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;end_user&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;passwords&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Unknown DEAL end user: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;wallet_dir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;first_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ADB_WALLET_LOCATION&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DB_WALLET_DIR&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;dsn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;optional_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ADB_DSN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="nf"&gt;optional_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DB_DSN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="nf"&gt;wallet_dsn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wallet_dir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;oracledb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;passwords&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;dsn&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;dsn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;config_dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;wallet_dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;wallet_location&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;wallet_dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;wallet_password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;optional_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ADB_WALLET_PASSPHRASE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ADB_WALLET_PASSWORD&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DB_WALLET_PASSWORD&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run_for_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;optional_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DEEPSEC_CONTEXT_MODE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;direct_logon&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;direct_logon&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;direct_logon_connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;work&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;This tutorial validates DEEPSEC_CONTEXT_MODE=direct_logon.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;object_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;owner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;optional_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DEAL_OBJECT_OWNER&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DEEPSEC&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;owner&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This helper uses the prepared local-user passwords from &lt;code&gt;.env&lt;/code&gt;. The public Python functions still accept friendly names such as &lt;code&gt;"linda"&lt;/code&gt; and &lt;code&gt;"wendy"&lt;/code&gt;, then the helper connects with the unquoted database identifiers &lt;code&gt;LINDA&lt;/code&gt; and &lt;code&gt;WENDY&lt;/code&gt;. No &lt;code&gt;DEEPSEC_DATABASE_ACCESS_TOKEN&lt;/code&gt; is required in &lt;code&gt;direct_logon&lt;/code&gt; mode. In a production app, your authentication layer must still establish the real user before choosing which local Deep Sec user or service-user context to use.&lt;/p&gt;

&lt;p&gt;Create &lt;code&gt;05_context_read_demo.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;deal_db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;object_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;run_for_user&lt;/span&gt;


&lt;span class="n"&gt;SQL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
select *
from {loan_applications}
order by id
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;loan_applications&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;object_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;loan_applications&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;FIELDS_TO_SHOW&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;customer_ssn&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;customer_credit_score&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;underwriting_decision&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;risk_score&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;display_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;NULL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;rows_for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;work&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;cur&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SQL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;columns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchall&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;run_for_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;show&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;rows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;rows_for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;ids&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;As &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;  Rows returned: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;  Visible application ids: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ids&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;sample&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;  Selected sensitive and underwriting fields:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;FIELDS_TO_SHOW&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;    &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;display_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sample&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Read and column enforcement demo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;show&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;linda&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;show&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wendy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python 05_context_read_demo.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Read and column enforcement demo

As linda:
  Rows returned: 3
  Visible application ids: 101, 102, 105
  Selected sensitive and underwriting fields:
    customer_ssn: NULL
    customer_credit_score: NULL
    underwriting_decision: NULL
    risk_score: NULL

As wendy:
  Rows returned: 4
  Visible application ids: 102, 103, 105, 106
  Selected sensitive and underwriting fields:
    customer_ssn: 222-33-4444
    customer_credit_score: 695
    underwriting_decision: PENDING_REVIEW
    risk_score: 72
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same SQL is used for Linda and Wendy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;loan_applications&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is no &lt;code&gt;WHERE assigned_officer = :end_user&lt;/code&gt; predicate in the application.&lt;/p&gt;

&lt;p&gt;If this fails:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Confirm &lt;code&gt;DEEPSEC_CONTEXT_MODE=direct_logon&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Confirm Linda and Wendy can log on as local Deep Sec end users.&lt;/li&gt;
&lt;li&gt;Confirm &lt;code&gt;DEAL_OBJECT_OWNER&lt;/code&gt; points to the owner of the demo tables.&lt;/li&gt;
&lt;li&gt;Confirm data role grants and data grants exist.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Before continuing, you should understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The application does not add a user-specific &lt;code&gt;WHERE&lt;/code&gt; clause.&lt;/li&gt;
&lt;li&gt;The application does not redact restricted columns.&lt;/li&gt;
&lt;li&gt;The database returns the authorized row and column view for the active direct-logon Deep Sec end user.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 7: Demonstrate vector distance with manual vectors
&lt;/h3&gt;

&lt;p&gt;Before using vector retrieval as a RAG building block, inspect a simple vector query.&lt;/p&gt;

&lt;p&gt;A vector is a list of numbers. In AI applications, embedding models produce vectors that place related concepts near one another. In this tutorial, we use small deterministic vectors so you can see the mechanics directly.&lt;/p&gt;

&lt;p&gt;The query vector &lt;code&gt;[0.9, 0.1, 0.0]&lt;/code&gt; is closest to loan-officer workflow style documents in our demo data.&lt;/p&gt;

&lt;p&gt;Create &lt;code&gt;07_vector_basics.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;array&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;array&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;deal_db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;object_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;run_for_user&lt;/span&gt;


&lt;span class="n"&gt;query_vector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0.9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="n"&gt;sql&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
select title,
       vector_distance(embedding, :query_vector, COSINE) as distance
from {loan_policies}
order by distance
fetch first 3 rows only
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;loan_policies&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;object_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;loan_policies&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Vector warm-up with manual 3-dimensional vectors&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Vector warm-up user context: linda&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Query vector: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query_vector&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;work&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;cur&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;query_vector&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;query_vector&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Nearest policy vectors:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;distance&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;. &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;35&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; distance: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;distance&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nf"&gt;run_for_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;linda&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python 07_vector_basics.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Vector warm-up with manual 3-dimensional vectors
Vector warm-up user context: linda
Query vector: [0.8999999761581421, 0.10000000149011612, 0.0]

Nearest policy vectors:
1. Loan officer workflow checklist     distance: 0.001723
2. General lending eligibility         distance: 0.013266
3. Officer notes standards             distance: 0.018206
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your exact distances may vary slightly by database patch level or floating-point formatting; the important result is the role-scoped result set.&lt;/p&gt;

&lt;p&gt;A quick metric mental model:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cosine distance&lt;/strong&gt; compares vector direction and is common for embedding-style retrieval.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Euclidean distance&lt;/strong&gt; compares straight-line distance in vector space.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dot product&lt;/strong&gt; is often used as a similarity score and is sensitive to vector magnitude.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Production embedding models may normalize vectors or document how they should be compared. Normalization can affect cosine, Euclidean, and dot-product rankings. DEAL uses one explicit &lt;code&gt;VECTOR_DISTANCE()&lt;/code&gt; expression so the query shape stays clear.&lt;/p&gt;

&lt;p&gt;Oracle AI Vector Search also supports vector indexes for larger corpora, including HNSW-style neighbor graph indexes and IVF/IVFFlat-style partitioned indexes. DEAL does not create an index because this security proof has eight policy rows; when you scale the corpus, validate a &lt;code&gt;CREATE VECTOR INDEX&lt;/code&gt; statement for your metric and inspect index metadata such as &lt;code&gt;USER_INDEXES&lt;/code&gt; before relying on performance claims.&lt;/p&gt;

&lt;p&gt;Before continuing, you should understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;VECTOR(3, FLOAT32)&lt;/code&gt; stores three &lt;code&gt;FLOAT32&lt;/code&gt; values.&lt;/li&gt;
&lt;li&gt;Python binds the query vector with &lt;code&gt;array.array("f", [...])&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The primitive vector example comes before any embedding model or chatbot tooling.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 8: Run Deep Sec-scoped vector retrieval
&lt;/h3&gt;

&lt;p&gt;Now run a vector query that represents a RAG-style policy search. The search intent is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;unstable cash flow or credit risk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The tutorial maps that intent to a fixed demo vector. In a real AI application, an embedding model such as OCI Generative AI would create the query vector and document vectors at the model’s actual dimension.&lt;/p&gt;

&lt;p&gt;Create &lt;code&gt;08_secure_rag_retrieval.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;array&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;array&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;deal_db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;object_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;run_for_user&lt;/span&gt;


&lt;span class="n"&gt;query_text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;unstable cash flow or credit risk&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;query_vector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="n"&gt;sql&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
select id, title, audience,
       vector_distance(embedding, :query_vector, COSINE) as distance
from {loan_policies}
order by distance
fetch first 3 rows only
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;loan_policies&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;object_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;loan_policies&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;search_as&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;work&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;cur&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;query_vector&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;query_vector&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchall&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;run_for_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Deep Sec-scoped vector retrieval demo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Policy search query: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;query_text&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Output below is scoped by the active Deep Sec end user.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;end_user&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;linda&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wendy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;As &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;search_as&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;. &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;35&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; distance: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python 08_secure_rag_retrieval.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Deep Sec-scoped vector retrieval demo
Policy search query: unstable cash flow or credit risk
Output below is scoped by the active Deep Sec end user.

As linda:
  1. Income verification basics          distance: 0.529221
  2. Document retention requirements     distance: 0.604677
  3. General lending eligibility         distance: 0.686696

As wendy:
  1. Credit risk escalation policy      distance: 0.000000
  2. Debt-to-income exception review    distance: 0.001895
  3. Collateral review guidance         distance: 0.020925
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If this fails:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Confirm Deep Sec grants exist on &lt;code&gt;loan_policies&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Confirm the active end-user context is set.&lt;/li&gt;
&lt;li&gt;Confirm the vector query runs against the protected table.&lt;/li&gt;
&lt;li&gt;Confirm no application-side &lt;code&gt;audience&lt;/code&gt; filter has been added.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Before continuing, you should understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The same query vector is used for Linda and Wendy.&lt;/li&gt;
&lt;li&gt;The same SQL shape is used for Linda and Wendy.&lt;/li&gt;
&lt;li&gt;The SQL does not filter by &lt;code&gt;audience&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The Python code does not filter by &lt;code&gt;audience&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Deep Sec data grants govern which policy rows are available to the vector query.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Optional: Compare keyword and vector-style retrieval
&lt;/h3&gt;

&lt;p&gt;Vector retrieval and keyword retrieval answer different relevance questions. Keyword search finds literal text matches. Vector retrieval can find nearby concepts represented by numbers.&lt;/p&gt;

&lt;p&gt;Neither one should become the security boundary. Run both retrieval styles under the same Deep Sec contexts.&lt;/p&gt;

&lt;p&gt;Create &lt;code&gt;08b_keyword_vs_vector_check.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;array&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;array&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;deal_db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;object_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;run_for_user&lt;/span&gt;


&lt;span class="n"&gt;query_vector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="n"&gt;vector_sql&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
select title
from {loan_policies}
order by vector_distance(embedding, :query_vector, COSINE)
fetch first 3 rows only
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;loan_policies&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;object_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;loan_policies&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;keyword_sql&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
select title
from {loan_policies}
where lower(body) like :term
order by id
fetch first 3 rows only
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;loan_policies&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;object_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;loan_policies&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run_as&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;work&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;cur&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vector_sql&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;query_vector&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;query_vector&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;vector_titles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchall&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;

        &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;keyword_sql&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;term&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;%credit risk%&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;keyword_titles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchall&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;vector_titles&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;keyword_titles&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;run_for_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Optional vector-style vs keyword retrieval check&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;end_user&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;linda&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wendy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="n"&gt;vector_titles&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;keyword_titles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;run_as&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;As &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;  Vector-style titles: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vector_titles&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;  Keyword titles for &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;credit risk&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;keyword_titles&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;keyword_titles&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;none&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it if you want the extra comparison:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python 08b_keyword_vs_vector_check.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Representative output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Optional vector-style vs keyword retrieval check

As linda:
  Vector-style titles: Income verification basics, Document retention requirements, General lending eligibility
  Keyword titles for 'credit risk': none

As wendy:
  Vector-style titles: Credit risk escalation policy, Debt-to-income exception review, Collateral review guidance
  Keyword titles for 'credit risk': Credit risk escalation policy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before continuing, you should understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keyword search and vector retrieval are relevance strategies.&lt;/li&gt;
&lt;li&gt;Deep Sec policy still scopes the rows available to the query.&lt;/li&gt;
&lt;li&gt;The app still does not use an &lt;code&gt;audience&lt;/code&gt; security filter.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Try this next: Wrap the proven operations as DEAL tool functions
&lt;/h3&gt;

&lt;p&gt;Only after proving the database operations directly should we wrap them as assistant tools.&lt;/p&gt;

&lt;p&gt;Create &lt;code&gt;deal_tools.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;array&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;array&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;deal_db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;object_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;run_for_user&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_rows_as_dicts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;columns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchall&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_run_for_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;run_for_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_query_vector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;risk&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;credit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cash flow&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0.9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;_query_vector()&lt;/code&gt; function is relevance logic for this deterministic demo. It is not authorization logic.&lt;/p&gt;

&lt;p&gt;Append the read tools:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_loan_applications&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;work&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;cur&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
            select *
            from &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;object_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;loan_applications&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
            order by id
            &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;_rows_as_dicts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;_run_for_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_application_detail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;app_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;work&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;cur&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
            select *
            from &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;object_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;loan_applications&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
            where id = :id
            &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;app_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;rows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;_rows_as_dicts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;_run_for_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;WHERE id = :id&lt;/code&gt; predicate is a business lookup. It is not a user-specific authorization filter.&lt;/p&gt;

&lt;p&gt;Append the policy-search tool:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;search_policies&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;query_vector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;_query_vector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;work&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;cur&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
            select id, title, body, audience,
                   vector_distance(embedding, :query_vector, COSINE) as distance
            from &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;object_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;loan_policies&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
            order by distance
            fetch first 3 rows only
            &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;query_vector&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;query_vector&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;_rows_as_dicts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;_run_for_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create &lt;code&gt;09_deal_tools_demo.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;deal_tools&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;get_loan_applications&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;search_policies&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Tool demo: linda&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;linda_loans&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_loan_applications&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;linda&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;linda_policies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;search_policies&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;linda&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;credit risk&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;  get_loan_applications returned &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;linda_loans&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; rows.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;  search_policies returned &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;linda_policies&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; policy documents.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Tool demo: wendy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;wendy_loans&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_loan_applications&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wendy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;wendy_policies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;search_policies&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wendy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;credit risk&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;  get_loan_applications returned &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wendy_loans&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; rows.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;  search_policies returned &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wendy_policies&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; policy documents.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python 09_deal_tools_demo.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Tool demo: linda
  get_loan_applications returned 3 rows.
  search_policies returned 3 policy documents.

Tool demo: wendy
  get_loan_applications returned 4 rows.
  search_policies returned 3 policy documents.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before continuing, you should understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Each tool runs database work under the requested Deep Sec end user.&lt;/li&gt;
&lt;li&gt;In direct-logon mode, each user-scoped operation opens its own connection as &lt;code&gt;LINDA&lt;/code&gt; or &lt;code&gt;WENDY&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Tools do not filter rows by user.&lt;/li&gt;
&lt;li&gt;Tools do not redact restricted columns.&lt;/li&gt;
&lt;li&gt;Tools do not filter policy documents by &lt;code&gt;audience&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Try this next: Run DEAL sessions and bypass checks
&lt;/h3&gt;

&lt;p&gt;Now run the final terminal assistant session. This script calls the tool functions and prints a short transcript for Linda, Wendy, and a bypass check.&lt;/p&gt;

&lt;p&gt;Create &lt;code&gt;10_run_deal_sessions.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;deal_tools&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;get_application_detail&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;get_loan_applications&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;search_policies&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;display_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;NULL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;titles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;linda_session&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;========================&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DEAL session: linda&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;========================&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;loans&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_loan_applications&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;linda&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;ids&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;loans&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;detail&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_application_detail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;linda&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;102&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;policies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;search_policies&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;linda&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;credit risk&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Visible applications: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;loans&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Application ids: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ids&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Restricted risk_score: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;display_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;detail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;risk_score&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Policy results: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;titles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;policies&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;wendy_session&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;========================&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DEAL session: wendy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;========================&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;loans&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_loan_applications&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wendy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;ids&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;loans&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;detail&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_application_detail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wendy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;102&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;policies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;search_policies&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wendy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;credit risk&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Visible applications: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;loans&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Application ids: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ids&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Underwriting risk_score: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;display_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;detail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;risk_score&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Policy results: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;titles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;policies&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Append the bypass check and runner:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;bypass_check&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;========================&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bypass check&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;========================&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;linda_rows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_loan_applications&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;linda&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;wendy_rows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_loan_applications&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wendy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;linda_docs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;search_policies&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;linda&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;credit risk&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;wendy_docs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;search_policies&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wendy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;credit risk&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Broad loan query returned &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;linda_rows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; Linda-scoped rows for linda.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Broad loan query returned &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wendy_rows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; Wendy-scoped rows for wendy.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Linda policy titles: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;titles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;linda_docs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Wendy policy titles: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;titles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wendy_docs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;No application-side row filter, redaction, or audience filter was used.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nf"&gt;linda_session&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nf"&gt;wendy_session&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nf"&gt;bypass_check&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python 10_run_deal_sessions.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;========================
DEAL session: linda
========================
Visible applications: 3
Application ids: 101, 102, 105
Restricted risk_score: NULL
Policy results: Income verification basics, Document retention requirements, General lending eligibility

========================
DEAL session: wendy
========================
Visible applications: 4
Application ids: 102, 103, 105, 106
Underwriting risk_score: 72
Policy results: Credit risk escalation policy, Debt-to-income exception review, Collateral review guidance

========================
Bypass check
========================
Broad loan query returned 3 Linda-scoped rows for linda.
Broad loan query returned 4 Wendy-scoped rows for wendy.
Linda policy titles: Income verification basics, Document retention requirements, General lending eligibility
Wendy policy titles: Credit risk escalation policy, Debt-to-income exception review, Collateral review guidance
No application-side row filter, redaction, or audience filter was used.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdkin7af9us1khpmxjk79.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdkin7af9us1khpmxjk79.png" alt="Terminal-style output showing the final DEAL demo session, where Linda sees three authorized loan applications with restricted underwriting fields, Wendy sees four underwriting queue applications with underwriting fields visible, and bypass checks show broad loan and policy retrieval queries remain scoped by Deep Sec policies." width="800" height="315"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Final DEAL session: the same tool functions run under Linda and Wendy’s Deep Sec context, and the database returns user-scoped loan and policy retrieval results.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Before finishing, you should understand the full trust boundary:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;DEAL establishes the correct Deep Sec end user for each operation.&lt;/li&gt;
&lt;li&gt;The same broad read function returns different rows and restricted values.&lt;/li&gt;
&lt;li&gt;The same vector retrieval function returns different policy documents.&lt;/li&gt;
&lt;li&gt;The assistant is not trusted to filter or redact protected data.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Optional: Add an OCI GenAI chatbot loop
&lt;/h3&gt;

&lt;p&gt;The terminal session proves the secured tool functions directly. As an optional extension, add a small LLM loop that lets OCI Generative AI choose which DEAL tool to call. The OCI GenAI code path is an integration pattern; the Deep Sec read and vector security proof does not depend on it. Validate the OCI SDK request and response shape in your tenancy before relying on a captured chatbot transcript.&lt;/p&gt;

&lt;p&gt;The model is not trusted for authorization; it only selects an action. Every tool call still runs under the chosen Deep Sec end user and lets Oracle Database enforce the policy.&lt;/p&gt;

&lt;p&gt;Create &lt;code&gt;11_oci_genai_chatbot.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pathlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;oci&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dotenv&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_dotenv&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;oci.generative_ai_inference&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;GenerativeAiInferenceClient&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;oci.generative_ai_inference.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;ChatDetails&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;GenericChatRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;OnDemandServingMode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;UserMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;deal_tools&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;get_application_detail&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;get_loan_applications&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;search_policies&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nf"&gt;load_dotenv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;


&lt;span class="n"&gt;TOOLS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;get_loan_applications&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;get_loan_applications&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;get_application_detail&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;get_application_detail&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;search_policies&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;search_policies&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;required_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'"'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"'"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;your-&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Set &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; in .env before running this script.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;oci_client&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;config_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OCI_CONFIG_FILE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;~/.oci/config&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;expanduser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;profile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OCI_PROFILE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DEFAULT&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;oci&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config_file&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;GenerativeAiInferenceClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;service_endpoint&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;required_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OCI_GENAI_ENDPOINT&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;message_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chat_response&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chat_response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;hasattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;chat_response&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="nf"&gt;hasattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chat_response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;choices&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;choice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chat_response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;choice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;oci&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;util&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;chat_once&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;GenericChatRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;api_format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;GenericChatRequest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;API_FORMAT_GENERIC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="nc"&gt;UserMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ROLE_USER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;TEXT&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;max_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;details&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ChatDetails&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;compartment_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;required_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OCI_GENAI_COMPARTMENT_ID&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;serving_mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;OnDemandServingMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;required_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OCI_GENAI_MODEL_ID&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
        &lt;span class="n"&gt;chat_request&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;message_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;details&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;choose_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
You are DEAL, a lending assistant running as end user &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.
Choose exactly one tool call for the user&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s request.
Return only JSON with keys tool and arguments.

Available tools:
- get_loan_applications: {{}}
- get_application_detail: {{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;app_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: number}}
- search_policies: {{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;query&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: string}}

User request: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;chat_once&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;JSONDecodeError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;fenced&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;```

(?:json)?\s*(\{.*?\})\s*

```&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DOTALL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;fenced&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fenced&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rfind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run_tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;tool_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tool&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;arguments&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;arguments&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;tool_name&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;TOOLS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Unknown tool requested by model: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;tool_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;TOOLS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;tool_name&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;answer_with_result&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
You are DEAL, a lending assistant running as end user &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.
The database has already enforced Oracle Deep Data Security.
Answer the user briefly from the tool result only.
If the tool result is a list of loan applications, count the list and list the id values exactly.
If the tool result is a list of policies, list only the returned policy titles.
Do not say that no rows or policies were found unless the tool result is an empty list.

User request: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
Tool action: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
Tool result: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;chat_once&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run_session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;questions&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;========================&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OCI GenAI DEAL session: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;========================&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;question&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;questions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;choose_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;run_tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;answer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;answer_with_result&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;User: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Tool: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;tool&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;arguments&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="si"&gt;{}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DEAL: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;answer&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;oci_client&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nf"&gt;run_session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;linda&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Which loan applications can I see?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Find policy guidance about credit risk.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;run_session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wendy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Which applications are in my underwriting queue?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Find policy guidance about credit risk.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it after the direct tool demo succeeds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python 11_oci_genai_chatbot.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;========================
OCI GenAI DEAL session: linda
========================

User: Which loan applications can I see?
Tool: get_loan_applications {}
DEAL: You can see 3 loan applications: 101, 102, 105.

User: Find policy guidance about credit risk.
Tool: search_policies {'query': 'credit risk'}
DEAL: Income verification basics, Document retention requirements, General lending eligibility

========================
OCI GenAI DEAL session: wendy
========================

User: Which applications are in my underwriting queue?
Tool: get_loan_applications {}
DEAL: There are 4 applications in your underwriting queue: 102, 103, 105, 106.

User: Find policy guidance about credit risk.
Tool: search_policies {'query': 'credit risk'}
DEAL: Credit risk escalation policy
Debt-to-income exception review
Collateral review guidance
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important part is the tool transcript, not the wording of the generated answer. If the model asks for a broad loan query, broad application detail, or broad policy search, Oracle Database still scopes the result to the active Deep Sec end user.&lt;/p&gt;

&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The connection fails
&lt;/h3&gt;

&lt;p&gt;Check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ADB_DSN&lt;/code&gt; matches your ADB-S wallet alias or connect descriptor, or the wallet contains a usable &lt;code&gt;tnsnames.ora&lt;/code&gt; alias.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ADB_WALLET_LOCATION&lt;/code&gt; points to the unzipped wallet directory.&lt;/li&gt;
&lt;li&gt;The directory includes wallet files used by Thin mode, such as &lt;code&gt;tnsnames.ora&lt;/code&gt; and &lt;code&gt;ewallet.pem&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ADB_WALLET_PASSPHRASE&lt;/code&gt; is the wallet password, not the database password.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Do not call &lt;code&gt;oracledb.init_oracle_client()&lt;/code&gt; for this tutorial.&lt;/p&gt;

&lt;h3&gt;
  
  
  The driver is in Thick mode
&lt;/h3&gt;

&lt;p&gt;Deep Data Security in this Python tutorial uses &lt;code&gt;python-oracledb&lt;/code&gt; Thin mode. Remove any call to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;oracledb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init_oracle_client&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then rerun &lt;code&gt;01_verify_deepsec.py&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Linda and Wendy see the same loan rows
&lt;/h3&gt;

&lt;p&gt;Check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;DEEPSEC_CONTEXT_MODE=direct_logon&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DEEPSEC_LINDA_KEY&lt;/code&gt; and &lt;code&gt;DEEPSEC_WENDY_KEY&lt;/code&gt; contain the local Deep Sec end-user passwords.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DEAL_OBJECT_OWNER&lt;/code&gt; points to the schema that owns &lt;code&gt;loan_applications&lt;/code&gt; and &lt;code&gt;loan_policies&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Data roles were granted to the correct Deep Sec end users.&lt;/li&gt;
&lt;li&gt;Data grants reference the expected Deep Sec username.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Vector inserts or queries fail
&lt;/h3&gt;

&lt;p&gt;Check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The table uses &lt;code&gt;VECTOR(3, FLOAT32)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Every vector bind uses exactly three values.&lt;/li&gt;
&lt;li&gt;Python binds vectors with &lt;code&gt;array.array("f", [...])&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The vector query uses the same metric expression consistently.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  You plan to use connection pools
&lt;/h3&gt;

&lt;p&gt;The tutorial uses simple standalone connections so the request boundary is obvious. If you later switch to the service-user context path with a pool, set the Deep Sec context after acquiring a connection and clear it before releasing the connection. Keep this pattern:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;acquire&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;context_for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_end_user_security_context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# Run user-scoped database work.
&lt;/span&gt;&lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clear_end_user_security_context&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  You plan to replace demo vectors with real embeddings
&lt;/h3&gt;

&lt;p&gt;The core tutorial uses deterministic demo vectors so the security proof is self-contained. When you switch to a real embedding API:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use the model’s actual vector dimension in the table definition.&lt;/li&gt;
&lt;li&gt;Batch embedding requests where the provider supports batching.&lt;/li&gt;
&lt;li&gt;Track API cost and rate limits.&lt;/li&gt;
&lt;li&gt;Store enough metadata to regenerate embeddings when the model changes.&lt;/li&gt;
&lt;li&gt;Keep authorization in Deep Sec data grants, not in embedding metadata filters.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Related Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/markxnelson/deep-data-security" rel="noopener noreferrer"&gt;Companion DEAL demo code repository&lt;/a&gt; — Clone the runnable Python project used in this tutorial.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/ddscg/index.html" rel="noopener noreferrer"&gt;Oracle Deep Data Security Guide for Oracle AI Database 26ai&lt;/a&gt; — Learn the Deep Data Security concepts, objects, and administration model behind the tutorial.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/ddscg/create-data-grants.html" rel="noopener noreferrer"&gt;Create Data Grants in Oracle Deep Data Security&lt;/a&gt; — Review how data grants define row, column, and operation access for protected tables.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/ddscg/configure-data-roles-l.html" rel="noopener noreferrer"&gt;Configure Data Roles in Oracle Deep Data Security&lt;/a&gt; — See how Deep Sec data roles are created and assigned to end users.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://python-oracledb.readthedocs.io/en/latest/user_guide/connection_handling.html#deep-data-security" rel="noopener noreferrer"&gt;python-oracledb Deep Data Security&lt;/a&gt; — Use the Python driver APIs for creating, setting, and clearing end-user security context.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://paul-parkinson.medium.com/develop-database-enforced-end-user-auth-with-oracle-ai-database-deep-data-security-and-java-5a845ba1ebfd" rel="noopener noreferrer"&gt;Paul Parkinson's Deep Data Security Java article&lt;/a&gt; — Compare the same Deep Sec application model in a Java application.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://livelabs.oracle.com/ords/r/dbpm/livelabs/run-workshop?p210_wid=4393" rel="noopener noreferrer"&gt;Oracle LiveLabs: Getting Started with Oracle Deep Data Security&lt;/a&gt; — Try a guided Deep Data Security workshop.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/vecse/index.html" rel="noopener noreferrer"&gt;Oracle AI Vector Search User's Guide&lt;/a&gt; — Review Oracle AI Vector Search concepts and SQL reference material.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://python-oracledb.readthedocs.io/en/latest/user_guide/vector_data_type.html" rel="noopener noreferrer"&gt;python-oracledb Vector Data Type&lt;/a&gt; — Learn how Python applications bind and fetch Oracle Database &lt;code&gt;VECTOR&lt;/code&gt; values.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;You built DEAL, a Python loan assistant that demonstrates database-enforced access control for structured SQL, vector retrieval, and LLM-driven tool calls. Linda and Wendy use the same broad tool functions, but Oracle Database applies the active Deep Sec end-user identity before returning loan rows, restricted columns, or policy documents.&lt;/p&gt;

&lt;p&gt;That matters because AI applications widen the blast radius of small authorization mistakes. A chatbot can call a broad query, a tool can forget a role predicate, and a prompt can ask for data the user should not receive. With Deep Data Security, the tutorial's security boundary sits in Oracle Database instead of in prompts, redaction branches, or application-side filters.&lt;/p&gt;

&lt;p&gt;For production, replace the deterministic demo vectors with embeddings from your chosen model, test the workflow in your own ADB-S environment, and add auditing around high-risk read and retrieval paths. Keep the same rule as you evolve the assistant: the model and application can request data, but the database decides what the end user is allowed to see.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>database</category>
      <category>rag</category>
      <category>security</category>
    </item>
    <item>
      <title>Vector‑native RAG on Oracle: embeddings, HNSW/IVF, and hybrid search under database governance</title>
      <dc:creator>Mark Nelson</dc:creator>
      <pubDate>Wed, 27 May 2026 10:38:06 +0000</pubDate>
      <link>https://dev.to/oracledevs/vector-native-rag-on-oracle-embeddings-hnswivf-and-hybrid-search-under-database-governance-2ep1</link>
      <guid>https://dev.to/oracledevs/vector-native-rag-on-oracle-embeddings-hnswivf-and-hybrid-search-under-database-governance-2ep1</guid>
      <description>&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Storing vectors in an Oracle &lt;code&gt;VECTOR&lt;/code&gt; column alongside content, metadata, and provenance means retrieval happens inside the database. Existing governance — row-level security, auditing, data masking — applies to vector queries the same way it applies to any other query.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Hybrid retrieval is ordinary SQL: &lt;code&gt;VECTOR_DISTANCE&lt;/code&gt; handles semantic similarity and &lt;code&gt;WHERE&lt;/code&gt; clauses handle business predicates in the same statement. Any reviewer who can read a query can understand what rows qualified and why.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;HNSW and IVF are index strategies with real trade-offs in recall, memory footprint, and query latency. Choosing between them requires measuring both on your own corpus — they are not interchangeable defaults.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The answer step can stay inspectable too. Connecting retrieval to a Select AI profile with &lt;code&gt;SHOWSQL&lt;/code&gt; means the SQL generation step is reviewable, not just the retrieval that fed it.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Retrieval‑augmented generation works when you can fetch the right context quickly and explain—in plain SQL—why those rows qualified. Splitting storage between a stand‑alone vector store and the database of record speeds early prototypes but makes governance fragile: metadata is duplicated, filters are reimplemented, and audit trails often end at a gateway you do not control. Oracle’s vector‑native approach keeps the entire RAG path inside the database. Embeddings live in a VECTOR column next to the text, JSON, and provenance you already track; you index them with HNSW or IVF using CREATE VECTOR INDEX; you retrieve with SQL that combines semantic similarity and business predicates. Because retrieval runs on database objects, existing policies and audits—when configured on those objects—stay in the path. When you need an LLM to compose the answer, Select AI with AI Profiles lets you scope what the model can see and inspect generated SQL before execution. The result is a retrieval path that is fast enough for interactive use and reviewable under the same governance you already operate.&lt;/p&gt;

&lt;p&gt;Figure 1 — Route → act → trust for in‑database RAG. Retrieval is SQL; governance stays in the path.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F62gf3cmu38067bt9zmod.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F62gf3cmu38067bt9zmod.png" alt="Vector-Native RAG in One Database" width="653" height="1024"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you read Article 5, you saw how AI Profiles keep NL2SQL inspectable. This article focuses on retrieval: how to store vectors, choose HNSW or IVF, and write hybrid queries you can defend in a review meeting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Oracle AI Database 26ai or Autonomous Database with AI Vector Search enabled for your RU/service tier.&lt;/li&gt;
&lt;li&gt;Privileges to CREATE TABLE and CREATE INDEX in a schema with sufficient quota.&lt;/li&gt;
&lt;li&gt;For external embeddings or Select AI, configure credentials and network ACLs per your organization’s policy.&lt;/li&gt;
&lt;li&gt;Verify CREATE VECTOR INDEX options and the DBMS_* package signatures in your RU before automating builds.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What “vector‑native” means in Oracle
&lt;/h2&gt;

&lt;p&gt;Vector‑native means embeddings are first‑class database data, not a sidecar. You add a &lt;code&gt;VECTOR(dim, element_type)&lt;/code&gt; column to the table that holds your chunked content and provenance; you build an approximate nearest‑neighbor (ANN) index—HNSW for interactive recall or IVF for very large corpora—and you query with SQL. The practical effect is governance continuity: when Virtual Private Database (VPD) policies, Unified Auditing, Data Redaction, and Transparent Data Encryption (TDE) are configured on the relevant objects, they continue to filter, record, mask, and protect your retrieval path. When someone asks why a row entered the context window, you can point to the same table‑scoped policies that govern every other query.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fots7aelunrssekyy8e7j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fots7aelunrssekyy8e7j.png" alt="Oracle Vector Objects" width="800" height="409"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For architecture and security details, see the Oracle AI Vector Search overview and the Database Security Guide for VPD, Unified Auditing, Data Redaction, and TDE:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/vecse/overview-ai-vector-search.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/vecse/overview-ai-vector-search.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/dbseg" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/dbseg&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  From document to chunk to embedding
&lt;/h2&gt;

&lt;p&gt;RAG quality depends heavily on two choices you control: where you cut chunks and how you embed them. Oracle provides &lt;code&gt;DBMS_VECTOR_CHAIN&lt;/code&gt; utilities for deterministic chunking with tunable size and overlap, so the same document splits the same way as you iterate. For embeddings, you can run an in‑database model (see DBMS_VECTOR support for installed models and ONNX where documented for your RU) or call an external provider with credentials stored in the database and governed by network ACLs. Treat those decisions as versioned preferences you can update deliberately.&lt;/p&gt;

&lt;p&gt;Keep the storage shape simple: one row per chunk with the content, the provenance you already carry (source, timestamps, tenant), and a &lt;code&gt;VECTOR&lt;/code&gt; column for the embedding. That single‑row design makes hybrid retrieval straightforward because eligibility is a &lt;code&gt;WHERE&lt;/code&gt; clause and semantic similarity is an &lt;code&gt;ORDER BY&lt;/code&gt;, all in one statement.&lt;/p&gt;

&lt;p&gt;Docs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;DBMS_VECTOR (embedding/model support)
&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/arpls/dbms_vector1.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/arpls/dbms_vector1.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;DBMS_VECTOR_CHAIN (chunking and embedding helpers)
&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/arpls/dbms_vector_chain1.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/arpls/dbms_vector_chain1.html&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  DBMS_HYBRID_VECTOR: keyword + semantic in one call
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;DBMS_VECTOR_CHAIN&lt;/code&gt; and &lt;code&gt;DBMS_VECTOR&lt;/code&gt; handle pure vector workflows. When your retrieval also needs full‑text keyword matching—relevance ranking by term frequency, Oracle Text lexer rules, or multi‑language tokenization—&lt;code&gt;DBMS_HYBRID_VECTOR&lt;/code&gt; adds that layer. The package provides a JSON‑based &lt;code&gt;SEARCH&lt;/code&gt; API that searches by keywords and vectors against hybrid vector indexes. By integrating traditional keyword‑based text search with vector‑based similarity search in a single call, it can improve recall for queries where either approach alone misses relevant results.&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;DBMS_HYBRID_VECTOR&lt;/code&gt; when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your corpus has mixed-quality writing (exact keywords matter for short queries, semantic similarity matters for paraphrased ones).&lt;/li&gt;
&lt;li&gt;You support multiple languages with different stemming rules that Oracle Text already handles.&lt;/li&gt;
&lt;li&gt;You want a single managed search API rather than hand-tuned SQL merging of separate text and vector result sets.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For pure vector retrieval with business-predicate filters, the &lt;code&gt;WHERE&lt;/code&gt; + &lt;code&gt;VECTOR_DISTANCE&lt;/code&gt; pattern in the section below is simpler and sufficient. Reserve &lt;code&gt;DBMS_HYBRID_VECTOR&lt;/code&gt; for the cases where keyword recall meaningfully improves results on your corpus—measure both before committing to the more complex path.&lt;/p&gt;

&lt;p&gt;Doc:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;DBMS_HYBRID_VECTOR
&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/arpls/dbms_hybrid_vector1.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/arpls/dbms_hybrid_vector1.html&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Choosing an index: HNSW or IVF
&lt;/h2&gt;

&lt;p&gt;HNSW and IVF both deliver sub‑second nearest‑neighbor search, but they trade memory, build time, and recall differently.&lt;/p&gt;

&lt;p&gt;HNSW builds an in‑memory navigable small‑world graph. In practice, it often yields high recall at low latency for interactive workloads, at the cost of a memory footprint that grows with corpus size and neighbor degree. Use the documented memory and accuracy advisors in &lt;code&gt;DBMS_VECTOR&lt;/code&gt; to size the index pool and validate recall on your corpus before you build. If you are powering a help center, runbooks, or an internal knowledge base where queries arrive piecemeal and you value accuracy, HNSW is a common starting point—measure and tune for your data.&lt;/p&gt;

&lt;p&gt;IVF clusters the vector space into partitions and probes only a subset per query. That design can lower memory footprint and sustain good throughput on very large corpora. You trade some recall and tune it by changing how many partitions to probe. If you manage millions of chunks and steady traffic, evaluate IVF operationally using Oracle’s accuracy and performance advisors.&lt;/p&gt;

&lt;p&gt;One optimizer detail matters to plans and expectations: the optimizer uses a vector index only when your query’s distance metric matches the index’s metric. If they differ, the index will not be used and an exact search is performed. If your embeddings use cosine similarity (common for text), create the index with &lt;code&gt;DISTANCE COSINE&lt;/code&gt; and use the same metric in &lt;code&gt;ORDER BY&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flggcwksmxyi8hle0l3ii.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flggcwksmxyi8hle0l3ii.png" alt="Choosing a Vector Index" width="742" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Docs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CREATE VECTOR INDEX (HNSW/IVF)
&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/create-vector-index.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/create-vector-index.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;DBMS_VECTOR advisors and accuracy helpers
&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/arpls/dbms_vector1.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/arpls/dbms_vector1.html&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Writing hybrid retrieval you can defend
&lt;/h2&gt;

&lt;p&gt;Hybrid search in Oracle is just SQL. Your business predicates—tenant, source system, publish date, access level—are ordinary filters in &lt;code&gt;WHERE&lt;/code&gt;, and &lt;code&gt;VECTOR_DISTANCE&lt;/code&gt; ranks eligible rows by semantic similarity. Because this happens on a single table with a single row shape, the execution plan can combine a vector index for similarity with conventional indexes for filters. When configured on the relevant objects, VPD policies still apply to the session.&lt;/p&gt;

&lt;p&gt;A typical query looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="n"&gt;documents&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;  &lt;span class="k"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'kb'&lt;/span&gt;
&lt;span class="k"&gt;AND&lt;/span&gt;    &lt;span class="n"&gt;published&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nb"&gt;DATE&lt;/span&gt; &lt;span class="s1"&gt;'2025-01-01'&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt;  &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;VECTOR_DISTANCE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;qvec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;COSINE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;FETCH&lt;/span&gt;  &lt;span class="k"&gt;FIRST&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="k"&gt;ROWS&lt;/span&gt; &lt;span class="k"&gt;ONLY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ensure the index’s &lt;code&gt;DISTANCE&lt;/code&gt; metric matches the query’s metric; otherwise the vector index may not be used.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6aes9wheh80hk6opoyxk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6aes9wheh80hk6opoyxk.png" alt="Hybrid Retrieval You Can Review" width="800" height="361"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Two small details avoid surprises in production. &lt;code&gt;VECTOR_DISTANCE&lt;/code&gt; returns smaller values for closer vectors, so you sort ascending and keep the metric explicit to match the index. Also, the query vector’s dimension and element type must match the column’s or the statement will error. In plans, you will often see the vector index satisfy similarity ordering while B‑trees or partitions satisfy filters; the optimizer’s choice depends on predicate selectivity, statistics, and metric/index alignment.&lt;/p&gt;

&lt;p&gt;If you prefer operator syntax, the &lt;code&gt;&amp;lt;=&amp;gt;&lt;/code&gt; operator is the cosine distance operator in 26ai. Use it only if your target version documents it; otherwise prefer &lt;code&gt;VECTOR_DISTANCE(..., COSINE)&lt;/code&gt; explicitly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="n"&gt;documents&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt;  &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;embedding&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;qvec&lt;/span&gt;
&lt;span class="k"&gt;FETCH&lt;/span&gt;  &lt;span class="k"&gt;FIRST&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="k"&gt;ROWS&lt;/span&gt; &lt;span class="k"&gt;ONLY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Docs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;VECTOR, TO_VECTOR, VECTOR_DISTANCE
&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/vector.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/vector.html&lt;/a&gt;
&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/to_vector.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/to_vector.html&lt;/a&gt;
&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/vector_distance.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/vector_distance.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Hybrid query patterns
&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/vecse/query-data-similarity-and-hybrid-searches.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/vecse/query-data-similarity-and-hybrid-searches.html&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A tiny end‑to‑end demo
&lt;/h2&gt;

&lt;p&gt;This toy example shows the loop from rows to top‑K in a few statements. It runs on Oracle AI Database 26ai or Autonomous Database. Replace the three‑dimensional vectors with your model’s actual dimension (384/768/1536 are common), and use &lt;code&gt;FLOAT32&lt;/code&gt; for typical embeddings. For real data, prefer &lt;code&gt;CLOB&lt;/code&gt; when content exceeds 4000 bytes.&lt;/p&gt;

&lt;p&gt;1) Create a table that keeps content, provenance, and a vector together:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;documents&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt;         &lt;span class="n"&gt;NUMBER&lt;/span&gt; &lt;span class="k"&gt;GENERATED&lt;/span&gt; &lt;span class="n"&gt;ALWAYS&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="k"&gt;IDENTITY&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;source&lt;/span&gt;     &lt;span class="n"&gt;VARCHAR2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;published&lt;/span&gt;  &lt;span class="nb"&gt;DATE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;content&lt;/span&gt;    &lt;span class="n"&gt;VARCHAR2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;-- use CLOB for larger text in production&lt;/span&gt;
  &lt;span class="n"&gt;embedding&lt;/span&gt;  &lt;span class="n"&gt;VECTOR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FLOAT32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2) Insert a few rows with deterministic toy vectors so results are obvious:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;documents&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;published&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'kb'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;DATE&lt;/span&gt; &lt;span class="s1"&gt;'2025-01-15'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'How to reset your password'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="n"&gt;TO_VECTOR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'[0.10, 0.05, 0.90]'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FLOAT32&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;documents&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;published&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'kb'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;DATE&lt;/span&gt; &lt;span class="s1"&gt;'2025-02-10'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'How to export monthly invoices'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="n"&gt;TO_VECTOR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'[0.80, 0.10, 0.10]'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FLOAT32&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;documents&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;published&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'runbook'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;DATE&lt;/span&gt; &lt;span class="s1"&gt;'2025-03-05'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Rotate API keys every 90 days'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="n"&gt;TO_VECTOR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'[0.15, 0.85, 0.10]'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FLOAT32&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="k"&gt;COMMIT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3) Create a cosine HNSW index and keep the metric explicit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;VECTOR&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;docs_hnsw_idx&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;documents&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;ORGANIZATION&lt;/span&gt; &lt;span class="n"&gt;INMEMORY&lt;/span&gt; &lt;span class="n"&gt;NEIGHBOR&lt;/span&gt; &lt;span class="n"&gt;GRAPH&lt;/span&gt;
&lt;span class="n"&gt;DISTANCE&lt;/span&gt; &lt;span class="n"&gt;COSINE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;ORGANIZATION&lt;/code&gt; clause names the HNSW structure as documented in the SQL Reference. Verify feature availability and any licensing considerations in your environment, and keep the query metric aligned (COSINE here) for the optimizer to consider this index.&lt;/p&gt;

&lt;p&gt;4) Run a top‑K query. &lt;code&gt;VECTOR_DISTANCE&lt;/code&gt; returns smaller values for closer matches, so use ascending ORDER BY. Using a literal vector for illustration (in applications, pass vectors as binds rather than literals to avoid hard parsing and ensure type/dimension alignment):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="n"&gt;documents&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt;  &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;VECTOR_DISTANCE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
           &lt;span class="n"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
           &lt;span class="n"&gt;TO_VECTOR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'[0.12, 0.04, 0.92]'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FLOAT32&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
           &lt;span class="n"&gt;COSINE&lt;/span&gt;
         &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;FETCH&lt;/span&gt;  &lt;span class="k"&gt;FIRST&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="k"&gt;ROWS&lt;/span&gt; &lt;span class="k"&gt;ONLY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using a bind (recommended in applications). Ensure &lt;code&gt;:qvec&lt;/code&gt; matches the column’s dimension and element type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Suppose :qvec is a VECTOR(3, FLOAT32) bind variable&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="n"&gt;documents&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt;  &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;VECTOR_DISTANCE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;qvec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;COSINE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;FETCH&lt;/span&gt;  &lt;span class="k"&gt;FIRST&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="k"&gt;ROWS&lt;/span&gt; &lt;span class="k"&gt;ONLY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Operator shorthand (if documented in your RU; &lt;code&gt;&amp;lt;=&amp;gt;&lt;/code&gt; is cosine):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="n"&gt;documents&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt;  &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;embedding&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;qvec&lt;/span&gt;
&lt;span class="k"&gt;FETCH&lt;/span&gt;  &lt;span class="k"&gt;FIRST&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="k"&gt;ROWS&lt;/span&gt; &lt;span class="k"&gt;ONLY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;5) Add a business predicate to show hybrid eligibility in action:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="n"&gt;documents&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;  &lt;span class="k"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'kb'&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt;  &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;VECTOR_DISTANCE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
           &lt;span class="n"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
           &lt;span class="n"&gt;TO_VECTOR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'[0.12, 0.04, 0.92]'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FLOAT32&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
           &lt;span class="n"&gt;COSINE&lt;/span&gt;
         &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;FETCH&lt;/span&gt;  &lt;span class="k"&gt;FIRST&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="k"&gt;ROWS&lt;/span&gt; &lt;span class="k"&gt;ONLY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your corpus grows very large, evaluate IVF. Drop the HNSW index first. Keep a single active vector index per column to simplify planning and maintenance.&lt;/p&gt;

&lt;p&gt;Use the documented IVF clause (&lt;code&gt;NEIGHBOR PARTITIONS&lt;/code&gt;) in your RU’s SQL Reference:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;DROP&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;docs_hnsw_idx&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;VECTOR&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;docs_ivf_idx&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;documents&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;ORGANIZATION&lt;/span&gt; &lt;span class="n"&gt;NEIGHBOR&lt;/span&gt; &lt;span class="n"&gt;PARTITIONS&lt;/span&gt;
&lt;span class="n"&gt;DISTANCE&lt;/span&gt; &lt;span class="n"&gt;COSINE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After creating IVF, verify search‑effort controls and probe counts for your RU in the SQL Reference and DBMS_VECTOR docs; use those documented knobs to balance recall and latency.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnjw55yapgdsdbdr4vw1y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnjw55yapgdsdbdr4vw1y.png" alt="Reviewable Answer Path" width="722" height="510"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Orchestrating the answer: in your app or with Select AI
&lt;/h2&gt;

&lt;p&gt;Retrieval yields top‑K rows with provenance; you still need to compose an answer. Many teams keep this step in the application: run the retrieval SQL, log the query and bind values, and pass the returned snippets—plus &lt;code&gt;id&lt;/code&gt;, &lt;code&gt;source&lt;/code&gt;, and &lt;code&gt;published&lt;/code&gt;—to an LLM. Because the context is explicit, reviewers can reconstruct what the model saw and sign off on the path.&lt;/p&gt;

&lt;p&gt;If you prefer to orchestrate inside the database, use Select AI with a narrowly scoped AI Profile. During early review, enable SHOWSQL/EXPLAINSQL so a human can inspect generated SQL before execution. Syntax for USING/profile attributes varies by RU; follow the Select AI examples for your environment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- In a session with an AI Profile that scopes access&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;AI&lt;/span&gt; &lt;span class="n"&gt;SHOWSQL&lt;/span&gt; &lt;span class="s1"&gt;'List the top 3 KB articles about password resets from 2025.'&lt;/span&gt;
&lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="s1"&gt;'profile = &amp;lt;your_ai_profile&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To prevent table data from being sent to a provider during review, use the data‑access controls documented for your RU/service tier (see Select AI examples). Enable the appropriate session/profile setting rather than relying on defaults.&lt;/p&gt;

&lt;p&gt;Select AI can include sources in responses when configured; check the SOURCES option and relevant profile attributes for your RU, and keep SHOWSQL/EXPLAINSQL enabled until stakeholders are comfortable with the generated SQL.&lt;/p&gt;

&lt;p&gt;Docs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Select AI examples (SHOWSQL, EXPLAINSQL, object scoping, data access)
&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/selai/examples-using-select-ai.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/selai/examples-using-select-ai.html&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Operating in production shape
&lt;/h2&gt;

&lt;p&gt;Demos are short; production is measurement and repeatability. For HNSW, memory depends on graph parameters and corpus size. Use &lt;code&gt;DBMS_VECTOR&lt;/code&gt; memory advisors before large builds so you can size the index pool without starving the buffer cache, and measure recall and latency as you adjust graph parameters. For IVF, recall depends on partitioning and probe counts. Use accuracy helpers to compare index settings against exact search on a representative sample, and decide whether a point of recall is worth the latency trade.&lt;/p&gt;

&lt;p&gt;Plan hygiene matters. Keep the query metric aligned with the index metric, and gather statistics after bulk loads so the optimizer can choose the intended plan. A minimal stats call looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;
  &lt;span class="n"&gt;DBMS_STATS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GATHER_TABLE_STATS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'DOCUMENTS'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;
  &lt;span class="n"&gt;DBMS_STATS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GATHER_INDEX_STATS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'DOCS_HNSW_IDX'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Gather index stats after index builds or rebuilds so the optimizer can cost similarity ordering accurately.&lt;/p&gt;

&lt;p&gt;If you evolve index settings or dimensions, make DDL idempotent and, where your release supports it, prefer online rebuilds to avoid unnecessary outages. Article_07 covers schema and index evolution strategies.&lt;/p&gt;

&lt;p&gt;Governance continuity does not require special workarounds. When policies are configured on the relevant objects, VPD continues to filter rows, Unified Auditing can capture activity, and Data Redaction can mask columns that should not appear in clear text. As with any sensitive path, set &lt;code&gt;MODULE&lt;/code&gt; and &lt;code&gt;ACTION&lt;/code&gt; at session start, run your retrieval, and make evidence easy to find in audit views. Consider tagging vector queries with a distinct &lt;code&gt;ACTION&lt;/code&gt; (for example, &lt;code&gt;RAG_RETRIEVAL&lt;/code&gt;) to simplify Unified Auditing filters.&lt;/p&gt;

&lt;p&gt;Docs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;DBMS_VECTOR advisors and accuracy reporting
&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/arpls/dbms_vector1.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/arpls/dbms_vector1.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Security guide landing (VPD, Unified Auditing, Data Redaction, TDE)
&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/dbseg" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/dbseg&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Version scope and environment notes
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;VECTOR&lt;/code&gt; data type, &lt;code&gt;VECTOR_DISTANCE&lt;/code&gt;, ANN vector indexes (HNSW/IVF via &lt;code&gt;CREATE VECTOR INDEX&lt;/code&gt;), &lt;code&gt;DBMS_VECTOR&lt;/code&gt;, &lt;code&gt;DBMS_VECTOR_CHAIN&lt;/code&gt;, and the Select AI features shown here require Oracle AI Database 26ai or Autonomous Database. If you are on 19c, you can persist arrays as JSON or BLOB and compute distances in the application, but you do not have native vector columns, vector indexes, or &lt;code&gt;VECTOR_DISTANCE&lt;/code&gt;. Always verify your exact RU or service tier for option names, index clauses, and package signatures before you automate builds. If you use external embedding providers, configure credentials and network ACLs before calling them from the database. Ensure the query vector dimension and element type match the column definition; otherwise the statement will error.&lt;/p&gt;

&lt;h2&gt;
  
  
  Out of scope
&lt;/h2&gt;

&lt;p&gt;This article stays focused on storing vectors, building the two ANN index types, writing hybrid retrieval SQL, and keeping the answer path reviewable. It does not cover Oracle Text, summarization prompts, or application frameworks. For NL2SQL hardening and AI Profile design, see Article 5. For safe schema and index evolution, see Article 7.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Load 50–100 real chunks from your docs, store embeddings in a &lt;code&gt;VECTOR&lt;/code&gt; column, and build an HNSW index; compare recall and latency before and after switching to IVF on the same corpus.&lt;/li&gt;
&lt;li&gt;Turn on &lt;code&gt;SELECT AI SHOWSQL&lt;/code&gt; for the answer step so reviewers can sign off on generated SQL before execution.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Oracle AI Vector Search overview
&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/vecse/overview-ai-vector-search.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/vecse/overview-ai-vector-search.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;VECTOR type, TO_VECTOR, and VECTOR_DISTANCE
&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/vector.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/vector.html&lt;/a&gt;
&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/to_vector.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/to_vector.html&lt;/a&gt;
&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/vector_distance.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/vector_distance.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;CREATE VECTOR INDEX (HNSW/IVF)
&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/create-vector-index.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/create-vector-index.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Querying with similarity and hybrid predicates
&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/vecse/query-data-similarity-and-hybrid-searches.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/vecse/query-data-similarity-and-hybrid-searches.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;DBMS_VECTOR and DBMS_VECTOR_CHAIN
&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/arpls/dbms_vector1.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/arpls/dbms_vector1.html&lt;/a&gt;
&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/arpls/dbms_vector_chain1.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/arpls/dbms_vector_chain1.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Select AI examples (SHOWSQL, EXPLAINSQL, object scoping, data access)
&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/selai/examples-using-select-ai.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/selai/examples-using-select-ai.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Security guide landing (VPD, Unified Auditing, Data Redaction, TDE)
&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/dbseg" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/dbseg&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>database</category>
      <category>rag</category>
      <category>sql</category>
    </item>
    <item>
      <title>Safer NL2SQL with Select AI and AI Profiles</title>
      <dc:creator>Mark Nelson</dc:creator>
      <pubDate>Mon, 25 May 2026 16:30:20 +0000</pubDate>
      <link>https://dev.to/oracledevs/safer-nl2sql-with-select-ai-and-ai-profiles-18jc</link>
      <guid>https://dev.to/oracledevs/safer-nl2sql-with-select-ai-and-ai-profiles-18jc</guid>
      <description>&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;AI Profiles bind the model to a defined list of database objects. With &lt;code&gt;enforce_object_list&lt;/code&gt; enabled, generated SQL cannot reach tables you did not explicitly name — scope is a constraint, not just a suggestion.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;SHOWSQL&lt;/code&gt; returns the candidate SQL without executing it, using schema metadata rather than table data. You see what the assistant intends before any data moves or any query runs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;EXPLAINSQL&lt;/code&gt; surfaces the model's reasoning — the join choices, date windows, and grouping logic — so reviewers can catch wrong assumptions before they become surprises in a result set.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;DISABLE_DATA_ACCESS&lt;/code&gt; keeps early experiments in a prompt-only lane: metadata-driven inspection continues, but actions that would send table data to the model are blocked until you re-enable them.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Natural-language-to-SQL is fast until it guesses your schema, misreads a join, or touches the wrong table. On Oracle, you don’t have to accept that trade-off. AI Profiles bind provider and model to a defined set of database objects; Select AI adds database-native, inspectable actions so you can review intent and generated SQL before anything runs. In practice, you scope a profile, inspect with SHOWSQL and EXPLAINSQL, capture feedback, and only then execute. You keep the speed of NL2SQL without giving up control.&lt;/p&gt;

&lt;p&gt;This article builds on the managed MCP posture from Article 4, where we put a governed action surface in front of assistants. Here, we add an Oracle-native reasoning loop: Select AI’s profiles and actions make the model’s intent reviewable and controllable inside the database.&lt;/p&gt;

&lt;h2&gt;
  
  
  Version scope and sandbox assumptions
&lt;/h2&gt;

&lt;p&gt;Select AI is available in Oracle Database 26ai and in Autonomous Database (Serverless, Dedicated, and Cloud@Customer). The examples assume Select AI is enabled, a provider credential exists (for example, created with DBMS_CLOUD for external providers), and any required network ACLs are in place. The HR sample schema is used for illustration with read-only access, and table/owner casing must match your environment. You’ll need EXECUTE on DBMS_CLOUD_AI and access to the referenced credential. Profiles are created via DBMS_CLOUD_AI.CREATE_PROFILE and take effect for the current session when set with DBMS_CLOUD_AI.SET_PROFILE. Supported providers and models vary by platform and region; consult the Providers and Models page for your environment.&lt;/p&gt;

&lt;p&gt;Prompts that include punctuation or special characters parse more reliably when enclosed in single quotes (for example, &lt;code&gt;SELECT AI SHOWSQL '…';&lt;/code&gt;). Some prompts work unquoted; if parsing fails, add quotes. All examples below use quoted prompts.&lt;/p&gt;

&lt;p&gt;Action tokens and profile JSON keys can be release-sensitive; confirm exact spellings and supported values in your target release. For current behavior, providers/models, action syntax, conversations, and end-to-end examples, see the Select AI docs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Overview: &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/selai/select-ai-about.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/selai/select-ai-about.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Providers and models: &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/selai/select-your-ai-provider-and-llms.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/selai/select-your-ai-provider-and-llms.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Examples (profiles, actions, scoping, data-access toggle, feedback): &lt;a href="https://docs.oracle.com/en/cloud/paas/autonomous-database/serverless/adbsb/select-ai-examples.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/cloud/paas/autonomous-database/serverless/adbsb/select-ai-examples.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Conversations and action syntax: &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/selai/select-ai-enable-conversations.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/selai/select-ai-enable-conversations.html&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why reasoning quality matters for database AI
&lt;/h2&gt;

&lt;p&gt;LLMs know SQL patterns; they don’t know your database. Ask, “How many employees were hired last quarter by department?” and an open-ended model can guess a table name, pick the wrong join key, or hard-code a date window that doesn’t match your calendars. The remedy is twofold: make the model schema-aware, and force an inspection step before anything executes.&lt;/p&gt;

&lt;p&gt;AI Profiles handle schema awareness by binding the provider, model, and an object list the model may consider. Review then happens inside the database with &lt;code&gt;SELECT AI SHOWSQL&lt;/code&gt; (candidate SQL) and &lt;code&gt;SELECT AI EXPLAINSQL&lt;/code&gt; (rationale). That combination—scope plus inspection—turns NL2SQL from a clever demo into a workflow you can standardize. Per docs, for generation and inspection, SHOWSQL and EXPLAINSQL use database metadata (schemas/tables/columns/comments) rather than table contents by default. This keeps them safe defaults during review; consult docs for any action-specific exceptions in your release.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI Profiles in practice: provider, credential, model, and scope
&lt;/h2&gt;

&lt;p&gt;An AI Profile is a database object created with &lt;code&gt;DBMS_CLOUD_AI.CREATE_PROFILE&lt;/code&gt;. It specifies which provider and model to use, which credential authorizes access, and which database objects are in bounds. The &lt;code&gt;object_list&lt;/code&gt; does double duty: it gives the model the vocabulary it needs and, when enforced, constrains generation to the allowed objects. Profiles are selected per session with &lt;code&gt;DBMS_CLOUD_AI.SET_PROFILE&lt;/code&gt;; after that, SELECT AI actions in the session use the bound provider/model and respect your scoping choices.&lt;/p&gt;

&lt;p&gt;The following example enables HR headcount questions without inviting schema drift. The model string is illustrative—choose a provider/model combination supported in your environment and release.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;
  &lt;span class="n"&gt;DBMS_CLOUD_AI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CREATE_PROFILE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;profile_name&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'HR_READONLY'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;attributes&lt;/span&gt;   &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'{
      "provider": "openai",
      "credential_name": "OPENAI_CRED",
      "model": "gpt-4o-mini",
      "object_list": [
        {"owner":"HR","name":"EMPLOYEES"},
        {"owner":"HR","name":"DEPARTMENTS"}
      ],
      "enforce_object_list": true,
      "case_sensitive_values": true
    }'&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="k"&gt;EXEC&lt;/span&gt; &lt;span class="n"&gt;DBMS_CLOUD_AI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SET_PROFILE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'HR_READONLY'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Replace provider, model, and credential with values supported in your release. Supported providers vary by platform and region; check the Providers and Models page.&lt;/li&gt;
&lt;li&gt;Verify attribute keys and supported values (for example, &lt;code&gt;case_sensitive_values&lt;/code&gt;) against your release; use JSON booleans (&lt;code&gt;true&lt;/code&gt;/&lt;code&gt;false&lt;/code&gt;), not quoted strings.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A few practical notes help avoid surprises. Profiles are session-scoped, so call &lt;code&gt;DBMS_CLOUD_AI.SET_PROFILE&lt;/code&gt; in each session that will use SELECT AI (new SQL Developer tabs, pooled connections, and so on). Creating or using profiles requires appropriate privileges (for example, EXECUTE on &lt;code&gt;DBMS_CLOUD_AI&lt;/code&gt;), access to the referenced credential, and any needed network ACLs. The object list can span multiple schemas; execution still honors your roles and any VPD policies. Keeping &lt;code&gt;"enforce_object_list": true&lt;/code&gt; curbs schema drift, though the constraint isn’t absolute—if your environment uses synonyms or views, validate with SHOWSQL that generated SQL stays within intended objects. The &lt;code&gt;case_sensitive_values&lt;/code&gt; attribute steers how string literal comparisons are produced; if your text data is mixed-case and recall matters more than exact match semantics, consider setting it to &lt;code&gt;false&lt;/code&gt; while confirming the exact predicate shape your chosen provider/model generates. For example, with &lt;code&gt;"case_sensitive_values": false&lt;/code&gt; a generated comparison might use a case-insensitive form (such as &lt;code&gt;UPPER(e.email) = UPPER(:email)&lt;/code&gt;), but exact SQL varies by model and release. Finally, attribute keys such as &lt;code&gt;enforce_object_list&lt;/code&gt;, &lt;code&gt;case_sensitive_values&lt;/code&gt;, and any automated scoping modes are release-sensitive; verify names and supported values against current docs before relying on them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Inspectable actions: SHOWSQL first, then EXPLAINSQL if needed
&lt;/h2&gt;

&lt;p&gt;Once a profile is active, you can ask natural-language questions with database-native actions designed for review. SHOWSQL returns a candidate SQL statement without running it. Generation uses metadata—schemas, tables, and columns—rather than table contents.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;AI&lt;/span&gt; &lt;span class="n"&gt;SHOWSQL&lt;/span&gt; &lt;span class="s1"&gt;'how many employees were hired last quarter by department?'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;EXPLAINSQL summarizes the model’s intent so you can catch wrong assumptions before execution. Like SHOWSQL, it relies on metadata, not table data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;AI&lt;/span&gt; &lt;span class="n"&gt;EXPLAINSQL&lt;/span&gt; &lt;span class="s1"&gt;'how many employees were hired last quarter by department?'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Together, these two actions keep review and execution separate. SHOWSQL gives you something concrete to approve or adjust; EXPLAINSQL tells you why the model chose certain filters or join keys. Use them as your default path during early adoption. A third action, NARRATE, answers in natural language and can incorporate data when enabled; reserve it for contexts where your data-sharing posture allows model access to results and you have documented that decision (docs: Examples page).&lt;/p&gt;

&lt;h2&gt;
  
  
  Hardening the path: scoping, case handling, and data minimization
&lt;/h2&gt;

&lt;p&gt;Start with guardrails. Enforce the object list so the model stays inside approved tables or views. Decide on case handling up front so string comparisons behave predictably. And be explicit about what data, if any, can leave the database during NL2SQL interactions.&lt;/p&gt;

&lt;p&gt;Oracle provides an administrative toggle for data transfer. Calling &lt;code&gt;DBMS_CLOUD_AI.DISABLE_DATA_ACCESS&lt;/code&gt; blocks actions that would send table data or results to the model. Metadata-only flows—SHOWSQL and EXPLAINSQL—continue to work when disabled. Data-bearing actions (for example, NARRATE, and in some releases certain chat or result-enriched flows) are blocked until re-enabled. See the Select AI docs for the per-release action matrix.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;EXEC&lt;/span&gt; &lt;span class="n"&gt;DBMS_CLOUD_AI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DISABLE_DATA_ACCESS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Still allowed: metadata-driven candidate SQL (no data sent)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;AI&lt;/span&gt; &lt;span class="n"&gt;SHOWSQL&lt;/span&gt; &lt;span class="s1"&gt;'total headcount by department?'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Expected to raise an error while disabled (data-bearing action)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;AI&lt;/span&gt; &lt;span class="n"&gt;NARRATE&lt;/span&gt; &lt;span class="s1"&gt;'total headcount by department?'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;EXEC&lt;/span&gt; &lt;span class="n"&gt;DBMS_CLOUD_AI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ENABLE_DATA_ACCESS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Treat this toggle as a policy switch you can document and audit. When disabled, reviewers can still shape and approve SQL; when enabled, teams can opt into natural-language answers in contexts where that’s appropriate. Roles, VPD, and auditing continue to apply to any approved SQL you run, regardless of whether the SQL was generated by Select AI (docs: Overview and Examples).&lt;/p&gt;

&lt;p&gt;Guardrails to standardize in your runbooks: keep &lt;code&gt;enforce_object_list&lt;/code&gt; enabled; choose &lt;code&gt;case_sensitive_values&lt;/code&gt; deliberately; default to SHOWSQL/EXPLAINSQL during review; and use the data-access toggle to separate prompt-only review from data-bearing actions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Quick scope test you can try
&lt;/h3&gt;

&lt;p&gt;With the &lt;code&gt;HR_READONLY&lt;/code&gt; profile above (only EMPLOYEES and DEPARTMENTS in scope), ask something that tempts the model to reach for &lt;code&gt;HR.LOCATIONS&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- LOCATIONS is out of scope; we expect generation to stay within allowed objects&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;AI&lt;/span&gt; &lt;span class="n"&gt;SHOWSQL&lt;/span&gt; &lt;span class="s1"&gt;'list each department with its city and headcount'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Review the candidate. With &lt;code&gt;enforce_object_list&lt;/code&gt; on, generation is constrained to the allowed objects. If the candidate includes a disallowed object (for example, &lt;code&gt;HR.LOCATIONS&lt;/code&gt;), do not approve it—either change the question or explicitly add the needed object to the profile and regenerate. Scope isn’t a security bypass; it’s a generation boundary that works best when the object list is precise. Synonyms and views can influence behavior, so verify the metadata your profile exposes. When you want to encapsulate joins and filters deliberately, consider scoping to views rather than base tables.&lt;/p&gt;

&lt;h2&gt;
  
  
  A small, governed NL2SQL demo
&lt;/h2&gt;

&lt;p&gt;Here is a path your team can adopt without changing how you secure or audit the database. First, create and set a narrow profile; the &lt;code&gt;HR_READONLY&lt;/code&gt; example above is sufficient for headcount questions, and keeping &lt;code&gt;enforce_object_list&lt;/code&gt; enabled prevents drift to unapproved tables. Next, inspect with SHOWSQL and, when the intent needs clarification, EXPLAINSQL. For the HR schema, a typical review might begin with a candidate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Candidate SQL; not executed&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;AI&lt;/span&gt; &lt;span class="n"&gt;SHOWSQL&lt;/span&gt; &lt;span class="s1"&gt;'list the number of employees hired in the last 90 days by department with department name'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the candidate looks structurally right but relies on &lt;code&gt;SYSDATE - 90&lt;/code&gt; and you need calendar quarters instead, either edit the date predicate yourself or nudge the prompt and generate another candidate. When you want to see the model’s assumptions—date windows, join keys, grouping logic—ask for a short rationale:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Short rationale to help review&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;AI&lt;/span&gt; &lt;span class="n"&gt;EXPLAINSQL&lt;/span&gt; &lt;span class="s1"&gt;'list the number of employees hired in the last 90 days by department with department name'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After review, execute the approved SQL manually under a read-only role. Generated or not, it is just SQL, so your roles, VPD, and unified auditing remain in force.&lt;/p&gt;

&lt;h2&gt;
  
  
  Feedback and evaluation loops
&lt;/h2&gt;

&lt;p&gt;You’ll learn faster if you keep lightweight records of what you approved and why. A tiny table works well:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;ai_sql_feedback&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt;             &lt;span class="n"&gt;NUMBER&lt;/span&gt; &lt;span class="k"&gt;GENERATED&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="k"&gt;IDENTITY&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;asked_at&lt;/span&gt;       &lt;span class="nb"&gt;TIMESTAMP&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="n"&gt;SYSTIMESTAMP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;profile_name&lt;/span&gt;   &lt;span class="n"&gt;VARCHAR2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;nl_question&lt;/span&gt;    &lt;span class="k"&gt;CLOB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;generated_sql&lt;/span&gt;  &lt;span class="k"&gt;CLOB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;corrected_sql&lt;/span&gt;  &lt;span class="k"&gt;CLOB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;rating&lt;/span&gt;         &lt;span class="n"&gt;NUMBER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;-- 1–5&lt;/span&gt;
  &lt;span class="n"&gt;notes&lt;/span&gt;          &lt;span class="k"&gt;CLOB&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Record your decision after each review round:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;ai_sql_feedback&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;profile_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nl_question&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;generated_sql&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;corrected_sql&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rating&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;notes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s1"&gt;'HR_READONLY'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'list the number of employees hired in the last 90 days by department with department name'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;paste_candidate_sql_here&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;paste_corrected_or_approved_sql_here&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'Approved after switching SYSDATE-90 to quarter boundaries'&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Oracle also exposes a built-in feedback hook so you can record positive or negative signals tied to generated SQL. See &lt;code&gt;DBMS_CLOUD_AI.FEEDBACK&lt;/code&gt; in the Select AI docs for the current procedure signature; confirm parameter names such as &lt;code&gt;sql_text&lt;/code&gt; versus &lt;code&gt;sql_id&lt;/code&gt; and accepted feedback types/ratings for your release (docs: Examples).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Example only; verify parameter names/values for your release (e.g., sql_text vs sql_id)&lt;/span&gt;
&lt;span class="k"&gt;BEGIN&lt;/span&gt;
  &lt;span class="n"&gt;DBMS_CLOUD_AI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FEEDBACK&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;feedback_type&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'SQL_QUALITY'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;             &lt;span class="c1"&gt;-- verify accepted values&lt;/span&gt;
    &lt;span class="n"&gt;rating&lt;/span&gt;        &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                         &lt;span class="c1"&gt;-- verify rating scale&lt;/span&gt;
    &lt;span class="n"&gt;sql_text&lt;/span&gt;      &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;paste_candidate_sql_here&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;-- or use sql_id =&amp;gt; '...' if supported&lt;/span&gt;
    &lt;span class="n"&gt;notes&lt;/span&gt;         &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Good structure; adjusted date window to fiscal quarter'&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keep the loop simple: store the question, what Select AI proposed, what you ran, and a short note. In reviews, patterns emerge quickly—repeated date-window mistakes, common join fixes, or &lt;code&gt;object_list&lt;/code&gt; gaps you can close in profiles. If you depend on case-insensitive matching, pilot with a small test set that validates generated predicates against your data.&lt;/p&gt;

&lt;h2&gt;
  
  
  What changes when you do this under managed MCP
&lt;/h2&gt;

&lt;p&gt;In Article 4 we hosted an MCP server on Autonomous Database to make the database the control plane for actions. Select AI fits neatly into that posture. An agent can plan and call tools inside the database, but the way it generates and explains SQL remains inspectable and scoped by the active profile. Your identity, roles, private endpoints, VPD, and auditing don’t get bypassed; they’re the runway. This is series context, not a formal product integration; Select AI behavior and governance follow the documented database APIs. The operational benefit is simple: teams can standardize on "SHOWSQL first," even when an assistant is in the loop, and your audit trail still shows a normal query execution under the right account, module, and action once you approve the run.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common failure modes, and how the Select AI pattern avoids them
&lt;/h2&gt;

&lt;p&gt;Open-ended NL2SQL tends to fail the same ways: hallucinated table names, ambiguous joins, and fragile filters. An enforced &lt;code&gt;object_list&lt;/code&gt; removes most schema drift by design. SHOWSQL gives reviewers something concrete to approve or fix, and EXPLAINSQL surfaces incorrect assumptions before they become surprises in production. When data sharing is sensitive or still under review, &lt;code&gt;DISABLE_DATA_ACCESS&lt;/code&gt; keeps experiments in the prompt-only lane. None of this is exotic. It is a repeatable workflow you can hand to a junior developer—or an assistant—and expect the same outcome: first we scope; then we inspect; then we run; and we prove it in logs and policy after the fact.&lt;/p&gt;

&lt;h2&gt;
  
  
  What comes next
&lt;/h2&gt;

&lt;p&gt;This same discipline carries into retrieval-augmented generation. In Article 6, we will combine Select AI with Oracle AI Vector Search so answers are grounded in embeddings stored next to the business data itself. You’ll use the same pattern: bind provider and model with a profile, scope what’s visible, inspect what will run, and keep governance in the path.&lt;/p&gt;

&lt;h2&gt;
  
  
  A tiny sequence to keep on a team wall
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frhkvj7ao81i36lt0aku7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frhkvj7ao81i36lt0aku7.png" alt="Review-first NL2SQL workflow" width="539" height="826"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Further reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;About Select AI (overview, prompt/data handling): &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/selai/select-ai-about.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/selai/select-ai-about.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Providers and models: &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/selai/select-your-ai-provider-and-llms.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/selai/select-your-ai-provider-and-llms.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Examples (profiles, SHOWSQL/EXPLAINSQL/NARRATE, scoping, data-access toggle, feedback): &lt;a href="https://docs.oracle.com/en/cloud/paas/autonomous-database/serverless/adbsb/select-ai-examples.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/cloud/paas/autonomous-database/serverless/adbsb/select-ai-examples.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Action syntax and conversations: &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/selai/select-ai-enable-conversations.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/selai/select-ai-enable-conversations.html&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Closing
&lt;/h2&gt;

&lt;p&gt;NL2SQL doesn’t have to be a black box. On Oracle, you bind the model and the data surface with AI Profiles, inspect what the model intends with SHOWSQL and EXPLAINSQL, and execute only when you’re satisfied—under the same roles, VPD, and auditing you already trust. Start narrow. Keep &lt;code&gt;enforce_object_list&lt;/code&gt; on. Use the data-access toggle when you’re in prompt-only mode. Log decisions. The result is faster iteration with guardrails you can explain—and prove.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>database</category>
      <category>security</category>
      <category>sql</category>
    </item>
    <item>
      <title>Managed MCP in Autonomous AI Database: remote, governed tools per database</title>
      <dc:creator>Mark Nelson</dc:creator>
      <pubDate>Thu, 21 May 2026 10:32:46 +0000</pubDate>
      <link>https://dev.to/oracledevs/managed-mcp-in-autonomous-ai-database-remote-governed-tools-per-database-5edb</link>
      <guid>https://dev.to/oracledevs/managed-mcp-in-autonomous-ai-database-remote-governed-tools-per-database-5edb</guid>
      <description>&lt;p&gt;This is article 4 of 8 in my Oracle Database Skills series.&lt;/p&gt;

&lt;p&gt;Key Takeaways&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Managed MCP moves the action surface into the database itself. Tools run under real database identities with existing network controls, VPD policies, and audit trails already in force — no separate trust stack to build.&lt;/li&gt;
&lt;li&gt;Custom tools are database objects. You define the logic in PL/SQL and register it with DBMS_CLOUD_AI_AGENT.CREATE_TOOL, so governance travels with the tool definition rather than depending on each caller to do the right thing.&lt;/li&gt;
&lt;li&gt;The five questions security teams ask — who acted, under which identity, from what network path, against which tool, and where is the audit record — are answerable by design when the endpoint is managed by the database.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most teams begin where Article 3 ended: a single developer running a local MCP (Model Context Protocol) server to prove out tools and workflows. That’s perfect for a solo experiment and brittle the moment work becomes shared. As soon as you put an IDE on a shared sandbox or add a second teammate, you need identity that isn’t “whoever is at the laptop,” network boundaries that aren’t home Wi‑Fi, and an audit trail your security team can actually review. Autonomous AI Database’s managed MCP server gives you those primitives without rebuilding your trust stack. Each database exposes its own HTTP MCP endpoint. Clients authenticate as a database user. Tools are defined and governed inside the database. Existing controls—roles, Access Control Lists (ACLs) or Private Endpoint reachability, Virtual Private Database (VPD)/redaction, and Unified Auditing—stay in the path for every tool call.&lt;/p&gt;

&lt;p&gt;If you liked the local model—small, named tools; least‑privilege credentials; proof after the fact—you keep it. What changes is location and blast radius. The server runs with the database service. Calls execute with a database user’s roles. Network policy and auditing you already rely on continue to apply over streamable HTTP.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1cnfq0myr5hotuestm84.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1cnfq0myr5hotuestm84.png" alt=" " width="800" height="821"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sources: consult the Autonomous Database MCP documentation for concepts, enablement, security, and troubleshooting:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.oracle.com/en/cloud/paas/autonomous-database/serverless/adbsb/about-mcp-server.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/cloud/paas/autonomous-database/serverless/adbsb/about-mcp-server.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.oracle.com/en/cloud/paas/autonomous-database/serverless/adbsb/use-mcp-server.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/cloud/paas/autonomous-database/serverless/adbsb/use-mcp-server.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.oracle.com/en/cloud/paas/autonomous-database/serverless/adbsb/mcp-server-concepts.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/cloud/paas/autonomous-database/serverless/adbsb/mcp-server-concepts.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.oracle.com/en/cloud/paas/autonomous-database/serverless/adbsb/security.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/cloud/paas/autonomous-database/serverless/adbsb/security.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.oracle.com/en-us/iaas/autonomous-database-serverless/doc/troubleshoot-mcp-server.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en-us/iaas/autonomous-database-serverless/doc/troubleshoot-mcp-server.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Version scope and assumptions
&lt;/h2&gt;

&lt;p&gt;This article focuses on Autonomous Database Serverless (Autonomous AI Database) with its managed, per‑database HTTP MCP endpoint and on custom tool registration with &lt;code&gt;DBMS_CLOUD_AI_AGENT&lt;/code&gt;. It does not cover on‑premises databases, self‑hosted proxies, or alternative deployment topologies. For authentication, follow the documented OAuth and short‑lived bearer token flows supported at publish time. The examples assume you can modify an Autonomous AI Database in Oracle Cloud Infrastructure (OCI) and create PL/SQL in your schema. Private Endpoint deployments require clients to run within, or be routed into, the VCN. Availability is documented as exclusive to Oracle Autonomous AI Database (versions 26ai and 19c) on the “About MCP Server” page; verify your environment against current docs before starting.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you’ll need (prerequisites)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;An Autonomous AI Database (Serverless) you can modify in OCI.&lt;/li&gt;
&lt;li&gt;Permission in OCI to update free‑form tags on that database (enablement uses a tag).&lt;/li&gt;
&lt;li&gt;A database user to authenticate to MCP (least‑privilege strongly recommended).&lt;/li&gt;
&lt;li&gt;Database privileges to create/compile PL/SQL in your schema and to execute &lt;code&gt;DBMS_CLOUD_AI_AGENT&lt;/code&gt; (DBA can grant &lt;code&gt;EXECUTE&lt;/code&gt; on the package).&lt;/li&gt;
&lt;li&gt;Network posture set appropriately:

&lt;ul&gt;
&lt;li&gt;Public endpoint constrained by ACLs to known IPs/CIDRs, or&lt;/li&gt;
&lt;li&gt;Private Endpoint with clients running inside or routed/peered into the VCN.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;An MCP‑aware client that supports streamable HTTP (e.g., Claude Desktop via &lt;code&gt;mcp-remote&lt;/code&gt;; VS Code with Cline). Ensure the client’s transport is set to streamable HTTP per its configuration schema.&lt;/li&gt;

&lt;li&gt;To view audit trails, privileges such as &lt;code&gt;AUDIT_VIEWER&lt;/code&gt; (or equivalent access to &lt;code&gt;UNIFIED_AUDIT_TRAIL&lt;/code&gt;) and any required unified audit policies per your security team.&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why move from local to managed
&lt;/h2&gt;

&lt;p&gt;Local MCP gets you speed but leaves basic enterprise questions unanswered: Who ran this? Under which roles? From where? What policy filtered the result? Where’s the audit record? A managed MCP endpoint answers them by design. Identity is a database user, so tools run with that user’s roles and any VPD or redaction policies. Network is first‑class: public endpoints honor ACLs; Private Endpoint confines reachability to your VCN. Evidence lives with the data in Unified Auditing, and—if your organization uses it—Oracle Data Safe can centralize review. You don’t assemble a separate trust stack for assistants; you reuse the one already protecting your data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enable the per‑database MCP endpoint
&lt;/h2&gt;

&lt;p&gt;Enabling MCP is an OCI operation. You turn it on per database with a free‑form tag. After a brief delay, the database details page shows the MCP URL. Use that exact URL; Private Endpoint deployments will reflect your VCN in the hostname. The documented public endpoint pattern is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://dataaccess.adb.{region-identifier}.oraclecloudapps.com/adb/mcp/v1/databases/{database-ocid}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For Private Endpoint databases, the documented pattern uses the database Private Endpoint hostname prefix:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://{hostname_prefix}.adb.{region-identifier}.oraclecloudapps.com/adb/mcp/v1/databases/{database-ocid}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the OCI Console, open your Autonomous AI Database (Serverless) and add this free‑form tag:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Name: &lt;code&gt;adb$feature&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Value: &lt;code&gt;{"name":"mcp_server","enable":true}&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tighten network posture before you connect. For public endpoints, restrict inbound IPs with an ACL. For Private Endpoint, keep the MCP URL accessible only within your VCN; clients must run in or have routed/peered reachability to that network. The enablement and security pages in the docs provide step‑by‑step screens for both configurations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Authenticate to the managed MCP endpoint
&lt;/h2&gt;

&lt;p&gt;The managed server accepts OAuth or short‑lived bearer tokens as documented. Tokens are issued per database and presented on every request as a bearer header over streamable HTTP.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Token endpoint path (documented): &lt;code&gt;/adb/auth/v1/databases/{database-ocid}/token&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Present the token in client requests as: &lt;code&gt;Authorization: Bearer &amp;lt;token&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Tokens are typically valid for approximately one hour; confirm the current duration and request body in the Use MCP Server page before automating refresh.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Client configuration varies slightly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Claude Desktop via &lt;code&gt;mcp-remote&lt;/code&gt;: configure a remote MCP server using your database’s MCP URL and add the &lt;code&gt;Authorization: Bearer ...&lt;/code&gt; header in the bridge’s HTTP headers field. Set the transport to streamable HTTP as required by your client (for example, a setting named &lt;code&gt;streamable-http&lt;/code&gt; or &lt;code&gt;streamableHttp&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;VS Code with Cline: add a remote HTTP MCP server pointing to the MCP URL and set &lt;code&gt;Authorization: Bearer ...&lt;/code&gt; in the request headers section. Ensure the transport is explicitly set to streamable HTTP.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A minimal Cline-style configuration looks like this, with placeholders for your database URL and bearer token:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"adb-managed-mcp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"streamableHttp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://dataaccess.adb.{region-identifier}.oraclecloudapps.com/adb/mcp/v1/databases/{database-ocid}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"headers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Authorization"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bearer &amp;lt;token&amp;gt;"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your database uses a Private Endpoint, make sure the client host has network reachability into the VCN. Valid credentials against an unreachable hostname still fail.&lt;/p&gt;

&lt;h2&gt;
  
  
  Publish a custom tool the database can govern
&lt;/h2&gt;

&lt;p&gt;Unlike local MCP servers that host code beside the client, the managed server looks up tools defined in the database. You create small, purposeful functions in your schema and register them with &lt;code&gt;DBMS_CLOUD_AI_AGENT.CREATE_TOOL&lt;/code&gt;. That keeps control centralized: the definitions live in the database, metadata is queryable from views, and execution inherits database roles and policy.&lt;/p&gt;

&lt;p&gt;To keep the example easy to reason about, we’ll publish a read‑only tool that returns a paginated JSON array of objects in a given schema that are visible to the executing database user and are not in Oracle‑maintained schemas. This variant excludes Oracle‑maintained schemas using &lt;code&gt;ALL_USERS.ORACLE_MAINTAINED&lt;/code&gt;. The Oracle docs also include a sample that filters on &lt;code&gt;ALL_OBJECTS.ORACLE_MAINTAINED = 'N'&lt;/code&gt;; either approach works on Autonomous Database.&lt;/p&gt;

&lt;p&gt;First, create and compile the function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="k"&gt;REPLACE&lt;/span&gt; &lt;span class="k"&gt;FUNCTION&lt;/span&gt; &lt;span class="n"&gt;list_objects_json&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;p_schema&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="n"&gt;VARCHAR2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;p_offset&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="n"&gt;NUMBER&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;p_limit&lt;/span&gt;  &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="n"&gt;NUMBER&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;RETURN&lt;/span&gt; &lt;span class="k"&gt;CLOB&lt;/span&gt;
&lt;span class="k"&gt;AS&lt;/span&gt;
  &lt;span class="n"&gt;v_json&lt;/span&gt;  &lt;span class="k"&gt;CLOB&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;BEGIN&lt;/span&gt;
  &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;COALESCE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
           &lt;span class="n"&gt;JSON_ARRAYAGG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
             &lt;span class="n"&gt;JSON_OBJECT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
               &lt;span class="s1"&gt;'SCHEMA_NAME'&lt;/span&gt; &lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="k"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
               &lt;span class="s1"&gt;'OBJECT_NAME'&lt;/span&gt; &lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="n"&gt;object_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
               &lt;span class="s1"&gt;'OBJECT_TYPE'&lt;/span&gt; &lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="n"&gt;object_type&lt;/span&gt;
             &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;RETURNING&lt;/span&gt; &lt;span class="k"&gt;CLOB&lt;/span&gt;
           &lt;span class="p"&gt;),&lt;/span&gt;
           &lt;span class="s1"&gt;'[]'&lt;/span&gt;
         &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;v_json&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;object_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;object_type&lt;/span&gt;
      &lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="n"&gt;all_objects&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;
      &lt;span class="k"&gt;WHERE&lt;/span&gt;  &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;owner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;UPPER&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p_schema&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;AND&lt;/span&gt;    &lt;span class="k"&gt;EXISTS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
               &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
               &lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="n"&gt;all_users&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;
               &lt;span class="k"&gt;WHERE&lt;/span&gt;  &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;owner&lt;/span&gt;
               &lt;span class="k"&gt;AND&lt;/span&gt;    &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;oracle_maintained&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'N'&lt;/span&gt;
             &lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;ORDER&lt;/span&gt;  &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;object_name&lt;/span&gt;
      &lt;span class="k"&gt;OFFSET&lt;/span&gt; &lt;span class="n"&gt;p_offset&lt;/span&gt; &lt;span class="k"&gt;ROWS&lt;/span&gt; &lt;span class="k"&gt;FETCH&lt;/span&gt; &lt;span class="k"&gt;NEXT&lt;/span&gt; &lt;span class="n"&gt;p_limit&lt;/span&gt; &lt;span class="k"&gt;ROWS&lt;/span&gt; &lt;span class="k"&gt;ONLY&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;RETURN&lt;/span&gt; &lt;span class="n"&gt;v_json&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check for compilation errors before registering a tool:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SHOW&lt;/span&gt; &lt;span class="n"&gt;ERRORS&lt;/span&gt; &lt;span class="k"&gt;FUNCTION&lt;/span&gt; &lt;span class="n"&gt;list_objects_json&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Or:&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;position&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="n"&gt;user_errors&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt;  &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sequence&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, register the function as an MCP tool. Use the attribute names exactly as shown in the documentation for &lt;code&gt;DBMS_CLOUD_AI_AGENT.CREATE_TOOL.attributes&lt;/code&gt;; mismatches can cause registration or invocation errors. Common keys include &lt;code&gt;instruction&lt;/code&gt;, &lt;code&gt;function&lt;/code&gt;, and &lt;code&gt;tool_inputs&lt;/code&gt;. For this example, rely on the PL/SQL defaults defined in the function and do not include unverified default values in the &lt;code&gt;tool_inputs&lt;/code&gt; JSON.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;
  &lt;span class="n"&gt;DBMS_CLOUD_AI_AGENT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CREATE_TOOL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;tool_name&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'LIST_OBJECTS'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;attributes&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="s1"&gt;'~{
      "instruction": "Return a JSON array of objects visible to the executing database user for the given schema (excluding Oracle-maintained schemas). Always respect p_limit and p_offset.",
      "function": "LIST_OBJECTS_JSON",
      "tool_inputs": [
        { "name": "p_schema", "type": "string", "required": true,  "description": "Schema name (case-insensitive)" },
        { "name": "p_offset", "type": "number", "required": false, "description": "Row offset for pagination" },
        { "name": "p_limit",  "type": "number", "required": false, "description": "Max rows to return" }
      ]
    }~'&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify that registration succeeded by querying the registry view. Column sets differ by version, so keep the check simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;tool_name&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="n"&gt;user_cloud_ai_agent_tools&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt;  &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;tool_name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you see “insufficient privileges,” ask your DBA to grant &lt;code&gt;EXECUTE&lt;/code&gt; on &lt;code&gt;DBMS_CLOUD_AI_AGENT&lt;/code&gt; to your user and retry.&lt;/p&gt;

&lt;p&gt;Note: &lt;code&gt;DBMS_CLOUD_AI_AGENT&lt;/code&gt; supports both custom PL/SQL‑backed tools and built‑in tool types. For the current list of built‑in types and parameters, see the package reference:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.oracle.com/en-us/iaas/autonomous-database-serverless/doc/dbms-cloud-ai-agent-package.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en-us/iaas/autonomous-database-serverless/doc/dbms-cloud-ai-agent-package.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Also note: MLE JavaScript is not supported for MCP tools. Java can be used via PL/SQL calling Java stored procedures when &lt;code&gt;JAVAVM&lt;/code&gt; is enabled on the database (see the docs for details).&lt;/p&gt;

&lt;h2&gt;
  
  
  Connect a client and try the tool
&lt;/h2&gt;

&lt;p&gt;Once authentication works and the tool is registered, connect an MCP‑aware client and ask it to use the tool. In Claude Desktop, add a remote MCP server that points to your database’s MCP URL, include the &lt;code&gt;Authorization: Bearer &amp;lt;token&amp;gt;&lt;/code&gt; header, and set transport to streamable HTTP per the client’s schema. In a fresh chat, prompt the assistant: “Use the LIST_OBJECTS tool to list 5 objects in the HR schema.” You should see the assistant invoke the tool and stream back JSON. In VS Code with Cline, add the same MCP URL in the extension’s settings, supply the same header, and ensure the streamable HTTP transport is selected; then request &lt;code&gt;LIST_OBJECTS&lt;/code&gt; with &lt;code&gt;p_schema=HR&lt;/code&gt; and &lt;code&gt;p_limit=5&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A representative response from the tool looks like this (truncated):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"SCHEMA_NAME"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"HR"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"OBJECT_NAME"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"COUNTRIES"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"OBJECT_TYPE"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"TABLE"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"SCHEMA_NAME"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"HR"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"OBJECT_NAME"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"EMPLOYEES"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"OBJECT_TYPE"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"TABLE"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"SCHEMA_NAME"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"HR"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"OBJECT_NAME"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"JOBS"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"OBJECT_TYPE"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"TABLE"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On Private Endpoint databases, run those clients from a host inside the VCN or with routed/peered access. Lack of reachability is the most common reason a seemingly correct configuration fails.&lt;/p&gt;

&lt;h2&gt;
  
  
  Observe the audit trail (roles permitting)
&lt;/h2&gt;

&lt;p&gt;Unified Auditing on Autonomous Database records activity under your database user. Exact fields and event names depend on your version and your organization’s audit policy. With appropriate privileges (for example, &lt;code&gt;AUDIT_VIEWER&lt;/code&gt; or equivalent access to &lt;code&gt;UNIFIED_AUDIT_TRAIL&lt;/code&gt;), you can review a time‑bounded window of recent activity:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;event_timestamp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dbusername&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;return_code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;obj_privilege&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unified_audit_policies&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="n"&gt;unified_audit_trail&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;  &lt;span class="n"&gt;event_timestamp&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;SYSTIMESTAMP&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;INTERVAL&lt;/span&gt; &lt;span class="s1"&gt;'15'&lt;/span&gt; &lt;span class="k"&gt;MINUTE&lt;/span&gt;
&lt;span class="k"&gt;AND&lt;/span&gt;    &lt;span class="k"&gt;UPPER&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dbusername&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;UPPER&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt;  &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;event_timestamp&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;span class="k"&gt;FETCH&lt;/span&gt; &lt;span class="k"&gt;FIRST&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt; &lt;span class="k"&gt;ROWS&lt;/span&gt; &lt;span class="k"&gt;ONLY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Autonomous Database retains unified audit data for about 14 days by default; if you need longer retention or centralized reporting, integrate Oracle Data Safe. When you author VPD or audit policies for MCP, do not rely on &lt;code&gt;SESSION_USER&lt;/code&gt;. Use MCP context attributes instead—for example, &lt;code&gt;sys_context('MCP_SERVER_ACCESS_CONTEXT','USER_IDENTITY')&lt;/code&gt; as shown in the Security docs. Some concept pages may refer to the MCP context container using slightly different naming in prose; follow the exact string in the Security examples. Your security team may also enable additional unified audit policies to capture MCP‑specific attributes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Operate safely and clean up
&lt;/h2&gt;

&lt;p&gt;Treat tools as product surface area. Keep them small and read‑heavy at first; add write paths only when you can articulate least privilege and validate inputs. Prefer schemas you own to avoid accidental privilege sprawl. Version tool registrations as code and require review before deployment.&lt;/p&gt;

&lt;p&gt;When a tool is no longer needed, remove both the registration and the underlying function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;
  &lt;span class="n"&gt;DBMS_CLOUD_AI_AGENT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DROP_TOOL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tool_name&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'LIST_OBJECTS'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;/&lt;/span&gt;

&lt;span class="k"&gt;DROP&lt;/span&gt; &lt;span class="k"&gt;FUNCTION&lt;/span&gt; &lt;span class="n"&gt;list_objects_json&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you expand usage, periodically re‑check ACLs on public endpoints and confirm Private Endpoint posture remains correct, especially after network changes. Rate limiting is enforced by the managed service; consult current release notes for behavior and limits.&lt;/p&gt;

&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;p&gt;Most issues fall into a few buckets:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Compilation and registration: fix PL/SQL errors (&lt;code&gt;SHOW ERRORS&lt;/code&gt;, &lt;code&gt;USER_ERRORS&lt;/code&gt;) before registering. Ensure your user has &lt;code&gt;EXECUTE&lt;/code&gt; on &lt;code&gt;DBMS_CLOUD_AI_AGENT&lt;/code&gt;. If registry queries fail with unknown columns, select only &lt;code&gt;tool_name&lt;/code&gt; and consult versioned docs for metadata.&lt;/li&gt;
&lt;li&gt;Connectivity: verify you’re using the MCP URL exactly as shown in the console. For public endpoints, confirm your IP/CIDR is in the database ACL. For Private Endpoint, confirm the client host has routed/peered access to the VCN.&lt;/li&gt;
&lt;li&gt;Authentication: obtain tokens from &lt;code&gt;/adb/auth/v1/databases/{database-ocid}/token&lt;/code&gt; and present them as &lt;code&gt;Authorization: Bearer &amp;lt;token&amp;gt;&lt;/code&gt;. Copy the exact request body and lifetime from the current docs. Place headers where your client expects them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If a client claims to connect but tool calls fail, double-check that attribute keys in &lt;code&gt;CREATE_TOOL&lt;/code&gt; match the documentation exactly; case mismatches cause registration or invocation errors.&lt;/p&gt;

&lt;h2&gt;
  
  
  What comes next
&lt;/h2&gt;

&lt;p&gt;With a per-database MCP endpoint, one governed tool, and a way to verify activity in Unified Auditing, you can expand safely. Start with a small set of read-heavy tools your team actually needs. Introduce narrow write paths with explicit inputs and clear privileges. Treat tool definitions like code: review them, version them, and track changes.&lt;/p&gt;

&lt;p&gt;The next article moves from action surfaces to reasoning scope: Select AI and AI Profiles. That is where you decide what natural-language-to-SQL should be allowed to see, how generated SQL should be inspected, and how to keep an assistant’s query generation inside a deliberate profile instead of a broad database connection.&lt;/p&gt;

&lt;h2&gt;
  
  
  Freshness note
&lt;/h2&gt;

&lt;p&gt;Content validated against the Autonomous Database MCP documentation on 2026-05-08. Re-check the Use MCP Server and Security pages for any changes to endpoint paths, JSON key names, token lifetimes, and audit details before publishing or automating.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>database</category>
      <category>mcp</category>
      <category>security</category>
    </item>
    <item>
      <title>Controlled Oracle Actions with SQLcl MCP</title>
      <dc:creator>Mark Nelson</dc:creator>
      <pubDate>Tue, 19 May 2026 12:07:51 +0000</pubDate>
      <link>https://dev.to/oracledevs/controlled-oracle-actions-with-sqlcl-mcp-120e</link>
      <guid>https://dev.to/oracledevs/controlled-oracle-actions-with-sqlcl-mcp-120e</guid>
      <description>&lt;p&gt;This is article 3 of 8 in my Oracle Database Skills series.&lt;/p&gt;

&lt;p&gt;Kay Takeaways&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The assistant connects using a saved connection name, not a pasted password. Credentials stay in the SQLcl store and out of prompts, so the model sees the name but never the secret.&lt;/li&gt;
&lt;li&gt;Restrict levels define the tool surface at runtime. Starting at the most restrictive setting limits what the assistant can do; you widen the surface deliberately as the use case earns it.&lt;/li&gt;
&lt;li&gt;Session tagging and the MCP activity log give a DBA verifiable proof of what ran. Evidence comes from the database, not the chat transcript — that distinction matters when someone asks what the assistant actually did.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the last piece, you routed Oracle prompts through db/SKILL.md to keep intent explicit (see &lt;a href="https://blogs.oracle.com/developers/ai-agents-know-sql-oracle-database-skills-teach-them-oracle" rel="noopener noreferrer"&gt;Article 1&lt;/a&gt; and &lt;a href="https://blogs.oracle.com/developers/route-dont-flood-using-db-skill-md-to-steer-oracle%e2%80%91aware-assistants" rel="noopener noreferrer"&gt;Article 2&lt;/a&gt; in this series). Now it’s time to act—locally, with guardrails, and with an evidence trail your DBA can verify. SQLcl MCP gives you that posture. You launch it as a local process (&lt;code&gt;sql -mcp&lt;/code&gt;); it exposes a small catalog of named tools over stdio; it connects through saved connections you control; it starts in a highly restricted mode by default; and it leaves database‑native traces you can query later. That’s the right shape for early assistant experiments on a developer machine.&lt;/p&gt;

&lt;p&gt;Citations for versioned behavior are in Oracle’s SQLcl documentation (for example, “Using SQLcl MCP Server,” “SQLcl MCP Server tools,” “Starting and managing SQLcl MCP Server,” “Restrict Levels,” “Preparing your environment,” and “Saving connections” in the 25.4 and 26.1 editions). Treat any version‑sensitive claim here as “verify against your installed docs.”&lt;/p&gt;

&lt;h2&gt;
  
  
  What you need before you start
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;SQLcl 25.2.0 or newer with MCP support&lt;/li&gt;
&lt;li&gt;Java 17 or 21 available to SQLcl (per “Preparing your environment”)&lt;/li&gt;
&lt;li&gt;A non‑production Oracle Database (19c+ suffices for this demo)&lt;/li&gt;
&lt;li&gt;A least‑privilege, read‑only database user for testing&lt;/li&gt;
&lt;li&gt;A saved SQLcl connection that includes a stored password, created with &lt;code&gt;-save&lt;/code&gt; and &lt;code&gt;-savepwd&lt;/code&gt; (required for non‑interactive MCP connects)&lt;/li&gt;
&lt;li&gt;Permission to query &lt;code&gt;V$SESSION&lt;/code&gt; for evidence, or access to an equivalent DBA‑provided view that exposes &lt;code&gt;MODULE&lt;/code&gt; and &lt;code&gt;ACTION&lt;/code&gt; for your sessions&lt;/li&gt;
&lt;li&gt;If you use TNS aliases, configure Oracle Net as usual (for example, set &lt;code&gt;TNS_ADMIN&lt;/code&gt; to your &lt;code&gt;tnsnames.ora&lt;/code&gt; directory). If your MCP client launches &lt;code&gt;sql -mcp&lt;/code&gt; with a custom environment, pass &lt;code&gt;TNS_ADMIN&lt;/code&gt; there as needed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Security note: Storing passwords is a prerequisite for non‑interactive MCP connects. Keep this to a sandbox, use a read‑only account, and lock down your local SQLcl store (commonly under &lt;code&gt;~/.dbtools&lt;/code&gt;) per your organization’s secrets policy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why SQLcl MCP is the right local action layer
&lt;/h2&gt;

&lt;p&gt;A tempting shortcut is to paste credentials into a prompt and let a model “try something.” That’s exactly what you want to avoid. SQLcl MCP flips the pattern: you launch &lt;code&gt;sql -mcp&lt;/code&gt; locally and expose a small set of structured tools. Your client can list saved connections, connect by name, run SQL, and disconnect. The MCP connect path uses saved/named connections; the tool does not accept raw credentials in the call. For non‑interactive use, the password must be saved (&lt;code&gt;-savepwd&lt;/code&gt;). You decide which saved connections exist and what privileges they carry.&lt;/p&gt;

&lt;p&gt;Restrict levels are SQLcl’s runtime safety gates that limit which features and commands are allowed. In MCP mode the server defaults to restrict level 4 (most restrictive) unless you override it with &lt;code&gt;-R&lt;/code&gt;, so the surface stays small until you intentionally broaden it. That’s a posture you can explain to a DBA and adjust deliberately as you earn trust.&lt;/p&gt;

&lt;p&gt;References: SQLcl MCP overview and startup; Saved connections; Restrict levels (25.4/26.1 docs).&lt;/p&gt;

&lt;h2&gt;
  
  
  How the server behaves under the hood
&lt;/h2&gt;

&lt;p&gt;When your client launches &lt;code&gt;sql -mcp&lt;/code&gt;, SQLcl starts a small server over stdio and advertises a catalog of named tools. Message envelopes vary by client, but tool names and arguments follow the SQLcl MCP documentation. As of the 26.1 docs, commonly listed tools include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;list-connections&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;connect&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;disconnect&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;run-sql&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;run-sqlcl&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some builds also include &lt;code&gt;schema-information&lt;/code&gt;. Tool availability and argument shapes can vary by version/build; verify against your installed docs.&lt;/p&gt;

&lt;p&gt;Connections are discovered from your local SQLcl store (for example, &lt;code&gt;~/.dbtools&lt;/code&gt;). To make a connection usable without a prompt, save it with a stored password. Oracle documents this as a prerequisite for hands‑off connection by name.&lt;/p&gt;

&lt;p&gt;Restrict levels shape what the process can do. In MCP mode, the server defaults to restrict level 4 (most restrictive) unless you override it with &lt;code&gt;-R&lt;/code&gt;. Outside MCP, generic SQLcl defaults to restrict level 0 (unrestricted) unless you pass &lt;code&gt;-R&lt;/code&gt;; always verify the “Restrict Levels” page for your exact build.&lt;/p&gt;

&lt;p&gt;References: SQLcl MCP tools; Saved connections; Restrict levels and &lt;code&gt;-R&lt;/code&gt; flag (25.4/26.1 docs).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd6ad104v03cpgapuc7ht.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd6ad104v03cpgapuc7ht.png" alt="MCP Restrict Levels" width="800" height="312"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Traceability you can prove (monitoring and governance)
&lt;/h2&gt;

&lt;p&gt;Before you run anything, plan how you’ll show what happened. The most portable approach is to tag your session with &lt;code&gt;DBMS_APPLICATION_INFO&lt;/code&gt; and then confirm it in &lt;code&gt;V$SESSION&lt;/code&gt;. The tag appears in performance tooling and helps a DBA correlate what ran and why. By default, SQLcl MCP records the execution history of requests in &lt;code&gt;DBTOOLS$MCP_LOG&lt;/code&gt; (per Oracle docs). Access, owner/schema, and privileges vary by environment, so confirm presence and permissions with your DBA.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tagging mechanism: &lt;code&gt;DBMS_APPLICATION_INFO.SET_MODULE&lt;/code&gt; and &lt;code&gt;SET_ACTION&lt;/code&gt; (documented in Oracle Database PL/SQL packages)&lt;/li&gt;
&lt;li&gt;Evidence view: &lt;code&gt;V$SESSION&lt;/code&gt; (privilege‑gated; ask a DBA for a scoped alternative if you lack access)&lt;/li&gt;
&lt;li&gt;Logs: &lt;code&gt;DBTOOLS$MCP_LOG&lt;/code&gt; records requests by default; access and retention are environment‑specific&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;References: &lt;code&gt;DBMS_APPLICATION_INFO&lt;/code&gt;; &lt;code&gt;V$SESSION&lt;/code&gt;; SQLcl MCP monitoring/logging pages (25.4/26.1 docs).&lt;/p&gt;

&lt;h2&gt;
  
  
  Version scope and environment notes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Keep SQLcl at 25.2.0+ for MCP features and run it with Java 17 or 21 (per “Preparing your environment”).&lt;/li&gt;
&lt;li&gt;The tool catalog and defaults can evolve; the 25.4 and 26.1 guides capture current behavior, but verify against your installed version.&lt;/li&gt;
&lt;li&gt;The demo assumes Oracle Database 19c or later and avoids database features tied to 26ai.&lt;/li&gt;
&lt;li&gt;Some clients (for example, SQL Developer for VS Code) can launch and manage the MCP server for you; follow your client’s documentation for its configuration format and capabilities, which can change by version.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A small, traceable demo you can run today
&lt;/h2&gt;

&lt;p&gt;This walkthrough saves a single read‑only connection, launches the MCP server, validates that the connection is discoverable, connects by name, tags its session, runs one read‑only discovery query, and disconnects. Each step includes copy‑paste commands and why the step matters.&lt;/p&gt;

&lt;p&gt;1) Save a named connection for a read‑only sandbox user&lt;/p&gt;

&lt;p&gt;Open SQLcl, connect once interactively, and persist the connection with a stored password so MCP can use it without a prompt.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sql /nolog
&lt;span class="c"&gt;# If you connect by service name:&lt;/span&gt;
conn demo_ro@//host:1521/service &lt;span class="nt"&gt;-save&lt;/span&gt; demo_ro &lt;span class="nt"&gt;-savepwd&lt;/span&gt;
&lt;span class="c"&gt;# If you connect by TNS alias (ensure Oracle Net is configured and tnsnames.ora is discoverable):&lt;/span&gt;
&lt;span class="c"&gt;# conn demo_ro@ALIAS -save demo_ro -savepwd&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When prompted, enter the password interactively. The &lt;code&gt;-save&lt;/code&gt; and &lt;code&gt;-savepwd&lt;/code&gt; flags persist the named connection (commonly in &lt;code&gt;~/.dbtools&lt;/code&gt;) with a stored password so MCP can use it non‑interactively. Use a sandbox‑only, least‑privilege account and follow your org’s secrets policy for local credential storage.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe3pdi777ir9vojfm7mmd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe3pdi777ir9vojfm7mmd.png" alt="Saved Connection Handshake" width="800" height="463"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;2) Start the MCP server (keep the default restrict level)&lt;/p&gt;

&lt;p&gt;Launch SQLcl in MCP mode and leave the terminal open. Unless your client does this for you, this process is the local server your MCP‑aware client will talk to.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sql &lt;span class="nt"&gt;-mcp&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By default, restrict level 4 applies. If you ever see broader capability than expected, confirm that the process was not started with a lower restrict level (for example, &lt;code&gt;-R 0&lt;/code&gt;). For early experiments, stick with the default level 4.&lt;/p&gt;

&lt;p&gt;3) Point your MCP‑aware client at SQLcl&lt;/p&gt;

&lt;p&gt;Configure your client to launch &lt;code&gt;sql -mcp&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Representative MCP server configuration for a desktop client. Field names and file locations vary by client; follow your client’s documentation for the exact schema.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"sqlcl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sql"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"-mcp"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"TNS_ADMIN"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/path/to/network/admin"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;4) Verify that the saved connection is discoverable&lt;/p&gt;

&lt;p&gt;Before you connect, ask the server to list saved connections. Seeing &lt;code&gt;demo_ro&lt;/code&gt; here confirms that the server can use it non‑interactively.&lt;/p&gt;

&lt;p&gt;Representative MCP tool call; adapt to your client’s message envelope (for example, a surrounding &lt;code&gt;"type": "call_tool"&lt;/code&gt;), server name, and argument key names as defined by your client and the SQLcl MCP tools page for your installed version.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"tool"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"list-connections"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"arguments"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If &lt;code&gt;demo_ro&lt;/code&gt; does not appear, revisit the &lt;code&gt;conn ... -save -savepwd&lt;/code&gt; step and make sure you created the connection under the same OS user/profile that SQLcl is using.&lt;/p&gt;

&lt;p&gt;5) Connect by name and tag the session&lt;/p&gt;

&lt;p&gt;Connect using the saved name, then tag the session with &lt;code&gt;MODULE&lt;/code&gt; and &lt;code&gt;ACTION&lt;/code&gt; so you can find it later in performance views and, if accessible, MCP logs.&lt;/p&gt;

&lt;p&gt;Representative MCP tool call; adapt to your client’s envelope and argument names. Note: some clients use &lt;code&gt;name&lt;/code&gt;, others &lt;code&gt;connectionName&lt;/code&gt; or &lt;code&gt;connection_name&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"tool"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"connect"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"arguments"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"demo_ro"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"tool"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"run-sql"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"arguments"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"sql"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"BEGIN DBMS_APPLICATION_INFO.SET_MODULE('sqlcl-mcp','schema-discovery'); END;"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tagging is optional but strongly recommended. It creates a human‑readable breadcrumb for exactly this experiment.&lt;/p&gt;

&lt;p&gt;6) Run one read‑only discovery query&lt;/p&gt;

&lt;p&gt;To keep the example universal, start with a query against objects you own:&lt;/p&gt;

&lt;p&gt;Representative MCP tool call; adapt to your client’s envelope and argument names.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"tool"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"run-sql"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"arguments"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"sql"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SELECT table_name FROM user_tables ORDER BY table_name FETCH FIRST 5 ROWS ONLY"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your account has cross‑schema visibility, you can adjust the query to &lt;code&gt;ALL_TABLES&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;Representative MCP tool call; adapt to your client’s envelope and argument names.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"tool"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"run-sql"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"arguments"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"sql"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SELECT owner, table_name FROM all_tables WHERE owner = UPPER('&amp;lt;YOUR_SCHEMA&amp;gt;') ORDER BY table_name FETCH FIRST 5 ROWS ONLY"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keep the action read‑only while you learn the surface and review the evidence with a DBA partner.&lt;/p&gt;

&lt;p&gt;Optional: Some clients also expose &lt;code&gt;run-sqlcl&lt;/code&gt; for SQLcl console commands (for example, &lt;code&gt;DESC&lt;/code&gt;). At restrict level 4, many SQLcl commands are disabled; simple describe operations may work depending on your build—verify in your environment. If enabled in your version and posture, a representative call looks like:&lt;/p&gt;

&lt;p&gt;Representative MCP tool call; adapt to your client’s envelope and argument names.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"tool"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"run-sqlcl"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"arguments"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DESC USER_TABLES"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Treat this as optional; the minimal surface for early tests is &lt;code&gt;run-sql&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;7) Disconnect when you are done&lt;/p&gt;

&lt;p&gt;Disconnecting is good hygiene and makes your test’s boundaries clear.&lt;/p&gt;

&lt;p&gt;Representative MCP tool call; adapt to your client’s envelope and argument names.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"tool"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"disconnect"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"arguments"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Prove what happened
&lt;/h2&gt;

&lt;p&gt;Evidence checks depend on privileges. If your account cannot query performance views, ask your DBA for a scoped alternative that exposes &lt;code&gt;MODULE&lt;/code&gt; and &lt;code&gt;ACTION&lt;/code&gt; for your sessions.&lt;/p&gt;

&lt;p&gt;See the tag in a live session view:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;machine&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="k"&gt;session&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;  &lt;span class="n"&gt;module&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'sqlcl-mcp'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By default, SQLcl MCP records requests in &lt;code&gt;DBTOOLS$MCP_LOG&lt;/code&gt;. You can review recent entries there as well. Presence is standard per the docs, but owner/schema and access vary by setup; in some environments the table resides under a DBA‑owned schema. Treat the query below as an example and ask your DBA for the owner or a scoped view if needed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mcp_client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end_point_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end_point_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;log_message&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="n"&gt;DBTOOLS&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;MCP_LOG&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt;  &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;span class="k"&gt;FETCH&lt;/span&gt;  &lt;span class="k"&gt;FIRST&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="k"&gt;ROWS&lt;/span&gt; &lt;span class="k"&gt;ONLY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If neither view is available to you directly, a DBA can run the checks or provide a narrow view limited to your test user.&lt;/p&gt;

&lt;h2&gt;
  
  
  When something goes wrong
&lt;/h2&gt;

&lt;p&gt;If the client prompts for a password, the saved connection likely lacks a stored secret. Recreate it with both &lt;code&gt;-save&lt;/code&gt; and &lt;code&gt;-savepwd&lt;/code&gt; so MCP can connect non‑interactively. When the tool catalog looks unfamiliar or is missing entries, you may be on a different SQLcl version; check the MCP tools page for your installed build (the 25.4 and 26.1 docs capture current behavior). If you don’t see your session tag in &lt;code&gt;V$SESSION&lt;/code&gt;, it’s probably a privilege boundary rather than a failure to tag; ask for a scoped view or a one‑time verification. If the server shows more capability than your risk posture allows, confirm the restrict level and restart without any &lt;code&gt;-R&lt;/code&gt; override so the default restrictive posture applies.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this posture earns DBA trust
&lt;/h2&gt;

&lt;p&gt;DBAs reasonably resist assistants that can write to production. SQLcl MCP avoids that leap of faith. You connect through named entries you control. You default to a restrictive mode. You tag your sessions with &lt;code&gt;MODULE&lt;/code&gt; and &lt;code&gt;ACTION&lt;/code&gt; so activity shows up cleanly in performance tooling. And, where available, MCP logs give an after‑the‑fact record. The point is not speed; it’s reversibility and evidence. In a world where models sometimes propose creative fixes, that’s the currency that gets you permission to try again tomorrow.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsbuc74iqyrd98sluda8d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsbuc74iqyrd98sluda8d.png" alt="Local SQLcl MCP Evidence Trail" width="719" height="769"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s intentionally out of scope here
&lt;/h2&gt;

&lt;p&gt;This article focuses on local SQLcl MCP on a developer workstation, read‑only sandboxes, and minimal evidence checks. It does not cover managed or remote MCP hosting, enterprise audit/policy, advanced reasoning patterns (for example, Select AI or AI Profiles), vector‑native RAG, or change‑management workflows. Those topics are addressed next when we move beyond a single machine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this leaves you next
&lt;/h2&gt;

&lt;p&gt;You now have a local, developer‑friendly path for safe, traceable Oracle actions. Previously in this series: Article 1 (routing) and Article 2 (route‑to‑action bridge). Next up: Article 4, where we carry these guardrails into a managed path with audit, policy, and team controls so you can scale without losing the evidence and restraint you established here.&lt;/p&gt;

&lt;p&gt;Freshness Note: Version‑sensitive behavior (tool catalog, defaults, restrict levels) should be verified against your installed SQLcl documentation (notably the 25.4 and 26.1 editions).&lt;/p&gt;

</description>
      <category>ai</category>
      <category>database</category>
      <category>mcp</category>
      <category>security</category>
    </item>
    <item>
      <title>Route, Don’t Flood: Using db/SKILL.md to Steer Oracle‑Aware Assistants</title>
      <dc:creator>Mark Nelson</dc:creator>
      <pubDate>Fri, 15 May 2026 12:21:32 +0000</pubDate>
      <link>https://dev.to/oracledevs/route-dont-flood-using-dbskillmd-to-steer-oracle-aware-assistants-4el0</link>
      <guid>https://dev.to/oracledevs/route-dont-flood-using-dbskillmd-to-steer-oracle-aware-assistants-4el0</guid>
      <description>&lt;p&gt;If you’ve tried to make an assistant “Oracle‑aware,” you’ve likely hit the same wall: you paste a stack of links, the model blends vendor‑neutral habits with stale Oracle guidance, and the answers get vague when you need precision. The fix isn’t more context—it’s better routing. One Oracle skill at a time, in the right order, so every next move follows Oracle’s own sequence.&lt;/p&gt;

&lt;p&gt;In Article 1 we outlined an operating model for AI on Oracle Database: route → act → trust. This piece goes deep on the first verb. It shows how to use &lt;code&gt;db/SKILL.md&lt;/code&gt; in the public Oracle Database Skills repository as your front door; how to route by persona or by task; and how to enforce progressive discovery so your assistant loads exactly one file at a time. This is a no‑execution article; we stay in the routing lane and leave tools to Article 3 (SQLcl MCP). Prerequisite: you only need to browse &lt;a href="https://github.com/oracle/skills" rel="noopener noreferrer"&gt;https://github.com/oracle/skills&lt;/a&gt; in a web browser.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why routing beats context dumping
&lt;/h2&gt;

&lt;p&gt;Dumping a pile of links into a prompt feels thorough, but it blurs version lines and lets generic “SQL” patterns overrule Oracle‑specific guidance. It also bloats token budgets without improving the next decision. &lt;code&gt;db/SKILL.md&lt;/code&gt; fixes this by providing a maintained map of Oracle Database skills, pointing you to the right starting file for common jobs, and encoding short, opinionated sequences for multi‑step workflows. The goal is not to read everything; it’s to pick the next file, digest it, and decide whether one more file is warranted. When you’re actually ready to execute a command, you’ve left routing and entered “act” (Article 3 covers SQLcl MCP).&lt;/p&gt;

&lt;h2&gt;
  
  
  Treat db/SKILL.md as the front door
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;db/SKILL.md&lt;/code&gt; is the router for the Database domain. It opens with a routing table and guidance to keep context tight—begin at the table, then read only the specific file or category you need.&lt;/p&gt;

&lt;p&gt;It also shows the shape of the Database skills, including these directories:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;db/agent&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;db/features&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;db/frameworks&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;db/performance&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;db/security&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;db/devops&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;db/migrations&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;db/sqlcl&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Two parts matter most on your first pass. First, the file highlights concrete entry points for real jobs—where performance triage begins, where SQLcl basics and MCP server entries sit (setup is deferred to Article 3), how to approach schema migrations, and which agent behaviors (like schema discovery) must be understood before you write anything. Second, it outlines common sequences that act as guardrails. Examples include RAG (skills under &lt;code&gt;db/features&lt;/code&gt;: ai‑profiles → vector‑search → dbms‑vector), slow‑query diagnosis (skills under &lt;code&gt;db/performance&lt;/code&gt;: explain‑plan → wait‑events → optimizer‑stats → awr‑reports), and agent‑safe schema change (skills under &lt;code&gt;db/agent&lt;/code&gt; and &lt;code&gt;db/migrations&lt;/code&gt;: schema‑discovery → destructive‑op‑guards → idempotency‑patterns → schema‑migrations). There’s also an MCP setup path you’ll use later when you move from routing to action: sqlcl‑basics → a least‑privilege/privilege‑management skill in &lt;code&gt;db/security&lt;/code&gt; → sqlcl‑mcp‑server.&lt;/p&gt;

&lt;p&gt;Starting here changes how your assistant behaves. Instead of grabbing whatever looks related on the internet, it asks, “What’s the next Oracle‑authored file?” The result is fewer wrong turns and shorter, more reviewable prompts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Route by persona when you’re scoping a project
&lt;/h2&gt;

&lt;p&gt;When you’re defining scope rather than responding to a ticket, match your role and load one file. Don’t skim the whole repository first—route, read, and decide.&lt;/p&gt;

&lt;h3&gt;
  
  
  App developer
&lt;/h3&gt;

&lt;p&gt;If you’re wiring a service to Oracle, start with frameworks and then layer on application‑specific skills. The &lt;code&gt;db/frameworks&lt;/code&gt; directory anchors guidance for stacks such as Spring, Django, and SQLAlchemy: connection configuration, driver/dialect selection, data type mapping, and recommended connection patterns. Once the framework is set, dip into app‑dev skills for JSON, Spatial, Text, or pooling nuances you actually need. Doing it in this order avoids needless trial and error—you adopt the Oracle‑tested path before you customize.&lt;/p&gt;

&lt;h3&gt;
  
  
  AI engineer
&lt;/h3&gt;

&lt;p&gt;Begin in &lt;code&gt;db/agent&lt;/code&gt; and &lt;code&gt;db/features&lt;/code&gt;. The agent skills cover behaviors that matter when a model touches a database: discover schema before DML, guard against destructive operations, make steps idempotent so retries don’t double‑apply, and identify clients for traceability. The features skills introduce Oracle’s AI‑native building blocks—Select AI and AI Profiles for governed NL2SQL, and AI Vector Search/&lt;code&gt;DBMS_VECTOR&lt;/code&gt; for retrieval and RAG. Version gate: check each feature’s documentation. &lt;code&gt;DBMS_VECTOR&lt;/code&gt; and many Select AI/AI Profiles examples are documented for 26ai; several AI capabilities are also available in Autonomous Database. Do not assume 19c availability unless a skill or doc explicitly states it.&lt;/p&gt;

&lt;h3&gt;
  
  
  DBA
&lt;/h3&gt;

&lt;p&gt;The fastest way to get Oracle‑native outcomes from an assistant is to route through &lt;code&gt;db/performance&lt;/code&gt; and then &lt;code&gt;db/security&lt;/code&gt;. Performance skills teach the assistant to obtain and read the plan that actually executed (for example, via &lt;code&gt;DBMS_XPLAN.DISPLAY_CURSOR(NULL, NULL, 'ALLSTATS LAST')&lt;/code&gt;), interpret row sources and cardinality, and pivot at the right moment from plan reading to wait events and then, if warranted, to AWR. Security skills ground privilege design, auditing, and encryption in Oracle’s vocabulary so “least privilege” becomes an actionable design rather than a slogan.&lt;/p&gt;

&lt;h3&gt;
  
  
  Migration lead
&lt;/h3&gt;

&lt;p&gt;Treat &lt;code&gt;db/migrations&lt;/code&gt; and &lt;code&gt;db/devops&lt;/code&gt; as two halves of one job. Migration skills help with assessment and translation; devops skills handle delivery mechanics such as schema change workflows, online operations, Edition‑Based Redefinition, and testing. Loading one file at a time keeps heterogeneous estates from collapsing into generic, lowest‑common‑denominator advice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Route by task when you’re on the clock
&lt;/h2&gt;

&lt;p&gt;When a ticket arrives with a single job, follow the ordered sequences in the repository. Oracle’s diagnostics and defaults are opinionated for a reason; ignoring their order usually costs time.&lt;/p&gt;

&lt;h3&gt;
  
  
  RAG on Oracle Database
&lt;/h3&gt;

&lt;p&gt;Start with AI Profiles, because that’s where you choose a provider and model and, crucially, define which database objects the model may see. Only after scope and governance are set should you learn retrieval patterns in vector‑search and then orchestrate pipelines with &lt;code&gt;DBMS_VECTOR&lt;/code&gt;. The payoff is a governed RAG flow that can be inspected (&lt;code&gt;SHOW SQL&lt;/code&gt;) and tightened (&lt;code&gt;OBJECT_LIST&lt;/code&gt; and data access controls), rather than a bag of embeddings taped onto a database.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Route (skills under &lt;code&gt;db/features&lt;/code&gt;): ai‑profiles → vector‑search → dbms‑vector&lt;/li&gt;
&lt;li&gt;Reference: Select AI examples (&lt;code&gt;SHOW SQL&lt;/code&gt;, &lt;code&gt;OBJECT_LIST&lt;/code&gt;, and data access controls): &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/selai/examples-using-select-ai.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/selai/examples-using-select-ai.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Version note: Follow the feature docs. &lt;code&gt;DBMS_VECTOR&lt;/code&gt; and many Select AI/AI Profiles examples are documented for 26ai; several AI capabilities are also available in Autonomous Database. Do not assume 19c availability unless a skill or doc explicitly states it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Slow‑query diagnosis
&lt;/h3&gt;

&lt;p&gt;Begin by reading the plan that actually ran. In Oracle that means capturing the executed plan from the cursor (for example, &lt;code&gt;DBMS_XPLAN.DISPLAY_CURSOR(NULL, NULL, 'ALLSTATS LAST')&lt;/code&gt;), not relying solely on an EXPLAIN PLAN estimate. If the plan looks sane but runtime stalls, pivot to wait events. If rows and joins are misestimated, fix statistics and cardinality before widening the window with AWR.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Route (skills under &lt;code&gt;db/performance&lt;/code&gt;): explain‑plan → wait‑events → optimizer‑stats → awr‑reports&lt;/li&gt;
&lt;li&gt;Availability note: AWR/ASH access depends on environment and licensing.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Agent‑safe schema change
&lt;/h3&gt;

&lt;p&gt;Discover what exists before you propose change, add loss‑prevention so a single bad predicate can’t harm a table, and make each step idempotent so a retry won’t double‑apply. Only then move into an audited migration workflow.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Route (skills under &lt;code&gt;db/agent&lt;/code&gt; and &lt;code&gt;db/migrations&lt;/code&gt;): schema‑discovery → destructive‑op‑guards → idempotency‑patterns → schema‑migrations&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A compact prompt that enforces progressive discovery
&lt;/h2&gt;

&lt;p&gt;A small prompt can prevent freelancing by enforcing a loop: route, read one file, summarize, decide. That discipline keeps the context precise and affordable.&lt;/p&gt;

&lt;p&gt;Prompt&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Open db/SKILL.md. I’m [persona or task].
Which single skill file should I load next, and why?
Summarize that file in 5 bullets, then propose the next decision (read another file or stop).
Do not execute any actions.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Progressive routing loop&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftmfmmwuxt6yaw7o2aawr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftmfmmwuxt6yaw7o2aawr.png" alt="Progressive disclosure loop" width="800" height="787"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Mini demos (no DB access, no execution)
&lt;/h2&gt;

&lt;p&gt;These prompts validate routing behavior before you connect any tools. They’re deliberately constrained to “read and decide.”&lt;/p&gt;

&lt;h3&gt;
  
  
  Demo A: Persona‑first route for a Spring app developer
&lt;/h3&gt;

&lt;p&gt;Prompt&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Open db/SKILL.md. I’m wiring a Spring Boot service to Oracle.
Which single frameworks/appdev skill should I load next, and why?
Summarize that file in 5 bullets, then propose the next decision.
Do not execute any actions.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example assistant response&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Next file: A Spring Data JPA with Oracle skill under db/frameworks/.
Why: It covers Oracle-specific connection configuration, driver/dialect selection, pooling, and type mapping.

Summary:
- Recommended Oracle JDBC URL formats and service naming for Spring Boot
- Dialect/driver selection and common DataSource pitfalls
- Transaction, batching, and fetch-size options tuned for Oracle
- LOB, DATE/TIMESTAMP, and NUMBER mapping considerations
- Starter checks and links to follow-up appdev skills

Next decision:
- If you need pooling/retry/circuit specifics, read a connection/pooling skill next.
- Otherwise, stop and apply these configuration steps before proceeding.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Demo B: Task‑first route for diagnosing a slow query
&lt;/h3&gt;

&lt;p&gt;Prompt&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Starting from db/SKILL.md, list the ordered skill files for slow-query triage.
Load only the first file, summarize it in 5 bullets, then recommend whether to read the next file or stop.
Do not execute any actions.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example assistant response&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Ordered files:
1) The explain-plan skill in db/performance/
2) The wait-events skill in db/performance/
3) The optimizer-stats skill in db/performance/
4) The awr-reports skill in db/performance/

Loaded: explain-plan

Summary:
- Obtain the executed plan from the cursor (vs. EXPLAIN PLAN estimates) and where to find it
- Read row sources and cardinality; spot misestimates and join-order issues
- Recognize anti-patterns (forced full scans, skewed histograms, implicit conversions)
- Interpret plan notes for adaptive features and parallel decisions
- Decide when to pivot from plan analysis to wait-event inspection

Recommendation:
- Read wait-events next if the plan looks reasonable but runtime stalls.
- If the plan is clearly wrong, stop and fix plan issues before proceeding.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The “explain‑plan” skill in this route teaches capturing and reading the actual cursor plan, not just the EXPLAIN PLAN estimate.&lt;/li&gt;
&lt;li&gt;AWR/ASH may not be available in all environments and may require licensing.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Version scope and context‑budget discipline
&lt;/h2&gt;

&lt;p&gt;Assume Oracle Database 19c as the baseline unless a skill states a higher requirement. For AI features, check per‑feature documentation: &lt;code&gt;DBMS_VECTOR&lt;/code&gt; and many Select AI/AI Profiles examples are documented for 26ai; several AI capabilities are also available in Autonomous Database. Do not assume 19c availability unless a skill or doc explicitly states it. Label those steps clearly when you route.&lt;/p&gt;

&lt;p&gt;Two habits keep assistants accurate and affordable:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Load one file or short index at a time, then summarize in five bullets.&lt;/li&gt;
&lt;li&gt;Decide whether to stop, read one more, or hand off to action (SQLcl MCP in Article 3). Don’t blur routing with execution.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why “route, don’t flood” holds up under pressure
&lt;/h2&gt;

&lt;p&gt;Teams tend to bulk‑load context when deadlines loom. Ironically, progressive discovery pays off most when time is tight. &lt;code&gt;db/SKILL.md&lt;/code&gt; compresses Oracle experience into short routes you can trust: read the executed plan before hunting waits, scope an AI Profile before experimenting with retrieval, and prove discovery and guards before you accept any schema change. Staying inside those lanes doesn’t slow you down; it eliminates the detours that burn days.&lt;/p&gt;

&lt;p&gt;Make the front door easy to reach. The simplest path is to browse the repository in a web browser and open &lt;code&gt;db/SKILL.md&lt;/code&gt;. If you prefer a local copy, install the Database domain directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx skills add oracle/skills/db
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This puts the domain files in your working directory so you can open &lt;code&gt;db/SKILL.md&lt;/code&gt; immediately. Then set your assistant’s “Oracle Database” system prompt to begin with “Open db/SKILL.md,” and keep the progressive‑discovery loop in place until the next decision is truly to act.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Oracle Database Skills is not a PDF dump for models. It’s an operating method that starts with routing. Use &lt;code&gt;db/SKILL.md&lt;/code&gt; as your first touch, load one file at a time, and stop before you wander into execution. In Article 3, we’ll introduce SQLcl MCP as the bounded action surface that pairs naturally with this approach. Until then: route → read one → summarize → decide.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Oracle Database Skills repository root: &lt;a href="https://github.com/oracle/skills" rel="noopener noreferrer"&gt;https://github.com/oracle/skills&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Database router (&lt;code&gt;db/SKILL.md&lt;/code&gt;): &lt;a href="https://github.com/oracle/skills/blob/main/db/SKILL.md" rel="noopener noreferrer"&gt;https://github.com/oracle/skills/blob/main/db/SKILL.md&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Frameworks directory: &lt;a href="https://github.com/oracle/skills/tree/main/db/frameworks" rel="noopener noreferrer"&gt;https://github.com/oracle/skills/tree/main/db/frameworks&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Performance directory: &lt;a href="https://github.com/oracle/skills/tree/main/db/performance" rel="noopener noreferrer"&gt;https://github.com/oracle/skills/tree/main/db/performance&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Select AI examples (&lt;code&gt;SHOW SQL&lt;/code&gt;, &lt;code&gt;OBJECT_LIST&lt;/code&gt;, and data access controls): &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/selai/examples-using-select-ai.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/selai/examples-using-select-ai.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DBMS_XPLAN.DISPLAY_CURSOR&lt;/code&gt; (19c): &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/19/arpls/DBMS_XPLAN.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/19/arpls/DBMS_XPLAN.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Licensing Information User Manual (AWR/ASH availability/licensing): &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/19/dblic/index.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/19/dblic/index.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DBMS_VECTOR&lt;/code&gt; (26ai): &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/arpls/DBMS_VECTOR.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/arpls/DBMS_VECTOR.html&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>database</category>
      <category>llm</category>
    </item>
    <item>
      <title>AI agents know SQL. Oracle Database Skills teach them Oracle</title>
      <dc:creator>Mark Nelson</dc:creator>
      <pubDate>Wed, 13 May 2026 14:02:12 +0000</pubDate>
      <link>https://dev.to/oracledevs/ai-agents-know-sql-oracle-database-skills-teach-them-oracle-n5m</link>
      <guid>https://dev.to/oracledevs/ai-agents-know-sql-oracle-database-skills-teach-them-oracle-n5m</guid>
      <description>&lt;p&gt;This is article 1 of 8 in my Oracle Database Skills series.&lt;/p&gt;

&lt;p&gt;Most assistants can write SQL. That’s not the same as knowing Oracle Database. The difference shows up in production. A model that learned generic patterns can assume another vendor’s isolation semantics, borrow date arithmetic that silently miscounts in Oracle, rely on hints that “fix” one plan but fight the optimizer elsewhere, or forget Edition‑Based Redefinition (EBR) during a migration. Give that assistant a live connection and those differences turn into risk.&lt;/p&gt;

&lt;p&gt;Oracle Database Skills change the operating model. Instead of hoping a generalist remembers every Oracle rule, you route it—one decision at a time—to Oracle‑authored, source‑backed instructions. When action is appropriate, you expose only small, named tools via Oracle MCP servers (MCP = Model Context Protocol), never a blank connection string. And you keep Oracle’s governance in the path so identity, scope, and evidence ride with every request. In short: route first, act through bounded tools, and let the database prove what happened.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwz7qk0eibiasgs9d28cq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwz7qk0eibiasgs9d28cq.png" alt=" " width="800" height="827"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why generic SQL knowledge fails on Oracle Database
&lt;/h2&gt;

&lt;p&gt;Large models generalize across vendors; your database does not. Oracle’s transaction semantics, time idioms, plan management, and governance are exactly where generic advice goes sideways.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Isolation and consistency are different in Oracle. Many public answers treat isolation levels as dials that change write visibility. Oracle’s multi‑version read consistency gives readers a clean snapshot but doesn’t use the same language or behavior as those engines. Ask a generalist assistant to “make this serializable” and you can get a non‑Oracle explanation—or an irrelevant fix. See Oracle Database Concepts on consistent reads for details.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Time math bites hard. DATE/TIMESTAMP/INTERVAL semantics and NLS/time zone interactions vary by vendor; Oracle requires vendor‑specific idioms. Skills provide tested patterns for Oracle SQL and PL/SQL so an assistant doesn’t have to guess which expression respects your NLS settings, time zones, and interval choices.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Plan stability demands a different order of operations. Oracle recommends maintaining accurate optimizer statistics and using SQL Plan Management (baselines) for stability; avoid over‑reliance on ad‑hoc hints in application code. Skills teach that progression so the assistant recommends fixes that survive the next gather‑stats job.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Governance cannot be an afterthought. “Connect and run” examples usually ignore Virtual Private Dat&lt;br&gt;
abase (VPD), Unified Auditing, Fine‑Grained Auditing, Data Redaction, Transparent Data Encryption (TDE), and network ACLs/private endpoints. In Oracle, those are how you make AI access presentable to a DBA, a security reviewer, or an auditor. If you want to ship, keep those controls in the path.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Route first with Oracle Database Skills (progressive discovery)
&lt;/h2&gt;

&lt;p&gt;The oracle/skills repository is not a doc dump. It’s installable operating instructions, loaded sparingly—one file at a time—so assistants (and humans) can make the next decision correctly without carrying 200 pages of context. The Database domain (oracle/skills/db) is organized the way you work: agent safety; features like Select AI, AI Profiles, and Vector Search; app frameworks; performance; DevOps and migrations; SQLcl; and security. The front door is a routing file: db/SKILL.md.&lt;/p&gt;

&lt;p&gt;Progressive discovery narrows the model’s attention to what moves the work forward, preserves version truth, and leaves a clean review trail. Because the assistant reads only the next skill, it doesn’t mix versions or unrelated features. Skills mark Oracle Database 19c as the baseline and flag when you need Autonomous Database (Serverless) or Oracle Database 23ai/26ai features. Teams can see which Oracle sources the assistant consulted, in what order, and why.&lt;/p&gt;

&lt;p&gt;A concrete path makes this real. Suppose you’re building retrieval‑augmented generation on Oracle. The route in db/SKILL.md steers you through Select AI profiles (to set provider and scope), then Vector Search basics (to choose the right data model and index), then DBMS_VECTOR (to make retrieval programmable). You don’t need AWR or EBR on day one. The route keeps you focused until you do.&lt;/p&gt;

&lt;h2&gt;
  
  
  Act through bounded tools: Oracle’s MCP surfaces
&lt;/h2&gt;

&lt;p&gt;Sometimes the assistant must do more than explain. It needs to run a read‑only query, inspect a schema, or check a plan. That’s where Oracle’s MCP servers help: they expose small, named capabilities rather than hand the assistant a general‑purpose connection.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Local development: SQLcl MCP server. Built into SQLcl, it exposes a concise set of named tools—for example, to list saved connections, connect using a saved connection, and run SQL—and supports configurable “restrict levels,” with higher numbers enforcing tighter limits. As of SQLcl 25.4, the MCP server defaults to restrict level 4 (most restrictive). Confirm your version and configuration. Teams can set the most restrictive mode for local experiments. SQLcl MCP can also leave evidence when configured: it can log activity (for example, to a DBTOOLS$MCP_LOG table), set V$SESSION.MODULE and V$SESSION.ACTION to identifiable values (such as the MCP client and a client‑provided model/agent label), and tag LLM‑generated statements with a comment. See the SQLcl MCP docs linked below for version‑specific details.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Managed, governed execution: Autonomous Database (Serverless) MCP server. Enable it on a specific Autonomous Database (via an OCI free‑form tag) and you get a per‑database HTTP endpoint and authentication that serve built‑in tools or custom tools you define with DBMS_CLOUD_AI_AGENT.CREATE_TOOL. Identity and roles apply as usual. So do network ACLs and private endpoints, VPD policies, and Unified Auditing and Fine‑Grained Auditing when configured. If your team wants a controlled tool surface without hosting anything, this is the right shape: the database remains the control plane for what tools exist and who can use them.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The shared property is restraint. In both cases, the assistant gets the minimum set of named actions appropriate for the task, and Oracle‑native controls keep doing their job.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep Oracle governance in the path (trust and evidence)
&lt;/h2&gt;

&lt;p&gt;Oracle’s governance features aren’t an appendix; they are the safety net that lets you ship. Roles and least privilege define what an assistant can reach. Network ACLs and private endpoints restrict egress. VPD enforces row‑level policies you already rely on. Unified Auditing and Fine‑Grained Auditing can be configured to record the activity you care about. Data Redaction and TDE protect sensitive data at query time and at rest. These controls are available and can be configured; they are not enabled by default in every environment.&lt;/p&gt;

&lt;p&gt;The benefit for AI work is simple once you see it: your proof is in the database. When actions go through SQLcl MCP or the Autonomous Database (Serverless) MCP server, the same identity, scoping, and auditing options that protect your applications also protect your assistant. The question becomes less “Can we trust the agent?” and more “Show me the database evidence.”&lt;/p&gt;

&lt;h2&gt;
  
  
  Mini demo: install skills and open the routing door
&lt;/h2&gt;

&lt;p&gt;Begin without touching a database or setting up MCP. Fetch the Database Skills domain and open the routing file. From there, pick the path that matches your role and objective.&lt;/p&gt;

&lt;p&gt;You only need internet access, Node.js installed, and a text editor. This step does not connect to a database, handle credentials or secrets, or change any MCP restrict levels.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx skills add oracle/skills/db
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This installs the Database domain into your working directory. Open db/SKILL.md in your editor. If you’re an app developer wiring a Spring app, follow the frameworks route. If you’re an AI engineer, start with the agent and features routes (Select AI, AI Profiles, Vector) before you let any assistant write SQL. If you’re a DBA, begin with performance and security so the assistant speaks AWR/ASH/optimizer and respects governance. Keep the habit: load only the next file you need, then stop. That discipline keeps model context clean and your review surface small.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to find this in Oracle docs and repos
&lt;/h2&gt;

&lt;p&gt;The instruction layer is the skills repo itself, with db/SKILL.md acting as the router. The controlled action layer is documented for both entry points: SQLcl MCP (restrict levels, tool catalog examples, saved connections, monitoring, activity logging and tagging) and the Autonomous Database (Serverless) MCP server (enablement via OCI free‑form tag, per‑database endpoint and authentication, custom tool publication, and governance integration). The trust layer is the familiar security stack: roles and privileges; network ACLs and private endpoints; VPD; Unified Auditing and Fine‑Grained Auditing when configured; Data Redaction; encryption. Each is a first‑class Oracle feature you can point to in docs and verify in your environment.&lt;/p&gt;

&lt;p&gt;Source links:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Skills repo and routing (start at db/SKILL.md)&lt;/li&gt;
&lt;li&gt;SQLcl MCP (using the server; logging/tagging; restrict levels)&lt;/li&gt;
&lt;li&gt;Autonomous Database (Serverless) MCP server (enable and use)&lt;/li&gt;
&lt;li&gt;Oracle Database Concepts — Consistent Reads/MVRC&lt;/li&gt;
&lt;li&gt;Oracle SQL Language Reference — Data Types (DATE/TIMESTAMP/INTERVAL and NLS)&lt;/li&gt;
&lt;li&gt;Optimizer statistics and SQL Plan Management&lt;/li&gt;
&lt;li&gt;Oracle Database Security Guide (19c baseline)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Version scope and environment notes
&lt;/h2&gt;

&lt;p&gt;Most skills target Oracle Database 19c as the baseline. Files that require features from Autonomous Database (Serverless) or newer Oracle Database 23ai/26ai releases—such as Select AI, AI Profiles, Vector Search, or the Autonomous Database (Serverless) MCP server—are labeled in the repo. If you work in a mixed‑version estate, treat those labels as gates and route accordingly. For hands‑on tries in this opener, you need only a local machine with git and a text editor. MCP client steps and database connectivity come later, when action is appropriate.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s out of scope (in this opener)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Enabling or configuring the Autonomous Database (Serverless) MCP server&lt;/li&gt;
&lt;li&gt;Connecting to any database or handling credentials&lt;/li&gt;
&lt;li&gt;Changing SQLcl MCP restrict levels&lt;/li&gt;
&lt;li&gt;Configuring Unified Auditing or Fine‑Grained Auditing&lt;/li&gt;
&lt;li&gt;Demonstrating Select AI or Vector Search live&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A short story from the field
&lt;/h2&gt;

&lt;p&gt;A team asked an assistant to “optimize a daily sales query.” The first answer added an index and a couple of hints that looked fine in isolation. The skills route would have sent it somewhere else: read the explain-plan and optimizer-stats files first; check cardinalities and stale stats; consider a plan baseline if volatility is the real problem. That path took longer but avoided chasing a hint that didn’t survive the next gather‑stats job. The difference wasn’t model IQ—it was having Oracle‑specific operating instructions and the habit of loading only what came next.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this series goes from here
&lt;/h2&gt;

&lt;p&gt;Everything you need to begin is in your editor: the skills repo, the routing file, and the mental model—route, act, trust. We’ll build outward from that front door, showing how local and managed MCP surfaces work when action is warranted, and how Select AI and Vector Search improve reasoning close to your data. Expect short, source‑backed posts that follow the same grammar:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Route: pick a path in db/SKILL.md for your role and task.&lt;/li&gt;
&lt;li&gt;Act: use SQLcl MCP locally or the Autonomous Database (Serverless) MCP server when you need controlled actions.&lt;/li&gt;
&lt;li&gt;Guardrail: keep roles, ACLs/private endpoints, VPD, and auditing in‑path for evidence and scope.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The eight posts are organized into four groups:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Foundation
1 — this post; 2 — Route, Don’t Flood
Mental model and &lt;code&gt;db/SKILL.md&lt;/code&gt; as routing layer&lt;/li&gt;
&lt;li&gt;Action
3 — SQLcl MCP; 4 — Managed MCP in Autonomous AI Database
Local and remote bounded action surfaces&lt;/li&gt;
&lt;li&gt;Intelligence
5 — Select AI and AI Profiles; 6 — Vector‑Native RAG
NL2SQL reasoning quality and in‑database retrieval&lt;/li&gt;
&lt;li&gt;Rollout
7 — Agent‑Safe Change Delivery; 8 — The Trust Layer
Safe change workflows and enterprise governance&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion: a safer default for Oracle‑backed AI
&lt;/h2&gt;

&lt;p&gt;If your assistant can touch Oracle Database, “just let it connect” isn’t an acceptable plan. Put the skills repo between the model and the problem so it learns to work the Oracle way. When execution is appropriate, send it through SQLcl MCP or the Autonomous Database (Serverless) MCP server so actions are small, named, and governed. Keep Oracle’s controls—roles, ACLs, VPD, auditing, redaction, encryption—in the path so every action is scoped and traceable.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>database</category>
      <category>sql</category>
    </item>
    <item>
      <title>Oracle AI Agent Memory: A Governed, Unified Memory Core for Enterprise AI Agents</title>
      <dc:creator>Wojtek Pluta</dc:creator>
      <pubDate>Mon, 04 May 2026 08:57:04 +0000</pubDate>
      <link>https://dev.to/oracledevs/oracle-ai-agent-memory-a-governed-unified-memory-core-for-enterprise-ai-agents-4ml8</link>
      <guid>https://dev.to/oracledevs/oracle-ai-agent-memory-a-governed-unified-memory-core-for-enterprise-ai-agents-4ml8</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This article is syndicated from the original post on &lt;a href="https://blogs.oracle.com/developers/oracle-ai-agent-memory-a-governed-unified-memory-core-for-enterprise-ai-agents" rel="noopener noreferrer"&gt;blogs.oracle.com&lt;/a&gt;. Read the canonical version there for the latest updates.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Recently, Oracle introduced the &lt;a href="https://www.oracle.com/database/ai-agent-memory/" rel="noopener noreferrer"&gt;Oracle AI Agent Memory&lt;/a&gt; Python package, a model and framework-agnostic memory solution that gives enterprise AI teams a governed memory core on Oracle AI Database: short-term threads with summaries and context cards, long-term durable memories with vector search, automatic LLM-based memory extraction, and the governance and isolation production agents require.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F05%2FDiagram-1-transparent-1024x698.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F05%2FDiagram-1-transparent-1024x698.png" title="Oracle Agent Memory: a unified agent memory layer built on Oracle AI Database." alt="Diagram titled “Oracle Agent Memory” showing a layered architecture. At the top, a framework layer includes LangGraph, Claude Agent SDK, OpenAI Agent SDK, WayFlow, and custom integrations feeding into a unified Oracle Agent Memory client. The client manages working, semantic, episodic, and procedural memory types. Below, the system is powered by Oracle AI Database, which provides governed, isolated, audited, encrypted, and highly available infrastructure with vector search, graph traversal, and relational query capabilities." width="800" height="545"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Oracle Agent Memory: a unified agent memory layer built on Oracle AI Database.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Oracle AI Agent Memory is available now via &lt;a href="https://pypi.org/project/oracleagentmemory/" rel="noopener noreferrer"&gt;PyPI&lt;/a&gt; as &lt;code&gt;oracleagentmemory&lt;/code&gt; and documented in the &lt;a href="https://docs.oracle.com/en/database/oracle/agent-memory/26.4/" rel="noopener noreferrer"&gt;Oracle Help Center&lt;/a&gt;. It is designed to replace the patchwork memory stack most production agents inherit with a single governed memory substrate built on Oracle AI Database.&lt;/p&gt;

&lt;p&gt;This is the difference between an agent that is memory-augmented, given a vector store to consult, and one that is memory-aware, responsible for reading from and writing to its own governed, durable state with one enterprise-grade backend.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"&lt;em&gt;Agent memory has shifted from a research curiosity to a production requirement in under two years. Teams shipping serious agentic systems need a backend that handles vectors, structured data, and transactional consistency in one place, not three stitched together. Oracle AI Database is one of the few platforms that delivers all of that natively, which is why we built Hindsight to run on it as a first-class backend.&lt;/em&gt;"&lt;br&gt;
&lt;em&gt;—&lt;/em&gt; &lt;em&gt;Chris Latimer, Co-Founder &amp;amp; CEO of Vectorize&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Why Agent Memory, Why Now
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F05%2FDiagram-2-transparent-1024x914.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F05%2FDiagram-2-transparent-1024x914.png" title="The four types of agent memory: working, semantic, episodic, and procedural." alt="Diagram titled “The four types of agent memory” showing an Oracle Agent Memory taxonomy. A central “Agent memory” layer branches into four categories: working memory (active state like current conversation and in-flight tasks), semantic memory (durable facts such as user preferences and entity data), episodic memory (specific past experiences like prior sessions and resolved tasks), and procedural memory (behavioral rules and tool preferences). Each category includes a short description and example elements." width="800" height="714"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The four types of agent memory: working, semantic, episodic, and procedural.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Most agent implementations treat memory as a bolt-on. A vector store consulted at retrieval time, a chat history table glued on beside it, and whatever hand-written extraction logic the team can maintain.&lt;/p&gt;

&lt;p&gt;That stack holds together for a demo. It falls apart from the moment an enterprise AI team asks questions that actually matter. Who owns the memory? Where is it governed? How do we isolate tenants? How do we audit what the agent learned, and how do we forget it on request?&lt;/p&gt;

&lt;p&gt;Context windows have grown over the years, but no context window is large enough to hold the full state of a long-running agent: weeks of user preferences, accumulated domain knowledge, prior tool outcomes, evolving task state, and the reasoning history that makes each decision defensible.&lt;/p&gt;

&lt;p&gt;Agents need memory for the same reasons people do: to hold an active state while working on a problem, to retain facts learned over time, to recall specific past experiences, and to encode behavioral rules and procedures.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A practical taxonomy for agent memory commonly used in agent design covers four types:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Working memory is the active state the agent is reasoning over right now, the running conversation and the scratchpad the model sees at inference time.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Semantic memory is the durable facts and knowledge the agent accumulates about users, entities, and the world: preferences, canonical definitions, structured reference data.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Episodic memory is specific past experiences the agent can recall, what happened on a prior session, what the user asked three weeks ago, how a similar task resolved last time.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Procedural memory is the behavioral rules, guidelines, and learned procedures that shape how the agent acts, how to handle customers, which tools to prefer, what not to do.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These are not four different systems. They are four access patterns over the same underlying state, which is what makes a unified memory core the right architectural answer rather than four bolted-together services.&lt;/p&gt;




&lt;h2&gt;
  
  
  Oracle AI Database as the Memory Core
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F05%2FDiagmar-3-transparent-775x1024.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F05%2FDiagmar-3-transparent-775x1024.png" title="Reference architecture for Oracle AI Agent Memory on Oracle AI Database." alt="Reference architecture diagram titled “Oracle AI Agent Memory on Oracle AI Database.” It shows the flow from a customer-owned application tier (end users interacting via natural language with an AI agent built using frameworks like LangGraph or OpenAI Agent SDK) to the Oracle-owned memory SDK and Oracle AI Database. The Oracle AI Agent Memory layer provides APIs for search, message handling, and memory extraction with tenant isolation and governance. It connects to Oracle AI Database, which supports vector search, relational queries, graph traversal, and JSON storage. The diagram also highlights enterprise capabilities like backup, replication, high availability, encryption, access control, and auditing, with arrows indicating request and response flow." width="775" height="1024"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Reference architecture for Oracle AI Agent Memory on Oracle AI Database.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Oracle AI Database combines vector similarity search, relational querying, and graph-aware data access in one governed engine, enabling semantic recall alongside precise transactional and relationship-centric retrieval. Combined with Oracle's operational story, backups, replication, high availability, encryption, fine-grained access control, and audit, teams get a path from notebook to regulated production without swapping storage layers, rewriting compliance reviews, or stitching together bespoke isolation logic along the way.&lt;/p&gt;

&lt;p&gt;Memory engineering, as a discipline, demands substrate choices that hold up under the access patterns a real enterprise agent actually has: concurrent writes, per-user and per-tenant scoping, full audit, and semantic retrieval at scale.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;“Enterprise agents need an agent memory solution with robust security guarantees, strong governance controls, sophisticated workload isolation, as well as deep integration within the enterprise data platform. Oracle AI Agent Memory greatly simplifies building agent memory solutions by consolidating what are usually multiple separate and fragmented services, within the converged database architecture that customers already trust for their most critical data.”&lt;/em&gt;&lt;br&gt;
&lt;em&gt;— Tirthankar Lahiri, SVP, Mission-Critical Data and AI Engines, Oracle Database&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Production agent memory carries two loads. Developers wire the stack together: vector store, chat log, extraction scripts, isolation logic, governance per piece. Agents reason over the fragments, deciding what to retrieve from where and fitting the relevant world into a finite context window each turn.&lt;/p&gt;

&lt;p&gt;Oracle AI Agent Memory lifts both. One governed client replaces the four-service stack, with one set of credentials, one compliance review, and one backup story. Working, semantic, episodic, and procedural memory share one substrate and one retrieval surface, so the model reasons over a coherent view of its state. Summarization and scoped retrieval put the right subset into context at the right moment, freeing the model to spend its reasoning budget on the task rather than memory bookkeeping.&lt;/p&gt;

&lt;p&gt;Automatic LLM-based extraction turns conversation into durable memories without hand-rolled prompt chains. Multi-tenant isolation is enforced at the store layer, so a single schema can host multiple deployments without cross-tenant leakage. And because the SDK is framework-agnostic, integrating with &lt;a href="https://github.com/oracle-devrel/oracle-ai-developer-hub/tree/main/notebooks/agent_memory" rel="noopener noreferrer"&gt;LangGraph, Claude Agent SDK, OpenAI Agent SDK, WayFlow, and custom harnesses&lt;/a&gt;, teams aren't locked into a single runtime to get the substrate.&lt;/p&gt;




&lt;h2&gt;
  
  
  Key Benefits For AI Workloads
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F05%2FDiagram-4-transparent-1024x584.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F05%2FDiagram-4-transparent-1024x584.png" title="Configuration: gpt-5.5, reasoning effort xhigh, nomic-embed-v1.5 embeddings, local HNSW index, top-K = 200. X-axis truncated; all categories scored above 88%." alt="Bar chart showing LongMemEval results with 93.8% overall accuracy (469/500). Per-category scores: single-session assistant 100%, temporal reasoning 96.2%, knowledge update 94.9%, single-session user 94.3%, single-session preference 93.3%, and multi-session 88.0%. Configuration notes include GPT-5.5, nomic-embed-v1.5 embeddings, and HNSW index." width="800" height="456"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Configuration: gpt-5.5, reasoning effort xhigh, nomic-embed-v1.5 embeddings, local HNSW index, top-K = 200. X-axis truncated; all categories scored above 88%.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Oracle AI Agent Memory is built for the operational realities of running AI agents in production.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Production-grade recall on long-horizon memory benchmarks.&lt;/strong&gt; On &lt;a href="https://arxiv.org/abs/2410.10813" rel="noopener noreferrer"&gt;LongMemEval&lt;/a&gt;, the standard academic benchmark for long-context agent memory, Oracle AI Agent Memory scores &lt;strong&gt;93.8%&lt;/strong&gt; (469 of 500), with the strongest results on the categories that matter most for production agents: 100% on single-session assistant recall, 96% on temporal reasoning, and 95% on knowledge-update tasks. Multi-session recall, the hardest category in the benchmark, lands at 88%. Configuration: OpenAI gpt-5.5 (reasoning effort xhigh), nomic-embed-text-v1.5 embeddings, local HNSW index, top-K = 200.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bounded per-turn cost as sessions extend.&lt;/strong&gt; Periodic thread summarization, durable memory extraction, and prompt-time message compaction keep the working context bounded as conversations grow. In an 80-turn scripted conversation, Oracle AI Agent Memory held per-request input around 1,300 tokens for the full run while a flat-history baseline grew linearly past 13,900 — roughly 9.5× more tokens per request by the final turn, and a much steeper bill across the full conversation. Teams shipping long-running agents trade a linear-in-history cost curve for a flat one.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F05%2FDiagram-5-transparent-1024x552.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F05%2FDiagram-5-transparent-1024x552.png" title="80-turn ChromAtlas-ND scripted conversation · gpt-5.4 (raw OpenAI client, no framework). Token estimate: chars / 4 (notebook convention)" alt="Line chart comparing tokens per request over 80 conversation turns. A gray line (no memory management) rises steadily to ~13,900 tokens, while a red line (Oracle AI Agent Memory) stays flat around ~1,300 tokens. The chart highlights ~9.5× lower token usage with memory, showing stable context size as conversations grow." width="800" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;80-turn ChromAtlas-ND scripted conversation · gpt-5.4 (raw OpenAI client, no framework). Token estimate: chars / 4 (notebook convention)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Better answers than a flat-history baseline.&lt;/strong&gt; A flat-history agent has the entire verbatim conversation in its prompt — every fact ever mentioned, in order. By rights it should be hard to beat on recall. Across the same 80-turn conversation, evaluated by an impartial gpt-5.4 judge on accuracy, completeness, relevance, and coherence, Oracle AI Agent Memory won &lt;strong&gt;48 turns to flat history's 13&lt;/strong&gt;, with 19 ties: &lt;strong&gt;3.7× more wins despite the baseline's information advantage&lt;/strong&gt;. A retrieved context card focuses the model on what matters; a sprawling transcript dilutes attention across noise.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F05%2FDiagram-6-transparent-1024x563.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F05%2FDiagram-6-transparent-1024x563.png" title="80-turn ChromAtlas-ND scripted conversation; judge: gpt-5.4; scored on accuracy, completeness, relevance, coherence" alt="Bar chart showing agent performance over 80 turns. Oracle AI Agent Memory wins 48 turns (60%), compared to 13 wins (16%) for a naive flat history" width="800" height="440"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;80-turn ChromAtlas-ND scripted conversation; judge: gpt-5.4; scored on accuracy, completeness, relevance, coherence&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cost is a tunable knob, not a fixed value.&lt;/strong&gt; The summarization trigger controls how aggressively the package compacts thread context, and it moves the cost-fidelity trade-off directly. In an 8-query demo conversation (five runs per threshold), a 10,000-token trigger landed at a mean of 121,268 total tokens, about 60% under the 306,823-token flat-history baseline. As the trigger rises, the package compacts less often and preserves more raw context per turn; by a 50–70k trigger, mean total tokens approach or exceed the baseline, and run-to-run variance widens. Teams pick the threshold that matches their answer-quality requirements and lock in the cost envelope they want, rather than accepting whatever curve a fragmented stack produces.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F05%2FDiagram-7-transparent-scaled.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F05%2FDiagram-7-transparent-scaled.png" title="Memory Agent Efficiency vs Summarization Threshold on Demo Conversation. Num queries = 8; num runs per threshold = 5." alt="Line chart titled “Oracle Agent Memory Threshold Sweep” showing total tokens vs. summarization trigger (10k–70k). Mean tokens rise as the threshold increases, with shaded min–max and standard deviation bands. The lowest mean (~121k tokens) occurs at 10k, while higher thresholds approach or exceed a dashed naive baseline (~306k tokens)." width="800" height="438"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Memory Agent Efficiency vs Summarization Threshold on Demo Conversation. Num queries = 8; num runs per threshold = 5.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One backend, every Python runtime.&lt;/strong&gt; &lt;a href="https://github.com/oracle-devrel/oracle-ai-developer-hub/tree/main/notebooks/agent_memory" rel="noopener noreferrer"&gt;LangGraph, the Claude Agent SDK, the OpenAI Agents SDK, WayFlow, and custom Python harnesses&lt;/a&gt; all instantiate the same OracleAgentMemory client and read and write the same Oracle Database store. Teams running more than one framework no longer rebuild memory per runtime, and migrations between frameworks no longer mean migrating memory.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Primitives for audit and erasure on a single substrate.&lt;/strong&gt; Every record carries user, agent, thread, and timestamp scoping fields, and the SDK exposes search, list, and per-record delete operations across memories, threads, and messages, so callers can locate records for a subject and remove them on request. Oracle Database's native auditing covers the storage layer underneath. Compliance reviews land on a single substrate (one database with audit, retention, and access controls already in the data plane) rather than four services with four reviews.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One vendor relationship for production agent memory.&lt;/strong&gt; A single Oracle AI Database instance carries vector search, structured state, JSON document retrieval, transactional consistency, and database-native audit. No second vector database to license, no third service to monitor and scale, no fourth backup pipeline to maintain.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who Oracle AI Agent Memory Is For
&lt;/h2&gt;

&lt;p&gt;Oracle AI Agent Memory is designed for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AI Developers and engineers&lt;/strong&gt; building production agents who need durable short-term and long-term memory in one place, with enterprise security and isolation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Teams already running Oracle AI Database&lt;/strong&gt; who want their agents to write to the same governed backend as the rest of the business&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Technical leaders&lt;/strong&gt; evaluating Oracle AI Database for agent memory infrastructure at scale, with compliance and audit requirements&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;Install the Oracle AI Agent Memory package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;oracleagentmemory
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A minimal end-to-end loop in Python looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;oracleagentmemory&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AgentMemory&lt;/span&gt;

&lt;span class="n"&gt;memory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AgentMemory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;connection_string&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user_123&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Add conversation turns to a short-term thread
&lt;/span&gt;&lt;span class="n"&gt;thread_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_thread&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user_123&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_messages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;thread_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;I prefer vegan meals.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;assistant&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Noted.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="c1"&gt;# Extract durable long-term memories from the thread
&lt;/span&gt;&lt;span class="n"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extract_memories&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;thread_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Scoped search over long-term memory, enforced per-user
&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user_123&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dietary preferences&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Code samples are illustrative; the final API surface is documented in the &lt;a href="https://docs.oracle.com/en/database/oracle/agent-memory/26.4/" rel="noopener noreferrer"&gt;Oracle Help Center&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/oracle-devrel/oracle-ai-developer-hub/tree/main/notebooks/agent_memory" rel="noopener noreferrer"&gt;quickstart notebook and framework how-to guides&lt;/a&gt; are available in the Oracle AI Developer Hub, and the full API reference is available in the &lt;a href="https://docs.oracle.com/en/database/oracle/agent-memory/26.4/" rel="noopener noreferrer"&gt;Oracle Help Center&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Oracle AI Agent Memory is the first release of a broader commitment to a governed memory substrate enterprise agents need. Memory engineering is still an emerging discipline. The infrastructure behind it should not be.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>database</category>
      <category>oracle</category>
    </item>
    <item>
      <title>What Is Agent Memory? A Beginner’s Guide for AI Developers</title>
      <dc:creator>Anya Summers</dc:creator>
      <pubDate>Wed, 29 Apr 2026 09:11:11 +0000</pubDate>
      <link>https://dev.to/oracledevs/what-is-agent-memory-a-beginners-guide-for-ai-developers-5djd</link>
      <guid>https://dev.to/oracledevs/what-is-agent-memory-a-beginners-guide-for-ai-developers-5djd</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; Agent memory is stored state an AI agent can retrieve across sessions to maintain continuity. A bigger context window does not fix the problem. Once memory has to persist, be scoped to the right user, and be retrieved reliably, it becomes a data problem, and is often best handled in a database such as Oracle AI Database.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;You can build a convincing AI agent surprisingly fast. Give it a model, wire up a few tools, and it can look sharp in the first session. Then the user comes back the next day. They ask a follow-up question. They refer to a failed attempt from yesterday. They expect the agent to remember that they prefer Python examples and concise answers. Instead, the agent starts from scratch.&lt;/p&gt;

&lt;p&gt;That is usually the moment when a demo stops feeling clever and starts feeling flimsy. A lot of beginner guides blur this point. They talk as if a larger context window solves the whole problem. It does not. A larger context window gives the model more room to work during one session. It does not give the system a memory of what happened last week. When the session ends, the context goes with it. Memory is the layer that preserves what matters.&lt;/p&gt;




&lt;h2&gt;
  
  
  What You'll Learn
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;What agent memory actually is, and why a bigger context window is not a substitute&lt;/li&gt;
&lt;li&gt;The four useful types of agent memory: working, procedural, semantic, and episodic&lt;/li&gt;
&lt;li&gt;When memory stops being a prompt trick and starts being infrastructure&lt;/li&gt;
&lt;li&gt;How to implement a persistent semantic memory store using LangChain and Oracle AI Database&lt;/li&gt;
&lt;li&gt;Common mistakes to avoid and a checklist for building your first memory layer&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What Is Agent Memory?
&lt;/h2&gt;

&lt;p&gt;Agent memory is the information an AI agent can carry from one interaction to the next. That information might be a user preference, a summary of an earlier conversation, a previous task result, or facts the system has learned and may need later.&lt;/p&gt;

&lt;p&gt;The key point is simple. It is not enough that the model saw the information once. The system needs to be able to bring it back when it matters.&lt;/p&gt;

&lt;p&gt;Imagine a user tells an assistant three things today:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;they prefer concise answers&lt;/li&gt;
&lt;li&gt;they are working in Python&lt;/li&gt;
&lt;li&gt;the last attempt failed because their API key had expired&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the assistant can use that information tomorrow without being told again, it has memory. If the user has to repeat all three points, it does not. That is the difference.&lt;/p&gt;




&lt;h2&gt;
  
  
  Context Window vs Memory: What Is the Difference?
&lt;/h2&gt;

&lt;p&gt;This is the part that trips people up. A context window is the text the model can see right now. That includes the prompt, the recent messages, retrieved documents, tool outputs, and any system instructions passed into the current call. It is the model's live working space. Memory is different. Memory is stored state the system can recover later.&lt;/p&gt;

&lt;p&gt;The simplest analogy is this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the context window is the desk&lt;/li&gt;
&lt;li&gt;memory is the filing cabinet&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A bigger desk is useful. You can spread out more notes and hold more detail in front of the model. But the desk gets cleared. The filing cabinet is what lets you come back tomorrow, open the right folder, and pick up where you left off.&lt;/p&gt;

&lt;p&gt;It also helps to separate memory from Retrieval Augmented Generation (RAG). RAG brings in external knowledge (e.g.,company PDFs) so the model can answer a question with better grounding. Memory, by contrast, preserves useful state from previous interactions. One helps the agent know more in the moment; the other helps it behave with continuity over time.&lt;/p&gt;

&lt;p&gt;In practice, the strongest systems usually use all three layers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;context window for active reasoning&lt;/li&gt;
&lt;li&gt;retrieval for outside knowledge&lt;/li&gt;
&lt;li&gt;memory for continuity across sessions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A simple scenario makes the difference concrete. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Monday:&lt;/strong&gt; The user says, "I am learning Python and I prefer short answers." The agent helps them debug a script and the session ends. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tuesday:&lt;/strong&gt; The user returns and asks, "Can you help me sort a list?" &lt;/p&gt;

&lt;p&gt;Without memory, the agent gives a long answer in whichever language it guesses. With memory, the agent retrieves the user's preference, responds in Python, and keeps the answer concise. Same model. &lt;/p&gt;

&lt;p&gt;Same prompt. Different system around it.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Four Types of Agent Memory
&lt;/h2&gt;

&lt;p&gt;A simple way to understand agent memory is to borrow a rough model from human memory. It's not perfect, but it's useful.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
  &lt;tr&gt;
    &lt;th&gt;Memory type&lt;/th&gt;
    &lt;th&gt;Simple meaning&lt;/th&gt;
&lt;th&gt;Example in an agent&lt;/th&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Working memory&lt;/td&gt;
    &lt;td&gt;What the agent is handling right now&lt;/td&gt;
&lt;td&gt;Current messages, tool outputs, temporary reasoning state&lt;/td&gt;
  &lt;/tr&gt;
&lt;tr&gt;
    &lt;td&gt;Procedural memory&lt;/td&gt;
    &lt;td&gt;How the agent does things&lt;/td&gt;
&lt;td&gt;Instructions, workflows, and tool-use rules&lt;/td&gt;
  &lt;/tr&gt;
&lt;tr&gt;
    &lt;td&gt;Semantic memory&lt;/td&gt;
    &lt;td&gt;Facts the agent has learned&lt;/td&gt;
&lt;td&gt;User preferences, saved facts, product knowledge&lt;/td&gt;
  &lt;/tr&gt;
&lt;tr&gt;
    &lt;td&gt;Episodic memory&lt;/td&gt;
    &lt;td&gt;Specific past events&lt;/td&gt;
&lt;td&gt;Previous sessions, task history, and failed attempts&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This framework matters because not every agent needs the same mix. A customer support agent may need semantic memory for customer preferences and episodic memory for past tickets. A coding agent may care more about procedural memory for workflows and semantic memory for project conventions. A one-shot Q&amp;amp;A bot may not need much memory at all.&lt;/p&gt;

&lt;p&gt;That's worth keeping in mind: "add memory" is not a universal requirement. It only makes sense when continuity actually improves the experience or the outcome.&lt;/p&gt;




&lt;h2&gt;
  
  
  When Does Agent Memory Become a Data Problem?
&lt;/h2&gt;

&lt;p&gt;The moment you want memory to persist, you're no longer just writing prompts. You are making storage and retrieval decisions.&lt;/p&gt;

&lt;p&gt;You need to decide:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;what is worth storing&lt;/li&gt;
&lt;li&gt;what should be ignored&lt;/li&gt;
&lt;li&gt;how memories are tied to the right user&lt;/li&gt;
&lt;li&gt;how old or stale memories get updated or removed&lt;/li&gt;
&lt;li&gt;how the system finds the right memory at the right time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is where many first agent builds get messy. Saving text is easy. Bringing back the right memory for the right user, in the right context, without pulling in noise, is the hard part. Once memory has to persist and be searchable, structure matters. You typically need metadata such as user_id, memory_type, timestamps, and maybe expiry rules. You also need a retrieval strategy that avoids surfacing irrelevant or outdated information.&lt;/p&gt;

&lt;p&gt;Persistence also introduces governance concerns. As soon as an agent is storing anything that can be traced to a person, you are dealing with personally identifiable information, and every mature system needs answers to a small set of questions. What personal data is being stored? How long is it kept? How does a user request deletion, and can the system actually honour that request? &lt;/p&gt;

&lt;p&gt;Building those answers in from day one is much easier than retrofitting them after the first audit or data subject request. Governance lives best as code and schemas, not as a Confluence page somebody hopes to find later.&lt;/p&gt;

&lt;p&gt;This is why databases appear so quickly in serious agent systems. Prompts are temporary. Memory needs storage, filtering, and lifecycle rules. If you are storing embeddings, scoping memories to a user, and reusing them later, you are designing a small data system whether you planned to or not. &lt;/p&gt;

&lt;p&gt;That sounds heavier than it is. You do not need a giant memory platform on day one. But you do need to stop thinking of memory as "extra text for the prompt". It is a system component. Without structure and filtering, memory quickly turns into noisy context that reduces answer quality instead of improving it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Architecture Overview
&lt;/h2&gt;

&lt;p&gt;At a high level, a production-ready agent memory system has three layers working together: a context window for active reasoning, a retrieval layer for outside knowledge (RAG), and a persistent memory store for continuity across sessions. The memory store is where platforms like Oracle AI Database provide the most value.&lt;/p&gt;

&lt;p&gt;Oracle AI Database is a strong fit for production memory systems for three reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Durability&lt;/strong&gt;: Memory is only valuable if it survives restarts, deployments, and the kind of quiet infrastructure changes that happen in any real engineering environment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Metadata-driven filtering&lt;/strong&gt;: Storing vectors next to structured columns like user_id, tenant_id, memory_type, and created_at means retrieval can be scoped cleanly without building a second database to hold the filters. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lifecycle control&lt;/strong&gt;: Expiry, archival, soft-delete, and audit trails are problems databases have been solving for decades, and memory needs all of them. Running vector search in the same database that already holds the relational and governance layer removes a whole category of synchronisation bugs that would otherwise appear on week three.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Python 3.9 or later&lt;/li&gt;
&lt;li&gt;Access to an Oracle AI Database 26ai instance (Autonomous Database, container, or local install)&lt;/li&gt;
&lt;li&gt;An embedding model configured and callable from your environment&lt;/li&gt;
&lt;li&gt;Basic familiarity with LangChain concepts (vector stores, retrievers)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step-by-Step Guide: A Simple Memory Layer with LangChain and Oracle
&lt;/h2&gt;

&lt;p&gt;The goal here is not to build a huge platform. It is to make the pattern concrete: save a useful memory, attach metadata, and retrieve it later when it becomes relevant.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Install the Packages
&lt;/h3&gt;

&lt;p&gt;Install the LangChain Oracle integration along with the Oracle Python driver and LangChain core.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;langchain-oracledb oracledb langchain-core
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Connect to a Persistent Store
&lt;/h3&gt;

&lt;p&gt;Open a connection to Oracle and wrap it in a LangChain OracleVS vector store. This example assumes you already have an embedding model configured. The broad idea matters more than the exact class names — the agent now has somewhere durable to store semantic memory outside the prompt.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;oracledb&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain_oracledb.vectorstores&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OracleVS&lt;/span&gt;

&lt;span class="n"&gt;connection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;oracledb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;agent_user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;password&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;dsn&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hostname:port/service&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;memory_store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OracleVS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;embedding_function&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;embeddings&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;table_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AGENT_MEMORY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Store a Memory and Retrieve It Later
&lt;/h3&gt;

&lt;p&gt;Save something worth remembering, attach metadata so it stays scoped correctly, and retrieve it when the next interaction needs it. That is the core loop.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;memory_store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_texts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;texts&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;User prefers concise answers and Python examples.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;metadatas&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user_123&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;memory_type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;preference&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;memory_store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;similarity_search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;How should I answer this user?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nb"&gt;filter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user_123&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you only remember one design lesson from this section, make it this: memory quality depends less on storing more information and more on retrieving the right information cleanly.&lt;/p&gt;




&lt;h2&gt;
  
  
  When Do You Actually Need Agent Memory?
&lt;/h2&gt;

&lt;p&gt;Not every agent needs memory. This is where it is easy to overbuild. You probably need memory when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the same user comes back repeatedly&lt;/li&gt;
&lt;li&gt;the agent needs to remember preferences or previous decisions&lt;/li&gt;
&lt;li&gt;tasks span multiple sessions&lt;/li&gt;
&lt;li&gt;the system improves when it learns from earlier outcomes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You may not need much memory when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the task is one-off question answering&lt;/li&gt;
&lt;li&gt;document retrieval is enough&lt;/li&gt;
&lt;li&gt;users are unlikely to return&lt;/li&gt;
&lt;li&gt;continuity adds more complexity than value&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A lot of first agent projects do not need a big memory layer. They need a clear use case and a small amount of well-scoped memory. That's usually a better place to start.&lt;/p&gt;




&lt;h2&gt;
  
  
  Validation &amp;amp; Troubleshooting
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Retrieval returns noise&lt;/strong&gt;: If results look bad with ten stored memories, they will be worse at ten thousand. Validate retrieval quality early by running a handful of realistic queries and inspecting what comes back.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wrong user's memories appear&lt;/strong&gt;: Every similarity_search call should include a filter on user_id. If you see cross-user leakage, check that metadata is written on every add_texts call.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stale memories resurface&lt;/strong&gt;: Add a created_at timestamp and define lifecycle rules. Preferences change. Facts expire. If the system never updates or retires old memories, it will eventually return stale context.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Connection errors to Oracle&lt;/strong&gt;: Verify your DSN string matches your service name, and that the oracledb driver can reach the host. Autonomous Database users should confirm their wallet configuration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Everything gets saved as a "memory"&lt;/strong&gt;: That sounds safe, but it usually creates noise. Decide upfront what qualifies as a memory worth storing.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Common Mistakes to Avoid
&lt;/h2&gt;

&lt;p&gt;A few mistakes show up again and again. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;First&lt;/strong&gt;, people treat a larger context window as if it solves memory. It helps within a single session. It does not create continuity on its own.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Second&lt;/strong&gt;, they save everything. That sounds safe, but it usually creates noise. If every past detail becomes a "memory", retrieval quality drops fast. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Third&lt;/strong&gt;, they skip metadata. Without fields like user_id, memory_type, or timestamps, the system has no reliable way to determine which memory belongs to whom or whether it is still relevant.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fourth&lt;/strong&gt;, they forget memory lifecycle. Preferences change. Facts expire. Previous failures become irrelevant. If the system never updates or retires old memories, it will eventually return stale context.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Finally, some teams add memory before they have proved they need it. That is backwards. Start with the user problem. Then decide whether continuity genuinely improves the product.&lt;/p&gt;




&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Agent memory is stored state an agent retrieves across sessions to maintain continuity. A context window helps with the current interaction; memory enables continuity over time.&lt;/li&gt;
&lt;li&gt;Four useful memory types are working, procedural, semantic, and episodic. Not every agent needs the same mix.&lt;/li&gt;
&lt;li&gt;As soon as memory must persist, be scoped to the right user, and be retrieved reliably, you are dealing with a data problem.&lt;/li&gt;
&lt;li&gt;Start with one memory type to begin with. Semantic memory for user preferences is usually the highest-value entry point.&lt;/li&gt;
&lt;li&gt;Scope every memory by user_id, and include memory_type and a timestamp from the first write.&lt;/li&gt;
&lt;li&gt;Oracle AI Database 26ai fits well here by combining durable storage, metadata-driven filtering, and lifecycle control in the same system that already holds your relational and governance layer.&lt;/li&gt;
&lt;li&gt;Building an agent is easy. Keeping one alive in production is where memory stops being a prompt trick and bedomes infrastructure.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What is the difference between a context window and agent memory?&lt;/strong&gt;&lt;br&gt;
A context window is the information the model can see during the current interaction. Agent memory is information the system stores and can bring back in future interactions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What are the main types of agent memory?&lt;/strong&gt;&lt;br&gt;
A simple framework uses four types: working, procedural, semantic, and episodic. In practice, most agent builds care most about semantic memory for facts and preferences, and episodic memory for past interactions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do all AI agents need memory?&lt;/strong&gt;&lt;br&gt;
No. Some agents only answer one-off questions and do fine with a prompt plus retrieval. Memory becomes useful when continuity across sessions actually improves the result.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can a vector database be used for agent memory?&lt;/strong&gt;&lt;br&gt;
Yes. A vector database or vector-capable store can work well for semantic memory, especially when you need similarity search. It still needs metadata and retrieval rules, otherwise it turns into a pile of loosely relevant text.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When should I use Oracle AI Database 26ai for agent memory?&lt;/strong&gt;&lt;br&gt;
Use it when you need durable storage, vector similarity search, and metadata-driven filtering in the same system. It is especially valuable when your application already has a relational and governance layer, because running vector search alongside it removes a whole category of synchronisation bugs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What are the limitations?&lt;/strong&gt;&lt;br&gt;
Memory architectures are still largely bespoke. There is no clean default answer for when to summarise versus store verbatim, or how to balance recall against retrieval noise as the store grows. Eviction, forgetting, and evaluation of memory systems are all genuinely open problems. Start small, instrument retrieval, and treat the design as something you will revise.&lt;/p&gt;




&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/oracle-devrel/oracle-ai-developer-hub" rel="noopener noreferrer"&gt;Oracle AI Developer Hub - GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://python.langchain.com/docs/integrations/vectorstores/oracle/" rel="noopener noreferrer"&gt;LangChain Oracle integration docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/23/vecse/" rel="noopener noreferrer"&gt;Oracle AI Vector Search documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.oracle.com/developer/resources/" rel="noopener noreferrer"&gt;Build with Oracle AI Database - Resources&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://livelabs.oracle.com/" rel="noopener noreferrer"&gt;Oracle LiveLabs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>database</category>
      <category>oracle</category>
      <category>agents</category>
    </item>
    <item>
      <title>Unified Memory Core for AI Agents</title>
      <dc:creator>Anya Summers</dc:creator>
      <pubDate>Mon, 27 Apr 2026 16:05:40 +0000</pubDate>
      <link>https://dev.to/oracledevs/unified-memory-core-for-ai-agents-3da3</link>
      <guid>https://dev.to/oracledevs/unified-memory-core-for-ai-agents-3da3</guid>
      <description>&lt;p&gt;A practical guide to building episodic, lexical, vector, and graph memory workflows in Oracle AI Database&lt;/p&gt;

&lt;p&gt;Companion notebook: &lt;a href="https://github.com/oracle-devrel/oracle-ai-developer-hub/blob/main/notebooks/unified_agent_memory_oracle_ai_database.ipynb" rel="noopener noreferrer"&gt;Unified Agent Memory with Oracle AI Database&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Key takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A unified memory core combines episodic, lexical, semantic, and relationship-aware retrieval in one governed platform.&lt;/li&gt;
&lt;li&gt;Hybrid retrieval (Oracle Text + vector + metadata filters) improves reliability in enterprise queries.&lt;/li&gt;
&lt;li&gt;GRAPH_TABLE adds business relationship context beyond nearest-neighbor similarity.&lt;/li&gt;
&lt;li&gt;DBMS_SCHEDULER and VPD patterns make memory lifecycle and security operational.&lt;/li&gt;
&lt;li&gt;The companion notebook demonstrates all core patterns in a runnable workflow.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What agent memory is and why it matters
&lt;/h2&gt;

&lt;p&gt;Agent memory is the stored context an AI system can access across steps, sessions, or workflows. In practice, it supports several critical functions:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;State persistence&lt;/strong&gt; – remembering what the agent is currently doing&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Context continuity&lt;/strong&gt; – carrying forward prior user goals and constraints&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Knowledge retrieval&lt;/strong&gt; – finding facts, documents, and learned abstractions&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Workflow resilience&lt;/strong&gt; – resuming long-running tasks after delays or failures&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Personalization&lt;/strong&gt; – adapting behavior based on historical interactions&lt;/p&gt;

&lt;p&gt;This matters because modern agents are not just answering isolated questions. They are coordinating tools, operating over enterprise systems, and producing outputs that depend on both immediate context and historical knowledge.&lt;/p&gt;

&lt;p&gt;At a high level, memory is what allows an agent to behave less like a stateless API and more like a system that can learn and adapt over time.&lt;/p&gt;




&lt;h2&gt;
  
  
  You'll learn how to
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;model episodic memory with JSON and query it using SQL/JSON.&lt;/li&gt;
&lt;li&gt;run lexical retrieval with Oracle Text and semantic retrieval with vectors.&lt;/li&gt;
&lt;li&gt;combine lexical, semantic, and metadata constraints into hybrid retrieval.&lt;/li&gt;
&lt;li&gt;traverse user-ticket-document context with SQL Property Graph and GRAPH_TABLE.&lt;/li&gt;
&lt;li&gt;apply lifecycle and tenant-aware security patterns for governed agent memory.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Architecture overview
&lt;/h2&gt;

&lt;p&gt;The unified memory flow keeps ingestion, storage, retrieval, and governance inside Oracle AI Database. Episodic events are stored in JSON, reusable knowledge is retrieved with Oracle Text and vectors, relationship context is traversed with SQL Property Graph, and lifecycle/security controls are enforced with scheduler and VPD patterns.&lt;/p&gt;




&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Python 3.10+&lt;/li&gt;
&lt;li&gt;Oracle AI Database 26ai (or compatible environment)&lt;/li&gt;
&lt;li&gt;Dependencies: oracledb, python-dotenv, pandas, optional langchain-core&lt;/li&gt;
&lt;li&gt;Privileges for tables, indexes, SQL/JSON, and queries&lt;/li&gt;
&lt;li&gt;Optional privileges for Oracle Text and SQL Property Graph features&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Types of agent memory and their storage needs
&lt;/h2&gt;

&lt;p&gt;Not all memory behaves the same way. Different memory types have different latency, durability, and retrieval requirements.&lt;/p&gt;

&lt;h3&gt;
  
  
  Short-term vs. long-term memory
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Short-term memory&lt;/strong&gt; is the agent's working context. It typically includes the current conversation window, recent tool outputs, temporary plans, and session variables. It requires very low latency but does not need to be persisted indefinitely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Long-term memory&lt;/strong&gt; persists beyond a single interaction. It may include user preferences, completed tasks, conversation summaries, business objects, knowledge artifacts, and execution history. It should be durable, searchable, and governed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Episodic memory
&lt;/h3&gt;

&lt;p&gt;Episodic memory stores events and experiences. For agents, that can mean:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;previous conversations&lt;/li&gt;
&lt;li&gt;tool calls and outputs&lt;/li&gt;
&lt;li&gt;actions taken on behalf of a user&lt;/li&gt;
&lt;li&gt;workflow checkpoints&lt;/li&gt;
&lt;li&gt;timestamps, actors, and outcome metadata&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This memory is usually time-oriented and benefits from structured metadata, durable storage, and filtering by user, task, tenant, or date range.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example: episodic memory as JSON documents&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A practical pattern is to store each conversation turn, tool invocation, or workflow checkpoint as &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/jsnvu/overview-json-relational-duality-views.html" rel="noopener noreferrer"&gt;a JSON document&lt;/a&gt; and query it with SQL/JSON functions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;agent_events&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;event_id&lt;/span&gt;    &lt;span class="n"&gt;NUMBER&lt;/span&gt; &lt;span class="k"&gt;GENERATED&lt;/span&gt; &lt;span class="n"&gt;ALWAYS&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="k"&gt;IDENTITY&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;session_id&lt;/span&gt;  &lt;span class="n"&gt;VARCHAR2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;event_data&lt;/span&gt;  &lt;span class="n"&gt;JSON&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;created_at&lt;/span&gt;  &lt;span class="nb"&gt;TIMESTAMP&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="n"&gt;SYSTIMESTAMP&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;JSON_VALUE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;event_data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'$.type'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;event_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;jt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tool_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;jt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;latency_ms&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="n"&gt;agent_events&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;JSON_TABLE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;event_data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'$'&lt;/span&gt;
         &lt;span class="n"&gt;COLUMNS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
           &lt;span class="n"&gt;tool_name&lt;/span&gt;  &lt;span class="n"&gt;VARCHAR2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;PATH&lt;/span&gt; &lt;span class="s1"&gt;'$.tool.name'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
           &lt;span class="n"&gt;latency_ms&lt;/span&gt; &lt;span class="n"&gt;NUMBER&lt;/span&gt;        &lt;span class="n"&gt;PATH&lt;/span&gt; &lt;span class="s1"&gt;'$.tool.latencyMs'&lt;/span&gt;
         &lt;span class="p"&gt;)&lt;/span&gt;
       &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;jt&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;  &lt;span class="n"&gt;JSON_VALUE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;event_data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'$.type'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'tool_call'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This pattern is especially useful when an agent needs durable session history without flattening every attribute into separate columns on day one.&lt;/p&gt;

&lt;h3&gt;
  
  
  Semantic memory
&lt;/h3&gt;

&lt;p&gt;Semantic memory stores generalized knowledge rather than a raw event log. It includes facts, policies, product information, documentation, ontologies, embeddings, and derived knowledge the agent can reuse across tasks.&lt;/p&gt;

&lt;p&gt;This memory often benefits from a combination of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/vecse/vector-search-pl-sql-packages-node.html" rel="noopener noreferrer"&gt;vector search&lt;/a&gt; for semantic similarity&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/ccapp/overview-getting-started-oracle-text.html" rel="noopener noreferrer"&gt;keyword/text search&lt;/a&gt; for exact terminology and domain-specific phrases&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;relational filters&lt;/strong&gt; for governance, freshness, and access constraints&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;graph relationships&lt;/strong&gt; for connected business meaning&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example: semantic retrieval with Oracle Text&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Why does lexical retrieval still matter in a vector-first architecture? An agent can use CONTAINS to rank policy, support, or product documents by relevance and combine that with vector search in the surrounding workflow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;article_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;SCORE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;relevance&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="n"&gt;knowledge_articles&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;  &lt;span class="k"&gt;CONTAINS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'database performance'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt;  &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;relevance&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;span class="k"&gt;FETCH&lt;/span&gt; &lt;span class="k"&gt;FIRST&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="k"&gt;ROWS&lt;/span&gt; &lt;span class="k"&gt;ONLY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For short text catalogs, &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/ccapp/overview-getting-started-oracle-text.html" rel="noopener noreferrer"&gt;Oracle Text&lt;/a&gt;'s CTXCAT model is also a strong fit when agents need keyword matching plus structured filters such as product family, severity, or tenant.&lt;/p&gt;

&lt;h2&gt;
  
  
  Matching memory types to storage technologies
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7jcz1aev0s7q6ovzmy6q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7jcz1aev0s7q6ovzmy6q.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The key design principle is simple: &lt;strong&gt;use multiple memory types, but avoid fragmenting them across too many disconnected systems unless you truly need to.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Storage technologies for agent memory
&lt;/h2&gt;

&lt;h3&gt;
  
  
  In-memory storage: pros, cons, and use cases
&lt;/h3&gt;

&lt;p&gt;This type of storage is well suited for fast, transient state.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;extremely low latency&lt;/li&gt;
&lt;li&gt;simple fit for session state and active workflow context&lt;/li&gt;
&lt;li&gt;useful for intermediate reasoning artifacts and recent tool results&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;limited durability&lt;/li&gt;
&lt;li&gt;not suitable as the system of record&lt;/li&gt;
&lt;li&gt;difficult to govern and audit if used alone&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Best use cases&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;active conversation buffers&lt;/li&gt;
&lt;li&gt;current plan state for an orchestrator&lt;/li&gt;
&lt;li&gt;short-lived coordination across steps in a single run&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  File-based storage: when and why to use it
&lt;/h2&gt;

&lt;p&gt;Files and object storage are useful for large, unstructured artifacts such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PDFs and reports&lt;/li&gt;
&lt;li&gt;images and media&lt;/li&gt;
&lt;li&gt;transcript archives&lt;/li&gt;
&lt;li&gt;exported workflow bundles&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;They work well when the artifact itself is large, rarely updated, or naturally belongs in a document repository. However, file-based storage alone is a weak memory layer because it lacks rich query semantics. In practice, it works best when paired with database metadata, vector indexes, or a catalog layer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Databases: SQL, NoSQL, and key-value stores
&lt;/h2&gt;

&lt;p&gt;Databases are the backbone of durable agent memory.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SQL databases&lt;/strong&gt; excel when agents need strong consistency, joins, transactions, governance, and structured filters.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NoSQL/document stores&lt;/strong&gt; are useful when schemas evolve quickly and payloads are semi-structured.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Key-value stores&lt;/strong&gt; are effective for simple lookups, caching, and session persistence at high speed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For enterprise agents, the strongest pattern is often not choosing one memory store per memory type, but choosing a platform that can support multiple memory representations together.&lt;/p&gt;

&lt;h2&gt;
  
  
  Vector databases for semantic memory retrieval
&lt;/h2&gt;

&lt;p&gt;Vector retrieval is essential when an agent must find content by meaning rather than exact wording. It is especially effective for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;semantic search over documents&lt;/li&gt;
&lt;li&gt;similarity matching for prior cases&lt;/li&gt;
&lt;li&gt;memory recall from summarized or embedded interactions&lt;/li&gt;
&lt;li&gt;grounding RAG workflows with relevant context&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But vector search should not be treated as the entire memory architecture. Enterprise retrieval often requires semantic matching plus exact filtering, freshness rules, tenant boundaries, business keys, and joins to live data.&lt;/p&gt;

&lt;p&gt;That is where Oracle AI Database stands out as a unified memory core. It brings &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/vecse/overview-node.html" rel="noopener noreferrer"&gt;vector search&lt;/a&gt; next to &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/cncpt/tables-and-table-clusters.html#GUID-F845B1A7-71E3-4312-B66D-BC16C198ECE5" rel="noopener noreferrer"&gt;relational data&lt;/a&gt;, &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/adjsn/json-data-and-oracle-ai-database.html" rel="noopener noreferrer"&gt;JSON&lt;/a&gt;, &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/rdfrm/rdf-graph-overview.html#GUID-F422BB9F-8473-4980-9D6C-848F708C10E0" rel="noopener noreferrer"&gt;graph&lt;/a&gt;, &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/spatl/what-is-oracle-spatial.html" rel="noopener noreferrer"&gt;spatial&lt;/a&gt;, and enterprise governance features, allowing agents to retrieve semantically relevant context without losing operational control.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why text search belongs in the unified memory core
&lt;/h2&gt;

&lt;p&gt;A significant portion of production searches need both &lt;strong&gt;vector similarity and keyword matching&lt;/strong&gt;. Users do not always ask only by meaning. They often include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;exact product names&lt;/li&gt;
&lt;li&gt;policy clauses&lt;/li&gt;
&lt;li&gt;ticket IDs&lt;/li&gt;
&lt;li&gt;account numbers&lt;/li&gt;
&lt;li&gt;legal terms&lt;/li&gt;
&lt;li&gt;error messages and codes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is why a mature memory core should include &lt;strong&gt;&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/ccapp/" rel="noopener noreferrer"&gt;text search&lt;/a&gt; alongside vector, graph, spatial, JSON, and relational capabilities&lt;/strong&gt;. Semantic retrieval helps with meaning; keyword retrieval helps with precision. Together they produce more reliable enterprise context.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hybrid storage architectures and patterns
&lt;/h2&gt;

&lt;p&gt;Most serious agent systems use a hybrid pattern, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;in-memory working context&lt;/strong&gt; for active sessions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;relational/JSON persistence&lt;/strong&gt; for durable state and episodic history&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;vector indexes&lt;/strong&gt; for semantic recall&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;text search&lt;/strong&gt; for lexical precision&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;object storage&lt;/strong&gt; for large source artifacts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;graph structures&lt;/strong&gt; where relationships are central to reasoning&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The question is not whether hybrid memory exists—it almost always does. The real design decision is whether those layers are operationally fragmented or organized around a unified platform.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3itrb9narv7ihy5vjtua.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3itrb9narv7ihy5vjtua.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Example: relationship-aware memory with SQL Property Graph
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://docs.oracle.com/en/database/oracle/property-graph/26.1/spgdg/sql-property-graph.html" rel="noopener noreferrer"&gt;Oracle SQL Property Graph&lt;/a&gt; lets you model and query graph data — vertices (nodes) and edges (relationships) — directly on top of existing relational tables, views, materialized views, or external tables. No data is copied; the graph definition stores only metadata, and queries operate against current table data. This is useful when an agent must follow connected context such as user → ticket → service → document:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;GRAPH_TABLE&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;memory_graph&lt;/span&gt;
  &lt;span class="k"&gt;MATCH&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="n"&gt;opened&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="n"&gt;ticket&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="n"&gt;mentions&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;COLUMNS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;  &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;user_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;ticket_title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;document_title&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That kind of retrieval complements vector similarity by surfacing the business relationships around a memory item, not just the nearest semantic neighbors.&lt;/p&gt;




&lt;h2&gt;
  
  
  Scaling agent memory for large applications
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Externalizing memory from models
&lt;/h3&gt;

&lt;p&gt;Large language models should not be expected to carry all relevant context in their parameters or prompt window. As applications grow, memory must be externalized into governed stores that can be queried on demand.&lt;/p&gt;

&lt;p&gt;This improves:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;freshness of retrieved context&lt;/li&gt;
&lt;li&gt;controllability of business logic&lt;/li&gt;
&lt;li&gt;auditability and compliance&lt;/li&gt;
&lt;li&gt;reuse across workflows and teams&lt;/li&gt;
&lt;li&gt;cost efficiency versus oversizing prompts&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Hierarchical and tiered memory layers
&lt;/h3&gt;

&lt;p&gt;At scale, memory is usually tiered:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Hot memory&lt;/strong&gt; – immediate session context and recent tool outputs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Warm memory&lt;/strong&gt; – summaries, recent episodes, and active task state&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cold memory&lt;/strong&gt; – historical records, archived artifacts, and long-term facts&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This layered approach keeps latency manageable while retaining historical depth.&lt;/p&gt;

&lt;h2&gt;
  
  
  Retrieval-augmented generation (RAG) techniques
&lt;/h2&gt;

&lt;p&gt;RAG is the most common pattern for grounding an agent with external memory. Strong RAG systems typically combine:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;semantic retrieval over embeddings&lt;/li&gt;
&lt;li&gt;lexical retrieval for exact terms&lt;/li&gt;
&lt;li&gt;metadata filtering for tenant, time, trust, or policy&lt;/li&gt;
&lt;li&gt;reranking for relevance and precision&lt;/li&gt;
&lt;li&gt;source attribution for traceability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In most real-world systems, no single retrieval method is enough on its own. In enterprise settings, hybrid retrieval matters. Many useful searches depend on both meaning and exact terminology, so text search is not a legacy feature to bolt on later. It is an important part of the unified memory core.&lt;/p&gt;

&lt;h2&gt;
  
  
  Retrieval evaluation metrics
&lt;/h2&gt;

&lt;p&gt;To validate retrieval quality rigorously, hybrid memory systems should be measured with explicit ranking and coverage metrics, not only by subjective answer quality.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Precision@K&lt;/strong&gt;: how many of the top-K retrieved results are relevant.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Recall@K:&lt;/strong&gt; how much of the relevant context is recovered within top-K results.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MRR (Mean Reciprocal Rank)&lt;/strong&gt;: how early the first relevant result appears in the ranked list.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Latency (p50/p95)&lt;/strong&gt;: retrieval responsiveness under realistic concurrency and tenant load.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In practice, these metrics should be tracked per retrieval mode (lexical, semantic, hybrid, graph-augmented) and per query class (policy, troubleshooting, identity, compliance) to detect ranking drift early and keep retrieval behavior stable over time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Memory management: summarization, pruning, and lifecycle policies
&lt;/h2&gt;

&lt;p&gt;Memory is not just about storing more. It is also about deciding what to keep, compress, expire, and promote.&lt;/p&gt;

&lt;p&gt;Important practices include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;summarization&lt;/strong&gt; to condense long histories into reusable state&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;pruning&lt;/strong&gt; to remove redundant or low-value context&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;retention policies&lt;/strong&gt; based on legal, business, and product rules&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;promotion rules&lt;/strong&gt; to move temporary knowledge into durable memory&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;staleness checks&lt;/strong&gt; so outdated facts do not keep reappearing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example: scheduled summarization and pruning&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/21/arpls/DBMS_SCHEDULER.html" rel="noopener noreferrer"&gt;DBMS_SCHEDULER&lt;/a&gt; is Oracle's enterprise job scheduling framework. It provides a rich feature set for scheduling PL/SQL code, stored procedures, executables, and scripts — either on a time-based calendar expression, in response to external events, or as part of dependency chains. The DBMS_SCHEDULER maps naturally to memory lifecycle automation. Teams can schedule summarization, retention enforcement, and archive workflows directly in the database:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plsql"&gt;&lt;code&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;
  &lt;span class="n"&gt;DBMS_SCHEDULER&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CREATE_JOB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;job_name&lt;/span&gt;        &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SUMMARIZE_AGENT_SESSIONS&lt;/span&gt;&lt;span class="o"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;job_type&lt;/span&gt;        &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;PLSQL_BLOCK&lt;/span&gt;&lt;span class="o"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;job_action&lt;/span&gt;      &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;BEGIN memory_pkg.summarize_old_sessions(30); END;&lt;/span&gt;&lt;span class="o"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;repeat_interval&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;FREQ=HOURLY;INTERVAL=6&lt;/span&gt;&lt;span class="o"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;enabled&lt;/span&gt;         &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;TRUE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;auto_drop&lt;/span&gt;       &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;FALSE&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;/&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same scheduling pattern can also keep search infrastructure fresh, for example by running CTX_DDL.SYNC_INDEX and CTX_DDL.OPTIMIZE_INDEX jobs for Oracle Text indexes on a predictable cadence.&lt;/p&gt;

&lt;h2&gt;
  
  
  Infrastructure considerations for scalability
&lt;/h2&gt;

&lt;p&gt;As memory volume grows, architects should consider:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;partitioning by tenant, time, or workload&lt;/li&gt;
&lt;li&gt;indexing strategies for vector, text, and relational access&lt;/li&gt;
&lt;li&gt;concurrency and transaction isolation for multi-agent workflows&lt;/li&gt;
&lt;li&gt;cost of re-embedding and re-ranking pipelines&lt;/li&gt;
&lt;li&gt;observability for recall quality, latency, and drift&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Oracle AI Database is compelling here because it supports enterprise-grade scalability while keeping multiple data modalities close together. That reduces the coordination overhead of moving context between independent systems.&lt;/p&gt;

&lt;h2&gt;
  
  
  Operations runbook for memory systems
&lt;/h2&gt;

&lt;p&gt;To keep unified memory reliable in production, teams should operationalize a lightweight runbook that covers indexing, partitioning, scheduler cadence, and observability.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Indexing&lt;/strong&gt;: monitor Oracle Text and vector index health, and schedule regular sync/optimize routines.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Partitioning&lt;/strong&gt;: partition event and knowledge tables by tenant and/or time windows to control growth and query cost.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scheduler cadence&lt;/strong&gt;: run summarization, pruning, retention, and index-maintenance jobs on predictable intervals.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Observability&lt;/strong&gt;: track retrieval latency (p50/p95), result quality metrics, and drift signals across retrieval modes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Operational review&lt;/strong&gt;: review failed jobs, low-confidence retrieval patterns, and tenant hot spots on a recurring schedule.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A compact runbook helps maintain retrieval quality, governance compliance, and performance consistency as memory volume and workload complexity increase.&lt;/p&gt;




&lt;h2&gt;
  
  
  Implementation walkthrough
&lt;/h2&gt;

&lt;p&gt;This implementation builds a &lt;a href="https://github.com/oracle-devrel/oracle-ai-developer-hub/blob/main/notebooks/unified_agent_memory_oracle_ai_database.ipynb" rel="noopener noreferrer"&gt;unified memory flow&lt;/a&gt; in Oracle AI Database, from event storage to multi-mode retrieval and governance.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Initialize the environment and establish one Oracle connection reused across the workflow.&lt;/li&gt;
&lt;li&gt;Create episodic memory (agent_events) with realistic JSON events for user messages, tool calls, tool results, checkpoints, and summaries.&lt;/li&gt;
&lt;li&gt;Query episodic memory with SQL/JSON (JSON_VALUE, JSON_TABLE) for filtering, extraction, and analytics.&lt;/li&gt;
&lt;li&gt;Apply tenant-aware retrieval patterns so every recall path remains policy-aligned.&lt;/li&gt;
&lt;li&gt;Create and populate the knowledge store (knowledge_articles) with tenant-scoped support and policy content.&lt;/li&gt;
&lt;li&gt;Run lexical retrieval with Oracle Text (CONTAINS, SCORE) for exact-term precision.&lt;/li&gt;
&lt;li&gt;Add semantic retrieval with vectors and rank results by similarity using VECTOR_DISTANCE.&lt;/li&gt;
&lt;li&gt;Combine lexical, semantic, and metadata constraints into hybrid retrieval ranking.&lt;/li&gt;
&lt;li&gt;Execute unified recall by combining latest episodic context with knowledge retrieval candidates.&lt;/li&gt;
&lt;li&gt;Add relationship-aware retrieval with SQL Property Graph and GRAPH_TABLE over user-ticket-document paths.&lt;/li&gt;
&lt;li&gt;Apply lifecycle automation and security patterns using DBMS_SCHEDULER and DBMS_RLS.&lt;/li&gt;
&lt;li&gt;Validate outputs end to end and keep the workflow rerunnable with cleanup.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  How this guide maps to the notebook
&lt;/h2&gt;

&lt;p&gt;To make the guide easier to navigate, each concept in this article is implemented in a corresponding notebook section.&lt;/p&gt;

&lt;p&gt;Episodic memory is introduced through the agent_events model and sample event ingestion, then expanded with SQL/JSON extraction using JSON_VALUE and JSON_TABLE.&lt;/p&gt;

&lt;p&gt;Tenant-aware retrieval and knowledge storage follow, along with lexical retrieval using Oracle Text and semantic retrieval using vectors (VECTOR_DISTANCE).&lt;/p&gt;

&lt;p&gt;Hybrid retrieval then combines lexical relevance, semantic distance, and metadata filtering in one ranking path.&lt;/p&gt;

&lt;p&gt;The workflow continues with unified episodic-plus-knowledge recall, relationship-aware traversal using GRAPH_TABLE, and operational patterns for lifecycle automation (DBMS_SCHEDULER) and row-level tenant isolation (DBMS_RLS / VPD).&lt;/p&gt;

&lt;p&gt;The notebook concludes with an optional &lt;a href="https://python.langchain.com/docs/integrations/vectorstores/oracle" rel="noopener noreferrer"&gt;LangChain interoperability layer&lt;/a&gt; that keeps retrieval Oracle-native.&lt;/p&gt;




&lt;h2&gt;
  
  
  Security and privacy considerations in agent memory storage
&lt;/h2&gt;

&lt;p&gt;Memory makes agents more capable, but it also expands the attack surface.&lt;/p&gt;

&lt;h3&gt;
  
  
  Common security risks and attack vectors
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;data leakage across users, roles, or tenants&lt;/li&gt;
&lt;li&gt;prompt injection through retrieved content&lt;/li&gt;
&lt;li&gt;memory poisoning from incorrect or malicious inputs&lt;/li&gt;
&lt;li&gt;over-retention of sensitive information&lt;/li&gt;
&lt;li&gt;stale or conflicting memory causing unsafe decisions&lt;/li&gt;
&lt;li&gt;weak authorization around recalled context and tool actions&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Data protection and access control strategies
&lt;/h3&gt;

&lt;p&gt;Best practices include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;role-based and attribute-based access control&lt;/li&gt;
&lt;li&gt;encryption in transit and at rest&lt;/li&gt;
&lt;li&gt;row-level or tenant-aware data isolation&lt;/li&gt;
&lt;li&gt;retrieval filters tied to identity and policy&lt;/li&gt;
&lt;li&gt;audit logs for memory access and mutation events&lt;/li&gt;
&lt;li&gt;data classification for sensitive memory types&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Operational security checklist
&lt;/h3&gt;

&lt;p&gt;To translate security principles into day-to-day practice, teams should validate a compact operational checklist for every memory workflow.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enforce tenant isolation in every retrieval path, not only at the application layer.&lt;/li&gt;
&lt;li&gt;Log memory reads and writes for auditability, including tool-triggered retrieval actions.&lt;/li&gt;
&lt;li&gt;Apply data classification and PII handling rules before memory is persisted or retrieved.&lt;/li&gt;
&lt;li&gt;Use role-aware authorization checks for both retrieval and mutation operations.&lt;/li&gt;
&lt;li&gt;Define retention and deletion controls so sensitive memory does not persist beyond policy windows.&lt;/li&gt;
&lt;li&gt;Protect retrieved context against prompt-injection and memory-poisoning propagation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This checklist helps ensure that memory quality, security, and compliance remain aligned as agent usage scales.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example: tenant-aware memory access with VPD&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/dbseg/using-oracle-vpd-to-control-data-access.html" rel="noopener noreferrer"&gt;Virtual Private Database (VPD)&lt;/a&gt;, also called Fine-Grained Access Control (FGAC), is Oracle's mechanism for enforcing row-level security transparently at the database kernel level. Unlike application-layer filtering — which can be bypassed by ad-hoc queries, ETL tools, or reporting applications — VPD policies are enforced by the Oracle query engine itself, regardless of how a query reaches the database.. The Row-Level Security can enforce tenant isolation directly in the database with VPD (DBMS_RLS):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plsql"&gt;&lt;code&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;
  &lt;span class="n"&gt;DBMS_RLS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ADD_POLICY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;object_schema&lt;/span&gt;   &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;APP&lt;/span&gt;&lt;span class="o"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;object_name&lt;/span&gt;     &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;AGENT_MEMORY&lt;/span&gt;&lt;span class="o"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;policy_name&lt;/span&gt;     &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;TENANT_ISOLATION&lt;/span&gt;&lt;span class="o"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;function_schema&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;APP&lt;/span&gt;&lt;span class="o"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;policy_function&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;TENANT_ISOLATION_POLICY&lt;/span&gt;&lt;span class="o"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;statement_types&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SELECT, INSERT, UPDATE, DELETE&lt;/span&gt;&lt;span class="o"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;update_check&lt;/span&gt;    &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;TRUE&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With SYS_CONTEXT-driven predicates, the same memory tables can serve many tenants while ensuring an agent only recalls context it is authorized to access.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mitigation best practices and compliance
&lt;/h3&gt;

&lt;p&gt;Organizations should design agent memory with compliance in mind from the start. That means applying retention rules, provenance tracking, redaction strategies, and approval workflows where needed. It also means ensuring the retrieval layer does not bypass the same governance standards applied to transactional systems.&lt;/p&gt;

&lt;p&gt;This is another reason a unified, governed database platform is attractive: it allows memory retrieval to inherit mature &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/dbseg/" rel="noopener noreferrer"&gt;enterprise security controls&lt;/a&gt; instead of recreating them separately for every store.&lt;/p&gt;




&lt;h3&gt;
  
  
  Recap of memory types and storage options
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Short-term memory&lt;/strong&gt; supports immediate task execution and should be fast.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Long-term memory&lt;/strong&gt; preserves durable context across sessions and workflows.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Episodic memory&lt;/strong&gt; captures what happened, when, and under what conditions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Semantic memory&lt;/strong&gt; helps the agent retrieve meaning, facts, and abstractions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No single storage mechanism solves every memory problem. In-memory caches, files, relational stores, key-value systems, text indexes, graph structures, and vector search all have a role.&lt;/p&gt;

&lt;h3&gt;
  
  
  Guidelines for choosing and managing agent memory
&lt;/h3&gt;

&lt;p&gt;Use the following rules of thumb:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Match storage to memory behavior, not just data format.&lt;/li&gt;
&lt;li&gt;Keep working memory fast, but make durable memory governed and auditable.&lt;/li&gt;
&lt;li&gt;Combine semantic retrieval with keyword and metadata filtering.&lt;/li&gt;
&lt;li&gt;Treat lifecycle management as part of memory design, not an afterthought.&lt;/li&gt;
&lt;li&gt;Prefer unified platforms when governance, consistency, and scale matter.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Balancing performance, scalability, and security
&lt;/h3&gt;

&lt;p&gt;The best agent memory architectures do not optimize only for retrieval quality. They balance:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;performance&lt;/strong&gt; for responsive agent interactions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;scalability&lt;/strong&gt; for growing users, tasks, and data volumes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;security&lt;/strong&gt; for enterprise trust and compliance&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Oracle AI Database fits this balance especially well for enterprise agents. It provides one of the most sophisticated and scalable ways to unify vector, JSON, graph, spatial, relational, and analytic data access in a governed platform. That makes it a strong foundation for a true unified memory core rather than a collection of disconnected memory services.&lt;/p&gt;

&lt;p&gt;When memory becomes a first-class architectural concern, agents become more reliable, more context-aware, and more useful in real business workflows.&lt;/p&gt;




&lt;h2&gt;
  
  
  Validation &amp;amp; troubleshooting: failure modes and fallback strategy
&lt;/h2&gt;

&lt;p&gt;Production memory systems should not assume every retrieval mode is always available. A resilient workflow defines deterministic fallback behavior so the agent can continue safely and predictably.&lt;/p&gt;

&lt;p&gt;In this architecture, retrieval degrades gracefully: if lexical retrieval is unavailable or returns weak matches, the workflow can fall back to semantic retrieval; if vector retrieval is unavailable, lexical retrieval and tenant-scoped filtering remain active; if graph traversal is unavailable, relationship context can be reconstructed with relational joins; and if all retrieval modes return low-confidence results, the system should return a safe tenant-scoped fallback response and request clarification.&lt;/p&gt;

&lt;p&gt;This fallback strategy preserves continuity, improves user trust, and prevents silent retrieval failure in enterprise workflows.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Validate Oracle Text index creation and lexical ranking output.&lt;/li&gt;
&lt;li&gt;Validate vector dimensions and input format used by TO_VECTOR / VECTOR_DISTANCE.&lt;/li&gt;
&lt;li&gt;If GRAPH_TABLE parsing fails, avoid reserved labels and use names like user_v, ticket_v, document_v.&lt;/li&gt;
&lt;li&gt;If results are empty, verify tenant/category filters and fallback query paths.&lt;/li&gt;
&lt;li&gt;Run the notebook end-to-end and confirm outputs across episodic, lexical, vector, hybrid, and graph sections.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Why not use vector search alone?&lt;/strong&gt;&lt;br&gt;
Enterprise queries often contain exact policy/product terms and IDs, so hybrid retrieval is usually more reliable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What does unified memory mean in practice?&lt;/strong&gt;&lt;br&gt;
It means episodic, lexical, semantic, and relationship-aware retrieval are handled in one governed database workflow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What happens if one retrieval mode is unavailable?&lt;/strong&gt;&lt;br&gt;
The workflow can use fallback paths (lexical, semantic, or relational fallback) to preserve continuity and safe defaults.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How does this relate to the companion notebook?&lt;/strong&gt;&lt;br&gt;
The notebook implements each pattern as executable steps so readers can validate outputs end-to-end.&lt;/p&gt;




&lt;h2&gt;
  
  
  Related documentation and further reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/" rel="noopener noreferrer"&gt;Oracle Database 26ai documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/vecse/" rel="noopener noreferrer"&gt;Oracle AI Vector Search User's Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/adjsn/" rel="noopener noreferrer"&gt;Oracle JSON Developer's Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/ccapp/" rel="noopener noreferrer"&gt;Oracle Text Application Developer's Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/spatl/" rel="noopener noreferrer"&gt;Oracle Spatial and Graph documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/dbseg/" rel="noopener noreferrer"&gt;Oracle Database Security Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://python.langchain.com/docs/integrations/vectorstores/oracle" rel="noopener noreferrer"&gt;LangChain Oracle vector store integration&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>database</category>
      <category>memory</category>
    </item>
    <item>
      <title>Agent Memory with LangChain4j and Oracle AI Database</title>
      <dc:creator>Anders Swanson</dc:creator>
      <pubDate>Wed, 22 Apr 2026 17:22:17 +0000</pubDate>
      <link>https://dev.to/oracledevs/agent-memory-with-langchain4j-and-oracle-ai-database-27bl</link>
      <guid>https://dev.to/oracledevs/agent-memory-with-langchain4j-and-oracle-ai-database-27bl</guid>
      <description>&lt;p&gt;One of the quickest ways to make an impressive agent demo is to prepare a clever prompt. One of the quickest ways to make that same agent fall apart in production is to give it no durable memory.&lt;/p&gt;

&lt;p&gt;In this article, we'll build a small, memory-backed assistant with &lt;a href="https://github.com/langchain4j/langchain4j" rel="noopener noreferrer"&gt;LangChain4j&lt;/a&gt; and Oracle AI Database. The assistant can search prior incidents, runbooks, decisions, and shift handoffs to answer questions. It can write new memories back to the database so they become searchable in any session. Additionally, all user, agent, and tool messages are logged to database table for observability and auditing.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Database feature overview&lt;/li&gt;
&lt;li&gt;Run the sample&lt;/li&gt;
&lt;li&gt;Chat Memory vs Durable Memory&lt;/li&gt;
&lt;li&gt;Hybrid retrieval: semantic + full-text search&lt;/li&gt;
&lt;li&gt;Lightweight reranking&lt;/li&gt;
&lt;li&gt;LangChain4j agent&lt;/li&gt;
&lt;li&gt;Memory writeback&lt;/li&gt;
&lt;li&gt;Recording user, agent, and tool messages&lt;/li&gt;
&lt;li&gt;Why database memory is useful for agents&lt;/li&gt;
&lt;li&gt;Code pointers&lt;/li&gt;
&lt;li&gt;Where you can take this next&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Database feature overview
&lt;/h4&gt;

&lt;p&gt;The agent is built with modern Oracle AI Database features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;persistent &lt;code&gt;JSON&lt;/code&gt; memory documents in Oracle AI Database&lt;/li&gt;
&lt;li&gt;vector embeddings in a &lt;code&gt;VECTOR&lt;/code&gt; column&lt;/li&gt;
&lt;li&gt;Oracle Text search over the same JSON document&lt;/li&gt;
&lt;li&gt;hybrid ranking that blends semantic and exact-match retrieval&lt;/li&gt;
&lt;li&gt;append-only transcript logging by conversation ID&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using these features, the agent (a fictional operations assistant) can answer question about runbooks, incident reviews, change requests, and shift handoffs from its persistent memory. Because the memory is database backed, multiple agents from concurrent sessions may access the same data safely.&lt;/p&gt;

&lt;h4&gt;
  
  
  Run the sample
&lt;/h4&gt;

&lt;p&gt;You will need Java 21+, Maven, Docker, and an &lt;a href="https://platform.openai.com/" rel="noopener noreferrer"&gt;OpenAI API Key&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;From the &lt;a href="https://github.com/andersswanson/oracle-ai-database-examples/tree/main/langchain4j-agent-memory" rel="noopener noreferrer"&gt;module root&lt;/a&gt;, run the tests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OPENAI_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;your key&amp;gt;
mvn &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To run the live terminal app using your database connection string and user:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OPENAI_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;your key&amp;gt;
mvn compile &lt;span class="nb"&gt;exec&lt;/span&gt;:java &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-Dexec&lt;/span&gt;.args&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"jdbc:oracle:thin:@localhost:1521/freepdb1 testuser testpwd"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once it starts, try prompts like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;What happened during the checkout incident after CHG2145?&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Which runbook section should I use for the checkout rollback?&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Draft a next-shift handoff and remember it.&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Chat Memory vs Durable Memory
&lt;/h4&gt;

&lt;p&gt;Chat memory and durable memory solve different problems. Operational memory has different requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;it should survive process restarts&lt;/li&gt;
&lt;li&gt;it should be queryable across conversations from distributed, concurrent agents&lt;/li&gt;
&lt;li&gt;it should support structured metadata like service, environment, incident ID, and change ticket&lt;/li&gt;
&lt;li&gt;it should be searchable both semantically and exactly&lt;/li&gt;
&lt;li&gt;it should allow writeback when the agent learns something worth preserving&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That starts to look a lot more like a database problem than a prompt engineering problem.&lt;/p&gt;

&lt;h4&gt;
  
  
  Hybrid retrieval: semantic + full-text search
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvjj8zm4u2n7p6vt8zr5v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvjj8zm4u2n7p6vt8zr5v.png" alt="Hybrid search" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/andersswanson/oracle-ai-database-examples/blob/main/langchain4j-agent-memory/src/main/java/dev/andersswanson/oracle/langchain4jmemory/MemoryRepository.java" rel="noopener noreferrer"&gt;&lt;code&gt;MemoryRepository&lt;/code&gt;&lt;/a&gt; runs two queries, which are fused into one ranked list:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://github.com/andersswanson/oracle-ai-database-examples/blob/main/langchain4j-agent-memory/src/main/java/dev/andersswanson/oracle/langchain4jmemory/MemoryRepository.java#L53-L63" rel="noopener noreferrer"&gt;Vector search&lt;/a&gt; over the &lt;code&gt;embedding&lt;/code&gt; column using cosine distance.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/andersswanson/oracle-ai-database-examples/blob/main/langchain4j-agent-memory/src/main/java/dev/andersswanson/oracle/langchain4jmemory/MemoryRepository.java#L65-L75" rel="noopener noreferrer"&gt;Oracle Text search&lt;/a&gt; over the JSON payload using &lt;code&gt;json_textcontains&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here is the vector query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;memory_kind&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;memory_doc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;vector_distance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;COSINE&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;vector_score&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;agent_memories&lt;/span&gt;
&lt;span class="k"&gt;order&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="n"&gt;vector_score&lt;/span&gt; &lt;span class="k"&gt;desc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;
&lt;span class="k"&gt;fetch&lt;/span&gt; &lt;span class="k"&gt;first&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="k"&gt;rows&lt;/span&gt; &lt;span class="k"&gt;only&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here is the text query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;memory_kind&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;memory_doc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;text_score&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;agent_memories&lt;/span&gt;
&lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;json_textcontains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;memory_doc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'$'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;order&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;desc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;
&lt;span class="k"&gt;fetch&lt;/span&gt; &lt;span class="k"&gt;first&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="k"&gt;rows&lt;/span&gt; &lt;span class="k"&gt;only&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pure vector search is often too fuzzy for ticket IDs. Pure text search is often too brittle for paraphrases. Hybrid retrieval handles both.&lt;/p&gt;

&lt;h4&gt;
  
  
  Lightweight reranking
&lt;/h4&gt;

&lt;p&gt;Once both branches return hits, &lt;a href="https://github.com/andersswanson/oracle-ai-database-examples/blob/main/langchain4j-agent-memory/src/main/java/dev/andersswanson/oracle/langchain4jmemory/MemorySearchRanker.java" rel="noopener noreferrer"&gt;&lt;code&gt;MemorySearchRanker&lt;/code&gt;&lt;/a&gt; merges the results with deterministic weights:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a bonus when the incident ID or change ticket matches directly&lt;/li&gt;
&lt;li&gt;a bonus for keyword overlap in the indexed memory text&lt;/li&gt;
&lt;li&gt;a combined &lt;code&gt;matchedBy&lt;/code&gt; indicator of &lt;code&gt;VECTOR&lt;/code&gt;, &lt;code&gt;TEXT&lt;/code&gt;, or &lt;code&gt;BOTH&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The deterministic ranker could be implemented by an LLM judge or a more complex re-ranking system. For this sample, I kept it intentionally lightweight and low-latency.&lt;/p&gt;

&lt;h4&gt;
  
  
  LangChain4j agent
&lt;/h4&gt;

&lt;p&gt;The &lt;a href="https://github.com/andersswanson/oracle-ai-database-examples/blob/main/langchain4j-agent-memory/src/main/java/dev/andersswanson/oracle/langchain4jmemory/OpsMemoryAssistant.java" rel="noopener noreferrer"&gt;LangChain4j agent implementation&lt;/a&gt; is quite small, using a single interface:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;OpsMemoryAssistant&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@SystemMessage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"""
            You are an operations handoff assistant backed by Oracle AI Database memory.
            Use searchMemories when prior incidents, runbooks, handoffs, decisions, or change history are relevant.
            When you rely on memory results, include the references in the form [M123].
            If the user asks you to remember or preserve a new handoff or decision, call storeMemory after drafting it.
            Keep answers concise and operational. Mention incident IDs and change tickets when they matter.
            """&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@UserMessage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{{message}}"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;chat&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@V&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"message"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;userMessage&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is the right level of abstraction for this sample.&lt;/p&gt;

&lt;p&gt;LangChain4j handles chat orchestration and tool wiring. Oracle AI Database handles durable memory, search, and transcript persistence. Each layer is doing the job it is actually good at.&lt;/p&gt;

&lt;h4&gt;
  
  
  Memory writeback
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9u662b85vn3qic184gnd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9u662b85vn3qic184gnd.png" alt="Memory writeback" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The sample keeps two memory stores:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a curated durable memory store for retrieval&lt;/li&gt;
&lt;li&gt;an append-only transcript for observability and auditing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This one also stores new durable memory through the &lt;code&gt;storeMemory&lt;/code&gt; tool when the user explicitly asks the assistant to preserve a handoff or decision.&lt;/p&gt;

&lt;p&gt;That matters because an agent memory system should not just be a read-only archive. If a useful conclusion comes out of a conversation, the system should be able to keep it.&lt;/p&gt;

&lt;p&gt;In this sample, writeback creates a new &lt;code&gt;MemoryDocument&lt;/code&gt;, generates an embedding, and inserts both the JSON payload and vector into &lt;code&gt;agent_memories&lt;/code&gt;. Because the JSON search index is configured with &lt;code&gt;sync (on commit)&lt;/code&gt;, newly stored handoffs are searchable immediately after commit.&lt;/p&gt;

&lt;p&gt;That last detail is important. Delayed indexing is exactly the kind of thing that makes an agent feel unreliable.&lt;/p&gt;

&lt;h4&gt;
  
  
  Recording user, agent, and tool messages
&lt;/h4&gt;

&lt;p&gt;With our database connection, it's easy to record chat sessions in the database. To do this with LangChain4j, we implement the ChatMemory interface in the &lt;a href="https://github.com/andersswanson/oracle-ai-database-examples/blob/main/langchain4j-agent-memory/src/main/java/dev/andersswanson/oracle/langchain4jmemory/LoggingChatMemory.java" rel="noopener noreferrer"&gt;LoggingChatMemory.java class&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Each session gets its own unique conversation ID, and user/agent/tool messages are written to the &lt;code&gt;agent_conversation_log&lt;/code&gt; table.&lt;/p&gt;

&lt;p&gt;That table captures:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;conversation_id&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;message_seq&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;role and message type&lt;/li&gt;
&lt;li&gt;message text&lt;/li&gt;
&lt;li&gt;tool name and tool call ID when relevant&lt;/li&gt;
&lt;li&gt;optional JSON context&lt;/li&gt;
&lt;li&gt;creation timestamp&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That distinction tends to get blurred in agent demos. It should not.&lt;/p&gt;

&lt;h4&gt;
  
  
  Why database memory is useful for agents
&lt;/h4&gt;

&lt;p&gt;Chat windows and flat files can't scale the same way a database can. A database-backed memory layer gives you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;durable storage&lt;/li&gt;
&lt;li&gt;structured metadata&lt;/li&gt;
&lt;li&gt;many types of retrieval: semantic, text, relationship, graph, etc.&lt;/li&gt;
&lt;li&gt;transactional writes and concurrency&lt;/li&gt;
&lt;li&gt;better auditability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Databases can help you progress from agent demos to real applications that effectively utilize agent memory.&lt;/p&gt;

&lt;h4&gt;
  
  
  Code pointers
&lt;/h4&gt;

&lt;p&gt;If you want to explore the implementation, start here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/andersswanson/oracle-ai-database-examples/blob/main/langchain4j-agent-memory/README.md" rel="noopener noreferrer"&gt;README.md&lt;/a&gt; -&amp;gt; app overview&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/andersswanson/oracle-ai-database-examples/blob/main/langchain4j-agent-memory/src/main/java/dev/andersswanson/oracle/langchain4jmemory/OpsMemoryAgentApplication.java" rel="noopener noreferrer"&gt;OpsMemoryAgentApplication.java&lt;/a&gt; -&amp;gt; Main class and agent loop&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/andersswanson/oracle-ai-database-examples/blob/main/langchain4j-agent-memory/src/main/java/dev/andersswanson/oracle/langchain4jmemory/MemoryRepository.java" rel="noopener noreferrer"&gt;MemoryRepository.java&lt;/a&gt; -&amp;gt; Memory retrieval for text and vector search&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/andersswanson/oracle-ai-database-examples/blob/main/langchain4j-agent-memory/src/main/java/dev/andersswanson/oracle/langchain4jmemory/MemoryTools.java" rel="noopener noreferrer"&gt;MemoryTools.java&lt;/a&gt; -&amp;gt; LangChain4j tool bindings to search and store memories&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/andersswanson/oracle-ai-database-examples/blob/main/langchain4j-agent-memory/src/main/java/dev/andersswanson/oracle/langchain4jmemory/LoggingChatMemory.java" rel="noopener noreferrer"&gt;LoggingChatMemory.java&lt;/a&gt; -&amp;gt; LangChain4j ChatMemory implementation to log chat interactions&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/andersswanson/oracle-ai-database-examples/blob/main/langchain4j-agent-memory/src/test/java/dev/andersswanson/oracle/langchain4jmemory/MemoryRepositoryIntegrationTest.java" rel="noopener noreferrer"&gt;MemoryRepositoryIntegrationTest.java&lt;/a&gt; -&amp;gt; test using Oracle AI Database Free and Testcontainers&lt;/li&gt;
&lt;/ul&gt;

&lt;h6&gt;
  
  
  The tests validate the behavior that matters
&lt;/h6&gt;

&lt;p&gt;The integration tests are worth reading because they verify the actual retrieval patterns we care about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;exact text search finds the checkout incident for &lt;code&gt;CHG2145&lt;/code&gt; and &lt;code&gt;INC4721&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;vector search finds the same incident from a paraphrased outage description&lt;/li&gt;
&lt;li&gt;hybrid fusion marks the strongest result as matched by both channels&lt;/li&gt;
&lt;li&gt;a stored handoff can be found on the next combined search&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Where you can take this next
&lt;/h4&gt;

&lt;p&gt;If you'd like to extend this sample, here's a few ideas to play with:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add "forgetting" with recency ranking so newer memories are ranked as more relevant.&lt;/li&gt;
&lt;li&gt;Parameterize scoring and filtering mechanisms to make the app more flexible.&lt;/li&gt;
&lt;li&gt;Add another agent tool that uses an LLM to judge search results.&lt;/li&gt;
&lt;li&gt;Add approval/rejection when storing memories. Maintain a log of failures so the agent knows what not to do.&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>oracle</category>
      <category>java</category>
      <category>agents</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
