<?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: Anya Summers</title>
    <description>The latest articles on DEV Community by Anya Summers (@anya_summers_a5881a3d01ce).</description>
    <link>https://dev.to/anya_summers_a5881a3d01ce</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3876445%2F58b8ee11-40e1-4eaf-ad97-5a4089a51887.png</url>
      <title>DEV Community: Anya Summers</title>
      <link>https://dev.to/anya_summers_a5881a3d01ce</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/anya_summers_a5881a3d01ce"/>
    <language>en</language>
    <item>
      <title>LangGraph persistence with Oracle AI Database</title>
      <dc:creator>Anya Summers</dc:creator>
      <pubDate>Wed, 08 Jul 2026 15:58:59 +0000</pubDate>
      <link>https://dev.to/oracledevs/langgraph-persistence-with-oracle-ai-database-59in</link>
      <guid>https://dev.to/oracledevs/langgraph-persistence-with-oracle-ai-database-59in</guid>
      <description>&lt;p&gt;&lt;em&gt;&lt;strong&gt;Durable LangGraph workflows with Oracle AI Database&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;





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

&lt;ul&gt;
&lt;li&gt;Oracle AI Database can be used as a persistence layer for LangGraph using &lt;a href="https://github.com/oracle/langchain-oracle" rel="noreferrer noopener"&gt;langchain-oracle&lt;/a&gt; libraries. This persistence layer powers agent workflows with retries, audit trails, and repeatability.&lt;/li&gt;



&lt;li&gt;OracleSaver preserves (checkpoints) graph state at a point in time. This state may be resumed, inspected, or replayed using a the run’s unique&lt;code&gt;thread_id&lt;/code&gt;.&lt;/li&gt;



&lt;li&gt;OracleStore persists long-term, cross-thread memory, including user preferences, facts, and shared knowledge.&lt;/li&gt;
&lt;/ul&gt;





&lt;p&gt;A human-in-the-loop graph is easy to demonstrate when the entire workflow stays in memory. The more realistic challenge starts when the graph pauses for a reviewer, waits for a business decision, and later resumes with the same state intact.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;&lt;a href="https://github.com/anders-swanson/oracle-database-code-samples/tree/main/python-oracle/src/python_oracle/langgraph_persistence" rel="noreferrer noopener"&gt;langgraph_persistence&lt;/a&gt;&lt;/code&gt; sample shows how to build that pattern with LangGraph and Oracle AI Database. It evaluates a travel request, uses OCI Generative AI to draft a concise reviewer brief, checkpoints the graph state with &lt;code&gt;langgraph-oracledb&lt;/code&gt;, interrupts for approval, resumes the same &lt;code&gt;thread_id&lt;/code&gt; with the reviewer’s decision, and stores the approved record separately with &lt;code&gt;OracleStore&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This sample keeps the workflow intentionally small so the persistence pattern is easy to follow: graph state is durable, human approval happens outside the running process, and the final approved request is stored as application data.&lt;/p&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%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F06%2Fimage-resized-1-1024x790.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%2F06%2Fimage-resized-1-1024x790.png" alt="Diagram showing a LangGraph application connected to a human review workflow and Oracle AI Database persistence. The application runs a StateGraph approval flow and runtime context. A draft summary pauses for human approval before resuming execution. OracleSaver stores resumable graph checkpoints and thread state, while OracleStore persists approved records and durable application data." width="800" height="617"&gt;&lt;/a&gt;LangGraph persistence architecture using Oracle AI Database for checkpoints and durable application state.&lt;p&gt;&lt;/p&gt;





&lt;h2&gt;Sample Description&lt;/h2&gt;

&lt;p&gt;This sample demonstrates a LangGraph travel approval workflow that persists graph state in Oracle AI Database using &lt;code&gt;langgraph-oracledb&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The workflow evaluates a travel request, generates a reviewer brief with OCI Generative AI, and then pauses for human approval. While the graph is interrupted, LangGraph checkpoints the current state in Oracle AI Database. After a reviewer provides a decision, the workflow resumes using the same &lt;code&gt;thread_id&lt;/code&gt; and continues from the saved state.&lt;/p&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%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F06%2Fimage-resized-2-1024x842.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%2F06%2Fimage-resized-2-1024x842.png" alt="Diagram showing a policy-driven approval workflow. A travel request enters a policy evaluation stage and is routed either to a fast path or a human review path. Requests within policy are finalized automatically. Requests needing approval generate an LLM draft brief, pause with an interrupt payload, and resume after approval. OracleSaver stores paused workflow state, while OracleStore records approved outcomes." width="800" height="658"&gt;&lt;/a&gt;LangGraph approval workflow with human review, checkpointing, and Oracle AI Database persistence.&lt;p&gt;&lt;/p&gt;

&lt;p&gt;When a request is approved, the sample also writes a separate approved record through &lt;code&gt;OracleStore&lt;/code&gt;, including the original request, approval decision, policy reason, and generated brief.&lt;/p&gt;

&lt;p&gt;The accompanying diagrams show how the Python sample, LangGraph, &lt;code&gt;OracleSaver&lt;/code&gt;, &lt;code&gt;OracleStore&lt;/code&gt;, Testcontainers, OCI Generative AI, and Oracle AI Database fit together.&lt;/p&gt;





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

&lt;ul&gt;
&lt;li&gt;Python 3.13+&lt;/li&gt;



&lt;li&gt;Poetry&lt;/li&gt;



&lt;li&gt;Docker compatible environment to run Oracle AI Database Free&lt;/li&gt;



&lt;li&gt;Local OCI configuration for OCI Generative AI on-demand chat&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Install dependencies from the &lt;code&gt;python-oracle/&lt;/code&gt; directory:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;poetry install&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Set the OCI compartment before running the command-line sample:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;export OCI_COMPARTMENT_ID=&amp;lt;your-compartment-ocid&amp;gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The sample assumes an OCI Generative AI on-demand model and defaults to the model alias &lt;code&gt;cohere.command-latest&lt;/code&gt;. It builds the regional service endpoint from the &lt;code&gt;region&lt;/code&gt; in your &lt;code&gt;DEFAULT&lt;/code&gt; OCI config profile, so no dedicated AI cluster endpoint is required.&lt;/p&gt;





&lt;h2&gt;Run the&amp;nbsp;Sample&lt;/h2&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%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F06%2Fimage-resized-2-1-1024x842.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%2F06%2Fimage-resized-2-1-1024x842.png" alt="Diagram showing a policy-driven approval workflow. A travel request enters a policy evaluation stage and is routed either to a fast path or a human review path. Requests within policy are finalized automatically. Requests needing approval generate an LLM draft brief, pause with an interrupt payload, and resume after approval. OracleSaver stores paused workflow state, while OracleStore records approved outcomes." width="800" height="658"&gt;&lt;/a&gt;LangGraph approval workflow with automated routing, human review, and Oracle AI Database persistence.&lt;p&gt;&lt;/p&gt;

&lt;p&gt;From the &lt;code&gt;python-oracle/&lt;/code&gt; directory:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;poetry run python src/python_oracle/langgraph_persistence/travel_approval_graph.py&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The script starts Oracle AI Database Free with Testcontainers, creates the LangGraph checkpoint and store tables, runs the request, and prints the final outcome. For the default over-limit request, it also drafts an OCI-generated approval brief, pauses with &lt;code&gt;interrupt()&lt;/code&gt; (which pauses graph execution), prints a checkpoint summary from &lt;code&gt;OracleSaver&lt;/code&gt;, resumes with &lt;code&gt;Command(resume=...)&lt;/code&gt;, and reads the approved business record back from &lt;code&gt;OracleStore&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To run the rejection branch:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;poetry run python src/python_oracle/langgraph_persistence/travel_approval_graph.py --reject&lt;/code&gt;&lt;/pre&gt;





&lt;h2&gt;LangGraph bits&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;thread_id&lt;/code&gt; identifies the LangGraph run, used for the checkpoint summary, and the resume command.&lt;/li&gt;



&lt;li&gt;
&lt;code&gt;OracleSaver&lt;/code&gt; is the persistence &lt;a href="https://docs.langchain.com/oss/python/langgraph/persistence" rel="noreferrer noopener"&gt;&lt;strong&gt;checkpointer&lt;/strong&gt;&lt;/a&gt;. It persists graph state so the human approval interrupt can survive outside the process.&lt;/li&gt;



&lt;li&gt;
&lt;code&gt;OracleStore&lt;/code&gt; is the persistence store. The last graph node writes approved business records and the CLI reads the record back with &lt;code&gt;store.get(...)&lt;/code&gt;.&lt;/li&gt;



&lt;li&gt;
&lt;code&gt;Runtime[ApprovalContext]&lt;/code&gt; carries the live &lt;code&gt;ChatOCIGenAI&lt;/code&gt; model into the graph. The model is runtime context, not checkpointed graph state — it can be easily reconstructed on a new run.&lt;/li&gt;



&lt;li&gt;OCI Generative AI drafts reviewer context only after policy says approval is required. Auto-approved requests skip the model call and go straight to finalization.&lt;/li&gt;
&lt;/ul&gt;





&lt;h2&gt;Building the graph with OracleStore and OracleSaver&lt;/h2&gt;

&lt;p&gt;The OracleStore and OracleSaver objects are easily constructed with a database connection string:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;with (
    OracleSaver.from_conn_string(conn_string) as checkpointer,
    OracleStore.from_conn_string(conn_string) as store,
):
    checkpointer.setup()
    store.setup()&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then, the store and checkpointer can be used to build our graph:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;def build_graph(checkpointer: OracleSaver, store: OracleStore):
    builder = StateGraph(ApprovalState, context_schema=ApprovalContext)
    builder.add_node("evaluate_policy", evaluate_policy)
    builder.add_node("draft_approval_brief", draft_approval_brief)
    builder.add_node("request_approval", request_approval)
    builder.add_node("finalize_request", finalize_request)
    builder.add_edge(START, "evaluate_policy")
    builder.add_conditional_edges(
        "evaluate_policy",
        route_after_policy,
        {"approval": "draft_approval_brief", "finalize": "finalize_request"},
    )
    builder.add_edge("draft_approval_brief", "request_approval")
    builder.add_edge("request_approval", "finalize_request")
    builder.add_edge("finalize_request", END)
    return builder.compile(
        checkpointer=checkpointer,
        store=store,
    )&lt;/code&gt;&lt;/pre&gt;





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

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Why use OracleSaver?&lt;/strong&gt; OracleSaver is used as the LangGraph checkpointer. It persists graph state under a thread_id so the workflow can survive a pause and resume later.&lt;/li&gt;



&lt;li&gt;
&lt;strong&gt;Why use OracleStore? &lt;/strong&gt;OracleStore stores the final approved business record. This is separate from checkpointing because application records and graph execution state have different purposes.&lt;/li&gt;



&lt;li&gt;
&lt;strong&gt;What is stored as the approved record? &lt;/strong&gt;The approved record includes the travel request, approval decision, final status, policy reason, and generated approval brief.&lt;/li&gt;



&lt;li&gt;
&lt;strong&gt;What’s the difference between &lt;/strong&gt;&lt;a href="https://docs.langchain.com/oss/python/langgraph/persistence#checkpointer-vs-store" rel="noreferrer noopener"&gt;&lt;strong&gt;OracleSaver and OracleStore&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt;?&lt;/strong&gt; The Saver (Checkpointer) is for checkpoints of in-progress work, and the Store (Durable memory) is for workflow history.&lt;/li&gt;



&lt;li&gt;
&lt;strong&gt;What data is checkpointed?&lt;/strong&gt; Checkpointers like OracleSaver save a snapshot of the graph state at each step, organized by &lt;code&gt;thread_id&lt;/code&gt;. Checkpoints enable human-in-the-loop workflows, time travel debugging, fault-tolerant execution, and conversational memory.&lt;/li&gt;



&lt;li&gt;
&lt;strong&gt;Why does &lt;/strong&gt;&lt;code&gt;&lt;strong&gt;thread_id&lt;/strong&gt;&lt;/code&gt;&lt;strong&gt; matter?&lt;/strong&gt; The &lt;code&gt;thread_id&lt;/code&gt; corresponds to a specific checkpoints in graph state. The state can be inspected, replayed, and more from a given &lt;code&gt;thread_id&lt;/code&gt;&amp;nbsp;.&lt;/li&gt;



&lt;li&gt;
&lt;strong&gt;Why use Oracle AI Database persistence?&lt;/strong&gt; Use persistence when you want to keep information beyond a single graph run. Persistence helps when you want to continue a conversation, resume after an interruption, recover from a failure, or remember information across interactions.&lt;/li&gt;
&lt;/ol&gt;





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

&lt;ul&gt;
&lt;li&gt;sample app -&amp;gt; &lt;a href="https://github.com/anders-swanson/oracle-database-code-samples/blob/main/python-oracle/src/python_oracle/langgraph_persistence/travel_approval_graph.py" rel="noreferrer noopener"&gt;travel_approval_graph.py&lt;/a&gt;
&lt;/li&gt;



&lt;li&gt;Oracle AI Database testcontainers integration -&amp;gt; &lt;a href="https://github.com/anders-swanson/oracle-database-code-samples/blob/main/python-oracle/src/python_oracle/testcontainers_sample/oracle_database_container.py" rel="noreferrer noopener"&gt;oracle_database_container.py&lt;/a&gt;
&lt;/li&gt;



&lt;li&gt;&lt;a href="https://andersswanson.dev/2026/05/29/agent-memory-techniques-for-oracle-ai-database/" rel="noreferrer noopener"&gt;Designing agent memory with Oracle AI Database&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://github.com/oracle/langchain-oracle" rel="noreferrer noopener"&gt;langchain-oracle&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;



</description>
      <category>langgraph</category>
      <category>oracle</category>
      <category>ai</category>
      <category>database</category>
    </item>
    <item>
      <title>A tour of LangChain Oracle ingestion and retrieval</title>
      <dc:creator>Anya Summers</dc:creator>
      <pubDate>Wed, 08 Jul 2026 15:57:47 +0000</pubDate>
      <link>https://dev.to/oracledevs/a-tour-of-langchain-oracle-ingestion-and-retrieval-1e4l</link>
      <guid>https://dev.to/oracledevs/a-tour-of-langchain-oracle-ingestion-and-retrieval-1e4l</guid>
      <description>&lt;p&gt;&lt;em&gt;&lt;strong&gt;A practical walkthrough for building a compact LangChain retrieval pipeline on Oracle AI Database, from document ingestion and vector storage to hybrid search, semantic caching, and chat history.&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;





&lt;h2&gt;Key takeaways&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;&lt;strong&gt;langchain-oracledb&lt;/strong&gt;&lt;/code&gt;&lt;strong&gt; connects LangChain workflows directly to Oracle AI Database.&lt;/strong&gt; The article shows how Oracle-specific LangChain classes can load, split, store, search, cache, and persist chat-related data in one workflow.&lt;/li&gt;



&lt;li&gt;
&lt;code&gt;&lt;strong&gt;OracleVS&lt;/strong&gt;&lt;/code&gt;&lt;strong&gt; supports both storage and semantic retrieval.&lt;/strong&gt; After embeddings are saved in Oracle AI Database, those vectors are used to run similarity search and return the nearest matching documents for a user question.&lt;/li&gt;



&lt;li&gt;
&lt;strong&gt;Hybrid retrieval improves answer selection.&lt;/strong&gt; Reciprocal Rank Fusion (RRF), is a method used to combine different search results into one single list. The sample combines &lt;strong&gt;semantic search&lt;/strong&gt; from &lt;code&gt;OracleVS&lt;/code&gt; with &lt;strong&gt;keyword/full-text search&lt;/strong&gt; from &lt;code&gt;OracleTextSearchRetriever&lt;/code&gt;, then fuses the results into a single best match.&lt;/li&gt;



&lt;li&gt;
&lt;strong&gt;Caching and chat history are built into the workflow.&lt;/strong&gt; &lt;code&gt;OracleSemanticCache&lt;/code&gt; avoids regenerating answers for similar questions, while &lt;code&gt;OracleChatMessageHistory&lt;/code&gt; stores human and AI messages for conversation persistence.&lt;/li&gt;



&lt;li&gt;
&lt;strong&gt;The sample is designed to run locally.&lt;/strong&gt; It uses Testcontainers with Oracle AI Database Free, a local embedding model, Python 3.13+, Poetry, and a Docker-compatible environment.&lt;/li&gt;
&lt;/ul&gt;





&lt;p&gt;The &lt;a href="https://pypi.org/project/langchain-oracledb/" rel="noreferrer noopener"&gt;langchain-oracledb package&lt;/a&gt; makes it easy to integrate LangChain concepts with your Oracle AI Database instance.&lt;/p&gt;

&lt;p&gt;In this article, we’ll explore the &lt;a href="https://github.com/anders-swanson/oracle-database-code-samples/tree/main/python-oracle/src/python_oracle/langchain_retrieval" rel="noreferrer noopener"&gt;langchain_retrieval sample&lt;/a&gt;, using &lt;code&gt;&lt;strong&gt;langchain-oracledb&lt;/strong&gt;&lt;/code&gt; for content retrieval. The sample composes various &lt;code&gt;langchain-oracledb&lt;/code&gt; classes, including to load, split, save, and retrieve content. This post is a companion article to my prior post, &lt;a href="https://blogs.oracle.com/developers/langgraph-persistence-with-oracle-ai-database%E2%86%97" rel="noreferrer noopener"&gt;LangGraph persistence with Oracle AI Database&lt;/a&gt;.&lt;/p&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%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F06%2Fresized-1-1024x732.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%2F06%2Fresized-1-1024x732.png" alt="Diagram titled “LangChain Oracle Composition.” Source data, including runbooks and Oracle AI Database content, flows through LangChain OracleDB components: OracleDocLoader, OracleTextSplitter, and OracleVS for vector storage and metadata. The answer flow combines vector-based semantic retrieval and Oracle Text keyword search to produce a fused answer. Persistence features include OracleChatMessageHistory for conversation storage and OracleSemanticCache for prompt caching." width="800" height="572"&gt;&lt;/a&gt;LangChain and Oracle AI Database integration for hybrid retrieval, persistence, and answer generation.&lt;p&gt;&lt;/p&gt;

&lt;p&gt;These are the &lt;code&gt;langchain-oracledb&lt;/code&gt; classes we’re going to use:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# langchain-oracledb imports
from langchain_oracledb.cache import OracleSemanticCache
from langchain_oracledb.chat_message_histories import OracleChatMessageHistory
from langchain_oracledb.document_loaders import OracleDocLoader, OracleTextSplitter
from langchain_oracledb.retrievers import OracleTextSearchRetriever
from langchain_oracledb.vectorstores import DistanceStrategy, OracleVS&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This sample uses Testcontainers with Oracle AI Database Free, and a local embedding model.&lt;strong&gt; If you want to jump to the code&lt;/strong&gt;, start here: &lt;a href="https://github.com/anders-swanson/oracle-database-code-samples/tree/main/python-oracle/src/python_oracle/langchain_retrieval" rel="noreferrer noopener"&gt;langchain_retrieval (GitHub)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here’s what all the Oracle pieces look like together:&lt;/p&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%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F06%2Fimage-2-resized-1024x788.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%2F06%2Fimage-2-resized-1024x788.png" alt="Diagram titled “LangChain State in Oracle AI Database.” Oracle AI Database stores four state layers used by a LangChain application: source rows containing runbook content and metadata, vector chunks managed by OracleVS with text, JSON, and embeddings, chat history storing human and AI messages by session, and a semantic cache storing prompt embeddings and generated answers. The diagram highlights separate database surfaces for data, retrieval, memory, and caching." width="800" height="616"&gt;&lt;/a&gt;LangChain state management in Oracle AI Database, including retrieval, memory, and semantic&amp;nbsp;caching.&lt;p&gt;&lt;/p&gt;





&lt;h2&gt;Create a vector store: load, split, and embed&amp;nbsp;content&lt;/h2&gt;

&lt;p&gt;On startup, our sample populates a vector store with documents from a database table. It does this by chaining an &lt;code&gt;OracleDocLoader&lt;/code&gt; to load documents, splitting those documents with &lt;code&gt;OracleTextSplitter&lt;/code&gt;, and then storing them in a vector table with &lt;code&gt;OracleVS&lt;/code&gt;. These utility classes avoid hand tuned logic, allowing you to easily build retrieval pipelines. Let’s break this down, step-by-step.&lt;/p&gt;

&lt;h3&gt;Load data&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;&lt;a href="https://github.com/anders-swanson/oracle-database-code-samples/blob/main/python-oracle/src/python_oracle/langchain_retrieval/runbook_retrieval.py#L151" rel="noreferrer noopener"&gt;OracleDocLoader&lt;/a&gt;&lt;/code&gt; class can be used to ingest documents from various sources. We’re going to use it to load documents from a database table, that’s populated at app startup:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;loader = OracleDocLoader(&lt;br&gt;    conn=conn,&lt;br&gt;    params={&lt;br&gt;        "owner": conn.username,&lt;br&gt;        "tablename": SOURCE_TABLE,&lt;br&gt;        "colname": "BODY",&lt;br&gt;        "mdata_cols": ["RUNBOOK_ID", "TITLE", "PRODUCT"],&lt;br&gt;    },&lt;br&gt;)&lt;br&gt;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Using a database connection, the loader is provisioned with a table name and any relevant columns to load. From this information, it returns a list of LangChain Document objects that are usable in any LangChain workflow.&lt;/p&gt;

&lt;h3&gt;Split text&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;&lt;a href="https://github.com/anders-swanson/oracle-database-code-samples/blob/main/python-oracle/src/python_oracle/langchain_retrieval/runbook_retrieval.py#L193C1-L193C35" rel="noreferrer noopener"&gt;OracleTextSplitter&lt;/a&gt;&lt;/code&gt; is handy to break documents into chunks for embedding. Using a few parameters, we can split the document by words, sentence, with a max size per chunk.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;splitter = OracleTextSplitter(
    conn=conn,
    params={"by": "words", "max": 30, "split": "sentence", "normalize": "all"},
)
ids = [str(document.metadata["runbook_id"]) for document in source_documents]&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Save and&amp;nbsp;embed&lt;/h3&gt;

&lt;p&gt;With our loaded and split content, it’s time to save them into a vector store. The &lt;code&gt;&lt;a href="https://github.com/anders-swanson/oracle-database-code-samples/blob/main/python-oracle/src/python_oracle/langchain_retrieval/runbook_retrieval.py#L174" rel="noreferrer noopener"&gt;OracleVS&lt;/a&gt;&lt;/code&gt;&lt;a href="https://github.com/anders-swanson/oracle-database-code-samples/blob/main/python-oracle/src/python_oracle/langchain_retrieval/runbook_retrieval.py#L174" rel="noreferrer noopener"&gt; class&lt;/a&gt; provides a nifty interface to do this for us, without much code:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;embedding_model = embeddings or AllMiniLMEmbeddings()
return OracleVS(
    conn,
    embedding_model,
    table_name=VECTOR_TABLE,
    distance_strategy=DistanceStrategy.COSINE,
    mutate_on_duplicate=True,
)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Note that you should provide an embedding model when creating an &lt;code&gt;OracleVS&lt;/code&gt; object. We’re using the popular local &lt;code&gt;AllMiniLMEmbeddings&lt;/code&gt; model, which you can find on HuggingFace. We also supply a table name, a vector distance strategy, and an update policy.&lt;/p&gt;

&lt;h3&gt;Putting it all&amp;nbsp;together&lt;/h3&gt;

&lt;p&gt;We now have all the pieces of an ingestion pipeline, using LangChain interfaces! Let’s see what it looks like, in one piece: load documents, split, and embed into &lt;code&gt;OracleVS&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;def load_source_documents(conn: oracledb.Connection) -&amp;gt; list[Document]:
    loader = OracleDocLoader(
        conn=conn,
        params={
            "owner": conn.username,
            "tablename": SOURCE_TABLE,
            "colname": "BODY",
            "mdata_cols": ["RUNBOOK_ID", "TITLE", "PRODUCT"],
        },
    )
    return [_normalize_document(document) for document in loader.load()]
def build_vector_store(
    conn: oracledb.Connection,
    source_documents: list[Document],
    embeddings: Embeddings | None = None,
) -&amp;gt; tuple[OracleVS, list[str]]:
    vector_store = create_vector_store(conn, embeddings)
    chunk_ids = add_documents_to_vector_store(conn, vector_store, source_documents)
    return vector_store, chunk_ids

def create_vector_store(
    conn: oracledb.Connection,
    embeddings: Embeddings | None = None,
) -&amp;gt; OracleVS:
    embedding_model = embeddings or AllMiniLMEmbeddings()
    return OracleVS(
        conn,
        embedding_model,
        table_name=VECTOR_TABLE,
        distance_strategy=DistanceStrategy.COSINE,
        mutate_on_duplicate=True,
    )

def add_documents_to_vector_store(
    conn: oracledb.Connection,
    vector_store: OracleVS,
    source_documents: list[Document],
) -&amp;gt; list[str]:
    splitter = OracleTextSplitter(
        conn=conn,
        params={"by": "words", "max": 30, "split": "sentence", "normalize": "all"},
    )
    ids = [str(document.metadata["runbook_id"]) for document in source_documents]
    return vector_store.add_documents(
        source_documents,
        text_splitter=splitter,
        ids=ids,
        add_chunk_metadata=True,
    )&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The great thing about LangChain is that this is fairly compact. If I were to write this code without the library classes, it’d be several hundred lines of Python!&lt;/p&gt;





&lt;h2&gt;Now, let’s try retrieval and question answering&lt;/h2&gt;

&lt;p&gt;Retrieval is where things get a bit more interesting. &lt;code&gt;langchain-oracledb&lt;/code&gt; offers a few nice helpers in this area:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;OracleVS&lt;/code&gt; for semantic search&lt;/li&gt;



&lt;li&gt;
&lt;code&gt;OracleSemanticCache&lt;/code&gt; to cache embeddings/answers. Embeddings are stored in Oracle AI Database.&lt;/li&gt;



&lt;li&gt;
&lt;code&gt;OracleTextSearchRetriever&lt;/code&gt; for keyword based full-text search, which complements semantic search&lt;/li&gt;



&lt;li&gt;and, &lt;code&gt;OracleChatMessageHistory&lt;/code&gt; to work with chat histories.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s see how these fit together.&lt;/p&gt;

&lt;h3&gt;Semantic search&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;OracleVS&lt;/code&gt;, which helped us store embeddings, of course also allows us &lt;a href="https://github.com/anders-swanson/oracle-database-code-samples/blob/main/python-oracle/src/python_oracle/langchain_retrieval/runbook_retrieval.py#L251" rel="noreferrer noopener"&gt;to retrieve them.&lt;/a&gt; This is quite easy using the Python interface:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;def semantic_search(
    vector_store: OracleVS,
    question: str,
    *,
    product_filter: str | None = None,
    k: int = 4,
) -&amp;gt; list[RunbookHit]:
    metadata_filter = {"product": {"$eq": product_filter}} if product_filter else None
    documents_with_scores = vector_store.similarity_search_with_score(
        question,
        k=k,
        filter=metadata_filter,
    )
    return [
        _document_hit(document, score, "semantic", rank)
        for rank, (document, score) in enumerate(documents_with_scores, start=1)
    ]&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;We use an optional metadata filter to further refine our results, and return the nearest K results to the input question. Note that the input question is embedded using the same embedding model we initialized the vector store with.&lt;/pre&gt;

&lt;h3&gt;Keyword search&lt;/h3&gt;

&lt;p&gt;We can supplement semantic search with text search using &lt;code&gt;&lt;a href="https://github.com/anders-swanson/oracle-database-code-samples/blob/main/python-oracle/src/python_oracle/langchain_retrieval/runbook_retrieval.py#L270" rel="noreferrer noopener"&gt;OracleTextSearchRetriever&lt;/a&gt;&lt;/code&gt;, which returns a separate score. This is done using text operators in the database to find relevant content:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;def keyword_search(vector_store: OracleVS, question: str, k: int = 4) -&amp;gt; list[RunbookHit]:
    retriever = OracleTextSearchRetriever(
        vector_store=vector_store,
        k=k,
        return_scores=True,
    )
    return [
        _document_hit(document, float(document.metadata.get("score", 0)), "keyword", rank)
        for rank, document in enumerate(retriever.invoke(question), start=1)
    ]&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Fusing results&lt;/h3&gt;

&lt;p&gt;Taking results from both similarity and full-text search, we can accumulate them into one result set. It’s important to note which result came from which search method, as this helps the consumer determine relevancy:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;def fuse_hits(semantic_hits: list[RunbookHit], keyword_hits: list[RunbookHit]) -&amp;gt; RunbookHit:
    if not semantic_hits and not keyword_hits:
        raise RuntimeError("No runbook matched the question.")
    by_runbook: dict[int, RankAccumulator] = {}
    for hit in [*semantic_hits, *keyword_hits]:
        accumulator = by_runbook.setdefault(hit.runbook_id, RankAccumulator(hit))
        accumulator.score += 1.0 / (hit.rank + 1)
    return max(by_runbook.values(), key=lambda entry: entry.score).hit&lt;/code&gt;&lt;/pre&gt;





&lt;h2&gt;Putting retrieval together to answer a&amp;nbsp;question&lt;/h2&gt;

&lt;p&gt;We now have all the components needed to scaffold a basic question answering function. Our &lt;code&gt;answer_question&lt;/code&gt; method combines &lt;code&gt;OracleVS&lt;/code&gt; semantic search, &lt;code&gt;OracleTextSearchRetriever&lt;/code&gt; full-text search, and &lt;code&gt;OracleSemanticCache&lt;/code&gt; for answer caching:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;def answer_question(
    conn: oracledb.Connection,
    vector_store: OracleVS,
    question: str,
    *,
    embeddings: Embeddings | None = None,
    product_filter: str | None = None,
) -&amp;gt; QuestionResult:
    embedding_model = embeddings or AllMiniLMEmbeddings()
    cache = OracleSemanticCache(
        conn,
        embedding_model,
        table_name=CACHE_TABLE,
        score_threshold=0.001,
    )
    cached_generations = cache.lookup(question, LLM_CACHE_KEY) or []
    cache_hit = bool(cached_generations)
    semantic_hits = semantic_search(vector_store, question, product_filter=product_filter)
    keyword_hits = keyword_search(vector_store, question)
    fused_hit = fuse_hits(semantic_hits, keyword_hits)
    answer = cached_generations[0].text if cached_generations else build_answer(question, fused_hit)
    if not cached_generations:
        cache.update(question, LLM_CACHE_KEY, [Generation(text=answer)])
    history = OracleChatMessageHistory(
        SESSION_ID,
        client=conn,
        table_name=HISTORY_TABLE,
    )
    if not cache_hit:
        history.add_messages([HumanMessage(content=question), AIMessage(content=answer)])
    return QuestionResult(
        question=question,
        answer=answer,
        cache_hit=cache_hit,
        semantic_hits=semantic_hits,
        keyword_hits=keyword_hits,
        fused_hit=fused_hit,
        history_count=len(history.messages),
    )&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In a semantic cache, the &lt;code&gt;score_threshold&lt;/code&gt; determines how similar an entry must be to signify a cache hit. Embeding is required for a semantic cache, so expect additional latency/work on cache lookups.&lt;/p&gt;





&lt;h2&gt;Time to take the sample for a&amp;nbsp;spin&lt;/h2&gt;

&lt;p&gt;Let’s run it locally. You’ll need the following prerequisites, as the example spins up a disposable &lt;a href="https://andersswanson.dev/2025/09/11/test-python-applications-with-oracle-database-free-using-testcontainers/" rel="noreferrer noopener"&gt;Oracle AI Database Free container&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Python 3.13+&lt;/li&gt;



&lt;li&gt;Poetry&lt;/li&gt;



&lt;li&gt;Docker compatible environment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;From &lt;code&gt;python-oracle/&lt;/code&gt; directory, Install dependencies:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;poetry install&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then run the sample:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;poetry run python src/python_oracle/langchain_retrieval/runbook_retrieval.py&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The script starts the full Oracle AI Database Free image, expected output is similar to:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;#### Loaded runbooks into Oracle AI Database ####
Source runbooks: 4
Vector chunks:   12

#### Retrieval ####
Question:      My VPN disconnects every few minutes on Wi-Fi, but it stays connected on Ethernet. What should I try?
Semantic top:  Stabilize VPN over Wi-Fi
Keyword top:   Stabilize VPN over Wi-Fi
Fused top:     Stabilize VPN over Wi-Fi

#### Response Persistence ####
For: My VPN disconnects every few minutes on Wi-Fi, but it stays connected on Ethernet. What should I try?
Use runbook: Stabilize VPN over Wi-Fi.
Why: it matches the network product area and says to Use this runbook when a VPN client disconnects every few minutes on Wi-Fi but stays connected on Ethernet.

Chat history messages: 2
Second lookup used OracleSemanticCache: True&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The chunk count may vary if Oracle AI Database chunking behavior changes, but it should be greater than the four source runbooks.&lt;/p&gt;





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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://pypi.org/project/langchain-oracledb/" rel="noreferrer noopener"&gt;langchain-oracledb Python package&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://github.com/oracle/langchain-oracle" rel="noreferrer noopener"&gt;LangChain-Oracle GitHub repository&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://blogs.oracle.com/developers/langgraph-persistence-with-oracle-ai-database%E2%86%97" rel="noreferrer noopener"&gt;LangGraph persistence with Oracle AI Database&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;





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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What problem does this article solve?&lt;/strong&gt;&amp;nbsp;It demonstrates how to build a LangChain-based retrieval workflow using Oracle AI Database for ingestion, vector storage, search, caching, and chat history.&lt;/li&gt;



&lt;li&gt;
&lt;strong&gt;What is &lt;/strong&gt;&lt;code&gt;&lt;strong&gt;langchain-oracledb&lt;/strong&gt;&lt;/code&gt;&lt;strong&gt; used for?&lt;/strong&gt;&amp;nbsp;It provides Oracle Database integrations for LangChain concepts, including document loaders, text splitters, vector stores, retrievers, semantic cache, and chat message history.&lt;/li&gt;



