| """All prompts utilized by the RAG pipeline""" |
| from langchain_core.prompts import ChatPromptTemplate, PromptTemplate, SystemMessagePromptTemplate, HumanMessagePromptTemplate, MessagesPlaceholder |
| from langchain_core.output_parsers import StrOutputParser |
| from langchain_openai import ChatOpenAI |
| from pydantic import BaseModel, Field |
| from langchain_google_genai import ChatGoogleGenerativeAI |
| import os |
| from tools import json_to_table, goal_feasibility, save_data, rag_tool |
| from langchain.agents import initialize_agent, Tool |
| from langchain.agents import AgentType |
| from langgraph.prebuilt import create_react_agent |
| from langchain.tools import Tool |
| from dotenv import load_dotenv |
| load_dotenv() |
|
|
|
|
| gemini = ChatGoogleGenerativeAI(model = 'gemini-2.0-flash') |
| llm = ChatOpenAI( |
| model='gpt-4.1-nano', |
| api_key=os.environ.get('OPEN_AI_KEY'), |
| temperature=0.2 |
| ) |
|
|
| |
| class GradeDocuments(BaseModel): |
| binary_score: str = Field(description="Documents are relevant to the question, 'yes' or 'no'") |
|
|
| structured_llm_grader = llm.with_structured_output(GradeDocuments) |
| system = """You are a grader assessing relevance of a retrieved document to a user question. |
| If the document contains keyword(s) or semantic meaning related to the question, grade it as relevant. |
| Give a binary score 'yes' or 'no' score to indicate whether the document is relevant to the question.""" |
|
|
| grade_prompt = ChatPromptTemplate.from_messages([ |
| ("system", system), |
| ("human", "Retrieved document: \n\n {data} \n\n User question: {query}") |
| ]) |
|
|
| retrieval_grader = grade_prompt | structured_llm_grader |
|
|
|
|
| prompt = PromptTemplate( |
| template=''' |
| You are a SEBI-Registered Investment Advisor (RIA) specializing in Indian financial markets and client relationship management. |
| |
| Your task is to understand and respond to the user's financial query using the following inputs: |
| - Query: {query} |
| - Documents: {data} |
| - User Profile: {user_data} |
| - Savings Allocations: {allocations} |
| |
| |
| |
| Instructions: |
| 1. Understand the User's Intent: Carefully interpret what the user is asking about their investments. |
| 2. Analyze Allocations: Evaluate the savings allocation data to understand the user's current financial posture. |
| 3. Personalized Response: |
| - If detailed user profile and allocation data are available, prioritize your response based on this data. |
| - If profile or allocation data is sparse, rely more heavily on the query context. |
| 4. Use Supporting Documents: Extract relevant insights from the provided documents ({data}) to support your answer. |
| 5. When Unsure: If the documents or data do not contain the necessary information, say "I don't know" rather than guessing. |
| |
| Always aim to give a response that is: |
| - Data-informed |
| - Client-centric |
| - Aligned with Indian financial regulations and norms |
| |
| |
| ''', |
| input_variables=['query', 'data', 'user_data', 'allocations'] |
| ) |
|
|
| rag_chain = prompt | gemini | StrOutputParser() |
|
|
|
|
| |
| system_rewrite = """You a question re-writer that converts an input question to a better version that is optimized \n |
| for web search. Look at the input and try to reason about the underlying semantic intent / meaning.""" |
| re_write_prompt = ChatPromptTemplate.from_messages( |
| [ |
| ("system", system_rewrite), |
| ( |
| "human", |
| "Here is the initial question: \n\n {query} \n Formulate an improved question.", |
| ), |
| ] |
| ) |
|
|
| question_rewriter = re_write_prompt | llm | StrOutputParser() |
|
|
|
|
| from pydantic import BaseModel, Field, RootModel |
| from typing import Dict |
| from langchain_core.output_parsers import JsonOutputParser |
|
|
| |
| class CategoryProbabilities(RootModel): |
| """Probabilities for different knowledge base categories.""" |
| root: Dict[str, float] = Field(description="Dictionary mapping category names to probability scores") |
|
|
| system_classifier = """You are a query classifier that determines the most relevant knowledge bases (KBs) for a given user query. |
| Analyze the semantic meaning and intent of the query and assign probability scores (between 0 and 1) to each KB. |
| |
| Ensure the probabilities sum to 1 and output a JSON dictionary with category names as keys and probabilities as values. |
| """ |
|
|
| classification_prompt = ChatPromptTemplate.from_messages( |
| [ |
| ("system", system_classifier), |
| ( |
| "human", |
| "Here is the user query: \n\n {query} \n\n Assign probability scores to each of the following KBs:\n" |
| "{categories}\n\nReturn a JSON object with category names as keys and probability scores as values." |
| ), |
| ] |
| ) |
|
|
| |
| json_parser = JsonOutputParser(pydantic_object=CategoryProbabilities) |
|
|
| |
| query_classifier = classification_prompt | llm | json_parser |
|
|
|
|
| |
|
|
| """ |
| name: str |
| |
| position: Dict[str, int] |
| riskiness: int |
| illiquidity: int |
| |
| amount: float |
| currency: str = "inr" |
| percentage: float |
| explanation: Dict[str, str] |
| |
| assets: List[AssetAllocation] |
| """ |
| |
| tools = [ |
| { |
| "type": "function", |
| "function": { |
| "name": "json_to_table", |
| "description": "Convert JSON data to a markdown table. Use when user asks to visualise or tabulate structured data.", |
| "parameters": { |
| "type": "object", |
| "properties": { |
| "arguments": { |
| "type": "object", |
| "properties": { |
| "json_data": { |
| "type": "object", |
| "description": "The JSON data to convert to a table" |
| } |
| }, |
| "required": ["json_data"] |
| } |
| }, |
| "required": ["arguments"] |
| } |
| } |
| }, |
| { |
| "type": "function", |
| "function": { |
| "name": "rag_tool", |
| "description": "Lets the agent use RAG system as a tool", |
| "parameters": { |
| "type": "object", |
| "properties": { |
| "arguments": { |
| "type": "object", |
| "properties": { |
| "query": { |
| "type": "string", |
| "description": "The query to search for in the RAG system" |
| } |
| }, |
| "required": ["query"] |
| } |
| }, |
| "required": ["arguments"] |
| } |
| } |
| } |
| ] |
|
|
|
|
| template = '''You are a SEBI-Registered Investment Advisor (RIA) specializing in Indian financial markets and client relationship management. |
| |
| Your task is to understand and respond to the user's financial query using the following inputs: |
| - Query: {query} |
| - User Profile: {user_data} |
| - Savings Allocations: {allocations} |
| - Chat History: {chat_history} |
| - π Retrieved Context (optional): {retrieved_context} |
| |
| Instructions: |
| 1. **Understand the User's Intent**: Carefully interpret what the user is asking about their investments. If a user input contradicts previously stated preferences or profile attributes (e.g., low risk appetite or crypto aversion), ask a clarifying question before proceeding. Do not update allocations or goals unless the user confirms the change explicitly. |
| 2. **Analyze Allocations**: Evaluate the savings allocation data to understand the user's current financial posture. |
| 3. **Use Retrieved Context**: If any contextual information is provided in `retrieved_context`, leverage it to improve your response quality and relevance. |
| 4. **Always Update Information**: If the user shares any new demographic, financial, or preference-related data, update the user profile accordingly. If they request changes in their allocations, ensure the changes are applied **proportionally** and that the total allocation always sums to 100%. |
| 5. **IMPORTANT: When displaying or updating allocations, you MUST format the data as a Markdown table and always display allocations as a table only** using the following columns: |
| - Asset Class |
| - Type |
| - Label |
| - Old Amount (βΉ) |
| - Change (βΉ) |
| - New Amount (βΉ) |
| - Justification |
| |
| |
| 7. **Maintain Conversational Memory**: Ensure updates are passed to memory using the specified `updates` structure. |
| 8. **Tool Use Policy**: |
| - β
Use `rag_tool` for retrieving **external financial knowledge or regulation** context when necessary. |
| |
| |
| --- |
| |
| ### π― Response Style Guide: |
| |
| - π Keep it under 300 words. |
| - π Friendly tone: be warm and helpful. |
| - π Structured: use bullet points, short paragraphs, and headers. |
| - π Visually clear: break sections clearly. |
| - π Use emojis to guide attention and convey tone. |
| - π― Be direct and focused on the user's request. |
| |
| --- |
| |
| ### π If There Are Allocation Changes: |
| |
| You **must** display a Markdown table as per the format above. Then, return memory update instructions using this JSON structure: |
| ```json |
| {{ |
| "updates": {{ |
| "user_data": {{ ... }}, // Include only changed fields |
| "allocations": {{...}} // Include only changed rows |
| }} |
| }} |
| ''' |
|
|
| |
| simple_prompt = ChatPromptTemplate.from_messages([ |
| SystemMessagePromptTemplate.from_template(template=template), |
| MessagesPlaceholder(variable_name="chat_history", optional=True), |
| HumanMessagePromptTemplate.from_template("User Query: {query}"), |
| HumanMessagePromptTemplate.from_template("Current User Profile:\n{user_data}"), |
| HumanMessagePromptTemplate.from_template("Current Allocations:\n{allocations}"), |
| HumanMessagePromptTemplate.from_template("π Retrieved Context (if any):\n{retrieved_context}"), |
| ]) |
|
|
| |
| llm = ChatOpenAI( |
| temperature=0.1, |
| model="gpt-4.1-nano", |
|
|
| ) |
| llm_with_tools = llm.bind_tools(tools) |
| simple_chain = simple_prompt | llm_with_tools |
|
|