|
"""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 |
|
|