Spaces:
Runtime error
Runtime error
| from typing import Dict, List | |
| import os | |
| from app.llm.client import llm_client | |
| from app.services.rag_service import rag_service | |
| class PolicyAgent: | |
| """Agent for construction policy and regulatory queries using official policy documents.""" | |
| def __init__(self): | |
| """Initialize policy agent with prompt template.""" | |
| prompt_path = os.path.join( | |
| os.path.dirname(__file__), | |
| "..", | |
| "prompts", | |
| "policy.txt" | |
| ) | |
| with open(prompt_path, "r") as f: | |
| self.system_prompt = f.read() | |
| def answer(self, query: str, context_chunks: List[Dict] = None) -> Dict[str, any]: | |
| """ | |
| Generate answer for policy/regulatory queries using official policy documents. | |
| Args: | |
| query: User query string | |
| context_chunks: Retrieved policy chunks from RAG search | |
| Returns: | |
| Dictionary with 'answer', 'agent', and 'sources' keys | |
| """ | |
| try: | |
| # If no context provided, search all official policies (retrieve more chunks for better coverage) | |
| if context_chunks is None: | |
| context_chunks = rag_service.collection.query( | |
| query_embeddings=[rag_service.embedding_generator.generate_embedding(query)], | |
| n_results=10, # Increased from 5 to 10 for better coverage | |
| where={"user_id": "official_policies"} | |
| ) | |
| # Format results | |
| if context_chunks and context_chunks['documents']: | |
| context_chunks = [ | |
| { | |
| "content": context_chunks['documents'][0][i], | |
| "metadata": context_chunks['metadatas'][0][i] | |
| } | |
| for i in range(len(context_chunks['documents'][0])) | |
| ] | |
| else: | |
| context_chunks = [] | |
| # Build context from chunks | |
| if not context_chunks: | |
| return { | |
| "answer": "I don't have any official policy documents to answer this question. Please ensure policies are uploaded in the Admin Panel.", | |
| "agent": "policy", | |
| "sources": [] | |
| } | |
| # Format context with clear chunk numbering | |
| context_sections = [] | |
| for i, chunk in enumerate(context_chunks, 1): | |
| doc_id = chunk['metadata'].get('document_id', 'unknown') | |
| filename = chunk['metadata'].get('filename', 'Official Policy') | |
| chunk_idx = chunk['metadata'].get('chunk_index', '?') | |
| context_sections.append( | |
| f"=== EXCERPT {i} ===\n" | |
| f"Document: {filename}\n" | |
| f"Document ID: {doc_id}\n" | |
| f"Section: Chunk {chunk_idx}\n" | |
| f"---\n" | |
| f"{chunk['content']}\n" | |
| ) | |
| context_text = "\n".join(context_sections) | |
| # Create strict user message | |
| user_message = f"""DOCUMENT EXCERPTS FROM OFFICIAL POLICY: | |
| {context_text} | |
| ======================================== | |
| USER QUESTION: {query} | |
| ======================================== | |
| REMEMBER: | |
| - Answer using ONLY the excerpts above | |
| - Include clause/section numbers if present in the text | |
| - Quote exact definitions or requirements | |
| - If the answer is not in the excerpts, say "The provided document sections do not contain this information" | |
| - Do NOT use external knowledge from other building codes | |
| Now provide your answer:""" | |
| messages = [ | |
| {"role": "system", "content": self.system_prompt}, | |
| {"role": "user", "content": user_message} | |
| ] | |
| answer = llm_client.get_completion( | |
| messages=messages, | |
| temperature=0.1, # Very low temperature for maximum accuracy and minimal creativity | |
| max_tokens=2000 | |
| ) | |
| # Extract sources and policy names | |
| sources = [] | |
| policy_names = set() | |
| # Get policy titles from database | |
| from app.database.connection import SessionLocal | |
| from app.database.models import OfficialPolicy | |
| db = SessionLocal() | |
| try: | |
| # Collect unique document IDs | |
| doc_ids = set() | |
| for chunk in context_chunks: | |
| doc_id = chunk["metadata"].get("document_id", "") | |
| if doc_id: | |
| doc_ids.add(doc_id) | |
| # Fetch policy titles from database | |
| policy_title_map = {} | |
| if doc_ids: | |
| policies = db.query(OfficialPolicy).filter( | |
| OfficialPolicy.id.in_(doc_ids) | |
| ).all() | |
| policy_title_map = {p.id: p.title for p in policies} | |
| # Build sources and collect policy names | |
| for chunk in context_chunks: | |
| doc_id = chunk["metadata"].get("document_id", "") | |
| policy_title = policy_title_map.get(doc_id, chunk["metadata"].get("filename", "Official Policy")) | |
| sources.append({ | |
| "content": chunk["content"][:300] + "...", | |
| "document_id": doc_id, | |
| "filename": chunk["metadata"].get("filename", "Official Policy"), | |
| "title": policy_title, | |
| "chunk_index": chunk["metadata"].get("chunk_index", 0) | |
| }) | |
| # Collect unique policy titles (not filenames) | |
| if policy_title: | |
| policy_names.add(policy_title) | |
| finally: | |
| db.close() | |
| print(f"[Policy Agent] Returning policy_names: {list(policy_names)}") | |
| return { | |
| "answer": answer, | |
| "agent": "policy", | |
| "sources": sources, | |
| "policy_names": list(policy_names) # List of policy titles used | |
| } | |
| except Exception as e: | |
| print(f"Policy agent error: {e}") | |
| return { | |
| "answer": "I encountered an error while processing your policy question. Please try again.", | |
| "agent": "policy", | |
| "sources": [] | |
| } | |
| # Global policy agent instance | |
| policy_agent = PolicyAgent() | |