Spaces:
Sleeping
Sleeping
Commit ·
28baf2e
1
Parent(s): 86ca72c
Added Plan Recommendations Functionality
Browse files
.gitignore
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# --- Python ---
|
| 2 |
+
# Byte-compiled / optimized / C files
|
| 3 |
+
__pycache__/
|
| 4 |
+
*.py[cod]
|
| 5 |
+
*$py.class
|
| 6 |
+
|
| 7 |
+
# C extensions
|
| 8 |
+
*.so
|
| 9 |
+
|
| 10 |
+
# Distribution / packaging
|
| 11 |
+
.Python
|
| 12 |
+
build/
|
| 13 |
+
develop-eggs/
|
| 14 |
+
dist/
|
| 15 |
+
downloads/
|
| 16 |
+
eggs/
|
| 17 |
+
.eggs/
|
| 18 |
+
lib/
|
| 19 |
+
lib64/
|
| 20 |
+
parts/
|
| 21 |
+
sdist/
|
| 22 |
+
var/
|
| 23 |
+
wheels/
|
| 24 |
+
pip-wheel-metadata/
|
| 25 |
+
share/python-wheels/
|
| 26 |
+
*.egg-info/
|
| 27 |
+
.installed.cfg
|
| 28 |
+
*.egg
|
| 29 |
+
MANIFEST
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
# --- Virtual Environments ---
|
| 33 |
+
# Common virtual environment names
|
| 34 |
+
.venv/
|
| 35 |
+
venv/
|
| 36 |
+
env/
|
| 37 |
+
ENV/
|
| 38 |
+
# User-specific virtual environment name
|
| 39 |
+
ic_venv/
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
# --- Project-specific Files ---
|
| 43 |
+
# Local data and databases - These are generated by scripts and should not be versioned
|
| 44 |
+
data/
|
| 45 |
+
*.db
|
| 46 |
+
*.sqlite
|
| 47 |
+
*.sqlite3
|
| 48 |
+
insucompass.db
|
| 49 |
+
checkpoints.db
|
| 50 |
+
|
| 51 |
+
# Log files
|
| 52 |
+
*.log
|
| 53 |
+
*.log.*
|
| 54 |
+
|
| 55 |
+
# Secrets - CRITICAL: Never commit your .env file
|
| 56 |
+
.env
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
# --- IDE / Editor Configurations ---
|
| 60 |
+
.idea/
|
| 61 |
+
.vscode/
|
| 62 |
+
*.sublime-project
|
| 63 |
+
*.sublime-workspace
|
| 64 |
+
|
| 65 |
+
|
| 66 |
+
# --- Operating System Files ---
|
| 67 |
+
# macOS
|
| 68 |
+
.DS_Store
|
| 69 |
+
.AppleDouble
|
| 70 |
+
.LSOverride
|
| 71 |
+
|
| 72 |
+
# Windows
|
| 73 |
+
Thumbs.db
|
| 74 |
+
ehthumbs.db
|
| 75 |
+
Desktop.ini
|
| 76 |
+
|
| 77 |
+
|
| 78 |
+
# --- Testing ---
|
| 79 |
+
# Pytest cache and coverage reports
|
| 80 |
+
.pytest_cache/
|
| 81 |
+
.coverage
|
| 82 |
+
.coverage.*
|
| 83 |
+
htmlcov/
|
| 84 |
+
nosetests.xml
|
| 85 |
+
coverage.xml
|
insucompass/api/endpoints.py
CHANGED
|
@@ -61,6 +61,7 @@ async def chat(request: ChatRequest):
|
|
| 61 |
updated_profile = final_state.get("user_profile")
|
| 62 |
updated_history = final_state.get("conversation_history")
|
| 63 |
is_profile_complete = final_state.get("is_profile_complete")
|
|
|
|
| 64 |
|
| 65 |
if not agent_response:
|
| 66 |
agent_response = "I'm sorry, I encountered an issue. Could you please rephrase?"
|
|
@@ -71,7 +72,8 @@ async def chat(request: ChatRequest):
|
|
| 71 |
agent_response=agent_response,
|
| 72 |
updated_profile=updated_profile,
|
| 73 |
updated_history=updated_history,
|
| 74 |
-
is_profile_complete=is_profile_complete
|
|
|
|
| 75 |
)
|
| 76 |
|
| 77 |
except Exception as e:
|
|
|
|
| 61 |
updated_profile = final_state.get("user_profile")
|
| 62 |
updated_history = final_state.get("conversation_history")
|
| 63 |
is_profile_complete = final_state.get("is_profile_complete")
|
| 64 |
+
plan_recs = final_state.get("plan_recommendations")
|
| 65 |
|
| 66 |
if not agent_response:
|
| 67 |
agent_response = "I'm sorry, I encountered an issue. Could you please rephrase?"
|
|
|
|
| 72 |
agent_response=agent_response,
|
| 73 |
updated_profile=updated_profile,
|
| 74 |
updated_history=updated_history,
|
| 75 |
+
is_profile_complete=is_profile_complete,
|
| 76 |
+
plan_recommendations=plan_recs
|
| 77 |
)
|
| 78 |
|
| 79 |
except Exception as e:
|
insucompass/core/agent_orchestrator.py
CHANGED
|
@@ -11,6 +11,7 @@ from langgraph.checkpoint.sqlite import SqliteSaver
|
|
| 11 |
|
| 12 |
# Import all our custom agent and service classes
|
| 13 |
from insucompass.core.agents.profile_agent import profile_builder
|
|
|
|
| 14 |
from insucompass.core.agents.query_trasformer import QueryTransformationAgent
|
| 15 |
from insucompass.core.agents.router_agent import router
|
| 16 |
from insucompass.services.ingestion_service import IngestionService
|
|
@@ -40,6 +41,7 @@ class AgentState(TypedDict):
|
|
| 40 |
documents: List[Document]
|
| 41 |
is_relevant: bool
|
| 42 |
generation: str
|
|
|
|
| 43 |
|
| 44 |
# --- Graph Nodes ---
|
| 45 |
|
|
@@ -63,12 +65,43 @@ def profile_builder_node(state: AgentState) -> Dict[str, Any]:
|
|
| 63 |
|
| 64 |
if agent_response == "PROFILE_COMPLETE":
|
| 65 |
logger.info("Profile building complete.")
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
|
|
|
|
|
|
| 69 |
|
| 70 |
return {"user_profile": updated_profile, "is_profile_complete": False, "conversation_history": new_history, "generation": agent_response}
|
| 71 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 72 |
def reformulate_query_node(state: AgentState) -> Dict[str, Any]:
|
| 73 |
"""Reformulates the user's question to be self-contained."""
|
| 74 |
logger.info("---NODE: REFORMULATE QUERY---")
|
|
@@ -120,12 +153,19 @@ def decide_entry_point(state: AgentState) -> str:
|
|
| 120 |
"""Decides the initial path based on profile completion status."""
|
| 121 |
logger.info("---ROUTING: ENTRY POINT---")
|
| 122 |
if state.get("is_profile_complete"):
|
| 123 |
-
logger.info(">>> Route: Profile is complete. Starting Q&A.")
|
| 124 |
return "qna"
|
| 125 |
else:
|
| 126 |
-
logger.info(">>> Route: Profile is not complete. Starting Profile Builder.")
|
| 127 |
return "profile"
|
| 128 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 129 |
# --- Build the Graph ---
|
| 130 |
db_connection = sqlite3.connect("data/checkpoints.db", check_same_thread=False)
|
| 131 |
memory = SqliteSaver(db_connection)
|
|
@@ -134,6 +174,7 @@ builder = StateGraph(AgentState)
|
|
| 134 |
|
| 135 |
# (CORRECTED) Removed the faulty entry_router_node
|
| 136 |
builder.add_node("profile_builder", profile_builder_node)
|
|
|
|
| 137 |
builder.add_node("reformulate_query", reformulate_query_node)
|
| 138 |
builder.add_node("retrieve_and_grade", retrieve_and_grade_node)
|
| 139 |
builder.add_node("search_and_ingest", search_and_ingest_node)
|
|
@@ -148,8 +189,19 @@ builder.set_conditional_entry_point(
|
|
| 148 |
}
|
| 149 |
)
|
| 150 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 151 |
# Define graph edges
|
| 152 |
builder.add_edge("profile_builder", END) # A profile turn is one full loop. The state is saved, and the next call will re-evaluate at the entry point.
|
|
|
|
| 153 |
builder.add_edge("reformulate_query", "retrieve_and_grade")
|
| 154 |
builder.add_conditional_edges("retrieve_and_grade", should_search_web, {"search": "search_and_ingest", "generate": "generate_answer"})
|
| 155 |
builder.add_edge("search_and_ingest", "generate_answer")
|
|
|
|
| 11 |
|
| 12 |
# Import all our custom agent and service classes
|
| 13 |
from insucompass.core.agents.profile_agent import profile_builder
|
| 14 |
+
from insucompass.core.agents.plan_agent import planner
|
| 15 |
from insucompass.core.agents.query_trasformer import QueryTransformationAgent
|
| 16 |
from insucompass.core.agents.router_agent import router
|
| 17 |
from insucompass.services.ingestion_service import IngestionService
|
|
|
|
| 41 |
documents: List[Document]
|
| 42 |
is_relevant: bool
|
| 43 |
generation: str
|
| 44 |
+
plan_recommendations: Dict[str, Any]
|
| 45 |
|
| 46 |
# --- Graph Nodes ---
|
| 47 |
|
|
|
|
| 65 |
|
| 66 |
if agent_response == "PROFILE_COMPLETE":
|
| 67 |
logger.info("Profile building complete.")
|
| 68 |
+
return {"user_profile": updated_profile, "is_profile_complete": True, "conversation_history": new_history, "generation": "PROFILE_COMPLETE_TRANSITION"}
|
| 69 |
+
|
| 70 |
+
# final_message = "Great! Your profile is complete. How can I help you with your health insurance questions?"
|
| 71 |
+
# new_history[-1] = f"Agent: {final_message}" # Replace "PROFILE_COMPLETE"
|
| 72 |
+
# return {"user_profile": updated_profile, "is_profile_complete": True, "conversation_history": new_history, "generation": final_message}
|
| 73 |
|
| 74 |
return {"user_profile": updated_profile, "is_profile_complete": False, "conversation_history": new_history, "generation": agent_response}
|
| 75 |
|
| 76 |
+
def plan_recommender_node(state: AgentState) -> Dict[str, Any]:
|
| 77 |
+
"""
|
| 78 |
+
(NEW NODE) Generates initial plan recommendations after profile is complete.
|
| 79 |
+
"""
|
| 80 |
+
logger.info("---NODE: PLAN RECOMMENDER---")
|
| 81 |
+
user_profile = state["user_profile"]
|
| 82 |
+
|
| 83 |
+
# Formulate a generic query to retrieve a broad set of plan documents
|
| 84 |
+
initial_plan_query = f"Find general health insurance plan options available in {user_profile.get('state', 'the US')} suitable for a {user_profile.get('age')}-year-old."
|
| 85 |
+
|
| 86 |
+
# Retrieve documents
|
| 87 |
+
documents = transformer.transform_and_retrieve(initial_plan_query)
|
| 88 |
+
|
| 89 |
+
# Generate structured recommendations
|
| 90 |
+
recommendations = planner.generate_recommendations(user_profile, documents)
|
| 91 |
+
|
| 92 |
+
# Create a human-friendly message to present the plans
|
| 93 |
+
generation_message = "Thank you! I've completed your profile and based on your information, here are a few initial plan recommendations for you to consider. You can ask me more detailed questions about them."
|
| 94 |
+
|
| 95 |
+
history = state["conversation_history"]
|
| 96 |
+
# history.append(f"Agent: {generation_message}")
|
| 97 |
+
history[-1] = f"Agent: {generation_message}"
|
| 98 |
+
|
| 99 |
+
return {
|
| 100 |
+
"plan_recommendations": recommendations,
|
| 101 |
+
"generation": generation_message,
|
| 102 |
+
"conversation_history": history
|
| 103 |
+
}
|
| 104 |
+
|
| 105 |
def reformulate_query_node(state: AgentState) -> Dict[str, Any]:
|
| 106 |
"""Reformulates the user's question to be self-contained."""
|
| 107 |
logger.info("---NODE: REFORMULATE QUERY---")
|
|
|
|
| 153 |
"""Decides the initial path based on profile completion status."""
|
| 154 |
logger.info("---ROUTING: ENTRY POINT---")
|
| 155 |
if state.get("is_profile_complete"):
|
|
|
|
| 156 |
return "qna"
|
| 157 |
else:
|
|
|
|
| 158 |
return "profile"
|
| 159 |
|
| 160 |
+
def after_profile_build(state: AgentState) -> str:
|
| 161 |
+
"""Checks if the profile was just completed."""
|
| 162 |
+
if state.get("is_profile_complete"):
|
| 163 |
+
logger.info(">>> Route: Profile just completed. Transitioning to Plan Recommender.")
|
| 164 |
+
return "recommend_plans"
|
| 165 |
+
else:
|
| 166 |
+
logger.info(">>> Route: Profile not yet complete. Ending turn.")
|
| 167 |
+
return "end_turn"
|
| 168 |
+
|
| 169 |
# --- Build the Graph ---
|
| 170 |
db_connection = sqlite3.connect("data/checkpoints.db", check_same_thread=False)
|
| 171 |
memory = SqliteSaver(db_connection)
|
|
|
|
| 174 |
|
| 175 |
# (CORRECTED) Removed the faulty entry_router_node
|
| 176 |
builder.add_node("profile_builder", profile_builder_node)
|
| 177 |
+
builder.add_node("plan_recommender", plan_recommender_node)
|
| 178 |
builder.add_node("reformulate_query", reformulate_query_node)
|
| 179 |
builder.add_node("retrieve_and_grade", retrieve_and_grade_node)
|
| 180 |
builder.add_node("search_and_ingest", search_and_ingest_node)
|
|
|
|
| 189 |
}
|
| 190 |
)
|
| 191 |
|
| 192 |
+
# (CRITICAL FIX) Add a conditional edge after the profile builder
|
| 193 |
+
builder.add_conditional_edges(
|
| 194 |
+
"profile_builder",
|
| 195 |
+
after_profile_build,
|
| 196 |
+
{
|
| 197 |
+
"recommend_plans": "plan_recommender",
|
| 198 |
+
"end_turn": END
|
| 199 |
+
}
|
| 200 |
+
)
|
| 201 |
+
|
| 202 |
# Define graph edges
|
| 203 |
builder.add_edge("profile_builder", END) # A profile turn is one full loop. The state is saved, and the next call will re-evaluate at the entry point.
|
| 204 |
+
builder.add_edge("plan_recommender", END)
|
| 205 |
builder.add_edge("reformulate_query", "retrieve_and_grade")
|
| 206 |
builder.add_conditional_edges("retrieve_and_grade", should_search_web, {"search": "search_and_ingest", "generate": "generate_answer"})
|
| 207 |
builder.add_edge("search_and_ingest", "generate_answer")
|
insucompass/core/agents/plan_agent.py
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import logging
|
| 2 |
+
import json
|
| 3 |
+
from typing import List, Dict, Any
|
| 4 |
+
|
| 5 |
+
from langchain_core.documents import Document
|
| 6 |
+
|
| 7 |
+
from insucompass.services import llm_provider
|
| 8 |
+
from insucompass.config import settings
|
| 9 |
+
from insucompass.prompts.prompt_loader import load_prompt
|
| 10 |
+
|
| 11 |
+
# Configure logging
|
| 12 |
+
logging.basicConfig(level=settings.LOG_LEVEL, format='%(asctime)s - %(levelname)s - %(message)s')
|
| 13 |
+
logger = logging.getLogger(__name__)
|
| 14 |
+
|
| 15 |
+
llm = llm_provider.get_gemini_llm()
|
| 16 |
+
|
| 17 |
+
class PlanAgent:
|
| 18 |
+
"""
|
| 19 |
+
An agent that analyzes a user profile and retrieved documents to
|
| 20 |
+
recommend the top 3 most suitable health insurance plans.
|
| 21 |
+
"""
|
| 22 |
+
|
| 23 |
+
def __init__(self):
|
| 24 |
+
"""Initializes the PlanAgent."""
|
| 25 |
+
try:
|
| 26 |
+
self.agent_prompt = load_prompt("plan_agent")
|
| 27 |
+
logger.info("PlanAgent initialized successfully.")
|
| 28 |
+
except FileNotFoundError:
|
| 29 |
+
logger.critical("PlanAgent prompt file not found. The agent cannot function.")
|
| 30 |
+
raise
|
| 31 |
+
|
| 32 |
+
def generate_recommendations(
|
| 33 |
+
self,
|
| 34 |
+
user_profile: Dict[str, Any],
|
| 35 |
+
documents: List[Document]
|
| 36 |
+
) -> Dict[str, Any]:
|
| 37 |
+
"""
|
| 38 |
+
Generates plan recommendations in a structured JSON format.
|
| 39 |
+
"""
|
| 40 |
+
if not documents:
|
| 41 |
+
logger.warning("PlanAgent received no documents. Cannot generate recommendations.")
|
| 42 |
+
return {"recommendations": []}
|
| 43 |
+
|
| 44 |
+
profile_str = json.dumps(user_profile, indent=2)
|
| 45 |
+
context_str = "\n\n---\n\n".join([d.page_content for d in documents])
|
| 46 |
+
|
| 47 |
+
full_prompt = (
|
| 48 |
+
f"{self.agent_prompt}\n\n"
|
| 49 |
+
f"### CONTEXT FOR YOUR RECOMMENDATION\n"
|
| 50 |
+
f"user_profile: {profile_str}\n\n"
|
| 51 |
+
f"retrieved_context:\n{context_str}"
|
| 52 |
+
)
|
| 53 |
+
|
| 54 |
+
logger.info("Generating plan recommendations with PlanAgent...")
|
| 55 |
+
try:
|
| 56 |
+
response = llm.invoke(full_prompt)
|
| 57 |
+
# Clean the response to ensure it's valid JSON
|
| 58 |
+
response_content = response.content.strip().replace("```json", "").replace("```", "")
|
| 59 |
+
recommendations = json.loads(response_content)
|
| 60 |
+
logger.info(f"Successfully generated structured plan recommendations \n\n {recommendations}.")
|
| 61 |
+
return recommendations
|
| 62 |
+
except json.JSONDecodeError as e:
|
| 63 |
+
logger.error(f"Failed to decode JSON from PlanAgent: {e}\nResponse was: {response.content}")
|
| 64 |
+
return {"recommendations": [], "error": "Failed to generate plan recommendations."}
|
| 65 |
+
except Exception as e:
|
| 66 |
+
logger.error(f"Error during plan recommendation generation: {e}")
|
| 67 |
+
return {"recommendations": [], "error": "An error occurred while generating plans."}
|
| 68 |
+
|
| 69 |
+
# Singleton instance
|
| 70 |
+
planner = PlanAgent()
|
insucompass/core/models.py
CHANGED
|
@@ -107,5 +107,6 @@ class ChatResponse(BaseModel):
|
|
| 107 |
updated_profile: Dict[str, Any]
|
| 108 |
updated_history: List[str]
|
| 109 |
is_profile_complete: bool
|
|
|
|
| 110 |
|
| 111 |
|
|
|
|
| 107 |
updated_profile: Dict[str, Any]
|
| 108 |
updated_history: List[str]
|
| 109 |
is_profile_complete: bool
|
| 110 |
+
plan_recommendations: Optional[Dict[str, Any]]
|
| 111 |
|
| 112 |
|
insucompass/prompts/plan_agent.txt
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
You are an expert U.S. health insurance plan advisor. Your task is to analyze a user's complete profile and the provided context of available health plans to recommend the top 3 most suitable plans. You MUST format your response as a single, clean JSON object.
|
| 2 |
+
|
| 3 |
+
### YOUR CONTEXT
|
| 4 |
+
1. `user_profile`: A detailed JSON object of the user's demographics, income, and health needs.
|
| 5 |
+
2. `retrieved_context`: Text snippets from official sources describing various health plans, benefits, and regulations.
|
| 6 |
+
|
| 7 |
+
### YOUR CHAIN-OF-THOUGHT REASONING PROCESS (Internal)
|
| 8 |
+
1. **Analyze Profile:** What are the key drivers for this user? (e.g., low income suggests subsidy focus; specific medical conditions suggest focus on low out-of-pocket costs and good coverage for that condition; young and healthy suggests a focus on low premiums).
|
| 9 |
+
2. **Scan Context for Plans:** Identify distinct health plans mentioned in the `retrieved_context`. Look for plan names (e.g., "Silver PPO 2000", "Bronze HSA 6500"), types (HMO, PPO), and key features (deductible, out-of-pocket max, premiums).
|
| 10 |
+
3. **Match and Rank:** For each identified plan, evaluate how well it matches the user's profile. Assign a mental score based on this match.
|
| 11 |
+
4. **Select Top 3:** Choose the three highest-scoring plans.
|
| 12 |
+
5. **Generate Reasoning:** For each of the top 3 plans, write a concise, personalized `reasoning` statement explaining *why* it's a good fit for this specific user, referencing their profile.
|
| 13 |
+
6. **Construct JSON:** Assemble the final JSON object according to the format below.
|
| 14 |
+
|
| 15 |
+
### OUTPUT FORMAT (JSON ONLY)
|
| 16 |
+
Your output MUST be a single JSON object containing a single key "recommendations", which is a list of plan objects. Each plan object must have the following keys:
|
| 17 |
+
- `plan_name`: (string) The name of the plan.
|
| 18 |
+
- `plan_type`: (string) e.g., "PPO", "HMO", "EPO".
|
| 19 |
+
- `key_features`: (list of strings) A list of 3-4 key highlights, e.g., ["$500 Deductible", "Low copay for specialists", "Includes dental & vision"].
|
| 20 |
+
- `estimated_premium`: (string) e.g., "$350/month". If not available, use "Varies".
|
| 21 |
+
- `reasoning`: (string) Your personalized explanation for why this plan is a good fit for the user.
|
| 22 |
+
|
| 23 |
+
### EXAMPLE OUTPUT
|
| 24 |
+
```json
|
| 25 |
+
{
|
| 26 |
+
"recommendations": [
|
| 27 |
+
{
|
| 28 |
+
"plan_name": "BlueCross Silver PPO 2500",
|
| 29 |
+
"plan_type": "PPO",
|
| 30 |
+
"key_features": [
|
| 31 |
+
"Moderate Premium",
|
| 32 |
+
"$2,500 Deductible",
|
| 33 |
+
"Flexibility to see out-of-network doctors"
|
| 34 |
+
],
|
| 35 |
+
"estimated_premium": "$420/month",
|
| 36 |
+
"reasoning": "A good balance of premium and deductible. The PPO flexibility is suitable given your need to see a specific specialist."
|
| 37 |
+
}
|
| 38 |
+
]
|
| 39 |
+
}
|
scripts/data_processing/__pycache__/document_loader.cpython-310.pyc
CHANGED
|
Binary files a/scripts/data_processing/__pycache__/document_loader.cpython-310.pyc and b/scripts/data_processing/__pycache__/document_loader.cpython-310.pyc differ
|
|
|