diff --git a/dev-ai-app-dev-finance/dev-guide-ai-finance/build/build.md b/dev-ai-app-dev-finance/dev-guide-ai-finance/build/build.md
index c0db7b1b..9ce016f6 100644
--- a/dev-ai-app-dev-finance/dev-guide-ai-finance/build/build.md
+++ b/dev-ai-app-dev-finance/dev-guide-ai-finance/build/build.md
@@ -97,7 +97,410 @@ This lab assumes you have:
4. Run the code block to connect to the database.
- 
+ 
+
+5. Create the tables and load the data.
+
+* We begin by dropping any old objects.
+* Then we create all tables.
+* We build a JSON duality view called `clients_dv`.
+* We load the ONNX model we will later use for embedding.
+
+ Run and review the code in a new cell:
+
+ ```python
+
+DROP_OBJECTS_SQL = [
+"DROP VIEW IF EXISTS CLIENT_DV",
+"DROP PROPERTY GRAPH IF EXISTS LOANS_GRAPH_VW",
+"DROP TABLE IF EXISTS CLIENTS_TO_LOAN_RECOMMENDATIONS CASCADE CONSTRAINTS PURGE",
+"DROP TABLE IF EXISTS CLIENTS_TO_LOAN CASCADE CONSTRAINTS PURGE",
+"DROP TABLE IF EXISTS CLIENT_DEBT CASCADE CONSTRAINTS PURGE",
+"DROP TABLE IF EXISTS LOAN_CHUNK CASCADE CONSTRAINTS PURGE",
+"DROP TABLE IF EXISTS LOAN_APPLICATIONS CASCADE CONSTRAINTS PURGE",
+"DROP TABLE IF EXISTS MOCK_LOAN_DATA CASCADE CONSTRAINTS PURGE",
+"DROP TABLE IF EXISTS CLIENTS CASCADE CONSTRAINTS PURGE",
+"DROP TABLE IF EXISTS FUNDING_PROVIDER CASCADE CONSTRAINTS PURGE",
+"DROP TABLE IF EXISTS FUNDING_PROVIDER_TERMS CASCADE CONSTRAINTS PURGE",
+"DROP TABLE IF EXISTS LENDER_TERMS CASCADE CONSTRAINTS PURGE",
+"DROP TABLE IF EXISTS AFFORDABLE_HOUSING_ZONE CASCADE CONSTRAINTS PURGE",
+"DROP TABLE IF EXISTS FLOODZONE CASCADE CONSTRAINTS PURGE",
+]
+
+# -------------------------------------------------------------
+# CREATE TABLE STATEMENTS
+# -------------------------------------------------------------
+
+CREATE_CLIENTS = """
+CREATE TABLE IF NOT EXISTS CLIENTS (
+CUSTOMER_ID VARCHAR2(4000) PRIMARY KEY,
+FIRST_NAME VARCHAR2(4000),
+LAST_NAME VARCHAR2(4000),
+CITY VARCHAR2(4000),
+STATE VARCHAR2(4000),
+ZIP_CODE NUMBER,
+AGE NUMBER,
+INCOME NUMBER,
+VETERAN VARCHAR2(4000)
+)
+"""
+
+CREATE_LOAN_APPLICATIONS = """
+CREATE TABLE IF NOT EXISTS LOAN_APPLICATIONS (
+CUSTOMER_ID VARCHAR2(4000),
+APPLICATION_ID NUMBER GENERATED BY DEFAULT AS IDENTITY (START WITH 1000 INCREMENT BY 1000) PRIMARY KEY,
+REQUESTED_LOAN_AMOUNT NUMBER,
+CREDIT_SCORE NUMBER,
+ZIPCODE NUMBER,
+LOAN_PURPOSE VARCHAR2(4000),
+LOAN_STATUS VARCHAR2(4000),
+STUDENT_STATUS VARCHAR2(4000),
+EDUCATION_LEVEL VARCHAR2(4000),
+FINAL_DECISION VARCHAR2(32767),
+RECOMMENDATIONS VARCHAR2(32767),
+TOTAL_DEBT NUMBER,
+CREDIT_RANK NUMBER DEFAULT NULL,
+CONSTRAINT fk_loan_app_clients FOREIGN KEY (CUSTOMER_ID) REFERENCES CLIENTS(CUSTOMER_ID)
+)
+"""
+
+CREATE_MOCK_LOAN_DATA = """
+CREATE TABLE IF NOT EXISTS MOCK_LOAN_DATA (
+LOAN_ID NUMBER PRIMARY KEY,
+LOAN_PROVIDER_NAME VARCHAR2(4000),
+LOAN_TYPE VARCHAR2(4000),
+INTEREST_RATE NUMBER,
+ORIGINATION_FEE NUMBER,
+TIME_TO_CLOSE NUMBER,
+CREDIT_SCORE NUMBER,
+DEBT_TO_INCOME_RATIO NUMBER,
+INCOME NUMBER,
+DOWN_PAYMENT_PERCENT NUMBER,
+IS_FIRST_TIME_HOME_BUYER VARCHAR2(10) NOT NULL,
+OFFER_BEGIN_DATE DATE DEFAULT SYSDATE,
+OFFER_END_DATE DATE DEFAULT SYSDATE + 30
+)
+"""
+
+CREATE_CLIENT_DEBT = """
+CREATE TABLE IF NOT EXISTS CLIENT_DEBT (
+ID NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
+CUSTOMER_ID VARCHAR2(100),
+APPLICATION_ID NUMBER,
+DEBT_TYPE VARCHAR2(100),
+DEBT_AMOUNT NUMBER
+)
+"""
+
+CREATE_CLIENTS_TO_LOAN = """
+CREATE TABLE IF NOT EXISTS CLIENTS_TO_LOAN (
+ID NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
+CUSTOMER_ID VARCHAR2(4000),
+LOAN_ID NUMBER,
+LOAN_APPLICATION_ID NUMBER,
+CONSTRAINT fk_ctl_clients FOREIGN KEY (CUSTOMER_ID) REFERENCES CLIENTS(CUSTOMER_ID),
+CONSTRAINT fk_ctl_loans FOREIGN KEY (LOAN_ID) REFERENCES MOCK_LOAN_DATA(LOAN_ID),
+CONSTRAINT fk_ctl_loan_applications FOREIGN KEY (LOAN_APPLICATION_ID) REFERENCES LOAN_APPLICATIONS(APPLICATION_ID)
+)
+"""
+
+CREATE_CLIENTS_TO_LOAN_RECS = """
+CREATE TABLE IF NOT EXISTS CLIENTS_TO_LOAN_RECOMMENDATIONS (
+ID NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
+CUSTOMER_ID VARCHAR2(4000),
+LOAN_ID NUMBER,
+LOAN_APPLICATION_ID NUMBER,
+ACTION_NEEDED VARCHAR2(4000),
+CONSTRAINT fk_cltr_clients FOREIGN KEY (CUSTOMER_ID) REFERENCES CLIENTS(CUSTOMER_ID),
+CONSTRAINT fk_cltr_loans FOREIGN KEY (LOAN_ID) REFERENCES MOCK_LOAN_DATA(LOAN_ID),
+CONSTRAINT fk_cltr_loan_applications FOREIGN KEY (LOAN_APPLICATION_ID) REFERENCES LOAN_APPLICATIONS(APPLICATION_ID)
+)
+"""
+
+CREATE_LOAN_CHUNK = """
+CREATE TABLE IF NOT EXISTS LOAN_CHUNK (
+ID NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
+CUSTOMER_ID VARCHAR2(100),
+CHUNK_ID NUMBER,
+CHUNK_TEXT CLOB,
+CHUNK_VECTOR VECTOR(384,*,DENSE)
+)
+"""
+
+CREATE_FUNDING_PROVIDER_TERMS = """
+CREATE TABLE IF NOT EXISTS FUNDING_PROVIDER_TERMS (
+LOAN_PROVIDER_ID NUMBER,
+TERMS_ID NUMBER PRIMARY KEY,
+INTEREST_RATE NUMBER,
+LOAN_DESCRIPTION VARCHAR2(1000),
+TIME_TO_CLOSE NUMBER,
+LOAN_COSTS NUMBER,
+OFFER_BEGIN_DATE DATE,
+OFFER_END_DATE DATE
+)
+"""
+
+CREATE_LENDER_TERMS = """
+CREATE TABLE IF NOT EXISTS LENDER_TERMS (
+LENDER_ID NUMBER,
+TERMS_ID NUMBER PRIMARY KEY,
+LOAN_DESCRIPTION VARCHAR2(1000),
+INTEREST_RATE_MARKUP NUMBER,
+ORIGINATION_FEE NUMBER,
+LENDER_TIME_TO_CLOSE NUMBER,
+CREDIT_SCORE NUMBER,
+DEBT_TO_INCOME_RATIO NUMBER,
+INCOME NUMBER,
+DOWN_PAYMENT_PERCENT NUMBER,
+OFFER_BEGIN_DATE DATE,
+OFFER_END_DATE DATE,
+CONSTRAINT fk_lt_mock_loan_data FOREIGN KEY (LENDER_ID) REFERENCES MOCK_LOAN_DATA(LOAN_ID)
+)
+"""
+
+CREATE_AFFORDABLE_HOUSING_ZONE = """
+CREATE TABLE IF NOT EXISTS AFFORDABLE_HOUSING_ZONE (
+LAND_TRACT_ID NUMBER PRIMARY KEY,
+ZIPCODE NUMBER
+)
+"""
+
+CREATE_FLOODZONE = """
+CREATE TABLE IF NOT EXISTS FLOODZONE (
+GEOMETRY MDSYS.SDO_GEOMETRY,
+FID NUMBER,
+DESCR VARCHAR2(32767)
+)
+"""
+
+# -------------------------------------------------------------
+# JSON DUALITY VIEW
+# -------------------------------------------------------------
+
+CREATE_CLIENTS_DV = """
+CREATE OR REPLACE JSON RELATIONAL DUALITY VIEW clients_dv AS
+SELECT JSON {
+'_id': c.customer_id,
+'firstName': c.first_name,
+'lastName': c.last_name,
+'city': c.city,
+'state': c.state,
+'zipCode': c.zip_code,
+'age': c.age,
+'income': c.income,
+'veteran': c.veteran,
+'clientDebt': [
+SELECT JSON{
+ 'id': cd.id,
+ 'debtType': cd.debt_type,
+ 'debtAmount': cd.debt_amount
+}
+FROM client_debt cd WITH INSERT UPDATE DELETE
+WHERE cd.customer_id = c.customer_id
+],
+'loanApplications': [
+SELECT JSON {
+ 'applicationId': la.application_id,
+ 'requestedLoanAmount': la.requested_loan_amount,
+ 'creditScore': la.credit_score,
+ 'zipcode': la.zipcode,
+ 'loanPurpose': la.loan_purpose,
+ 'loanStatus': la.loan_status,
+ 'studentStatus': la.student_status,
+ 'educationLevel': la.education_level,
+ 'finalDecision': la.final_decision,
+ 'recommendations': la.recommendations,
+ 'totalDebt' : la.total_debt,
+ 'creditRank': la.credit_rank
+}
+FROM loan_applications la WITH INSERT UPDATE DELETE
+WHERE la.customer_id = c.customer_id
+]
+}
+FROM clients c WITH INSERT UPDATE DELETE
+"""
+
+# -------------------------------------------------------------
+# ONNX MODEL LOAD
+# -------------------------------------------------------------
+
+ONNX_MODEL_SQL = """
+DECLARE
+model_count INT;
+BEGIN
+SELECT COUNT(*) INTO model_count
+FROM user_mining_models
+WHERE model_name = 'DEMO_MODEL';
+IF (model_count < 1) THEN
+EXECUTE IMMEDIATE 'CREATE OR REPLACE DIRECTORY DEMO_DIR AS ''demodir''';
+DBMS_CLOUD.GET_OBJECT(
+ object_uri => 'https://objectstorage.us-ashburn-1.oraclecloud.com/p/mFBJq8UCjdar89xJpTQVOy_tONdxyrQI-B8UT0OS-nllmg8xyVAIIOTbGyIVJ1iJ/n/c4u04/b/llm/o/all_MiniLM_L12_v2.onnx',
+ directory_name => 'DEMO_DIR',
+ file_name => 'all_minilm_l12_v2.onnx'
+);
+DBMS_DATA_MINING.DROP_MODEL(model_name => 'DEMO_MODEL', force => TRUE);
+DBMS_VECTOR.LOAD_ONNX_MODEL('DEMO_DIR', 'all_minilm_l12_v2.onnx', 'DEMO_MODEL');
+END IF;
+END;
+"""
+
+# -------------------------------------------------------------
+# SAMPLE DATA ARRAYS
+# -------------------------------------------------------------
+
+AFFORDABLE_HOUSING_ZONE_DATA = [
+ (1, 48202)
+]
+
+CLIENTS_DATA = [
+ ('CUST_1000','James','Smith','New York City','NY',10033,52,130000,'Yes'),
+ ('CUST_2000','James','Woods','East Gina','AR',58967,27,5000,'Yes'),
+ ('CUST_3000','Evan','Burton','Detroit','MI',48202,24,65000,'No'),
+ ('CUST_4000','Alex','Anderson','Austin','TX',78744,26,25000,'No'),
+]
+
+LOAN_APPLICATIONS_DATA = [
+ ('CUST_1000',1000,200000,780,10033,'Mortgage','Pending Review','No','PhD',None,None,None,0),
+ ('CUST_2000',1001,100000,520,58967,'Mortgage','Pending Review','No','High School',None,None,None,0),
+ ('CUST_3000',1002,150000,675,48202,'Mortgage','Pending Review','No','Masters',None,None,None,0),
+]
+
+MOCK_LOAN_DATA = [
+ (1,'provider1','Bridge loan',4.75,1.06,38,649,27.15,150377,12.43,'NO'),
+ (2,'provider2','Second mortgage',3.31,1.92,25,776,28.81,154763,3.76,'NO'),
+]
+
+CLIENT_DEBT_DATA = [
+ ('CUST_1000',1000,'First Time Home Owner',90000),
+ ('CUST_2000',1001,'Home',8843),
+ ('CUST_2000',1001,'Credit Card',31560),
+]
+
+CLIENTS_TO_LOAN_RECS_DATA = [
+ ('CUST_1000', 1, 'Review Documents'),
+ ('CUST_2000', 2, 'Verify Credit'),
+]
+
+# -------------------------------------------------------------
+# HELPERS
+# -------------------------------------------------------------
+
+def safe_execute(sql: str, params=None):
+ try:
+ cursor.execute(sql, params or {})
+ except Exception as e:
+ print(f"(ignore) {e}")
+
+def run_schema_setup():
+ # Drops
+ for stmt in DROP_OBJECTS_SQL:
+ safe_execute(stmt)
+ print("✅ Dropped any existing objects")
+
+ # Creates
+ cursor.execute(CREATE_CLIENTS)
+ cursor.execute(CREATE_LOAN_APPLICATIONS)
+ cursor.execute(CREATE_MOCK_LOAN_DATA)
+ cursor.execute(CREATE_CLIENT_DEBT)
+ cursor.execute(CREATE_CLIENTS_TO_LOAN)
+ cursor.execute(CREATE_CLIENTS_TO_LOAN_RECS)
+ cursor.execute(CREATE_LOAN_CHUNK)
+ cursor.execute(CREATE_FUNDING_PROVIDER_TERMS)
+ cursor.execute(CREATE_LENDER_TERMS)
+ cursor.execute(CREATE_AFFORDABLE_HOUSING_ZONE)
+ cursor.execute(CREATE_FLOODZONE)
+ connection.commit()
+ print("✅ Tables created")
+
+ # Duality View
+ cursor.execute(CREATE_CLIENTS_DV)
+ connection.commit()
+ print("✅ JSON Duality View clients_dv created")
+
+ # ONNX model
+ cursor.execute(ONNX_MODEL_SQL)
+ connection.commit()
+ print("✅ DEMO_MODEL loaded (or already present)")
+
+def insert_seed_data():
+ # Truncate to avoid duplicates
+ cursor.execute("TRUNCATE TABLE AFFORDABLE_HOUSING_ZONE")
+ cursor.execute("TRUNCATE TABLE CLIENTS_TO_LOAN_RECOMMENDATIONS")
+ cursor.execute("TRUNCATE TABLE CLIENT_DEBT")
+ cursor.execute("TRUNCATE TABLE LOAN_APPLICATIONS")
+ cursor.execute("TRUNCATE TABLE MOCK_LOAN_DATA")
+ cursor.execute("TRUNCATE TABLE CLIENTS")
+ connection.commit()
+
+ # Affordable housing
+ cursor.executemany("""
+ INSERT INTO AFFORDABLE_HOUSING_ZONE (LAND_TRACT_ID, ZIPCODE) VALUES (:1, :2)
+ """, AFFORDABLE_HOUSING_ZONE_DATA)
+
+ # Clients
+ cursor.executemany("""
+ INSERT INTO CLIENTS (CUSTOMER_ID, FIRST_NAME, LAST_NAME, CITY, STATE, ZIP_CODE, AGE, INCOME, VETERAN)
+ VALUES (:1,:2,:3,:4,:5,:6,:7,:8,:9)
+ """, CLIENTS_DATA)
+
+ # Loan applications
+ cursor.executemany("""
+ INSERT INTO LOAN_APPLICATIONS (
+ CUSTOMER_ID, APPLICATION_ID, REQUESTED_LOAN_AMOUNT, CREDIT_SCORE, ZIPCODE,
+ LOAN_PURPOSE, LOAN_STATUS, STUDENT_STATUS, EDUCATION_LEVEL,
+ FINAL_DECISION, RECOMMENDATIONS, CREDIT_RANK, TOTAL_DEBT
+ ) VALUES (:1,:2,:3,:4,:5,:6,:7,:8,:9,:10,:11,:12,:13)
+ """, LOAN_APPLICATIONS_DATA)
+
+ # Mock loan data
+ cursor.executemany("""
+ INSERT INTO MOCK_LOAN_DATA (
+ LOAN_ID, LOAN_PROVIDER_NAME, LOAN_TYPE, INTEREST_RATE, ORIGINATION_FEE,
+ TIME_TO_CLOSE, CREDIT_SCORE, DEBT_TO_INCOME_RATIO, INCOME, DOWN_PAYMENT_PERCENT, IS_FIRST_TIME_HOME_BUYER
+ ) VALUES (:1,:2,:3,:4,:5,:6,:7,:8,:9,:10,:11)
+ """, MOCK_LOAN_DATA)
+
+ # Client debt
+ cursor.executemany("""
+ INSERT INTO CLIENT_DEBT (CUSTOMER_ID, APPLICATION_ID, DEBT_TYPE, DEBT_AMOUNT)
+ VALUES (:1,:2,:3,:4)
+ """, CLIENT_DEBT_DATA)
+
+ # Client->Loan recs
+ cursor.executemany("""
+ INSERT INTO CLIENTS_TO_LOAN_RECOMMENDATIONS (CUSTOMER_ID, LOAN_ID, ACTION_NEEDED)
+ VALUES (:1,:2,:3)
+ """, CLIENTS_TO_LOAN_RECS_DATA)
+
+ # Derive total_debt per application
+ cursor.execute("""
+ UPDATE LOAN_APPLICATIONS la
+ SET total_debt = (
+ SELECT NVL(SUM(cd.debt_amount),0)
+ FROM CLIENT_DEBT cd
+ WHERE cd.customer_id = la.customer_id
+ AND cd.application_id = la.application_id
+ )
+ """)
+
+ connection.commit()
+ print("✅ Seed data inserted & totals updated")
+
+# -------------------------------------------------------------
+# RUN EVERYTHING
+# -------------------------------------------------------------
+run_schema_setup()
+insert_seed_data()
+print("schema initialized.")
+
+
+ ```
+
+6. Validate that all assets have been created successfully:
+
+ 
+
+ 
## Task 3: Create a Function to retrieve data from the database.
@@ -114,37 +517,44 @@ You will query customer data from the `clients_dv` JSON duality view, which comb
```python
def fetch_customer_data(customer_id):
- cursor.execute("SELECT data FROM clients_dv WHERE JSON_VALUE(data, '$._id') = :customer_id", {'customer_id': customer_id})
- result = cursor.fetchone()
- return json.loads(result[0]) if result and isinstance(result[0], str) else result[0] if result else None
+ cursor.execute(
+ "SELECT data FROM clients_dv WHERE JSON_VALUE(data, '$._id') = :customer_id",
+ {'customer_id': customer_id}
+ )
+ result = cursor.fetchone()
+ return json.loads(result[0]) if result and isinstance(result[0], str) else result[0] if result else None
selected_customer_id = "CUST_1000"
customer_json = fetch_customer_data(selected_customer_id)
if customer_json:
- loan_app = customer_json.get("loanApplications", [{}])[0]
- print(f"Customer: {customer_json['firstName']} {customer_json['lastName']}")
- print(f"Loan Status: {loan_app['loanStatus']}")
-
- desired_fields = [
- ("Customer ID", selected_customer_id),
- ("Application ID", loan_app.get("applicationId", "")),
- ("First Name", customer_json.get("firstName", "")),
- ("Last Name", customer_json.get("lastName", "")),
- ("City", customer_json.get("city", "")),
- ("State", customer_json.get("state", "")),
- ("Zip code", customer_json.get("zipCode", "")),
- ("Age", customer_json.get("age", 0)),
- ("Income", customer_json.get("income", 0)),
- ("Credit score", loan_app.get("creditScore", 600)),
- ("Requested loan amount", loan_app.get("requestedLoanAmount", 0)),
- ("Total Debt", loan_app.get("totalDebt", 0)),
- ("Loan status", loan_app.get("loanStatus", "Pending Review"))
- ]
- df_customer_details = pd.DataFrame({field_name: [field_value] for field_name, field_value in desired_fields})
- display(df_customer_details)
+ loan_app = customer_json.get("loanApplications", [{}])[0]
+ print(f"Customer: {customer_json['firstName']} {customer_json['lastName']}")
+ print(f"Loan Status: {loan_app['loanStatus']}")
+
+ desired_fields = [
+ ("Customer ID", selected_customer_id),
+ ("Application ID", loan_app.get("applicationId", "")),
+ ("First Name", customer_json.get("firstName", "")),
+ ("Last Name", customer_json.get("lastName", "")),
+ ("City", customer_json.get("city", "")),
+ ("State", customer_json.get("state", "")),
+ ("Zip code", customer_json.get("zipCode", "")),
+ ("Age", customer_json.get("age", 0)),
+ ("Income", customer_json.get("income", 0)),
+ ("Credit score", loan_app.get("creditScore", 600)),
+ ("Requested loan amount", loan_app.get("requestedLoanAmount", 0)),
+ ("Total Debt", loan_app.get("totalDebt", 0)),
+ ("Loan status", loan_app.get("loanStatus", "Pending Review"))
+ ]
+
+ df_customer_details = pd.DataFrame(
+ {field_name: [field_value] for field_name, field_value in desired_fields}
+ )
+ display(df_customer_details)
+
else:
- print("No data found for customer ID:", selected_customer_id)
+ print("No data found for customer ID:", selected_customer_id)
```
@@ -177,29 +587,29 @@ df_mock_loans = pd.DataFrame(cursor.fetchall(), columns=["LOAN_ID", "LOAN_PROVID
# Generate Recommendations
def generate_recommendations(customer_id, customer_json, df_mock_loans):
- loan_app = customer_json.get("loanApplications", [{}])[0]
- available_loans_text = "\n".join([f"{loan['LOAN_ID']}: {loan['LOAN_TYPE']} | {loan['INTEREST_RATE']}% interest | Credit Score: {loan['CREDIT_SCORE']} | DTI: {loan['DEBT_TO_INCOME_RATIO']}" for loan in df_mock_loans.to_dict(orient='records')])
- customer_profile_text = "\n".join([f"- {key.replace('_', ' ').title()}: {value}" for key, value in {**customer_json, **loan_app}.items() if key not in ["embedding_vector", "ai_response_vector", "chunk_vector"]])
-
- prompt = f"""[INST] <>You are a Loan Approver AI. Use only the provided context to evaluate the applicant’s profile and recommend loans. Format results as plain text with numbered sections (1. Comprehensive Evaluation, 2. Top 3 Loan Recommendations, 3. Recommendations Explanations, 4. Final Suggestion). Use newlines between sections.> [/INST]
- [INST]Available Loan Options:\n{available_loans_text}\nApplicant's Full Profile:\n{customer_profile_text}\nTasks:\n1. Comprehensive Evaluation\n2. Top 3 Loan Recommendations\n3. Recommendations Explanations\n4. Final Suggestion"""
-
- print("Generating AI response...")
- print(" ")
-
- genai_client = oci.generative_ai_inference.GenerativeAiInferenceClient(config=oci.config.from_file(os.getenv("OCI_CONFIG_PATH", "~/.oci/config")), service_endpoint=os.getenv("ENDPOINT"))
- chat_detail = oci.generative_ai_inference.models.ChatDetails(
- compartment_id=os.getenv("COMPARTMENT_OCID"),
- chat_request=oci.generative_ai_inference.models.GenericChatRequest(messages=[oci.generative_ai_inference.models.UserMessage(content=[oci.generative_ai_inference.models.TextContent(text=prompt)])], temperature=0.0, top_p=1.00),
- serving_mode=oci.generative_ai_inference.models.OnDemandServingMode(model_id="meta.llama-3.2-90b-vision-instruct")
- )
- chat_response = genai_client.chat(chat_detail)
- recommendations = chat_response.data.chat_response.choices[0].message.content[0].text
-
- return recommendations
-
-recommendations = generate_recommendations(selected_customer_id, customer_json, df_mock_loans)
-print(recommendations)
+ loan_app = customer_json.get("loanApplications", [{}])[0]
+ available_loans_text = "\n".join([f"{loan['LOAN_ID']}: {loan['LOAN_TYPE']} | {loan['INTEREST_RATE']}% interest | Credit Score: {loan['CREDIT_SCORE']} | DTI: {loan['DEBT_TO_INCOME_RATIO']}" for loan in df_mock_loans.to_dict(orient='records')])
+ customer_profile_text = "\n".join([f"- {key.replace('_', ' ').title()}: {value}" for key, value in {**customer_json, **loan_app}.items() if key not in ["embedding_vector", "ai_response_vector", "chunk_vector"]])
+
+ prompt = f"""[INST] <>You are a Loan Approver AI. Use only the provided context to evaluate the applicant’s profile and recommend loans. Format results as plain text with numbered sections (1. Comprehensive Evaluation, 2. Top 3 Loan Recommendations, 3. Recommendations Explanations, 4. Final Suggestion). Use newlines between sections.> [/INST]
+ [INST]Available Loan Options:\n{available_loans_text}\nApplicant's Full Profile:\n{customer_profile_text}\nTasks:\n1. Comprehensive Evaluation\n2. Top 3 Loan Recommendations\n3. Recommendations Explanations\n4. Final Suggestion"""
+
+ print("Generating AI response...")
+ print(" ")
+
+ genai_client = oci.generative_ai_inference.GenerativeAiInferenceClient(config=oci.config.from_file(os.getenv("OCI_CONFIG_PATH", "~/.oci/config")), service_endpoint=os.getenv("ENDPOINT"))
+ chat_detail = oci.generative_ai_inference.models.ChatDetails(
+ compartment_id=os.getenv("COMPARTMENT_OCID"),
+ chat_request=oci.generative_ai_inference.models.GenericChatRequest(messages=[oci.generative_ai_inference.models.UserMessage(content=[oci.generative_ai_inference.models.TextContent(text=prompt)])], temperature=0.0, top_p=1.00),
+ serving_mode=oci.generative_ai_inference.models.OnDemandServingMode(model_id="meta.llama-3.2-90b-vision-instruct")
+ )
+ chat_response = genai_client.chat(chat_detail)
+ recommendations = chat_response.data.chat_response.choices[0].message.content[0].text
+
+ return recommendations
+
+ recommendations = generate_recommendations(selected_customer_id, customer_json, df_mock_loans)
+ print(recommendations)
```
@@ -211,7 +621,7 @@ print(recommendations)
>*Note:* Your result may be different due to non-deterministic character of generative AI.
- 
+ 
## Task 5: Chunk & Store the Recommendations
@@ -235,26 +645,26 @@ chunk_sizes = [50] # e.g., [50, 200, 500]
# Insert chunks using VECTOR_CHUNKS. Make CHUNK_ID unique by (size + chunk_offset).
for size in chunk_sizes:
- insert_sql = f"""
- INSERT INTO LOAN_CHUNK (CUSTOMER_ID, CHUNK_ID, CHUNK_TEXT)
- SELECT :cust_id,
- :chunk_size + vc.chunk_offset,
- vc.chunk_text
- FROM (SELECT :rec_text AS txt FROM dual) s,
- VECTOR_CHUNKS(
- dbms_vector_chain.utl_to_text(s.txt)
- BY words
- MAX {size}
- OVERLAP 0
- SPLIT BY sentence
- LANGUAGE american
- NORMALIZE all
- ) vc
- """
- cursor.execute(
- insert_sql,
- {'cust_id': selected_customer_id, 'chunk_size': size, 'rec_text': recommendations}
- )
+ insert_sql = f"""
+ INSERT INTO LOAN_CHUNK (CUSTOMER_ID, CHUNK_ID, CHUNK_TEXT)
+ SELECT :cust_id,
+ :chunk_size + vc.chunk_offset,
+ vc.chunk_text
+ FROM (SELECT :rec_text AS txt FROM dual) s,
+ VECTOR_CHUNKS(
+ dbms_vector_chain.utl_to_text(s.txt)
+ BY words
+ MAX {size}
+ OVERLAP 0
+ SPLIT BY sentence
+ LANGUAGE american
+ NORMALIZE all
+ ) vc
+ """
+ cursor.execute(
+ insert_sql,
+ {'cust_id': selected_customer_id, 'chunk_size': size, 'rec_text': recommendations}
+ )
# Fetch chunks for preview
cursor.execute("""
@@ -270,16 +680,16 @@ def _lob_to_str(v): return v.read() if isinstance(v, oracledb.LOB) else v
items = []
for cid, ctext in rows:
- txt = _lob_to_str(ctext) or ""
- items.append({
- "CHUNK_ID": cid,
- "Chars": len(txt),
- "Words": len(txt.split()),
- "Preview": (txt[:160] + "…") if len(txt) > 160 else txt
- })
-
-df_chunks = pd.DataFrame(items).sort_values("CHUNK_ID")
-connection.commit()
+ txt = _lob_to_str(ctext) or ""
+ items.append({
+ "CHUNK_ID": cid,
+ "Chars": len(txt),
+ "Words": len(txt.split()),
+ "Preview": (txt[:160] + "…") if len(txt) > 160 else txt
+ })
+
+ df_chunks = pd.DataFrame(items).sort_values("CHUNK_ID")
+ connection.commit()
print(f"✅ Task 5 complete: recommendation chunked for customer {selected_customer_id} (sizes: {chunk_sizes}).")
display(df_chunks)
@@ -339,67 +749,67 @@ This step:
- **Performs AI Vector Search**: Retrieve the relevant recommendation text from `LOAN_CHUNKS` table. Then find the most relevant recommendations using similarity search.
- **Use RAG**: Combines the customer profile, policy rules using the retrieved recommendation context.
-1. Review
+1. Copy the code block below to implement RAG:
```python
- question = "What 4th loan would James qualify for?"
+question = "What 4th loan would James qualify for?"
def vectorize_question(q):
- cursor.execute("""
- SELECT dbms_vector_chain.utl_to_embedding(
- :q,
- JSON('{"provider":"database","model":"DEMO_MODEL","dimensions":384}')
- ) FROM DUAL
- """, {'q': q})
- return cursor.fetchone()[0]
+ cursor.execute("""
+ SELECT dbms_vector_chain.utl_to_embedding(
+ :q,
+ JSON('{"provider":"database","model":"DEMO_MODEL","dimensions":384}')
+ ) FROM DUAL
+ """, {'q': q})
+ return cursor.fetchone()[0]
print("Processing your question using AI Vector Search across chunked recommendations...")
try:
- q_vec = vectorize_question(question)
-
- # Retrieve top recommendation chunks (across all sizes) for this customer
- cursor.execute("""
- SELECT CHUNK_ID, CHUNK_TEXT
- FROM LOAN_CHUNK
- WHERE CUSTOMER_ID = :cust_id
- AND CHUNK_VECTOR IS NOT NULL
- ORDER BY VECTOR_DISTANCE(CHUNK_VECTOR, :qv, COSINE)
- FETCH FIRST 4 ROWS ONLY
- """, {'cust_id': selected_customer_id, 'qv': q_vec})
- retrieved = [
- (r[0], r[1].read() if isinstance(r[1], oracledb.LOB) else r[1])
- for r in cursor.fetchall()
- ]
-
- if not retrieved:
- # Fallback to full text as one chunk
- retrieved = [(0, recommendations)]
-
- # Prepare clean context for the LLM
- cleaned = [re.sub(r'[^\w\s\d.,\-\'"]', ' ', t).strip() for _, t in retrieved]
- docs_as_one_string = "\n=========\n".join(cleaned) + "\n=========\n"
-
- # Rebuild available loans + customer profile (same structures used earlier)
- available_loans_text = "\n".join(
- [f"{loan['LOAN_ID']}: {loan['LOAN_TYPE']} | {loan['INTEREST_RATE']}% interest | "
- f"Credit Score: {loan['CREDIT_SCORE']} | DTI: {loan['DEBT_TO_INCOME_RATIO']} | "
- f"Origination Fee: ${loan['ORIGINATION_FEE']} | Time to Close: {loan['TIME_TO_CLOSE']} days"
- for loan in df_mock_loans.to_dict(orient='records')]
- )
- loan_app = customer_json.get("loanApplications", [{}])[0]
- customer_profile_text = "\n".join(
- [f"- {k.replace('_',' ').title()}: {v}"
- for k, v in {**customer_json, **loan_app}.items()
- if k not in ["embedding_vector","ai_response_vector","chunk_vector"]]
- )
-
- rag_prompt = f"""\
+ q_vec = vectorize_question(question)
+
+ # Retrieve top recommendation chunks (across all sizes) for this customer
+ cursor.execute("""
+ SELECT CHUNK_ID, CHUNK_TEXT
+ FROM LOAN_CHUNK
+ WHERE CUSTOMER_ID = :cust_id
+ AND CHUNK_VECTOR IS NOT NULL
+ ORDER BY VECTOR_DISTANCE(CHUNK_VECTOR, :qv, COSINE)
+ FETCH FIRST 4 ROWS ONLY
+ """, {'cust_id': selected_customer_id, 'qv': q_vec})
+ retrieved = [
+ (r[0], r[1].read() if isinstance(r[1], oracledb.LOB) else r[1])
+ for r in cursor.fetchall()
+ ]
+
+ if not retrieved:
+ # Fallback to full text as one chunk
+ retrieved = [(0, recommendations)]
+
+ # Prepare clean context for the LLM
+ cleaned = [re.sub(r'[^\w\s\d.,\-\'"]', ' ', t).strip() for _, t in retrieved]
+ docs_as_one_string = "\n=========\n".join(cleaned) + "\n=========\n"
+
+ # Rebuild available loans + customer profile
+ available_loans_text = "\n".join(
+ [f"{loan['LOAN_ID']}: {loan['LOAN_TYPE']} | {loan['INTEREST_RATE']}% interest | "
+ f"Credit Score: {loan['CREDIT_SCORE']} | DTI: {loan['DEBT_TO_INCOME_RATIO']} | "
+ f"Origination Fee: ${loan['ORIGINATION_FEE']} | Time to Close: {loan['TIME_TO_CLOSE']} days"
+ for loan in df_mock_loans.to_dict(orient='records')]
+ )
+ loan_app = customer_json.get("loanApplications", [{}])[0]
+ customer_profile_text = "\n".join(
+ [f"- {k.replace('_',' ').title()}: {v}"
+ for k, v in {**customer_json, **loan_app}.items()
+ if k not in ["embedding_vector","ai_response_vector","chunk_vector"]]
+ )
+
+ rag_prompt = f"""\
[INST] <>
You are AI Loan Guru. Use only the provided context to answer. Do not mention sources outside of the provided context.
- Do NOT provide warnings, disclaimers, or exceed the specified response length.
- Keep under 300 words. Be specific and actionable. Have the ability to respond in Spanish, French, Italian, German, and Portuguese if asked.
+Do NOT provide warnings, disclaimers, or exceed the specified response length.
+Keep under 300 words. Be specific and actionable. Have the ability to respond in Spanish, French, Italian, German, and Portuguese if asked.
<> [/INST]
[INST]
Question: "{question}"
@@ -418,40 +828,40 @@ Tasks:
2) Briefly justify based on profile + loan options.
[/INST]"""
- print("Generating AI response...")
-
- genai_client = oci.generative_ai_inference.GenerativeAiInferenceClient(
- config=oci.config.from_file(os.getenv("OCI_CONFIG_PATH","~/.oci/config")),
- service_endpoint=os.getenv("ENDPOINT")
- )
- chat_detail = oci.generative_ai_inference.models.ChatDetails(
- compartment_id=os.getenv("COMPARTMENT_OCID"),
- chat_request=oci.generative_ai_inference.models.GenericChatRequest(
- messages=[oci.generative_ai_inference.models.UserMessage(
- content=[oci.generative_ai_inference.models.TextContent(text=rag_prompt)]
- )],
- temperature=0.0,
- top_p=0.90
- ),
- serving_mode=oci.generative_ai_inference.models.OnDemandServingMode(
- model_id="meta.llama-3.2-90b-vision-instruct"
+ print("Generating AI response...")
+
+ genai_client = oci.generative_ai_inference.GenerativeAiInferenceClient(
+ config=oci.config.from_file(os.getenv("OCI_CONFIG_PATH","~/.oci/config")),
+ service_endpoint=os.getenv("ENDPOINT")
+ )
+ chat_detail = oci.generative_ai_inference.models.ChatDetails(
+ compartment_id=os.getenv("COMPARTMENT_OCID"),
+ chat_request=oci.generative_ai_inference.models.GenericChatRequest(
+ messages=[oci.generative_ai_inference.models.UserMessage(
+ content=[oci.generative_ai_inference.models.TextContent(text=rag_prompt)]
+ )],
+ temperature=0.0,
+ top_p=0.90
+ ),
+ serving_mode=oci.generative_ai_inference.models.OnDemandServingMode(
+ model_id="meta.llama-3.2-90b-vision-instruct"
+ )
)
- )
- chat_response = genai_client.chat(chat_detail)
- ai_response = chat_response.data.chat_response.choices[0].message.content[0].text
- ai_response = re.sub(r'[^\w\s\d.,\-\'"]', ' ', ai_response)
+ chat_response = genai_client.chat(chat_detail)
+ ai_response = chat_response.data.chat_response.choices[0].message.content[0].text
+ ai_response = re.sub(r'[^\w\s\d.,\-\'"]', ' ', ai_response)
- print("\n🤖 AI Loan Guru Response:")
- print(ai_response)
+ print("\n🤖 AI Loan Guru Response:")
+ print(ai_response)
- # Print which chunks were retrieved (for transparency/debug)
- print("\n📑 Retrieved Chunks Used in Response:")
- for cid, text in retrieved:
- preview = text[:140].replace("\n", " ") + ("..." if len(text) > 140 else "")
- print(f"[Chunk {cid}] : {preview}")
+ # Print which chunks were retrieved (for transparency/debug)
+ print("\n📑 Retrieved Chunks Used in Response:")
+ for cid, text in retrieved:
+ preview = text[:140].replace("\n", " ") + ("..." if len(text) > 140 else "")
+ print(f"[Chunk {cid}] : {preview}")
except Exception as e:
- print(f"RAG flow error: {e}")
+ print(f"RAG flow error: {e}")
```
@@ -488,5 +898,5 @@ You may now proceed to the next lab.
## Acknowledgements
* **Authors** - Francis Regalado
-* **Contributors** - Kevin Lazarz
+* **Contributors** - Kevin Lazarz, Linda Foinding
* **Last Updated By/Date** - Linda Foinding, September 2025
\ No newline at end of file
diff --git a/dev-ai-app-dev-finance/dev-guide-ai-finance/build/images/lab4task1.png b/dev-ai-app-dev-finance/dev-guide-ai-finance/build/images/lab4task1.png
new file mode 100644
index 00000000..853011d9
Binary files /dev/null and b/dev-ai-app-dev-finance/dev-guide-ai-finance/build/images/lab4task1.png differ
diff --git a/dev-ai-app-dev-finance/dev-guide-ai-finance/build/images/tas2.png b/dev-ai-app-dev-finance/dev-guide-ai-finance/build/images/tas2.png
new file mode 100644
index 00000000..cb46679e
Binary files /dev/null and b/dev-ai-app-dev-finance/dev-guide-ai-finance/build/images/tas2.png differ
diff --git a/dev-ai-app-dev-finance/dev-guide-ai-finance/build/images/task2result.png b/dev-ai-app-dev-finance/dev-guide-ai-finance/build/images/task2result.png
new file mode 100644
index 00000000..5cdcd48d
Binary files /dev/null and b/dev-ai-app-dev-finance/dev-guide-ai-finance/build/images/task2result.png differ
diff --git a/dev-ai-app-dev-finance/dev-guide-ai-finance/user-story/images/lab101.png b/dev-ai-app-dev-finance/dev-guide-ai-finance/user-story/images/lab101.png
new file mode 100644
index 00000000..4438d355
Binary files /dev/null and b/dev-ai-app-dev-finance/dev-guide-ai-finance/user-story/images/lab101.png differ
diff --git a/dev-ai-app-dev-finance/dev-guide-ai-finance/user-story/images/lab102.png b/dev-ai-app-dev-finance/dev-guide-ai-finance/user-story/images/lab102.png
new file mode 100644
index 00000000..21af547e
Binary files /dev/null and b/dev-ai-app-dev-finance/dev-guide-ai-finance/user-story/images/lab102.png differ
diff --git a/dev-ai-app-dev-finance/dev-guide-ai-finance/user-story/images/lab103.png b/dev-ai-app-dev-finance/dev-guide-ai-finance/user-story/images/lab103.png
new file mode 100644
index 00000000..8a53a515
Binary files /dev/null and b/dev-ai-app-dev-finance/dev-guide-ai-finance/user-story/images/lab103.png differ
diff --git a/dev-ai-app-dev-finance/dev-guide-ai-finance/user-story/images/lab104.png b/dev-ai-app-dev-finance/dev-guide-ai-finance/user-story/images/lab104.png
new file mode 100644
index 00000000..ad842b21
Binary files /dev/null and b/dev-ai-app-dev-finance/dev-guide-ai-finance/user-story/images/lab104.png differ
diff --git a/dev-ai-app-dev-finance/dev-guide-ai-finance/user-story/images/lab105.png b/dev-ai-app-dev-finance/dev-guide-ai-finance/user-story/images/lab105.png
new file mode 100644
index 00000000..a7960c75
Binary files /dev/null and b/dev-ai-app-dev-finance/dev-guide-ai-finance/user-story/images/lab105.png differ
diff --git a/dev-ai-app-dev-finance/dev-guide-ai-finance/user-story/images/lab106.png b/dev-ai-app-dev-finance/dev-guide-ai-finance/user-story/images/lab106.png
new file mode 100644
index 00000000..67e7e05e
Binary files /dev/null and b/dev-ai-app-dev-finance/dev-guide-ai-finance/user-story/images/lab106.png differ
diff --git a/dev-ai-app-dev-finance/dev-guide-ai-finance/user-story/images/lab131.png b/dev-ai-app-dev-finance/dev-guide-ai-finance/user-story/images/lab131.png
new file mode 100644
index 00000000..cdb0d82c
Binary files /dev/null and b/dev-ai-app-dev-finance/dev-guide-ai-finance/user-story/images/lab131.png differ
diff --git a/dev-ai-app-dev-finance/dev-guide-ai-finance/user-story/images/lab132.png b/dev-ai-app-dev-finance/dev-guide-ai-finance/user-story/images/lab132.png
new file mode 100644
index 00000000..4053f280
Binary files /dev/null and b/dev-ai-app-dev-finance/dev-guide-ai-finance/user-story/images/lab132.png differ
diff --git a/dev-ai-app-dev-finance/dev-guide-ai-finance/user-story/images/lab133.png b/dev-ai-app-dev-finance/dev-guide-ai-finance/user-story/images/lab133.png
new file mode 100644
index 00000000..aa06e8fb
Binary files /dev/null and b/dev-ai-app-dev-finance/dev-guide-ai-finance/user-story/images/lab133.png differ
diff --git a/dev-ai-app-dev-finance/dev-guide-ai-finance/user-story/images/lab134.png b/dev-ai-app-dev-finance/dev-guide-ai-finance/user-story/images/lab134.png
new file mode 100644
index 00000000..e98ed2eb
Binary files /dev/null and b/dev-ai-app-dev-finance/dev-guide-ai-finance/user-story/images/lab134.png differ
diff --git a/dev-ai-app-dev-finance/dev-guide-ai-finance/user-story/images/lab135.png b/dev-ai-app-dev-finance/dev-guide-ai-finance/user-story/images/lab135.png
new file mode 100644
index 00000000..6636ecc7
Binary files /dev/null and b/dev-ai-app-dev-finance/dev-guide-ai-finance/user-story/images/lab135.png differ
diff --git a/dev-ai-app-dev-finance/dev-guide-ai-finance/user-story/images/lab136.png b/dev-ai-app-dev-finance/dev-guide-ai-finance/user-story/images/lab136.png
new file mode 100644
index 00000000..662c202a
Binary files /dev/null and b/dev-ai-app-dev-finance/dev-guide-ai-finance/user-story/images/lab136.png differ
diff --git a/dev-ai-app-dev-finance/dev-guide-ai-finance/user-story/images/lab137.png b/dev-ai-app-dev-finance/dev-guide-ai-finance/user-story/images/lab137.png
new file mode 100644
index 00000000..100b4787
Binary files /dev/null and b/dev-ai-app-dev-finance/dev-guide-ai-finance/user-story/images/lab137.png differ
diff --git a/dev-ai-app-dev-finance/dev-guide-ai-finance/user-story/images/lowincome.png b/dev-ai-app-dev-finance/dev-guide-ai-finance/user-story/images/lowincome.png
new file mode 100644
index 00000000..194a265e
Binary files /dev/null and b/dev-ai-app-dev-finance/dev-guide-ai-finance/user-story/images/lowincome.png differ
diff --git a/dev-ai-app-dev-finance/dev-guide-ai-finance/user-story/user-story.md b/dev-ai-app-dev-finance/dev-guide-ai-finance/user-story/user-story.md
index e3734510..d3a19bf9 100644
--- a/dev-ai-app-dev-finance/dev-guide-ai-finance/user-story/user-story.md
+++ b/dev-ai-app-dev-finance/dev-guide-ai-finance/user-story/user-story.md
@@ -26,13 +26,13 @@ This lab assumes you have:

-2. Enter in a username and click **Login**.
+2. Welcome to Seer Holdings! Select **Finance** as Industry and **Approval Officer** as role. Enter in a username and click **Login**.
- 
+ 
3. Welcome to the SeerEquities Loan Management application! Congratulations, you are now connected to the demo environment. You can now execute the different tasks for this Lab.
- 
+ 
## Task 2: Demo - Customer with strong credit score
@@ -40,11 +40,11 @@ In this first example, you will use the application to approve a customer with s
1. On the Dashboard page, from the pending review list, select the Customer ID for **James Smith**.
- 
+ 
2. Opening James Smith’s profile reveals his loan application details—name, location, requested amount, debt, and credit score.
- 
+ 
3. At the bottom of James Smith’s profile, you will find the **AI Loan Guru**—a chatbot built on Oracle Database 23ai and Vector search. When prompted, the system uses **RAG** to generate a response. It converts the question and loan data into embeddings, performs a similarity search, and then uses the **GenAI service** to turn the enriched context into a clear, natural language answer. If the customer calls with a question, you can quickly enter it into the AI Loan Guru to generate a relevant response.
@@ -57,13 +57,13 @@ In this first example, you will use the application to approve a customer with s
```
- 
+ 
>💡 In Oracle Database 23ai, **AI Vector Search** allows you to combine your business data with the Large Language Model (LLM) to reduce hallucinations and get accurate answers from your data.
4. Select the **Navigate To Decisions** button.
- 
+ 
After navigating to the decisions page, the AI evaluation runs in the background. It analyzes James’s profile and matches it against available loan options in the database. A custom AI prompt ensures the system uses only internal data—never the internet. In this case, the AI returns three loan options, each with a clear explanation. All options are displayed alongside the AI’s final recommendation: approval.
@@ -120,7 +120,7 @@ In this example, you will navigate the application to review a customer and deny
1. On the Dashboard page, from the pending review list, select the Customer ID for **James Woods**.
- 
+ 
2. Opening James Woods’s profile displays his loan application details. Within a few seconds, the AI automatically generates recommendations. In this case, the system evaluates a less favorable profile and identifies key risk factors.
@@ -132,11 +132,11 @@ In this example, you will navigate the application to review a customer and deny
Despite the risk factors, the AI evaluates the profile and suggests next steps. In this case, it recommends a denial—but also provides clear, actionable guidance to help the customer improve their chances of approval in the future.
- 
+ 
3. Select the **Navigate to Decisions** button.
- 
+ 
>⁉️ **What is the reason that the AI decided to deny this applicant?** ⁉️
@@ -238,6 +238,6 @@ By combining these advanced tools, the application enables faster, smarter decis
* [Oracle Database 23ai Documentation](https://docs.oracle.com/en/database/oracle/oracle-database/23/)
## Acknowledgements
-* **Authors** - Kamryn Vinson, Linda Foinding, Francis Regalado
+* **Authors** - Linda Foinding, Francis Regalado
* **Contributors** - Kevin Lazarz, Eddie Ambler, Ramona Magadan, Mark Nelson, Andy Tael, Anders Swanson, Rahul Tasker
-* **Last Updated By/Date** - Linda Foinding, June 2025
\ No newline at end of file
+* **Last Updated By/Date** - Linda Foinding, September 2025
\ No newline at end of file