diff --git a/dev-ai-app-dev-finance/build/build.md b/dev-ai-app-dev-finance/build/build.md
index ecea08ce0..496bf63a9 100644
--- a/dev-ai-app-dev-finance/build/build.md
+++ b/dev-ai-app-dev-finance/build/build.md
@@ -172,41 +172,143 @@ With customer profiles in place, you will use OCI Generative AI to generate pers
Here’s what we’ll do:
- **Fetch Mock Loan Data**: Retrieve all mock loan data and combine them with customer data.
- **Build a Prompt**: Construct a structured prompt that combines the customer’s profile with loan requests instructing the LLM to evaluate and recommend a loan (APPROVE, REQUEST INFO, DENY) based solely on this data.
-- **Use OCI Generative AI**: Send the prompt to the `meta.llama-3.2-90b-vision-instruct` model via OCI’s inference client, which will process the input and generate a response.
+- **Use OCI Generative AI**: Send the prompt to the `cohere.command-r-plus-08-2024` model via OCI’s inference client, which will process the input and generate a response.
- **Format the Output**: Display the recommendations with styled headers and lists, covering evaluation, top picks, and explanations—making it easy to read and understand.
1. Copy and paste the code in a new cell:
```python
- # Fetch Mock Loan Data
-cursor.execute("SELECT 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 FROM MOCK_LOAN_DATA")
-df_mock_loans = pd.DataFrame(cursor.fetchall(), columns=["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"])
+ def generate_recommendations(customer_id, customer_json, df_policy_rules):
+ try:
+ return_request = customer_json.get("returnRequests", [{}])[0]
+ recommendation = return_request.get("recommendation", {})
+ reason = recommendation.get("reason", {})
+
+ cursor.execute("""
+ SELECT p.PRODUCT_NAME
+ FROM PRODUCTS p
+ JOIN ORDERITEMS oi ON p.PRODUCT_ID = oi.PRODUCT_ID
+ JOIN RETURN_REASONS rr ON oi.ORDERITEMS_ID = rr.ORDERITEMS_ID
+ WHERE rr.REASON_ID = :reason_id
+ """, {"reason_id": int(reason.get("reasonId", 0))})
+ product_result = cursor.fetchone()
+ product_name = product_result[0] if product_result else "Unknown Product"
+
+ available_rules_text = "\n".join(
+ f"{rule['RULE_ID']}: {rule['RULE_CODE']} | "
+ f"{rule['RULE_DESCRIPTION']} | Applies To: {rule['APPLIES_TO']}"
+ for rule in df_policy_rules.to_dict(orient="records")
+ )
+ customer_profile_text = "\n".join(
+ f"- {key.replace('_', ' ').title()}: {value}"
+ for key, value in customer_json.items()
+ if key not in ["returnRequests", "_metadata"]
+ )
+ return_request_text = "\n".join(
+ f"- {key.replace('_', ' ').title()}: {value}"
+ for key, value in return_request.items()
+ if key != "recommendation"
+ )
+ reason_text = f"- Return Reason: {reason.get('description', 'N/A')}"
-# 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"""You are a Retail Decision AI. Use only the supplied context.
+ Evaluate the return request and recommend APPROVE, REQUEST INFO, or DENY.
- 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"""
+ Available Policy Rules:
+ {available_rules_text}
- 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
+ Customer Profile:
+ {customer_profile_text}
+
+ Return Request:
+ {return_request_text}
+ {reason_text}
+
+ Respond with these sections:
+
+ Suggested Action
+ - State APPROVE, REQUEST INFO, or DENY.
+
+ Comprehensive Evaluation
+ - Explain customer history, loyalty, return frequency, receipt, product condition,
+ return amount, and risk level.
+
+ Top 3 Recommendations
+ - Provide up to three specific recommendations with supporting policy rules.
+
+ Recommendations Explanation
+ - Explain how available evidence supports the recommendation.
+
+ Risk Management
+ - Identify safeguards such as additional evidence, partial refund, or store credit.
+
+ Actionable Steps
+ - List the next actions the customer or reviewer should take.
+
+ Keep the response under 500 words and use plain text."""
+
+ 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"),
+ )
- return recommendations
+ # Compatible with the OCI SDK currently installed in this notebook.
+ models = oci.generative_ai_inference.models
+ chat_request = models.CohereChatRequest(
+ api_format="COHERE",
+ message=prompt,
+ max_tokens=800,
+ temperature=0.0,
+ top_p=1.0,
+ )
+ chat_detail = models.ChatDetails(
+ compartment_id=os.getenv("COMPARTMENT_OCID"),
+ chat_request=chat_request,
+ serving_mode=models.OnDemandServingMode(
+ model_id="cohere.command-r-plus-08-2024"
+ ),
+ )
- recommendations = generate_recommendations(selected_customer_id, customer_json, df_mock_loans)
+ chat_response = genai_client.chat(chat_detail)
+ chat_result = chat_response.data.chat_response
+ return (
+ getattr(chat_result, "text", None)
+ or chat_result.choices[0].message.content[0].text
+ )
+
+ except oracledb.DatabaseError as e:
+ print(f"Database error: {e}")
+ return None
+ except Exception as e:
+ print(f"Unexpected error in generate_recommendations: {e}")
+ return None
+
+
+ print("Fetching policy rules...")
+ cursor.execute("""
+ SELECT rule_id, rule_code, rule_description, applies_to, is_active
+ FROM RETURN_POLICY_RULES
+ """)
+ df_policy_rules = pd.DataFrame(
+ cursor.fetchall(),
+ columns=[
+ "RULE_ID", "RULE_CODE", "RULE_DESCRIPTION",
+ "APPLIES_TO", "IS_ACTIVE",
+ ],
+ )
+
+ recommendations = generate_recommendations(
+ selected_customer_id,
+ customer_json,
+ df_policy_rules,
+ )
+
+ print("\nAI Recommendation:\n")
print(recommendations)
```
@@ -351,23 +453,59 @@ This step:
```python
-question = "What 4th loan would James qualify for?"
-
-def vectorize_question(q):
+ # Fetch loan data needed by the RAG prompt.
+ cursor.execute("""
+ SELECT
+ 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
+ FROM MOCK_LOAN_DATA
+ """)
+
+ df_mock_loans = pd.DataFrame(
+ cursor.fetchall(),
+ columns=[
+ "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",
+ ],
+ )
+
+ 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})
+ )
+ FROM DUAL
+ """, {"q": q})
return cursor.fetchone()[0]
-print("Processing your question using AI Vector Search across chunked recommendations...")
-try:
+ 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
@@ -375,90 +513,114 @@ try:
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})
+ """, {"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()
+ (
+ row[0],
+ row[1].read() if isinstance(row[1], oracledb.LOB) else row[1],
+ )
+ for row 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]
+ cleaned = [
+ re.sub(r"""[^\w\s\d.,\-\'"]""", " ", text).strip()
+ for _, text 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')]
+ f"{loan['LOAN_ID']}: {loan['LOAN_TYPE']} | "
+ f"{loan['INTEREST_RATE']}% interest | "
+ f"Credit Score: {loan['CREDIT_SCORE']} | "
+ f"DTI: {loan['DEBT_TO_INCOME_RATIO']} | "
+ f"Income Required: ${loan['INCOME']} | "
+ f"Origination Fee: ${loan['ORIGINATION_FEE']} | "
+ f"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"]]
+ 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",
+ ]
)
- 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.
-<> [/INST]
-[INST]
-Question: "{question}"
+ rag_prompt = f"""You are AI Loan Guru.
+ Use only the provided context.
+
+ Question:
+ {question}
-# Context (top chunks from prior AI recommendations):
-{docs_as_one_string}
+ Retrieved Recommendation Context:
+ {docs_as_one_string}
-# Available Loan Options:
-{available_loans_text}
+ Available Loan Options:
+ {available_loans_text}
-# Applicant Profile:
-{customer_profile_text}
+ Applicant Profile:
+ {customer_profile_text}
-Tasks:
-1) Provide a direct answer to the question.
-2) Briefly justify based on profile + loan options.
-[/INST]"""
+ Tasks:
+ 1. Answer the question directly.
+ 2. Identify the fourth qualifying loan, if one exists.
+ 3. Justify it using credit score, DTI, income, and eligibility requirements.
+ 4. If no fourth loan qualifies, explain why.
+
+ Keep the answer under 300 words."""
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")
+ 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)]
- )],
+ messages=[
+ oci.generative_ai_inference.models.UserMessage(
+ content=[
+ oci.generative_ai_inference.models.TextContent(
+ text=rag_prompt
+ )
+ ]
+ )
+ ],
+ max_tokens=800,
temperature=0.0,
- top_p=0.90
+ top_p=0.9,
),
serving_mode=oci.generative_ai_inference.models.OnDemandServingMode(
- model_id="meta.llama-3.2-90b-vision-instruct"
- )
+ model_id="meta.llama-3.3-70b-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)
- print("\n🤖 AI Loan Guru Response:")
+ print("\n🤖 AI Loan Guru Response:\n")
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}")
+ for chunk_id, text in retrieved:
+ preview = text[:140].replace("\n", " ")
+ if len(text) > 140:
+ preview += "..."
+ print(f"[Chunk {chunk_id}] {preview}")
-except Exception as e:
+ except Exception as e:
print(f"RAG flow error: {e}")
```