&lt;li&gt;
&lt;strong&gt;How does the sample ingest documents?&lt;/strong&gt;&amp;nbsp;It reads documents from a database table using &lt;code&gt;OracleDocLoader&lt;/code&gt;, includes selected metadata columns, normalizes the documents, splits them into chunks, and stores the chunks in a vector table.&lt;/li&gt;



&lt;li&gt;
&lt;strong&gt;Why does the article split text before embedding it?&lt;/strong&gt;&amp;nbsp;Splitting turns larger documents into smaller chunks that are better suited for embedding and retrieval; the sample uses word-based splitting with sentence boundaries and normalization.&lt;/li&gt;



&lt;li&gt;
&lt;strong&gt;What embedding model does the sample use?&lt;/strong&gt;&amp;nbsp;The sample uses a local &lt;code&gt;AllMiniLMEmbeddings&lt;/code&gt; model by default, though the vector store creation function can accept another embedding model.&lt;/li&gt;



&lt;li&gt;
&lt;strong&gt;What is the difference between semantic search and keyword search here?&lt;/strong&gt;&amp;nbsp;Semantic search finds meaning-based matches through vector similarity, while keyword search uses Oracle text search capabilities to find lexical matches; the sample combines both approaches.&lt;/li&gt;



&lt;li&gt;
&lt;strong&gt;How are final retrieval results chosen?&lt;/strong&gt;&amp;nbsp;The sample ranks semantic and keyword hits, accumulates scores by runbook, and selects the strongest fused result as the best match for the question.&lt;/li&gt;



&lt;li&gt;
&lt;strong&gt;What does &lt;/strong&gt;&lt;code&gt;&lt;strong&gt;OracleSemanticCache&lt;/strong&gt;&lt;/code&gt;&lt;strong&gt; add?&lt;/strong&gt;&amp;nbsp;It stores and retrieves prior answers for semantically similar questions, so repeated or near-repeated queries can reuse cached responses instead of rebuilding the answer.&lt;/li&gt;



&lt;li&gt;
&lt;strong&gt;What does &lt;/strong&gt;&lt;code&gt;&lt;strong&gt;OracleChatMessageHistory&lt;/strong&gt;&lt;/code&gt;&lt;strong&gt; add?&lt;/strong&gt;&amp;nbsp;It persists the user question and AI response in Oracle Database, giving the workflow a durable chat history.&lt;/li&gt;



&lt;li&gt;
&lt;strong&gt;Who is this article most useful for?&lt;/strong&gt;&amp;nbsp;It is most useful for developers building RAG-style applications with LangChain who want Oracle AI Database to handle document ingestion, vector search, hybrid retrieval, caching, and conversation persistence.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>langchain</category>
      <category>oracle</category>
      <category>database</category>
      <category>ai</category>
    </item>
    <item>
      <title>Is Oracle AI Database the best choice for small to mid size shops?</title>
      <dc:creator>Anya Summers</dc:creator>
      <pubDate>Wed, 08 Jul 2026 15:55:17 +0000</pubDate>
      <link>https://dev.to/oracledevs/is-oracle-ai-database-the-best-choice-for-small-to-mid-size-shops-lb8</link>
      <guid>https://dev.to/oracledevs/is-oracle-ai-database-the-best-choice-for-small-to-mid-size-shops-lb8</guid>
      <description>&lt;p&gt;&lt;strong&gt;Developers, startups, and small-to-medium-sized businesses (SMBs) all benefit from a converged data platform that scales from free development to global production.&lt;/strong&gt;&lt;/p&gt;





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

&lt;ul&gt;
&lt;li&gt;Oracle AI Database is worth a serious look for small and midsize teams when one application needs relational data, JSON, search, vectors, spatial queries, REST endpoints, and internal app tooling without operating a stack of separate services.&lt;/li&gt;



&lt;li&gt;
&lt;a href="https://www.oracle.com/database/free/" rel="noreferrer noopener"&gt;The free path&lt;/a&gt; lets you verify it: local containers, FreeSQL, SQLcl, SQL Developer, and Always Free Autonomous AI Database let you prove before paying.&lt;/li&gt;



&lt;li&gt;The free path isn’t everything: Resource limits, Always Free cloud constraints, backups, networking, and operational skills still matter.&lt;/li&gt;



&lt;li&gt;Oracle AI Database scales from local development and free tiers to enterprise deployments without changing database platforms.&lt;/li&gt;



&lt;li&gt;
&lt;a href="https://www.oracle.com/cloud/multicloud/" rel="noreferrer noopener"&gt;Oracle’s multicloud story&lt;/a&gt; is stronger than you might think: Oracle deployment patterns across AWS, Azure, and Google Cloud. Multicloud deployment options may differ slightly between providers.&lt;/li&gt;



&lt;li&gt;I’m not saying “Oracle for everything”, but to consider Oracle when its capabilities meet your needs: fewer databases, fewer synchronization layers, less operational overhead. &lt;a href="https://andersswanson.dev/2026/05/19/an-app-that-keeps-its-data-in-one-place-multi-model-database-pattern/" rel="noreferrer noopener"&gt;&lt;strong&gt;Try it with a sample app&lt;/strong&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&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%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F06%2FResized-i1-351x1024.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%2F06%2FResized-i1-351x1024.png" alt="Diagram comparing two application architectures. In the multi-service path, an application integrates separately with a SQL database, search service, vector store, queue, and administration interface, creating synchronization, monitoring, backup, and credential-management overhead. In the one-engine path, the application connects directly to Oracle AI Database, which provides relational, JSON, search, graph, vector, and application services in a single platform." width="351" height="1024"&gt;&lt;/a&gt;Comparing multi-service architectures with a consolidated Oracle AI Database approach.&lt;p&gt;&lt;/p&gt;

&lt;p&gt;Oracle AI Database isn’t an &lt;strong&gt;automatic “best database”&lt;/strong&gt; choice for every app, side project, or business system. If you need the absolute simplest embedded database, use SQLite. If your team already knows Postgres, that might be the shortest path.&lt;/p&gt;

&lt;p&gt;However, if your team needs one database that can cover normal relational data, JSON documents, search, spatial data, graph relationships, events, REST endpoints, APEX apps, local testing, and a managed cloud path, &lt;a href="https://www.oracle.com/database/free/" rel="noreferrer noopener"&gt;Oracle AI Database Free&lt;/a&gt; deserves a look.&lt;/p&gt;

&lt;p&gt;Everyone knows Oracle can run giant enterprise systems, but people generally assume this comes with complexity. People miss that a hobbyist or small-to-medium-size business can start small, paying little-to-nothing, and scale massively on a single, converged data platform.&lt;/p&gt;





&lt;h2&gt;Where Oracle May Surprise&amp;nbsp;You&lt;/h2&gt;

&lt;p&gt;Your app may start with customers and invoices. Then someone asks for geofenced service regions. Then support tickets need full-text search. Then a feature asks for semantic search over notes. Then an internal team wants a quick admin app. Then you need REST endpoints for a partner integration. Then the owner wants reporting.&lt;/p&gt;

&lt;p&gt;Each of those requests could nudge you to another service to deploy, operate, and patch. This results in ever-increasing operational overhead and architectural complexity.&lt;/p&gt;

&lt;p&gt;With Oracle AI Database, more of that can stay close to the data:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Relational tables for core records&lt;/li&gt;



&lt;li&gt;JSON for flexible payloads&lt;/li&gt;



&lt;li&gt;Hybrid search workloads, including full-text, i.e., combining search methods like vector and full-text.&lt;/li&gt;



&lt;li&gt;Spatial for location-aware queries&lt;/li&gt;



&lt;li&gt;Property graph features for relationship-heavy questions&lt;/li&gt;



&lt;li&gt;Events and database-backed messaging patterns, i.e., combining messaging with database transactions for atomic workflows.&lt;/li&gt;



&lt;li&gt;
&lt;a href="https://www.oracle.com/database/ai-vector-search/" rel="noreferrer noopener"&gt;Vector search&lt;/a&gt; for embedding-driven retrieval&lt;/li&gt;



&lt;li&gt;
&lt;a href="https://www.oracle.com/database/technologies/appdev/rest.html" rel="noreferrer noopener"&gt;ORDS&lt;/a&gt; for REST and SQL Developer Web workflows&lt;/li&gt;



&lt;li&gt;
&lt;a href="https://www.oracle.com/apex/" rel="noreferrer noopener"&gt;APEX&lt;/a&gt; for browser apps and internal tools&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’m not saying you have to use every feature: the options are there, and should be considered before adding another moving part from a third-party vendor.&lt;/p&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%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F06%2Fresized-i2-1-1024x675.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%2F06%2Fresized-i2-1-1024x675.png" alt="Diagram comparing suitable and unsuitable use cases for Oracle AI Database Free. Recommended uses include local development, integration testing, demonstrations, learning Oracle technologies, cloud validation, and small workloads. Production-oriented requirements such as large datasets, replication, clustering, long-term retention, higher concurrency, and complex workloads require paid deployments." width="799" height="527"&gt;&lt;/a&gt;Guidance on when Oracle AI Database Free is appropriate versus when production deployments are needed.&lt;p&gt;&lt;/p&gt;

&lt;p&gt;It’s the difference between &lt;strong&gt;“we can ship this in the database we already run”&lt;/strong&gt; vs. &lt;strong&gt;“we need to run and sync another service forever”&lt;/strong&gt;.&lt;/p&gt;





&lt;h2&gt;The Free Tooling Is Excellent&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://blogs.oracle.com/developers/5-oracle-ai-database-dev-tools-id-put-in-a-starter-kit" rel="noreferrer noopener"&gt;5 Free Oracle AI Database Dev Tools I’d Put in a Starter Kit&lt;/a&gt;, I laid out the starter kit I use day-to-day. These are also tools you can use prove your workload. Start small, then scale to cloud:&lt;/p&gt;

&lt;h4&gt;Local:&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://www.oracle.com/database/free/" rel="noreferrer noopener"&gt;Oracle AI Database Free container images&lt;/a&gt;: local or small development, POCs, and testing.&lt;/li&gt;



&lt;li&gt;SQLcl MCP Server: MCP integrations with your database.&lt;/li&gt;



&lt;li&gt;
&lt;a href="https://www.oracle.com/database/sqldeveloper/" rel="noreferrer noopener"&gt;SQL Developer&lt;/a&gt;: When you need a developer UI for your database.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;&lt;strong&gt;Cloud:&lt;/strong&gt;&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://freesql.com/" rel="noreferrer noopener"&gt;FreeSQL&lt;/a&gt;: free online labs, SQL testing, and disposable database connections for learning.&lt;/li&gt;



&lt;li&gt;
&lt;a href="https://docs.oracle.com/en/cloud/paas/autonomous-database/serverless/adbsb/autonomous-always-free.html" rel="noreferrer noopener"&gt;Always Free Autonomous AI Database&lt;/a&gt;: managed-cloud free tier for development, POCs, and testing.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Use the container when you need repeatable local development or disposable integration tests. Use FreeSQL when the right setup is no setup: a browser-based SQL environment with a personal schema for learning, examples, or quick query checks. Use SQLcl when you want scripts, automation, setup validation, exports, loads, and command-line repeatability.&lt;/p&gt;

&lt;p&gt;SQLcl also matters for AI-assisted development because it includes an MCP server. That gives tools like Codex or Claude Code a structured way to inspect schema metadata and run database operations through saved SQLcl connections instead of guessing from stale prompt context.&lt;/p&gt;

&lt;p&gt;Use SQL Developer when you need to browse schemas visually, inspect rows, debug SQL or PL/SQL, or explain something on a screen share. Use Always Free Autonomous AI Database when the app needs a persistent managed database in OCI for demos, APEX, ORDS, wallet-based connectivity, or cloud deployment validation.&lt;/p&gt;





&lt;h2&gt;Run on any major hyperscaler&lt;/h2&gt;

&lt;p&gt;One practical change from the old Oracle mental model is that Oracle AI Database is not only an Oracle-only conversation.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;Oracle can run on-prem, Oracle Cloud Infrastructure (OCI), and other cloud providers like &lt;a href="https://docs.oracle.com/en-us/iaas/Content/multicloud/get-started-deployment.htm" rel="noreferrer noopener"&gt;AWS, Azure, and Google Cloud&lt;/a&gt;. If you’re already bought into one of the “big three” cloud providers, that doesn’t preclude you from Oracle.&lt;/p&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%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F06%2Fresized-i3-1024x651.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%2F06%2Fresized-i3-1024x651.png" alt="Diagram comparing two application architectures. In the multi-service path, an application integrates separately with a SQL database, search service, vector store, queue, and administration interface, creating synchronization, monitoring, backup, and credential-management overhead. In the one-engine path, the application connects directly to Oracle AI Database, which provides relational, JSON, search, graph, vector, and application services in a single platform." width="800" height="509"&gt;&lt;/a&gt;Comparing multi-service architectures with a consolidated Oracle AI Database approach.&lt;p&gt;&lt;/p&gt;





&lt;h2&gt;My Practical Recommendation&lt;/h2&gt;

&lt;p&gt;For hobbyists, I’d start with Oracle AI Database Free when you need something more than basic relational storage.&lt;/p&gt;

&lt;p&gt;If you want to learn serious database development, build a portfolio app, test SQL beyond toy examples, try vector search, expose REST endpoints, build APEX screens, and run realistic integration tests.&lt;/p&gt;

&lt;p&gt;For small-to-mid-size businesses, I would evaluate it when the app is likely to need multiple data patterns but the team does not want to operate multiple databases and sync layers.&lt;/p&gt;

&lt;p&gt;A good first POC is small:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Run the local container.&lt;/li&gt;



&lt;li&gt;Create the first schema.&lt;/li&gt;



&lt;li&gt;Connect with SQLcl.&lt;/li&gt;



&lt;li&gt;Browse it with SQL Developer.&lt;/li&gt;



&lt;li&gt;Add one feature that would otherwise require another service.&lt;/li&gt;



&lt;li&gt;Rebuild the environment from scripts.&lt;/li&gt;



&lt;li&gt;Try the same app against Always Free Autonomous AI Database.&lt;/li&gt;
&lt;/ol&gt;





&lt;h2&gt;Final Thoughts&lt;/h2&gt;

&lt;p&gt;Oracle is an exceptional database when you want one data platform that can work with any kind data, that starts free, scales globally, and can absorb almost any app needs without adding another service.&lt;/p&gt;

&lt;p&gt;Start free. Prove one workflow. Consolidate services and data onto one platform. Scale globally.&lt;/p&gt;





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

&lt;p&gt;&lt;strong&gt;Is Oracle AI Database really a good choice for a hobby project?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Definitely. If you want to test against a real database, try vector search, build APEX screens, expose REST endpoints, or keep multiple data patterns in one place. If you only need a local file-backed store, SQLite is usually simpler.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is this just for Oracle shops?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No. A neutral team should evaluate it with containers, SQLcl, SQL Developer, FreeSQL, and Always Free Autonomous AI Database before committing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does multicloud support mean I can ignore cloud choice?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No. Oracle Database offerings across AWS, Azure, and Google Cloud make the deployment story more flexible, but they are not identical checkboxes. Regions, service availability, preview status, networking, support, pricing, and operational ownership still need review.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can a small business run production on the free tier?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Free tiers are great for learning, demos, and small hobby workloads, but not as a reliable production resource. Production data needs backup and restore expectations, monitoring, access control, scaling guarantees, security patching, recovery testing, support, and a clear owner.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When would I avoid Oracle AI Database?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Avoid it when the app only needs the simplest persistence layer, when the team already has a database workflow that fits, or when the Oracle learning curve would slow delivery more than consolidation helps. A good database choice (and architecture in general) makes your life simpler.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What are the limits of Oracle AI Database Free and Always Free Autonomous AI Database?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Refer to the &lt;a href="https://www.oracle.com/database/free/faq/" rel="noreferrer noopener"&gt;Free FAQ&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How does Oracle AI Database compare with PostgreSQL for SMB workloads?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Choose Oracle AI Database if regulated data, strong built-in security, mixed operational + analytical workloads are a priority, or if you want native AI/vector/agent workflows governed inside the database.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do I need Oracle-specific skills to get started?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No. It’s best to get started with the free tier, experiment, and see how your business can benefit.&lt;/p&gt;





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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://andersswanson.dev/2026/05/19/an-app-that-keeps-its-data-in-one-place-multi-model-database-pattern/" rel="noreferrer noopener"&gt;&lt;strong&gt;Try it with a sample app&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://blogs.oracle.com/developers/5-oracle-ai-database-dev-tools-id-put-in-a-starter-kit" rel="noreferrer noopener"&gt;5 Free Oracle AI Database Dev Tools I’d Put in a Starter Kit&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://andersswanson.dev/2025/05/22/oracle-database-for-free/" rel="noreferrer noopener"&gt;Oracle AI Database for Free?&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://docs.oracle.com/en/cloud/paas/autonomous-database/serverless/adbsb/autonomous-always-free.html" rel="noreferrer noopener"&gt;Always Free Autonomous AI Database&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://www.oracle.com/autonomous-database/pricing/" rel="noreferrer noopener"&gt;Autonomous AI Database Pricing&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://docs.oracle.com/en-us/iaas/Content/multicloud/get-started-deployment.htm" rel="noreferrer noopener"&gt;OCI Multicloud Deployment with Oracle&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://docs.cloud.google.com/oracle/database/docs/overview" rel="noreferrer noopener"&gt;Oracle Database@Google Cloud overview&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/oracle/" rel="noreferrer noopener"&gt;Oracle on Azure documentation&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://aws.amazon.com/multicloud/features/" rel="noreferrer noopener"&gt;Multicloud on AWS features&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>database</category>
      <category>oracle</category>
      <category>ai</category>
      <category>aidatabase</category>
    </item>
    <item>
      <title>What Is a Converged Database? Definition, Five Tests, and AI Use Cases</title>
      <dc:creator>Anya Summers</dc:creator>
      <pubDate>Mon, 06 Jul 2026 16:09:42 +0000</pubDate>
      <link>https://dev.to/oracledevs/what-is-a-converged-database-definition-five-tests-and-ai-use-cases-3ngf</link>
      <guid>https://dev.to/oracledevs/what-is-a-converged-database-definition-five-tests-and-ai-use-cases-3ngf</guid>
      <description>&lt;p&gt;A converged database is a single database engine that natively supports multiple data models - relational, document/JSON, graph, vector, spatial, and text - under &lt;strong&gt;one optimizer, one transaction boundary, one consistency model, and one security and governance domain&lt;/strong&gt;, exposed through the access surfaces developers expect, including &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/" rel="noopener noreferrer"&gt;SQL&lt;/a&gt;, document APIs, and &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/" rel="noopener noreferrer"&gt;REST&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In the &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/" rel="noopener noreferrer"&gt;Oracle AI Database&lt;/a&gt; context, this matters because &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/vecse/overview-ai-vector-search.html" rel="noopener noreferrer"&gt;vector search&lt;/a&gt;, JSON/document access, graph patterns, and relational joins can be treated as one data architecture rather than a chain of specialized services.&lt;/p&gt;

&lt;p&gt;Many databases can store several of these models. The qualifier that matters isn't the list of models. It's everything after the dash - because that is where multi-store architectures incur their costs.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Answer box - the short version&lt;/strong&gt;&lt;br&gt; &lt;strong&gt;What it is:&lt;/strong&gt; one database engine in which relational tables, JSON documents, graphs, vectors, spatial data, and text share the same transactions, the same query optimizer, the same consistency guarantees, and the same security model.&lt;br&gt; &lt;strong&gt;Why it matters:&lt;/strong&gt; AI and modern operational workloads need retrieval that is simultaneously fresh, governed, and joined across data models - properties that are difficult to assemble from separate specialized stores connected by synchronization pipelines.&lt;br&gt; &lt;strong&gt;How it differs from multi-model:&lt;/strong&gt; &lt;em&gt;multi-model&lt;/em&gt; describes what a product can store; &lt;em&gt;converged&lt;/em&gt; describes which guarantees span the models. Storing several models is now common; one transaction boundary, one optimizer, and one governance domain across them is not.&lt;/p&gt;
&lt;/blockquote&gt;





&lt;h2&gt;Key takeaways&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A converged database is defined by cross-model guarantees, not just by storage support.&lt;/li&gt;



&lt;li&gt;The five tests are one transaction boundary, one optimizer, one consistency model, one governance domain, and shared access surfaces.&lt;/li&gt;



&lt;li&gt;For RAG and AI agent workloads, convergence matters because retrieval must be fresh, governed, and joined with operational data.&lt;/li&gt;



&lt;li&gt;Multi-model storage is common; cross-model guarantees are the differentiator.&lt;/li&gt;
&lt;/ul&gt;





&lt;h2&gt;Converged database vs multi-model database vs vector database&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;th&gt;Database pattern&lt;/th&gt;
&lt;th&gt;What it means&lt;/th&gt;
&lt;th&gt;AI workload implication&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Converged database&lt;/td&gt;
&lt;td&gt;One engine supports multiple data models under shared transaction, optimizer, consistency, and governance guarantees.&lt;/td&gt;
&lt;td&gt;Useful when RAG or agents need retrieval that is fresh, governed, and joined with operational context.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-model database&lt;/td&gt;
&lt;td&gt;One product can store several models, but the guarantees may stop at model boundaries.&lt;/td&gt;
&lt;td&gt;Useful for consolidation, but not sufficient if cross-model queries, rollback, or access control must behave as one system.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vector database&lt;/td&gt;
&lt;td&gt;A specialized system optimized for embedding similarity search.&lt;/td&gt;
&lt;td&gt;Useful for standalone similarity serving; less complete when answers also need live predicates, transactions, and relational joins.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;One disambiguation before we start: this is about converged &lt;em&gt;databases&lt;/em&gt;, not converged &lt;em&gt;infrastructure&lt;/em&gt;. Hyperconverged infrastructure is a hardware story about collapsing compute, storage, and networking. This is a data architecture story about collapsing several database engines into one.&lt;/p&gt;

&lt;p&gt;Everything in this article runs. Each claim maps to a numbered assertion in a public companion repository, &lt;a href="https://github.com/oracle-devrel/oracle-umt-developer-hub" rel="noopener noreferrer"&gt;converged-database-lab&lt;/a&gt;, executed by CI against Oracle AI Database 26ai Free - a freely available container (See &lt;a href="https://docs.oracle.com/en/database/oracle/mongodb-api/mgapi/overview-oracle-database-api-mongodb.html" rel="noopener noreferrer"&gt;Oracle Database API for MongoDB, overview&lt;/a&gt; (includes beta-stage notes for &lt;code&gt;$vectorSearch&lt;/code&gt;/&lt;code&gt;$search&lt;/code&gt;/&lt;code&gt;$changeStream&lt;/code&gt;)&lt;a href="https://docs.oracle.com/en/database/oracle/mongodb-api/mgapi/overview-oracle-database-api-mongodb.html" rel="noopener noreferrer"&gt;)&lt;/a&gt;. The methodology section at the end describes how to reproduce every result.&lt;/p&gt;





&lt;h2&gt;Where did the term converged database come from?&lt;/h2&gt;

&lt;p&gt;Oracle introduced the term "converged database" around 2020. Maria Colgan's original definition - native support for all modern data types and development paradigms in a single engine (See &lt;a href="https://sqlmaria.com/2020/03/05/what-is-a-converged-database/" rel="noopener noreferrer"&gt;M. Colgan, "What is a Converged Database?&lt;/a&gt;," March 2020) - described convergence primarily as &lt;em&gt;consolidation&lt;/em&gt;: one system instead of five, fewer licenses, fewer backups, less integration plumbing.&lt;/p&gt;

&lt;p&gt;Three developments since then changed convergence from a convenience into a structural property.&lt;/p&gt;

&lt;p&gt;First, the SQL standard absorbed the models. SQL:2016 brought JSON operators into the language, and SQL:2023 added a native JSON type and an entire new part - ISO/IEC 9075-16, &lt;a href="https://docs.oracle.com/en/database/oracle/property-graph/" rel="noopener noreferrer"&gt;Property Graph Queries&lt;/a&gt; (SQL/PGQ) - bringing graph pattern matching into standard SQL (See &lt;a href="https://peter.eisentraut.org/blog/2023/04/04/sql-2023-is-finished-here-is-whats-new." rel="noopener noreferrer"&gt;ISO/IEC 9075:2023, &lt;em&gt;SQL&lt;/em&gt;, including Part 16, &lt;em&gt;Property Graph Queries (SQL/PGQ)&lt;/em&gt;, June 2023; SQL/JSON operators in SQL:2016; native JSON type (T801) in SQL:2023. Summary: P. Eisentraut, "SQL:2023 is finished: Here is what's new."&lt;/a&gt;) Graph traversal is no longer a separate database category's exclusive capability; it is a clause in the FROM list.&lt;/p&gt;

&lt;p&gt;Second, AI workloads arrived with a requirement that multi-store architectures must engineer around: retrieval that is simultaneously &lt;em&gt;fresh&lt;/em&gt;, &lt;em&gt;governed&lt;/em&gt;, and &lt;em&gt;joined&lt;/em&gt;. We return to this below.&lt;/p&gt;

&lt;p&gt;Third, a rigorous academic argument for the same convergence pattern arrived independently. In "What Goes Around Comes Around... And Around..." (SIGMOD Record, June 2024, &lt;a href="https://db.cs.cmu.edu/papers/2024/whatgoesaround-sigmodrec2024.pdf" rel="noopener noreferrer"&gt;please see the full version under here&lt;/a&gt;, Michael Stonebraker and Andrew Pavlo - two of the field's most credentialed relational researchers - surveyed twenty years of data-model alternatives and concluded that document databases are "on a collision course with RDBMSs," whose differences "have diminished over time and should become nearly indistinguishable in the future." Vector databases, in their analysis, "are essentially document-oriented DBMSs with specialized ANN indexes" - indexes being "a feature, not the foundation of a new system architecture." On text search engines: "It would be valuable if RDBMSs had a better story for search so these would not have to be a separate product."&lt;/p&gt;

&lt;p&gt;The term began as Oracle vocabulary. The architectural trajectory it names is now argued, on independent evidence, in the field's own literature.&lt;/p&gt;





&lt;h2&gt;What are the five tests for a converged database?&lt;/h2&gt;

&lt;p&gt;"Supports multiple models" is a property of a product's storage layer. Convergence is a property of its guarantees. Five testable criteria separate the two - each one demonstrated by a runnable, asserted proof in the companion repository.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. One transaction boundary.&lt;/strong&gt; A single ACID transaction can span a relational insert, a document write, a vector update, and the indexes that serve them - and a rollback reverts all of it atomically. This is the test most multi-model systems do not attempt: Lu and Holubová's survey of multi-model databases (ACM Computing Surveys, 2019) examined some twenty systems and reported finding no "explicit information about existence of a special type of transaction management" across data models (&lt;a href="https://dl.acm.org/doi/10.1145/3323214" rel="noopener noreferrer"&gt;J. Lu and I. Holubová, "Multi-model Databases: A New Journey to Handle the Variety of Data," &lt;em&gt;ACM Computing Surveys&lt;/em&gt; 52(3), Article 55, 2019.)&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. One optimizer.&lt;/strong&gt; A cost-based planner produces a &lt;em&gt;single costed plan&lt;/em&gt; for a statement that touches several models. This claim is checkable: the companion repository runs &lt;code&gt;EXPLAIN PLAN&lt;/code&gt; over a statement combining a graph pattern, a JSON predicate, a vector distance ranking, and relational joins, and asserts that one plan tree covers all four (proof 5 below). If a "graph query" is an application-side loop over a service API, there is no such plan - there is a distributed system whose join order is hard-coded in application logic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. One consistency model.&lt;/strong&gt; Read-your-writes holds across every model and every API, because no replication pipeline sits between the models - no change streams feeding a sidecar process, no oplog window, no reindex delay.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. One security and governance domain.&lt;/strong&gt; The same grants, the same row-level policies, the same audit stream cover the document API, the SQL interface, the vector search path, and the graph traversal, because each is a projection of the same engine over the same rows.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Shared access surfaces.&lt;/strong&gt; SQL, a &lt;a href="https://docs.oracle.com/en/database/oracle/mongodb-api/mgapi/overview-oracle-database-api-mongodb.html" rel="noopener noreferrer"&gt;MongoDB-compatible document API&lt;/a&gt;, and REST operate on the same data as projections of one engine - rather than different engines behind one gateway.&lt;/p&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%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F06%2FPicture1-1.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%2F06%2FPicture1-1.png" alt="Diagram showing multiple data models—relational, document, graph, vector, spatial, and text—feeding into a converged database. Between them are five shared guarantees: one transaction boundary, one optimizer, one consistency model, one governance domain, and shared access surfaces. A note contrasts a simple multi-model system, where guarantees stop at the model boundary, with a fully converged database." width="799" height="345"&gt;&lt;/a&gt;&lt;em&gt;Converged = multi-model + the guarantees: many data models pass through five gates - one transaction boundary, one optimizer, one consistency model, one governance domain, shared access surfaces - to qualify as converged; multi-model stops at the first gate.&lt;/em&gt;&lt;p&gt;&lt;/p&gt;

&lt;p&gt;The proof matrix (every script runs in CI; assertion counts per script):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Test&lt;/th&gt;
&lt;th&gt;Proof&lt;/th&gt;
&lt;th&gt;Assertions&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;One transaction boundary&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/oracle-devrel/oracle-umt-developer-hub/blob/main/labs/converged-database-lab/modules/01-what-is-a-converged-database/scripts/01-one-transaction-every-model.sql" rel="noopener noreferrer"&gt;&lt;code&gt;01-one-transaction-every-model.sql&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;One engine under two APIs&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/oracle-devrel/oracle-umt-developer-hub/blob/main/labs/converged-database-lab/modules/01-what-is-a-converged-database/scripts/02-duality-roundtrip.js" rel="noopener noreferrer"&gt;&lt;code&gt;02-duality-roundtrip.js&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cross-model statement&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/oracle-devrel/oracle-umt-developer-hub/blob/main/labs/converged-database-lab/modules/01-what-is-a-converged-database/scripts/03-one-optimizer.sql" rel="noopener noreferrer"&gt;&lt;code&gt;03-one-optimizer.sql&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;One consistency model&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/oracle-devrel/oracle-umt-developer-hub/blob/main/labs/converged-database-lab/modules/01-what-is-a-converged-database/scripts/04-read-your-writes.js" rel="noopener noreferrer"&gt;&lt;code&gt;04-read-your-writes.js&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;One optimizer, one plan&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/oracle-devrel/oracle-umt-developer-hub/blob/main/labs/converged-database-lab/modules/01-what-is-a-converged-database/scripts/05-one-plan.sql" rel="noopener noreferrer"&gt;&lt;code&gt;05-one-plan.sql&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The first proof is the one to internalize. Four writes - a relational order, its line item, a JSON document into a collection, and a vector embedding update - in one uncommitted transaction:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;INSERT INTO orders (customer_id, store_id, status, total_amount)
VALUES (1, 1, 'placed', 99.99);

INSERT INTO order_items (order_id, line_no, product_id, qty, unit_price)
VALUES ((SELECT MAX(order_id) FROM orders), 1, 1, 1, 99.99);

INSERT INTO events (data) VALUES (JSON('{"type":"order_placed","channel":"lab","note":"document write, same txn"}'));

UPDATE support_tickets
   SET status = 'pending',
       embedding = TO_VECTOR('[0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5]', 8, FLOAT32)
 WHERE ticket_id = 1;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now consider the same four writes spread across a typical specialized stack - DynamoDB for the order, OpenSearch for the searchable event, Pinecone for the embedding. Each system's atomicity stops at its own boundary: DynamoDB's &lt;code&gt;TransactWriteItems&lt;/code&gt; groups up to 100 actions &lt;em&gt;within DynamoDB&lt;/em&gt; (See &lt;a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/transaction-apis.html" rel="noopener noreferrer"&gt;Amazon DynamoDB Developer Guide, "Amazon DynamoDB transactions."&lt;/a&gt;), and Pinecone documents that its index updates are eventually consistent, providing a freshness-check mechanism precisely because written data is not immediately visible (See &lt;a href="https://docs.pinecone.io/guides/index-data/check-data-freshness)" rel="noopener noreferrer"&gt;Pinecone documentation, "Check data freshness."&lt;/a&gt;). No transaction API spans the three systems. Across separate systems, rollback becomes an application-level compensation problem - code that must be written, tested, and maintained for every failure mode.&lt;/p&gt;

&lt;p&gt;The optimizer proof pairs a cross-model statement with its execution evidence. The statement traverses a referral graph, ranks the reachable customers' support tickets by vector similarity, and joins relational context:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;ring AS (
  SELECT DISTINCT cid FROM GRAPH_TABLE (customer_graph
    MATCH (a IS customers) -[IS referrals]-&amp;gt;{1,4} (b IS customers)
    WHERE a.customer_id = 10
    COLUMNS (b.customer_id AS cid))
)
SELECT 'ASSERT:converged-query-returns:' ||
       CASE WHEN COUNT(*) &amp;gt; 0 THEN 'PASS' ELSE 'FAIL' END
FROM (
  SELECT c.customer_id
  FROM ring r
  JOIN customers c        ON c.customer_id = r.cid
  JOIN support_tickets st ON st.customer_id = c.customer_id
  WHERE st.status IN ('open','pending')
  ORDER BY VECTOR_DISTANCE(st.embedding,
           TO_VECTOR('[0.35,-0.35,0.35,-0.35,0.35,-0.35,0.35,-0.35]', 8, FLOAT32), COSINE)
  FETCH FIRST 10 ROWS ONLY
);&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Note that the claim &lt;em&gt;is&lt;/em&gt; the assertion - that is the contract of the companion repository: the article quotes tests, not aspirations.&lt;/p&gt;

&lt;p&gt;What does the engine do with such a statement? Oracle's documentation states that the &lt;code&gt;GRAPH_TABLE&lt;/code&gt; operator "is internally translated into equivalent SQL" (See &lt;a href="https://docs.oracle.com/en/database/oracle/property-graph/" rel="noopener noreferrer"&gt;Oracle Database Property Graph Developer's Guide - GRAPH_TABLE operator and SQL translation.&lt;/a&gt;) - the graph pattern becomes relational algebra and is costed by the same optimizer as everything else. Proof 5 captures the evidence: &lt;code&gt;EXPLAIN PLAN&lt;/code&gt; over a four-model statement (graph + JSON + vector + relational), with assertions that the plan references the graph's edge table, the JSON collection, the vector column's table, and the relational tables - in &lt;strong&gt;one&lt;/strong&gt; plan tree:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;| Id  | Operation                         | Name            |
|   0 | SELECT STATEMENT                  |                 |
|   1 |  COUNT STOPKEY                    |                 |
|   2 |   VIEW                            |                 |
|   3 |    SORT ORDER BY STOPKEY          |                 |  ← vector-distance ranking
|   4 |     HASH JOIN                     |                 |
|   5 |      HASH JOIN ANTI               |                 |  ← JSON NOT EXISTS
|   6 |       HASH JOIN                   |                 |
|   7 |        VIEW                       |                 |
|   8 |         HASH UNIQUE               |                 |
|   9 |          VIEW                     | CUSTOMER_GRAPH  |  ← the graph, as a row source
|  10 |           UNION-ALL               |                 |  ← {1,4} hops, unrolled
|  11 |            INDEX RANGE SCAN       | SYS_C008779     |
|  ...|            ... 1–4 hop joins over the referral edge index ...      |
|  28 |        TABLE ACCESS FULL          | CUSTOMERS       |  ← relational
|  29 |       TABLE ACCESS FULL           | EVENTS          |  ← JSON collection
|  30 |      TABLE ACCESS FULL            | SUPPORT_TICKETS |  ← vector column's table&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;em&gt;(Illustrative output from &lt;code&gt;DBMS_XPLAN.DISPLAY&lt;/code&gt;, abridged; plan hash 4056235962 on the lab container. System-generated index names such as &lt;code&gt;SYS_C008779&lt;/code&gt; vary per build; the proof's assertions therefore resolve index names through &lt;code&gt;user_indexes&lt;/code&gt; rather than hard-coding them.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Read what the plan shows: the graph quantifier &lt;code&gt;{1,4}&lt;/code&gt; unrolls into a UNION-ALL of one- to four-hop joins over the referral edge index, appearing as an ordinary view row source named for the graph; the JSON predicate becomes a hash anti-join against the collection table; the vector ranking is a sort over the tickets table - one tree, one cost model. There is no federation seam in that plan, no per-model planner boundary, and no statistics boundary. That is the concrete meaning of "one optimizer."&lt;/p&gt;





