nikeshn commited on
Commit
a8a6287
Β·
verified Β·
1 Parent(s): f8ccb74

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +138 -14
app.py CHANGED
@@ -1838,6 +1838,15 @@ async def agent_query(req: AgentRequest):
1838
  rag = await tool_library_info(enriched_question, history[-5:] if history else None, model=req.model)
1839
  tools_used = ["get_library_info"]
1840
 
 
 
 
 
 
 
 
 
 
1841
  if rag.get("has_answer") and rag.get("answer"):
1842
  # Good RAG answer β€” synthesise and return
1843
  context_parts = [f"Library Knowledge Base:\n{rag['answer']}"]
@@ -1845,7 +1854,8 @@ async def agent_query(req: AgentRequest):
1845
  synthesis_prompt = (
1846
  f"{behavior}\n\n"
1847
  "You are the Khalifa University Library AI Assistant (Abu Dhabi, UAE). KU = Khalifa University.\n"
1848
- "Answer in 2-4 sentences. Include URLs from the context when relevant.\n\n"
 
1849
  f"Context:\n{chr(10).join(context_parts)}\n\n"
1850
  f"Question: {question}\nAnswer:"
1851
  )
@@ -1860,26 +1870,140 @@ async def agent_query(req: AgentRequest):
1860
  answer = rag["answer"]
1861
  elapsed = time.time() - start
1862
  return _make_agent_response(
1863
- answer=answer, intent="library_info", tools_used=tools_used,
1864
  search_results=[], sources=rag.get("sources", []),
1865
  model=req.model, elapsed=elapsed, question=question,
1866
  )
1867
  else:
1868
- # RAG found nothing β€” fall back to general LLM with library persona
1869
- general = await _answer_general(question, history)
1870
- gen_answer = general.get("answer", "").strip()
1871
- # Safety: if general also failed, give a redirect
1872
- if not gen_answer:
1873
- gen_answer = (
1874
- "I'm not sure I have the exact answer for that. "
1875
- "Please try <a href=\"https://library.ku.ac.ae/AskUs\" target=\"_blank\">Ask a Librarian</a> "
1876
- "for personalised help from the KU library team."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1877
  )
 
 
 
1878
  elapsed = time.time() - start
1879
  return _make_agent_response(
1880
- answer=gen_answer, intent="library_info",
1881
- tools_used=tools_used + ["general_fallback"],
1882
- search_results=[], sources=general.get("sources", []),
1883
  model=req.model, elapsed=elapsed, question=question,
1884
  )
1885
 
 
1838
  rag = await tool_library_info(enriched_question, history[-5:] if history else None, model=req.model)
1839
  tools_used = ["get_library_info"]
1840
 
1841
+ # Ask a Librarian footer β€” appended to ALL library_info answers
1842
+ ASK_LIB = (
1843
+ '<br><br><span style="font-size:.82rem;color:#6b7280">'
1844
+ 'πŸ’¬ Need more help? '
1845
+ '<a href="https://library.ku.ac.ae/AskUs" target="_blank" style="color:#003366;font-weight:600">Ask a Librarian</a>'
1846
+ ' β€” or browse <a href="https://library.ku.ac.ae/lib" target="_blank" style="color:#003366">library.ku.ac.ae</a>.'
1847
+ '</span>'
1848
+ )
1849
+
1850
  if rag.get("has_answer") and rag.get("answer"):
1851
  # Good RAG answer β€” synthesise and return
1852
  context_parts = [f"Library Knowledge Base:\n{rag['answer']}"]
 
1854
  synthesis_prompt = (
1855
  f"{behavior}\n\n"
1856
  "You are the Khalifa University Library AI Assistant (Abu Dhabi, UAE). KU = Khalifa University.\n"
1857
+ "Answer in 2-4 sentences. Include URLs from the context when relevant.\n"
1858
+ "Answer ONLY from the provided context. Do not add information not present in the context.\n\n"
1859
  f"Context:\n{chr(10).join(context_parts)}\n\n"
1860
  f"Question: {question}\nAnswer:"
1861
  )
 
