<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Kailasj on Medium]]></title>
        <description><![CDATA[Stories by Kailasj on Medium]]></description>
        <link>https://medium.com/@kailasj?source=rss-7861b2e8797c------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/0*mPL2YG5SrILrcdL5</url>
            <title>Stories by Kailasj on Medium</title>
            <link>https://medium.com/@kailasj?source=rss-7861b2e8797c------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Thu, 05 Nov 2026 04:25:56 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@kailasj/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Building a Canary Release Pipeline with OCI DevOps (Part I)]]></title>
            <link>https://medium.com/oracledevs/building-a-canary-release-pipeline-with-oci-devops-part-i-cfa6253c3705?source=rss-7861b2e8797c------2</link>
            <guid isPermaLink="false">https://medium.com/p/cfa6253c3705</guid>
            <category><![CDATA[oracle-kubernetes-engine]]></category>
            <category><![CDATA[devops]]></category>
            <category><![CDATA[oraclecloudinfrastructure]]></category>
            <category><![CDATA[oracle-cloud]]></category>
            <dc:creator><![CDATA[Kailasj]]></dc:creator>
            <pubDate>Wed, 04 Nov 2026 14:45:40 GMT</pubDate>
            <atom:updated>2026-11-04T14:45:40.455Z</atom:updated>
            <content:encoded><![CDATA[<h4>Part I: Build a private Python API foundation on OCI</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*waKxpydtdo5yEl23aLWLtA.png" /></figure><h3>Key Takeaways</h3><ul><li>Build a private Python API foundation using FastAPI, OCI Kubernetes Engine, Terraform, and OCI DevOps.</li><li>Track the serving release through version, commit, and environment metadata, preparing for canary verification.</li><li>Package repeatable containers with pinned dependencies, unique release identifiers, and restricted runtime permissions.</li><li>Use OCI Bastion for temporary administrative access while keeping the cluster and application private.</li><li>Connect source, images, deployment manifests, and delivery permissions ahead of the canary rollout in Part II.</li></ul><p>A successful container build is only one part of releasing an application. We also need somewhere to run it, a controlled way to reach it, and a way to identify the version answering our requests.</p><p>We built a small Python API and developer portal on Oracle Cloud Infrastructure (OCI), with a private Oracle Kubernetes Engine (OKE) cluster and OCI DevOps delivery services. This post explains the foundation. <a href="02-oci-devops-canary-release.md">Part 2</a> follows a commit through testing, a 10% canary rollout, approval, and production promotion.</p><p><strong>Naming note:</strong> Resource names such as demo-compartment, demo-production, and demo-canary are illustrative pseudonyms. Actual compartment, region, tenancy, repository, cluster, identity, network-address, and release identifiers are omitted.</p><h3>Choose a small, private deployment</h3><p>We placed the resources in a dedicated compartment, represented here as demo-compartment, within one OCI region. A compartment groups resources and provides a scope for access policies. Keeping regional resources together simplifies the demo&#39;s connectivity.</p><p>Our OKE Basic cluster used one <strong>VM.Standard.E4.Flex worker with 1 OCPU and 4 GB RAM</strong>, plus a 50 GB boot volume. This was sufficient for our stateless application’s production, candidate, and router pods.</p><p>That is a demonstration footprint, not a general sizing rule. The workloads share one worker, so a worker failure affects all of them. Namespace separation does not provide host redundancy.</p><p>We kept the Kubernetes API, worker, and application private. OCI Bastion provided temporary administrative access from an allowlisted client address.</p><h3>Make the application identify itself</h3><p>We used <strong>FastAPI</strong> to handle HTTP requests and <strong>Uvicorn</strong> to run the application server. The same application serves the portal page and API endpoints, keeping the demo to one application image.</p><p>The portal describes the delivery path and displays the serving revision. Pipeline execution and approval records remain in OCI DevOps.</p><p>EndpointWhat it tells us/The portal is reachable/api/helloThe example API contract behaves correctly/healthzThe application process responds/readyzThe application is ready for traffic/api/releaseWhich version, commit, and environment answered/docsThe API contract can be explored interactively</p><p>Liveness and readiness serve different purposes. Repeated liveness failures can cause Kubernetes to restart a container. Readiness failures exclude an unready pod from normal Service traffic.</p><p>Our readiness signal follows application startup and shutdown. There is no database or other application dependency to check. If we added one, we would need to reconsider the readiness contract.</p><p>Release metadata becomes especially useful when two versions run simultaneously. Both may return HTTP 200, but only one is the candidate. The release endpoint reports version, commit, and environment, allowing us to attribute each response without exposing credentials.</p><p>The image records the source and version; deployment configuration supplies the environment. This creates a traceable connection from source to the request we observe.</p><h3>Package a repeatable container</h3><p>A container image packages the application, runtime, and dependencies. A container is a running instance of that image. Kubernetes runs containers inside pods.</p><p>Our Dockerfile starts with a digest-pinned Python 3.12 base image and installs dependencies with package-hash verification. It copies in the application, selects a non-root user, and starts Uvicorn on port 8080.</p><p>Pinning versions and hashes controls what enters the build. It also means updates must be deliberate: a pinned dependency does not update itself when a fix becomes available.</p><p>Each build creates a version using this structure:</p><pre>&lt;source-commit-prefix&gt;-&lt;generated-build-suffix&gt;</pre><p>The version connects the image tag, manifest artifact, and application metadata. Separate builds receive distinct identifiers, and deployment avoids latest.</p><p>We verified execution with a read-only root filesystem, dropped Linux capabilities, and no privilege escalation. These restrictions reduce what the process can change and are also reflected in the OKE workload configuration.</p><p>A tag names an image reference; a digest identifies image content. We later compared the running production digest with OCIR metadata. This provided evidence beyond the application’s own version string.</p><p>The container repository did not accept enforced immutability in this exercise. We used unique tags and restricted writers; immutable versions were enforced for the separate manifest repository.</p><h3>Build the private foundation with Terraform</h3><p>Terraform describes both the infrastructure and delivery services. A plan previews proposed changes, an apply carries them out, and later plans reveal differences between configuration and deployed resources.</p><p>We separated the network and OKE configuration from the DevOps configuration so their responsibilities remained clear.</p><p>The private network used an isolated Virtual Cloud Network (VCN), with subnets for the Kubernetes API, workers, and access resources. Outbound connectivity came from two gateways:</p><p>ComponentRoleNAT gatewayAllows private resources to initiate internet connections without public worker addressesService gatewayProvides access to supported OCI services through the OCI service network</p><p>Network rules constrain those paths. Outbound connectivity supports image retrieval and service communication without publishing the application for inbound internet traffic.</p><p>OKE manages the Kubernetes control plane, while the worker supplies CPU and memory for pods. We organized workloads into namespaces represented here as demo-production, demo-canary, and demo-router. These names will also appear in the release discussion.</p><h3>Reach OKE through OCI Bastion</h3><p>The private Kubernetes API cannot be reached directly from a laptop across the public internet. We created a time-limited OCI Bastion session restricted to one allowlisted client address.</p><p>An SSH tunnel forwarded a laptop port to the private Kubernetes API. Through that connection, kubectl port-forward forwarded a second local port to the router Service.</p><pre>Laptop browser<br>      ↓ local forwarded port<br>kubectl port-forward<br>      ↓ API connection through Bastion<br>Private OKE router Service<br>      ↓<br>Application Service and pod</pre><p>The browser used localhost, but the application ran in OCI. A dedicated kubeconfig kept the connection separate from existing Kubernetes contexts, and API certificate verification remained enabled.</p><p>Access requires an active Bastion session and forwarding processes. This is a temporary administrative access path rather than a permanent application URL.</p><h3>Connect the OCI delivery services</h3><p>The delivery foundation gives each artifact and operation a home:</p><p>OCI resourceResponsibilityDevOps project and Code RepositoryGroup delivery resources and store sourceManaged build pipelineTest source and build release artifactsPrivate OCI Container Registry (OCIR)Store application imagesGeneric Artifact repositoryStore versioned deployment manifestsOKE deployment environmentIdentify the target cluster and connectivityBaseline and canary pipelinesEstablish production and release later candidates</p><p>The image contains the application; the manifest tells Kubernetes how to run it. It describes the image reference, replicas, probes, security settings, and routing resources. Delivering both lets promotion consume the artifact evaluated during the canary.</p><p>OCI dynamic groups identify authorized pipeline resources, and IAM policies grant their required access. Image pulling uses a separate auth-token-only identity with read access restricted to the application repository. Kubernetes pull secrets provide that credential in both application namespaces, outside the image, source control, and Terraform state.</p><p>The foundation now connects identifiable application code, a versioned image, private compute, controlled access, and delivery permissions. The next step is to prove that a source change can move through all of them.</p><p><strong>Continue to </strong><a href="https://medium.com/oracledevs/building-a-canary-release-pipeline-with-oci-devops-part-ii-4e707b9b619e"><strong>part 2</strong></a><strong>: Release through an OCI DevOps canary</strong></p><h3>FAQs</h3><h3>What does Part I cover?</h3><p>Part I establishes the Python application, container image, private OKE infrastructure, administrative access, and OCI delivery services. Part II follows a source change through canary deployment, approval, and production promotion.</p><h3>Is this architecture production-ready?</h3><p>It is a demonstration foundation. All workloads share one worker, so a worker failure affects the application and router. Production use requires additional resilience, capacity planning, and operational validation.</p><h3>How do I access the private application?</h3><p>A time-limited OCI Bastion session provides an SSH tunnel to the private Kubernetes API. From there, kubectl port-forward makes the router Service accessible through localhost while the application remains in OCI.</p><h3>Why does the application expose release metadata?</h3><p>Successful HTTP responses do not identify which version served them. The release endpoint reports the version, commit, and environment so requests can be attributed to the baseline or candidate during a canary rollout.</p><h3>How are application images and deployment manifests different?</h3><p>The image contains the application, runtime, and dependencies. The manifest defines how Kubernetes runs it, including replicas, probes, security settings, and routing. Versioning both supports promotion of the release evaluated during the canary.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=cfa6253c3705" width="1" height="1" alt=""><hr><p><a href="https://medium.com/oracledevs/building-a-canary-release-pipeline-with-oci-devops-part-i-cfa6253c3705">Building a Canary Release Pipeline with OCI DevOps (Part I)</a> was originally published in <a href="https://medium.com/oracledevs">Oracle Developers</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Building a Canary Release Pipeline with OCI DevOps (Part II)]]></title>
            <link>https://medium.com/oracledevs/building-a-canary-release-pipeline-with-oci-devops-part-ii-4e707b9b619e?source=rss-7861b2e8797c------2</link>
            <guid isPermaLink="false">https://medium.com/p/4e707b9b619e</guid>
            <category><![CDATA[oracle-container-registry]]></category>
            <category><![CDATA[devops]]></category>
            <category><![CDATA[oracle-kubernetes-engine]]></category>
            <category><![CDATA[oraclecloudinfrastructure]]></category>
            <category><![CDATA[oracle-cloud]]></category>
            <dc:creator><![CDATA[Kailasj]]></dc:creator>
            <pubDate>Tue, 03 Nov 2026 22:28:22 GMT</pubDate>
            <atom:updated>2026-11-04T14:50:12.311Z</atom:updated>
            <content:encoded><![CDATA[<h4>Part II: Release a Python API through an OCI DevOps canary</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*5NeegX2XRUsTIGk7gwc59Q.png" /></figure><h3>Key Takeaways</h3><ul><li>Build a commit-triggered release pipeline with OCI DevOps: test, package, publish artifacts, and deploy a canary to private OKE.</li><li>Verify which release actually serves requests; healthy pods and a configured traffic weight do not prove the traffic split works.</li><li>Evaluate the candidate with 10% of traffic, then approve promotion using the same release artifacts without rebuilding.</li><li>Retain known-good artifacts for recovery. Deployment-stage rollback does not detect every application regression.</li></ul><p>A delivery pipeline becomes useful when we can follow a source change into a running application, check its behavior, and control its promotion. A green build is a starting point; the serving revision and traffic path provide the next evidence.</p><p>In <a href="https://medium.com/@kailasj/cfa6253c3705">part 1</a>, we built a Python API, packaged it, and established private OKE infrastructure with OCI delivery services. Here we follow the release: baseline deployment, commit-triggered build, 10% canary, approval, and production verification.</p><p><strong>Naming note:</strong> Namespace names such as demo-production and demo-canary are pseudonyms. Actual environment names, locations, network addresses, and release identifiers are omitted. Results describe the exercise verified on October 4, 2026, rather than current cloud health.</p><h3>Establish production before introducing a candidate</h3><p>A canary rollout needs a working production version to receive most requests. We first ran the managed build with automatic canary delivery disabled, then deployed its artifacts through the separate baseline pipeline into demo-production.</p><p>Several Kubernetes objects work together:</p><p>ObjectResponsibilityDeploymentMaintains application pods and coordinates updatesPodRuns the application containerServiceGives ready pods a stable destinationIngressDescribes HTTP routing rulesRouterImplements those rules and forwards requests.</p><p>An Ingress needs a controller to implement its rules. A Service remains stable as individual pods are replaced and their addresses change.</p><p>The private demo runs one application replica per application namespace. Startup, liveness, and readiness probes check the application, and the Service selects ready pods.</p><p>Through the private router, we verified health, readiness, the example API response, and the expected release identity. This established a known working baseline before we enabled the complete commit-triggered path.</p><h3>Turn a source push into a release</h3><p>We enabled the trigger for pushes to main in OCI Code Repository and the build stage that starts canary delivery.</p><p>The resulting path connected source, artifacts, and deployment:</p><pre>Push to main<br>↓<br>Test the API<br>↓<br>Build and smoke-test the container<br>↓<br>Publish image and manifest<br>↓<br>Deploy candidate to OKE<br>↓<br>Shift 10 percent of traffic<br>↓<br>Wait for approval<br>↓<br>Promote the tested release</pre><p>The build specification directed the runner to:</p><ol><li>Install hash-verified dependencies and run API tests.</li><li>Create a unique release version and build the amd64 image.</li><li>Start the image with the intended runtime restrictions.</li><li>Run smoke checks and verify the reported version.</li><li>Render a manifest containing the release’s image reference and metadata.</li></ol><p>OCI pipeline stages then delivered the image to OCIR, delivered the manifest to Artifact Registry, and triggered the deployment pipeline with that version. The verified source push started this process automatically, and every build stage succeeded.</p><p>This connects continuous integration with continuous delivery. Tests and packaging happen automatically, while production promotion retains an approval point.</p><h3>Use native OCI DevOps canary stages</h3><p>The deployment pipeline has four stages:</p><p>StageBehaviorCandidate deploymentApplies the candidate into demo-canaryTraffic shiftAssigns 10% of normal traffic to itApprovalWaits for one recorded approvalProduction deploymentApplies the evaluated release into demo-production</p><p>The established version continues serving most requests while the candidate is checked. The configured traffic-shift batch delay is not an automated error-rate or latency gate; observation remains necessary.</p><p>OCI manages NGINX-style canary annotations. Our private router used Traefik v3.7.13’s NGINX compatibility provider to interpret them. This integration needed verification with live requests.</p><h3>Diagnose the traffic path before approving</h3><p>Our first sample revealed a mismatch: the candidate was healthy and its ingress showed a 10% weight, yet every sampled request reached the baseline.</p><p>The annotation described the desired behavior. The release endpoint revealed the behavior actually occurring.</p><p>Router logs and its implementation showed the cause: this provider matches primary and canary ingress rules within the same namespace. Our initial cross-namespace arrangement did not meet that requirement.</p><p>We changed the routing layout while preserving separate application namespaces:</p><pre>Router watches demo-canary<br>    │<br>    ├── Primary ingress<br>    │       ↓<br>    │   ExternalName Service alias<br>    │       ↓<br>    │   Production Service in demo-production<br>    │<br>    └── Candidate ingress<br>            ↓<br>        Candidate Service in demo-canary</pre><p>An ExternalName Service supplies a DNS alias. Here it points to the production Service’s internal Kubernetes name. It does not expose a public endpoint or move production pods.</p><p>The matching ingress rules now reside together in demo-canary, while OCI manages the candidate ingress and traffic weight. The router&#39;s workload permissions are scoped to that watched namespace.</p><p>This is why ingress compatibility must be tested beyond checking that deployment resources exist. Controller matching rules can determine whether the configured split takes effect.</p><h3>Evaluate the candidate using release identity</h3><p>After correcting the route, we checked candidate health, readiness, API behavior, release identity, and updated portal content. We then sampled the router’s release endpoint:</p><p>Response before approvalCountCandidate20Baseline180HTTP errors0Total requests200</p><p>The observed split matched the configured 10% share. Future samples need not divide exactly the same way.</p><p>We also verified that the redirect-to-canary: always header selected the candidate, allowing deliberate checks through the router without waiting for normal traffic selection.</p><p>Counting successful responses alone would have missed the original problem. Identifying which revision answered demonstrated that the candidate was actually receiving traffic.</p><h3>Approve and promote without rebuilding</h3><p>After the checks passed, we recorded approval in OCI DevOps with the verification reason. OCI reset normal candidate traffic to zero during the transition and continued to production deployment.</p><p>The production stage consumed the delivered release manifest. No new application build occurred between canary evaluation and promotion.</p><p>We verified the resulting production workload through the private router:</p><p>CheckRecorded resultAll four deployment stagesSucceededProduction podReady, with zero restarts at verificationHealth and readinessPassedPost-promotion sample50 of 50 requests served the new production releaseHTTP errors in that sample0Running image digestMatched OCIR metadata</p><p>The digest comparison supplemented the application-reported version with a check of the running image content.</p><h3>Keep recovery and future delivery ready</h3><p>The production stage has automatic rollback configured for deployment-stage failure. Retained baseline artifacts also allow a known-good release to be redeployed without rebuilding.</p><p>These controls have limits. A ready application can contain a business-logic defect. This demo has no automated error-rate or latency analysis gate, and stage rollback cannot detect every regression after a successful deployment. Its traffic samples demonstrate functionality rather than production capacity. The single worker also provides no host redundancy.</p><p>For a later regression, the recovery path is to remove candidate traffic and redeploy a retained known-good release. Deployment runs should not overlap.</p><p>We saved the routing fix and verification record, briefly pausing the trigger for that audit commit. After restoring it, we checked that the trigger was active and Terraform reported no infrastructure drift.</p><p>The verified recurring workflow is <strong>edit → commit → push → OCI build and artifact delivery → private OKE canary → review → approval → production verification</strong>. The key operational habit is to check the revision actually serving requests before deciding a release is ready to promote.</p><h3>FAQs</h3><h3>What do I need before following Part II?</h3><p>Part I establishes the Python API, container packaging, private OKE infrastructure, and OCI delivery services. A working production baseline is required before introducing the canary.</p><h3>Does a 10% canary weight guarantee exactly 10% of requests?</h3><p>No. Individual samples can vary. Check the release identity returned by each request to confirm that both versions receive traffic and the observed split is consistent with the configured weight.</p><h3>Why did the ingress routing need adjustment?</h3><p>The Traefik NGINX compatibility provider used in this exercise matched primary and canary ingress rules within the same namespace. An ExternalName Service alias allowed those rules to sit together while the application workloads remained in separate namespaces.</p><h3>Is the application rebuilt after approval?</h3><p>No. The production stage uses the release manifest and image already evaluated during the canary phase.</p><h3>What happens if a release fails?</h3><p>Automatic rollback is configured for production deployment-stage failure. For a regression discovered after successful deployment, the recovery path is to remove candidate traffic and redeploy retained known-good artifacts. This demo does not include automated error-rate or latency gates.</p><p><strong>Read </strong><a href="https://medium.com/oracledevs/building-a-canary-release-pipeline-with-oci-devops-part-i-cfa6253c3705"><strong>part 1</strong></a><strong>: Build the private OCI foundation for the application and infrastructure design.</strong></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4e707b9b619e" width="1" height="1" alt=""><hr><p><a href="https://medium.com/oracledevs/building-a-canary-release-pipeline-with-oci-devops-part-ii-4e707b9b619e">Building a Canary Release Pipeline with OCI DevOps (Part II)</a> was originally published in <a href="https://medium.com/oracledevs">Oracle Developers</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Building a Document Knowledge Base with Oracle APEX and Oracle AI Vector Search]]></title>
            <link>https://medium.com/oracledevs/building-a-document-knowledge-base-with-oracle-apex-and-oracle-ai-vector-search-394009d991fe?source=rss-7861b2e8797c------2</link>
            <guid isPermaLink="false">https://medium.com/p/394009d991fe</guid>
            <category><![CDATA[agentic-rag]]></category>
            <category><![CDATA[aivectorsearch]]></category>
            <category><![CDATA[oracle-apex]]></category>
            <category><![CDATA[oracle-ai-database]]></category>
            <category><![CDATA[ai-vector-databases]]></category>
            <dc:creator><![CDATA[Kailasj]]></dc:creator>
            <pubDate>Thu, 22 Oct 2026 11:04:58 GMT</pubDate>
            <atom:updated>2026-10-22T11:04:58.239Z</atom:updated>
            <content:encoded><![CDATA[<h4><strong>A document knowledge base with Oracle APEX, Oracle AI Database, AI Vector Search, and RAG</strong></h4><p><em>By Kailas Jawadekar</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*GxHepBaMysjkeLEFlM7uKw.png" /></figure><p>A collection of documents can hold valuable knowledge while still being difficult to use. Finding an answer may require opening several files, searching for related terms, and reconciling different versions. We built a Document Knowledge Base to make that content searchable and reusable through natural-language questions, generated drafts, and document comparisons.</p><p>The Document Knowledge Base combines Oracle APEX, PL/SQL, Oracle AI Database vector storage, and AI inference. Its core workflow is independent of the subject matter: organize a document collection, retrieve relevant passages, and use them to answer questions or create new content. Support articles, developer guides, operational runbooks, and product documentation are all examples of collections suited to this pattern.</p><h3>Key takeaways</h3><ul><li>Ingest DOCX documents into stored files, metadata, text chunks, and embeddings.</li><li>Use Oracle AI Vector Search and RAG to retrieve evidence for each question.</li><li>Ask for source-grounded answers with filenames and headings to support review.</li><li>Generate editable content from retrieved passages and prompt templates.</li><li>Compare selected documents within a bounded context; check coverage before interpreting omissions.</li></ul><p><strong>Review generated results:</strong> tables, long passages, missing embeddings, or limited retrieval can leave evidence out. Check answers, drafts, and comparisons against the originals.</p><h3>Implementation scope</h3><p><strong>Document Knowledge Base is a simple internal project, not a production deployment.</strong> It demonstrates a reusable workflow for finding answers, drafting content, and comparing documents. The collection supplies the subject matter: support guidance, developer documentation, operational procedures, onboarding material, or product knowledge. These broader examples illustrate how the pattern can be adapted; each needs suitable documents, prompts, and coverage checks.</p><figure><img alt="Document Knowledge Base brings document upload, search, content generation, and comparison into one application." src="https://cdn-images-1.medium.com/max/1024/1*twoyTlVVMKT50gXvh267wA.png" /><figcaption><em>`Document Knowledge Base brings document upload, search, content generation, and comparison into one application.</em></figcaption></figure><h3>What users can do</h3><p>The Document Knowledge Base organizes work around three capabilities:</p><ul><li><strong>Ask a question.</strong> Semantic search retrieves relevant passages and generates an answer with source references. For a support collection, a question might be “How do I troubleshoot a connection timeout?”</li><li><strong>Create a draft.</strong> Retrieved content and template instructions guide an output such as a summary, knowledge article, or response draft. The document collection and prompt determine the subject and format.</li><li><strong>Compare documents.</strong> A selected document and baseline provide the evidence for a structured comparison. In a documentation collection, the task might be to summarize changes between two versions.</li></ul><p>The current uploader adds DOCX files and metadata. Administration views show document coverage, ingestion status, and missing embeddings.</p><h3>Use cases for a Document Knowledge Base</h3><p>The collection and task give the knowledge base its purpose. These examples illustrate applications of the same architecture; each requires suitable source content and prompts.</p><figure><img alt="Use cases for a Document Knowledge Base" src="https://cdn-images-1.medium.com/max/1024/1*fwjCV-nDU3pbs187eneZXQ.png" /><figcaption>Use cases for a Document Knowledge Base</figcaption></figure><h3>How the architecture works</h3><p>Oracle APEX supplies the browser interface, forms, and reports. PL/SQL packages extract text, retrieve passages, assemble prompts, and call the AI service. Oracle AI Database provides native vector storage and AI Vector Search alongside relational data. In this implementation, the schema stores original DOCX BLOBs, filenames, dates, hashes, processing status, extracted text, ordered chunks, and embeddings. It also stores search questions and query vectors, generated answers, reusable prompt templates, and assembled prompts and outputs for generated reports and comparisons. The answer package stores answer text, but does not persist its assembled prompt.</p><p>Keeping these records in Oracle AI Database lets SQL join evidence and metadata directly, apply category filters, and retrieve saved generation prompts and outputs without synchronizing a separate vector store. This makes the workflow easier to inspect and maintain; it does not by itself guarantee complete extraction or accurate answers.</p><p>The application has two main flows:</p><pre>Document → Extract text → Split into chunks → Create embeddings → Store<br>Question → Create embedding → Find relevant chunks → Generate answer</pre><p>This is retrieval augmented generation, or RAG. The model receives selected source passages with each request; we do not train it on the uploaded documents. Keeping vectors alongside relational data lets SQL combine semantic ranking with metadata filters and explicit document selection.</p><p>Our AI wrapper is configured for OCI Generative AI endpoints using OpenAI-compatible embedding and chat request formats. Endpoint URLs and model identifiers are configuration values. API secrets belong in APEX Web Credentials and are referenced by the REST call, keeping them out of application code. The checked-in endpoint defaults and model placeholders are setup values, not proof that those combinations are currently supported. Confirm both embedding and chat access in the target environment using the service’s current documentation.</p><h3>Prerequisites and setup</h3><ul><li><strong>Oracle APEX and database support.</strong> Use an APEX workspace and parsing schema on an Oracle Database environment that supports VECTOR, TO_VECTOR, and VECTOR_DISTANCE. Verify the installed APEX and database versions; the source does not establish a tested minimum APEX release. The custom packages use APEX_ZIP, APEX_JSON, and APEX_WEB_SERVICE.</li><li><strong>OCI Generative AI access.</strong> Configure reachable HTTPS embedding and chat endpoints, authorized model IDs, regional availability, and the required service permissions. Set EMBEDDINGS_URL, EMBEDDING_MODEL, CHAT_URL, and CHAT_MODEL. Test both calls; an OpenAI-compatible chat endpoint does not automatically imply compatible embedding support. The current vector columns and conversion expect 1,536 dimensions, so check the embedding-dimension diagnostic before ingestion.</li><li><strong>APEX Web Credentials.</strong> For the project’s bearer-token setup, create an HTTP Header credential with header Authorization and secret value Bearer &lt;API key&gt;, restricted with <strong>Valid for URLs</strong> to the intended service prefixes. Set WEB_CREDENTIAL_STATIC_ID to that credential’s Static ID. The later project build instructions use credentials-for-standards-ai, while the original seed uses openai-api-key; match the active workspace credential rather than copying either blindly. Configure outbound network access as required by the database environment.</li><li><strong>Schema, pages, and a small DOCX trial.</strong> Install the schema and ingestion, AI, search, answer, template, report, and comparison packages, check compilation, and connect the APEX page processes. Start with a representative DOCX and inspect extracted text, chunks, and embeddings before testing answers. The supplied source is not a complete, verified one-click APEX deployment. The current uploader is DOCX-only; PDF, HTML, Markdown, and images need additional parsers.</li></ul><p>See Oracle’s <a href="https://docs.oracle.com/en/database/oracle/apex/26.1/htmdb/understanding-web-credentials.html">Web Credentials guidance</a> and <a href="https://docs.oracle.com/en-us/iaas/Content/generative-ai/api-keys.htm">OCI Generative AI API key and endpoint documentation</a> for the supported authentication and service configuration.</p><h3>Turning documents into searchable content</h3><p>When a user uploads a DOCX file, the application retains the original file and calculates a SHA-256 hash to detect an identical upload. It uses APEX_ZIP and XMLTABLE to extract body paragraphs, detects headings, and groups text into chunks of approximately 5,000 characters. Each chunk retains its heading and connection to the source document.</p><p><strong>Extraction limitation:</strong> the XPath reads direct body paragraphs, not paragraphs inside table cells. The helper takes at most 200 characters per XML text run and caps the assembled paragraph at 3,000 bytes. Long passages can therefore be incomplete before chunking; the original DOCX remains the reference for checking coverage. Chunks have no overlap.</p><p>The application creates an embedding for each chunk and stores it as VECTOR(1536, FLOAT32). An embedding is a list of numbers representing the text in a form that supports similarity comparisons. Related passages can be close together even when they use different words. For example, “unable to sign in” may match a passage about “authentication failures.”</p><p>The embedding function sends text to the configured model, then converts the returned JSON array into an Oracle vector. This excerpt shows the conversion inside our PL/SQL package:</p><pre>l_response := embedding_response(p_text);<br>l_embedding_json := json_query(<br>  l_response, &#39;$.data[0].embedding&#39; returning clob);</pre><pre>select to_vector(l_embedding_json, 1536, float32)<br>  into l_vector<br>  from dual;</pre><p>embedding_response builds the JSON request with the configured embedding model and input text. Document chunks and questions use the same embedding model so their vectors can be compared.</p><h3>Finding evidence and generating an answer</h3><p>For each search, we embed the question once and store its vector. SQL ranks document chunks by cosine distance: a smaller distance means greater semantic similarity. The simplified query below shows the core operation using generic knowledge-base table names. Metadata filters can narrow the candidate documents before ranking.</p><pre>select c.chunk_id, c.heading, c.chunk_text,<br>       vector_distance(c.embedding,<br>                       q.query_embedding, cosine) distance<br>  from kb_doc_chunks c<br>  join kb_search_queries q on q.search_id = :search_id<br> where c.embedding is not null<br> order by distance<br> fetch first :top_n rows only;</pre><p>We assemble the selected passages, filenames, headings, and document dates into a prompt. The instructions ask the model to answer the specific question, cite its sources, and say when the supplied material is insufficient. A shared function handles generation:</p><pre>l_output := kb_ai_pkg.generate_text(<br>  p_system_prompt =&gt;<br>    &#39;Ground every claim in provided source material.&#39;,<br>  p_prompt =&gt; l_prompt<br>);</pre><p>Inside this function, APEX_JSON constructs the chat request and APEX_WEB_SERVICE.MAKE_REST_REQUEST sends it using the configured Web Credential. The response text is extracted from choices[0].message.content and displayed in APEX.</p><h3>Reusing the evidence for content and comparisons</h3><p>Content generation adds a topic, output type, and template instructions to the retrieved context. A template defines the audience, structure, and tone while the source passages supply the evidence. Our implementation saves the assembled prompt and generated text and offers a Markdown download.</p><figure><img alt="A topic, content type, category, and template guide content generation. This screenshot shows the input form; generated drafts still need source review." src="https://cdn-images-1.medium.com/max/1024/1*8vJ0tiy3BR7b7JRDaGFqEA.png" /><figcaption><em>A topic, content type, category, and template guide content generation. This screenshot shows the input form; generated drafts still need source review.</em></figcaption></figure><p>Document comparison answers a different question: “What changed between these two documents?” Users select a current document and a baseline, then generate a structured comparison with source references. The screenshot shows the current document and baseline selectors, an optional category filter, and the comparison action.</p><figure><img alt="Select a current document and a baseline to compare their content. The documents currently loaded in this internal project provide the example selection." src="https://cdn-images-1.medium.com/max/1024/1*m7-8WRCW5M_L-VuCR8r4Yg.png" /><figcaption><em>Select a current document and a baseline to compare their content. The documents currently loaded in this internal project provide the example selection.</em></figcaption></figure><p>Unlike semantic search, this workflow takes a bounded set of chunks from each selected document in document order. The prompt labels the two sets as current and baseline and asks for supported changes. The comparison defaults to the first 10 embedded chunks per document, with a maximum of 15, subject to the optional category filter. Because context is bounded, an omitted item is not automatically evidence that it was resolved.</p><p>The reusable pieces are document storage, extraction, chunking, embeddings, retrieval, and generation. The domain determines the metadata, filters, and prompts. A support repository might filter by product and version; developer documentation might use API, language, and release. Adapting the application also requires parsers for the source formats and access controls appropriate to the collection. Our current uploader handles DOCX files.</p><p>Answer quality depends on the evidence supplied. Our current extractor focuses on body paragraphs, so table content and long passages need attention when assessing coverage. Similarity scores do not establish factual accuracy or freshness, and generated answers still need review against their sources.</p><p>The Document Knowledge Base brings these pieces into one workflow: add documents, ask questions, create drafts, and compare sources. Keeping documents, metadata, vectors, reusable prompts, and generated artifacts together in Oracle Database supports that workflow with familiar joins and filters. The next step is to validate extraction and retrieval against representative documents, then review generated outputs against their sources before broader use.</p><p><em>The images show the live Document Knowledge Base application. Code snippets are simplified examples using generic </em><em>kb_ names; they are not standalone installation scripts.</em></p><p>Further reading: <a href="https://docs.oracle.com/en/database/oracle/apex/24.2/aeapi/APEX_WEB_SERVICE.html">Oracle APEX web service API</a> and <a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/vecse/vector-distance-functions-and-operators.html">Oracle vector distance functions</a>.</p><h3>Frequently asked questions</h3><h3>What is this app for?</h3><p>Document Knowledge Base makes a DOCX collection searchable and reusable through questions, generated drafts, and selected-document comparisons. For example, a support team could retrieve troubleshooting guidance, a developer team could draft an API summary, or an onboarding team could help employees find procedures. These uses depend on the documents and prompts supplied.</p><h3>Which file types are supported?</h3><p>The current uploader and extractor handle DOCX. They do not implement PDF, HTML, Markdown, scanned images, or complete DOCX table extraction. Adding another format requires a parser and coverage validation.</p><h3>What does RAG do here?</h3><p>Retrieval augmented generation embeds a question, ranks stored chunks by semantic similarity, and supplies selected passages to the model as context. Instructions request source-grounded answers and source references. The comparison workflow instead takes bounded, ordered chunks from two explicitly selected documents.</p><h3>Do uploaded documents train the model?</h3><p>The application code makes embedding and generation calls; it does not train or fine-tune a model. Selected source text is sent to the configured inference service. That implementation fact is not a blanket statement about provider retention or data use: verify the chosen service, model, and applicable terms before uploading sensitive documents.</p><h3>What limits coverage and answer accuracy?</h3><p>Table cells are skipped, long XML runs and paragraphs can be truncated, and chunks have no overlap. Missing embeddings, metadata filters, and bounded retrieval or comparison can leave relevant evidence out. Similarity and source citations do not prove correctness or freshness. Check extracted coverage and review every generated answer, draft, and comparison against the original documents.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=394009d991fe" width="1" height="1" alt=""><hr><p><a href="https://medium.com/oracledevs/building-a-document-knowledge-base-with-oracle-apex-and-oracle-ai-vector-search-394009d991fe">Building a Document Knowledge Base with Oracle APEX and Oracle AI Vector Search</a> was originally published in <a href="https://medium.com/oracledevs">Oracle Developers</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>