&lt;h2&gt;How is a converged database different from a multi-model database?&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;It is not "multi-model."&lt;/strong&gt; Multi-model means several data models are storable. Converged means the five tests pass. The distinction has academic prior art: the same 2019 survey that documented the transaction gap also ruled that an RDBMS storing another model's data without a cross-model query language and "optimization of query evaluation" is not meaningfully multi-model ((&lt;a href="https://dl.acm.org/doi/10.1145/3323214" rel="noopener noreferrer"&gt;J. Lu and I. Holubová, "Multi-model Databases: A New Journey to Handle the Variety of Data," &lt;em&gt;ACM Computing Surveys&lt;/em&gt; 52(3), Article 55, 2019.)&lt;/a&gt;. Storage is necessary, not sufficient. The full treatment of this line is its own article in this series.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It is not a vector index with a database attached.&lt;/strong&gt; Similarity search is one capability of an AI data architecture, not the architecture itself. The deeper requirements - filtered search against live relational predicates, access control enforced inside retrieval, embeddings updated in the same transaction as the facts they encode - are the qualifiers in the definition above. That argument, with the vendor-documented consistency behaviors of the specialized stores, is developed later in this series.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It is not five engines behind one API gateway.&lt;/strong&gt; A unified API over separate engines unifies syntax and nothing else; the transaction boundary, the optimizer, the consistency model, and the governance domain remain fragmented.&lt;/p&gt;

&lt;p&gt;To be precise about what the alternatives genuinely provide - this series does not argue against capabilities its subjects do not claim, and the following is sourced from each vendor's own documentation, as of June 2026:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;MongoDB's multi-document ACID transactions are real, including across shards, with documented operational parameters (See &lt;a href="https://www.mongodb.com/docs/manual/core/transactions-production-consideration/)" rel="noopener noreferrer"&gt;MongoDB documentation, "Production Considerations" (transactions)&lt;/a&gt;) Its search and vector search, however, run in &lt;code&gt;mongot&lt;/code&gt;, a separate Lucene-based process fed from the database by change streams; MongoDB's architecture documentation describes indexes "built from the data continuously sourced from the database," and its search documentation describes eventual consistency without read-after-write guarantees (See &lt;a href="https://www.mongodb.com/docs/manual/tutorial/mongot-sizing/advanced-guidance/architecture/" rel="noopener noreferrer"&gt;MongoDB documentation, "mongot Architecture" (search process, change-stream sourcing) &lt;/a&gt;and &lt;a href="https://www.mongodb.com/docs/atlas/atlas-search/performance/index-performance/" rel="noopener noreferrer"&gt;Atlas Search index performance (consistency)&lt;/a&gt;. &lt;/li&gt;



&lt;li&gt;ArangoDB's single-server deployments offer genuine cross-model ACID transactions; its own documentation states the qualifiers that apply to sharded clusters (See &lt;a href="https://docs.arangodb.com/3.12/develop/transactions/limitations/" rel="noopener noreferrer"&gt;ArangoDB documentation, "Transactions - Limitations."&lt;/a&gt; ).&lt;/li&gt;