1870
  answer = rag["answer"]
1871
  elapsed = time.time() - start
1872
  return _make_agent_response(
1873
+ answer=answer + ASK_LIB, intent="library_info", tools_used=tools_used,
1874
  search_results=[], sources=rag.get("sources", []),
1875
  model=req.model, elapsed=elapsed, question=question,
1876
  )
1877
  else:
1878
+ # ── RAG miss β€” three-tier fallback for library questions ──
1879
+ #
1880
+ # Tier 1: Strict KU-library-only LLM prompt (no hallucination risk β€”
1881
+ # LLM is told to say it doesn't know rather than invent).
1882
+ # Tier 2: If tier 1 is vague/empty β†’ web search scoped to library.ku.ac.ae
1883
+ # so all facts come from the real KU library website.
1884
+ # Tier 3: Always append "Ask a Librarian" + library homepage link.
1885
+ #
1886
+ # General web search (_answer_general) is NOT used here β€” that's only
1887
+ # for intent==general/general_recent where unrestricted search is correct.
1888
+
1889
+ ASK_LIB = (
1890
+ '<br><br><span style="font-size:.82rem;color:#6b7280">'
1891
+ 'πŸ’¬ Need more help? '
1892
+ '<a href="https://library.ku.ac.ae/AskUs" target="_blank" style="color:#003366;font-weight:600">Ask a Librarian</a>'
1893
+ ' β€” or browse <a href="https://library.ku.ac.ae/lib" target="_blank" style="color:#003366">library.ku.ac.ae</a>.'
1894
+ '</span>'
1895
+ )
1896
+
1897
+ # ── Tier 1: Strict KU-only LLM ──
1898
+ ku_only_prompt = (
1899
+ "You are LibBee, the Khalifa University Library AI Assistant (Abu Dhabi, UAE). "
1900
+ "KU = Khalifa University.\n\n"
1901
+ "STRICT RULES:\n"
1902
+ "1. Answer ONLY using your knowledge of Khalifa University Library services, "
1903
+ "databases, policies, staff, and resources.\n"
1904
+ "2. Be concise (2-4 sentences). Include real KU library URLs when relevant "
1905
+ "(e.g. https://library.ku.ac.ae/AskUs, https://library.ku.ac.ae/ill/, "
1906
+ "https://library.ku.ac.ae/eresources, https://library.ku.ac.ae/oa/).\n"
1907
+ "3. If you are not confident about a specific detail (phone number, exact policy, "
1908
+ "specific database URL), do NOT invent it β€” instead say you are not certain and "
1909
+ "direct the user to Ask a Librarian or the library website.\n"
1910
+ "4. Never make up staff names, contact details, or database names.\n"
1911
+ "5. Do NOT use general web knowledge or facts unrelated to KU Library.\n\n"
1912
+ f"Question: {question}\nAnswer:"
1913
+ )
1914
+ tier1_answer = ""
1915
+ try:
1916
+ if use_claude:
1917
+ from langchain_anthropic import ChatAnthropic
1918
+ t1_llm = ChatAnthropic(model="claude-haiku-4-5-20251001", temperature=0.1, max_tokens=400)
1919
+ else:
1920
+ t1_llm = ChatOpenAI(model="gpt-4o-mini", temperature=0.1, max_tokens=400)
1921
+ t1_response = t1_llm.invoke([
1922
+ {"role": "system", "content": ku_only_prompt},
1923
+ {"role": "user", "content": question},
1924
+ ])
1925
+ tier1_answer = t1_response.content.strip()
1926
+ except Exception as t1_err:
1927
+ print(f"[LibBee tier1 LLM error] {t1_err}")
1928
+
1929
+ # ── Tier 2: If tier 1 is vague, search library.ku.ac.ae ──
1930
+ # Detect vague answers: short, uncertain, or explicitly admitting no knowledge
1931
+ vague_phrases = [
1932
+ "i'm not sure", "i am not sure", "i don't have", "i do not have",
1933
+ "not certain", "cannot confirm", "not available", "unable to find",
1934
+ "please contact", "you may want to", "i would recommend checking",
1935
+ "i don't have specific", "i lack specific",
1936
+ ]
1937
+ is_vague = (
1938
+ not tier1_answer
1939
+ or len(tier1_answer.split()) < 15
1940
+ or any(p in tier1_answer.lower().replace("\u2019", "'") for p in vague_phrases)
1941
+ )
1942
+
1943
+ tier2_answer = ""
1944
+ tier2_sources = []
1945
+ if is_vague:
1946
+ try:
1947
+ import openai as _openai
1948
+ _client = _openai.OpenAI(api_key=os.environ.get("OPENAI_API_KEY"))
1949
+ # Scope web search to KU library domain only
1950
+ scoped_query = f"site:library.ku.ac.ae {question}"
1951
+ _resp = _client.responses.create(
1952
+ model="gpt-4o-mini",
1953
+ tools=[{"type": "web_search_preview"}],
1954
+ input=[{
1955
+ "role": "system",
1956
+ "content": (
1957
+ "You are LibBee, the Khalifa University Library AI Assistant. "
1958
+ "Search the KU library website (library.ku.ac.ae) only. "
1959
+ "Answer in 2-4 sentences using only what you find on the KU library website. "
1960
+ "Always include the source URL. "
1961
+ "If nothing relevant is found on library.ku.ac.ae, say so briefly."
1962
+ )
1963
+ }, {"role": "user", "content": scoped_query}],
1964
+ )
1965
+ for block in _resp.output:
1966
+ if hasattr(block, "content"):
1967
+ for item in block.content:
1968
+ if hasattr(item, "text"):
1969
+ tier2_answer += item.text
1970
+ if hasattr(item, "annotations"):
1971
+ for ann in item.annotations:
1972
+ if hasattr(ann, "url") and hasattr(ann, "title"):
1973
+ if "library.ku.ac.ae" in getattr(ann, "url", ""):
1974
+ tier2_sources.append({"url": ann.url, "title": ann.title})
1975
+ if not tier2_answer:
1976
+ tier2_answer = getattr(_resp, "output_text", "") or ""
1977
+ tier2_answer = tier2_answer.strip()
1978
+ except Exception as t2_err:
1979
+ print(f"[LibBee tier2 web search error] {t2_err}")
1980
+
1981
+ # ── Combine best answer + Ask a Librarian footer ──
1982
+ if tier2_answer and len(tier2_answer.split()) >= 10:
1983
+ # Web search found something useful
1984
+ final_answer = tier2_answer + ASK_LIB
1985
+ final_sources = tier2_sources
1986
+ final_tools = tools_used + ["ku_web_search"]
1987
+ elif tier1_answer and len(tier1_answer.split()) >= 10:
1988
+ # Tier 1 LLM gave a decent answer
1989
+ final_answer = tier1_answer + ASK_LIB
1990
+ final_sources = []
1991
+ final_tools = tools_used + ["ku_only_llm"]
1992
+ else:
1993
+ # Both failed β€” clean redirect only
1994
+ final_answer = (
1995
+ "I don't have that specific information in my knowledge base right now. "
1996
+ "The KU Library team will be able to help directly."
1997
+ + ASK_LIB
1998
  )
1999
+ final_sources = []
2000
+ final_tools = tools_used + ["redirected"]
2001
+
2002
  elapsed = time.time() - start
2003
  return _make_agent_response(
2004
+ answer=final_answer, intent="library_info",
2005
+ tools_used=final_tools,
2006
+ search_results=[], sources=final_sources,
2007
  model=req.model, elapsed=elapsed, question=question,
2008
  )
2009