&lt;li&gt;PostgreSQL's extension ecosystem (pgvector, PostGIS, and others) shares one transaction manager, one planner, and one security model - a real architectural achievement. The seams documented by the projects themselves concern optimization depth: pgvector's README notes that with HNSW indexes, filtering "is applied after the index is scanned," with iterative scan modes added as mitigation (See &lt;a href="https://github.com/pgvector/pgvector" rel="noopener noreferrer"&gt;pgvector README (filtering and iterative scans)&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each of these systems passes some of the five tests. Converged means passing all five at once.&lt;/p&gt;

&lt;p&gt;One further distinction: a converged database is not what results from storing JSON in an unindexed text or BLOB column. Storage without first-class semantics - native indexing, optimizer statistics, partial updates, path expressions in the query language - is storage, not support. A native JSON type with a binary representation designed for the engine (OSON, in Oracle's case), multivalue indexes, and full SQL/JSON integration is what "native" means here (See &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/jsnvu/overview-json-relational-duality-views.html" rel="noopener noreferrer"&gt;Oracle JSON-Relational Duality Developer's Guide&lt;/a&gt; (duality views, etags, &lt;code&gt;_id&lt;/code&gt; requirement, documented restrictions); &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/vecse/overview-ai-vector-search.html" rel="noopener noreferrer"&gt;Oracle AI Vector Search overview&lt;/a&gt;).&lt;/p&gt;





&lt;h2&gt;Why did document databases diverge from relational databases?&lt;/h2&gt;

&lt;p&gt;A credible definition of convergence has to account for why divergence happened in the first place. I can speak to this directly: I was part of it.&lt;/p&gt;

&lt;p&gt;The common relational account holds that NoSQL was a misunderstanding - developers drawn in by marketing ("SQL is slow," "ACID is optional") who needed twenty years to rediscover transactions. Even Stonebraker and Pavlo, in an otherwise rigorous paper, characterize the document movement as impedance-mismatch complaints plus marketing, and close the denormalization question with "the problems with denormalization/prejoining is an old topic that dates back to the 1970s" (&lt;a href="https://db.cs.cmu.edu/papers/2024/whatgoesaround-sigmodrec2024.pdf" rel="noopener noreferrer"&gt;See the source here&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;The published record tells a more specific story. Werner Vogels documented the workload analysis behind Amazon's move: roughly 70 percent of Amazon's relational operations were single-row, key-value accesses, and another 20 percent returned rows from a single table (See W. Vogels, &lt;a href="https://www.allthingsdistributed.com/2017/10/a-decade-of-dynamo.html" rel="noopener noreferrer"&gt;"A Decade of Dynamo,"&lt;/a&gt; October 2017&lt;a href="https://www.allthingsdistributed.com/2017/10/a-decade-of-dynamo.html" rel="noopener noreferrer"&gt;)&lt;/a&gt;. At that scale, on workloads with known, fixed access patterns, distributed join cost was a measurable per-request tax. Denormalizing around the access pattern - the discipline that became single-table design, which I spent years building and teaching at AWS - was not a rejection of relational theory. It was engineering against the physics of the read path.&lt;/p&gt;

&lt;p&gt;The citation in that SIGMOD passage repays a closer read. The reference for "settled in the 1970s" is E. F. Codd - the 1971 normalization paper, RJ909 (E. F. Codd, "Further Normalization of the Data Base Relational Model," IBM Research Report RJ909, 1971.). But Codd's contemporaneous writing frames stored redundancy as a workload-dependent tradeoff, not a prohibition. In his 1969 IBM research report: "Only in an environment with a heavy load of queries relative to the other kinds of interaction with the data bank would strong redundancy be justified in the stored set of relations" (See E. F. Codd, &lt;a href="https://technology.amis.nl/wp-content/uploads/images/RJ599.pdf" rel="noopener noreferrer"&gt;"Derivability, Redundancy and Consistency of Relations Stored in Large Data Banks,"&lt;/a&gt; IBM Research Report RJ599, August 1969 (reprinted in &lt;em&gt;SIGMOD Record&lt;/em&gt; 38(1), 2009), §5.). In the 1970 CACM paper, he priced it: stored redundancy consumes "extra storage space and update time" in exchange for "a potential drop in query time" (See E. F. Codd, "&lt;a href="https://www.seas.upenn.edu/~zives/03f/cis550/codd.pdf" rel="noopener noreferrer"&gt;A Relational Model of Data for Large Shared Data Banks," &lt;/a&gt;CACM 13(6), June 1970, §2.2.1.). That is a cost model - reads versus writes - and it is the same dial document data modelers have been turning for fifteen years. (A corollary, which is ours rather than Codd's: immutable data is the limiting case in which the update side of the tradeoff goes to zero and redundancy becomes nearly free.)&lt;/p&gt;

&lt;p&gt;Codd drew one more distinction in 1969 that the document era set aside and the converged era restores: the &lt;em&gt;named set&lt;/em&gt; of relations - the logical model - versus the &lt;em&gt;stored set&lt;/em&gt;, the physical representation (See E. F. Codd, &lt;a href="https://technology.amis.nl/wp-content/uploads/images/RJ599.pdf" rel="noopener noreferrer"&gt;"Derivability, Redundancy and Consistency of Relations Stored in Large Data Banks,"&lt;/a&gt; IBM Research Report RJ599, August 1969 (reprinted in &lt;em&gt;SIGMOD Record&lt;/em&gt; 38(1), 2009), §5.). Keep the logical model normalized; let the stored representation serve the workload. Document databases collapsed that distinction: they won read locality at the price of data independence, because the schema &lt;em&gt;was&lt;/em&gt; the access pattern.&lt;/p&gt;

&lt;p&gt;Convergence, implemented carefully, rebuilds Codd's separation with modern machinery. In Oracle AI Database 26ai, a &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/jsnvu/overview-json-relational-duality-views.html" rel="noopener noreferrer"&gt;JSON Relational Duality View&lt;/a&gt; is a document that &lt;em&gt;is&lt;/em&gt; its underlying rows - readable and writable as a document through the MongoDB-compatible API, fully normalized underneath, with lock-free optimistic concurrency via etags (See &lt;a href="https://docs.oracle.com/en/database/oracle/mongodb-api/mgapi/overview-oracle-database-api-mongodb.html" rel="noopener noreferrer"&gt;Oracle Database API for MongoDB, overview&lt;/a&gt; (includes beta-stage notes for &lt;code&gt;$vectorSearch&lt;/code&gt;/&lt;code&gt;$search&lt;/code&gt;/&lt;code&gt;$changeStream&lt;/code&gt;) and (See &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/jsnvu/overview-json-relational-duality-views.html" rel="noopener noreferrer"&gt;Oracle JSON-Relational Duality Developer's Guide&lt;/a&gt; (duality views, etags, &lt;code&gt;_id&lt;/code&gt; requirement, documented restrictions); &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/vecse/overview-ai-vector-search.html" rel="noopener noreferrer"&gt;Oracle AI Vector Search overview&lt;/a&gt;). The proof script updates a customer's segment through the document API and reads the change back through SQL in the same engine:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;// Update segment THROUGH THE DOCUMENT API...
col.updateOne({ _id: 42 }, { $set: { segment: 'vip' } });
const after = col.findOne({ _id: 42 });
print('ASSERT:dv-doc-updated:' + (after.segment === 'vip' ? 'PASS' : 'FAIL'));

// ...and read it back through SQL in the SAME api (one engine underneath):
const rows = db.aggregate([{ $sql: 'SELECT segment AS "segment" FROM customers WHERE customer_id = 42' }]).toArray();
print('ASSERT:dv-sql-sees-doc-write:' + (rows.length === 1 &amp;amp;&amp;amp; rows[0].segment === 'vip' ? 'PASS' : 'FAIL'));&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Two qualifications, from the product documentation: duality views require an &lt;code&gt;_id&lt;/code&gt; field as the document identifier, and they carry documented restrictions (among them, no Virtual Private Database policies on the view itself) (See &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/jsnvu/overview-json-relational-duality-views.html" rel="noopener noreferrer"&gt;Oracle JSON-Relational Duality Developer's Guide&lt;/a&gt; (duality views, etags, &lt;code&gt;_id&lt;/code&gt; requirement, documented restrictions); &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/vecse/overview-ai-vector-search.html" rel="noopener noreferrer"&gt;Oracle AI Vector Search overview&lt;/a&gt;); the MongoDB-compatible API documents &lt;code&gt;$vectorSearch&lt;/code&gt;, &lt;code&gt;$search&lt;/code&gt;, and &lt;code&gt;$changeStream&lt;/code&gt; as beta features (See &lt;a href="https://docs.oracle.com/en/database/oracle/mongodb-api/mgapi/overview-oracle-database-api-mongodb.html" rel="noopener noreferrer"&gt;Oracle Database API for MongoDB, overview&lt;/a&gt; (includes beta-stage notes for &lt;code&gt;$vectorSearch&lt;/code&gt;/&lt;code&gt;$search&lt;/code&gt;/&lt;code&gt;$changeStream&lt;/code&gt;). Neither qualification affects the scripts above, which use general-availability surfaces only.&lt;/p&gt;

&lt;p&gt;Model the domain. Project the access. The document patterns still matter - embedding, referencing, bucketing are still bets on read/write ratios, and the dials Codd priced in 1970 still exist. What changed is that you stop paying so much for the joins you cannot avoid in a pure document database. When joins are cheap, denormalization more often becomes a &lt;em&gt;projection&lt;/em&gt; you declare instead of a copy you maintain.&lt;/p&gt;





&lt;h2&gt;Why does a converged database matter for RAG and AI agents?&lt;/h2&gt;

&lt;p&gt;AI did not create the multi-store consistency problem. It removed the tolerance for it.&lt;/p&gt;

&lt;p&gt;A retrieval-augmented pipeline - and, more acutely, an agent that takes actions - needs context that is fresh, governed, and joined. Fresh: the embedding must reflect the row as it is now, not as of the last synchronization. Governed: the user's (or agent's) permissions must be enforced &lt;em&gt;inside&lt;/em&gt; retrieval, not in application code that every access path is assumed to traverse. Joined: "similar documents" is rarely the production question; "similar documents for this customer, in this region, with an open ticket" is.&lt;/p&gt;

&lt;p&gt;In a multi-store architecture, each of those properties is a pipeline. The vector index trails the operational store by design: Pinecone's documentation provides a data-freshness checking mechanism because updates are not immediately visible to queries (See &lt;a href="https://docs.pinecone.io/guides/index-data/check-data-freshness)" rel="noopener noreferrer"&gt;Pinecone documentation, "Check data freshness."&lt;/a&gt;), and MongoDB's documentation describes search indexes continuously sourced from the database by a separate process, with eventual consistency and no read-after-write guarantee (See &lt;a href="https://www.mongodb.com/docs/manual/tutorial/mongot-sizing/advanced-guidance/architecture/" rel="noopener noreferrer"&gt;MongoDB documentation, "mongot Architecture" (search process, change-stream sourcing) &lt;/a&gt;and &lt;a href="https://www.mongodb.com/docs/atlas/atlas-search/performance/index-performance/" rel="noopener noreferrer"&gt;Atlas Search index performance (consistency)&lt;/a&gt;. When the index disagrees with the table, a language model does not become uncertain; it becomes &lt;em&gt;confidently wrong&lt;/em&gt; about stale facts. I call this State Vector Dissonance - otherwise known as hallucinating with confidence. When the consumer is an agent with the authority to act, the staleness window converts directly into business risk.&lt;/p&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%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F06%2FPicture2-1.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%2F06%2FPicture2-1.png" alt="Side-by-side architecture comparison. On the left, an application queries separate search, graph, vector, and cache services synchronized from an operational store, introducing network hops and synchronization complexity. On the right, the application sends requests to Oracle AI Database 26ai, which natively supports document, relational, graph, vector, spatial, time-series, full-text, SQL, and JSON capabilities within a single engine." width="800" height="367"&gt;&lt;/a&gt;&lt;em&gt;One request, two architectures: a polyglot stack assembles every answer in the application across an operational store, search, graph, vector, and cache services connected by query and CDC/ETL sync buses - every edge adds a network hop, serialization, sync lag, and stale reads. A converged engine answers the same request in one transaction with one optimizer.&lt;/em&gt;&lt;p&gt;&lt;/p&gt;

&lt;p&gt;The converged response to this problem is not a faster pipeline; it is the absence of one:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;const evts = db.getCollection('events');
const marker = 'rww-' + Math.floor(Math.random() * 1e9);
evts.insertOne({ type: 'consistency_probe', marker: marker });

const viaSql = db.aggregate([
  { $sql: 'SELECT COUNT(*) AS "n" FROM events e WHERE e.data.marker.string() = \'' + marker + '\'' }
]).toArray();
print('ASSERT:read-your-writes-sql:' + (viaSql.length === 1 &amp;amp;&amp;amp; Number(viaSql[0].n) === 1 ? 'PASS' : 'FAIL'));&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;A write through the document API, visible to SQL in the same second, because nothing sits between them to lag. How agents should consume enterprise data - memory, retrieval, permissions, audit - is the subject of a later article in this series.&lt;/p&gt;





&lt;h2&gt;Example: one commerce domain across relational, JSON, graph, vector, and spatial data&lt;/h2&gt;

&lt;p&gt;Every article in this series runs against the same small domain in the companion repository: 200 customers, 1,000 orders, 300 support tickets with embeddings, a referral-and-device graph, store locations. It is a deliberately ordinary commerce domain that requires every model &lt;em&gt;naturally&lt;/em&gt;: orders are relational, profiles are documents, fraud rings are graphs, ticket similarity is vectors, store proximity is spatial.&lt;/p&gt;

&lt;p&gt;Here is customer 42 as the document API sees it - a duality view document, abridged from the live container:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;{
  "_id": 42,
  "_metadata": { "etag": "E9BA8572B721D85E653B49930B83D911", "asof": "000000000022B79D" },
  "email": "customer42@example.com",
  "fullName": "Customer 42",
  "segment": "standard",
  "orders": [
    { "orderId": 90, "status": "delivered", "orderTs": "2026-05-22T00:00:00",
      "total": 273.96,
      "items": [ { "line": 1, "productId": 32, "qty": 3, "unitPrice": 273.96 } ] }
  ]
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This document and the &lt;code&gt;customers&lt;/code&gt;, &lt;code&gt;orders&lt;/code&gt;, and &lt;code&gt;order_items&lt;/code&gt; rows beneath it are the same logical data managed by one engine - no second persisted copy, no synchronization pipeline. The etag gives the document world lock-free optimistic concurrency; the rows give the relational world its constraints, its statistics, its joins (See &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/jsnvu/overview-json-relational-duality-views.html" rel="noopener noreferrer"&gt;Oracle JSON-Relational Duality Developer's Guide&lt;/a&gt; (duality views, etags, &lt;code&gt;_id&lt;/code&gt; requirement, documented restrictions); &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/vecse/overview-ai-vector-search.html" rel="noopener noreferrer"&gt;Oracle AI Vector Search overview&lt;/a&gt;)&lt;/p&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%2Fblogs.oracle.com%2Fdevelopers%2Fwp-content%2Fuploads%2Fsites%2F129%2F2026%2F06%2FPicture3-1.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%2F06%2FPicture3-1.png" alt="Diagram illustrating JSON-relational duality. On the left, customer and order information appears as a JSON document exposed through a MongoDB-compatible API. On the right, the same information is represented in relational tables for customers, orders, and order items. Arrows indicate both views reference the same underlying data without duplication or synchronization pipelines, using an etag for optimistic concurrency." width="799" height="388"&gt;&lt;/a&gt;&lt;em&gt;One truth, two shapes: customer 42's duality view document on the left and the customers, orders, and order_items rows it is built from on the right - the same data, one copy, no sync pipeline, with the etag providing lock-free optimistic concurrency.&lt;/em&gt;&lt;p&gt;&lt;/p&gt;

&lt;p&gt;The lab's stated limits, for the record: Oracle AI Database 26ai Free is capped at 2 CPUs, 2 GB of database memory, and 12 GB of user data - sufficient for correctness proofs, deliberately unsuitable for benchmarks, which is why this article contains no performance numbers. The demonstration embeddings are 8-dimensional and deterministic so that CI results are reproducible; engine behavior is dimension-independent, and a real-model flow with in-database ONNX embedding generation is planned for a later module.&lt;/p&gt;





&lt;h2 id="faq"&gt;FAQ&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What is a converged database?&lt;/strong&gt; A single database engine that natively supports relational, document, graph, vector, spatial, and text data under one optimizer, one transaction boundary, one consistency model, and one security domain - exposed through the major access surfaces: SQL, document APIs, and REST. The defining property is not storing many models; it is that the guarantees span them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What's the difference between a converged database and a multi-model database?&lt;/strong&gt; Multi-model means multiple data models can be stored in one product. Converged means the architectural guarantees - transactions, optimization, consistency, governance - apply &lt;em&gt;across&lt;/em&gt; those models. The 2019 ACM survey of multi-model systems reported no evidence of cross-model transaction management in the products it examined (&lt;a href="https://dl.acm.org/doi/10.1145/3323214" rel="noopener noreferrer"&gt;J. Lu and I. Holubová, "Multi-model Databases: A New Journey to Handle the Variety of Data," &lt;em&gt;ACM Computing Surveys&lt;/em&gt; 52(3), Article 55, 2019.)&lt;/a&gt;; that gap is the line. Full comparison: later article in this series.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do converged databases replace vector databases?&lt;/strong&gt; For enterprise RAG and agent workloads, similarity search increasingly belongs beside the data it describes - filtered by live predicates, governed by the database's access controls, updated in the same transaction as the source rows. Specialized vector stores remain a defensible choice for standalone similarity serving without relational context. The architectural comparison appears later in this series.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Isn't "converged database" just an Oracle marketing term?&lt;/strong&gt; The term originated at Oracle in 2020 (See &lt;a href="https://sqlmaria.com/2020/03/05/what-is-a-converged-database/" rel="noopener noreferrer"&gt;M. Colgan, "What is a Converged Database?&lt;/a&gt;," March 2020). The architecture it names has a strong academic argument behind it: Stonebraker and Pavlo (SIGMOD Record, 2024) describe document databases converging with relational systems and vector search as a feature of existing engines &lt;a href="https://db.cs.cmu.edu/papers/2024/whatgoesaround-sigmodrec2024.pdf" rel="noopener noreferrer"&gt;(See the source here&lt;/a&gt;), and the SQL standard itself absorbed JSON (2016, native type 2023) and property graphs (See &lt;a href="https://peter.eisentraut.org/blog/2023/04/04/sql-2023-is-finished-here-is-whats-new." rel="noopener noreferrer"&gt;ISO/IEC 9075:2023, &lt;em&gt;SQL&lt;/em&gt;, including Part 16, &lt;em&gt;Property Graph Queries (SQL/PGQ)&lt;/em&gt;, June 2023; SQL/JSON operators in SQL:2016; native JSON type (T801) in SQL:2023. Summary: P. Eisentraut, "SQL:2023 is finished: Here is what's new."&lt;/a&gt;). The vocabulary is a vendor's; the trajectory is documented in the field's literature.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does convergence make document data modeling obsolete?&lt;/strong&gt; No. Embedding, referencing, bucketing, and computing remain bets on read/write ratios and access patterns - the tradeoff Codd priced in 1970 (See E. F. Codd, "&lt;a href="https://www.seas.upenn.edu/~zives/03f/cis550/codd.pdf" rel="noopener noreferrer"&gt;A Relational Model of Data for Large Shared Data Banks," &lt;/a&gt;CACM 13(6), June 1970, §2.2.1.) did not disappear. What convergence changes is the cost of being wrong: when documents are projections of normalized rows (duality views), changing the access pattern means changing the projection, not migrating the data. The patterns still matter; there are simply more tools now.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why does a converged database matter for RAG?&lt;/strong&gt; RAG systems need retrieved context that is current, permission-aware, and connected to operational facts. A converged database can reduce the synchronization gap between source rows, embeddings, and the queries that use them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is Oracle AI Database in this article?&lt;/strong&gt; Oracle AI Database is the Oracle database platform context used by the proof scripts and examples in this article, including SQL, document/JSON, graph, vector, and related access surfaces.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is vector search in a converged database?&lt;/strong&gt; Vector search ranks rows or documents by embedding similarity while remaining close to relational filters, access controls, transactions, and other database context.&lt;/p&gt;





&lt;h2&gt;Related Oracle resources and next reads&lt;/h2&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;Oracle AI Vector Search overview&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://docs.oracle.com/en/database/oracle/mongodb-api/mgapi/overview-oracle-database-api-mongodb.html" rel="noopener noreferrer"&gt;Oracle Database API for MongoDB overview&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://docs.oracle.com/en/database/oracle/property-graph/" rel="noopener noreferrer"&gt;Oracle Database Property Graph documentation&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/jsnvu/overview-json-relational-duality-views.html" rel="noopener noreferrer"&gt;JSON-Relational Duality Views overview&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/" rel="noopener noreferrer"&gt;Oracle REST Data Services documentation&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/" rel="noopener noreferrer"&gt;Oracle Database SQL Language Reference&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://sqlmaria.com/2020/03/05/what-is-a-converged-database/" rel="noopener noreferrer"&gt;Original converged database definition by Maria Colgan&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;





&lt;h2&gt;How fresh is the methodology and proof?&lt;/h2&gt;

&lt;p&gt;Every code sample in this article is a verbatim excerpt of a script in &lt;a href="https://github.com/oracle-devrel/oracle-umt-developer-hub" rel="noopener noreferrer"&gt;converged-database-lab&lt;/a&gt;, executed by GitHub Actions CI - on every change and on a nightly schedule - against Oracle AI Database 26ai Free (the &lt;code&gt;gvenzl/oracle-free&lt;/code&gt; 23.26.x container line; year.quarter version tags correspond to the 26ai release). Current status: 5 proof scripts, 20 assertions, passing as of June 12, 2026 (including the day's scheduled nightly run). Reproduce it in three commands:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;docker compose up -d --build oracle
pip install -r validator/requirements.txt
python validator/run.py&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id="references"&gt;&lt;/h2&gt;

</description>
      <category>converged</category>
      <category>ai</category>
      <category>vectordatabase</category>
      <category>oracle</category>
    </item>
    <item>
      <title>The Agent Communication Matrix: When MCP, A2A, and Plain REST Each Win</title>
      <dc:creator>Anya Summers</dc:creator>
      <pubDate>Mon, 29 Jun 2026 16:04:28 +0000</pubDate>
      <link>https://dev.to/oracledevs/the-agent-communication-matrix-when-mcp-a2a-and-plain-rest-each-win-4omo</link>
      <guid>https://dev.to/oracledevs/the-agent-communication-matrix-when-mcp-a2a-and-plain-rest-each-win-4omo</guid>
      <description>&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Agent communication has three problems, not just one.&lt;/strong&gt; Tool access, peer coordination, and system integration each need a different solution. Most production failures occur when one protocol tries to cover all three.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MCP and A2A are complements, not rivals.&lt;/strong&gt; The &lt;a href="https://modelcontextprotocol.io/specification" rel="noopener noreferrer"&gt;Model Context Protocol&lt;/a&gt; (Anthropic, 2024) defines how models find and use tools. The &lt;a href="https://google.github.io/A2A/" rel="noopener noreferrer"&gt;Agent-to-Agent (Google, 2025)&lt;/a&gt; explains how agents cooperate. Generally, agents use A2A for coordination and MCP for tool access.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simple infrastructure still works well for many tasks.&lt;/strong&gt; Message queues provide at-least-once delivery, dead-letter queues, and automatic back-pressure. Adding these features to MCP or A2A requires coding idempotency, retry coordination, and ordering manually. When the LLM acts as a worker, use a queue.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Three reference patterns cover most production needs.&lt;/strong&gt; MCP-Centric Tool Access (one orchestrator, multiple tools), A2A Mesh with Oracle Memory (peer agents coordinating via task envelopes), and Queue-Backed Backoffice Agents (RabbitMQ workers writing to Oracle, without agent protocol). Each includes runnable Python in the companion repo.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The protocol layer can change; the memory layer should stay stable.&lt;/strong&gt; The Oracle AI Database remains consistent across all three patterns (vector-indexed, transactional, audit-friendly). This consistency allows the protocol above to evolve while keeping the system of record intact.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The protocol you picked is doing three jobs at once
&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flncpccd66iw3hhemsvax.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flncpccd66iw3hhemsvax.png" alt="Diagram describing three communication patterns for AI agents. Tool Access enables models to call external capabilities such as SQL queries and vector search. Peer Coordination allows agents to hand work to other agents with their own state and lifecycle. System Integration connects agents to enterprise systems such as databases, queues, and audit services." width="800" height="388"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Three communication patterns for AI agents: tool access, peer coordination, and system integration.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Imagine your team created a multi-step research agent. It has three specialist sub-agents: a retriever, a synthesizer, and a reviewer. They connect over plain REST. It worked well in staging. But in production, p99 latency hit 14 seconds at the third hop. Retries piled up. A failed downstream call left the orchestrator with a half-written database row. The rollback logic, designed for a different failure mode, made things worse.&lt;/p&gt;

&lt;p&gt;Then they implemented RabbitMQ. Latency stabilized, and throughput increased. Now, retry issues were someone else’s concern, which was the goal. But two weeks later, the security team filed a ticket. They asked which agent had touched which row during a specific time, and nobody could answer. Request-scoped tracing had disappeared into the queue.&lt;/p&gt;

&lt;p&gt;The LLM-facing tool interface had splintered into six unique queue-message schemas, one for each specialist. None were introspectable by the model. The synthesizer agent started calling tools it didn’t know about and failed silently if they didn’t respond.&lt;/p&gt;

&lt;p&gt;The team hadn’t chosen a bad protocol. They had picked the same protocol for three different jobs, twice in a row.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Agent communication isn’t just one problem. It involves tool access, peer coordination, and system integration. Each area needs its own solution.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tool access&lt;/strong&gt; occurs when a model needs to use a capability it lacks, like a SQL query or memory write. The Model Context Protocol (MCP) addresses this need. It’s often the first task for production agent systems.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Peer coordination&lt;/strong&gt; happens when one agent assigns work to another. This isn’t just a function call; it’s a task with its own state and lifecycle. The second agent may work independently on this task. The Agent-to-Agent protocol (A2A) supports this, solving problems that the tool-call model can’t handle well.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;System integration&lt;/strong&gt; involves agents interacting with your broader infrastructure—databases, queues, services, scheduled jobs, and audit pipelines. For two decades, REST, message queues, and event buses have managed this. Often, the simplest solution is the best.&lt;/p&gt;

&lt;p&gt;This article offers a framework to help you choose the right protocol for your needs. It includes three reference patterns for building with these protocols, each with runnable Python examples using Oracle AI Database. One constant remains true as the protocol layer evolves: the governed memory core.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Agent Communication Matrix
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Key insight:&lt;/strong&gt; Protocols aren’t ranked on a single axis. They differ on five concrete attributes (interaction shape, streaming, reliability semantics, governance surface, and primary job), and the right choice is the one whose attribute profile matches the job.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Protocol&lt;/th&gt;
&lt;th&gt;Primary job&lt;/th&gt;
&lt;th&gt;Interaction shape&lt;/th&gt;
&lt;th&gt;Streaming&lt;/th&gt;
&lt;th&gt;Reliability semantics&lt;/th&gt;
&lt;th&gt;Governance surface&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;MCP&lt;/td&gt;
&lt;td&gt;Expose tools and resources to a model&lt;/td&gt;
&lt;td&gt;Typed request/response: model calls a discoverable tool, server returns structured output&lt;/td&gt;
&lt;td&gt;Native: supports streaming responses and progress notifications&lt;/td&gt;
&lt;td&gt;At-most-once over HTTP/JSON-RPC; retries are the client’s job&lt;/td&gt;
&lt;td&gt;Strong: tools self-describe via JSON Schema; capabilities are discoverable at connection time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A2A&lt;/td&gt;
&lt;td&gt;Coordinate work between peer agents&lt;/td&gt;
&lt;td&gt;Task-oriented: one agent submits a task, another reports state changes (submitted, working, completed, failed)&lt;/td&gt;
&lt;td&gt;Native: status and artifact updates stream as the task progresses&lt;/td&gt;
&lt;td&gt;At-most-once with task-level retry; tasks are addressable and resumable&lt;/td&gt;
&lt;td&gt;Medium: agent cards declare capabilities, but task semantics are author-defined&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;REST&lt;/td&gt;
&lt;td&gt;Service-to-service integration&lt;/td&gt;
&lt;td&gt;Synchronous request/response: caller blocks until server returns&lt;/td&gt;
&lt;td&gt;None native; long-poll or upgrade to SSE/WebSocket if needed&lt;/td&gt;
&lt;td&gt;Best-effort; retry and idempotency are the application’s problem&lt;/td&gt;
&lt;td&gt;Weak by default: OpenAPI helps, but it’s convention, not contract&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Message queue&lt;/td&gt;
&lt;td&gt;Hand work to a worker asynchronously&lt;/td&gt;
&lt;td&gt;Fire-and-forget: producer drops a message, worker consumes when ready&lt;/td&gt;
&lt;td&gt;None: queues deliver discrete messages, not streams&lt;/td&gt;
&lt;td&gt;At-least-once with ack/nack; dead-letter queues catch poison messages&lt;/td&gt;
&lt;td&gt;Medium: per-queue ACLs and DLQs give operational control, but no schema layer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Event bus&lt;/td&gt;
&lt;td&gt;Broadcast facts to many consumers&lt;/td&gt;
&lt;td&gt;Publish-subscribe: one producer, N consumers, decoupled in time&lt;/td&gt;
&lt;td&gt;Stream-native: consumers replay from offsets&lt;/td&gt;
&lt;td&gt;At-least-once, often with ordering guarantees per partition; replayable history&lt;/td&gt;
&lt;td&gt;Medium: topic-level governance, schemas via registry (Avro, Protobuf), but consumer behavior is author-defined&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Caption: The Agent Communication Matrix. Use these five attributes to decide which protocol fits which job. WebSockets, SSE, and gRPC streaming appear in this discussion as transports, not as peers; they carry messages for several of the protocols above.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Three cells in this matrix do most of the real work, and they’re worth examining.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MCP and A2A look similar on the wire but interact differently.&lt;/strong&gt; Both use HTTP, JSON-RPC, and streaming. MCP treats interaction as atomic: the model makes a call, and the server returns a structured response. A2A treats interaction as stateful: an agent submits a task, which follows a lifecycle (submitted, working, completed, failed) that both sides monitor.&lt;/p&gt;

&lt;p&gt;This has clear implications for engineers. If your “agent” acts like a stateless function, MCP is ideal, and A2A adds overhead. If your “agent” has a lifecycle (it can pause, resume, check status, or cancel), A2A provides functionality that MCP lacks. This highlights the real differences between Pattern 1 and Pattern 2.&lt;/p&gt;

&lt;p&gt;The reliability column is the most important trade-off in the matrix. Teams often misjudge it.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3jnlfej33khs3vg7o5pq.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3jnlfej33khs3vg7o5pq.png" alt="Comparison of reliability models. Application-layer protocols such as MCP and REST provide at-most-once delivery and require retry logic in application code. Infrastructure services such as message queues and event buses provide at-least-once delivery with built-in retries, acknowledgments, back-pressure handling, and dead-letter queues. The diagram advises keeping infrastructure reliability concerns in the platform layer." width="800" height="709"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Application versus infrastructure responsibility for reliability, retries, and message delivery.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;At-most-once delivery, common in HTTP protocols like MCP and REST, means that a failed request might have been completed or not. This leaves the client uncertain.&lt;/p&gt;

&lt;p&gt;At-least-once delivery, typical for queues and event buses, ensures a message is processed at least once. However, it might be processed more than once if a worker crashes. Here, idempotency becomes the app’s responsibility.&lt;/p&gt;

&lt;p&gt;Neither approach is better than the other. The key question is where you want retry logic: in your application code (HTTP) or in your infrastructure (queues).&lt;/p&gt;

&lt;p&gt;Pattern 3 suggests that for certain agent tasks, placing retry logic in the infrastructure is better.&lt;/p&gt;

&lt;p&gt;MCP’s “strong” governance and REST’s “weak by default” rating tackle the same issue that created the OpenAPI ecosystem, but they do it differently. MCP servers self-describe when a connection happens. For example, a client requests tools/list and receives a complete schema of capabilities. This includes types, descriptions, and parameter constraints.&lt;/p&gt;

&lt;p&gt;In contrast, REST provides OpenAPI only if the producing team publishes and maintains it, while the consuming team must trust it. That’s three “if”s the agent runtime can’t resolve at runtime. MCP makes discoverability a requirement, not just a convention. This governance model includes auditable tool inventories, type-checked invocations, and capability negotiation for each session. Many underestimate this before adopting it. This is the edge that Pattern 1 uses.&lt;/p&gt;

&lt;p&gt;The matrix doesn’t choose a protocol for you. It shows what you’re trading.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6jh165h9dp4yqgad1kd5.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6jh165h9dp4yqgad1kd5.png" alt="Decision tree for selecting an agent communication pattern. If a workload does not require real-time or human-in-the-loop interaction, use message queues. If it does, determine whether multiple agents coordinate shared state. Coordinated agents use an A2A mesh, while a single orchestrator uses MCP. A note recommends starting with MCP unless peer coordination is required." width="800" height="844"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Decision tree for choosing between MCP, A2A, and message-queue architectures.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Pattern 1: MCP-Centric Tool Access
&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fojiz8ay5tol33ercotmh.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fojiz8ay5tol33ercotmh.png" alt="Diagram showing a single-agent architecture. An Orchestrator LLM accesses Oracle AI Database through four tools: vector search, SQL queries, memory reads, and memory writes. Oracle AI Database serves as the shared backend for vector-indexed, transactional, and audited data access." width="800" height="342"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Single-agent orchestration pattern using Oracle AI Database for retrieval, transactions, and memory.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;Spec: *&lt;/em&gt;&lt;a href="https://modelcontextprotocol.io/specification" rel="noopener noreferrer"&gt;Model Context Protocol specification, Anthropic (2024)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is the pattern for production agent systems: a single LLM orchestrator, one or more MCP servers, and a typed contract between them. The model doesn’t call your database directly. Instead, it uses a tool that interacts with it, and this difference is important.&lt;/p&gt;

&lt;p&gt;MCP excels at the discovery layer. When an MCP client connects to a server, it sends a tools/list request. In return, it receives the full schema of available capabilities: names, parameters, descriptions, and return types. The model sees this inventory before acting. Selecting tools becomes a reasoning step rather than a hardcoded choice. This is a significant change. A model with discoverable tools handles “I don’t know how to do that” better than one with fixed function calls. The lack of a tool provides useful information for the model to reason about.&lt;/p&gt;

&lt;p&gt;Oracle AI Database serves well as the MCP server. The capabilities you want to expose to an agent—like vector search over embedded content, parameterized SQL against business tables, and structured memory reads and writes—fit perfectly with MCP’s tool model. A typical Oracle-backed MCP server offers four or five tools: vector_search, run_sql, read_memory, write_memory, and summarize_thread. Each is a small, focused function with a typed schema. The model chooses among them based on the task.&lt;/p&gt;

&lt;p&gt;The code below shows the minimal version: an MCP server registering one tool that performs vector search against Oracle AI Database. Note the typed array.array("f", ... ) bind for the vector column; a plain Python list will not work. The full server, with authentication, retries, and the other four tools, is in the &lt;a href="https://github.com/JeremyMorgan/oracle-ai-developer-hub/tree/adding-agent-communication-matrix" rel="noopener noreferrer"&gt;companion repo&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import array

from mcp.server import Server

from mcp.types import Tool, TextContent

import oracledb, os

server = Server("oracle-tools")

pool = oracledb.create_pool(user=os.environ["DB_USER"],

                            password=os.environ["DB_PASS"],

                            dsn=os.environ["DB_DSN"], min=1, max=4)

@server.list_tools()

async def list_tools() -&amp;gt; list[Tool]:

    return [Tool(

        name="vector_search",

        description="Semantic search over the knowledge base. Returns top-k passages.",

        inputSchema={

            "type": "object",

            "properties": {"query": {"type": "string"}, "k": {"type": "integer", "default": 5}},

            "required": ["query"],

        },

    )]

@server.call_tool()

async def call_tool(name: str, arguments: dict) -&amp;gt; list[TextContent]:

    # Embed the query, then run an Oracle AI Vector Search against the indexed corpus.

    vec = array.array("f", await embed(arguments["query"]))

    with pool.acquire() as conn, conn.cursor() as cur:

        cur.execute("""

            SELECT chunk_text FROM kb_chunks

            ORDER BY VECTOR_DISTANCE(embedding, :q, COSINE)

            FETCH FIRST :k ROWS ONLY

        """, q=vec, k=arguments.get("k", 5))

        return [TextContent(type="text", text=row[0]) for row in cur]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here are a few key points about what’s happening here. The tool schema acts as a contract. The model views vector_search as a typed capability. It requires a query string and allows an optional integer k. This info helps the model decide when and how to use it.&lt;/p&gt;

&lt;p&gt;The Oracle AI Database vector search executes as a single SQL statement on a VECTOR column. It uses a cosine-distance HNSW index. There’s no separate vector store, no sync job, and no eventual-consistency window.&lt;/p&gt;

&lt;p&gt;The embed() call is left out here for clarity. In the repo, it connects to a local Ollama model. (This setup allows the demo to run without needing paid API keys).&lt;/p&gt;

&lt;p&gt;In tests with the demo corpus (1,000 chunks using 768-dimensional embeddings via Ollama’s nomic-embed-text on a GPU workstation), median tool-call round-trip latency is 15.3ms. This includes 14.1ms for embedding inference and 0.9ms for Oracle vector search. On CPU-only hardware, the embedding step is usually 5 to 20 times slower, but the database side remains constant. Remember this key point: an &lt;strong&gt;MCP tool call to an Oracle-backed server is fast like a database, not like an LLM&lt;/strong&gt;. The latency during an agent turn mainly relies on the model’s own inference, not the tool.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Recommended usage:&lt;/strong&gt; Use one orchestrator agent with various specialized tools. Teams should standardize access across different agent frameworks or model providers. This setup works best when the typed schema improves model behavior, which is often true. MCP is ideal if you plan to add tools later. The discovery layer allows new capabilities to integrate with the model without changes on the client side.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When not to use this:&lt;/strong&gt; Avoid having a single agent for just one tool. Don’t add a protocol if a function call is enough. If your “agent” is simply one model with a clear capability, an MCP server adds extra complexity. The discovery layer is helpful when there’s something to find; with only one tool, there’s nothing to discover.&lt;/p&gt;




&lt;h2&gt;
  
  
  Pattern 2: A2A Mesh with Oracle Memory
&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyj23gtzjsnys91iznvv9.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyj23gtzjsnys91iznvv9.png" alt="Diagram showing a multi-agent workflow with Researcher, Writer, and Reviewer agents. The Researcher gathers sources, the Writer drafts content, and the Reviewer validates and revises it. Agent state and payloads are stored in Oracle AI Database, which provides vector-indexed, transactional, and audited persistence across the workflow." width="800" height="339"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Multi-agent collaboration pattern with shared state stored in Oracle AI Database.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Spec:&lt;/strong&gt; &lt;a href="https://google.github.io/A2A/" rel="noopener noreferrer"&gt;Agent-to-Agent Protocol specification, Google (2025)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A2A solves a problem MCP doesn’t: what happens when an agent isn’t calling a tool but handing work to another agent. The distinction sounds semantic until you try to express “&lt;em&gt;the Researcher has finished gathering sources; the Writer should now draft a response using them&lt;/em&gt;” as a tool call. It doesn’t fit. The Writer isn’t a function the Researcher invokes. It’s a peer with its own model, its own prompt, its own lifecycle. A2A models that relationship as a task with state, addressable identity, and a status machine that both sides observe.&lt;/p&gt;

&lt;p&gt;Consider a two-agent research workflow. A Researcher agent gathers context from external sources, checks relevance, and produces findings. A Writer agent then uses those findings to draft a response in the desired tone and format. A simple setup would have the Researcher return findings directly as a response to a tool call. This works for two agents but fails with three. When a third agent, like a Reviewer, needs the same findings, you end up duplicating data in message history instead of having a central record.&lt;/p&gt;

&lt;p&gt;The A2A pattern changes this. Findings are stored in the Oracle AI Database as durable, vector-indexed rows. The Researcher writes them and sends a task message to the Writer with a reference to the findings, not the data itself. The Writer reads from the same table. This protocol ensures coordination, while the database holds the state. Multiple agents can access the same information without the protocol layer needing to see the contents.&lt;/p&gt;

&lt;p&gt;The code below is one half of the mesh: the Writer agent’s task handler, listening for task.created events from the Researcher and producing a draft. The Researcher side, plus the full A2A envelope with retries and status updates, lives in the &lt;a href="https://github.com/JeremyMorgan/oracle-ai-developer-hub/tree/adding-agent-communication-matrix" rel="noopener noreferrer"&gt;companion repo&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import asyncio, oracledb, os

from a2a.server import A2AServer

from a2a.types import Task, TaskStatus, Message

pool = oracledb.create_pool(user=os.environ["DB_USER"],

                            password=os.environ["DB_PASS"],

                            dsn=os.environ["DB_DSN"], min=1, max=4)

writer = A2AServer(agent_id="writer-v1")

@writer.on_task("draft_response")

async def handle_draft(task: Task) -&amp;gt; Message:

    # The Researcher passed a memory_id, not the findings themselves.

    memory_id = task.payload["memory_id"]

    with pool.acquire() as conn, conn.cursor() as cur:

        cur.execute("SELECT findings, source_refs FROM agent_memory WHERE id = :id",

                    id=memory_id)

        findings, sources = cur.fetchone()

    await writer.update_status(task.id, TaskStatus.WORKING)

    draft = await llm_draft(findings, sources, tone=task.payload["tone"])

    cur.execute("UPDATE agent_memory SET draft = :d WHERE id = :id", d=draft, id=memory_id)

    return Message(role="agent", content=draft, refs={"memory_id": memory_id})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few things in that snippet do the load-bearing work. The Writer never receives the findings in the message. It receives a memory_id and reads the actual content from Oracle. That’s the payload-by-reference pattern, and it’s the core architectural move of this pattern. The update_status call tells the Researcher (and any observer subscribed to the task) that work has begun; A2A’s status machine handles the streaming update without the Writer having to manage its own connection lifecycle. The final Message returns the draft inline because it’s the artifact of the task, but it also includes the memory_id ref, so a third agent picking this up next reads the same memory rather than re-deserializing a payload.&lt;/p&gt;

&lt;p&gt;The trade-off is clear in token counts. In the demo, using serialized findings in the message costs 1,394 tokens per Writer turn for 3KB of research. This size is typical for a research agent creating a synthesized summary with source references.&lt;/p&gt;

&lt;p&gt;In contrast, the payload-by-reference version only costs 61 tokens, no matter the findings’ size. This means a 22.9 times reduction at 3KB. The difference grows with findings size: at 500 characters, the reduction is 5.6 times; at 8KB, it reaches 58.9 times. The ratio isn’t fixed; it depends on how much data is in the message versus in the database. (Tokens counted using OpenAI’s cl100k_base tokenizer; Anthropic and Google tokenizers yield similar counts for English text.)&lt;/p&gt;

&lt;p&gt;The compounding effect is more important than any single hop. A three-agent mesh sharing the same research context across two handoffs costs about 4,000 tokens in the naive version. In the payload-by-reference version, it costs only 183 tokens. At five hops, the difference exceeds 6,500 tokens per request. This is before any agent has done actual reasoning work. The cost of “just put it in the message” increases linearly with mesh depth. Most mesh topologies grow over time, not the other way around.&lt;/p&gt;

&lt;p&gt;Oracle AI Database plays a key role here. The agent_memory table serves as a single source of truth. It is vector-indexed for semantic recall and transactional for consistency between reads and writes. Each row includes the agent ID, making it audit-friendly. The protocol layer can be A2A today and something else tomorrow. However, the memory layer stays the same.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Recommended usage:&lt;/strong&gt; Use multi-agent workflows that need peer coordination, like planner-and-specialist patterns or multi-step research pipelines. This applies when multiple agents require consistent access to the same conversational or task state. A2A is ideal for long-running tasks where a synchronous request/response model would keep connections open unnecessarily.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When not to use this:&lt;/strong&gt; Avoid A2A for one agent with a set of tools. This is an MCP problem, not an A2A issue. Using A2A with a single orchestrator adds unnecessary task lifecycle management. A good test: if you can name a second agent and explain its independent decisions, A2A works. If the “second agent” is just a different prompt using the same model, it’s a tool call.&lt;/p&gt;




&lt;h2&gt;
  
  
  Pattern 3: Queue-Backed Backoffice Agents
&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flkeyszc3rfr3vcvteexo.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flkeyszc3rfr3vcvteexo.png" alt="Diagram showing an asynchronous processing pattern. A producer publishes messages to a RabbitMQ exchange, which delivers work to an LLM worker. Failed messages are routed to a dead-letter exchange for inspection. The worker writes processed results into Oracle AI Database and uses queue features such as acknowledgments, retries, and back-pressure management." width="800" height="745"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Message-driven AI workflow using RabbitMQ and Oracle AI Database.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Imagine a document-processing pipeline. PDFs arrive in a queue. A worker agent picks them up, extracts text, embeds chunks, and writes them to the Oracle AI Database with vector indexing. It then shows results through a simple FastAPI endpoint. No MCP. No A2A. This is key. Adding either would increase the surface area without enhancing capability.&lt;/p&gt;

&lt;p&gt;This pattern resists the pull of new protocols. The urge to add MCP just because there’s an LLM involved somewhere is strong but should be resisted. A worker using the same embedding model and prompt for each message doesn’t need tool discovery. It needs at-least-once delivery, a dead-letter queue, and back-pressure for when the embedding service slows down. These are queue issues, not agent-protocol issues.&lt;/p&gt;

&lt;p&gt;The architectural shape predates the agent era, which is key to its function. Producers send messages to a queue. Workers process them at their own pace. If messages fail, they retry with exponential backoff and go to a dead-letter queue if they keep failing. The LLM acts as a worker in the pipeline, not as its orchestrator. This means the protocol layer above the LLM is as straightforward as the rest of the system, and that’s a benefit.&lt;/p&gt;

&lt;p&gt;The code below is the worker’s core loop: consume a message, embed the chunks, write to Oracle AI Database, acknowledge. Producer, dead-letter handling, and the FastAPI edge live in the &lt;a href="https://github.com/JeremyMorgan/oracle-ai-developer-hub/tree/adding-agent-communication-matrix" rel="noopener noreferrer"&gt;companion repo&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import array, json, oracledb, pika, os

from embed import embed_chunks  # local Ollama call, see repo

pool = oracledb.create_pool(user=os.environ["DB_USER"],

       password=os.environ["DB_PASS"],
       dsn=os.environ["DB_DSN"], min=1, max=4)

conn = pika.BlockingConnection(pika.URLParameters(os.environ["AMQP_URL"]))

ch = conn.channel()

ch.queue_declare(queue="documents", durable=True,

                 arguments={"x-dead-letter-exchange": "documents.dlx"})

ch.basic_qos(prefetch_count=4)  # back-pressure: at most 4 in-flight per worker

def handle(ch, method, _props, body):

    doc = json.loads(body)

    chunks = doc["chunks"]                          # already segmented upstream

    vectors = [array.array("f", v) for v in embed_chunks(chunks)]

    with pool.acquire() as db, db.cursor() as cur:

        cur.executemany("""

            INSERT INTO kb_chunks (doc_id, chunk_text, embedding)

            VALUES (:doc, :txt, :vec)

        """, [(doc["id"], t, v) for t, v in zip(chunks, vectors)])

        db.commit()

    ch.basic_ack(delivery_tag=method.delivery_tag)

ch.basic_consume(queue="documents", on_message_callback=handle)

ch.start_consuming()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The interesting parts are what isn’t there. There’s no tool schema, no agent identity, and no status state machine. The worker doesn’t need to show its capabilities because nothing is looking for it. The contract is the queue’s message schema, enforced by the producer’s chosen validation. The prefetch_count=4 setting tells the whole back-pressure story. If the embedding service slows down or Oracle’s connection pool fills up, messages stay on the queue instead of piling up in worker memory. The DLX (dead-letter exchange) on the queue means any message that fails repeatedly goes to a place where a human can check it, without the producer or any other agent needing to know.&lt;/p&gt;

&lt;p&gt;Reliability semantics play a crucial role here. RabbitMQ provides at-least-once delivery with ack/nack semantics. This means if a worker crashes during processing, the message is sent to another worker. You don’t need application-level retry logic. In contrast, achieving the same reliability with an MCP server involves manually writing idempotency keys, retry coordination, and ordering logic. The queue handles this for you, and “for free.” RabbitMQ has been improving these semantics since 2007. You’re not going to outdo that on a side project.&lt;/p&gt;

&lt;p&gt;The Oracle integration mirrors Patterns 1 and 2: it’s durable, vector-indexed, and transactional. The worker writes embedded chunks into the same kb_chunks table that Pattern 1’s MCP vector_search tool reads from. Teams using Oracle Database can merge the queue and memory layer into one component with Oracle Advanced Queuing. The trade-off is one less service to manage, but with slightly less portable demo code. This is the architectural benefit of the three-pattern arc: while the protocol layer changes (MCP, A2A, none), the memory layer remains constant. A document processed by Pattern 3’s queue worker is instantly searchable by Pattern 1’s MCP tool and can be referenced by Pattern 2’s A2A peers. This isn’t a coincidence; it reflects the efficiency gained when each protocol performs its best role, with Oracle AI Database maintaining the shared state for all three patterns.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Recommended usage:&lt;/strong&gt; Use this for asynchronous, idempotent, and throughput-sensitive tasks. Examples include document processing, batch embedding, ETL pipelines, scheduled report generation, and back-office automation. Here, the LLM acts as a worker, not an orchestrator. This pattern is ideal when you need to manage slow or temporarily down downstream services. Queues can handle those issues without the application noticing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When not to use this:&lt;/strong&gt; Avoid it for real-time, conversational, or human-in-the-loop tasks. Don’t place a queue between a user and a chatbot. It’s not suitable when latency is more critical than throughput, especially when users expect quick answers. The conversational loop fits in Patterns 1 or 2, while Pattern 3 works behind them, tackling non-interactive tasks.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Enterprise Reality
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Cost scales with protocol surface area.&lt;/strong&gt; Adding each protocol to an agent system creates another layer. This layer must be monitored, secured, and fixed if something goes wrong at 3 a.m. The patterns above outline the architecture, while what follows shows how that architecture shifts when faced with real users at scale.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Auditability across async boundaries.&lt;/strong&gt; When a request crosses from MCP to a queue to A2A, the question regulators and security teams actually ask is &lt;em&gt;which agent touched which row, and when?&lt;/em&gt; The answer almost never lives in any single protocol. According to &lt;a href="https://www.langchain.com/state-of-agent-engineering" rel="noopener noreferrer"&gt;LangChain’s State of Agent Engineering 2026 report&lt;/a&gt;, 89% of organizations have implemented some form of agent observability, and among teams already running agents in production that figure rises to 94%, with 71.5% reporting full tracing across individual agent steps and tool calls.&lt;/p&gt;

&lt;p&gt;The teams ahead of the curve are not the ones with the most sophisticated protocols; they are the ones who decided early that the system of record sits in the database, not in protocol message history. Oracle AI Database earns its place here as that record. Every memory write, every tool invocation, every agent identity is durable in a single governed store that does not care which protocol delivered the message.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cost scales with iteration, not just calls.&lt;/strong&gt; The LangChain report shows that 32% of respondents see quality as their main blocker, while latency follows at 20%. Interestingly, cost concerns have dropped over the year. Teams aren’t just paying for tokens; they’re paying for hops. Each protocol boundary adds latency, retries, and overhead. A multi-agent system crossing four boundaries per request multiplies the engineering effort. The key lesson from Pattern 3 is clear: Avoid adding a coordination protocol when the work is async, idempotent, and doesn’t need a model in the orchestration loop.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Multi-tenancy and Isolation.&lt;/strong&gt; A report shows that among enterprises with 2,000 or more employees, security is now the second-largest barrier to production, noted by 24.9% of respondents. This is more significant than latency, and it affects protocol choice. MCP servers can be deployed for each tenant or shared with tenant-scoped tools. A2A meshes follow the trust boundaries of their network. Queues can isolate by virtual host or topic. None of these options are wrong, but they differ. A tenancy model that works for one protocol often doesn’t fit all three. The constant factor is the database tenancy model. Row-level security, schema-per-tenant, and Oracle’s audit infrastructure remain relevant, regardless of which protocol is in vogue for the next roadmap.&lt;/p&gt;

&lt;p&gt;The protocol layer can change. The governed memory layer should not.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where This Is Heading
&lt;/h2&gt;

&lt;p&gt;Three things are visibly changing in the agent communication layer right now, and one of them is not yet resolved.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Protocols are converging on capability cards.&lt;/strong&gt; Both MCP’s tool schemas and A2A’s agent cards share a key idea: discoverable, typed descriptions of capabilities. These can be fetched at connection time instead of being hard-coded in client code. The two specifications came to this idea independently, suggesting it’s a fundamental concept. In the next two years, we can expect to see shared schema conventions across protocols. This may include cross-walks between MCP’s tools/list and A2A’s agent cards, or even a new specification that combines both. Teams using either spec now are not going against this convergence; they are ready for it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Database-resident memory is becoming the default.&lt;/strong&gt; In this article’s three patterns, the key constant is the memory layer, not the protocol. We see vector-typed columns, consistent transactions between agent writes and reads, and audit trails that endure even after framework updates. This marks a significant shift from the architecture of 2023 and early 2024. Back then, vector stores operated as separate sidecars, while agent memory was just a Python dictionary. Oracle AI Database showcases this trend. The larger pattern shows that durable agent state should exist in the same governed system that manages your data, not in a separate stack needing constant syncing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The tool/agent boundary is dissolving, and the taxonomy in this article will eventually need to be rewritten.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;An MCP server can wrap an LLM-powered backend, in which case calling it is functionally an agent invocation. An A2A agent can expose itself as an MCP tool, in which case it is being addressed as a capability rather than a peer. Both moves are legitimate, both are happening in production today, and the protocols themselves do not yet have an opinion on which framing is correct.&lt;/p&gt;

&lt;p&gt;This is the open question. The matrix in this article tells you what each protocol is good at &lt;em&gt;today&lt;/em&gt;, and the three patterns work today. But the line between &lt;em&gt;here is a tool, call it and here is a peer, coordinate with it&lt;/em&gt; is genuinely blurring, and I do not think the industry has agreed yet on where it settles. The people I trust most on this question are the ones building both patterns in production and treating the distinction as an engineering choice rather than a protocol mandate. That is the right posture for the next eighteen months. The taxonomy will catch up to the practice, or it will not, and the architectural decisions you make this quarter should be robust to either outcome.&lt;/p&gt;




&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Should I pick MCP or A2A for my first agent project?&lt;/strong&gt; Almost certainly MCP. A2A addresses peer coordination, which many initial projects lack. Start with one model and a set of tools. Introduce A2A when you have a second agent that needs to work with the first on a task that lasts beyond a single request. Using A2A too early creates extra coordination without a clear need.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do I need both MCP and A2A in the same system?&lt;/strong&gt; Yes, often. The typical production shape is A2A between agents and MCP from each agent to its tools. This is because the two protocols operate at different layers and handle different tasks. A system requires both when it has real peer coordination and actual tool access. If you don’t have one of these, you don’t need the related protocol yet.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I migrate from REST to MCP without rewriting my services?&lt;/strong&gt; Yes, that’s usually the cleanest adoption path. An MCP server acts as a thin wrapper over existing REST endpoints. It adds a typed tool schema and a discovery layer without altering your service code. The migration cost lies in the wrapper, not in the services. The services continue to serve their non-agent clients as before.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does Oracle AI Database require Oracle-specific tooling for any of these patterns?&lt;/strong&gt; No. All three patterns in this article use standard open-source Python libraries (oracledb, the mcp SDK, pika for RabbitMQ, FastAPI). Oracle AI Database participates through a connection string and a vector-typed column, not through a framework lock-in. Teams already running Oracle gain the option of collapsing the queue and the memory layer into a single component using Oracle Advanced Queuing, but it is an option, not a requirement.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is the cheapest way to try this end-to-end?&lt;/strong&gt; The companion repository ships a docker-compose.yml that stands up Oracle AI Database Free, RabbitMQ, and Ollama for local model inference. No paid API keys, no cloud accounts, no proprietary SDKs. The entire three-pattern demo runs on a developer laptop with roughly 16GB of RAM, and the Oracle AI Database Free edition supports up: 2 CPUs for foreground processes, 2GB of RAM (SGA and PGA combined), 12GB of user data on disk (irrespective of compression factor)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When is “just use a queue” the right answer?&lt;/strong&gt; When the work is asynchronous, idempotent, and sensitive to throughput, the LLM acts as a worker, not an orchestrator. This applies to most backoffice tasks, like automation, batch embedding, document processing, and scheduled reporting. The key test is if a human needs the result in real time. If not, a queue is usually the best choice. MCP or REST should only be used at the edges, where the system interacts with a human or a synchronous external service.&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>ai</category>
      <category>agents</category>
      <category>a2a</category>
    </item>
    <item>
      <title>An Agent Skill that uses Kafka Java APIs for Oracle AI Database</title>
      <dc:creator>Anya Summers</dc:creator>
      <pubDate>Mon, 29 Jun 2026 15:36:49 +0000</pubDate>
      <link>https://dev.to/oracledevs/an-agent-skill-that-uses-kafka-java-apis-for-oracle-ai-database-47j3</link>
      <guid>https://dev.to/oracledevs/an-agent-skill-that-uses-kafka-java-apis-for-oracle-ai-database-47j3</guid>
      <description>&lt;p&gt;&lt;strong&gt;Use skills to build OKafka apps with Oracle AI Database&lt;/strong&gt;&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;OKafka is a Kafka Java API for Oracle AI Database Transactional Event Queues. OKafka implements standard Kafka Java interfaces to create topics, produce, and consume messages directly in the database.&lt;/li&gt;
&lt;li&gt;This agent skill helps you write Kafka Java for Oracle AI Database Transactional Event Queues’ &lt;a href="https://github.com/oracle/okafka" rel="noopener noreferrer"&gt;OKafka library&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The skill encodes Oracle-specific additions to the Kafka Java API: authentication, using transactions, serialization, and database-specific topic behavior. &lt;/li&gt;
&lt;li&gt;Good agent skills raises the team baseline: better first pass code, fewer manual corrections, and improved integrations with Oracle AI Database.&lt;/li&gt;
&lt;/ul&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh936arajxhmzntp7mh6j.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh936arajxhmzntp7mh6j.png" alt="Diagram showing how hand-written examples feed an agent skill containing OKafka administration, transaction handling, database connections, and Testcontainers patterns. The skill generates an OKafka application and tests. Review effort shifts from setup corrections to validating transaction behavior, commit paths, rollback handling, and runnable proofs." width="800" height="785"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Skill-driven generation of OKafka applications with validated transaction patterns.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In my own work, I found most coding agents weren’t generating high-quality code for &lt;a href="https://andersswanson.dev/2025/07/09/authenticate-to-your-oracle-database-like-its-a-kafka-cluster/" rel="noopener noreferrer"&gt;Oracle AI Database’s Kafka Java API&lt;/a&gt; (OKafka). You can get results, but they’re not idiomatic, and miss subtleties. This is why I created the &lt;a href="https://github.com/anders-swanson/oracle-database-code-samples/tree/main/skills/okafka-java-code" rel="noopener noreferrer"&gt;okafka-java-code oracle agent skill&lt;/a&gt;, based off my hand-written &lt;a href="https://anders-swanson.github.io/oracle-database-code-samples/features/kafka/" rel="noopener noreferrer"&gt;Kafka Java API examples&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Agent skills can greatly enhance code generation for Oracle AI Database apps, and this skill encapsulates solutions to the problems I kept hand-coding: how to authenticate with OKafka, how to use transactions, how to create topics, and how to use Oracle-specific serialization.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;To install the skill, point your agents at this GitHub link:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://github.com/anders-swanson/oracle-database-code-samples/tree/main/skills/okafka-java-code
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  What’s in &lt;a href="https://github.com/anders-swanson/oracle-database-code-samples/tree/main/skills/okafka-java-code" rel="noopener noreferrer"&gt;the skill&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;This is a standard agent skill, with markdown references to code snippets &lt;a href="https://anders-swanson.github.io/oracle-database-code-samples/features/kafka/" rel="noopener noreferrer"&gt;implemented by my samples&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;skills/okafka-java-code
├── agent-skill-okafka-java-api.md
├── agents
│   └── openai.yaml
├── references
│   ├── authentication-and-properties.md
│   ├── dependencies.md
│   ├── oson-serialization.md
│   ├── producer-consumer.md
│   ├── testing-and-troubleshooting.md
│   ├── topics-and-admin.md
│   └── transactions.md
└── SKILL.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each reference markdown file covers specific areas of OKafka Java code: initializing OKafka classes, serialization, authentication, testing, and transactional workloads.&lt;/p&gt;




&lt;h2&gt;
  
  
  Let’s try using the skill to generate an app
&lt;/h2&gt;

&lt;p&gt;Start by installing the &lt;a href="https://github.com/anders-swanson/oracle-database-code-samples/blob/main/skills/okafka-java-code/SKILL.md" rel="noopener noreferrer"&gt;OKafka Java Code skill&lt;/a&gt; and see what you can generate.&lt;/p&gt;

&lt;p&gt;I used the Oracle agent skill to &lt;a href="https://github.com/anders-swanson/generated-okafka-app" rel="noopener noreferrer"&gt;generate an app&lt;/a&gt; with a transactional producer and consumer, and a Testcontainers test. The app was generated in one shot with Codex and GPT 5.5-high and is &lt;strong&gt;almost identical to code I’d write myself&lt;/strong&gt;. Transactional workflows are handled by calling getDBConnection on the producer and consumer, producing and consuming messages in the &lt;strong&gt;same database transaction&lt;/strong&gt; as insert and updates.&lt;/p&gt;

&lt;p&gt;The generated app creates a transactional event flow around Oracle AI Database Transactional Event Queues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/anders-swanson/generated-okafka-app/blob/main/src/main/java/com/example/okafka/transactions/TopicAdmin.java" rel="noopener noreferrer"&gt;TopicAdmin&lt;/a&gt; creates the topic through Kafka Admin with OKafka’s AdminClient.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/anders-swanson/generated-okafka-app/blob/main/src/main/java/com/example/okafka/transactions/OkafkaProperties.java" rel="noopener noreferrer"&gt;OkafkaProperties&lt;/a&gt; builds base properties and adds producer or consumer settings in separate methods.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/anders-swanson/generated-okafka-app/blob/main/src/main/java/com/example/okafka/transactions/TransactionalEventProducer.java" rel="noopener noreferrer"&gt;TransactionalEventProducer&lt;/a&gt; sends a record and writes to produced_events through producer.getDBConnection() before commitTransaction().&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/anders-swanson/generated-okafka-app/blob/main/src/main/java/com/example/okafka/transactions/TransactionalEventConsumer.java" rel="noopener noreferrer"&gt;TransactionalEventConsumer&lt;/a&gt; writes consumed records through consumer.getDBConnection() and calls commitSync() only after the database work succeeds.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/anders-swanson/generated-okafka-app/blob/main/src/test/java/com/example/okafka/transactions/TransactionalEventsIT.java" rel="noopener noreferrer"&gt;TransactionalEventsIT&lt;/a&gt; starts an Oracle AI Database Free container with Testcontainers, applies the OKafka grants, creates a topic, and verifies producer commit, producer abort, and consumer rollback behavior.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This producer method is the kind of output I wanted to nudge agent stoward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private void publish(BusinessEvent event, boolean failAfterDatabaseWrite) throws Exception {
    producer.beginTransaction();
    try {
        producer.send(new ProducerRecord&amp;lt;&amp;gt;(topic, event.id(), event.payload())).get();
        insertProducedEvent(producer.getDBConnection(), event);
        if (failAfterDatabaseWrite) {
            throw new IllegalStateException("Simulated failure before producer commit");
        }
        producer.commitTransaction();
    } catch (InterruptedException exception) {
        Thread.currentThread().interrupt();
        abortAndRethrow(exception);
    } catch (Exception exception) {
        abortAndRethrow(exception);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see the transaction boundary, the Kafka send, the database write, and the abort path in one place.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Femv50qh579kwtkr889y8.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Femv50qh579kwtkr889y8.png" alt="Diagram showing a transactional OKafka workflow. A producer begins a transaction, sends a Kafka record, inserts database rows, and either commits or aborts. A consumer processes records, applies side effects, commits offsets, and rolls back on failure. Kafka records and SQL state share the same Oracle Database transaction boundary." width="800" height="1244"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Transactional OKafka pattern coordinating Kafka messages and database changes.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The consumer side follows the same idea:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private void persistAndCommit(ConsumerRecords&amp;lt;String, String&amp;gt; records, boolean failAfterDatabaseWrite)
        throws Exception {
    Connection connection = consumer.getDBConnection();
    try {
        for (ConsumerRecord&amp;lt;String, String&amp;gt; record : records) {
            insertConsumedEvent(connection, record);
        }
        if (failAfterDatabaseWrite) {
            throw new IllegalStateException("Simulated failure before consumer commit");
        }
        consumer.commitSync();
    } catch (Exception exception) {
        connection.rollback();
        throw exception;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The generated code preserves the important bits: database work happens on the consumer’s OKafka connection, and the offset is committed only after that work succeeds.&lt;/p&gt;




&lt;h2&gt;
  
  
  Testing is part of the skill
&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flg9y0bt74nd4psu143wk.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flg9y0bt74nd4psu143wk.png" alt="Diagram titled “The test is the claim.” A runnable OKafka demo uses Testcontainers to provision Oracle AI Database Free, bootstraps grants and configuration, and creates a Kafka topic. Three test outcomes are validated: successful commit with visible rows and records, producer abort with no persisted data, and consumer rollback where messages remain available for retry." width="800" height="542"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Runnable OKafka test topology validating commit, abort, and rollback behavior.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This skill includes guidance to validate with an integration test or smoke path that creates the topic, produces records, consumes records, and queries the TxEventQ backing table or related database side effect.&lt;/p&gt;

&lt;p&gt;The generated app follows that direction. Its &lt;a href="https://github.com/anders-swanson/generated-okafka-app/blob/main/src/test/java/com/example/okafka/transactions/TransactionalEventsIT.java" rel="noopener noreferrer"&gt;integration test&lt;/a&gt; starts gvenzl/oracle-free:23.26.2-slim-faststart, writes an ojdbc.properties file for local PLAINTEXT OKafka access, and then checks three paths:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a committed producer transaction creates the database row and can be consumed;&lt;/li&gt;
&lt;li&gt;an aborted producer transaction leaves no produced row and no consumable record;&lt;/li&gt;
&lt;li&gt;a failed consumer batch rolls back the database write and leaves the record available for a later successful consume.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can run the generated app tests with &lt;strong&gt;mvn verify&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The test includes grants and setup for the local container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;alter session set container=freepdb1;

grant aq_user_role to TESTUSER;
grant execute on dbms_aq to TESTUSER;
grant execute on dbms_aqadm to TESTUSER;
grant select on gv_$session to TESTUSER;
grant select on v_$session to TESTUSER;
grant select on gv_$instance to TESTUSER;
grant select on gv_$listener_network to TESTUSER;
grant select on SYS.DBA_RSRC_PLAN_DIRECTIVES to TESTUSER;
grant select on gv_$pdbs to TESTUSER;
grant select on user_queue_partition_assignment_table to TESTUSER;
exec dbms_aqadm.GRANT_PRIV_FOR_RM_PLAN('TESTUSER');
commit;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is loaded and run on the local container at test startup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Container
private static final OracleContainer ORACLE = new OracleContainer(ORACLE_IMAGE)
        .withStartupTimeout(Duration.ofMinutes(4))
        .withUsername(TEST_USER)
        .withPassword(TEST_PASSWORD);

private static OracleDataSource dataSource;
private static Path okafkaConfigDirectory;

@BeforeAll
static void configureDatabase() throws Exception {
    ORACLE.copyFileToContainer(MountableFile.forClasspathResource("okafka.sql"), "/tmp/okafka.sql");
    org.testcontainers.containers.Container.ExecResult result =
            ORACLE.execInContainer("sqlplus", "sys / as sysdba", "@/tmp/okafka.sql");
    if (result.getExitCode() != 0) {
        throw new IllegalStateException("Unable to apply OKafka grants: " + result.getStderr());
    }

    dataSource = new OracleDataSource();
    dataSource.setURL(ORACLE.getJdbcUrl());
    dataSource.setUser(TEST_USER);
    dataSource.setPassword(TEST_PASSWORD);

    okafkaConfigDirectory = Files.createTempDirectory("okafka-tns-admin-");
    Files.writeString(okafkaConfigDirectory.resolve("ojdbc.properties"), """
            user = testuser
            password = Welcome123#
            """);

    try (Connection connection = dataSource.getConnection()) {
        EventSchema.createTables(connection);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Final Thoughts
&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F85v4v97duua5vjfeinru.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F85v4v97duua5vjfeinru.png" alt="Diagram titled “Package the corrections.” Workflow rules and review guidance are packaged into an OKafka Java coding skill covering topics, transactions, and testing. The skill generates reusable artifacts such as topic administration, configuration properties, producer/consumer code, and integration tests. The goal is to turn recurring review comments into reusable implementation guidance." width="800" height="1232"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Agent skill design for reusable OKafka coding patterns and validation workflows.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The real leverage here is developing and sharing agent skills that capture the Oracle AI Database patterns your team needs. Do you have common database workflows? Common development patterns? Encapsulate them in a skill, iterate on it, and share it.&lt;/p&gt;

&lt;p&gt;Once details are packaged, agents can operate at a higher level. You spend less time correcting boilerplate and more time designing stronger examples, testing real behavior, and building more powerful Oracle AI Database applications from a better starting point.&lt;/p&gt;




&lt;h2&gt;
  
  
  To summarize
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Any Java developer working with Oracle AI Database can use this skill to write pub/sub code with Kafka APIs that target the database.&lt;/li&gt;
&lt;li&gt;OKafka adds database connection APIs to standard Kafka Java APIs; otherwise, the same interfaces are used.&lt;/li&gt;
&lt;li&gt;The getDBConnection() method in OKafka KafkaProducer and KafkaConsumer classes allows developers to add database logic to produce and consume operations in a single transaction.&lt;/li&gt;
&lt;li&gt;To validate generated code yourself, refer to &lt;a href="https://andersswanson.dev/2025/07/09/authenticate-to-your-oracle-database-like-its-a-kafka-cluster/" rel="noopener noreferrer"&gt;concrete OKafka code examples&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The skill leverages hand-written, tested OKafka code to generate new code specific to your application. You can find &lt;a href="https://anders-swanson.github.io/oracle-database-code-samples/patterns/event-streaming/" rel="noopener noreferrer"&gt;additional samples here&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;




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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://andersswanson.dev/2026/04/21/using-agent-skills-to-develop-with-oracle-ai-database/" rel="noopener noreferrer"&gt;Using Agent Skills to develop with Oracle AI Database&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://andersswanson.dev/2025/07/09/authenticate-to-your-oracle-database-like-its-a-kafka-cluster/" rel="noopener noreferrer"&gt;Authenticate to your Oracle AI Database like it’s a Kafka cluster&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://andersswanson.dev/2025/09/18/pub-sub-in-your-db-oracle-database-txeventq/" rel="noopener noreferrer"&gt;Pub/Sub in your DB? Oracle AI Database TxEventQ&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://andersswanson.dev/2025/05/28/migrate-apache-kafka-applications-to-oracle-database/" rel="noopener noreferrer"&gt;Migrate Apache Kafka applications to Oracle AI Database: Part I&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://andersswanson.dev/2026/01/14/propagating-cross-database-events-with-oracle-ai-database/" rel="noopener noreferrer"&gt;Propagating Cross-Database Events with Oracle AI Database&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://andersswanson.dev/2025/05/29/easily-test-oracle-database-applications-with-testcontainers/" rel="noopener noreferrer"&gt;Easily test Oracle AI Database applications with Testcontainers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://andersswanson.dev/2025/05/22/oracle-database-for-free/" rel="noopener noreferrer"&gt;Oracle AI Database for Free?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>agentskills</category>
      <category>kafka</category>
      <category>javaapi</category>
      <category>oracle</category>
    </item>
    <item>
      <title>Single OpenAI-compatible endpoint for OCI Generative AI models with LiteLLM</title>
      <dc:creator>Anya Summers</dc:creator>
      <pubDate>Mon, 29 Jun 2026 14:30:28 +0000</pubDate>
      <link>https://dev.to/oracledevs/single-openai-compatible-endpoint-for-oci-generative-ai-models-with-litellm-43e3</link>
      <guid>https://dev.to/oracledevs/single-openai-compatible-endpoint-for-oci-generative-ai-models-with-litellm-43e3</guid>
      <description>&lt;p&gt;This post stands up a LiteLLM gateway on an OCI Compute instance that authenticates to OCI Generative AI using an &lt;strong&gt;instance principal&lt;/strong&gt; — the identity OCI already hands every VM — so there are no signing keys to generate, mount, or rotate. Supported OCI Generative AI models such as Grok, Gemini, Llama, and Cohere models can be reached through the gateway, subject to region and model availability. And because routing is pure passthrough, the new supported on-demand models can be discovered without maintaining a hardcoded model list.&lt;/p&gt;

&lt;p&gt;If you saw the &lt;a href="https://blogs.oracle.com/ai-and-datascience/litellm-natively-supports-generative-ai" rel="noopener noreferrer"&gt;announcement&lt;/a&gt; that LiteLLM now natively supports Oracle Generative AI, this is the hands-on companion: the exact resources, the IAM that makes instance principal work, and the networking detail that ties it together — start to finish.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why this shape
&lt;/h2&gt;

&lt;p&gt;LiteLLM gives you a single OpenAI-compatible surface (/v1/chat/completions, /v1/embeddings, /v1/models) in front of Grok, Llama, Gemini, Cohere Command/Embed, and OpenAI gpt-oss — all hosted on OCI Generative AI, with OCI Signature v1 signing handled inside LiteLLM. Running it inside your tenancy on a Compute instance buys a simpler OCI credential story: the instance authenticates as itself, governed by an IAM policy, and you never handle a private key.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F061gekqqcjp6dq1exacd.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F061gekqqcjp6dq1exacd.png" alt="Architecture: a client calls the gateway over HTTPS on port 443; inside the OCI tenancy a Compute VM runs Caddy (automatic TLS) reverse-proxying to a podman container with the LiteLLM SDK + FastAPI shim and the instance-principal signer, which pulls a token from the instance metadata service and makes Signature-v1-signed calls out through the Internet Gateway to OCI Generative AI; an IAM policy authorizes generative-ai-family in the compartment." width="800" height="454"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;LiteLLM with OCI GenAI Architecture&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The gateway runs inside your tenancy. The client hits Caddy on :443 (NSG-gated, SSH scoped to your IP); Caddy terminates TLS and reverse-proxies to the shim on localhost:4000. The shim signs each call with the VM’s own instance-principal identity — token fetched from 169.254.169.254 — and reaches OCI Generative AI via the Internet Gateway. One IAM policy authorizes it all; no OCI API signing keys on disk. The federated token is short-lived, so the shim re-federates automatically on an OCI 401 INVALID_AUTHENTICATION_INFO — token expiry self-heals rather than surfacing as an error.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The one caveat worth reading first
&lt;/h2&gt;

&lt;p&gt;LiteLLM exposes OCI two ways, and they are not interchangeable:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The LiteLLM Proxy&lt;/strong&gt; (litellm --config config.yaml) supports OCI via &lt;strong&gt;manual API-key&lt;/strong&gt; credentials only — oci_user, oci_fingerprint, oci_tenancy, oci_key/oci_key_file, oci_compartment_id. There is no way to hand the proxy an instance-principal signer object through YAML.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The LiteLLM SDK&lt;/strong&gt; (litellm.completion(...)) accepts an oci_signer= object, which is the door to instance principal, resource principal, and OKE workload identity.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So if you want instance principal without OCI API signing keys, you call the &lt;strong&gt;SDK&lt;/strong&gt; and put a thin OpenAI-compatible HTTP layer in front of it. That’s the path of this implementation. You trade away the proxy’s management UI (virtual keys, budgets, logs); you avoid storing OCI API signing credentials.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1 — IAM: one policy, no keys, no users
&lt;/h2&gt;

&lt;p&gt;Instance principal is an any-principal identity at request time; the only thing between your VM and OCI Generative AI is a policy. Broad version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;allow any-user to manage generative-ai-family in compartment &amp;lt;YourCompartment&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Least-privilege version, scoped to the instance via a dynamic group:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Dynamic group (Identity &amp;amp; Security &amp;gt; Domains &amp;gt; Dynamic groups)
ALL {instance.compartment.id = '&amp;lt;compartment-ocid&amp;gt;'}
# Policy
allow dynamic-group &amp;lt;litellm-dg&amp;gt; to use generative-ai-family in compartment &amp;lt;YourCompartment&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;‘use‘ is enough for inference. That’s the entire identity story — no OCI API signing key stored on the instance.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2 — Networking
&lt;/h2&gt;

&lt;p&gt;A dedicated VCN keeps the gateway self-contained and trivially removable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;COMP=&amp;lt;compartment-ocid&amp;gt;; REGION=us-ashburn-1
VCN=$(oci network vcn create -c $COMP --region $REGION --cidr-blocks '["10.20.0.0/16"]' \
  --display-name litellm-vcn --dns-label litellmvcn --query data.id --raw-output --wait-for-state AVAILABLE)
IGW=$(oci network internet-gateway create -c $COMP --region $REGION --vcn-id $VCN --is-enabled true \
  --display-name litellm-igw --query data.id --raw-output --wait-for-state AVAILABLE)
RT=$(oci network route-table create -c $COMP --region $REGION --vcn-id $VCN --display-name litellm-rt \
  --route-rules '[{"destination":"0.0.0.0/0","destinationType":"CIDR_BLOCK","networkEntityId":"'$IGW'"}]' \
  --query data.id --raw-output --wait-for-state AVAILABLE)
SUBNET=$(oci network subnet create -c $COMP --region $REGION --vcn-id $VCN --cidr-block 10.20.1.0/24 \
  --display-name litellm-subnet --dns-label litellmsub --route-table-id $RT \
  --prohibit-public-ip-on-vnic false --query data.id --raw-output --wait-for-state AVAILABLE)
NSG=$(oci network nsg create -c $COMP --region $REGION --vcn-id $VCN --display-name litellm-nsg \
  --query data.id --raw-output --wait-for-state AVAILABLE)
oci network nsg rules add --nsg-id $NSG --region $REGION --security-rules \
  '[{"direction":"INGRESS","protocol":"6","source":"0.0.0.0/0","sourceType":"CIDR_BLOCK","isStateless":false,"tcpOptions":{"destinationPortRange":{"min":4000,"max":4000}}}]'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keep SSH (22) on the VCN default security list but scope it to your own IP. Port 4000 lives on the NSG, so you open it as wide as your clients need.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3 — A baked image, not install-on-boot
&lt;/h2&gt;

&lt;p&gt;The LiteLLM image runs from a uv-managed venv at /app/.venv that ships &lt;strong&gt;without pip&lt;/strong&gt;, so python3 -m pip install oci fails with “No module named pip”. Bootstrap it once at build time and bake the result into an image, so every container start is fast and offline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM ghcr.io/berriai/litellm:main-stable
RUN /app/.venv/bin/python3 -m ensurepip --upgrade \
 &amp;amp;&amp;amp; /app/.venv/bin/python3 -m pip install --no-cache-dir oci fastapi uvicorn
COPY server.py /app/server.py
ENV PORT=4000
ENTRYPOINT ["/app/.venv/bin/python3", "/app/server.py"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;podman build -t oci-litellm-gateway:latest -f Containerfile .
podman run -d --name litellm --restart=always --network=host \
  -e OCI_REGION=us-ashburn-1 -e OCI_COMPARTMENT_ID=&amp;lt;compartment-ocid&amp;gt; \
  -e LITELLM_MASTER_KEY=&amp;lt;your-bearer-key&amp;gt; -e PORT=4000 \
  oci-litellm-gateway:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two details that can save you a lot of debugging time:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;--network=host is mandatory.&lt;/strong&gt; Instance principal fetches its leaf certificate and token from the metadata service at 169.254.169.254 (link-local address). A container on the default bridge network can’t route to that link-local address; host networking fixes it (and binds :4000 on the host directly).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use the venv’s python3.&lt;/strong&gt; litellm lives in /app/.venv; a system python won’t see it. The ENTRYPOINT above pins it.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Step 4 — The shim: LiteLLM SDK behind an OpenAI-compatible API
&lt;/h2&gt;

&lt;p&gt;This is the whole gateway. It builds the instance-principal signer once, exposes the OpenAI routes, forwards every model name straight through as oci/, and discovers /v1/models live from OCI so there is no list to maintain. Those routes are the &lt;strong&gt;Chat Completions–era&lt;/strong&gt; API (/v1/chat/completions, /v1/embeddings, /v1/models) — deliberately not OpenAI’s newer &lt;strong&gt;Responses API&lt;/strong&gt; (/v1/responses); LiteLLM’s completion() and embedding() map to Chat Completions and Embeddings, which is still what every mainstream chat client speaks. Configuration is entirely environment-driven.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import os, json, datetime, litellm
from oci.auth.signers import InstancePrincipalsSecurityTokenSigner
from fastapi import FastAPI, Request, HTTPException
from fastapi.responses import StreamingResponse

REGION = os.environ.get("OCI_REGION", "us-ashburn-1")
COMP   = os.environ.get("OCI_COMPARTMENT_ID", "")
MKEY   = os.environ.get("LITELLM_MASTER_KEY", "")
PORT   = int(os.environ.get("PORT", "4000"))

SIGNER = InstancePrincipalsSecurityTokenSigner()
OCI = dict(oci_signer=SIGNER, oci_region=REGION, oci_compartment_id=COMP)
app = FastAPI()

def resolve(name):                       # pure passthrough: any model -&amp;gt; oci/&amp;lt;model&amp;gt;
    if not name: raise HTTPException(400, "missing 'model'")
    return name if name.startswith("oci/") else f"oci/{name}"

def auth(r):
    if MKEY and r.headers.get("authorization", "") != f"Bearer {MKEY}":
        raise HTTPException(401, "unauthorized")

def discover_models():                   # live, best-effort; never fatal
    try:
        import oci
        c = oci.generative_ai.GenerativeAiClient(config={}, signer=SIGNER)
        now = datetime.datetime.now(datetime.timezone.utc); out = []
        for m in c.list_models(compartment_id=COMP).data.items:
            caps = set(m.capabilities or [])
            if m.lifecycle_state != "ACTIVE" or not ({"CHAT","TEXT_EMBEDDINGS"} &amp;amp; caps): continue
            r = m.time_on_demand_retired
            if r is not None and r.year &amp;gt; 1971 and r &amp;lt;= now: continue
            out.append(m.display_name)
        return sorted(set(out))
    except Exception:
        return []

@app.get("/health/readiness")
def ready(): return {"status": "connected"}

@app.get("/v1/models")
def models():
    return {"object": "list", "data": [{"id": m, "object": "model", "owned_by": "oci"} for m in discover_models()]}

@app.post("/v1/chat/completions")
async def chat(req: Request):
    auth(req); b = await req.json()
    common = dict(model=resolve(b.get("model")), messages=b["messages"], **OCI)
    if b.get("stream"):
        def gen():
            for c in litellm.completion(stream=True, **common):
                yield f"data: {json.dumps(c.model_dump())}\n\n"
            yield "data: [DONE]\n\n"
        return StreamingResponse(gen(), media_type="text/event-stream")
    return litellm.completion(**common).model_dump()

@app.post("/v1/embeddings")
async def embeddings(req: Request):
    auth(req); b = await req.json()
    inp = b["input"]; inp = [inp] if isinstance(inp, str) else inp
    return litellm.embedding(model=resolve(b.get("model")), input=inp, **OCI).model_dump()

if __name__ == "__main__":
    import uvicorn; uvicorn.run(app, host="0.0.0.0", port=PORT)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because routing is passthrough, supported on-demand OCI Generative AI models can be reached through the gateway, subject to region, tenancy access, model availability, and LiteLLM compatibility. The live /v1/models discovery means you do not need to maintain a hardcoded model list, and supported new models can become available through the endpoint as OCI exposes them.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 5 — Test it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;IP=&amp;lt;public-ip&amp;gt;; KEY=&amp;lt;your-bearer-key&amp;gt;
curl -s http://$IP:4000/health/readiness
curl -s http://$IP:4000/v1/chat/completions \
  -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{"model":"xai.grok-4.3","messages":[{"role":"user","content":"In one sentence, what is Oracle Generative AI?"}]}'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It’s a drop-in OpenAI base URL, so the OpenAI SDK works unchanged:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from openai import OpenAI
c = OpenAI(base_url="http://&amp;lt;public-ip&amp;gt;:4000/v1", api_key="&amp;lt;your-bearer-key&amp;gt;")
print(c.chat.completions.create(model="xai.grok-4.3",
    messages=[{"role": "user", "content": "Hello from OCI"}]).choices[0].message.content)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 6 — Harden
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Scope SSH to your IP&lt;/strong&gt; in the VCN default security list; leave port 4000 (on the NSG) as open as you need.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Front it with a name and TLS (Caddy).&lt;/strong&gt; Point a DNS-only A record at the instance, open 80 + 443 in the NSG, then run Caddy alongside the gateway with a two-line Caddyfile:chat.example.com { reverse_proxy localhost:4000 } podman run -d --name caddy --restart=always --network=host \ -v /opt/caddy/Caddyfile:/etc/caddy/Caddyfile:Z \ -v caddy_data:/data -v caddy_config:/config \ docker.io/library/caddy:latest Caddy obtains and renews a Let’s Encrypt certificate automatically (TLS-ALPN-01 on 443, HTTP-01 on 80) and reverse-proxies to the shim on localhost:4000, passing the Authorization header through. Now &lt;a href="https://chat.example.com/v1" rel="noopener noreferrer"&gt;https://chat.example.com/v1&lt;/a&gt; works with no port (your domain name may vary here) — which also unblocks browser-hosted chat UIs that refuse to call plain-HTTP endpoints (mixed content). Note most DNS proxies won’t forward arbitrary ports, so keep the record DNS-only (or let Caddy own 443).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Let browser UIs in (CORS).&lt;/strong&gt; Server-side clients (curl, the SDK, Open WebUI, LobeChat on Vercel) work as-is, but browser apps that call the endpoint straight from the page need CORS headers or the browser blocks the preflight. Set ENABLE_CORS=true and scope CORS_ORIGINS; the master key stays the gate, and since auth is a bearer header rather than a cookie, credentials mode stays off.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rotate the key&lt;/strong&gt; by changing LITELLM_MASTER_KEY and restarting the container.&lt;/li&gt;
&lt;li&gt;For real multi-tenant key management, budgets, and request logs, switch to the LiteLLM Proxy with a manual signing key.&lt;/li&gt;
&lt;/ul&gt;




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

&lt;p&gt;This is a single OpenAI-compatible endpoint for supported on-demand OCI Generative AI models, authenticated with the instance’s own identity, without storing an OCI API signing key. That last part is the real win. Revoking access is editing one IAM policy.&lt;/p&gt;

&lt;p&gt;And it’s small enough to trust: one VCN, one subnet, one NSG, one VM, one policy — a surface you can hand to a security reviewer on a single page or stamp out per environment from the cloud-init here.&lt;/p&gt;

&lt;p&gt;In return, any OpenAI-compatible client—desktop apps, browser UIs, or your own code—can access Grok, Gemini, Llama, and Cohere without SDKs or per-application credentials. And because models are discovered dynamically, new OCI Generative AI models become available through the endpoint automatically.&lt;/p&gt;

&lt;p&gt;The only implementation details worth remembering are the non-obvious ones: --network=host for metadata access, bootstrapping pip into the image’s venv at build time, and remembering that instance principal authentication lives on the SDK path, not in the proxy’s YAML configuration.&lt;/p&gt;

</description>
      <category>oracle</category>
      <category>ai</category>
      <category>litellm</category>
      <category>oci</category>
    </item>
    <item>
      <title>The Agent Loop Decoded</title>
      <dc:creator>Anya Summers</dc:creator>
      <pubDate>Mon, 29 Jun 2026 14:28:14 +0000</pubDate>
      <link>https://dev.to/oracledevs/the-agent-loop-decoded-2ak2</link>
      <guid>https://dev.to/oracledevs/the-agent-loop-decoded-2ak2</guid>
      <description>&lt;p&gt;This article was originally written and published by Richmond Alake on &lt;a href="https://blogs.oracle.com/developers/the-agent-loop-decoded-three-levels-every-agent-engineer-must-know" rel="noopener noreferrer"&gt;blogs.oracle&lt;/a&gt; on 11 June.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three Levels Every Agent Engineer Must Know
&lt;/h2&gt;

&lt;p&gt;Chances are you have already run an agent loop today without naming it.&lt;/p&gt;

&lt;p&gt;Every session with a coding companion such as Claude Code, Codex, or Cursor is one: the model reads a  request, inspects the repository, edits a file, runs the tests, observes the failures, and edits  again until the build passes.&lt;/p&gt;

&lt;p&gt;That cycle of reasoning, acting, and observing the result is the  agent loop at work, and it now sits at the centre of nearly every production agent system. &lt;strong&gt;The agent loop is the repeating cycle a harness runs within a single agent turn: assemble context, invoke the model to reason, act on its decision, and go again until a stop condition ends the run.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This piece unpacks that loop across three levels of understanding.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Level 1 is the minimal  loop most developers meet first: an LLM, a handful of tools, and a response.&lt;/li&gt;
&lt;li&gt;Level 2  introduces a lifecycle inside the loop, where memory operations turn a stateless process into a reasoning engine with state.&lt;/li&gt;
&lt;li&gt;Level 3 pushes operations both inside and outside the loop,  where the agent harness becomes a system in its own right.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By the end, you will know which level your system sits at, what breaks when the level and the task are mismatched, and what engineering work moves you up. Every pattern discussed is implemented in the &lt;a href="https://github.com/oracle-devrel/oracle-ai-developer-hub/blob/main/notebooks/agent_memory.ipynb" rel="noopener noreferrer"&gt;companion notebook&lt;/a&gt;, built on Oracle AI Database, so you can run the loop rather than just read about it.&lt;/p&gt;




&lt;h2&gt;
  
  
  What is an Agent
&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsx60rb5dufdka42vbpha.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsx60rb5dufdka42vbpha.png" alt="Diagram showing a basic AI agent architecture. The agent perceives an environment containing users, tools, and data, reasons using a large language model, and takes actions. The agent also reads from and writes to a memory system that stores state beyond the current message, enabling persistence across interactions." width="800" height="1000"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 1: An agent perceives its environment, reasons with an LLM, acts, and remembers&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;An agent is a computational system that perceives its environment, reasons about  what it perceives, takes actions to achieve a goal, and has some form of memory.&lt;/strong&gt; That description applies to many things: a thermostat, a chess engine, a human professional.  What makes an AI agent distinct is that the reasoning step is handled by a large language  model, and the range of possible actions extends well beyond a binary output.&lt;/p&gt;

&lt;p&gt;An agent’s architecture consists of two separable layers. The first is the model: the inference engine that does the reasoning. The second is the harness: the code that prepares context,  executes tool calls, enforces operational constraints, and persists state. Most agent  engineering work happens in the harness, not the model. Understanding that boundary  clarifies where failures originate and where interventions are effective.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbk9dx5n77vlt8lmuaw4i.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbk9dx5n77vlt8lmuaw4i.png" alt="Diagram showing an agent architecture with two layers. A model handles reasoning and decision-making inside a larger agent harness. The harness provides context assembly, tool execution, operational constraints, and state persistence around the model. A note emphasizes that most agent engineering work occurs in the harness rather than the model itself." width="800" height="329"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 2: The two layers of an agent’s architecture: the model and the harness&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;An agent needs at minimum four things to be useful:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Instructions:&lt;/strong&gt; a system prompt or goal that tells it what it is trying to accomplish.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory:&lt;/strong&gt; access to information beyond the current message, including prior context,  retrieved knowledge, and learned patterns.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The ability to take actions:&lt;/strong&gt; tool calls, API requests, database writes, or any operation with an external effect.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A reasoning engine:&lt;/strong&gt; an LLM that looks at context and decides what to do next.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What Is a Loop?
&lt;/h2&gt;

&lt;p&gt;A loop is a control structure that repeats a block of execution until a condition is met. In  programming you encounter this everywhere: iterating over a collection, running until a flag  is set, calling recursively until a base case is reached.&lt;/p&gt;

&lt;p&gt;The agent loop applies that same structure to an LLM-powered system. Rather than  processing a user message once and returning a static response, the agent feeds its output  back into itself, reasoning, acting, observing the result, and reasoning again, until it  determines the task is complete.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuhq5i1fg3fhiwrz36ty3.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuhq5i1fg3fhiwrz36ty3.png" alt="Flow diagram showing the agent loop. Context is assembled from instructions, memory, and tool outputs, then passed to a reasoning step. The agent acts by responding, calling tools, or writing state. The cycle repeats until a stop condition is met, producing a final response." width="800" height="827"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 3: The agent loop: assemble context, reason, act, and repeat until a stop condition ends the run&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The necessity for loops in agent execution can be derived from the nature of the use cases  and tasks agents are applied to. These common use cases can be referred to as &lt;strong&gt;application modes&lt;/strong&gt;: the expected interaction patterns between a user and an agent. There  are three:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Assistant&lt;/li&gt;
&lt;li&gt;Deep Research&lt;/li&gt;
&lt;li&gt;Coding&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Take the deep research mode. An agent tasked with finding relevant sources, identifying  contradictions across them, and producing a structured summary is not running a single-shot task. It requires the agent to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Search for relevant sources.&lt;/li&gt;
&lt;li&gt;Read and evaluate what it finds.&lt;/li&gt;
&lt;li&gt;Identify gaps and contradictions.&lt;/li&gt;
&lt;li&gt;Search again to fill in those gaps.&lt;/li&gt;
&lt;li&gt;Synthesise everything into a coherent output.&lt;/li&gt;
&lt;/ul&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdtndyipnfa3y8bb2bvsh.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdtndyipnfa3y8bb2bvsh.png" alt="Diagram showing an agentic research workflow. The process repeatedly searches for sources, reads and evaluates information, identifies gaps or contradictions, and performs additional searches until coverage is sufficient. The collected information is then synthesized into a structured summary." width="800" height="1000"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 4: The deep research cycle: search, evaluate, identify gaps, and search again until coverage is sufficient&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;No single LLM call can do all of that. What is required is the mechanism and scaffolding that  allows the model to reason, act, observe the result, reason again, and continue until the task is complete. That mechanism is the agent loop.&lt;/p&gt;

&lt;p&gt;Notably, implementations of agent frameworks and harnesses, however opinionated, have  shared one thing in common: convergence on a minimal agent loop design. That  convergence is arguably not much of a design choice, so much as a logical consequence of  the task itself.&lt;/p&gt;

&lt;p&gt;The agent loop exists because long-horizon tasks cannot be  completed in a single forward pass.&lt;/p&gt;

&lt;p&gt;The loop emerging as a design pattern draws a parallel to how humans operate in most  organisations: structured cycles of work, review, and feedback that repeat until the objective  is met.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stop Conditions
&lt;/h2&gt;

&lt;p&gt;Loops have to be exited eventually. The programmatic loops taught in computer science  classes usually exit in one of two ways: the iteration count for the loop is reached, or a break statement inside the loop triggers an exit.&lt;/p&gt;

&lt;p&gt;A well-designed agent loop defines explicit exit criteria. Common examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The model produces a final response with no pending tool calls.&lt;/li&gt;
&lt;li&gt;A goal-completion check returns true: an objective-specific predicate, not merely the  absence of tool calls.&lt;/li&gt;
&lt;li&gt;A maximum number of iterations is reached.&lt;/li&gt;
&lt;li&gt;A wall-clock timeout expires.&lt;/li&gt;
&lt;li&gt;An error occurs that the agent cannot recover from.&lt;/li&gt;
&lt;li&gt;The harness identifies a failure mode, such as the agent repeating the same action  without progress.&lt;/li&gt;
&lt;li&gt;The agent explicitly invokes an exit action or sets a completion flag.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the notebook accompanying this article, the stop conditions are implemented directly  inside the harness:&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;call_agent&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;thread_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;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;max_iterations&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
&lt;span class="n"&gt;max_execution_time_s&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;60.0&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; 
 &lt;span class="n"&gt;start_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; 
 &lt;span class="n"&gt;iteration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; 
 &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;iteration&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;max_iterations&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;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;start_time&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;max_execution_time_s&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; 
 &lt;span class="k"&gt;break&lt;/span&gt; &lt;span class="c1"&gt;# Wall-clock timeout 
&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;call_openai_chat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;,&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;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tool_calls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; 
 &lt;span class="k"&gt;break&lt;/span&gt; &lt;span class="c1"&gt;# Model produced a terminal message; exit the loop 
&lt;/span&gt; &lt;span class="c1"&gt;# Execute tools, append outputs, continue 
&lt;/span&gt; &lt;span class="n"&gt;iteration&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; 
 &lt;span class="c1"&gt;# Fallback if max iterations reached 
&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;Max iterations reached; please refine the request.&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The max iterations of the loop is set to 10 by default. This is a guard against the loop running indefinitely, which can incur high operational cost through the increase in token consumption across inference calls. There is also a max_execution_time_s parameter, which adds a  temporal guard to the agent loop’s execution.&lt;/p&gt;

&lt;p&gt;It is worth noting that a terminal message from the model, one with no further tool calls, ends the agent’s turn. It does not mean the user’s goal has been satisfied. The model may return  a clarifying question, a partial result, or a response that requires follow-up. The agent  harness is responsible for checking whether the goal is actually complete, not simply  whether the model has stopped emitting tool calls. This distinction becomes more  consequential as tasks grow in length and complexity, and it is where domain expertise  becomes paramount in agent harness engineering.&lt;/p&gt;

&lt;p&gt;Failure mode identification deserves its own mention as an exit path. A loop should break  not only when work completes but when work stops progressing.&lt;/p&gt;

&lt;p&gt;The clearest example is tool call repetition: the agent invokes the same tool with identical arguments for a third consecutive iteration, a strong signal that it is stuck rather than working. A well-instrumented harness keeps a window of recent tool calls, detects the repetition, and exits with a diagnostic instead of spending the remaining iterations on a stalled run. Oscillation between two states belongs to the same family of detectable failures.&lt;/p&gt;




&lt;h2&gt;
  
  
  Defining the Agent Loop
&lt;/h2&gt;

&lt;p&gt;With the components and the exit criteria established, the definition can now be stated with precision:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Agent Loop&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A cyclical, iterative execution pattern inside a single agent run where the harness  repeatedly:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Assembles execution context:&lt;/strong&gt; system instructions, conversation state, retrieved  memory, tool outputs, and any relevant external data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Invokes a reasoning model&lt;/strong&gt; to decide what to do next.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Acts:&lt;/strong&gt; responds to the user, calls tools, writes memory or state, or updates its plan.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Each cycle appends its trace (assistant messages, tool outputs, state updates) to the  context and repeats until a termination check ends the run. Context-window pressure  and operational safety (timeouts, iteration caps, budget guards) are first-class concerns, not afterthoughts.&lt;/p&gt;




&lt;h2&gt;
  
  
  Three Levels of the Agent Loop
&lt;/h2&gt;

&lt;p&gt;The agent loop is not a fixed pattern. The simple design presented above evolves as  memory, tooling, and opinionated scaffolding are added. The three levels below provide a  framework for where a system currently sits and what engineering work lies ahead. Most  production failures (agents that repeat themselves, lose context, or produce inconsistent  results across sessions) trace back to a mismatch between task complexity and agent level.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdej38hkepjf75hl5wh9n.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdej38hkepjf75hl5wh9n.png" alt="Diagram titled “Memory Maturity” showing three levels of AI memory architecture. Level 1 uses only an LLM, tools, and responses, with no persistence beyond the context window. Level 2 adds memory lifecycle management, including reading memory before actions and writing memory afterward across multiple memory types. Level 3 extends memory inside and outside the agent loop with compaction, offloading, tool discovery, idempotency, and prompt caching." width="648" height="1232"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 5: The three levels of the agent loop&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Level 1: LLM + Tools + Response
&lt;/h2&gt;

&lt;p&gt;At its simplest, the agent loop is an LLM that can call tools and return a response. There is  no persistent memory, no external state, and no scaffolding beyond the loop itself. The loop  iterates because tool results must be fed back to the model before it can produce a final  answer.&lt;/p&gt;

&lt;p&gt;The code below demonstrates the pattern most developers encounter when building simple  tool-calling agents:&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;messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;system_prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_message&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; 
&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; 
 &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;llm&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;messages&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="n"&gt;available_tools&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tool_calls&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;call&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tool_calls&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;execute_tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;call&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;call&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="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;tool_result&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="k"&gt;else&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="c1"&gt;# Terminal message; exit
&lt;/span&gt;&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzxmwlfkbzowmqk2pe48j.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzxmwlfkbzowmqk2pe48j.png" alt="Diagram showing a Level 1 agent architecture. A user interacts with an agent loop containing a model and tools. The model issues tool calls, receives results, and repeats until the task is complete, after which a response is returned. No persistent memory is included." width="800" height="1000"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 6: Level 1: the minimal tool-calling loop&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;LangChain’s ReAct agent provides this pattern out of the box. The agent receives an input  query, selects a tool, calls it, observes the output, and reasons again, all within a single run:&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;langchain.agents&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AgentExecutor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;create_react_agent&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain_openai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ChatOpenAI&lt;/span&gt; 
&lt;span class="n"&gt;llm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ChatOpenAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;gpt-4o&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_react_agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;llm&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="n"&gt;search_tool&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="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;executor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AgentExecutor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;agent&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="n"&gt;search_tool&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;  
&lt;span class="n"&gt;max_iterations&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;span class="n"&gt;executor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;input&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;What are the latest AI papers on 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;p&gt;Level 1 is where most developers start, and it is genuinely useful for self-contained tasks. Its  limitation is structural: the agent has no recollection of previous conversations. Every run  starts cold, the context window is the only memory it has, and it resets completely when the  run ends. On any multi-turn or long-horizon task, it will repeat work it already did, lose track  of decisions made earlier in the session, and produce output that contradicts its own prior  responses.&lt;/p&gt;

&lt;h2&gt;
  
  
  Level 2: Lifecycle Inside the Loop
&lt;/h2&gt;

&lt;p&gt;At Level 2, operations begin to appear inside the agent loop. Memory is read before the LLM is called, and memory is written after the agent acts. The loop now has a lifecycle. At Level  1, the loop can be seen as a transport mechanism for tool calls. At Level 2, the loop  becomes a reasoning engine with state. This is also where the distinction between a  memory-augmented agent and a memory-aware agent becomes consequential.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Memory-augmented agents&lt;/strong&gt; retrieve and inject information into context. They read  from memory, but they do not actively manage it. Memory is something that happens  to them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory-aware agents&lt;/strong&gt; treat memory as a first-class engineering concern. They  encode, store, retrieve, inject, and forget, actively managing their cognitive state within  each run and across sessions. Level 2 is where you begin building memory-aware  agents.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This distinction, and the engineering it implies, is the subject of the DeepLearning.AI short  course &lt;a href="https://www.deeplearning.ai/courses/agent-memory-building-memory-aware-agents" rel="noopener noreferrer"&gt;Agent Memory: Building Memory-Aware Agents&lt;/a&gt;, built with Oracle, if you want the full  overview.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgts166k1h4mduci779si.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgts166k1h4mduci779si.png" alt="Comparison of memory-augmented and memory-aware agents. In the memory-augmented approach, memory is retrieved and injected into the agent externally. In the memory-aware approach, the agent actively retrieves, stores, updates, and forgets information, directly managing its own memory state." width="800" height="1000"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 7: Memory-augmented agents read from memory; memory-aware agents manage it&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Level 2 makes context assembly trade-offs immediately visible. Adding more memory types  (conversation history, retrieved documents, entity records, workflow patterns) improves  grounding and action selection. On the other hand, it also introduces cost: more tokens,  higher latency, and a greater risk of injecting irrelevant or stale content that misleads the  model rather than informing it.&lt;/p&gt;

&lt;p&gt;There are a few failure modes worth mentioning:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Noisy retrieval:&lt;/strong&gt; semantically similar documents that are not actually relevant to the  current query. Mitigation approaches are implemented via relevance thresholds and  precision-oriented retrieval strategies such as hybrid search and pre-, post-, and in-filtering methods in retrieval pipelines.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stale memory:&lt;/strong&gt; data can quickly become irrelevant in a fast-paced problem domain:  cached facts, entity records, or summaries that are no longer accurate. Mitigate with  TTL policies and update-on-write patterns.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tool schema overload:&lt;/strong&gt; context bloat is a common problem, and it is most prevalent in tool-calling agents with too many tool definitions passed to the model at once,  degrading tool selection accuracy. Mitigate with semantic tool retrieval rather than  exhaustive enumeration; this is shown in the companion notebook for this piece.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There are more failure modes, and in production these are not edge cases. They are  predictable failures that any Level 2 agent will encounter as memory stores grow. Designing  mitigation strategies at the start is cheaper than retrofitting fixes later.&lt;/p&gt;

&lt;p&gt;Memory operations are common in Level 2 agent loops, mainly because agents at this level  are designed for continuity and adaptation. &lt;strong&gt;Memory operations are programmatic  methods designed to modify data and information within the agent’s system  boundary and across other system components such as databases and external  stores.&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;When It Runs&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Read conversational memory&lt;/td&gt;
&lt;td&gt;Before LLM call&lt;/td&gt;
&lt;td&gt;Load prior chat history into  context&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Read knowledge base&lt;/td&gt;
&lt;td&gt;Before LLM call&lt;/td&gt;
&lt;td&gt;Inject relevant documents&amp;nbsp;and facts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Read workflow memory&lt;/td&gt;
&lt;td&gt;Before LLM call&lt;/td&gt;
&lt;td&gt;Surface known action&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;patterns&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Read entity memory&lt;/td&gt;
&lt;td&gt;Before LLM call&lt;/td&gt;
&lt;td&gt;Resolve named references in the query&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Write conversational memory&lt;/td&gt;
&lt;td&gt;After user message&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;received&lt;/td&gt;
&lt;td&gt;Persist the user turn&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Write knowledge base&lt;/td&gt;
&lt;td&gt;After tool search&lt;/td&gt;
&lt;td&gt;Store retrieved results for future runs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Write entity memory&lt;/td&gt;
&lt;td&gt;After LLM response&lt;/td&gt;
&lt;td&gt;Extract and persist people, places, systems&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Write conversational memory&lt;/td&gt;
&lt;td&gt;After final response&lt;/td&gt;
&lt;td&gt;Persist the assistant turn&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In the accompanying notebook, these operations are centralised in a MemoryManager class  backed by Oracle AI Database. Before each run, the harness calls all read operations to  assemble context. After each run, write operations persist the new information:&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="c1"&gt;# -- Reads: all run BEFORE the tool-call loop ------------------------ conv_mem = memory_manager.read_conversational_memory(thread_id) knowledge = memory_manager.read_knowledge_base(query) 
&lt;/span&gt;&lt;span class="n"&gt;workflows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;memory_manager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_workflow&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;entities&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;memory_manager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_entity&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;summaries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;memory_manager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_summary_context&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;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;build_context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conv_mem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;knowledge&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;workflows&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;entities&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="n"&gt;summaries&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;span class="c1"&gt;# -- Inner tool-call loop -------------------------------------------- response = run_tool_call_loop(context, tools) 
# -- Writes: all run AFTER the loop exits ---------------------------- memory_manager.write_conversational_memory(thread_id, 'assistant',  response) 
&lt;/span&gt;&lt;span class="n"&gt;memory_manager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write_entity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;extract_entities&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;response&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The notebook uses six distinct memory types, each stored in Oracle AI Database and each  serving a specific cognitive function:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Conversational memory:&lt;/strong&gt; episodic chat history retrieved by thread ID via a standard  SQL table. Exact lookup, no similarity search required.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Knowledge base memory:&lt;/strong&gt; semantic memory backed by a vector-enabled SQL table  with HNSW indexing for similarity search.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Workflow memory:&lt;/strong&gt; procedural memory storing learned action patterns and tool  sequences.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Toolbox memory:&lt;/strong&gt; a vector-indexed registry of tool definitions enabling semantic  discovery rather than exhaustive schema enumeration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Entity memory:&lt;/strong&gt; LLM-extracted people, places, and systems, persisted across  sessions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Summary memory:&lt;/strong&gt; compressed context for long conversations, with just-in-time  expansion when the agent needs the full content.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At Level 2, the loop is no longer just executing tools. It is actively managing its own cognitive state.&lt;/p&gt;

&lt;h2&gt;
  
  
  Level 3: Operations Inside and Outside the Loop
&lt;/h2&gt;

&lt;p&gt;At this point, developers understand not only which operations they require inside the loop;  more opinionated scaffolding and harness begin to form around the agent loop itself.&lt;/p&gt;

&lt;p&gt;Operations now exist both within the loop and outside it, and there are deliberate  architectural choices about which side of the boundary each operation belongs on. This is  where agent engineering becomes opinionated, and where context engineering and memory engineering become distinct disciplines with separate concerns.&lt;/p&gt;

&lt;p&gt;In a Level 3 agent loop, some operations should be automatic. The agent should never have to decide whether to load its own conversation history. Others should be agent-triggered: the agent decides when to search the web, not the harness.&lt;/p&gt;

&lt;p&gt;Getting this boundary wrong produces either context bloat, when too much is loaded automatically, or missed context,  when content that should always be present is left to the model’s discretion.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Programmatic&lt;/th&gt;
&lt;th&gt;Agent Triggered&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Read conversational memory&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;The agent always needs its history&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Read knowledge base&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Relevant documents always  loaded at run start&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Read workflow base&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Known patterns always&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;surfaced before reasoning&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Read entity memory&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Named references always resolved upfront&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Read summary context&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Summary IDs always loaded; full content expanded on demand&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Expand a summary&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Agent decides when it needs the full content&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Search the web (Tavily)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Agent decides when stored knowledge is insufficient&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Summarise conversation&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Agent decides when context needs compaction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Write tool log (offload)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Automatic after every tool call; keeps context lean&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Context engineering at Level 3&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Three techniques only become necessary at Level 3. Below Level 3, your context is  manageable by construction. At Level 3, with memory reads, multiple tool calls, and iterated  reasoning, it is not.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Context window monitoring:&lt;/strong&gt; tracking token usage across iterations to detect when  compaction is needed before the window fills and performance degrades.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Conversation compaction:&lt;/strong&gt; replacing verbose chat history with compressed  summaries while preserving originals in the database. The notebook marks messages  with a summary_id rather than deleting them, keeping the full record available for audit  and on-demand expansion.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tool output offloading:&lt;/strong&gt; persisting full tool outputs to a tool log table and replacing  them in context with a compact one-line reference.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The tool log pattern is worth examining in detail. A single web search can return three to four thousand tokens of raw results. Without offloading, every subsequent iteration in the same  run carries those tokens. With offloading, the context receives only a reference:&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;execute_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="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tool_args&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;raw_output&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;tool_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tool_args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
 &lt;span class="c1"&gt;# Full output persisted to the database 
&lt;/span&gt; &lt;span class="n"&gt;log_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;memory_manager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write_tool_log&lt;/span&gt;&lt;span class="p"&gt;(&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;thread_id&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;tool_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
 &lt;span class="n"&gt;tool_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;raw_output&lt;/span&gt; 
 &lt;span class="p"&gt;)&lt;/span&gt; 
 &lt;span class="c1"&gt;# Context receives only the compact reference 
&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="s"&gt;[Tool Log ID: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;log_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;] Results stored. Call read_tool_log to  retrieve.&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Semantic tool discovery&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;At Level 3, the number of available tools is unlikely to stay small. Passing every tool schema  to the model on every iteration is a known failure mode: tool selection accuracy drops as the  schema list grows, and token costs climb regardless of how many tools are actually relevant.&lt;/p&gt;

&lt;p&gt;The notebook addresses this with a &lt;strong&gt;Toolbox&lt;/strong&gt;: a vector-indexed registry of tool definitions  where only semantically relevant tools are retrieved and passed to the model for each query. Tools are registered with LLM-augmented metadata so that embeddings capture intent and  use case, not just function signatures:&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="nd"&gt;@toolbox.register_tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;augment&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# LLM enriches description for  retrieval 
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;search_tavily&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="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_results&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&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="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Search the web and persist results in the knowledge base.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;  &lt;span class="p"&gt;...&lt;/span&gt; 
&lt;span class="c1"&gt;# At runtime: only semantically relevant tools passed to the model
&lt;/span&gt;&lt;span class="n"&gt;relevant_tools&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;memory_manager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_toolbox&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current_query&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;Idempotency and tool reliability&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Tool call failures are a production reality. Network errors, rate limits, and transient service  issues occur regularly. If the harness retries a failed tool call naively, it risks executing a  side-effecting operation twice: writing a record, sending a message, or triggering a payment  more than once.&lt;/p&gt;

&lt;p&gt;The mitigation is idempotency: assigning each tool call a stable key before execution so that  retries can be safely distinguished from duplicate calls. This is harness-level engineering, not model-level reasoning, and it belongs in the Level 3 design.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prompt caching and message ordering&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;At Level 3, the harness also starts to affect inference economics through prompt caching.  Most LLM providers implement prefix-based caching: if the beginning of a prompt is identical to a recent request, the cached computation can be reused, reducing latency and cost.&lt;/p&gt;

&lt;p&gt;The implication for agent design is concrete. Rewriting earlier messages mid-conversation,  to clean up history, reorder context, or inject new system instructions inline, breaks prefix  stability and degrades cache hit rates. The correct pattern is to append new instructions  rather than modifying existing message history. The &lt;a href="https://openai.com/index/unrolling-the-codex-agent-loop/" rel="noopener noreferrer"&gt;Codex implementation&lt;/a&gt; established this  explicitly: old prompts are preserved as exact prefixes of new prompts specifically to  maintain caching benefits across long multi-step runs.&lt;/p&gt;

&lt;p&gt;Level 3 is where the agent harness becomes a system in its own right. The inner loop,  assembling context, invoking the model, and acting, has not changed. What has changed is  everything around it: the scaffolding that feeds it, the operational constraints that govern it,  and the persistence layer that gives it continuity across time and sessions.&lt;/p&gt;




&lt;h2&gt;
  
  
  Other Loops the Agent Engineer Should Know
&lt;/h2&gt;

&lt;p&gt;The agent loop does not run in isolation. It sits inside a wider system of loops, and the  engineering decisions made inside the agent loop are shaped by what happens in the loops  around it.&lt;/p&gt;

&lt;p&gt;Three matter most to agent engineers and memory engineers: the training loop that produced the model, the feedback loop that signals whether the system is working, and  the human loop that bounds its authority.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fr02zt6qj6mfbylorrfu8.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fr02zt6qj6mfbylorrfu8.png" alt="Diagram showing an agent loop connected to an Oracle AI Database memory layer. The loop assembles context, reasons, and acts while reading and writing episodic, semantic, procedural, entity, summary, and tool-log memories. Human review and feedback loops provide corrections and evaluation signals, while accumulated experience can feed future model training." width="800" height="1000"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 8: The loops interconnected: the training loop produces the model, the agent loop generates experience, and the memory layer routes that experience back as training signal&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The training loop&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The training loop is the cycle that produced the model in the first place: data  collection, gradient updates, evaluation, and release.&lt;/strong&gt; It operates offline, at a timescale of days or weeks, on curated datasets. The agent loop operates online, in real time, on live  interactions.&lt;/p&gt;

&lt;p&gt;Today these two loops are largely decoupled. Training happens, weights are frozen, and the  agent loop runs on top of those fixed weights. The apparent learning you observe within a  session, an agent recalling prior context or adapting to corrections, is not weight updating. It  is retrieval. The agent is not learning; it is reading from memory.&lt;/p&gt;

&lt;p&gt;This separation defines the boundary of what the agent loop can and cannot accomplish on  its own. It can accumulate experience through memory operations. It cannot change the  underlying model without a training cycle. Understanding this boundary tells you which  problems belong to memory engineering and which require retraining.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The feedback loop&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every action the agent takes produces feedback. Tool results are feedback. User corrections are feedback. Evaluation metrics (hallucination rate, task completion, citation accuracy) are  feedback at a system level.&lt;/p&gt;

&lt;p&gt;At Level 3, the agent harness begins to make the feedback loop explicit and instrumentable.  The notebook’s context window growth chart is a primitive example: watching whether token  counts stabilize across runs tells you whether your context engineering is actually working.  More sophisticated systems route evaluation signals back into memory stores, marking  retrieved content as reliable or unreliable based on downstream outcomes, and gradually  improving retrieval quality without retraining.&lt;/p&gt;

&lt;p&gt;The feedback loop is what turns an agent into a system that improves over time. Without it,  every invocation starts from the same baseline regardless of what the agent has done  before.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Human in the loop&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Long-horizon tasks regularly reach decision points where the agent lacks the information,  authority, or confidence to proceed without human input. The human-in-the-loop pattern  introduces a pause condition: the agent surfaces a question or proposed action, waits for  review or correction, and then continues.&lt;/p&gt;

&lt;p&gt;This is a stop condition of a different kind. Rather than halting because the task is finished,  the loop pauses because it has reached the boundary of its autonomous authority.  Designing this well involves two things: knowing in advance where those boundaries should  sit for a given workflow, and ensuring the agent communicates specifically when it reaches  one. A generic request for help is insufficient. The agent must surface a precise description  of what information or decision is blocking progress.&lt;/p&gt;

&lt;p&gt;Human-in-the-loop is not a safety net for when the agent fails. It is a deliberate architectural  decision about where human judgment adds the most value in a system. The agent loop  handles what can be reasoned about autonomously. The human loop handles what requires authority, context, or accountability that the agent does not have.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where This Is Going
&lt;/h2&gt;

&lt;p&gt;The agent loop, the training loop, and the feedback loop are currently operated as separate  engineering concerns. That separation is practical, not fundamental. As agents accumulate  experience across millions of runs, the information they generate (episodic memories, entity &lt;/p&gt;

&lt;p&gt;graphs, workflow patterns, evaluation signals, context growth traces) becomes a training  signal. The training loop will eventually consume the output of the agent loop, closing the  circle.&lt;/p&gt;

&lt;p&gt;When that happens, the quality of the memory layer becomes the quality of the training data. Agents with well-engineered memory (clean episodic records, accurately extracted entities,  reliable retrieval signals) produce better training signals than agents that let context  accumulate without structure.&lt;/p&gt;

&lt;p&gt;This convergence has a name. **Continual learning is the ability of a model to acquire  new knowledge and capabilities from a stream of incoming data over time, without  retraining from scratch and without catastrophically forgetting what it has already &lt;/p&gt;

&lt;p&gt;learned.** It is a formal machine learning discipline, not a metaphor, and it is the bridge  between the two loops: the agent loop generates the experience, and continual learning is  the process by which the training loop absorbs that experience into model weights.&lt;/p&gt;

&lt;p&gt;Continual learning in agentic systems is the capacity of an agent to improve over time through the accumulation of high-signal memory units, with the extracted signal applied across three optimization surfaces: token space, weight space, and latent space.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Union of the Agent Loop and the Training Loop
&lt;/h2&gt;

&lt;p&gt;What connects them is the memory layer.&lt;/p&gt;

&lt;p&gt;Oracle AI Database serves as the agent memory core, providing vector search,  relational storage, and graph capabilities in a single engine. Memory operations that run inside the agent loop (encoding, storing, retrieving, injecting, and forgetting) produce a  durable record of agent experience.&lt;/p&gt;

&lt;p&gt;Oracle OCI provides the platform for continuous learning: the infrastructure to retrain  models on that accumulated experience at scale, closing the loop from runtime  behaviour back into model weights.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The agent loop and the training loop are converging. The memory layer is where  they meet.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For engineers building agents today, this means the decisions made about memory  architecture are not just operational decisions. They are decisions about what the system will be able to learn from tomorrow. A database that can serve low-latency semantic search at  runtime can also serve as the data source for a continuous training pipeline.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Design your memory layer accordingly.&lt;/strong&gt;&lt;/p&gt;




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

&lt;p&gt;&lt;strong&gt;1. What is the agent loop?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The agent loop is the repeating cycle a harness runs within a single agent turn:  assemble context, invoke the model to reason, act on its decision, and repeat until a  stop condition ends the run. It exists because long-horizon tasks cannot be completed  in a single LLM call.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. How do you stop an agent loop from running forever?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Define explicit stop conditions in the harness: a terminal message with no pending tool  calls, a goal-completion check, an iteration cap, a wall-clock timeout, unrecoverable  errors, and failure mode detection such as the agent repeating the same tool call with  identical arguments.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. What is the difference between a memory-augmented agent and a memory-aware agent?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A memory-augmented agent retrieves and injects information into context but does not  manage it; memory is something that happens to the agent. A memory-aware agent  encodes, stores, retrieves, injects, and forgets, actively managing its cognitive state  within each run and across sessions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. How do I know which level my agent system sits at?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If there is no persistence beyond the context window, it is Level 1. If memory is read  before the model call and written after the agent acts, it is Level 2. If there is a  deliberate boundary between programmatic and agent-triggered operations, with  techniques such as compaction, tool output offloading, and semantic tool discovery, it  is Level 3.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. What connects the agent loop to the training loop?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The memory layer. Agent runs generate experience: episodic records, entities,  workflows, and evaluation signals. With continual learning, that experience becomes  training signal. Oracle AI Database stores and serves it inside the agent loop; Oracle  OCI provides the platform to retrain models on it. The patterns are implemented in the  companion notebook.&lt;/p&gt;

</description>
      <category>agentloop</category>
      <category>agents</category>
      <category>memory</category>
      <category>ai</category>
    </item>
    <item>
      <title>Database-Enforced Authorization for Agentic AI .NET Applications</title>
      <dc:creator>Anya Summers</dc:creator>
      <pubDate>Thu, 25 Jun 2026 20:07:31 +0000</pubDate>
      <link>https://dev.to/oracledevs/database-enforced-authorization-for-agentic-ai-net-applications-75g</link>
      <guid>https://dev.to/oracledevs/database-enforced-authorization-for-agentic-ai-net-applications-75g</guid>
      <description>&lt;p&gt;Protect .NET applications from over-broad agent access, prompt injection, and tool misuse with Oracle Deep Data Security and ODP.NET&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy40s1qsxts8kt28pu04k.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy40s1qsxts8kt28pu04k.png" alt=" " width="800" height="408"&gt;&lt;/a&gt;&lt;/p&gt;




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

&lt;ul&gt;
&lt;li&gt;Agentic AI can perform complex tasks, but it often requires broad and dynamic data access, which increases security and compliance risk.&lt;/li&gt;
&lt;li&gt;Enforcing authorization in the database reduces duplicated application logic and keeps access rules consistent across applications, agents, and tools.&lt;/li&gt;
&lt;li&gt;Oracle Deep Data Security provides database-native policy enforcement. ODP.NET 23.26.2 adds support for passing end-user security context from .NET applications.&lt;/li&gt;
&lt;li&gt;.NET applications can adopt this model by integrating end-user security context into the data access layer.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Agentic AI changes how applications access data. Instead of following fixed application flows, agents can choose tools and generate actions at runtime.&lt;/p&gt;

&lt;p&gt;When given relevant data, agents can further optimize workflows to meet objectives. However, broader access also increases the risk of unauthorized use and data exfiltration.&lt;/p&gt;

&lt;p&gt;As organizations deploy agentic AI, keeping enterprise data protected and auditable becomes harder with the wrong security model. When agents access the database directly or through Model Context Protocol (MCP) server tools, authorization must still be enforced before data is returned. If the agent’s database access is broader than the end user’s authorization, it can expose sensitive data or modify protected records.&lt;/p&gt;

&lt;p&gt;The key design question is where authorization should be enforced: in every application, or in the database. At scale, maintaining separate authorization logic in every application becomes difficult to validate and easy to get wrong. When requirements, queries, or schemas change, teams must update authorization logic across every affected application. This becomes unmanageable as more AI applications are deployed across the enterprise.&lt;/p&gt;

&lt;p&gt;As attackers adopt AI-driven penetration testing tools to find application vulnerabilities faster than before, securing access control at every app level entry point becomes even more critical.&lt;/p&gt;

&lt;p&gt;On the other hand, database-layer enforcement centralizes authorization and applies policies consistently before data is returned. Instead of relying on every developer to secure their part of the app perimeter, the same database policies can apply whether access comes from an application, an AI agent, or an MCP-based tool. Oracle AI Database 26ai (23.26.2) enables this capability with Deep Data Security.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Agentic AI Apps Need Deep Data Security
&lt;/h2&gt;

&lt;p&gt;Oracle Deep Data Security is a database-native authorization model that extends traditional system and object privileges by using end-user, agent, role, and attribute context in authorization decisions. It is designed for workloads where users, applications, agents, and tools may access the same data through different paths.&lt;/p&gt;

&lt;p&gt;Deep Data Security securely propagates end-user and agent identities, roles, and attributes to the database at runtime using an end-user security context. It is important to note that the end-users may not necessarily be database users. They can be any user type, such as Microsoft Entra ID or web application users. The database uses this context to enforce policies that define what users and agents can do — and when — and to generate audit records that capture activity. For example, a policy can allow a sales manager to see only customer rows for their assigned region, even if an agent generates a broader query.&lt;/p&gt;

&lt;p&gt;The Deep Data Security authorization model enforces fine-grained security at the row, column, and cell levels, enabling least-privilege access so end-users and agents see only authorized data. The database can return only authorized rows and masks sensitive column values when the end-user or agent lacks the required entitlement. Because the database enforces these policies during SQL execution, authorization remains consistent even when different applications or agents access the same data.&lt;/p&gt;

&lt;p&gt;Since policies are enforced in the database, developers do not have to duplicate the same authorization rules in every application or agent workflow. When requirements change, teams can update the database policy instead of rewriting authorization logic across multiple applications.&lt;/p&gt;

&lt;p&gt;For sensitive workflows, access can be granted only for the duration and scope of that workflow, instead of giving the application broad standing privileges. This reduces reliance on shared high-privilege service accounts that can read or write more data than the end user should have access to.&lt;/p&gt;

&lt;p&gt;Access boundaries must stay manageable, enforceable, and auditable as workflows change. Deep Data Security enforces least-privilege access for users and agents while preserving user identity in audit records to support safer, compliant AI adoption. .NET applications should incorporate Deep Data Security and pass end-user context to the database in a way they can manage consistently.&lt;/p&gt;

&lt;h2&gt;
  
  
  Develop .NET Apps with Deep Data Security
&lt;/h2&gt;

&lt;p&gt;.NET applications can pass end-user identity, claims, roles, and application context to the database, where Deep Data Security evaluates policies during SQL execution before unauthorized rows, columns, or values can be returned. Managed ODP.NET and ODP.NET Core 23.26.2 add extension methods to use this context payload.&lt;/p&gt;

&lt;p&gt;With minimal code changes, existing ODP.NET applications can use Deep Data Security’s protection with agentic AI. Applications do not need to map each end user to a separate database user. The database evaluates authorization using the supplied end-user security context. The database manages session lifecycles automatically based on OAuth2 tokens, which include user authorization claims for resources and applications.&lt;/p&gt;

&lt;p&gt;To do this, you will set the end-user security context on the ODP.NET connection using OracleConnection.SetEndUserSecurityContext. The connection then executes commands on behalf of an end user identified by a token. The application supplies the end-user context separately from the database access token used by the mid-tier. Deep Data Security then evaluates policies using the end-user claims, roles, and attributes. Data roles and attributes allow Oracle AI Database to evaluate role mappings and token claims during authorization. This enables Deep Data Security to deliver fine-grained, end-user-aware access control in .NET without database user credentials.&lt;/p&gt;

&lt;p&gt;Deep Data Security evaluates policies during SQL execution, before unauthorized rows, columns, or values are returned. By default, unauthorized data is masked as NULL, though SQL functions can apply other formats.&lt;/p&gt;

&lt;p&gt;ODP.NET uses the OracleEndUserSecurityContext class to represent the security identity for an application end user’s database operations.&lt;/p&gt;

&lt;p&gt;Putting it altogether, the following .NET code sample shows how to set a connection’s end-user security context and clear it after use.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OracleConnection conn = new OracleConnection(connStr);
conn.Open();
string userToken = GetUserToken();
string midTierToken = GetMidtierToken();

// Create security context using tokens
OracleEndUserSecurityContext securityContext = OracleEndUserSecurityContext.CreateWithTokens(midTierToken, userToken);

// Set security context on the connection
conn.SetEndUserSecurityContext(securityContext);

// Execute database operations

// Clear security context from connection
conn.ClearEndUserSecurityContext();

// Close connection
conn.Close();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Start Developing with ODP.NET Deep Data Security
&lt;/h2&gt;

&lt;p&gt;With ODP.NET and Oracle Deep Data Security, you can build end-to-end agentic AI .NET applications while protecting data from current and emerging threats. Data protection rules can evolve with simple changes, and access can be centrally managed and audited.&lt;/p&gt;

&lt;p&gt;Get started by downloading &lt;a href="https://www.nuget.org/packages/Oracle.ManagedDataAccess" rel="noopener noreferrer"&gt;managed ODP.NET&lt;/a&gt; or &lt;a href="https://www.nuget.org/packages/Oracle.ManagedDataAccess.Core" rel="noopener noreferrer"&gt;ODP.NET Core&lt;/a&gt; 23.26.2 with Deep Data Security and reviewing the &lt;a href="https://www.oracle.com/security/database-security/features/deep-data-security/" rel="noopener noreferrer"&gt;Oracle Deep Data Security web page&lt;/a&gt; and &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/odpnt/featConnecting.html#GUID-5A35E30D-8252-4ED1-9903-80C28E9DE011" rel="noopener noreferrer"&gt;ODP.NET Developer’s Guide Deep Data Security section&lt;/a&gt;.&lt;/p&gt;




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

&lt;p&gt;&lt;strong&gt;What is agentic AI?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It’s AI that can plan, reason, and execute multi-step tasks independently, often without human supervision.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why is managing data security at the database-level preferred?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It centralizes data access control, making it easier to manage, update, and audit compared to securing each application individually.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How does Deep Data Security protect data?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It enforces policies at row, column, and cell levels, ensuring users and AI agents only access authorized data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do .NET apps use Deep Data Security?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;They pass user and app identity via tokens into an ODP.NET connection security context, allowing the database to enforce access rules without exposing credentials.&lt;/p&gt;

</description>
      <category>agenticai</category>
      <category>ai</category>
      <category>dotnet</category>
      <category>deepdatasecurity</category>
    </item>
    <item>
      <title>5 Oracle AI Database Dev Tools I’d Put in a Starter Kit</title>
      <dc:creator>Anya Summers</dc:creator>
      <pubDate>Thu, 25 Jun 2026 19:56:31 +0000</pubDate>
      <link>https://dev.to/oracledevs/5-oracle-ai-database-dev-tools-id-put-in-a-starter-kit-89d</link>
      <guid>https://dev.to/oracledevs/5-oracle-ai-database-dev-tools-id-put-in-a-starter-kit-89d</guid>
      <description>&lt;p&gt;A practical toolkit to quickly build, test, and validate Oracle AI Database workflows from local to cloud&lt;/p&gt;




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

&lt;ul&gt;
&lt;li&gt;Start fast with &lt;strong&gt;containers or FreeSQL&lt;/strong&gt; to reduce setup time and quickly validate ideas or queries.&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;SQLcl and SQL Developer together&lt;/strong&gt; for both automation (CLI) and visual inspection (GUI).&lt;/li&gt;
&lt;li&gt;Enable &lt;strong&gt;AI-assisted workflows&lt;/strong&gt; with SQLcl’s MCP Server while enforcing security at the data layer.&lt;/li&gt;
&lt;li&gt;Move seamlessly from local experiments to &lt;strong&gt;Always Free Autonomous AI Database&lt;/strong&gt; for realistic cloud testing.&lt;/li&gt;
&lt;/ul&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7e5yw6y4ie9pce9ex9g0.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7e5yw6y4ie9pce9ex9g0.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Developers need the &lt;strong&gt;shortest path from claim to proof&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“Can I start a database locally?”&lt;/li&gt;
&lt;li&gt;“Can I connect from my app?”&lt;/li&gt;
&lt;li&gt;“Can I run my tests against it?”&lt;/li&gt;
&lt;li&gt;“Can I inspect the schema without guessing?”&lt;/li&gt;
&lt;li&gt;“Can I use it with scripts, agents, and CI?”&lt;/li&gt;
&lt;li&gt;“Can I easily move from a laptop to a managed cloud database?”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this article, we’ll look at tools that help you shorten the feedback loop for the development process you’re trying to prove.&lt;/p&gt;

&lt;p&gt;Here are the five I would put in a practical starter kit. &lt;strong&gt;These are tools I use every day.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Oracle AI Database Free Container Images
&lt;/h2&gt;

&lt;p&gt;Start local when you can. While the database container images are around 4–5 GB, they are multi-arch and start quickly for easy dev workflows on your laptop.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/anders-swanson/oracle-database-code-samples/blob/main/oracle-ai-database-docker-compose/README.md" rel="noopener noreferrer"&gt;Oracle AI Database Docker Compose sample&lt;/a&gt; spins up a disposable database on localhost:1521. It’s enough for most app development: point your app at the container database and fire away. When you’re done, throw the container away.&lt;/p&gt;

&lt;p&gt;I like containers a lot and use them constantly for development work. Here are some more Oracle-specific container resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For API work, try &lt;a href="https://github.com/anders-swanson/oracle-database-code-samples/blob/main/ords-docker-compose/README.md" rel="noopener noreferrer"&gt;Oracle REST Data Services (ORDS) with Docker Compose&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;For Testcontainers developers, try both &lt;a href="https://andersswanson.dev/2025/09/11/learn-testcontainers-java-with-oracle-database-free/" rel="noopener noreferrer"&gt;Oracle AI Database Free&lt;/a&gt; and &lt;a href="https://andersswanson.dev/2026/04/07/test-ords-locally-with-testcontainers-oracle-ai-database-free-and-mongodb/" rel="noopener noreferrer"&gt;ORDS&lt;/a&gt; in your test suites.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Use containers when you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Need repeatable local development&lt;/li&gt;
&lt;li&gt;Are running integration tests that create and destroy their own database&lt;/li&gt;
&lt;li&gt;Are testing a feature before moving it into shared infrastructure&lt;/li&gt;
&lt;li&gt;Need ORDS locally for REST, JSON, or SQL Developer Web workflows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Containers help you proof your code, schema, and assumptions on a clean database environment.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2. FreeSQL
&lt;/h2&gt;

&lt;p&gt;Sometimes the right local setup is no local setup.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.oracle.com/database/technologies/oracle-free-sql/" rel="noopener noreferrer"&gt;Oracle FreeSQL&lt;/a&gt; gives you a browser-based SQL environment for learning, testing queries, and sharing examples without installing a database first. It is a good tool when the goal is to remove setup friction.&lt;/p&gt;

&lt;p&gt;With a free account, you get a personal schema and can connect from tools such as SQLcl, VS Code, and application code. I covered that workflow in &lt;a href="https://andersswanson.dev/2026/02/04/use-oracle-freesql-com-as-a-remote-test-database/" rel="noopener noreferrer"&gt;Use Oracle FreeSQL as a remote test database&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Use FreeSQL when you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Are learning SQL or teaching someone else&lt;/li&gt;
&lt;li&gt;Need a remote schema without provisioning cloud infrastructure&lt;/li&gt;
&lt;li&gt;Want to test a query from a browser&lt;/li&gt;
&lt;li&gt;Are looking for a simple database target for examples, demos, or agents&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;FreeSQL is a low-friction place to start proving small things.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. SQLcl MCP Server
&lt;/h2&gt;

&lt;p&gt;SQLcl is one of the first tools I install for Oracle AI Database work.&lt;/p&gt;

&lt;p&gt;It is fast, scriptable, and useful for normal database development. You can run SQL, execute setup scripts, inspect objects, export data, load data, and automate validation without opening a full IDE.&lt;/p&gt;

&lt;p&gt;Now SQLcl also matters for AI-assisted development. Oracle describes &lt;a href="https://www.oracle.com/database/sqldeveloper/technologies/sqlcl/" rel="noopener noreferrer"&gt;SQLcl&lt;/a&gt; as a free command-line interface with an integrated MCP Server, and the &lt;a href="https://docs.oracle.com/en/database/oracle/sql-developer-command-line/26.1/sqcug/sqlcl-mcp-server.html" rel="noopener noreferrer"&gt;SQLcl MCP Server documentation&lt;/a&gt; explains how AI clients can use saved SQLcl connections to discover database context and run database operations through a structured MCP interface. The MCP server is something you can plug into Codex or Claude Code to assist with database operations.&lt;/p&gt;

&lt;p&gt;Use SQLcl when you want:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A reliable command-line SQL tool&lt;/li&gt;
&lt;li&gt;Repeatable scripts for setup, validation, or data loading&lt;/li&gt;
&lt;li&gt;An MCP bridge between an AI assistant and Oracle AI Database&lt;/li&gt;
&lt;li&gt;Agents to inspect real schema metadata instead of guessing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you’re using MCP, I also recommend reading up on &lt;a href="https://blogs.oracle.com/database/oracle-deep-data-security-is-now-available-in-oracle-ai-database-26ai" rel="noopener noreferrer"&gt;Oracle Deep Data Security&lt;/a&gt;, which is aimed at solving problems around authorization for agentic AI. The practical idea of Deep Data Security is simple: enforce authorization at the data layer, not only in the app or the prompt.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. SQL Developer
&lt;/h2&gt;

&lt;p&gt;SQL Developer complements SQLcl, providing additional features beyond the capabilities of the command line.&lt;/p&gt;

&lt;p&gt;Most database developers eventually need a visual tool for browsing schemas, inspecting rows, reviewing objects, writing SQL, debugging PL/SQL, or explaining something on a screen share.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.oracle.com/database/sqldeveloper/" rel="noopener noreferrer"&gt;SQL Developer&lt;/a&gt; is Oracle’s tool family for that job. If you need a dedicated database IDE, use standalone SQL Developer. If your day already lives in VS Code, use &lt;a href="https://www.oracle.com/database/sqldeveloper/vscode/" rel="noopener noreferrer"&gt;SQL Developer for VS Code&lt;/a&gt; and keep database work closer to your application code.&lt;/p&gt;

&lt;p&gt;Use SQL Developer when you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Want to browse schemas and database objects visually&lt;/li&gt;
&lt;li&gt;You are writing or debugging SQL and PL/SQL&lt;/li&gt;
&lt;li&gt;You need to inspect data quickly&lt;/li&gt;
&lt;li&gt;Want Oracle AI Database tooling inside VS Code or as a standalone app&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  5. Always Free Autonomous AI Database
&lt;/h2&gt;

&lt;p&gt;Some work needs a managed cloud database.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.oracle.com/en/cloud/paas/autonomous-database/serverless/adbsb/autonomous-always-free.html" rel="noopener noreferrer"&gt;Always Free Autonomous AI Database&lt;/a&gt; is what I use when I need something closer to a real cloud deployment.&lt;/p&gt;

&lt;p&gt;It’s a strong fit for personal projects, demos, APEX and ORDS work, cloud-native experiments, and validation that needs real cloud connectivity. You can test wallets, network rules, deployment behavior, and managed database operations in a realistic environment.&lt;/p&gt;

&lt;p&gt;The tradeoff is that it’s still managed cloud infrastructure. You need an Oracle Cloud Infrastructure (OCI) account, and you need to understand wallets, networking, and free-tier quotas. Always Free is useful for learning and validation, but it is not production capacity. Treating it like production will lead to bad assumptions.&lt;/p&gt;

&lt;p&gt;Use Always Free Autonomous AI Database when you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Need a persistent, managed Oracle AI Database environment&lt;/li&gt;
&lt;li&gt;Are building demos or personal projects with Oracle AI Database&lt;/li&gt;
&lt;li&gt;Want to test wallet-based connectivity&lt;/li&gt;
&lt;li&gt;Need to validate cloud deployment behavior before using paid resources&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Always-Free tier includes not just one, but two free Autonomous AI Database instances. Beyond that limit, &lt;a href="https://docs.oracle.com/en-us/iaas/autonomous-database-serverless/doc/autonomous-database-for-developers.html" rel="noopener noreferrer"&gt;Database For Developers&lt;/a&gt; offers fixed size database instances for ~$30/month on OCI. &lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus: LiveLabs Training and Tutorials
&lt;/h2&gt;

&lt;p&gt;Tools are easier to adopt when there is a guided path.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://livelabs.oracle.com/ords/r/dbpm/livelabs/home" rel="noopener noreferrer"&gt;Oracle LiveLabs&lt;/a&gt; gives you hands-on labs and workshops across Oracle technologies. It is useful when you need more than documentation but less than a full course.&lt;/p&gt;

&lt;p&gt;Use Oracle LiveLabs when you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Are learning a feature for the first time&lt;/li&gt;
&lt;li&gt;A guided workshop before building your own version&lt;/li&gt;
&lt;li&gt;Need training material for a team&lt;/li&gt;
&lt;li&gt;Want examples that connect product features to real tasks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Start Small and Prove One Thing&lt;/strong&gt;&lt;br&gt;
The goal isn’t to collect Oracle tools.&lt;/p&gt;

&lt;p&gt;The goal is to keep the development loop short: write code, run SQL, inspect results, automate the boring parts, and move from local to cloud without changing the way you think about the database.&lt;/p&gt;




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

&lt;p&gt;&lt;strong&gt;Q: What’s the fastest way to start using Oracle AI Database locally?&lt;/strong&gt;&lt;br&gt;
 Use container images with Docker Compose to spin up a disposable database for development and testing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: When should I use FreeSQL instead of a local database?&lt;/strong&gt;&lt;br&gt;
 When you want zero setup — ideal for learning SQL, quick demos, or testing queries in a browser.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: Why use both SQLcl and SQL Developer?&lt;/strong&gt;&lt;br&gt;
 SQLcl is great for scripting and automation, while SQL Developer helps with visual tasks like browsing schemas and debugging.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: When do I move to a cloud database?&lt;/strong&gt;&lt;br&gt;
 Use Always Free Autonomous AI Database when you need persistent storage, cloud connectivity testing, or a more production-like environment.&lt;/p&gt;

</description>
      <category>oracle</category>
      <category>webdev</category>
      <category>sqlserver</category>
      <category>database</category>
    </item>
    <item>
      <title>When to use Claude memory, Oracle AI Agent Memory, and LangChain together</title>
      <dc:creator>Anya Summers</dc:creator>
      <pubDate>Thu, 25 Jun 2026 19:44:11 +0000</pubDate>
      <link>https://dev.to/oracledevs/when-to-use-claude-memory-oracle-ai-agent-memory-and-langchain-together-1g03</link>
      <guid>https://dev.to/oracledevs/when-to-use-claude-memory-oracle-ai-agent-memory-and-langchain-together-1g03</guid>
      <description>&lt;p&gt;&lt;strong&gt;Build a controlled Claude MCP workflow with Oracle SQLcl, Oracle AI Database, Oracle AI Agent Memory, and LangChain.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Companion notebook:&lt;/strong&gt; &lt;a href="https://github.com/oracle-devrel/oracle-ai-developer-hub/blob/main/notebooks/claude_mcp_oracle_ai_database_memory_langchain.ipynb" rel="noopener noreferrer"&gt;Claude MCP Oracle AI Database: When to use Claude memory, Oracle AI Agent Memory, and LangChain together&lt;/a&gt;&lt;/p&gt;




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

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://modelcontextprotocol.io/docs/getting-started/intro" rel="noopener noreferrer"&gt;MCP&lt;/a&gt; turns AI-to-database access into an explicit tool contract instead of implicit system access.  &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.oracle.com/en/database/oracle/sql-developer-command-line/25.4/sqcug/using-oracle-sqlcl-mcp-server.html" rel="noopener noreferrer"&gt;Oracle SQLcl in MCP mode&lt;/a&gt; (sql -mcp) is a direct, documented way to connect Claude Desktop to Oracle AI Database through an MCP server.  &lt;/li&gt;
&lt;li&gt;Oracle AI Database provides the persistent storage and &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/vecse/overview-ai-vector-search.html" rel="noopener noreferrer"&gt;vector search&lt;/a&gt; layer for memory workloads, while &lt;a href="https://docs.oracle.com/en/database/oracle/agent-memory/26.4/agmea/get-started.html" rel="noopener noreferrer"&gt;Oracle AI Agent Memory&lt;/a&gt; gives teams a Python API for threads, durable memory records, scoped retrieval, and context assembly on top of it. &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.langchain.com/oss/python/integrations/vectorstores/oracle" rel="noopener noreferrer"&gt;LangChain plus langchain-oracledb&lt;/a&gt; is useful for structured retrieval pipelines once the memory layer is in place. &lt;/li&gt;
&lt;li&gt;A hybrid model is a strong default for many teams: Claude + MCP for operational interaction, Oracle AI Database + LangChain for durable memory records and retrieval.
  &lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Here is how those components connect in this pattern. Claude talks to Oracle through SQLcl MCP, using the tools and database permissions you expose. Oracle AI Agent Memory is the Python package your app uses to manage durable memory records and context assembly. LangChain is an optional wrapper at the end of the retrieval path. The knowledge base is data in Oracle tables, with retrieval and access governed by your application and database design. &lt;/p&gt;

&lt;p&gt;Production success depends less on “prompt quality” and more on boundaries, privileges, logging, and repeatable runbooks.  &lt;/p&gt;

&lt;p&gt;Many AI assistant demos fail in the same place: not in the first interaction, but in week two. The assistant can generate SQL and explain concepts, but the workflow often lacks durable context across sessions. Teams also struggle to answer basic operational questions, like who executed what, where, and with which permissions.  &lt;/p&gt;

&lt;p&gt;That is why this topic matters for developer teams right now. If you are integrating AI into workflows that query, analyze, or modify data in Oracle- running reports, inspecting schemas, retrieving context, or writing results back- you need two things at once: controlled execution and durable memory.&lt;/p&gt;

&lt;p&gt;MCP defines the execution boundary. Oracle AI Database provides durable storage, vector search, and database controls for application memory records. You can build that layer directly with tables and retrieval logic, but the Oracle AI Agent Memory Python package makes the integration easier once memory workflows start getting more complex. LangChain comes in later when you need structured retrieval and orchestration on top of that.  &lt;/p&gt;

&lt;p&gt;By the end of this guide you will know how to connect Claude to Oracle AI Database through a controlled MCP boundary, when Claude’s built-in memory is sufficient and when your application needs Oracle AI Agent Memory to manage durable memory records in Oracle AI Database, and how to build a retrieval pipeline you can query, audit, and grow over time. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The developer path through this guide is simple:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Start with one approved Oracle connection and a read-only validation query.  &lt;/li&gt;
&lt;li&gt;Put SQLcl MCP in front of that connection so Claude sees tools, not raw database credentials.  &lt;/li&gt;
&lt;li&gt;Check the audit and activity trail before adding more tool access.  &lt;/li&gt;
&lt;li&gt;Add Oracle AI Agent Memory when the workflow needs durable thread context, scoped recall, or reusable context cards.  &lt;/li&gt;
&lt;li&gt;Add LangChain only when you need application-side retrieval orchestration beyond the MCP interaction loop.  &lt;/li&gt;
&lt;/ul&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgndzargo0v2g7the8flx.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgndzargo0v2g7the8flx.png" alt=" " width="799" height="305"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Claude Memory vs Oracle AI Agent Memory
&lt;/h2&gt;

&lt;p&gt;Claude’s built-in memory has improved significantly, with support for chat history and project-level context. It works well for assistant continuity, but it is still scoped to the assistant experience.  &lt;/p&gt;

&lt;p&gt;Before getting into the memory categories, it is worth introducing Oracle AI Agent Memory properly. It is a Python package that sits on top of Oracle AI Database and provides the application-facing API for conversation threads, durable memory records, scoped retrieval, and context cards you can pass back to an assistant. You can build the same tables and retrieval logic yourself, and the companion notebook shows exactly how that works at the table level. But once memory workflows grow-multiple users, cross-session context, retrieval at scale, this package saves a lot of repeated work. Think of Oracle AI Agent Memory as the API your application talks to, and Oracle AI Database as the storage and enforcement layer underneath it. &lt;/p&gt;

&lt;p&gt;In practice, “memory” means different things depending on the layer you are talking about. Claude Memory and Oracle AI Agent Memory solve different problems:  &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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8wmq44okx3udlnzu7rxz.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8wmq44okx3udlnzu7rxz.png" alt=" " width="800" height="367"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As of writing, Claude’s memory makes conversations smoother, but it’s still scoped to the assistant experience. It’s not built for querying application history, sharing context across users, or enforcing database-level audit and access controls. That’s where Oracle AI Agent Memory comes in. It gives you a persistent application memory layer you can query and manage across sessions and teams. Important decisions should still be grounded in systems of record, application authorization, and human or workflow review where required.  &lt;/p&gt;

&lt;p&gt;A simple way to think about it: Claude remembers for the conversation. Oracle AI Agent Memory remembers for the system. &lt;/p&gt;

&lt;p&gt;Because memory records live in Oracle AI Database and not on one local machine, they can become portable across approved clients. Point a new machine at the same database with the right credentials and policies, and the application can retrieve the same memory records. &lt;/p&gt;

&lt;p&gt;Even with Claude memory, teams often need an application-level memory layer. Claude memory is not designed for querying history across users, storing tool logs, or applying database access controls. Oracle AI Database can help fill that gap by providing durable, shared, and queryable memory records for application workflows.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use the layers this way:&lt;/strong&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use Claude memory for assistant continuity: preferences, project context, and conversational convenience inside the assistant experience.  &lt;/li&gt;
&lt;li&gt;Use SQLcl MCP when Claude needs to inspect or query Oracle through an explicit tool boundary.  &lt;/li&gt;
&lt;li&gt;Use Oracle AI Agent Memory when your application needs durable threads, searchable memory records, scoped retrieval, or context cards across users, agents, and sessions.  &lt;/li&gt;
&lt;li&gt;Use LangChain when your app needs reusable retrieval chains, routing logic, or orchestration around the memory and vector search layer.  &lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Why this architecture is useful for developers 
&lt;/h2&gt;

&lt;p&gt;The developer value is practical: each layer gives you something concrete to test before you trust the whole workflow. You can validate the MCP server, the saved SQLcl connection, the database role, the durable application-memory write path, and the retrieval query separately. &lt;/p&gt;

&lt;p&gt;That matters after the demo. When an answer looks wrong, a developer can inspect whether the tool call ran, which database user executed it, what SQL or retrieval path was used, which durable memory records or tool traces were returned, and whether the application assembled the right context. The failure stops being “the model was wrong” and becomes a narrower engineering problem. &lt;/p&gt;

&lt;p&gt;The responsibilities break down into testable layers: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The assistant translates user intent into a plan your app or MCP client can inspect.  &lt;/li&gt;
&lt;li&gt;MCP exposes a declared tool surface instead of broad implicit system access.  &lt;/li&gt;
&lt;li&gt;SQLcl MCP gives developers a reproducible bridge from Claude Desktop to approved Oracle connections.  &lt;/li&gt;
&lt;li&gt;Oracle AI Database keeps roles, privileges, memory records, tool logs, and vector retrieval close to the data layer.  &lt;/li&gt;
&lt;li&gt;Oracle AI Agent Memory gives Python developers a package API for threads, durable memory records, scoped search, and context cards. This is application memory, not just chat history.  &lt;/li&gt;
&lt;li&gt;LangChain handles retrieval workflows and tool coordination where application logic is needed, without becoming the permission boundary. &lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;The payoff is a workflow that is easier to review, easier to debug, and easier to grow. You can start read-only, prove the connection and logging path, add durable memory when the application needs continuity across sessions or workflows, and keep each new capability attached to a named layer instead of burying everything in prompts or a custom agent framework.  &lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding the two execution loops 
&lt;/h2&gt;

&lt;p&gt;Building on the separation of responsibilities above, the system naturally forms two execution loops:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Loop A: An operational loop for real-time interaction (Claude + MCP): This is the real-time interaction where Claude works with MCP to run queries, inspect data, and respond immediately.  &lt;/li&gt;
&lt;li&gt;Loop B: A persistence loop for cross-session memory (Oracle AI Database + LangChain): This is where Oracle AI Database and LangChain handle durable memory records, tool logs, and context retrieval across sessions. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One loop handles real-time interaction, the other handles durable memory records and retrieval.  &lt;/p&gt;

&lt;p&gt;SQLcl MCP is for Claude operating interactively- real-time queries during a conversation, routed through a declared tool contract. Oracle AI Agent Memory is for your application code- storing turns, retrieving history, assembling context before Claude sees a prompt. They serve different loops at different times. You can drop either one depending on your use case, but many production setups benefit from both. &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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcny0lwz4qw0pio1vyzp6.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcny0lwz4qw0pio1vyzp6.png" alt=" " width="800" height="324"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Setup Guide: Reproducing the Oracle SQLcl MCP and Claude Workflow 
&lt;/h2&gt;

&lt;p&gt;The SQLcl MCP setup is documented by Oracle and reproducible in the way that matters for developers: you can install it, test it, validate the saved connection, and inspect activity before Claude runs a real query.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prerequisites before you connect Claude&lt;/strong&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Oracle SQLcl 25.2.0 or higher.  &lt;/li&gt;
&lt;li&gt;Oracle JRE 17 or 21.  &lt;/li&gt;
&lt;li&gt;Claude Desktop or another MCP-capable client you are explicitly configuring and testing.  &lt;/li&gt;
&lt;li&gt;At least one saved SQLcl connection profile under ~/.dbtools, created with password persistence for MCP use.  &lt;/li&gt;
&lt;li&gt;A database user with the minimum permissions required for the workflow. Start with read-only access and a sanitized development or replica environment where possible.
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The core idea is simple. SQLcl runs in MCP mode with sql -mcp. Claude Desktop launches it as an MCP server and talks to the database through declared tools and the permissions attached to the saved connection. Connections come from saved profiles in the SQLcl connection store under ~/.dbtools. Claude does not invent them at runtime, it reuses ones you have already created and validated. &lt;/p&gt;

&lt;p&gt;One setup detail that catches people out: MCP-compatible saved connections need the password persisted. That is what the -savepwd flag does when you create the connection. Treat that saved profile as a credentialed application path: use a purpose-specific database user, keep the grant surface small, and avoid pointing first experiments at production data.&lt;/p&gt;

&lt;p&gt;Once that is done, you configure Claude Desktop to point at the SQLcl executable and pass -mcp as the argument. Claude Desktop manages the server lifecycle from there, and SQLcl translates tool calls into database operations. Oracle recommends granting the minimum permissions required, considering sanitized copies or read-only replicas for AI access, and auditing LLM activity. SQLcl MCP activity can be inspected through database-side traces such as DBTOOLS$MCP_LOG and session views such as V$SESSION. (&lt;a href="https://docs.oracle.com/en/database/oracle/sql-developer-command-line/25.4/sqcug/using-oracle-sqlcl-mcp-server.html" rel="noopener noreferrer"&gt;docs.oracle.com&lt;/a&gt;)  &lt;/p&gt;

&lt;p&gt;SQLcl MCP also supports restrict levels. The documented default is restrict level 4, which disables sensitive commands such as unrestricted file system access and host execution. Treat changes to the restrict level as an explicit security decision, not as a convenience toggle. (&lt;a href="https://docs.oracle.com/en/database/oracle/sql-developer-command-line/25.4/sqcug/configuring-restrict-levels-sqlcl-mcp-server.html" rel="noopener noreferrer"&gt;docs.oracle.com&lt;/a&gt;) &lt;/p&gt;

&lt;p&gt;A minimal configuration looks like this:&lt;br&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="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;"PATH/bin/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="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;That small JSON block defines the connection between Claude and SQLcl MCP Server. Claude interacts with the database through the tools and permissions exposed by the MCP server, using the saved SQLcl connection profile you created and tested first.  &lt;/p&gt;
&lt;h2&gt;
  
  
  Validation checklist before expanding access
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Run sql -mcp locally and confirm the server starts.  &lt;/li&gt;
&lt;li&gt;Restart Claude Desktop and confirm the SQLcl tools are discoverable.  &lt;/li&gt;
&lt;li&gt;Run one read-only query against an approved schema.  &lt;/li&gt;
&lt;li&gt;Check database-side MCP activity logs and session metadata.  &lt;/li&gt;
&lt;li&gt;Document the connection alias, database user, grant scope, restrict level, and troubleshooting owner.  &lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Good first proof looks like this:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The MCP server starts without a Java or path error.  &lt;/li&gt;
&lt;li&gt;Claude lists the SQLcl MCP tools after restart.  &lt;/li&gt;
&lt;li&gt;A read-only query succeeds against the expected schema.  &lt;/li&gt;
&lt;li&gt;The database-side activity trail shows the MCP interaction.  &lt;/li&gt;
&lt;li&gt;A denied query fails because of the database role, not because a prompt asked nicely.  &lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Why put application memory records in Oracle AI Database, not just outputs
&lt;/h2&gt;

&lt;p&gt;  &lt;br&gt;
Once your first tool calls work, the next challenge is continuity. If memory lives only in chat context, the system is fragile. If memory is scattered across files without structure, retrieval and auditing become expensive over time.  &lt;/p&gt;

&lt;p&gt;It’s worth calling out the difference here. At this point, the challenge shifts from conversation persistence to system-level memory.  &lt;/p&gt;

&lt;p&gt;A model that uses Oracle AI Agent Memory is often cleaner and easier to operate as the workflow grows. &lt;/p&gt;

&lt;p&gt;The companion notebook builds this memory layer from scratch, so the mechanics are visible and then shows how Oracle AI Agent Memory slots on top of it once the substrate is working. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Memory categories that matter in practice&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Conversational memory  
Stores user and assistant turns, thread IDs, timestamps, and metadata.  &lt;/li&gt;
&lt;li&gt;Operational memory  
Stores tool inputs, outputs, status, and error classes for troubleshooting and audit.  &lt;/li&gt;
&lt;li&gt;Semantic memory  
Stores chunks and embeddings for meaning-based retrieval when exact keywords are absent.  &lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Why this matters technically
&lt;/h2&gt;

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

&lt;ul&gt;
&lt;li&gt;SQL tables give deterministic filtering and ordering.  &lt;/li&gt;
&lt;li&gt;Transactions improve integrity under concurrent writes.  &lt;/li&gt;
&lt;li&gt;Vector retrieval helps with paraphrases and conceptual matches.  &lt;/li&gt;
&lt;li&gt;Keeping memory on one platform makes it easier to manage, audit, and keep consistent over time.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This works well with Oracle AI Database because structured records and semantic retrieval data can stay in one place. &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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjhqqd9i574gj7ch02x7m.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjhqqd9i574gj7ch02x7m.png" alt=" " width="800" height="334"&gt;&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Where LangChain adds value (and where it should not be overused)
&lt;/h2&gt;

&lt;p&gt;  &lt;br&gt;
LangChain is useful as orchestration glue, especially when teams want a documented path for tool definitions and retrieval calls. One thing worth stating clearly: in the architecture shown here, Claude Desktop does not call LangChain directly. LangChain runs in your application layer to format context before it reaches Claude’s prompt. With langchain-oracledb, teams can wire vector retrieval in Oracle AI Database while keeping control in database roles and runtime policies.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Good uses of LangChain in this architecture&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Declaring retrieval and memory tools in a consistent format.  &lt;/li&gt;
&lt;li&gt;Running retrieval-first answer pipelines.  &lt;/li&gt;
&lt;li&gt;Standardizing how context is assembled before generation.  &lt;/li&gt;
&lt;li&gt;&lt;p&gt;Building reusable agent patterns across teams.&lt;br&gt;
  &lt;br&gt;
&lt;strong&gt;Poor uses of LangChain in this architecture&lt;/strong&gt; &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Assuming LangChain automatically makes database access safe. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Relying only on prompts to limit what the assistant is allowed to do. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Adding too many tools before your team knows how to manage and troubleshoot them.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A good rule is to enforce permissions in the database and infrastructure layer, not only in framework code or prompts.&lt;/p&gt;


&lt;h2&gt;
  
  
  Practical Implementation Snippets
&lt;/h2&gt;

&lt;p&gt;The snippets below show the minimum useful shape of the implementation: the MCP boundary, the memory substrate, a package-level memory API, and the retrieval policy that keeps generated answers grounded.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1) MCP boundary snippet&lt;/strong&gt;&lt;br&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="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;"C:&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;tools&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;sqlcl&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;bin&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;sql.exe"&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="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;&lt;strong&gt;2) Memory schema concept snippet&lt;/strong&gt;&lt;br&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;-- CONVERSATIONAL_MEMORY  &lt;/span&gt;
&lt;span class="n"&gt;THREAD_ID&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;span class="n"&gt;CONTENT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;METADATA_JSON&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CREATED_AT&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt; 
&lt;span class="err"&gt; &lt;/span&gt; 
&lt;span class="c1"&gt;-- TOOL_LOGS  &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;TOOL_NAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TOOL_INPUT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TOOL_OUTPUT&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="n"&gt;ERROR_MESSAGE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CREATED_AT&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt; 
&lt;span class="err"&gt; &lt;/span&gt; 
&lt;span class="c1"&gt;-- KB_CHUNKS (used for vector retrieval via langchain-oracledb)  &lt;/span&gt;
&lt;span class="n"&gt;TEXT_CHUNK&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;METADATA_JSON&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;EMBEDDING&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;3) Oracle AI Agent Memory package path&lt;/strong&gt;&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="s2"&gt;"oracleagentmemory==26.4.0"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The package path expects Python 3.10 or later, Oracle AI Database, version 26ai or later for compatibility, an Oracle AI Database connection or connection pool, an embedding model for retrieval, and an optional LLM for memory extraction, summaries, and context cards. The exact adapters depend on your application, but the API shape is intentionally small:&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.apis.searchscope&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SearchScope&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;oracleagentmemory.core.oracleagentmemory&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OracleAgentMemory&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;oracleagentmemory.core.embedders.embedder&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Embedder&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;oracleagentmemory.core.llms.llm&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Llm&lt;/span&gt;

&lt;span class="n"&gt;embedder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Embedder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YOUR_EMBEDDING_MODEL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;llm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Llm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YOUR_LLM&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;db_pool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;  &lt;span class="c1"&gt;# your oracledb connection or connection pool
&lt;/span&gt;
&lt;span class="n"&gt;memory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OracleAgentMemory&lt;/span&gt;&lt;span class="p"&gt;(&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;db_pool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;embedder&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;embedder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;thread&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;thread&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="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;Remember that I prefer morning deployment reviews.&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;Got it. I will keep that preference in mind.&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;thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_memory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;The user prefers morning deployment reviews.&lt;/span&gt;&lt;span class="sh"&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&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;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;When does this user prefer deployment reviews?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;SearchScope&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="n"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_context_card&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use oracleagentmemory from your application layer when you need package-managed users, agents, memories, threads, scoped retrieval, and context assembly. Keep systems of record separate from memory records: memory helps provide context, but application logic and authoritative data sources should still decide what is true, allowed, and final. (&lt;a href="https://docs.oracle.com/en/database/oracle/agent-memory/26.4/agmea/get-started.html" rel="noopener noreferrer"&gt;docs.oracle.com&lt;/a&gt;) &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4) Retrieval-first policy snippet (pseudo-policy)&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Retrieve relevant memory before synthesizing final answer.  &lt;/li&gt;
&lt;li&gt;If retrieval is empty, say context is insufficient.  &lt;/li&gt;
&lt;li&gt;Keep answers evidence-first and concise.  &lt;/li&gt;
&lt;li&gt;Log tool calls with status and timestamp.
  &lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Engineering guidance for production teams
&lt;/h2&gt;

&lt;p&gt;  &lt;br&gt;
The difference between demo success and production success is disciplined operations. Most failures at this stage come from integration gaps, not model behavior.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Access and privilege model&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Separate accounts per environment (dev, test, prod).  &lt;/li&gt;
&lt;li&gt;Start read-only wherever possible.  &lt;/li&gt;
&lt;li&gt;Use least privilege grants and schema allowlists.  &lt;/li&gt;
&lt;li&gt;Gate write operations with explicit confirmation workflows.  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Observability model&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Log tool name, thread ID, timestamp, status, and sanitized inputs.  &lt;/li&gt;
&lt;li&gt;Classify failures into runtime, connection, privilege, query, and retrieval.  &lt;/li&gt;
&lt;li&gt;Keep a troubleshooting playbook in your repo.  &lt;/li&gt;
&lt;li&gt;Check whether retrieval results become less accurate as more data is added.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Reliability model&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prefer deterministic SQL patterns with bounded result sets.  &lt;/li&gt;
&lt;li&gt;Use retrieval-first context assembly for memory-heavy tasks.  &lt;/li&gt;
&lt;li&gt;Avoid giant context stuffing as a substitute for memory design.  &lt;/li&gt;
&lt;li&gt;Review and prune tool surfaces periodically. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is also where teams should align with platform and security teams early. Governance should be designed into the architecture, not bolted on after incidents.  &lt;/p&gt;




&lt;h2&gt;
  
  
  Typical failure modes and how to diagnose them fast 
&lt;/h2&gt;

&lt;p&gt;Most teams hit a predictable set of issues.   &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Runtime failure: sql -mcp does not start&lt;/strong&gt;  &lt;/p&gt;

&lt;p&gt;Check the absolute SQLcl path, confirm Java is available, and run sql -mcp outside Claude first. Resolve runtime first before checking assistant behavior.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Discovery failure: Claude does not see tools&lt;/strong&gt;  &lt;/p&gt;

&lt;p&gt;Check the Claude Desktop JSON, confirm the configured command points to the SQLcl executable, and restart Claude Desktop after edits. If the server starts in a terminal but not from Claude, treat it as a config or environment-path problem.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Connection failure: tools are present but queries fail immediately&lt;/strong&gt;  &lt;/p&gt;

&lt;p&gt;Check the saved SQLcl connection alias, confirm the profile lives under the expected SQLcl connection store, and verify password persistence for the MCP workflow. Then test the same connection outside Claude.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Permission failure: queries execute selectively and fail on specific objects&lt;/strong&gt;  &lt;/p&gt;

&lt;p&gt;Check the database role first. A selective failure can be the right outcome when least privilege is working. Add grants intentionally, prefer schema allowlists, and keep read-write access separate from the initial validation path.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Retrieval quality failure: answers are fluent but weakly grounded&lt;/strong&gt;  &lt;/p&gt;

&lt;p&gt;Inspect the retrieved records before blaming the model. Check chunk size, metadata filters, embedding choice, top-k settings, and whether the query is asking for exact history, semantic similarity, or operational logs.  &lt;/p&gt;




&lt;h2&gt;
  
  
  Why the hybrid model is a strong long-term default
&lt;/h2&gt;

&lt;p&gt;  &lt;br&gt;
By this point, you’ve probably noticed a pattern: no single layer handles both execution and memory well.  &lt;/p&gt;

&lt;p&gt;Trying to force everything into the assistant gets messy fast. You either lose control over execution, or you end up stuffing too much context into prompts just to keep things working. On the other side, if you only build backend memory systems, you lose the speed and usability that makes assistants useful in the first place.  &lt;/p&gt;

&lt;p&gt;The hybrid approach works because it doesn’t try to solve everything in one place:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Execution stays controlled through MCP.  &lt;/li&gt;
&lt;li&gt;Memory stays durable and queryable in the database.  &lt;/li&gt;
&lt;li&gt;The two are connected where needed, not tightly coupled.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In real teams, this usually evolves over time. It starts simple: Claude with SQLcl MCP, read-only access, and basic workflows. Once people start relying on it, the gaps show up: we lose context, we can’t trace what happened, or we are repeating work.  &lt;/p&gt;

&lt;p&gt;That’s when it makes sense to introduce Oracle AI Agent Memory and retrieval. Not earlier. &lt;/p&gt;

&lt;p&gt;The goal isn’t to build perfect architecture upfront. It’s to add structure where the system starts to break. &lt;/p&gt;




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

&lt;p&gt;  &lt;br&gt;
Setting up Claude with SQLcl MCP works well when treated as an architectural pattern, not just a series of setup steps.  Each layer has one job: Claude handles intent, MCP enforces the execution boundary, Oracle AI Database stores durable memory records and audit data, and LangChain handles retrieval orchestration where needed. &lt;/p&gt;

&lt;p&gt;With clear execution boundaries and durable memory, you can trace what happened, understand failures, and evolve the workflow without introducing hidden behaviour. &lt;/p&gt;

&lt;p&gt;That shift, from implicit access and adhoc context to explicit boundaries and durable memory, is what moves AI-assisted workflows from experiments to operational systems. &lt;/p&gt;




&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;p&gt;  &lt;br&gt;
&lt;strong&gt;What is MCP in this context?&lt;/strong&gt;  &lt;/p&gt;

&lt;p&gt;MCP is a protocol that lets Claude call explicit tools exposed by a server, rather than accessing systems implicitly.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why use SQLcl for Oracle MCP?&lt;/strong&gt;  &lt;/p&gt;

&lt;p&gt;SQLcl already understands Oracle workflows and can run as MCP server with sql -mcp, making integration practical and direct.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is this setup only for Claude Desktop?&lt;/strong&gt;  &lt;/p&gt;

&lt;p&gt;No. The same MCP and memory architecture concepts can be reused with other MCP-capable clients and backend services.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why include Oracle AI Database if MCP already works?&lt;/strong&gt;  &lt;/p&gt;

&lt;p&gt;MCP handles execution boundaries. Oracle AI Database handles durable memory records, retrieval, concurrency, and database access controls. Claude’s own memory helps within a session, but it is not designed as an application memory layer.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What versions are required for the SQLcl MCP setup?&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;Oracle documents that the SQLcl MCP Server requires Oracle SQLcl 25.2.0 or higher, Oracle JRE 17 or 21, Claude Desktop, and at least one saved SQLcl connection profile with password persistence enabled via -savepwd. Teams should verify the latest compatibility guidance in the official Oracle documentation as MCP support evolves. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where does Oracle AI Agent Memory fit?&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;Oracle AI Agent Memory sits between your application code and Oracle AI Database. The package manages threads, durable memories, scoped retrieval, and context cards, while Oracle AI Database remains the storage and enforcement layer underneath. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where does LangChain fit?&lt;/strong&gt;  &lt;/p&gt;

&lt;p&gt;LangChain is an orchestration layer for tools and retrieval. It can help assemble context and retrieval pipelines, but permissions still belong in the database, infrastructure, and application runtime.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do I need vector search for every use case?&lt;/strong&gt;  &lt;/p&gt;

&lt;p&gt;No. Start with structured memory. Add vector retrieval when paraphrase-heavy or concept-level retrieval becomes important.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I prevent risky SQL operations?&lt;/strong&gt;  &lt;/p&gt;

&lt;p&gt;Use least privilege roles, schema allowlists, read-only access where possible, SQLcl MCP restrict levels, and explicit confirmation workflows for high-impact actions.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can this support audit or compliance needs?&lt;/strong&gt;  &lt;/p&gt;

&lt;p&gt;It can support audit-oriented workflows if tool traces, SQL-level controls, retention policies, and review processes are implemented consistently. Do not treat memory records as the sole authoritative record for regulated or high-impact decisions.  &lt;/p&gt;




&lt;h2&gt;
  
  
  Companion Troubleshooting Appendix
&lt;/h2&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Minimum viable setup:&lt;/strong&gt; SQLcl MCP configured in Claude, one approved Oracle connection, read-only validation, and database-side activity logging.  &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;First checks:&lt;/strong&gt; confirm sql -mcp starts, Claude sees the tools after restart, and the saved SQLcl connection alias resolves.  &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Environment model:&lt;/strong&gt; use separate credentials and policies for dev, test, and prod, with stricter controls as capability expands.  &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Logging model:&lt;/strong&gt; capture tool name, timestamp, thread ID, status, sanitized input/output summaries, and relevant SQLcl MCP log records.  &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Retrieval quality:&lt;/strong&gt; tune chunk size, enrich metadata, review embedding choice, and evaluate retrieval against representative queries.  &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Common anti-pattern:&lt;/strong&gt; expanding tool surfaces before ownership, logging standards, and runbooks are in place.  &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rollout path:&lt;/strong&gt; pilot in dev with read-only access and strong logging, then expand capabilities in controlled phases.  &lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>claude</category>
      <category>oracle</category>
      <category>langchain</category>
      <category>agentmemory</category>
    </item>
    <item>
      <title>Which Agent Memory Approach Is Best for Long Conversations?</title>
      <dc:creator>Anya Summers</dc:creator>
      <pubDate>Tue, 23 Jun 2026 17:01:51 +0000</pubDate>
      <link>https://dev.to/oracledevs/which-agent-memory-approach-is-best-for-long-conversations-1me4</link>
      <guid>https://dev.to/oracledevs/which-agent-memory-approach-is-best-for-long-conversations-1me4</guid>
      <description>&lt;p&gt;&lt;strong&gt;How sliding windows, summaries, vector retrieval, structured memory, episodic memory, and memory managers work together to support long AI agent conversations.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Companion notebook: &lt;a href="https://github.com/oracle-devrel/oracle-ai-developer-hub/blob/main/notebooks/oracle_agent_memory_long_conversations.ipynb" rel="noopener noreferrer"&gt;Agent Memory for Long Conversations with Oracle AI Database&lt;/a&gt;&lt;/p&gt;




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

&lt;p&gt;Long conversations are continuity problems.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The best practical pattern is hybrid layered memory: recent context, summaries, vector retrieval, structured memory, episodic memory, and a memory manager.&lt;/li&gt;
&lt;li&gt;Sliding window memory keeps recent turns available, but older context still falls out.&lt;/li&gt;
&lt;li&gt;Summarization compresses older dialogue, but it can lose details or drift.&lt;/li&gt;
&lt;li&gt;Vector retrieval finds semantically related context, but similarity is not the same as relevance.&lt;/li&gt;
&lt;li&gt;Structured memory stores stable facts, preferences, entities, decisions, and state.&lt;/li&gt;
&lt;li&gt;Episodic memory preserves important events, outcomes, and prior attempts.&lt;/li&gt;
&lt;li&gt;A memory manager decides what gets stored, updated, retrieved, summarized, and passed into the model.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/" rel="noopener noreferrer"&gt;Oracle AI Database&lt;/a&gt; becomes useful when long-conversation memory needs durable storage, relational precision, vector retrieval, JSON metadata, and governed access patterns.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Practical Pattern
&lt;/h2&gt;

&lt;p&gt;For long AI agent conversations, the most reliable pattern is hybrid layered memory. In practice, that means each memory layer has a specific job:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keep the latest turns available as recent context.&lt;/li&gt;
&lt;li&gt;Summarize older dialogue so the model does not need the full transcript every time.&lt;/li&gt;
&lt;li&gt;Use vector retrieval when the user refers back to older context with different wording.&lt;/li&gt;
&lt;li&gt;Store stable facts, preferences, decisions, and state in structured memory.&lt;/li&gt;
&lt;li&gt;Preserve important events, outcomes, and prior attempts as episodic memory.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The memory manager sits above those layers and decides what gets written, updated, retrieved, summarized, and passed into the model for the current turn. &lt;a href="https://github.com/oracle-devrel/oracle-ai-developer-hub/blob/main/notebooks/oracle_agent_memory_long_conversations.ipynb" rel="noopener noreferrer"&gt;The companion notebook&lt;/a&gt; implements this pattern with Oracle AI Database, &lt;a href="https://docs.oracle.com/en/database/oracle/agent-memory/" rel="noopener noreferrer"&gt;Oracle AI Agent Memory&lt;/a&gt;, and LangChain, but the first idea is vendor-neutral: long conversation memory needs architecture, not just a larger prompt.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Long Conversations Break Simple Chat History
&lt;/h2&gt;

&lt;p&gt;A short chat can usually survive with raw conversation history. The model sees the latest turns, understands what the user is asking, and continues naturally. Long conversations are different because they contain many kinds of information at once:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;temporary details that only matter for the next response;&lt;/li&gt;
&lt;li&gt;durable decisions that should be remembered later;&lt;/li&gt;
&lt;li&gt;user preferences, project facts, and task state;&lt;/li&gt;
&lt;li&gt;tool results, failed attempts, successful outcomes, and follow-up actions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Treating all of that as one long transcript does not scale well. The model either receives too much irrelevant context, misses older details, or depends on a compressed summary that may have lost something important. Long conversation memory needs structure because not every part of a conversation has the same value, lifetime, or retrieval pattern.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Bigger Context Windows Are Not Enough
&lt;/h2&gt;

&lt;p&gt;A bigger context window can delay the problem, but it does not solve it. More context means the model can see more text at once, which is useful for long documents and extended sessions. But it does not answer the harder engineering questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which facts should survive across sessions?&lt;/li&gt;
&lt;li&gt;Which older details are still relevant?&lt;/li&gt;
&lt;li&gt;Which decisions are authoritative?&lt;/li&gt;
&lt;li&gt;Which prior attempts should not be repeated?&lt;/li&gt;
&lt;li&gt;Which memory belongs to this user, this project, or this task?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A bigger context window gives you more room. It does not give you a memory policy. That policy has to come from the application architecture: what to store, what to summarize, what to retrieve, what to trust, and what to pass into the model for a specific turn.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Memory Approaches That Actually Help
&lt;/h2&gt;

&lt;p&gt;Different memory approaches solve different parts of the long conversation problem. The useful framing is not to ask which one is universally best, but which layer should handle which kind of continuity.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Memory approach&lt;/th&gt;
&lt;th&gt;Best for&lt;/th&gt;
&lt;th&gt;Weakness&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Sliding window memory&lt;/td&gt;
&lt;td&gt;Recent turns and immediate continuity&lt;/td&gt;
&lt;td&gt;Older context falls out&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Conversation summary memory&lt;/td&gt;
&lt;td&gt;Compressing older dialogue&lt;/td&gt;
&lt;td&gt;Can lose detail or drift&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vector memory&lt;/td&gt;
&lt;td&gt;Semantic recall across older context&lt;/td&gt;
&lt;td&gt;Similarity is not the same as relevance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Structured memory&lt;/td&gt;
&lt;td&gt;Facts, preferences, entities, decisions, and state&lt;/td&gt;
&lt;td&gt;Requires extraction and update rules&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Episodic memory&lt;/td&gt;
&lt;td&gt;Events, outcomes, prior attempts, and task resumption&lt;/td&gt;
&lt;td&gt;Needs importance and retention rules&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Memory manager&lt;/td&gt;
&lt;td&gt;Coordinating what to store, retrieve, summarize, update, and pass forward&lt;/td&gt;
&lt;td&gt;Adds application logic that must be tested&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The important point is that none of these approaches is enough by itself. A useful long-conversation system combines them, then lets a memory manager decide which pieces are relevant for the current turn.&lt;/p&gt;




&lt;h2&gt;
  
  
  Sliding Window and Summarization for Short-Term Continuity
&lt;/h2&gt;

&lt;p&gt;The first layer is sliding window memory. It keeps the latest turns close to the model so the current exchange remains coherent. If a developer just asked a follow-up question, the model needs the most recent messages to understand the current task and avoid asking for context that was already provided.&lt;/p&gt;

&lt;p&gt;But a sliding window is temporary by design. Once the conversation gets long enough, older context falls out. Summarization helps by compressing older dialogue into a smaller representation, preserving continuity without passing the entire transcript into every request. The tradeoff is that summaries are not perfect memory. They can omit details, merge separate ideas, or drift over time. In practice, summaries work best when they are supported by more precise layers, especially structured memory and episodic memory.&lt;/p&gt;




&lt;h2&gt;
  
  
  Vector Retrieval for Long-Term Semantic Recall
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/ai.html" rel="noopener noreferrer"&gt;Oracle AI Vector Search&lt;/a&gt; helps when the user refers to older context with different wording. For example, the user might ask, “Earlier we debugged this issue. What did we decide, and what should I try next?” That question does not repeat every detail from the earlier debugging work. A vector memory layer can still retrieve related chunks about the root cause, the decision, the failed patch, and the rollout plan.&lt;/p&gt;

&lt;p&gt;Vector retrieval is especially useful for recall across sessions, paraphrased follow-up questions, large conversation histories, and knowledge that is easier to find by meaning than by exact keyword. But it should not be the only memory layer. Semantic similarity is not the same as correctness. A retrieved chunk can be related but outdated, incomplete, or less authoritative than a structured decision record.&lt;/p&gt;




&lt;h2&gt;
  
  
  Structured Memory for Facts, Preferences, and State
&lt;/h2&gt;

&lt;p&gt;Structured memory stores information that should be precise. This includes user preferences, project facts, entities, decisions, task state, configuration choices, and metrics to monitor. These are not just pieces of text; they are records the application may need to query, update, validate, and govern.&lt;/p&gt;

&lt;p&gt;In the &lt;a href="https://github.com/oracle-devrel/oracle-ai-developer-hub/blob/main/notebooks/oracle_agent_memory_long_conversations.ipynb" rel="noopener noreferrer"&gt;companion notebook&lt;/a&gt;, structured memory includes project state, decisions, metrics, and preferences. For example, it stores the decision to use a region-specific inventory lock timeout, the project state that EU payment authorization latency exceeded the existing timeout, and the metric to monitor expired inventory locks by region. This kind of memory helps the memory manager prefer authoritative facts over loosely related retrieved chunks.&lt;/p&gt;




&lt;h2&gt;
  
  
  Episodic Memory for What Happened and Why It Mattered
&lt;/h2&gt;

&lt;p&gt;Episodic memory stores important events and outcomes. It matters for long conversations because agents often need to resume work, explain prior decisions, or avoid repeating failed attempts. A fact says what is true. An episode says what happened, what changed, and why it mattered.&lt;/p&gt;

&lt;p&gt;In the notebook, episodic memory stores events such as a rejected global patch, an EU-only patch that passed staging, and an agreed rollout plan. If the developer later asks what to try next, the agent should know that the global patch already failed and that the EU-only patch passed staging. That is the difference between remembering text and remembering progress.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Best Pattern: Hybrid Layered Memory
&lt;/h2&gt;

&lt;p&gt;The best pattern for long conversation memory is a layered architecture. Recent context keeps the current exchange coherent. Summaries compress older dialogue. Vector retrieval brings back semantically related information. Structured memory preserves stable facts and decisions. Episodic memory records what happened and what was tried.&lt;/p&gt;

&lt;p&gt;The memory manager coordinates the layers. That coordination is what turns memory from a pile of stored text into a usable system.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0qzpy0lk8n2kjour8tch.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0qzpy0lk8n2kjour8tch.png" alt=" " width="800" height="476"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  How a Memory Manager Assembles Context for Each Turn
&lt;/h2&gt;

&lt;p&gt;A memory manager should not blindly stuff every stored item into the prompt. For each turn, it should decide:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;which recent turns to include;&lt;/li&gt;
&lt;li&gt;whether the rolling summary is needed;&lt;/li&gt;
&lt;li&gt;which structured facts and episodic events matter;&lt;/li&gt;
&lt;li&gt;which retrieved chunks are useful;&lt;/li&gt;
&lt;li&gt;what should be stored or updated after the response.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Example context package:
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;context_package&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;question&lt;/span&gt;&lt;span class="sh"&gt;"&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;recent_context&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;recent_turns&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rolling_summary&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;structured_memory&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;structured_memory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;episodic_memory&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;episodic_memory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;retrieved_memory&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;retrieved_memory&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;This shape is easier to inspect than a giant prompt. If the answer is wrong, developers can debug the context package: was the summary stale, did retrieval miss the right memory, was the structured decision missing, or did the episodic log omit a failed attempt?&lt;/p&gt;




&lt;h2&gt;
  
  
  Handling Memory Conflicts and Freshness
&lt;/h2&gt;

&lt;p&gt;Layered memory introduces a new engineering question: what happens when memory layers disagree?&lt;/p&gt;

&lt;p&gt;For example, a rolling summary might preserve an older plan, while structured memory contains the final decision. A vector search result might retrieve a semantically related note that is no longer current. An episodic memory entry might show that a previous attempt failed, even if the latest summary does not mention it.&lt;/p&gt;

&lt;p&gt;A reliable memory manager should treat memory as evidence, not as a flat transcript. Useful conflict and freshness rules include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;prefer structured decisions over summaries when both refer to the same fact;&lt;/li&gt;
&lt;li&gt;prefer newer memory when two records have the same authority;&lt;/li&gt;
&lt;li&gt;prefer scoped memory over generic memory, such as project-specific or region-specific records;&lt;/li&gt;
&lt;li&gt;downgrade retrieved chunks that are old, superseded, or weakly related to the current task;&lt;/li&gt;
&lt;li&gt;keep source, timestamp, scope, and memory type metadata with each memory record;&lt;/li&gt;
&lt;li&gt;mark important records as active, superseded, rejected, or archived instead of deleting context too early.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This makes long-conversation memory easier to inspect. If the agent gives the wrong answer, developers can check which memory layer supplied the evidence and why that evidence was selected.&lt;/p&gt;




&lt;h2&gt;
  
  
  Making the Memory Manager Concrete
&lt;/h2&gt;

&lt;p&gt;A memory manager is not just a helper that collects context. It is the policy layer for memory.&lt;/p&gt;

&lt;p&gt;For each turn, the memory manager can rank candidate memories using simple rules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;recent turns explain the current exchange;&lt;/li&gt;
&lt;li&gt;structured decisions are usually more precise than summaries;&lt;/li&gt;
&lt;li&gt;episodic memory is useful when the user asks about prior attempts, outcomes, or what to try next;&lt;/li&gt;
&lt;li&gt;vector results are useful when they pass a similarity threshold and match the current thread or task scope;&lt;/li&gt;
&lt;li&gt;stale or superseded memories should be excluded unless they explain why a previous path should not be repeated.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A simple priority order could look like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Current user message&lt;/li&gt;
&lt;li&gt;Recent conversation turns&lt;/li&gt;
&lt;li&gt;Active structured decisions and project state&lt;/li&gt;
&lt;li&gt;Relevant episodic events&lt;/li&gt;
&lt;li&gt;Rolling summary&lt;/li&gt;
&lt;li&gt;Vector-retrieved chunks&lt;/li&gt;
&lt;li&gt;Archived or superseded memory only when needed for explanation&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The exact policy depends on the application, but the principle is consistent: the memory manager should assemble the smallest useful context package that is current, scoped, and explainable.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where a Database-Backed Memory Layer Fits
&lt;/h2&gt;

&lt;p&gt;The first half of this architecture is intentionally vendor-neutral. Any serious long-conversation agent needs memory layers and a memory manager. Once memory needs to survive beyond a single session, a database-backed layer becomes useful because the system needs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;durable storage and queryable history;&lt;/li&gt;
&lt;li&gt;structured facts and state;&lt;/li&gt;
&lt;li&gt;vector retrieval and JSON metadata;&lt;/li&gt;
&lt;li&gt;timestamps, status fields, and policy metadata for freshness and conflict handling;&lt;/li&gt;
&lt;li&gt;user, thread, and task scoping;&lt;/li&gt;
&lt;li&gt;access controls and auditability.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is where Oracle AI Database fits naturally. It can store relational memory, JSON metadata, episodic logs, and vector-searchable chunks in one governed layer. The point is not that every application needs the same table names. The point is the separation of responsibilities.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkxzfqjfrtb81czljfqyu.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkxzfqjfrtb81czljfqyu.png" alt=" " width="800" height="488"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What the Companion Notebook Demonstrates
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://github.com/oracle-devrel/oracle-ai-developer-hub/blob/main/notebooks/oracle_agent_memory_long_conversations.ipynb" rel="noopener noreferrer"&gt;companion notebook&lt;/a&gt; implements the layered pattern end to end. It demonstrates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;every message stored in conversational memory;&lt;/li&gt;
&lt;li&gt;a rolling summary per thread;&lt;/li&gt;
&lt;li&gt;project state and decisions stored as structured memory;&lt;/li&gt;
&lt;li&gt;important events stored with timestamps and outcomes;&lt;/li&gt;
&lt;li&gt;retrieval chunks and Oracle vector search when available;&lt;/li&gt;
&lt;li&gt;a context package assembled for a follow-up question from older conversation history;&lt;/li&gt;
&lt;li&gt;a package-level validation path for &lt;a href="https://docs.oracle.com/en/database/oracle/agent-memory/26.4/agmea/api/agentmemory.html" rel="noopener noreferrer"&gt;oracleagentmemory&lt;/a&gt;, including creating a thread, writing memories, and searching them back.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The example follow-up question is:
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Earlier we debugged this issue.
What did we decide, and what should I try next?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The notebook stores enough memory to answer that question without relying only on the latest chat turns. It also shows Oracle AI Agent Memory as a higher-level package workflow and LangChain as an interoperability layer.&lt;/p&gt;




&lt;h2&gt;
  
  
  Building the Oracle-Backed Memory Workflow
&lt;/h2&gt;

&lt;p&gt;The notebook stores each memory layer in Oracle AI Database. Recent context is retrieved with a bounded query so the model receives the latest turns without carrying the full transcript.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example recent-context query:
&lt;/h2&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;turn_id&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;span class="n"&gt;content&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;lcam_conversation_memory&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;thread_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;thread_id&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;turn_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;6&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;Structured memory is stored separately from raw messages so facts, decisions, preferences, and project state can be updated and queried directly.&lt;/p&gt;

&lt;p&gt;Example structured-memory insert:&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;lcam_structured_memory&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;memory_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;memory_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;memory_value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scope_json&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="mi"&gt;1&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="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="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="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Vector retrieval can use Oracle vector search when the database supports it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example vector retrieval query:
&lt;/h2&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;chunk_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="nb"&gt;text&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;lcam_vector_memory&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;thread_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;thread_id&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;query_embedding&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;6&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;The notebook first stores retrieval chunks as inspectable memory records, then creates vector-searchable memory when Oracle VECTOR support is available. The query uses &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/vecse/vector_distance.html" rel="noopener noreferrer"&gt;VECTOR_DISTANCE&lt;/a&gt; to rank candidate chunks by distance from the query embedding. The snippets are intentionally small so the architecture stays visible. The notebook carries the full executable workflow and the real database results.&lt;/p&gt;




&lt;h2&gt;
  
  
  Oracle AI Agent Memory as a Higher-Level Memory API
&lt;/h2&gt;

&lt;p&gt;The custom tables in the notebook make the memory mechanics visible. Oracle AI Agent Memory provides a higher-level package interface for working with threads, memory records, and retrieval on top of Oracle AI Database. That is useful when a team wants the benefits of persistent memory without rebuilding every memory component from scratch.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/oracle-devrel/oracle-ai-developer-hub/blob/main/notebooks/oracle_agent_memory_long_conversations.ipynb" rel="noopener noreferrer"&gt;companion notebook&lt;/a&gt; also validates the oracleagentmemory package path by creating a thread, writing durable memories, and searching those memories back. That package-level proof is important because the table-level walkthrough explains the architecture, while Oracle AI Agent Memory shows the application-facing API path developers can use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example Oracle AI Agent Memory workflow:
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;agent_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;thread_id&lt;/span&gt;&lt;span class="o"&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;agent_memory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_memory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;EU checkout timeout decision: use 12 seconds for EU and 5 seconds for US.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&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;thread_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;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;agent_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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;What timeout did we choose for EU?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&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;thread_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;exact_thread_match&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&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;This higher-level API belongs after the architecture is understood. It should not hide the core design question: which memory should be stored, updated, retrieved, and trusted for the current turn?&lt;/p&gt;




&lt;h2&gt;
  
  
  Where LangChain Fits
&lt;/h2&gt;

&lt;p&gt;LangChain can help once the memory layer is working. It is useful for orchestration, document wrapping, retriever interfaces, and repeatable application flows. It should not replace database privileges, memory policy, or observability.&lt;/p&gt;

&lt;p&gt;In the notebook, retrieved Oracle-backed memory is converted into LangChain Document objects so the same memory layer can participate in LangChain-style application flows.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example LangChain document wrapping:
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;documents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nc"&gt;Document&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;page_content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;metadata&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;source&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="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;score&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;float&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="n"&gt;score&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;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;retrieved_memory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;itertuples&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;For Oracle-backed retrieval pipelines, &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/23/vecse/oracle-ai-vector-search-integration-langchain.html" rel="noopener noreferrer"&gt;Oracle AI Vector Search integration with LangChain&lt;/a&gt; gives developers a bridge between LangChain and Oracle AI Database.&lt;/p&gt;




&lt;h2&gt;
  
  
  Practical Recommendation for Developers
&lt;/h2&gt;

&lt;p&gt;Use the simplest memory layer that solves the problem, but do not pretend one layer solves everything. Short chats may only need a sliding window. Long linear chats usually need a sliding window plus summaries. Recall across sessions needs vector retrieval. Correct preferences and profile facts need structured memory. Task resumption needs episodic memory. Production-grade continuity needs hybrid layered memory with a memory manager.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Recommended approach&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Short chats&lt;/td&gt;
&lt;td&gt;Sliding window memory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Long linear chats&lt;/td&gt;
&lt;td&gt;Sliding window plus summaries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Recall across sessions&lt;/td&gt;
&lt;td&gt;Vector retrieval&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Correct preferences and profile facts&lt;/td&gt;
&lt;td&gt;Structured memory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Task resumption&lt;/td&gt;
&lt;td&gt;Episodic memory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reliable long-term continuity&lt;/td&gt;
&lt;td&gt;Hybrid layered memory with a memory manager&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A practical rollout is straightforward:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Store every message so the raw conversation can be inspected.&lt;/li&gt;
&lt;li&gt;Keep a bounded recent context window and add a rolling summary for older dialogue.&lt;/li&gt;
&lt;li&gt;Extract structured memory for facts, preferences, decisions, and state.&lt;/li&gt;
&lt;li&gt;Store episodic memory for important events and prior attempts.&lt;/li&gt;
&lt;li&gt;Add vector retrieval for semantic recall.&lt;/li&gt;
&lt;li&gt;Use a memory manager to assemble context for each turn.&lt;/li&gt;
&lt;li&gt;Move to a database-backed memory layer when memory needs to be durable, queryable, shared, and governed.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Long conversations are not solved by one memory technique. A bigger context window, raw chat history, summaries, vector retrieval, structured facts, and episodic logs each solve part of the problem. The best pattern is hybrid layered memory coordinated by a memory manager.&lt;/p&gt;

&lt;p&gt;Oracle AI Database provides a durable implementation layer for that pattern when teams need relational precision, vector retrieval, JSON metadata, and governed access. Oracle AI Agent Memory and LangChain can then sit above that layer when developers need higher-level APIs or orchestration. The goal is not to keep making prompts larger. The goal is to make memory inspectable, retrievable, updateable, and reliable.&lt;/p&gt;

&lt;p&gt;Run the &lt;a href="https://github.com/oracle-devrel/oracle-ai-developer-hub/blob/main/notebooks/oracle_agent_memory_long_conversations.ipynb" rel="noopener noreferrer"&gt;companion notebook&lt;/a&gt; to see the pattern stored, retrieved, scoped, and validated in Oracle AI Database, including the oracleagentmemory package workflow.&lt;/p&gt;




&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What is the best memory approach for long conversations?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Hybrid layered memory: recent context, summaries, vector retrieval, structured memory, episodic memory, and a memory manager.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is a larger context window enough?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No. It gives the model more room, but it does not define what should be stored, retrieved, updated, or trusted.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is conversation summary memory good for?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It compresses older dialogue so the model can keep continuity without receiving the full transcript.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is vector memory good for?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Vector memory helps retrieve semantically related context, especially when users ask follow-up questions with different wording.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is structured memory good for?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Structured memory stores stable facts, preferences, entities, decisions, and state.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is episodic memory good for?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Episodic memory stores important events, outcomes, and prior attempts, which helps with task resumption.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What does a memory manager do?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It decides what gets stored, updated, retrieved, summarized, and passed into the model for each turn.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where does Oracle AI Database fit?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It provides the durable memory layer for relational memory, JSON metadata, episodic logs, and vector-searchable chunks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where does Oracle AI Agent Memory fit?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It provides a higher-level package API for memory records, threads, and retrieval on top of Oracle AI Database.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where does LangChain fit?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;LangChain can help with orchestration and retriever interfaces after the memory layer is working.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agentmemory</category>
      <category>agents</category>
      <category>oracle</category>
    </item>
  </channel>
</rss>
